Fix data races across session/forwarder/registry, resolve POST/PUT hang, harden port allocation, add frontendURL env #157

Merged
bagas merged 6 commits from staging into main 2026-07-19 14:10:50 +07:00
12 changed files with 22 additions and 6 deletions
Showing only changes of commit d0a5033964 - Show all commits
+1
View File
@@ -36,6 +36,7 @@ The following environment variables can be configured in the `.env` file:
| Variable | Description | Default | Required | | Variable | Description | Default | Required |
|---------------------|-----------------------------------------------------------------------------|-------------------------|---------------------| |---------------------|-----------------------------------------------------------------------------|-------------------------|---------------------|
| `DOMAIN` | Domain name for subdomain routing | `localhost` | No | | `DOMAIN` | Domain name for subdomain routing | `localhost` | No |
| `FRONTEND_URL` | URL for the frontend dashboard/landing page | `https://<DOMAIN>` | No |
| `PORT` | SSH server port | `2200` | No | | `PORT` | SSH server port | `2200` | No |
| `HTTP_PORT` | HTTP server port | `8080` | No | | `HTTP_PORT` | HTTP server port | `8080` | No |
| `HTTPS_PORT` | HTTPS server port | `8443` | No | | `HTTPS_PORT` | HTTPS server port | `8443` | No |
+1
View File
@@ -80,6 +80,7 @@ type MockConfig struct {
} }
func (m *MockConfig) Domain() string { return m.Called().String(0) } func (m *MockConfig) Domain() string { return m.Called().String(0) }
func (m *MockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *MockConfig) SSHPort() string { return m.Called().String(0) } func (m *MockConfig) SSHPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) }
+2
View File
@@ -6,6 +6,7 @@ import (
type Config interface { type Config interface {
Domain() string Domain() string
FrontendURL() string
SSHPort() string SSHPort() string
HTTPPort() string HTTPPort() string
@@ -50,6 +51,7 @@ func MustLoad() (Config, error) {
} }
func (c *config) Domain() string { return c.domain } func (c *config) Domain() string { return c.domain }
func (c *config) FrontendURL() string { return c.frontendURL }
func (c *config) SSHPort() string { return c.sshPort } func (c *config) SSHPort() string { return c.sshPort }
func (c *config) HTTPPort() string { return c.httpPort } func (c *config) HTTPPort() string { return c.httpPort }
func (c *config) HTTPSPort() string { return c.httpsPort } func (c *config) HTTPSPort() string { return c.httpsPort }
+5 -2
View File
@@ -12,8 +12,9 @@ import (
) )
type config struct { type config struct {
domain string domain string
sshPort string frontendURL string
sshPort string
httpPort string httpPort string
httpsPort string httpsPort string
@@ -49,6 +50,7 @@ func parse() (*config, error) {
} }
domain := getenv("DOMAIN", "localhost") domain := getenv("DOMAIN", "localhost")
frontendURL := getenv("FRONTEND_URL", "https://"+domain)
sshPort := getenv("PORT", "2200") sshPort := getenv("PORT", "2200")
httpPort := getenv("HTTP_PORT", "8080") httpPort := getenv("HTTP_PORT", "8080")
@@ -89,6 +91,7 @@ func parse() (*config, error) {
return &config{ return &config{
domain: domain, domain: domain,
frontendURL: frontendURL,
sshPort: sshPort, sshPort: sshPort,
httpPort: httpPort, httpPort: httpPort,
httpsPort: httpsPort, httpsPort: httpsPort,
+1
View File
@@ -753,6 +753,7 @@ type MockConfig struct {
} }
func (m *MockConfig) Domain() string { return m.Called().String(0) } func (m *MockConfig) Domain() string { return m.Called().String(0) }
func (m *MockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *MockConfig) SSHPort() string { return m.Called().String(0) } func (m *MockConfig) SSHPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) }
+1
View File
@@ -33,6 +33,7 @@ type MockConfig struct {
} }
func (m *MockConfig) Domain() string { return m.Called().String(0) } func (m *MockConfig) Domain() string { return m.Called().String(0) }
func (m *MockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *MockConfig) SSHPort() string { return m.Called().String(0) } func (m *MockConfig) SSHPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) }
@@ -24,6 +24,7 @@ type mockConfig struct {
} }
func (m *mockConfig) Domain() string { return m.Called().String(0) } func (m *mockConfig) Domain() string { return m.Called().String(0) }
func (m *mockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *mockConfig) SSHPort() string { return m.Called().String(0) } func (m *mockConfig) SSHPort() string { return m.Called().String(0) }
func (m *mockConfig) HTTPPort() string { return m.Called().String(0) } func (m *mockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *mockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *mockConfig) HTTPSPort() string { return m.Called().String(0) }
@@ -32,6 +32,7 @@ type MockConfig struct {
} }
func (m *MockConfig) Domain() string { return m.Called().String(0) } func (m *MockConfig) Domain() string { return m.Called().String(0) }
func (m *MockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *MockConfig) SSHPort() string { return m.Called().String(0) } func (m *MockConfig) SSHPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) }
+3 -2
View File
@@ -38,8 +38,9 @@ type mockConfig struct {
config.Config config.Config
} }
func (m *mockConfig) Domain() string { return m.Called().String(0) } func (m *mockConfig) Domain() string { return m.Called().String(0) }
func (m *mockConfig) SSHPort() string { return m.Called().String(0) } func (m *mockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *mockConfig) SSHPort() string { return m.Called().String(0) }
func (m *mockConfig) Mode() types.ServerMode { func (m *mockConfig) Mode() types.ServerMode {
args := m.Called() args := m.Called()
if args.Get(0) == nil { if args.Get(0) == nil {
+1 -1
View File
@@ -116,7 +116,7 @@ func (hh *httpHandler) Handler(conn net.Conn, isTLS bool) {
Type: types.TunnelTypeHTTP, Type: types.TunnelTypeHTTP,
}) })
if err != nil { if err != nil {
_ = hh.redirect(conn, http.StatusMovedPermanently, fmt.Sprintf("https://tunnl.live/tunnel-not-found?slug=%s\r\n", slug)) _ = hh.redirect(conn, http.StatusMovedPermanently, fmt.Sprintf("%s/tunnel-not-found?slug=%s\r\n", hh.config.FrontendURL(), slug))
return return
} }
+4 -1
View File
@@ -223,6 +223,7 @@ func TestNewHTTPHandler(t *testing.T) {
msr := new(MockSessionRegistry) msr := new(MockSessionRegistry)
mockConfig := &MockConfig{} mockConfig := &MockConfig{}
mockConfig.On("Domain").Return("domain") mockConfig.On("Domain").Return("domain")
mockConfig.On("FrontendURL").Return("https://domain")
mockConfig.On("TLSRedirect").Return(false) mockConfig.On("TLSRedirect").Return(false)
hh := newHTTPHandler(mockConfig, msr) hh := newHTTPHandler(mockConfig, msr)
assert.NotNil(t, hh) assert.NotNil(t, hh)
@@ -290,7 +291,7 @@ func TestHandler(t *testing.T) {
isTLS: true, isTLS: true,
redirectTLS: false, redirectTLS: false,
request: []byte("GET / HTTP/1.1\r\nHost: test.domain\r\n\r\n"), request: []byte("GET / HTTP/1.1\r\nHost: test.domain\r\n\r\n"),
expected: []byte("HTTP/1.1 301 Moved Permanently\r\nLocation: https://tunnl.live/tunnel-not-found?slug=test\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"), expected: []byte("HTTP/1.1 301 Moved Permanently\r\nLocation: https://example.com/tunnel-not-found?slug=test\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"),
setupMocks: func(msr *MockSessionRegistry) { setupMocks: func(msr *MockSessionRegistry) {
msr.On("Get", types.SessionKey{ msr.On("Get", types.SessionKey{
Id: "test", Id: "test",
@@ -616,6 +617,7 @@ func TestHandler(t *testing.T) {
mockConfig := &MockConfig{} mockConfig := &MockConfig{}
port := "0" port := "0"
mockConfig.On("Domain").Return("example.com") mockConfig.On("Domain").Return("example.com")
mockConfig.On("FrontendURL").Return("https://example.com")
mockConfig.On("HTTPPort").Return(port) mockConfig.On("HTTPPort").Return(port)
mockConfig.On("HeaderSize").Return(4096) mockConfig.On("HeaderSize").Return(4096)
mockConfig.On("TLSRedirect").Return(true) mockConfig.On("TLSRedirect").Return(true)
@@ -726,6 +728,7 @@ func TestHandlerForwardsPostBody(t *testing.T) {
mockSessionRegistry := new(MockSessionRegistry) mockSessionRegistry := new(MockSessionRegistry)
mockConfig := &MockConfig{} mockConfig := &MockConfig{}
mockConfig.On("Domain").Return("example.com") mockConfig.On("Domain").Return("example.com")
mockConfig.On("FrontendURL").Return("https://example.com")
mockConfig.On("HTTPPort").Return("0") mockConfig.On("HTTPPort").Return("0")
mockConfig.On("HeaderSize").Return(4096) mockConfig.On("HeaderSize").Return(4096)
mockConfig.On("TLSRedirect").Return(true) mockConfig.On("TLSRedirect").Return(true)
+1
View File
@@ -25,6 +25,7 @@ type MockConfig struct {
} }
func (m *MockConfig) Domain() string { return m.Called().String(0) } func (m *MockConfig) Domain() string { return m.Called().String(0) }
func (m *MockConfig) FrontendURL() string { return m.Called().String(0) }
func (m *MockConfig) SSHPort() string { return m.Called().String(0) } func (m *MockConfig) SSHPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPPort() string { return m.Called().String(0) }
func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) } func (m *MockConfig) HTTPSPort() string { return m.Called().String(0) }