mirror of https://github.com/tailscale/tailscale/
Merge 91d05773f7 into f8cd07fb8a
commit
c3a83790a0
@ -0,0 +1,86 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/ipn"
|
||||
)
|
||||
|
||||
func TestServeUnixSocketCLI(t *testing.T) {
|
||||
// Create a temporary directory for our socket path
|
||||
tmpDir := t.TempDir()
|
||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
||||
|
||||
// Test that Unix socket targets are accepted by ExpandProxyTargetValue
|
||||
target := "unix:" + socketPath
|
||||
result, err := ipn.ExpandProxyTargetValue(target, []string{"http", "https", "https+insecure", "unix"}, "http")
|
||||
if err != nil {
|
||||
t.Fatalf("ExpandProxyTargetValue failed: %v", err)
|
||||
}
|
||||
|
||||
if result != target {
|
||||
t.Errorf("ExpandProxyTargetValue(%q) = %q, want %q", target, result, target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeUnixSocketConfigPreserved(t *testing.T) {
|
||||
// Test that Unix socket URLs are preserved in ServeConfig
|
||||
sc := &ipn.ServeConfig{
|
||||
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
||||
"foo.test.ts.net:443": {Handlers: map[string]*ipn.HTTPHandler{
|
||||
"/": {Proxy: "unix:/tmp/test.sock"},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
// Verify the proxy value is preserved
|
||||
handler := sc.Web["foo.test.ts.net:443"].Handlers["/"]
|
||||
if handler.Proxy != "unix:/tmp/test.sock" {
|
||||
t.Errorf("proxy = %q, want %q", handler.Proxy, "unix:/tmp/test.sock")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeUnixSocketVariousPaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "absolute-path",
|
||||
target: "unix:/var/run/docker.sock",
|
||||
},
|
||||
{
|
||||
name: "tmp-path",
|
||||
target: "unix:/tmp/myservice.sock",
|
||||
},
|
||||
{
|
||||
name: "relative-path",
|
||||
target: "unix:./local.sock",
|
||||
},
|
||||
{
|
||||
name: "home-path",
|
||||
target: "unix:/home/user/.local/service.sock",
|
||||
},
|
||||
{
|
||||
name: "empty-path",
|
||||
target: "unix:",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := ipn.ExpandProxyTargetValue(tt.target, []string{"http", "https", "unix"}, "http")
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ExpandProxyTargetValue(%q) error = %v, wantErr %v", tt.target, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package ipnlocal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/tstest"
|
||||
)
|
||||
|
||||
func TestExpandProxyArgUnix(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
wantURL string
|
||||
wantInsecure bool
|
||||
}{
|
||||
{
|
||||
input: "unix:/tmp/test.sock",
|
||||
wantURL: "unix:/tmp/test.sock",
|
||||
},
|
||||
{
|
||||
input: "unix:/var/run/docker.sock",
|
||||
wantURL: "unix:/var/run/docker.sock",
|
||||
},
|
||||
{
|
||||
input: "unix:./relative.sock",
|
||||
wantURL: "unix:./relative.sock",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
gotURL, gotInsecure := expandProxyArg(tt.input)
|
||||
if gotURL != tt.wantURL {
|
||||
t.Errorf("expandProxyArg(%q) url = %q, want %q", tt.input, gotURL, tt.wantURL)
|
||||
}
|
||||
if gotInsecure != tt.wantInsecure {
|
||||
t.Errorf("expandProxyArg(%q) insecure = %v, want %v", tt.input, gotInsecure, tt.wantInsecure)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeUnixSocket(t *testing.T) {
|
||||
// Create a temporary directory for our socket
|
||||
tmpDir := t.TempDir()
|
||||
socketPath := filepath.Join(tmpDir, "test.sock")
|
||||
|
||||
// Create a test HTTP server on Unix socket
|
||||
listener, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create unix socket listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
testResponse := "Hello from Unix socket!"
|
||||
testServer := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprint(w, testResponse)
|
||||
}),
|
||||
}
|
||||
|
||||
go testServer.Serve(listener)
|
||||
defer testServer.Close()
|
||||
|
||||
// Wait for server to be ready
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// Create LocalBackend with test logger
|
||||
logf := tstest.WhileTestRunningLogger(t)
|
||||
b := newTestBackend(t)
|
||||
b.logf = logf
|
||||
|
||||
// Test creating proxy handler for Unix socket
|
||||
handler, err := b.proxyHandlerForBackend("unix:" + socketPath)
|
||||
if err != nil {
|
||||
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify it's a reverseProxy with correct socketPath
|
||||
rp, ok := handler.(*reverseProxy)
|
||||
if !ok {
|
||||
t.Fatalf("expected *reverseProxy, got %T", handler)
|
||||
}
|
||||
if rp.socketPath != socketPath {
|
||||
t.Errorf("socketPath = %q, want %q", rp.socketPath, socketPath)
|
||||
}
|
||||
if rp.url.Host != "localhost" {
|
||||
t.Errorf("url.Host = %q, want %q", rp.url.Host, "localhost")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeUnixSocketErrors(t *testing.T) {
|
||||
logf := tstest.WhileTestRunningLogger(t)
|
||||
b := newTestBackend(t)
|
||||
b.logf = logf
|
||||
|
||||
// Test empty socket path
|
||||
_, err := b.proxyHandlerForBackend("unix:")
|
||||
if err == nil {
|
||||
t.Error("expected error for empty socket path")
|
||||
}
|
||||
|
||||
// Test non-existent socket - should create handler but fail on request
|
||||
nonExistentSocket := filepath.Join(t.TempDir(), "nonexistent.sock")
|
||||
handler, err := b.proxyHandlerForBackend("unix:" + nonExistentSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", "http://foo.test.ts.net/", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
// Should get a 502 Bad Gateway when socket doesn't exist
|
||||
if rec.Code != http.StatusBadGateway {
|
||||
t.Errorf("got status %d, want %d for non-existent socket", rec.Code, http.StatusBadGateway)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReverseProxyConfigurationUnix(t *testing.T) {
|
||||
b := newTestBackend(t)
|
||||
|
||||
// Test that Unix socket backend creates proper reverseProxy
|
||||
backend := "unix:/var/run/test.sock"
|
||||
handler, err := b.proxyHandlerForBackend(backend)
|
||||
if err != nil {
|
||||
t.Fatalf("proxyHandlerForBackend failed: %v", err)
|
||||
}
|
||||
|
||||
rp, ok := handler.(*reverseProxy)
|
||||
if !ok {
|
||||
t.Fatalf("expected *reverseProxy, got %T", handler)
|
||||
}
|
||||
|
||||
// Verify configuration
|
||||
if rp.socketPath != "/var/run/test.sock" {
|
||||
t.Errorf("socketPath = %q, want %q", rp.socketPath, "/var/run/test.sock")
|
||||
}
|
||||
if rp.backend != backend {
|
||||
t.Errorf("backend = %q, want %q", rp.backend, backend)
|
||||
}
|
||||
if rp.insecure {
|
||||
t.Error("insecure should be false for unix sockets")
|
||||
}
|
||||
expectedURL := url.URL{Scheme: "http", Host: "localhost"}
|
||||
if rp.url.Scheme != expectedURL.Scheme || rp.url.Host != expectedURL.Host {
|
||||
t.Errorf("url = %v, want %v", rp.url, expectedURL)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipn
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExpandProxyTargetValueUnix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
target string
|
||||
supportedSchemes []string
|
||||
defaultScheme string
|
||||
want string
|
||||
wantErr bool
|
||||
skipOnWindows bool
|
||||
}{
|
||||
{
|
||||
name: "unix-socket-absolute-path",
|
||||
target: "unix:/tmp/myservice.sock",
|
||||
supportedSchemes: []string{"http", "https", "unix"},
|
||||
defaultScheme: "http",
|
||||
want: "unix:/tmp/myservice.sock",
|
||||
skipOnWindows: true,
|
||||
},
|
||||
{
|
||||
name: "unix-socket-var-run",
|
||||
target: "unix:/var/run/docker.sock",
|
||||
supportedSchemes: []string{"http", "https", "unix"},
|
||||
defaultScheme: "http",
|
||||
want: "unix:/var/run/docker.sock",
|
||||
skipOnWindows: true,
|
||||
},
|
||||
{
|
||||
name: "unix-socket-relative-path",
|
||||
target: "unix:./myservice.sock",
|
||||
supportedSchemes: []string{"http", "https", "unix"},
|
||||
defaultScheme: "http",
|
||||
want: "unix:./myservice.sock",
|
||||
skipOnWindows: true,
|
||||
},
|
||||
{
|
||||
name: "unix-socket-empty-path",
|
||||
target: "unix:",
|
||||
supportedSchemes: []string{"http", "https", "unix"},
|
||||
defaultScheme: "http",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unix-socket-not-in-supported-schemes",
|
||||
target: "unix:/tmp/myservice.sock",
|
||||
supportedSchemes: []string{"http", "https"},
|
||||
defaultScheme: "http",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.skipOnWindows && runtime.GOOS == "windows" {
|
||||
t.Skip("skipping unix socket test on Windows")
|
||||
}
|
||||
|
||||
// On Windows, unix sockets should always error
|
||||
if runtime.GOOS == "windows" && !tt.wantErr {
|
||||
tt.wantErr = true
|
||||
}
|
||||
|
||||
got, err := ExpandProxyTargetValue(tt.target, tt.supportedSchemes, tt.defaultScheme)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ExpandProxyTargetValue() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr && got != tt.want {
|
||||
t.Errorf("ExpandProxyTargetValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue