Skip to content

Commit 8734f52

Browse files
committed
add test to confirm that closing the connection on handshake error (tlsConn.Close()) is necessary
1 parent be5c448 commit 8734f52

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

internal/api/proxy_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,53 @@ func TestWithProxyTransport_HTTPSProxy_HTTP2ToOrigin(t *testing.T) {
291291
}
292292
}
293293

294+
func TestWithProxyTransport_HandshakeFailureClosesConn(t *testing.T) {
295+
// Verify that when the TLS handshake to the origin fails, the underlying
296+
// tunnel connection is closed (regression test for tlsConn.Close on error).
297+
//
298+
// A plain TCP listener acts as the target. The proxy CONNECT succeeds
299+
// (TCP-level), but the subsequent TLS handshake fails because the target
300+
// is not a TLS server. If handshakeTLS properly closes tlsConn on failure,
301+
// the tunnel tears down and the target sees the connection close.
302+
connClosed := make(chan struct{})
303+
ln, err := net.Listen("tcp", "127.0.0.1:0")
304+
if err != nil {
305+
t.Fatalf("listen: %v", err)
306+
}
307+
defer ln.Close()
308+
309+
go func() {
310+
conn, err := ln.Accept()
311+
if err != nil {
312+
return
313+
}
314+
defer conn.Close()
315+
// Send non-TLS bytes so the client handshake fails immediately
316+
// rather than waiting for a timeout.
317+
conn.Write([]byte("not-tls\n"))
318+
// Drain until the remote side closes the tunnel.
319+
io.Copy(io.Discard, conn)
320+
close(connClosed)
321+
}()
322+
323+
proxyURL, _ := startProxy(t, true)
324+
transport := withProxyTransport(newTestTransport(), proxyURL, "")
325+
t.Cleanup(transport.CloseIdleConnections)
326+
client := &http.Client{Transport: transport, Timeout: 5 * time.Second}
327+
328+
_, err = client.Get("https://" + ln.Addr().String())
329+
if err == nil {
330+
t.Fatal("expected TLS handshake error, got nil")
331+
}
332+
333+
select {
334+
case <-connClosed:
335+
// Connection was properly cleaned up.
336+
case <-time.After(5 * time.Second):
337+
t.Fatal("connection was not closed after TLS handshake failure")
338+
}
339+
}
340+
294341
func TestWithProxyTransport_ProxyRejectsConnect(t *testing.T) {
295342
tests := []struct {
296343
name string

0 commit comments

Comments
 (0)