-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Description
Describe the bug
I can't connect to a websocket server over a HTTP proxy when I set both NetDialTLSContext
and Proxy
fields on the websocket.Dialer
struct. See the code snippet below for more details, it's best explained by code.
Versions
Go version: go1.18.1 linux/amd64
.
Package version: v1.5.0
.
Steps to Reproduce
- Get a HTTP proxy to connect to. You could install a local HTTP debugging proxy like Burp Suite, Charles Proxy, MITMProxy, ... on your development machine for testing.
- Execute the code snippet below and observe that no websocket connection can be established over the proxy you configured.
Expected behavior
The websocket dialer should be able to establish a websocket connection over a HTTP proxy while also applying a custom TLS connection without issues.
Code Snippets
The following code results in an error like websocket: bad handshake
or unexpected EOF
(depends on the proxy):
package main
import (
"context"
"crypto/tls"
"github.com/gorilla/websocket"
"log"
"net"
"net/http"
"net/url"
)
const (
// Local or remote HTTP proxy.
proxyUrl = "http://127.0.0.1:8888"
// Websocket endpoint for testing
websocketUrl = "wss://echo.websocket.events/"
)
func main() {
p, _ := url.Parse(proxyUrl)
dialer := websocket.Dialer{
Proxy: http.ProxyURL(p),
EnableCompression: true,
NetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
// TCP dial
netConn, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
// 'NetDialTLSContext' also gets called during the proxy CONNECT for some reason (at this point 'network' equals "TCP" and 'addr' equals "127.0.0.1:8888")
// The HTTP proxy doesn't support HTTPS however, so I return the established TCP connection early.
// If I don't do this check, the connection hangs forever (tested with several proxies).
// This feels kinda hacky though, not sure if this is the correct approach...
if p.Host == addr {
return netConn, err
}
// Example TLS handshake
tlsConn := tls.Client(netConn, &tls.Config{ServerName: "echo.websocket.events", InsecureSkipVerify: true})
if err = tlsConn.Handshake(); err != nil {
return nil, err
}
return tlsConn, nil
},
}
conn, _, err := dialer.Dial(websocketUrl, nil)
if err != nil {
log.Fatalln(err)
}
log.Println(conn.LocalAddr())
// ...
}
When I check the request in burp, it looks like this:
As highlighted in red, the host URL became http://echo.websocket.events:443/
, which obviously should just be https://echo.websocket.events/
. I've been trying to debug why this is happening exactly all day but can't seem to find the culprit so I need help.
If I remove NetDialTLSContext
and just specify TLSClientConfig
everything works as expected. Same result with the Proxy
field removed. The issue only occurs when both of these fields are set.
Am I missing something obvious?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status