Client-side WebSocket functionality allows establishing WebSocket connections to servers using the Dialer type. The Dialer provides support for custom headers, proxy configuration, TLS settings, connection timeouts, and subprotocol negotiation.
The Dialer type contains options for connecting to a WebSocket server. It is safe to call Dialer's methods concurrently.
type Dialer struct {
// NetDial specifies the dial function for creating TCP connections. If
// NetDial is nil, net.Dial is used.
NetDial func(network, addr string) (net.Conn, error)
// NetDialContext specifies the dial function for creating TCP connections. If
// NetDialContext is nil, NetDial is used.
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
// NetDialTLSContext is nil, NetDialContext is used.
// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
// TLSClientConfig is ignored.
NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
// Proxy specifies a function to return a proxy for a given
// Request. If the function returns a non-nil error, the
// request is aborted with the provided error.
// If Proxy is nil or returns a nil *URL, no proxy is used.
Proxy func(*http.Request) (*url.URL, error)
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
// If nil, the default configuration is used.
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
// is done there and TLSClientConfig is ignored.
TLSClientConfig *tls.Config
// HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time.Duration
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
// size is zero, then a useful default size is used. The I/O buffer sizes
// do not limit the size of the messages that can be sent or received.
ReadBufferSize int
WriteBufferSize int
// WriteBufferPool is a pool of buffers for write operations. If the value
// is not set, then write buffers are allocated to the connection for the
// lifetime of the connection.
//
// A pool is most useful when the application has a modest volume of writes
// across a large number of connections.
//
// Applications should use a single pool for each unique value of
// WriteBufferSize.
WriteBufferPool BufferPool
// Subprotocols specifies the client's requested subprotocols.
Subprotocols []string
// EnableCompression specifies if the client should attempt to negotiate
// per message compression (RFC 7692). Setting this value to true does not
// guarantee that compression will be supported. Currently only "no context
// takeover" modes are supported.
EnableCompression bool
// Jar specifies the cookie jar.
// If Jar is nil, cookies are not sent in requests and ignored
// in responses.
Jar http.CookieJar
}A pre-configured dialer with default values.
var DefaultDialer = &Dialer{
Proxy: http.ProxyFromEnvironment,
HandshakeTimeout: 45 * time.Second,
}Creates a new client connection by calling DialContext with a background context.
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error)Creates a new client connection with context support.
func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error)Use requestHeader to specify the origin (Origin), subprotocols (Sec-WebSocket-Protocol), and cookies (Cookie). Use response.Header to get the selected subprotocol (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
The context will be used in the request and in the Dialer.
If the WebSocket handshake fails, ErrBadHandshake is returned along with a non-nil *http.Response so that callers can handle redirects, authentication, etc. The response body may not contain the entire response and does not need to be closed by the application.
package main
import (
"log"
"net/url"
"github.com/gorilla/websocket"
)
func main() {
u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/ws"}
conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("Dial error:", err)
}
defer conn.Close()
// Send a message
err = conn.WriteMessage(websocket.TextMessage, []byte("Hello"))
if err != nil {
log.Fatal("Write error:", err)
}
// Read response
_, message, err := conn.ReadMessage()
if err != nil {
log.Fatal("Read error:", err)
}
log.Printf("Received: %s", message)
}import (
"net/http"
"net/url"
"github.com/gorilla/websocket"
)
func connectWithHeaders() (*websocket.Conn, error) {
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
// Set custom headers
headers := http.Header{}
headers.Set("Origin", "https://example.com")
headers.Set("Authorization", "Bearer token123")
conn, resp, err := websocket.DefaultDialer.Dial(u.String(), headers)
if err != nil {
log.Printf("Dial failed: %v, Response: %v", err, resp)
return nil, err
}
return conn, nil
}import (
"context"
"time"
"net/url"
"github.com/gorilla/websocket"
)
func connectWithTimeout() (*websocket.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := websocket.DefaultDialer.DialContext(ctx, u.String(), nil)
if err != nil {
return nil, err
}
return conn, nil
}import (
"crypto/tls"
"net/url"
"github.com/gorilla/websocket"
)
func connectTLS() (*websocket.Conn, error) {
u := url.URL{Scheme: "wss", Host: "secure.example.com", Path: "/ws"}
dialer := websocket.Dialer{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
ServerName: "secure.example.com",
},
HandshakeTimeout: 45 * time.Second,
}
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
return conn, nil
}import (
"net/http"
"net/url"
"github.com/gorilla/websocket"
)
func connectViaProxy() (*websocket.Conn, error) {
proxyURL, _ := url.Parse("http://proxy.example.com:8080")
dialer := websocket.Dialer{
Proxy: http.ProxyURL(proxyURL),
HandshakeTimeout: 45 * time.Second,
}
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
return conn, nil
}func connectWithSubprotocol() (*websocket.Conn, error) {
dialer := websocket.Dialer{
Subprotocols: []string{"v1.chat.example.com", "v2.chat.example.com"},
}
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
// Check which subprotocol was negotiated
protocol := conn.Subprotocol()
log.Printf("Using subprotocol: %s", protocol)
return conn, nil
}func connectWithCompression() (*websocket.Conn, error) {
dialer := websocket.Dialer{
EnableCompression: true,
}
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
// Messages will be automatically decompressed on read
// Use conn.EnableWriteCompression(true) to compress writes
return conn, nil
}import (
"net/http"
"net/http/cookiejar"
"net/url"
"github.com/gorilla/websocket"
)
func connectWithCookies() (*websocket.Conn, error) {
jar, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
dialer := websocket.Dialer{
Jar: jar,
}
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, resp, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
// Cookies from resp are automatically stored in jar
cookies := jar.Cookies(u.URL())
log.Printf("Received cookies: %v", cookies)
return conn, nil
}import (
"net"
"context"
"time"
"github.com/gorilla/websocket"
)
func connectWithCustomDial() (*websocket.Conn, error) {
dialer := websocket.Dialer{
NetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
// Custom dialing logic (e.g., binding to specific interface)
d := &net.Dialer{
Timeout: 10 * time.Second,
LocalAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100")},
}
return d.DialContext(ctx, network, addr)
},
}
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := dialer.Dial(u.String(), nil)
if err != nil {
return nil, err
}
return conn, nil
}func connectWithErrorHandling() (*websocket.Conn, error) {
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, resp, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
if err == websocket.ErrBadHandshake {
log.Printf("Handshake failed with status %d", resp.StatusCode)
// Handle redirects
if resp.StatusCode == http.StatusMovedPermanently ||
resp.StatusCode == http.StatusFound {
location := resp.Header.Get("Location")
log.Printf("Redirect to: %s", location)
// Follow redirect manually if needed
}
// Handle authentication
if resp.StatusCode == http.StatusUnauthorized {
log.Println("Authentication required")
// Add authentication and retry
}
}
return nil, err
}
return conn, nil
}func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error)Deprecated: Use Dialer instead.
Creates a new client connection using the given net connection. The URL u specifies the host and request URI.
var ErrBadHandshake = errors.New("websocket: bad handshake")Returned when the server response to the opening handshake is invalid. When this error is returned, a non-nil *http.Response is also returned, allowing callers to handle redirects, authentication, etc.
ws:// - Unencrypted WebSocket connectionwss:// - TLS-encrypted WebSocket connectionExample:
// Unencrypted
u1 := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
// TLS-encrypted
u2 := url.URL{Scheme: "wss", Host: "secure.example.com", Path: "/ws"}Example complete lifecycle:
func clientLifecycle() error {
// 1. Dial
u := url.URL{Scheme: "ws", Host: "example.com", Path: "/ws"}
conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
return err
}
defer conn.Close()
// 2. Communication
err = conn.WriteMessage(websocket.TextMessage, []byte("Hello"))
if err != nil {
return err
}
_, msg, err := conn.ReadMessage()
if err != nil {
return err
}
log.Printf("Received: %s", msg)
// 3. Close (defer handles it, or explicit close message)
err = conn.WriteMessage(websocket.CloseMessage,
websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
return err
}
return nil
}