Skip to content

Commit 6adfd74

Browse files
committed
pare down to smoke tests
1 parent ab2b7c1 commit 6adfd74

1 file changed

Lines changed: 63 additions & 173 deletions

File tree

internal/api/proxy_test.go

Lines changed: 63 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"net/http"
1010
"net/http/httptest"
1111
"net/url"
12+
"os"
13+
"path/filepath"
1214
"strings"
1315
"sync"
1416
"testing"
@@ -129,29 +131,57 @@ func startTargetServer(t *testing.T) *httptest.Server {
129131
return srv
130132
}
131133

132-
func TestWithProxyTransport_HTTPProxy(t *testing.T) {
134+
func TestWithProxyTransport_UDSProxy(t *testing.T) {
133135
target := startTargetServer(t)
136+
targetURL, _ := url.Parse(target.URL)
137+
138+
// Use /tmp directly because t.TempDir() paths on macOS could exceed
139+
// the 108-character limit for unix socket addresses.
140+
socketPath := filepath.Join("/tmp", fmt.Sprintf("src-cli-test-%d.sock", time.Now().UnixNano()))
141+
t.Cleanup(func() { os.Remove(socketPath) })
142+
ln, err := net.Listen("unix", socketPath)
143+
if err != nil {
144+
t.Fatalf("listen unix: %v", err)
145+
}
146+
t.Cleanup(func() { ln.Close() })
134147

148+
// The UDS proxy path dials the unix socket directly (no CONNECT).
149+
// Simulate a forwarding proxy that copies bytes to the target.
135150
var mu sync.Mutex
136151
var used bool
137-
var proto string
138152

139-
proxyURL := startProxy(t, proxyOpts{
140-
observe: func(r *http.Request) {
153+
go func() {
154+
for {
155+
conn, err := ln.Accept()
156+
if err != nil {
157+
return
158+
}
141159
mu.Lock()
142-
defer mu.Unlock()
143160
used = true
144-
proto = r.Proto
145-
},
146-
})
161+
mu.Unlock()
162+
go func() {
163+
defer conn.Close()
164+
dest, err := net.DialTimeout("tcp", targetURL.Host, 10*time.Second)
165+
if err != nil {
166+
return
167+
}
168+
defer dest.Close()
169+
var wg sync.WaitGroup
170+
wg.Add(2)
171+
go func() { defer wg.Done(); io.Copy(dest, conn) }()
172+
go func() { defer wg.Done(); io.Copy(conn, dest) }()
173+
wg.Wait()
174+
}()
175+
}
176+
}()
147177

148-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
178+
transport := withProxyTransport(newTestTransport(), nil, socketPath)
149179
t.Cleanup(transport.CloseIdleConnections)
150180
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
151181

152182
resp, err := client.Get(target.URL)
153183
if err != nil {
154-
t.Fatalf("GET through http proxy: %v", err)
184+
t.Fatalf("GET through unix proxy: %v", err)
155185
}
156186
defer resp.Body.Close()
157187
body, err := io.ReadAll(resp.Body)
@@ -169,27 +199,21 @@ func TestWithProxyTransport_HTTPProxy(t *testing.T) {
169199
mu.Lock()
170200
defer mu.Unlock()
171201
if !used {
172-
t.Fatal("proxy handler was never invoked")
173-
}
174-
if proto != "HTTP/1.1" {
175-
t.Errorf("expected proxy to see HTTP/1.1 CONNECT, got %s", proto)
202+
t.Fatal("unix socket proxy was never used")
176203
}
177204
}
178205

179-
func TestWithProxyTransport_HTTPSProxy(t *testing.T) {
206+
func TestWithProxyTransport_HTTPProxy(t *testing.T) {
180207
target := startTargetServer(t)
181208

182209
var mu sync.Mutex
183210
var used bool
184-
var proto string
185211

186212
proxyURL := startProxy(t, proxyOpts{
187-
useTLS: true,
188213
observe: func(r *http.Request) {
189214
mu.Lock()
190215
defer mu.Unlock()
191216
used = true
192-
proto = r.Proto
193217
},
194218
})
195219

@@ -199,7 +223,7 @@ func TestWithProxyTransport_HTTPSProxy(t *testing.T) {
199223

200224
resp, err := client.Get(target.URL)
201225
if err != nil {
202-
t.Fatalf("GET through https proxy: %v", err)
226+
t.Fatalf("GET through http proxy: %v", err)
203227
}
204228
defer resp.Body.Close()
205229
body, err := io.ReadAll(resp.Body)
@@ -219,60 +243,22 @@ func TestWithProxyTransport_HTTPSProxy(t *testing.T) {
219243
if !used {
220244
t.Fatal("proxy handler was never invoked")
221245
}
222-
if proto != "HTTP/1.1" {
223-
t.Errorf("expected proxy to see HTTP/1.1 CONNECT, got %s", proto)
224-
}
225246
}
226247

227-
func TestWithProxyTransport_ProxyAuth(t *testing.T) {
248+
func TestWithProxyTransport_HTTPSProxy(t *testing.T) {
228249
target := startTargetServer(t)
229250

230-
t.Run("http proxy with auth", func(t *testing.T) {
231-
proxyURL := startProxy(t, proxyOpts{username: "user", password: "pass"})
232-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
233-
t.Cleanup(transport.CloseIdleConnections)
234-
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
235-
236-
resp, err := client.Get(target.URL)
237-
if err != nil {
238-
t.Fatalf("GET through authenticated http proxy: %v", err)
239-
}
240-
defer resp.Body.Close()
241-
if _, err := io.ReadAll(resp.Body); err != nil {
242-
t.Fatalf("read body: %v", err)
243-
}
244-
245-
if resp.StatusCode != http.StatusOK {
246-
t.Errorf("expected 200, got %d", resp.StatusCode)
247-
}
248-
})
249-
250-
t.Run("https proxy with auth", func(t *testing.T) {
251-
proxyURL := startProxy(t, proxyOpts{useTLS: true, username: "user", password: "s3cret"})
252-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
253-
t.Cleanup(transport.CloseIdleConnections)
254-
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
255-
256-
resp, err := client.Get(target.URL)
257-
if err != nil {
258-
t.Fatalf("GET through authenticated https proxy: %v", err)
259-
}
260-
defer resp.Body.Close()
261-
if _, err := io.ReadAll(resp.Body); err != nil {
262-
t.Fatalf("read body: %v", err)
263-
}
251+
var mu sync.Mutex
252+
var used bool
264253

265-
if resp.StatusCode != http.StatusOK {
266-
t.Errorf("expected 200, got %d", resp.StatusCode)
267-
}
254+
proxyURL := startProxy(t, proxyOpts{
255+
useTLS: true,
256+
observe: func(r *http.Request) {
257+
mu.Lock()
258+
defer mu.Unlock()
259+
used = true
260+
},
268261
})
269-
}
270-
271-
func TestWithProxyTransport_HTTPSProxy_HTTP2ToOrigin(t *testing.T) {
272-
// Verify that when tunneling through an HTTPS proxy, the connection to
273-
// the origin target still negotiates HTTP/2 (not downgraded to HTTP/1.1).
274-
target := startTargetServer(t)
275-
proxyURL := startProxy(t, proxyOpts{useTLS: true})
276262

277263
transport := withProxyTransport(newTestTransport(), proxyURL, "")
278264
t.Cleanup(transport.CloseIdleConnections)
@@ -283,117 +269,21 @@ func TestWithProxyTransport_HTTPSProxy_HTTP2ToOrigin(t *testing.T) {
283269
t.Fatalf("GET through https proxy: %v", err)
284270
}
285271
defer resp.Body.Close()
286-
if _, err := io.ReadAll(resp.Body); err != nil {
287-
t.Fatalf("read body: %v", err)
288-
}
289-
290-
if resp.ProtoMajor != 2 {
291-
t.Errorf("expected HTTP/2 to origin, got %s", resp.Proto)
292-
}
293-
}
294-
295-
func TestWithProxyTransport_HandshakeFailureClosesConn(t *testing.T) {
296-
// Verify that when the TLS handshake to the origin fails, the underlying
297-
// tunnel connection is closed (regression test for tlsConn.Close on error).
298-
//
299-
// A plain TCP listener acts as the target. The proxy CONNECT succeeds
300-
// (TCP-level), but the subsequent TLS handshake fails because the target
301-
// is not a TLS server. If handshakeTLS properly closes tlsConn on failure,
302-
// the tunnel tears down and the target sees the connection close.
303-
connClosed := make(chan struct{})
304-
ln, err := net.Listen("tcp", "127.0.0.1:0")
272+
body, err := io.ReadAll(resp.Body)
305273
if err != nil {
306-
t.Fatalf("listen: %v", err)
307-
}
308-
defer ln.Close()
309-
310-
go func() {
311-
conn, err := ln.Accept()
312-
if err != nil {
313-
return
314-
}
315-
defer conn.Close()
316-
// Send non-TLS bytes so the client handshake fails immediately
317-
// rather than waiting for a timeout.
318-
conn.Write([]byte("not-tls\n"))
319-
// Drain until the remote side closes the tunnel.
320-
io.Copy(io.Discard, conn)
321-
close(connClosed)
322-
}()
323-
324-
proxyURL := startProxy(t, proxyOpts{useTLS: true})
325-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
326-
t.Cleanup(transport.CloseIdleConnections)
327-
client := &http.Client{Transport: transport, Timeout: 5 * time.Second}
328-
329-
_, err = client.Get("https://" + ln.Addr().String())
330-
if err == nil {
331-
t.Fatal("expected TLS handshake error, got nil")
274+
t.Fatalf("read body: %v", err)
332275
}
333276

334-
select {
335-
case <-connClosed:
336-
// Connection was properly cleaned up.
337-
case <-time.After(5 * time.Second):
338-
t.Fatal("connection was not closed after TLS handshake failure")
277+
if resp.StatusCode != http.StatusOK {
278+
t.Errorf("expected 200, got %d", resp.StatusCode)
339279
}
340-
}
341-
342-
func TestWithProxyTransport_ProxyRejectsConnect(t *testing.T) {
343-
tests := []struct {
344-
name string
345-
statusCode int
346-
body string
347-
wantErr string
348-
}{
349-
{"407 proxy auth required", http.StatusProxyAuthRequired, "proxy auth required", "Proxy Authentication Required"},
350-
{"403 forbidden", http.StatusForbidden, "access denied by policy", "Forbidden"},
351-
{"502 bad gateway", http.StatusBadGateway, "upstream unreachable", "Bad Gateway"},
280+
if got := strings.TrimSpace(string(body)); got != "ok" {
281+
t.Errorf("expected body 'ok', got %q", got)
352282
}
353283

354-
// Use a local target so we never depend on external DNS.
355-
target := startTargetServer(t)
356-
357-
for _, tt := range tests {
358-
t.Run("http proxy/"+tt.name, func(t *testing.T) {
359-
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
360-
http.Error(w, tt.body, tt.statusCode)
361-
}))
362-
t.Cleanup(srv.Close)
363-
364-
proxyURL, _ := url.Parse(srv.URL)
365-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
366-
t.Cleanup(transport.CloseIdleConnections)
367-
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
368-
369-
_, err := client.Get(target.URL)
370-
if err == nil {
371-
t.Fatal("expected error, got nil")
372-
}
373-
if !strings.Contains(err.Error(), tt.wantErr) {
374-
t.Errorf("error should contain %q, got: %v", tt.wantErr, err)
375-
}
376-
})
377-
378-
t.Run("https proxy/"+tt.name, func(t *testing.T) {
379-
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
380-
http.Error(w, tt.body, tt.statusCode)
381-
}))
382-
srv.StartTLS()
383-
t.Cleanup(srv.Close)
384-
385-
proxyURL, _ := url.Parse(srv.URL)
386-
transport := withProxyTransport(newTestTransport(), proxyURL, "")
387-
t.Cleanup(transport.CloseIdleConnections)
388-
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
389-
390-
_, err := client.Get(target.URL)
391-
if err == nil {
392-
t.Fatal("expected error, got nil")
393-
}
394-
if !strings.Contains(err.Error(), tt.wantErr) {
395-
t.Errorf("error should contain %q, got: %v", tt.wantErr, err)
396-
}
397-
})
284+
mu.Lock()
285+
defer mu.Unlock()
286+
if !used {
287+
t.Fatal("proxy handler was never invoked")
398288
}
399289
}

0 commit comments

Comments
 (0)