You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailscale/ipn/ipnlocal/serve_unix_test.go

163 lines
4.1 KiB
Go

// 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)
}
}