From bb3c912806f4f85d3bb8e360705424cd6f3da01d Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 21:10:05 +0900 Subject: [PATCH 1/9] move proxyFromURL to client --- client.go | 18 +++++++++++++++++- proxy.go | 18 ------------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/client.go b/client.go index 24bd7ff2..8666562c 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,8 @@ import ( "net/url" "strings" "time" + + "golang.org/x/net/proxy" ) // ErrBadHandshake is returned when the server response to opening handshake is @@ -282,7 +284,21 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h return nil, nil, err } if proxyURL != nil { - netDial, err = proxyFromURL(proxyURL, netDial) + netDial, err = func(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) { + if proxyURL.Scheme == "http" { + return (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil + } + dialer, err := proxy.FromURL(proxyURL, forwardDial) + if err != nil { + return nil, err + } + if d, ok := dialer.(proxy.ContextDialer); ok { + return d.DialContext, nil + } + return func(ctx context.Context, net, addr string) (net.Conn, error) { + return dialer.Dial(net, addr) + }, nil + }(proxyURL, netDial) if err != nil { return nil, nil, err } diff --git a/proxy.go b/proxy.go index b4683b9f..8c8a27e9 100644 --- a/proxy.go +++ b/proxy.go @@ -14,8 +14,6 @@ import ( "net/http" "net/url" "strings" - - "golang.org/x/net/proxy" ) type netDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error) @@ -28,22 +26,6 @@ func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) ( return fn(ctx, network, addr) } -func proxyFromURL(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) { - if proxyURL.Scheme == "http" { - return (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil - } - dialer, err := proxy.FromURL(proxyURL, forwardDial) - if err != nil { - return nil, err - } - if d, ok := dialer.(proxy.ContextDialer); ok { - return d.DialContext, nil - } - return func(ctx context.Context, net, addr string) (net.Conn, error) { - return dialer.Dial(net, addr) - }, nil -} - type httpProxyDialer struct { proxyURL *url.URL forwardDial netDialerFunc From 29bba1ad6ceb7e6a2d86b10e694eb0cb2f846415 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 21:34:27 +0900 Subject: [PATCH 2/9] move connection deadline set below --- client.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/client.go b/client.go index 8666562c..69347e88 100644 --- a/client.go +++ b/client.go @@ -260,23 +260,6 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h netDial = (&net.Dialer{}).DialContext } - // If needed, wrap the dial function to set the connection deadline. - if deadline, ok := ctx.Deadline(); ok { - forwardDial := netDial - netDial = func(ctx context.Context, network, addr string) (net.Conn, error) { - c, err := forwardDial(ctx, network, addr) - if err != nil { - return nil, err - } - err = c.SetDeadline(deadline) - if err != nil { - c.Close() - return nil, err - } - return c, nil - } - } - // If needed, wrap the dial function to connect through a proxy. if d.Proxy != nil { proxyURL, err := d.Proxy(req) @@ -305,6 +288,23 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h } } + // If needed, wrap the dial function to set the connection deadline. + if deadline, ok := ctx.Deadline(); ok { + forwardDial := netDial + netDial = func(ctx context.Context, network, addr string) (net.Conn, error) { + c, err := forwardDial(ctx, network, addr) + if err != nil { + return nil, err + } + err = c.SetDeadline(deadline) + if err != nil { + c.Close() + return nil, err + } + return c, nil + } + } + hostPort, hostNoPort := hostPortNoPort(u) trace := httptrace.ContextClientTrace(ctx) if trace != nil && trace.GetConn != nil { From dc42337df90160188081e7ed45a541c919ec2e34 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 21:43:52 +0900 Subject: [PATCH 3/9] move to newNetDialerFunc --- client.go | 14 +------------- proxy.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/client.go b/client.go index 69347e88..130e7794 100644 --- a/client.go +++ b/client.go @@ -246,19 +246,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h defer cancel() } - var netDial netDialerFunc - switch { - case u.Scheme == "https" && d.NetDialTLSContext != nil: - netDial = d.NetDialTLSContext - case d.NetDialContext != nil: - netDial = d.NetDialContext - case d.NetDial != nil: - netDial = func(ctx context.Context, net, addr string) (net.Conn, error) { - return d.NetDial(net, addr) - } - default: - netDial = (&net.Dialer{}).DialContext - } + netDial := newNetDialerFunc(u.Scheme, d.NetDial, d.NetDialContext, d.NetDialTLSContext) // If needed, wrap the dial function to connect through a proxy. if d.Proxy != nil { diff --git a/proxy.go b/proxy.go index 8c8a27e9..488d3d42 100644 --- a/proxy.go +++ b/proxy.go @@ -16,6 +16,26 @@ import ( "strings" ) +func newNetDialerFunc( + scheme string, + netDial func(network, addr string) (net.Conn, error), + netDialContext func(ctx context.Context, network, addr string) (net.Conn, error), + netDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error), +) netDialerFunc { + switch { + case scheme == "https" && netDialTLSContext != nil: + return netDialTLSContext + case netDialContext != nil: + return netDialContext + case netDial != nil: + return func(ctx context.Context, net, addr string) (net.Conn, error) { + return netDial(net, addr) + } + default: + return (&net.Dialer{}).DialContext + } +} + type netDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error) func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { From 75fbe70bee581318ee6b2660055ee586f5d1817d Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 21:44:12 +0900 Subject: [PATCH 4/9] move httpProxyDialer to newHTTPProxyDialerFunc() --- client.go | 22 ++++++------- proxy.go | 97 +++++++++++++++++++++++++++---------------------------- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/client.go b/client.go index 130e7794..0df5c311 100644 --- a/client.go +++ b/client.go @@ -255,23 +255,21 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h return nil, nil, err } if proxyURL != nil { - netDial, err = func(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) { - if proxyURL.Scheme == "http" { - return (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil - } + forwardDial := newNetDialerFunc(proxyURL.Scheme, d.NetDial, d.NetDialContext, d.NetDialTLSContext) + if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { + netDial = newHTTPProxyDialerFunc(proxyURL, forwardDial) + } else { dialer, err := proxy.FromURL(proxyURL, forwardDial) if err != nil { - return nil, err + return nil, nil, err } if d, ok := dialer.(proxy.ContextDialer); ok { - return d.DialContext, nil + netDial = d.DialContext + } else { + netDial = func(ctx context.Context, net, addr string) (net.Conn, error) { + return dialer.Dial(net, addr) + } } - return func(ctx context.Context, net, addr string) (net.Conn, error) { - return dialer.Dial(net, addr) - }, nil - }(proxyURL, netDial) - if err != nil { - return nil, nil, err } } } diff --git a/proxy.go b/proxy.go index 488d3d42..8638ab08 100644 --- a/proxy.go +++ b/proxy.go @@ -46,62 +46,59 @@ func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) ( return fn(ctx, network, addr) } -type httpProxyDialer struct { - proxyURL *url.URL - forwardDial netDialerFunc -} - -func (hpd *httpProxyDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) { - hostPort, _ := hostPortNoPort(hpd.proxyURL) - conn, err := hpd.forwardDial(ctx, network, hostPort) - if err != nil { - return nil, err - } +func newHTTPProxyDialerFunc(proxyURL *url.URL, forwardDial netDialerFunc) netDialerFunc { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + hostPort, _ := hostPortNoPort(proxyURL) + conn, err := forwardDial(ctx, network, hostPort) + if err != nil { + return nil, err + } - connectHeader := make(http.Header) - if user := hpd.proxyURL.User; user != nil { - proxyUser := user.Username() - if proxyPassword, passwordSet := user.Password(); passwordSet { - credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) - connectHeader.Set("Proxy-Authorization", "Basic "+credential) + connectHeader := make(http.Header) + if user := proxyURL.User; user != nil { + proxyUser := user.Username() + if proxyPassword, passwordSet := user.Password(); passwordSet { + credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) + connectHeader.Set("Proxy-Authorization", "Basic "+credential) + } } - } - connectReq := &http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Opaque: addr}, - Host: addr, - Header: connectHeader, - } + connectReq := &http.Request{ + Method: http.MethodConnect, + URL: &url.URL{Opaque: addr}, + Host: addr, + Header: connectHeader, + } - if err := connectReq.Write(conn); err != nil { - conn.Close() - return nil, err - } + if err := connectReq.Write(conn); err != nil { + conn.Close() + return nil, err + } - // Read response. It's OK to use and discard buffered reader here because - // the remote server does not speak until spoken to. - br := bufio.NewReader(conn) - resp, err := http.ReadResponse(br, connectReq) - if err != nil { - conn.Close() - return nil, err - } + // Read response. It's OK to use and discard buffered reader here because + // the remote server does not speak until spoken to. + br := bufio.NewReader(conn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + conn.Close() + return nil, err + } - // Close the response body to silence false positives from linters. Reset - // the buffered reader first to ensure that Close() does not read from - // conn. - // Note: Applications must call resp.Body.Close() on a response returned - // http.ReadResponse to inspect trailers or read another response from the - // buffered reader. The call to resp.Body.Close() does not release - // resources. - br.Reset(bytes.NewReader(nil)) - _ = resp.Body.Close() + // Close the response body to silence false positives from linters. Reset + // the buffered reader first to ensure that Close() does not read from + // conn. + // Note: Applications must call resp.Body.Close() on a response returned + // http.ReadResponse to inspect trailers or read another response from the + // buffered reader. The call to resp.Body.Close() does not release + // resources. + br.Reset(bytes.NewReader(nil)) + _ = resp.Body.Close() - if resp.StatusCode != http.StatusOK { - _ = conn.Close() - f := strings.SplitN(resp.Status, " ", 2) - return nil, errors.New(f[1]) + if resp.StatusCode != http.StatusOK { + _ = conn.Close() + f := strings.SplitN(resp.Status, " ", 2) + return nil, errors.New(f[1]) + } + return conn, nil } - return conn, nil } From bad5b0af7f13b787a7ca888eedf46e1964646edb Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 23:02:07 +0900 Subject: [PATCH 5/9] fix tls handshake on proxy --- client.go | 12 ++++++++++-- proxy.go | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index 0df5c311..0dc51196 100644 --- a/client.go +++ b/client.go @@ -256,8 +256,16 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h } if proxyURL != nil { forwardDial := newNetDialerFunc(proxyURL.Scheme, d.NetDial, d.NetDialContext, d.NetDialTLSContext) - if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { - netDial = newHTTPProxyDialerFunc(proxyURL, forwardDial) + if proxyURL.Scheme == "https" && d.NetDialTLSContext == nil { + tlsClientConfig := cloneTLSConfig(d.TLSClientConfig) + if d.TLSClientConfig == nil { + tlsClientConfig = &tls.Config{ + ServerName: proxyURL.Hostname(), + } + } + netDial = newHTTPProxyDialerFunc(proxyURL, forwardDial, tlsClientConfig) + } else if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { + netDial = newHTTPProxyDialerFunc(proxyURL, forwardDial, nil) } else { dialer, err := proxy.FromURL(proxyURL, forwardDial) if err != nil { diff --git a/proxy.go b/proxy.go index 8638ab08..c3c0e45f 100644 --- a/proxy.go +++ b/proxy.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "context" + "crypto/tls" "encoding/base64" "errors" "net" @@ -46,7 +47,11 @@ func (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) ( return fn(ctx, network, addr) } -func newHTTPProxyDialerFunc(proxyURL *url.URL, forwardDial netDialerFunc) netDialerFunc { +// newHTTPProxyDialerFunc returns a netDialerFunc that dials using the provided +// proxyURL. The forwardDial function is used to establish the connection to the +// proxy server. If tlsClientConfig is not nil, the connection to the proxy is +// upgraded to a TLS connection with tls.Client. +func newHTTPProxyDialerFunc(proxyURL *url.URL, forwardDial netDialerFunc, tlsClientConfig *tls.Config) netDialerFunc { return func(ctx context.Context, network, addr string) (net.Conn, error) { hostPort, _ := hostPortNoPort(proxyURL) conn, err := forwardDial(ctx, network, hostPort) @@ -54,6 +59,14 @@ func newHTTPProxyDialerFunc(proxyURL *url.URL, forwardDial netDialerFunc) netDia return nil, err } + if tlsClientConfig != nil { + tlsConn := tls.Client(conn, tlsClientConfig) + if err = tlsConn.HandshakeContext(ctx); err != nil { + return nil, err + } + conn = tlsConn + } + connectHeader := make(http.Header) if user := proxyURL.User; user != nil { proxyUser := user.Username() From 5e557d257ee50dce975600b89d927ba049df3c84 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Thu, 18 Jul 2024 23:43:04 +0900 Subject: [PATCH 6/9] add tests --- client.go | 9 ++- client_server_test.go | 146 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 0dc51196..043e4568 100644 --- a/client.go +++ b/client.go @@ -258,10 +258,9 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h forwardDial := newNetDialerFunc(proxyURL.Scheme, d.NetDial, d.NetDialContext, d.NetDialTLSContext) if proxyURL.Scheme == "https" && d.NetDialTLSContext == nil { tlsClientConfig := cloneTLSConfig(d.TLSClientConfig) - if d.TLSClientConfig == nil { - tlsClientConfig = &tls.Config{ - ServerName: proxyURL.Hostname(), - } + if tlsClientConfig.ServerName == "" { + _, hostNoPort := hostPortNoPort(proxyURL) + tlsClientConfig.ServerName = hostNoPort } netDial = newHTTPProxyDialerFunc(proxyURL, forwardDial, tlsClientConfig) } else if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" { @@ -369,7 +368,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h if proto != "http/1.1" { return nil, nil, fmt.Errorf( "websocket: protocol %q was given but is not supported;"+ - "sharing tls.Config with net/http Transport can cause this error: %w", + "sharing tlsServerName.Config with net/http Transport can cause this error: %w", proto, err, ) } diff --git a/client_server_test.go b/client_server_test.go index e4546aea..5a2113f8 100644 --- a/client_server_test.go +++ b/client_server_test.go @@ -85,6 +85,51 @@ func newTLSServer(t *testing.T) *cstServer { return &s } +type cstProxyServer struct{} + +func (s *cstProxyServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodConnect { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + + conn, _, err := w.(http.Hijacker).Hijack() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer conn.Close() + + upstream, err := (&net.Dialer{}).DialContext(req.Context(), "tcp", req.URL.Host) + if err != nil { + _, _ = fmt.Fprintf(conn, "HTTP/1.1 502 Bad Gateway\r\n\r\n") + return + } + defer upstream.Close() + + _, _ = fmt.Fprintf(conn, "HTTP/1.1 200 Connection established\r\n\r\n") + + wg := sync.WaitGroup{} + wg.Add(2) + go func() { + defer wg.Done() + _, _ = io.Copy(upstream, conn) + }() + go func() { + defer wg.Done() + _, _ = io.Copy(conn, upstream) + }() + wg.Wait() +} + +func newProxyServer() *httptest.Server { + return httptest.NewServer(&cstProxyServer{}) +} + +func newTLSProxyServer() *httptest.Server { + return httptest.NewTLSServer(&cstProxyServer{}) +} + func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Because tests wait for a response from a server, we are guaranteed that // the wait group count is incremented before the test waits on the group @@ -165,7 +210,6 @@ func sendRecv(t *testing.T, ws *Conn) { } func TestProxyDial(t *testing.T) { - s := newServer(t) defer s.Close() @@ -202,6 +246,106 @@ func TestProxyDial(t *testing.T) { sendRecv(t, ws) } +func TestProxyDialer(t *testing.T) { + testcases := []struct { + name string + isTLS bool + tlsServerName string + insecureSkipVerify bool + netDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error) + }{{ + name: "http", + isTLS: false, + }, { + name: "https", + isTLS: true, + }, { + name: "https with ServerName", + isTLS: true, + tlsServerName: "example.com", + }, { + name: "https with insecureSkipVerify", + isTLS: true, + insecureSkipVerify: true, + }, { + name: "https with netDialTLSContext", + isTLS: true, + netDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + dialer := &tls.Dialer{ + Config: &tls.Config{ + InsecureSkipVerify: true, + }, + } + return dialer.DialContext(ctx, network, addr) + }, + }} + + for _, tc := range testcases { + t.Run(tc.name, func(tt *testing.T) { + s := newServer(tt) + defer s.Close() + + var ps *httptest.Server + if tc.isTLS { + ps = newTLSProxyServer() + } else { + ps = newProxyServer() + } + + psurl, _ := url.Parse(ps.URL) + + netDialCalled := false + + cstDialer := cstDialer // make local copy for modification on next line. + cstDialer.Proxy = http.ProxyURL(psurl) + if tc.isTLS { + cstDialer.TLSClientConfig = &tls.Config{ + RootCAs: rootCAs(tt, ps), + ServerName: tc.tlsServerName, + InsecureSkipVerify: tc.insecureSkipVerify, + } + if tc.netDialTLSContext != nil { + cstDialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + netDialCalled = true + return tc.netDialTLSContext(ctx, network, addr) + } + } else { + netDialCalled = true + } + } else { + netDialCalled = true + } + + connect := false + origHandler := ps.Config.Handler + + // Capture the request Host header. + ps.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodConnect { + connect = true + } + + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + tt.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(tt, ws) + + if !connect { + tt.Error("connect not received") + } + if !netDialCalled { + tt.Error("netDialTLSContext not called") + } + }) + } +} + func TestProxyAuthorizationDial(t *testing.T) { s := newServer(t) defer s.Close() From d249ef1a3bc17069c04a649ec1f6d38ed3343de7 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Fri, 19 Jul 2024 00:32:35 +0900 Subject: [PATCH 7/9] use done chan for waiting pipe --- client_server_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client_server_test.go b/client_server_test.go index 5a2113f8..2dbd7e66 100644 --- a/client_server_test.go +++ b/client_server_test.go @@ -109,17 +109,16 @@ func (s *cstProxyServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { _, _ = fmt.Fprintf(conn, "HTTP/1.1 200 Connection established\r\n\r\n") - wg := sync.WaitGroup{} - wg.Add(2) + done := make(chan struct{}, 2) go func() { - defer wg.Done() _, _ = io.Copy(upstream, conn) + done <- struct{}{} }() go func() { - defer wg.Done() _, _ = io.Copy(conn, upstream) + done <- struct{}{} }() - wg.Wait() + <-done } func newProxyServer() *httptest.Server { From 23a5606d215bb63d4d2700a0d77d905dfff49cf4 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Fri, 19 Jul 2024 00:43:19 +0900 Subject: [PATCH 8/9] drop unexpected change --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 043e4568..9f50882d 100644 --- a/client.go +++ b/client.go @@ -368,7 +368,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h if proto != "http/1.1" { return nil, nil, fmt.Errorf( "websocket: protocol %q was given but is not supported;"+ - "sharing tlsServerName.Config with net/http Transport can cause this error: %w", + "sharing tls.Config with net/http Transport can cause this error: %w", proto, err, ) } From 06fe425b5ad922e18662e580c2dd9df025158769 Mon Sep 17 00:00:00 2001 From: Cooper Oh Date: Fri, 19 Jul 2024 00:43:19 +0900 Subject: [PATCH 9/9] Remove duplicated test --- client_server_test.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/client_server_test.go b/client_server_test.go index 2dbd7e66..149db2bf 100644 --- a/client_server_test.go +++ b/client_server_test.go @@ -209,43 +209,6 @@ func sendRecv(t *testing.T, ws *Conn) { } func TestProxyDial(t *testing.T) { - s := newServer(t) - defer s.Close() - - surl, _ := url.Parse(s.Server.URL) - - cstDialer := cstDialer // make local copy for modification on next line. - cstDialer.Proxy = http.ProxyURL(surl) - - connect := false - origHandler := s.Server.Config.Handler - - // Capture the request Host header. - s.Server.Config.Handler = http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodConnect { - connect = true - w.WriteHeader(http.StatusOK) - return - } - - if !connect { - t.Log("connect not received") - http.Error(w, "connect not received", http.StatusMethodNotAllowed) - return - } - origHandler.ServeHTTP(w, r) - }) - - ws, _, err := cstDialer.Dial(s.URL, nil) - if err != nil { - t.Fatalf("Dial: %v", err) - } - defer ws.Close() - sendRecv(t, ws) -} - -func TestProxyDialer(t *testing.T) { testcases := []struct { name string isTLS bool