// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package websocketimport ()// ErrBadHandshake is returned when the server response to opening handshake is// invalid.varErrBadHandshake = errors.New("websocket: bad handshake")var errInvalidCompression = errors.New("websocket: invalid compression negotiation")// NewClient creates a new client connection using the given net connection.// The URL u specifies the host and request URI. Use requestHeader to specify// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies// (Cookie). Use the response.Header to get the selected subprotocol// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).//// If the WebSocket handshake fails, ErrBadHandshake is returned along with a// non-nil *http.Response so that callers can handle redirects, authentication,// etc.//// Deprecated: Use Dialer instead.func ( net.Conn, *url.URL, http.Header, , int) ( *Conn, *http.Response, error) { := Dialer{ReadBufferSize: ,WriteBufferSize: ,NetDial: func(, string) (net.Conn, error) {return , nil }, }return .Dial(.String(), )}// A Dialer contains options for connecting to WebSocket server.//// It is safe to call Dialer's methods concurrently.typeDialerstruct {// 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, 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}// Dial creates a new client connection by calling DialContext with a background context.func ( *Dialer) ( string, http.Header) (*Conn, *http.Response, error) {return .DialContext(context.Background(), , )}var errMalformedURL = errors.New("malformed ws or wss URL")func hostPortNoPort( *url.URL) (, string) { = .Host = .Hostif := strings.LastIndex(.Host, ":"); > strings.LastIndex(.Host, "]") { = [:] } else {switch .Scheme {case"wss": += ":443"case"https": += ":443"default: += ":80" } }return , }// DefaultDialer is a dialer with all fields set to the default values.varDefaultDialer = &Dialer{Proxy: http.ProxyFromEnvironment,HandshakeTimeout: 45 * time.Second,}// nilDialer is dialer to use when receiver is nil.var nilDialer = *DefaultDialer// DialContext creates a new client connection. Use requestHeader to specify the// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).// Use the 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,// etcetera. The response body may not contain the entire response and does not// need to be closed by the application.func ( *Dialer) ( context.Context, string, http.Header) (*Conn, *http.Response, error) {if == nil { = &nilDialer } , := generateChallengeKey()if != nil {returnnil, nil, } , := url.Parse()if != nil {returnnil, nil, }switch .Scheme {case"ws": .Scheme = "http"case"wss": .Scheme = "https"default:returnnil, nil, errMalformedURL }if .User != nil {// User name and password are not allowed in websocket URIs.returnnil, nil, errMalformedURL } := &http.Request{Method: http.MethodGet,URL: ,Proto: "HTTP/1.1",ProtoMajor: 1,ProtoMinor: 1,Header: make(http.Header),Host: .Host, } = .WithContext()// Set the cookies present in the cookie jar of the dialerif .Jar != nil {for , := range .Jar.Cookies() { .AddCookie() } }// Set the request headers using the capitalization for names and values in // RFC examples. Although the capitalization shouldn't matter, there are // servers that depend on it. The Header.Set method is not used because the // method canonicalizes the header names. .Header["Upgrade"] = []string{"websocket"} .Header["Connection"] = []string{"Upgrade"} .Header["Sec-WebSocket-Key"] = []string{} .Header["Sec-WebSocket-Version"] = []string{"13"}iflen(.Subprotocols) > 0 { .Header["Sec-WebSocket-Protocol"] = []string{strings.Join(.Subprotocols, ", ")} }for , := range {switch {case == "Host":iflen() > 0 { .Host = [0] }case == "Upgrade" || == "Connection" || == "Sec-Websocket-Key" || == "Sec-Websocket-Version" || == "Sec-Websocket-Extensions" || ( == "Sec-Websocket-Protocol" && len(.Subprotocols) > 0):returnnil, nil, errors.New("websocket: duplicate header not allowed: " + )case == "Sec-Websocket-Protocol": .Header["Sec-WebSocket-Protocol"] = default: .Header[] = } }if .EnableCompression { .Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} }if .HandshakeTimeout != 0 {varfunc() , = context.WithTimeout(, .HandshakeTimeout)defer () }// Get network dial function.varfunc(, string) (net.Conn, error)switch .Scheme {case"http":if .NetDialContext != nil { = func(, string) (net.Conn, error) {return .NetDialContext(, , ) } } elseif .NetDial != nil { = .NetDial }case"https":if .NetDialTLSContext != nil { = func(, string) (net.Conn, error) {return .NetDialTLSContext(, , ) } } elseif .NetDialContext != nil { = func(, string) (net.Conn, error) {return .NetDialContext(, , ) } } elseif .NetDial != nil { = .NetDial }default:returnnil, nil, errMalformedURL }if == nil { := &net.Dialer{} = func(, string) (net.Conn, error) {return .DialContext(, , ) } }// If needed, wrap the dial function to set the connection deadline.if , := .Deadline(); { := = func(, string) (net.Conn, error) { , := (, )if != nil {returnnil, } = .SetDeadline()if != nil { .Close()returnnil, }return , nil } }// If needed, wrap the dial function to connect through a proxy.if .Proxy != nil { , := .Proxy()if != nil {returnnil, nil, }if != nil { , := proxy_FromURL(, netDialerFunc())if != nil {returnnil, nil, } = .Dial } } , := hostPortNoPort() := httptrace.ContextClientTrace()if != nil && .GetConn != nil { .GetConn() } , := ("tcp", )if != nil {returnnil, nil, }if != nil && .GotConn != nil { .GotConn(httptrace.GotConnInfo{Conn: , }) }deferfunc() {if != nil { .Close() } }()if .Scheme == "https" && .NetDialTLSContext == nil {// If NetDialTLSContext is set, assume that the TLS handshake has already been done := cloneTLSConfig(.TLSClientConfig)if .ServerName == "" { .ServerName = } := tls.Client(, ) = if != nil && .TLSHandshakeStart != nil { .TLSHandshakeStart() } := doHandshake(, , )if != nil && .TLSHandshakeDone != nil { .TLSHandshakeDone(.ConnectionState(), ) }if != nil {returnnil, nil, } } := newConn(, false, .ReadBufferSize, .WriteBufferSize, .WriteBufferPool, nil, nil)if := .Write(); != nil {returnnil, nil, }if != nil && .GotFirstResponseByte != nil {if , := .br.Peek(1); == nil && len() == 1 { .GotFirstResponseByte() } } , := http.ReadResponse(.br, )if != nil {if .TLSClientConfig != nil {for , := range .TLSClientConfig.NextProtos {if != "http/1.1" {returnnil, nil, fmt.Errorf("websocket: protocol %q was given but is not supported;"+"sharing tls.Config with net/http Transport can cause this error: %w", , , ) } } }returnnil, nil, }if .Jar != nil {if := .Cookies(); len() > 0 { .Jar.SetCookies(, ) } }if .StatusCode != 101 || !tokenListContainsValue(.Header, "Upgrade", "websocket") || !tokenListContainsValue(.Header, "Connection", "upgrade") || .Header.Get("Sec-Websocket-Accept") != computeAcceptKey() {// Before closing the network connection on return from this // function, slurp up some of the response to aid application // debugging. := make([]byte, 1024) , := io.ReadFull(.Body, ) .Body = ioutil.NopCloser(bytes.NewReader([:]))returnnil, , ErrBadHandshake }for , := rangeparseExtensions(.Header) {if [""] != "permessage-deflate" {continue } , := ["server_no_context_takeover"] , := ["client_no_context_takeover"]if ! || ! {returnnil, , errInvalidCompression } .newCompressionWriter = compressNoContextTakeover .newDecompressionReader = decompressNoContextTakeoverbreak } .Body = ioutil.NopCloser(bytes.NewReader([]byte{})) .subprotocol = .Header.Get("Sec-Websocket-Protocol") .SetDeadline(time.Time{}) = nil// to avoid close in defer.return , , nil}func cloneTLSConfig( *tls.Config) *tls.Config {if == nil {return &tls.Config{} }return .Clone()}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.