[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\njobs:\n  \"test\":\n    parameters:\n      version:\n        type: string\n        default: \"latest\"\n      golint:\n        type: boolean\n        default: true\n      modules:\n        type: boolean\n        default: true\n      goproxy:\n        type: string\n        default: \"\"\n    docker:\n      - image: \"cimg/go:<< parameters.version >>\"\n    working_directory: /home/circleci/project/go/src/github.com/gorilla/websocket\n    environment:\n      GO111MODULE: \"on\"\n      GOPROXY: \"<< parameters.goproxy >>\"\n    steps:\n      - checkout\n      - run:\n          name: \"Print the Go version\"\n          command: >\n            go version\n      - run:\n          name: \"Fetch dependencies\"\n          command: >\n            if [[ << parameters.modules >> = true ]]; then\n              go mod download\n              export GO111MODULE=on\n            else\n              go get -v ./...\n            fi\n      # Only run gofmt, vet & lint against the latest Go version\n      - run:\n          name: \"Run golint\"\n          command: >\n            if [ << parameters.version >> = \"latest\" ] && [ << parameters.golint >> = true ]; then\n              go get -u golang.org/x/lint/golint\n              golint ./...\n            fi\n      - run:\n          name: \"Run gofmt\"\n          command: >\n            if [[ << parameters.version >> = \"latest\" ]]; then\n              diff -u <(echo -n) <(gofmt -d -e .)\n            fi\n      - run:\n          name: \"Run go vet\"\n          command: >\n            if [[ << parameters.version >> = \"latest\" ]]; then\n              go vet -v ./...\n            fi\n      - run:\n          name: \"Run go test (+ race detector)\"\n          command: >\n            go test -v -race ./...\n\nworkflows:\n  tests:\n    jobs:\n      - test:\n          matrix:\n            parameters:\n              version: [\"1.22\", \"1.21\", \"1.20\"]\n"
  },
  {
    "path": ".github/release-drafter.yml",
    "content": "# Config for https://github.com/apps/release-drafter\ntemplate: |\n  \n  <summary of changes here>\n  \n  ## CHANGELOG\n  $CHANGES\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n\n.idea/\n*.iml\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the official list of Gorilla WebSocket authors for copyright\n# purposes.\n#\n# Please keep the list sorted.\n\nGary Burd <gary@beagledreams.com>\nGoogle LLC (https://opensource.google.com/)\nJoachim Bauch <mail@joachim-bauch.de>\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n  Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n  Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Gorilla WebSocket\n\n[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)\n[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket)\n\nGorilla WebSocket is a [Go](http://golang.org/) implementation of the\n[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.\n\n\n### Documentation\n\n* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)\n* [Chat example](https://github.com/gorilla/websocket/tree/main/examples/chat)\n* [Command example](https://github.com/gorilla/websocket/tree/main/examples/command)\n* [Client and server example](https://github.com/gorilla/websocket/tree/main/examples/echo)\n* [File watch example](https://github.com/gorilla/websocket/tree/main/examples/filewatch)\n\n### Status\n\nThe Gorilla WebSocket package provides a complete and tested implementation of\nthe [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The\npackage API is stable.\n\n### Installation\n\n    go get github.com/gorilla/websocket\n\n### Protocol Compliance\n\nThe Gorilla WebSocket package passes the server tests in the [Autobahn Test\nSuite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn\nsubdirectory](https://github.com/gorilla/websocket/tree/main/examples/autobahn).\n"
  },
  {
    "path": "client.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptrace\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ErrBadHandshake is returned when the server response to opening handshake is\n// invalid.\nvar ErrBadHandshake = errors.New(\"websocket: bad handshake\")\n\nvar errInvalidCompression = errors.New(\"websocket: invalid compression negotiation\")\n\n// NewClient creates a new client connection using the given net connection.\n// The URL u specifies the host and request URI. Use requestHeader to specify\n// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies\n// (Cookie). Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etc.\n//\n// Deprecated: Use Dialer instead.\nfunc NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {\n\td := Dialer{\n\t\tReadBufferSize:  readBufSize,\n\t\tWriteBufferSize: writeBufSize,\n\t\tNetDial: func(net, addr string) (net.Conn, error) {\n\t\t\treturn netConn, nil\n\t\t},\n\t}\n\treturn d.Dial(u.String(), requestHeader)\n}\n\n// A Dialer contains options for connecting to WebSocket server.\n//\n// It is safe to call Dialer's methods concurrently.\ntype Dialer struct {\n\t// The following custom dial functions can be set to establish\n\t// connections to either the backend server or the proxy (if it\n\t// exists). The scheme of the dialed entity (either backend or\n\t// proxy) determines which custom dial function is selected:\n\t// either NetDialTLSContext for HTTPS or NetDialContext/NetDial\n\t// for HTTP. Since the \"Proxy\" function can determine the scheme\n\t// dynamically, it can make sense to set multiple custom dial\n\t// functions simultaneously.\n\t//\n\t// NetDial specifies the dial function for creating TCP connections. If\n\t// NetDial is nil, net.Dialer DialContext is used.\n\t// If \"Proxy\" field is also set, this function dials the proxy--not\n\t// the backend server.\n\tNetDial func(network, addr string) (net.Conn, error)\n\n\t// NetDialContext specifies the dial function for creating TCP connections. If\n\t// NetDialContext is nil, NetDial is used.\n\t// If \"Proxy\" field is also set, this function dials the proxy--not\n\t// the backend server.\n\tNetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)\n\n\t// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If\n\t// NetDialTLSContext is nil, NetDialContext is used.\n\t// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and\n\t// TLSClientConfig is ignored.\n\t// If \"Proxy\" field is also set, this function dials the proxy (and performs\n\t// the TLS handshake with the proxy, ignoring TLSClientConfig). In this TLS proxy\n\t// dialing case the TLSClientConfig could still be necessary for TLS to the backend server.\n\tNetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)\n\n\t// Proxy specifies a function to return a proxy for a given\n\t// Request. If the function returns a non-nil error, the\n\t// request is aborted with the provided error.\n\t// If Proxy is nil or returns a nil *URL, no proxy is used.\n\tProxy func(*http.Request) (*url.URL, error)\n\n\t// TLSClientConfig specifies the TLS configuration to use with tls.Client.\n\t// If nil, the default configuration is used.\n\t// If NetDialTLSContext is set, Dial assumes the TLS handshake\n\t// is done there and TLSClientConfig is ignored.\n\tTLSClientConfig *tls.Config\n\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer\n\t// size is zero, then a useful default size is used. The I/O buffer sizes\n\t// do not limit the size of the messages that can be sent or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the client's requested subprotocols.\n\tSubprotocols []string\n\n\t// EnableCompression specifies if the client should attempt to negotiate\n\t// per message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n\n\t// Jar specifies the cookie jar.\n\t// If Jar is nil, cookies are not sent in requests and ignored\n\t// in responses.\n\tJar http.CookieJar\n}\n\n// Dial creates a new client connection by calling DialContext with a background context.\nfunc (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\treturn d.DialContext(context.Background(), urlStr, requestHeader)\n}\n\nvar errMalformedURL = errors.New(\"malformed ws or wss URL\")\n\nfunc hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {\n\thostPort = u.Host\n\thostNoPort = u.Host\n\tif i := strings.LastIndex(u.Host, \":\"); i > strings.LastIndex(u.Host, \"]\") {\n\t\thostNoPort = hostNoPort[:i]\n\t} else {\n\t\tswitch u.Scheme {\n\t\tcase \"wss\":\n\t\t\thostPort += \":443\"\n\t\tcase \"https\":\n\t\t\thostPort += \":443\"\n\t\tdefault:\n\t\t\thostPort += \":80\"\n\t\t}\n\t}\n\treturn hostPort, hostNoPort\n}\n\n// DefaultDialer is a dialer with all fields set to the default values.\nvar DefaultDialer = &Dialer{\n\tProxy:            http.ProxyFromEnvironment,\n\tHandshakeTimeout: 45 * time.Second,\n}\n\n// nilDialer is dialer to use when receiver is nil.\nvar nilDialer = *DefaultDialer\n\n// DialContext creates a new client connection. Use requestHeader to specify the\n// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).\n// Use the response.Header to get the selected subprotocol\n// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).\n//\n// The context will be used in the request and in the Dialer.\n//\n// If the WebSocket handshake fails, ErrBadHandshake is returned along with a\n// non-nil *http.Response so that callers can handle redirects, authentication,\n// etcetera. The response body may not contain the entire response and does not\n// need to be closed by the application.\nfunc (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {\n\tif d == nil {\n\t\td = &nilDialer\n\t}\n\n\tchallengeKey, err := generateChallengeKey()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tu, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tswitch u.Scheme {\n\tcase \"ws\":\n\t\tu.Scheme = \"http\"\n\tcase \"wss\":\n\t\tu.Scheme = \"https\"\n\tdefault:\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\tif u.User != nil {\n\t\t// User name and password are not allowed in websocket URIs.\n\t\treturn nil, nil, errMalformedURL\n\t}\n\n\treq := &http.Request{\n\t\tMethod:     http.MethodGet,\n\t\tURL:        u,\n\t\tProto:      \"HTTP/1.1\",\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t\tHost:       u.Host,\n\t}\n\treq = req.WithContext(ctx)\n\n\t// Set the cookies present in the cookie jar of the dialer\n\tif d.Jar != nil {\n\t\tfor _, cookie := range d.Jar.Cookies(u) {\n\t\t\treq.AddCookie(cookie)\n\t\t}\n\t}\n\n\t// Set the request headers using the capitalization for names and values in\n\t// RFC examples. Although the capitalization shouldn't matter, there are\n\t// servers that depend on it. The Header.Set method is not used because the\n\t// method canonicalizes the header names.\n\treq.Header[\"Upgrade\"] = []string{\"websocket\"}\n\treq.Header[\"Connection\"] = []string{\"Upgrade\"}\n\treq.Header[\"Sec-WebSocket-Key\"] = []string{challengeKey}\n\treq.Header[\"Sec-WebSocket-Version\"] = []string{\"13\"}\n\tif len(d.Subprotocols) > 0 {\n\t\treq.Header[\"Sec-WebSocket-Protocol\"] = []string{strings.Join(d.Subprotocols, \", \")}\n\t}\n\tfor k, vs := range requestHeader {\n\t\tswitch {\n\t\tcase k == \"Host\":\n\t\t\tif len(vs) > 0 {\n\t\t\t\treq.Host = vs[0]\n\t\t\t}\n\t\tcase k == \"Upgrade\" ||\n\t\t\tk == \"Connection\" ||\n\t\t\tk == \"Sec-Websocket-Key\" ||\n\t\t\tk == \"Sec-Websocket-Version\" ||\n\t\t\tk == \"Sec-Websocket-Extensions\" ||\n\t\t\t(k == \"Sec-Websocket-Protocol\" && len(d.Subprotocols) > 0):\n\t\t\treturn nil, nil, errors.New(\"websocket: duplicate header not allowed: \" + k)\n\t\tcase k == \"Sec-Websocket-Protocol\":\n\t\t\treq.Header[\"Sec-WebSocket-Protocol\"] = vs\n\t\tdefault:\n\t\t\treq.Header[k] = vs\n\t\t}\n\t}\n\n\tif d.EnableCompression {\n\t\treq.Header[\"Sec-WebSocket-Extensions\"] = []string{\"permessage-deflate; server_no_context_takeover; client_no_context_takeover\"}\n\t}\n\n\tif d.HandshakeTimeout != 0 {\n\t\tvar cancel func()\n\t\tctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)\n\t\tdefer cancel()\n\t}\n\n\tvar proxyURL *url.URL\n\tif d.Proxy != nil {\n\t\tproxyURL, err = d.Proxy(req)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\tnetDial, err := d.netDialFn(ctx, proxyURL, u)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\thostPort, hostNoPort := hostPortNoPort(u)\n\ttrace := httptrace.ContextClientTrace(ctx)\n\tif trace != nil && trace.GetConn != nil {\n\t\ttrace.GetConn(hostPort)\n\t}\n\n\tnetConn, err := netDial(ctx, \"tcp\", hostPort)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif trace != nil && trace.GotConn != nil {\n\t\ttrace.GotConn(httptrace.GotConnInfo{\n\t\t\tConn: netConn,\n\t\t})\n\t}\n\n\t// Close the network connection when returning an error. The variable\n\t// netConn is set to nil before the success return at the end of the\n\t// function.\n\tdefer func() {\n\t\tif netConn != nil {\n\t\t\t// It's safe to ignore the error from Close() because this code is\n\t\t\t// only executed when returning a more important error to the\n\t\t\t// application.\n\t\t\t_ = netConn.Close()\n\t\t}\n\t}()\n\n\t// Do TLS handshake over established connection if a proxy exists.\n\tif proxyURL != nil && u.Scheme == \"https\" {\n\n\t\tcfg := cloneTLSConfig(d.TLSClientConfig)\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.ServerName = hostNoPort\n\t\t}\n\t\ttlsConn := tls.Client(netConn, cfg)\n\t\tnetConn = tlsConn\n\n\t\tif trace != nil && trace.TLSHandshakeStart != nil {\n\t\t\ttrace.TLSHandshakeStart()\n\t\t}\n\t\terr := doHandshake(ctx, tlsConn, cfg)\n\t\tif trace != nil && trace.TLSHandshakeDone != nil {\n\t\t\ttrace.TLSHandshakeDone(tlsConn.ConnectionState(), err)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\n\tconn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)\n\n\tif err := req.Write(netConn); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif trace != nil && trace.GotFirstResponseByte != nil {\n\t\tif peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {\n\t\t\ttrace.GotFirstResponseByte()\n\t\t}\n\t}\n\n\tresp, err := http.ReadResponse(conn.br, req)\n\tif err != nil {\n\t\tif d.TLSClientConfig != nil {\n\t\t\tfor _, proto := range d.TLSClientConfig.NextProtos {\n\t\t\t\tif proto != \"http/1.1\" {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\n\t\t\t\t\t\t\"websocket: protocol %q was given but is not supported;\"+\n\t\t\t\t\t\t\t\"sharing tls.Config with net/http Transport can cause this error: %w\",\n\t\t\t\t\t\tproto, err,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil, nil, err\n\t}\n\n\tif d.Jar != nil {\n\t\tif rc := resp.Cookies(); len(rc) > 0 {\n\t\t\td.Jar.SetCookies(u, rc)\n\t\t}\n\t}\n\n\tif resp.StatusCode != 101 ||\n\t\t!tokenListContainsValue(resp.Header, \"Upgrade\", \"websocket\") ||\n\t\t!tokenListContainsValue(resp.Header, \"Connection\", \"upgrade\") ||\n\t\tresp.Header.Get(\"Sec-Websocket-Accept\") != computeAcceptKey(challengeKey) {\n\t\t// Before closing the network connection on return from this\n\t\t// function, slurp up some of the response to aid application\n\t\t// debugging.\n\t\tbuf := make([]byte, 1024)\n\t\tn, _ := io.ReadFull(resp.Body, buf)\n\t\tresp.Body = io.NopCloser(bytes.NewReader(buf[:n]))\n\t\treturn nil, resp, ErrBadHandshake\n\t}\n\n\tfor _, ext := range parseExtensions(resp.Header) {\n\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\tcontinue\n\t\t}\n\t\t_, snct := ext[\"server_no_context_takeover\"]\n\t\t_, cnct := ext[\"client_no_context_takeover\"]\n\t\tif !snct || !cnct {\n\t\t\treturn nil, resp, errInvalidCompression\n\t\t}\n\t\tconn.newCompressionWriter = compressNoContextTakeover\n\t\tconn.newDecompressionReader = decompressNoContextTakeover\n\t\tbreak\n\t}\n\n\tresp.Body = io.NopCloser(bytes.NewReader([]byte{}))\n\tconn.subprotocol = resp.Header.Get(\"Sec-Websocket-Protocol\")\n\n\tif err := netConn.SetDeadline(time.Time{}); err != nil {\n\t\treturn nil, resp, err\n\t}\n\n\t// Success! Set netConn to nil to stop the deferred function above from\n\t// closing the network connection.\n\tnetConn = nil\n\n\treturn conn, resp, nil\n}\n\n// Returns the dial function to establish the connection to either the backend\n// server or the proxy (if it exists). If the dialed entity is HTTPS, then the\n// returned dial function *also* performs the TLS handshake to the dialed entity.\n// NOTE: If a proxy exists, it is possible for a second TLS handshake to be\n// necessary over the established connection.\nfunc (d *Dialer) netDialFn(ctx context.Context, proxyURL *url.URL, backendURL *url.URL) (netDialerFunc, error) {\n\tvar netDial netDialerFunc\n\tif proxyURL != nil {\n\t\tnetDial = d.netDialFromURL(proxyURL)\n\t} else {\n\t\tnetDial = d.netDialFromURL(backendURL)\n\t}\n\t// If needed, wrap the dial function to set the connection deadline.\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tnetDial = netDialWithDeadline(netDial, deadline)\n\t}\n\t// Proxy dialing is wrapped to implement CONNECT method and possibly proxy auth.\n\tif proxyURL != nil {\n\t\treturn proxyFromURL(proxyURL, netDial)\n\t}\n\treturn netDial, nil\n}\n\n// Returns function to create the connection depending on the Dialer's\n// custom dialing functions and the passed URL of entity connecting to.\nfunc (d *Dialer) netDialFromURL(u *url.URL) netDialerFunc {\n\tvar netDial netDialerFunc\n\tswitch {\n\tcase d.NetDialContext != nil:\n\t\tnetDial = d.NetDialContext\n\tcase d.NetDial != nil:\n\t\tnetDial = func(ctx context.Context, net, addr string) (net.Conn, error) {\n\t\t\treturn d.NetDial(net, addr)\n\t\t}\n\tdefault:\n\t\tnetDial = (&net.Dialer{}).DialContext\n\t}\n\t// If dialed entity is HTTPS, then either use custom TLS dialing function (if exists)\n\t// or wrap the previously computed \"netDial\" to use TLS config for handshake.\n\tif u.Scheme == \"https\" {\n\t\tif d.NetDialTLSContext != nil {\n\t\t\tnetDial = d.NetDialTLSContext\n\t\t} else {\n\t\t\tnetDial = netDialWithTLSHandshake(netDial, d.TLSClientConfig, u)\n\t\t}\n\t}\n\treturn netDial\n}\n\n// Returns wrapped \"netDial\" function, performing TLS handshake after connecting.\nfunc netDialWithTLSHandshake(netDial netDialerFunc, tlsConfig *tls.Config, u *url.URL) netDialerFunc {\n\treturn func(ctx context.Context, unused, addr string) (net.Conn, error) {\n\t\thostPort, hostNoPort := hostPortNoPort(u)\n\t\ttrace := httptrace.ContextClientTrace(ctx)\n\t\tif trace != nil && trace.GetConn != nil {\n\t\t\ttrace.GetConn(hostPort)\n\t\t}\n\t\t// Creates TCP connection to addr using passed \"netDial\" function.\n\t\tconn, err := netDial(ctx, \"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcfg := cloneTLSConfig(tlsConfig)\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.ServerName = hostNoPort\n\t\t}\n\t\ttlsConn := tls.Client(conn, cfg)\n\t\t// Do the TLS handshake using TLSConfig over the wrapped connection.\n\t\tif trace != nil && trace.TLSHandshakeStart != nil {\n\t\t\ttrace.TLSHandshakeStart()\n\t\t}\n\t\terr = doHandshake(ctx, tlsConn, cfg)\n\t\tif trace != nil && trace.TLSHandshakeDone != nil {\n\t\t\ttrace.TLSHandshakeDone(tlsConn.ConnectionState(), err)\n\t\t}\n\t\tif err != nil {\n\t\t\ttlsConn.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn tlsConn, nil\n\t}\n}\n\n// Returns wrapped \"netDial\" function, setting passed deadline.\nfunc netDialWithDeadline(netDial netDialerFunc, deadline time.Time) netDialerFunc {\n\treturn func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\tc, err := netDial(ctx, network, addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\terr = c.SetDeadline(deadline)\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn c, nil\n\t}\n}\n\nfunc cloneTLSConfig(cfg *tls.Config) *tls.Config {\n\tif cfg == nil {\n\t\treturn &tls.Config{}\n\t}\n\treturn cfg.Clone()\n}\n\nfunc doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {\n\tif err := tlsConn.HandshakeContext(ctx); err != nil {\n\t\treturn err\n\t}\n\tif !cfg.InsecureSkipVerify {\n\t\tif err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "client_proxy_server_test.go",
    "content": "// Copyright 2025 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\n// These test cases use a websocket client (Dialer)/proxy/websocket server (Upgrader)\n// to validate the cases where a proxy is an intermediary between a websocket client\n// and server. The test cases usually 1) create a websocket server which echoes any\n// data received back to the client, 2) a basic duplex streaming proxy, and 3) a\n// websocket client which sends random data to the server through the proxy,\n// validating any subsequent data received is the same as the data sent. The various\n// permutations include the proxy and backend schemes (HTTP or HTTPS), as well as\n// the custom dial functions (e.g NetDialContext, NetDial) set on the Dialer.\n\nconst (\n\tsubprotocolV1 = \"subprotocol-version-1\"\n\tsubprotocolV2 = \"subprotocol-version-2\"\n)\n\n// Permutation 1\n//\n//\tBackend: HTTP\n//\tProxy:   HTTP\nfunc TestHTTPProxyAndBackend(t *testing.T) {\n\twebsocketTLS := false\n\tproxyTLS := false\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Dial the websocket server through the proxy server.\n\tdialer := Dialer{\n\t\tProxy:        http.ProxyURL(proxyServerURL),\n\t\tSubprotocols: []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 2\n//\n//\tBackend: HTTP\n//\tProxy:   HTTP\n//\tDialFn:  NetDial (dials proxy)\nfunc TestHTTPProxyWithNetDial(t *testing.T) {\n\twebsocketTLS := false\n\tproxyTLS := false\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Dial the websocket server through the proxy server.\n\tvar netDialCalled atomic.Int64\n\tdialer := Dialer{\n\t\tNetDial: func(network, addr string) (net.Conn, error) {\n\t\t\tnetDialCalled.Add(1)\n\t\t\treturn (&net.Dialer{}).DialContext(context.Background(), network, addr)\n\t\t},\n\t\tProxy:        http.ProxyURL(proxyServerURL),\n\t\tSubprotocols: []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), netDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 3\n//\n//\tBackend: HTTP\n//\tProxy:   HTTP\n//\tDialFn:  NetDialContext (dials proxy)\nfunc TestHTTPProxyWithNetDialContext(t *testing.T) {\n\twebsocketTLS := false\n\tproxyTLS := false\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Dial the websocket server through the proxy server.\n\tvar netDialCalled atomic.Int64\n\tdialer := Dialer{\n\t\tNetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tnetDialCalled.Add(1)\n\t\t\treturn (&net.Dialer{}).DialContext(ctx, network, addr)\n\t\t},\n\t\tProxy:        http.ProxyURL(proxyServerURL),\n\t\tSubprotocols: []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), netDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 4\n//\n//\tBackend:    HTTPS\n//\tProxy:      HTTP\n//\tDialFn:     NetDialTLSConfig (set but *ignored*)\n//\tTLS Config: set (used for backend TLS)\nfunc TestHTTPProxyWithHTTPSBackend(t *testing.T) {\n\twebsocketTLS := true\n\tproxyTLS := false\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tvar netDialTLSCalled atomic.Int64\n\tdialer := Dialer{\n\t\tProxy: http.ProxyURL(proxyServerURL),\n\t\t// This function should be ignored, because an HTTP proxy exists\n\t\t// and the backend TLS handshake should use TLSClientConfig.\n\t\tNetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tnetDialTLSCalled.Add(1)\n\t\t\treturn (&net.Dialer{}).DialContext(ctx, network, addr)\n\t\t},\n\t\t// Used for the backend server TLS handshake.\n\t\tTLSClientConfig: tlsConfig(websocketTLS, proxyTLS),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif numTLSDials := netDialTLSCalled.Load(); numTLSDials > 0 {\n\t\tt.Errorf(\"NetDialTLS should have been ignored\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 5\n//\n//\tBackend:    HTTPS\n//\tProxy:      HTTPS\n//\tTLS Config: set (used for both proxy and backend TLS)\nfunc TestHTTPSProxyAndBackend(t *testing.T) {\n\twebsocketTLS := true\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tdialer := Dialer{\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(websocketTLS, proxyTLS),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 6\n//\n//\tBackend:    HTTPS\n//\tProxy:      HTTPS\n//\tDialFn:     NetDial (used to dial proxy)\n//\tTLS Config: set (used for both proxy and backend TLS)\nfunc TestHTTPSProxyUsingNetDial(t *testing.T) {\n\twebsocketTLS := true\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tvar netDialCalled atomic.Int64\n\tdialer := Dialer{\n\t\tNetDial: func(network, addr string) (net.Conn, error) {\n\t\t\tnetDialCalled.Add(1)\n\t\t\treturn (&net.Dialer{}).DialContext(context.Background(), network, addr)\n\t\t},\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(websocketTLS, proxyTLS),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), netDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 7\n//\n//\tBackend:    HTTPS\n//\tProxy:      HTTPS\n//\tDialFn:     NetDialContext (used to dial proxy)\n//\tTLS Config: set (used for both proxy and backend TLS)\nfunc TestHTTPSProxyUsingNetDialContext(t *testing.T) {\n\twebsocketTLS := true\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tvar netDialCalled atomic.Int64\n\tdialer := Dialer{\n\t\tNetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tnetDialCalled.Add(1)\n\t\t\treturn (&net.Dialer{}).DialContext(ctx, network, addr)\n\t\t},\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(websocketTLS, proxyTLS),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), netDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 8\n//\n//\tBackend:    HTTPS\n//\tProxy:      HTTPS\n//\tDialFn:     NetDialTLSContext (used for proxy TLS)\n//\tTLS Config: set (used for backend TLS)\nfunc TestHTTPSProxyUsingNetDialTLSContext(t *testing.T) {\n\twebsocketTLS := true\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Configure the proxy dialing function which dials the proxy and\n\t// performs the TLS handshake.\n\tvar proxyDialCalled atomic.Int64\n\tproxyCerts := x509.NewCertPool()\n\tproxyCerts.AppendCertsFromPEM(proxyServerCert)\n\tproxyTLSConfig := &tls.Config{RootCAs: proxyCerts}\n\tproxyDial := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\tproxyDialCalled.Add(1)\n\t\treturn tls.Dial(network, addr, proxyTLSConfig)\n\t}\n\t// Configure the backend webscocket TLS configuration (handshake occurs\n\t// over the previously created proxy connection).\n\twebsocketCerts := x509.NewCertPool()\n\twebsocketCerts.AppendCertsFromPEM(websocketServerCert)\n\twebsocketTLSConfig := &tls.Config{RootCAs: websocketCerts}\n\tdialer := Dialer{\n\t\tProxy: http.ProxyURL(proxyServerURL),\n\t\t// Dial and TLS handshake function to proxy.\n\t\tNetDialTLSContext: proxyDial,\n\t\t// Used for second TLS handshake to backend server over previously\n\t\t// established proxy connection.\n\t\tTLSClientConfig: websocketTLSConfig,\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), proxyDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 9\n//\n//\tBackend:    HTTP\n//\tProxy:      HTTPS\n//\tTLS Config: set (used for proxy TLS)\nfunc TestHTTPSProxyHTTPBackend(t *testing.T) {\n\twebsocketTLS := false\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tdialer := Dialer{\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(websocketTLS, proxyTLS),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\n// Permutation 10\n//\n//\tBackend:    HTTP\n//\tProxy:      HTTPS\n//\tDialFn:     NetDialTLSContext (used for proxy TLS)\n//\tTLS Config: set (ignored)\nfunc TestHTTPSProxyUsingNetDialTLSContextWithHTTPBackend(t *testing.T) {\n\twebsocketTLS := false\n\tproxyTLS := true\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Start the proxy server.\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\tvar proxyDialCalled atomic.Int64\n\tdialer := Dialer{\n\t\tNetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tproxyDialCalled.Add(1)\n\t\t\treturn tls.Dial(network, addr, tlsConfig(websocketTLS, proxyTLS))\n\t\t},\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: &tls.Config{}, // Misconfigured, but ignored.\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\t// Send, receive, and validate random data over websocket connection.\n\tsendReceiveData(t, wsClient)\n\tif e, a := int64(1), proxyDialCalled.Load(); e != a {\n\t\tt.Errorf(\"netDial not called\")\n\t}\n\t// Validate the proxy server was called.\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy not called\")\n\t}\n}\n\nfunc TestTLSValidationErrors(t *testing.T) {\n\t// Both websocket and proxy servers are started with TLS.\n\twebsocketTLS := true\n\tproxyTLS := true\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\tproxyServer, proxyServerURL, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Dialer without proxy CA cert fails TLS verification.\n\ttlsError := \"tls: failed to verify certificate\"\n\tdialer := Dialer{\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(true, false),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\t_, _, err = dialer.Dial(websocketURL.String(), nil)\n\tif err == nil {\n\t\tt.Errorf(\"expected proxy TLS verification error did not arrive\")\n\t} else if !strings.Contains(err.Error(), tlsError) {\n\t\tt.Errorf(\"expected proxy TLS error (%s), got (%s)\", err.Error(), tlsError)\n\t}\n\t// Validate the proxy handler was *NOT* called (because proxy\n\t// server TLS validation failed).\n\tif e, a := int64(0), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy should not have been called\")\n\t}\n\t// Dialer without websocket CA cert fails TLS verification.\n\tdialer = Dialer{\n\t\tProxy:           http.ProxyURL(proxyServerURL),\n\t\tTLSClientConfig: tlsConfig(false, true),\n\t\tSubprotocols:    []string{subprotocolV1},\n\t}\n\t_, _, err = dialer.Dial(websocketURL.String(), nil)\n\tif err == nil {\n\t\tt.Errorf(\"expected websocket TLS verification error did not arrive\")\n\t} else if !strings.Contains(err.Error(), tlsError) {\n\t\tt.Errorf(\"expected websocket TLS error (%s), got (%s)\", err.Error(), tlsError)\n\t}\n\t// Validate the proxy server *was* called (but subsequent\n\t// websocket server failed TLS validation).\n\tif e, a := int64(1), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy have been called\")\n\t}\n}\n\nfunc TestProxyFnErrorIsPropagated(t *testing.T) {\n\twebsocketServer, websocketURL, err := newWebsocketServer(false)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\t// Create a Dialer where Proxy function always returns an error.\n\tproxyURLError := errors.New(\"proxy URL generation error\")\n\tdialer := Dialer{\n\t\tProxy: func(r *http.Request) (*url.URL, error) {\n\t\t\treturn nil, proxyURLError\n\t\t},\n\t\tSubprotocols: []string{subprotocolV1},\n\t}\n\t// Proxy URL generation error should halt request and be propagated.\n\t_, _, err = dialer.Dial(websocketURL.String(), nil)\n\tif err == nil {\n\t\tt.Fatalf(\"expected websocket dial error, received none\")\n\t} else if !errors.Is(proxyURLError, err) {\n\t\tt.Fatalf(\"expected error (%s), got (%s)\", proxyURLError, err)\n\t}\n}\n\nfunc TestProxyFnNilMeansNoProxy(t *testing.T) {\n\t// Both websocket and proxy servers are started.\n\twebsocketTLS := false\n\tproxyTLS := false\n\twebsocketServer, websocketURL, err := newWebsocketServer(websocketTLS)\n\tdefer websocketServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting websocket server: %v\", err)\n\t}\n\tproxyServer, _, err := newProxyServer(proxyTLS)\n\tdefer proxyServer.Close()\n\tif err != nil {\n\t\tt.Fatalf(\"error starting proxy server: %v\", err)\n\t}\n\t// Dialer created with Proxy URL generation function returning nil\n\t// proxy URL, which continues with backend server connection without\n\t// proxying.\n\tdialer := Dialer{\n\t\tProxy: func(r *http.Request) (*url.URL, error) {\n\t\t\treturn nil, nil\n\t\t},\n\t\tSubprotocols: []string{subprotocolV1},\n\t}\n\twsClient, _, err := dialer.Dial(websocketURL.String(), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"websocket dial error: %v\", err)\n\t}\n\tsendReceiveData(t, wsClient)\n\t// Validate the proxy handler was *NOT* called (because proxy\n\t// URL generation returned nil).\n\tif e, a := int64(0), proxyServer.numCalls(); e != a {\n\t\tt.Errorf(\"proxy should not have been called\")\n\t}\n}\n\n// \"counter\" interface can be implemented by a server to keep track\n// of the number of times a handler was called, as well as \"Close\".\ntype counter interface {\n\tincrement()\n\tnumCalls() int64\n\tcloser\n}\n\ntype closer interface {\n\tClose()\n}\n\n// testServer implements \"counter\" interface.\ntype testServer struct {\n\tserver     *httptest.Server\n\tnumHandled atomic.Int64\n}\n\nfunc (ts *testServer) numCalls() int64 {\n\treturn ts.numHandled.Load()\n}\n\nfunc (ts *testServer) increment() {\n\tts.numHandled.Add(1)\n}\n\nfunc (ts *testServer) Close() {\n\tif ts.server != nil {\n\t\tts.server.Close()\n\t}\n}\n\n// websocketEchoHandler upgrades the connection associated with the request, and\n// echoes binary messages read off the websocket connection back to the client.\nvar websocketEchoHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\tupgrader := Upgrader{\n\t\tCheckOrigin: func(r *http.Request) bool {\n\t\t\treturn true // Accepting all requests\n\t\t},\n\t\tSubprotocols: []string{\n\t\t\tsubprotocolV1,\n\t\t\tsubprotocolV2,\n\t\t},\n\t}\n\twsConn, err := upgrader.Upgrade(w, req, nil)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n\tdefer wsConn.Close()\n\tfor {\n\t\twriter, err := wsConn.NextWriter(BinaryMessage)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tmessageType, reader, err := wsConn.NextReader()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif messageType != BinaryMessage {\n\t\t\thttp.Error(w, \"websocket reader not binary message type\",\n\t\t\t\thttp.StatusInternalServerError)\n\t\t}\n\t\t_, err = io.Copy(writer, reader)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"websocket server io copy error\",\n\t\t\t\thttp.StatusInternalServerError)\n\t\t}\n\t}\n})\n\n// Returns a test backend websocket server as well as the URL pointing\n// to the server, or an error if one occurred. Sets up a TLS endpoint\n// on the server if the passed \"tlsServer\" is true.\n// func newWebsocketServer(tlsServer bool) (*httptest.Server, *url.URL, error) {\nfunc newWebsocketServer(tlsServer bool) (closer, *url.URL, error) {\n\t// Start the websocket server, which echoes data back to sender.\n\twebsocketServer := httptest.NewUnstartedServer(websocketEchoHandler)\n\tif tlsServer {\n\t\twebsocketKeyPair, err := tls.X509KeyPair(websocketServerCert, websocketServerKey)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\twebsocketServer.TLS = &tls.Config{\n\t\t\tCertificates: []tls.Certificate{websocketKeyPair},\n\t\t}\n\t\twebsocketServer.StartTLS()\n\t} else {\n\t\twebsocketServer.Start()\n\t}\n\twebsocketURL, err := url.Parse(websocketServer.URL)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif tlsServer {\n\t\twebsocketURL.Scheme = \"wss\"\n\t} else {\n\t\twebsocketURL.Scheme = \"ws\"\n\t}\n\treturn websocketServer, websocketURL, nil\n}\n\n// proxyHandler creates a full duplex streaming connection between the client\n// (hijacking the http request connection), and an \"upstream\" dialed connection\n// to the \"Host\". Creates two goroutines to copy between connections in each direction.\nvar proxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t// Validate the CONNECT method.\n\tif req.Method != http.MethodConnect {\n\t\thttp.Error(w, \"method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\t// Dial upstream server.\n\tupstream, err := (&net.Dialer{}).DialContext(req.Context(), \"tcp\", req.URL.Host)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer upstream.Close()\n\t// Return 200 OK to client.\n\tw.WriteHeader(http.StatusOK)\n\t// Hijack client connection.\n\tclient, _, err := w.(http.Hijacker).Hijack()\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer client.Close()\n\t// Create duplex streaming between client and upstream connections.\n\tdone := make(chan struct{}, 2)\n\tgo func() {\n\t\t_, _ = io.Copy(upstream, client)\n\t\tdone <- struct{}{}\n\t}()\n\tgo func() {\n\t\t_, _ = io.Copy(client, upstream)\n\t\tdone <- struct{}{}\n\t}()\n\t<-done\n})\n\n// Returns a new test HTTP server, as well as the URL to that server, or\n// an error if one occurred. numProxyCalls keeps track of the number of\n// times the proxy handler was called with this server.\nfunc newProxyServer(tlsServer bool) (counter, *url.URL, error) {\n\t// Start the proxy server, keeping track of how many times the handler is called.\n\tts := &testServer{}\n\tproxyServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\n\t\tts.increment()\n\t\tproxyHandler.ServeHTTP(w, req)\n\t}))\n\tif tlsServer {\n\t\tproxyKeyPair, err := tls.X509KeyPair(proxyServerCert, proxyServerKey)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tproxyServer.TLS = &tls.Config{\n\t\t\tCertificates: []tls.Certificate{proxyKeyPair},\n\t\t}\n\t\tproxyServer.StartTLS()\n\t} else {\n\t\tproxyServer.Start()\n\t}\n\tproxyURL, err := url.Parse(proxyServer.URL)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn ts, proxyURL, nil\n}\n\n// Returns the TLS config with the RootCAs cert pool set. If\n// neither websocket nor proxy server uses TLS, returns nil.\nfunc tlsConfig(websocketTLS bool, proxyTLS bool) *tls.Config {\n\tif !websocketTLS && !proxyTLS {\n\t\treturn nil\n\t}\n\tcertPool := x509.NewCertPool()\n\ttlsConfig := &tls.Config{\n\t\tRootCAs: certPool,\n\t}\n\tif websocketTLS {\n\t\ttlsConfig.RootCAs.AppendCertsFromPEM(websocketServerCert)\n\t}\n\tif proxyTLS {\n\t\ttlsConfig.RootCAs.AppendCertsFromPEM(proxyServerCert)\n\t}\n\treturn tlsConfig\n}\n\n// Sends, receives, and validates random data sent and received\n// over the passed websocket connection.\nconst randomDataSize = 128 * 1024\n\nfunc sendReceiveData(t *testing.T, wsConn *Conn) {\n\t// Create the random data.\n\trandomData := make([]byte, randomDataSize)\n\tif _, err := rand.Read(randomData); err != nil {\n\t\tt.Errorf(\"unexpected error reading random data: %v\", err)\n\t}\n\t// Send the random data.\n\terr := wsConn.WriteMessage(BinaryMessage, randomData)\n\tif err != nil {\n\t\tt.Errorf(\"websocket write error: %v\", err)\n\t}\n\t// Read from the websocket connection, and validate the\n\t// read data is the same as the previously sent data.\n\t_, received, err := wsConn.ReadMessage()\n\tif !bytes.Equal(randomData, received) {\n\t\tt.Errorf(\"unexpected data received: %d bytes sent, %d bytes received\",\n\t\t\tlen(received), len(randomData))\n\t}\n}\n\n// proxyServerCert was generated from crypto/tls/generate_cert.go with the following command:\n//\n//\tgo run generate_cert.go  --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date \"Jan 1 00:00:00 1970\" --duration=1000000h\n//\n// proxyServerCert is a self-signed.\nvar proxyServerCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIDGTCCAgGgAwIBAgIRALL5AZcefF4kkYV1SEG6YrMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2\nMDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBALQ/FHcyVwdFHxARbbD2KBtDUT7Eni+8ioNdjtGcmtXqBv45EC1C\nJOqqGJTroFGJ6Q9kQIZ9FqH5IJR2fOOJD9kOTueG4Vt1JY1rj1Kbpjefu8XleZ5L\nSBwIWVnN/lEsEbuKmj7N2gLt5AH3zMZiBI1mg1u9Z5ZZHYbCiTpBrwsq6cTlvR9g\ndyo1YkM5hRESCzsrL0aUByoo0qRMD8ZsgANJwgsiO0/M6idbxDwv1BnGwGmRYvOE\nHxpy3v0Jg7GJYrvnpnifJTs4nw91N5X9pXxR7FFzi/6HTYDWRljvTb0w6XciKYAz\nbWZ0+cJr5F7wB7ovlbm7HrQIR7z7EIIu2d8CAwEAAaNoMGYwDgYDVR0PAQH/BAQD\nAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wLgYDVR0R\nBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZI\nhvcNAQELBQADggEBAFPPWopNEJtIA2VFAQcqN6uJK+JVFOnjGRoCrM6Xgzdm0wxY\nXCGjsxY5dl+V7KzdGqu858rCaq5osEBqypBpYAnS9C38VyCDA1vPS1PsN8SYv48z\nDyBwj+7R2qar0ADBhnhWxvYO9M72lN/wuCqFKYMeFSnJdQLv3AsrrHe9lYqOa36s\n8wxSwVTFTYXBzljPEnSaaJMPqFD8JXaZK1ryJPkO5OsCNQNGtatNiWAf3DcmwHAT\nMGYMzP0u4nw47aRz9shB8w+taPKHx2BVwE1m/yp3nHVioOjXqA1fwRQVGclCJSH1\nD2iq3hWVHRENgjTjANBPICLo9AZ4JfN6PH19mnU=\n-----END CERTIFICATE-----`)\n\n// proxyServerKey is the private key for proxyServerCert.\nvar proxyServerKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAtD8UdzJXB0UfEBFtsPYoG0NRPsSeL7yKg12O0Zya1eoG/jkQ\nLUIk6qoYlOugUYnpD2RAhn0WofkglHZ844kP2Q5O54bhW3UljWuPUpumN5+7xeV5\nnktIHAhZWc3+USwRu4qaPs3aAu3kAffMxmIEjWaDW71nllkdhsKJOkGvCyrpxOW9\nH2B3KjViQzmFERILOysvRpQHKijSpEwPxmyAA0nCCyI7T8zqJ1vEPC/UGcbAaZFi\n84QfGnLe/QmDsYliu+emeJ8lOzifD3U3lf2lfFHsUXOL/odNgNZGWO9NvTDpdyIp\ngDNtZnT5wmvkXvAHui+VubsetAhHvPsQgi7Z3wIDAQABAoIBAGmw93IxjYCQ0ncc\nkSKMJNZfsdtJdaxuNRZ0nNNirhQzR2h403iGaZlEpmdkhzxozsWcto1l+gh+SdFk\nbTUK4MUZM8FlgO2dEqkLYh5BcMT7ICMZvSfJ4v21E5eqR68XVUqQKoQbNvQyxFk3\nEddeEGdNrkb0GDK8DKlBlzAW5ep4gjG85wSTjR+J+muUv3R0BgLBFSuQnIDM/IMB\nLWqsja/QbtB7yppe7jL5u8UCFdZG8BBKT9fcvFIu5PRLO3MO0uOI7LTc8+W1Xm23\nuv+j3SY0+v+6POjK0UlJFFi/wkSPTFIfrQO1qFBkTDQHhQ6q/7GnILYYOiGbIRg2\nNNuP52ECgYEAzXEoy50wSYh8xfFaBuxbm3ruuG2W49jgop7ZfoFrPWwOQKAZS441\nVIwV4+e5IcA6KkuYbtGSdTYqK1SMkgnUyD/VevwAqH5TJoEIGu0pDuKGwVuwqioZ\nfrCIAV5GllKyUJ55VZNbRr2vY2fCsWbaCSCHETn6C16DNuTCe5C0JBECgYEA4JqY\n5GpNbMG8fOt4H7hU0Fbm2yd6SHJcQ3/9iimef7xG6ajxsYrIhg1ft+3IPHMjVI0+\n9brwHDnWg4bOOx/VO4VJBt6Dm/F33bndnZRkuIjfSNpLM51P+EnRdaFVHOJHwKqx\nuF69kihifCAG7YATgCveeXImzBUSyZUz9UrETu8CgYARNBimdFNG1RcdvEg9rC0/\np9u1tfecvNySwZqU7WF9kz7eSonTueTdX521qAHowaAdSpdJMGODTTXaywm6cPhQ\njIfj9JZZhbqQzt1O4+08Qdvm9TamCUB5S28YLjza+bHU7nBaqixKkDfPqzCyilpX\nyVGGL8SwjwmN3zop/sQXAQKBgC0JMsESQ6YcDsRpnrOVjYQc+LtW5iEitTdfsaID\niGGKihmOI7B66IxgoCHMTws39wycKdSyADVYr5e97xpR3rrJlgQHmBIrz+Iow7Q2\nLiAGaec8xjl6QK/DdXmFuQBKqyKJ14rljFODP4QuE9WJid94bGqjpf3j99ltznZP\n4J8HAoGAJb4eb4lu4UGwifDzqfAPzLGCoi0fE1/hSx34lfuLcc1G+LEu9YDKoOVJ\n9suOh0b5K/bfEy9KrVMBBriduvdaERSD8S3pkIQaitIz0B029AbE4FLFf9lKQpP2\nKR8NJEkK99Vh/tew6jAMll70xFrE7aF8VLXJVE7w4sQzuvHxl9Q=\n-----END RSA PRIVATE KEY-----\n`)\n\n// websocketServerCert is self-signed.\nvar websocketServerCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIDOTCCAiGgAwIBAgIQYSN1VY/favsLUo+B7gJ5tTANBgkqhkiG9w0BAQsFADAS\nMRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw\nMDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEApBlintjkL1fO1Sk2pzNvl862CtTwU7/Jy6EZqWzI17wEbPn4sbSD\nbHhfDlPl2nmw3hVkc6LNK+eqzm2GX/ai4tgMiaH7kyyNit1K3g7y7GISMf9poWIa\nPOJhid2wmhKHbEtHECSdQ5c/jEN1UVzB4go5LO7MEEVo9kyQ+yBqS6gISyFmfaT4\nqOsPJBir33bBpptSend1JSXaRTXqRa1p+oudw2ILa4U7KfuKK3emp21m5/HYAuSf\nCV4WqqDoDiBPMpsQ0kPEPugWZKFeF3qanmqFFvptYx+zJbOznWYY2D3idWsvcg6q\nVLPEB19oXaVBV0HXPFtObm5m1jCpl8FI1wIDAQABo4GIMIGFMA4GA1UdDwEB/wQE\nAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\nDgQWBBQcSkjqA9rgos1daegNj49BpRCA0jAuBgNVHREEJzAlggtleGFtcGxlLmNv\nbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAnk9i\n9rogNTi9B1pn+Fbk3WALKdEjv/uyePsTnwdyvswVbeYbQweU9TrhYT2+eXbMA5kY\n7TaQm46idRqxCKMgc3Ip3DADJdm8cJX9p2ExU4fKdkPc1KD/J+4QHHx1W2Ml5S2o\nfoOo6j1F0UdZP/rBj0UumEZp32qW+4DhVV/QQjUB8J0gaDC7yZBMdyMIeClR0RqE\nYfZdCJbQHqtTwBXN+imQUHPGmksYkRDpFRvw/4crpcMIE04mVVd99nOpFCQnK61t\n9US1y17VW1lYpkqlCS+rkcAtor4Z5naSf9/oLGCxEAwyW0pwHGO6MXtMxvB/JD20\nhJdlz1I7wlSfF4MiRQ==\n-----END CERTIFICATE-----`)\n\n// websocketServerKey is the private key for websocketServerCert.\nvar websocketServerKey = []byte(`-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkGWKe2OQvV87V\nKTanM2+XzrYK1PBTv8nLoRmpbMjXvARs+fixtINseF8OU+XaebDeFWRzos0r56rO\nbYZf9qLi2AyJofuTLI2K3UreDvLsYhIx/2mhYho84mGJ3bCaEodsS0cQJJ1Dlz+M\nQ3VRXMHiCjks7swQRWj2TJD7IGpLqAhLIWZ9pPio6w8kGKvfdsGmm1J6d3UlJdpF\nNepFrWn6i53DYgtrhTsp+4ord6anbWbn8dgC5J8JXhaqoOgOIE8ymxDSQ8Q+6BZk\noV4XepqeaoUW+m1jH7Mls7OdZhjYPeJ1ay9yDqpUs8QHX2hdpUFXQdc8W05ubmbW\nMKmXwUjXAgMBAAECggEAE6BkTDgH//rnkP/Ej/Y17Zkv6qxnMLe/4evwZB7PsrBu\ncxOUAWUOpvA1UO215bh87+2XvcDbUISnyC1kpKDyAGGeC5llER2DXE11VokWgtvZ\nQ0OXavw5w83A+WVGFFdiUmXP0l10CxEm7OwQjFz6D21GQ1qC65tG9NZZghTxbFTe\niZKqgWqyHsaAWLOuDQbj1FTEBMFrY8f9RbclSh0luPZnzGc4BVI/t34jKPZBpH2N\nNCkr8aB7MMHGhrNZFHAu/KAvq8UBrDTX+O8ERMwcwQWB4nne2+GOTN0MdcAUc72i\nGryzIa8TgO+TpQOYoZ4NPnzFrsa+m3G2Tug3vbt62QKBgQDOPfM4/5/x/h/ggxQn\naRvEOC+8ldeqEOS1VTGiuDKJMWXrNkG+d+AsxfNP4k0QVNrpEAZSYcf0gnS9Odcl\nluEsi/yPZDDnPg/cS+Z3336VKsggly7BWFs1Ct/9I+ZfSCl88TkVpIfeCBC34XEb\n0mFUq/RdLqXj/mVLbBfr+H8cEwKBgQDLsJUm8lkWFAPJ8UMto8xeUMGk44VukYwx\n+oI6KhplFntiI0C1Dd9wrxyCjySlJcc0NFt6IPN84d7pI9LQSbiKXQ1jMvsBzd4G\nEMtG8SHpIY/mMU+KzWLHYVFS0FA4PvXXvPRNLOXas7hbALZdLshVKd7aDlkQAb5C\nKWFHeIFwrQKBgA8r5Xl67HQrwoKMge4IQF+l1nUj/LJo/boNI1KaBDWtaZbs7dcq\nEFaa1TQ6LHsYEuZ0JFLpGIF3G0lUOOxt9fCF97VApIxON3J4LuMAkNo+RGyJUoos\nisETJLkFbAv0TgD/6bga21fM9hXgwqZOSpSk9ZvpM5DbBO6QbA4SwJ77AoGAX7h1\n/z14XAW/2hDE7xfAnLn6plA9jj5b0cjVlhvfF44/IVlLuUnxrPS9wyUdpXZhbMkG\nDBicFB3ZMVqiYTuju3ILLojwqGJkahlOTeJXe0VIaHbX2HS4bNXw76fxat07jsy/\nSd1Fj0dR5YIqMRQhFNR+Y57Gf90x2cm0a2/X9GkCgYANawYx9bNfcX0HMVG7vktK\n6/80omnoBM0JUxA+V7DxS8kr9Cj2Y/kcS+VHb4yyoSkDgnsSdnCr1ZTctcj828MJ\n8AUwskAtEjPkHRXEgRRnEl2oJGD1TT5iwBNnuPAQDXwzkGCRYBnlfZNbILbOoSUz\nm+VDcqT5XzcRADa/TLlEXA==\n-----END PRIVATE KEY-----\n`)\n"
  },
  {
    "path": "client_server_test.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/cookiejar\"\n\t\"net/http/httptest\"\n\t\"net/http/httptrace\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar cstUpgrader = Upgrader{\n\tSubprotocols:      []string{\"p0\", \"p1\"},\n\tReadBufferSize:    1024,\n\tWriteBufferSize:   1024,\n\tEnableCompression: true,\n\tError: func(w http.ResponseWriter, r *http.Request, status int, reason error) {\n\t\thttp.Error(w, reason.Error(), status)\n\t},\n}\n\nvar cstDialer = Dialer{\n\tSubprotocols:     []string{\"p1\", \"p2\"},\n\tReadBufferSize:   1024,\n\tWriteBufferSize:  1024,\n\tHandshakeTimeout: 30 * time.Second,\n}\n\ntype cstHandler struct {\n\t*testing.T\n\ts *cstServer\n}\n\ntype cstServer struct {\n\tURL    string\n\tServer *httptest.Server\n\twg     sync.WaitGroup\n}\n\nconst (\n\tcstPath       = \"/a/b\"\n\tcstRawQuery   = \"x=y\"\n\tcstRequestURI = cstPath + \"?\" + cstRawQuery\n)\n\nfunc (s *cstServer) Close() {\n\ts.Server.Close()\n\t// Wait for handler functions to complete.\n\ts.wg.Wait()\n}\n\nfunc newServer(t *testing.T) *cstServer {\n\tvar s cstServer\n\ts.Server = httptest.NewServer(cstHandler{T: t, s: &s})\n\ts.Server.URL += cstRequestURI\n\ts.URL = makeWsProto(s.Server.URL)\n\treturn &s\n}\n\nfunc newTLSServer(t *testing.T) *cstServer {\n\tvar s cstServer\n\ts.Server = httptest.NewTLSServer(cstHandler{T: t, s: &s})\n\ts.Server.URL += cstRequestURI\n\ts.URL = makeWsProto(s.Server.URL)\n\treturn &s\n}\n\nfunc (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\t// Because tests wait for a response from a server, we are guaranteed that\n\t// the wait group count is incremented before the test waits on the group\n\t// in the call to (*cstServer).Close().\n\tt.s.wg.Add(1)\n\tdefer t.s.wg.Done()\n\n\tif r.URL.Path != cstPath {\n\t\tt.Logf(\"path=%v, want %v\", r.URL.Path, cstPath)\n\t\thttp.Error(w, \"bad path\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif r.URL.RawQuery != cstRawQuery {\n\t\tt.Logf(\"query=%v, want %v\", r.URL.RawQuery, cstRawQuery)\n\t\thttp.Error(w, \"bad path\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tsubprotos := Subprotocols(r)\n\tif !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) {\n\t\tt.Logf(\"subprotols=%v, want %v\", subprotos, cstDialer.Subprotocols)\n\t\thttp.Error(w, \"bad protocol\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tws, err := cstUpgrader.Upgrade(w, r, http.Header{\"Set-Cookie\": {\"sessionID=1234\"}})\n\tif err != nil {\n\t\tt.Logf(\"Upgrade: %v\", err)\n\t\treturn\n\t}\n\tdefer ws.Close()\n\n\tif ws.Subprotocol() != \"p1\" {\n\t\tt.Logf(\"Subprotocol() = %s, want p1\", ws.Subprotocol())\n\t\tws.Close()\n\t\treturn\n\t}\n\top, rd, err := ws.NextReader()\n\tif err != nil {\n\t\tt.Logf(\"NextReader: %v\", err)\n\t\treturn\n\t}\n\twr, err := ws.NextWriter(op)\n\tif err != nil {\n\t\tt.Logf(\"NextWriter: %v\", err)\n\t\treturn\n\t}\n\tif _, err = io.Copy(wr, rd); err != nil {\n\t\tt.Logf(\"NextWriter: %v\", err)\n\t\treturn\n\t}\n\tif err := wr.Close(); err != nil {\n\t\tt.Logf(\"Close: %v\", err)\n\t\treturn\n\t}\n}\n\nfunc makeWsProto(s string) string {\n\treturn \"ws\" + strings.TrimPrefix(s, \"http\")\n}\n\nfunc sendRecv(t *testing.T, ws *Conn) {\n\tconst message = \"Hello World!\"\n\tif err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil {\n\t\tt.Fatalf(\"SetWriteDeadline: %v\", err)\n\t}\n\tif err := ws.WriteMessage(TextMessage, []byte(message)); err != nil {\n\t\tt.Fatalf(\"WriteMessage: %v\", err)\n\t}\n\tif err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil {\n\t\tt.Fatalf(\"SetReadDeadline: %v\", err)\n\t}\n\t_, p, err := ws.ReadMessage()\n\tif err != nil {\n\t\tt.Fatalf(\"ReadMessage: %v\", err)\n\t}\n\tif string(p) != message {\n\t\tt.Fatalf(\"message=%s, want %s\", p, message)\n\t}\n}\n\nfunc TestProxyDial(t *testing.T) {\n\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tsurl, _ := url.Parse(s.Server.URL)\n\n\tcstDialer := cstDialer // make local copy for modification on next line.\n\tcstDialer.Proxy = http.ProxyURL(surl)\n\n\tconnect := false\n\torigHandler := s.Server.Config.Handler\n\n\t// Capture the request Host header.\n\ts.Server.Config.Handler = http.HandlerFunc(\n\t\tfunc(w http.ResponseWriter, r *http.Request) {\n\t\t\tif r.Method == http.MethodConnect {\n\t\t\t\tconnect = true\n\t\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !connect {\n\t\t\t\tt.Log(\"connect not received\")\n\t\t\t\thttp.Error(w, \"connect not received\", http.StatusMethodNotAllowed)\n\t\t\t\treturn\n\t\t\t}\n\t\t\torigHandler.ServeHTTP(w, r)\n\t\t})\n\n\tws, _, err := cstDialer.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestProxyAuthorizationDial(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tsurl, _ := url.Parse(s.Server.URL)\n\tsurl.User = url.UserPassword(\"username\", \"password\")\n\n\tcstDialer := cstDialer // make local copy for modification on next line.\n\tcstDialer.Proxy = http.ProxyURL(surl)\n\n\tconnect := false\n\torigHandler := s.Server.Config.Handler\n\n\t// Capture the request Host header.\n\ts.Server.Config.Handler = http.HandlerFunc(\n\t\tfunc(w http.ResponseWriter, r *http.Request) {\n\t\t\tproxyAuth := r.Header.Get(\"Proxy-Authorization\")\n\t\t\texpectedProxyAuth := \"Basic \" + base64.StdEncoding.EncodeToString([]byte(\"username:password\"))\n\t\t\tif r.Method == http.MethodConnect && proxyAuth == expectedProxyAuth {\n\t\t\t\tconnect = true\n\t\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !connect {\n\t\t\t\tt.Log(\"connect with proxy authorization not received\")\n\t\t\t\thttp.Error(w, \"connect with proxy authorization not received\", http.StatusMethodNotAllowed)\n\t\t\t\treturn\n\t\t\t}\n\t\t\torigHandler.ServeHTTP(w, r)\n\t\t})\n\n\tws, _, err := cstDialer.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestDial(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tws, _, err := cstDialer.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestDialCookieJar(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tjar, _ := cookiejar.New(nil)\n\td := cstDialer\n\td.Jar = jar\n\n\tu, _ := url.Parse(s.URL)\n\n\tswitch u.Scheme {\n\tcase \"ws\":\n\t\tu.Scheme = \"http\"\n\tcase \"wss\":\n\t\tu.Scheme = \"https\"\n\t}\n\n\tcookies := []*http.Cookie{{Name: \"gorilla\", Value: \"ws\", Path: \"/\"}}\n\td.Jar.SetCookies(u, cookies)\n\n\tws, _, err := d.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\n\tvar gorilla string\n\tvar sessionID string\n\tfor _, c := range d.Jar.Cookies(u) {\n\t\tif c.Name == \"gorilla\" {\n\t\t\tgorilla = c.Value\n\t\t}\n\n\t\tif c.Name == \"sessionID\" {\n\t\t\tsessionID = c.Value\n\t\t}\n\t}\n\tif gorilla != \"ws\" {\n\t\tt.Error(\"Cookie not present in jar.\")\n\t}\n\n\tif sessionID != \"1234\" {\n\t\tt.Error(\"Set-Cookie not received from the server.\")\n\t}\n\n\tsendRecv(t, ws)\n}\n\nfunc rootCAs(t *testing.T, s *httptest.Server) *x509.CertPool {\n\tcerts := x509.NewCertPool()\n\tfor _, c := range s.TLS.Certificates {\n\t\troots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1])\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error parsing server's root cert: %v\", err)\n\t\t}\n\t\tfor _, root := range roots {\n\t\t\tcerts.AddCert(root)\n\t\t}\n\t}\n\treturn certs\n}\n\nfunc TestDialTLS(t *testing.T) {\n\ts := newTLSServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, s.Server)}\n\tws, _, err := d.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestDialTimeout(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.HandshakeTimeout = -1\n\tws, _, err := d.Dial(s.URL, nil)\n\tif err == nil {\n\t\tws.Close()\n\t\tt.Fatalf(\"Dial: nil\")\n\t}\n}\n\n// requireDeadlineNetConn fails the current test when Read or Write are called\n// with no deadline.\ntype requireDeadlineNetConn struct {\n\tt                  *testing.T\n\tc                  net.Conn\n\treadDeadlineIsSet  bool\n\twriteDeadlineIsSet bool\n}\n\nfunc (c *requireDeadlineNetConn) SetDeadline(t time.Time) error {\n\tc.writeDeadlineIsSet = !t.Equal(time.Time{})\n\tc.readDeadlineIsSet = c.writeDeadlineIsSet\n\treturn c.c.SetDeadline(t)\n}\n\nfunc (c *requireDeadlineNetConn) SetReadDeadline(t time.Time) error {\n\tc.readDeadlineIsSet = !t.Equal(time.Time{})\n\treturn c.c.SetDeadline(t)\n}\n\nfunc (c *requireDeadlineNetConn) SetWriteDeadline(t time.Time) error {\n\tc.writeDeadlineIsSet = !t.Equal(time.Time{})\n\treturn c.c.SetDeadline(t)\n}\n\nfunc (c *requireDeadlineNetConn) Write(p []byte) (int, error) {\n\tif !c.writeDeadlineIsSet {\n\t\tc.t.Fatalf(\"write with no deadline\")\n\t}\n\treturn c.c.Write(p)\n}\n\nfunc (c *requireDeadlineNetConn) Read(p []byte) (int, error) {\n\tif !c.readDeadlineIsSet {\n\t\tc.t.Fatalf(\"read with no deadline\")\n\t}\n\treturn c.c.Read(p)\n}\n\nfunc (c *requireDeadlineNetConn) Close() error         { return c.c.Close() }\nfunc (c *requireDeadlineNetConn) LocalAddr() net.Addr  { return c.c.LocalAddr() }\nfunc (c *requireDeadlineNetConn) RemoteAddr() net.Addr { return c.c.RemoteAddr() }\n\nfunc TestHandshakeTimeout(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.NetDial = func(n, a string) (net.Conn, error) {\n\t\tc, err := net.Dial(n, a)\n\t\treturn &requireDeadlineNetConn{c: c, t: t}, err\n\t}\n\tws, _, err := d.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatal(\"Dial:\", err)\n\t}\n\tws.Close()\n}\n\nfunc TestHandshakeTimeoutInContext(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.HandshakeTimeout = 0\n\td.NetDialContext = func(ctx context.Context, n, a string) (net.Conn, error) {\n\t\tnetDialer := &net.Dialer{}\n\t\tc, err := netDialer.DialContext(ctx, n, a)\n\t\treturn &requireDeadlineNetConn{c: c, t: t}, err\n\t}\n\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second))\n\tdefer cancel()\n\tws, _, err := d.DialContext(ctx, s.URL, nil)\n\tif err != nil {\n\t\tt.Fatal(\"Dial:\", err)\n\t}\n\tws.Close()\n}\n\nfunc TestDialBadScheme(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tws, _, err := cstDialer.Dial(s.Server.URL, nil)\n\tif err == nil {\n\t\tws.Close()\n\t\tt.Fatalf(\"Dial: nil\")\n\t}\n}\n\nfunc TestDialBadOrigin(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tws, resp, err := cstDialer.Dial(s.URL, http.Header{\"Origin\": {\"bad\"}})\n\tif err == nil {\n\t\tws.Close()\n\t\tt.Fatalf(\"Dial: nil\")\n\t}\n\tif resp == nil {\n\t\tt.Fatalf(\"resp=nil, err=%v\", err)\n\t}\n\tif resp.StatusCode != http.StatusForbidden {\n\t\tt.Fatalf(\"status=%d, want %d\", resp.StatusCode, http.StatusForbidden)\n\t}\n}\n\nfunc TestDialBadHeader(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tfor _, k := range []string{\"Upgrade\",\n\t\t\"Connection\",\n\t\t\"Sec-Websocket-Key\",\n\t\t\"Sec-Websocket-Version\",\n\t\t\"Sec-Websocket-Protocol\"} {\n\t\th := http.Header{}\n\t\th.Set(k, \"bad\")\n\t\tws, _, err := cstDialer.Dial(s.URL, http.Header{\"Origin\": {\"bad\"}})\n\t\tif err == nil {\n\t\t\tws.Close()\n\t\t\tt.Errorf(\"Dial with header %s returned nil\", k)\n\t\t}\n\t}\n}\n\nfunc TestBadMethod(t *testing.T) {\n\ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tws, err := cstUpgrader.Upgrade(w, r, nil)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"handshake succeeded, expect fail\")\n\t\t\tws.Close()\n\t\t}\n\t}))\n\tdefer s.Close()\n\n\treq, err := http.NewRequest(http.MethodPost, s.URL, strings.NewReader(\"\"))\n\tif err != nil {\n\t\tt.Fatalf(\"NewRequest returned error %v\", err)\n\t}\n\treq.Header.Set(\"Connection\", \"upgrade\")\n\treq.Header.Set(\"Upgrade\", \"websocket\")\n\treq.Header.Set(\"Sec-Websocket-Version\", \"13\")\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Fatalf(\"Do returned error %v\", err)\n\t}\n\tresp.Body.Close()\n\tif resp.StatusCode != http.StatusMethodNotAllowed {\n\t\tt.Errorf(\"Status = %d, want %d\", resp.StatusCode, http.StatusMethodNotAllowed)\n\t}\n}\n\nfunc TestNoUpgrade(t *testing.T) {\n\tt.Parallel()\n\ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tws, err := cstUpgrader.Upgrade(w, r, nil)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"handshake succeeded, expect fail\")\n\t\t\tws.Close()\n\t\t}\n\t}))\n\tdefer s.Close()\n\n\treq, err := http.NewRequest(http.MethodGet, s.URL, strings.NewReader(\"\"))\n\tif err != nil {\n\t\tt.Fatalf(\"NewRequest returned error %v\", err)\n\t}\n\treq.Header.Set(\"Connection\", \"upgrade\")\n\treq.Header.Set(\"Sec-Websocket-Version\", \"13\")\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Fatalf(\"Do returned error %v\", err)\n\t}\n\tresp.Body.Close()\n\tif u := resp.Header.Get(\"Upgrade\"); u != \"websocket\" {\n\t\tt.Errorf(\"Upgrade response header is %q, want %q\", u, \"websocket\")\n\t}\n\tif resp.StatusCode != http.StatusUpgradeRequired {\n\t\tt.Errorf(\"Status = %d, want %d\", resp.StatusCode, http.StatusUpgradeRequired)\n\t}\n}\n\nfunc TestDialExtraTokensInRespHeaders(t *testing.T) {\n\ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tchallengeKey := r.Header.Get(\"Sec-Websocket-Key\")\n\t\tw.Header().Set(\"Upgrade\", \"foo, websocket\")\n\t\tw.Header().Set(\"Connection\", \"upgrade, keep-alive\")\n\t\tw.Header().Set(\"Sec-Websocket-Accept\", computeAcceptKey(challengeKey))\n\t\tw.WriteHeader(101)\n\t}))\n\tdefer s.Close()\n\n\tws, _, err := cstDialer.Dial(makeWsProto(s.URL), nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n}\n\nfunc TestHandshake(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tws, resp, err := cstDialer.Dial(s.URL, http.Header{\"Origin\": {s.URL}})\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\n\tvar sessionID string\n\tfor _, c := range resp.Cookies() {\n\t\tif c.Name == \"sessionID\" {\n\t\t\tsessionID = c.Value\n\t\t}\n\t}\n\tif sessionID != \"1234\" {\n\t\tt.Error(\"Set-Cookie not received from the server.\")\n\t}\n\n\tif ws.Subprotocol() != \"p1\" {\n\t\tt.Errorf(\"ws.Subprotocol() = %s, want p1\", ws.Subprotocol())\n\t}\n\tsendRecv(t, ws)\n}\n\nfunc TestRespOnBadHandshake(t *testing.T) {\n\tconst expectedStatus = http.StatusGone\n\tconst expectedBody = \"This is the response body.\"\n\n\ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(expectedStatus)\n\t\t_, _ = io.WriteString(w, expectedBody)\n\t}))\n\tdefer s.Close()\n\n\tws, resp, err := cstDialer.Dial(makeWsProto(s.URL), nil)\n\tif err == nil {\n\t\tws.Close()\n\t\tt.Fatalf(\"Dial: nil\")\n\t}\n\n\tif resp == nil {\n\t\tt.Fatalf(\"resp=nil, err=%v\", err)\n\t}\n\n\tif resp.StatusCode != expectedStatus {\n\t\tt.Errorf(\"resp.StatusCode=%d, want %d\", resp.StatusCode, expectedStatus)\n\t}\n\n\tp, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\tt.Fatalf(\"ReadFull(resp.Body) returned error %v\", err)\n\t}\n\n\tif string(p) != expectedBody {\n\t\tt.Errorf(\"resp.Body=%s, want %s\", p, expectedBody)\n\t}\n}\n\ntype testLogWriter struct {\n\tt *testing.T\n}\n\nfunc (w testLogWriter) Write(p []byte) (int, error) {\n\tw.t.Logf(\"%s\", p)\n\treturn len(p), nil\n}\n\n// TestHost tests handling of host names and confirms that it matches net/http.\nfunc TestHost(t *testing.T) {\n\n\tupgrader := Upgrader{}\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif IsWebSocketUpgrade(r) {\n\t\t\tc, err := upgrader.Upgrade(w, r, http.Header{\"X-Test-Host\": {r.Host}})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tc.Close()\n\t\t} else {\n\t\t\tw.Header().Set(\"X-Test-Host\", r.Host)\n\t\t}\n\t})\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\ttlsServer := httptest.NewTLSServer(handler)\n\tdefer tlsServer.Close()\n\n\taddrs := map[*httptest.Server]string{server: server.Listener.Addr().String(), tlsServer: tlsServer.Listener.Addr().String()}\n\twsProtos := map[*httptest.Server]string{server: \"ws://\", tlsServer: \"wss://\"}\n\thttpProtos := map[*httptest.Server]string{server: \"http://\", tlsServer: \"https://\"}\n\n\t// Avoid log noise from net/http server by logging to testing.T\n\tserver.Config.ErrorLog = log.New(testLogWriter{t}, \"\", 0)\n\ttlsServer.Config.ErrorLog = server.Config.ErrorLog\n\n\tcas := rootCAs(t, tlsServer)\n\n\ttests := []struct {\n\t\tfail               bool             // true if dial / get should fail\n\t\tserver             *httptest.Server // server to use\n\t\turl                string           // host for request URI\n\t\theader             string           // optional request host header\n\t\ttls                string           // optional host for tls ServerName\n\t\twantAddr           string           // expected host for dial\n\t\twantHeader         string           // expected request header on server\n\t\tinsecureSkipVerify bool\n\t}{\n\t\t{\n\t\t\tserver:     server,\n\t\t\turl:        addrs[server],\n\t\t\twantAddr:   addrs[server],\n\t\t\twantHeader: addrs[server],\n\t\t},\n\t\t{\n\t\t\tserver:     tlsServer,\n\t\t\turl:        addrs[tlsServer],\n\t\t\twantAddr:   addrs[tlsServer],\n\t\t\twantHeader: addrs[tlsServer],\n\t\t},\n\n\t\t{\n\t\t\tserver:     server,\n\t\t\turl:        addrs[server],\n\t\t\theader:     \"badhost.com\",\n\t\t\twantAddr:   addrs[server],\n\t\t\twantHeader: \"badhost.com\",\n\t\t},\n\t\t{\n\t\t\tserver:     tlsServer,\n\t\t\turl:        addrs[tlsServer],\n\t\t\theader:     \"badhost.com\",\n\t\t\twantAddr:   addrs[tlsServer],\n\t\t\twantHeader: \"badhost.com\",\n\t\t},\n\n\t\t{\n\t\t\tserver:     server,\n\t\t\turl:        \"example.com\",\n\t\t\theader:     \"badhost.com\",\n\t\t\twantAddr:   \"example.com:80\",\n\t\t\twantHeader: \"badhost.com\",\n\t\t},\n\t\t{\n\t\t\tserver:     tlsServer,\n\t\t\turl:        \"example.com\",\n\t\t\theader:     \"badhost.com\",\n\t\t\twantAddr:   \"example.com:443\",\n\t\t\twantHeader: \"badhost.com\",\n\t\t},\n\n\t\t{\n\t\t\tserver:     server,\n\t\t\turl:        \"badhost.com\",\n\t\t\theader:     \"example.com\",\n\t\t\twantAddr:   \"badhost.com:80\",\n\t\t\twantHeader: \"example.com\",\n\t\t},\n\t\t{\n\t\t\tfail:     true,\n\t\t\tserver:   tlsServer,\n\t\t\turl:      \"badhost.com\",\n\t\t\theader:   \"example.com\",\n\t\t\twantAddr: \"badhost.com:443\",\n\t\t},\n\t\t{\n\t\t\tserver:             tlsServer,\n\t\t\turl:                \"badhost.com\",\n\t\t\tinsecureSkipVerify: true,\n\t\t\twantAddr:           \"badhost.com:443\",\n\t\t\twantHeader:         \"badhost.com\",\n\t\t},\n\t\t{\n\t\t\tserver:     tlsServer,\n\t\t\turl:        \"badhost.com\",\n\t\t\ttls:        \"example.com\",\n\t\t\twantAddr:   \"badhost.com:443\",\n\t\t\twantHeader: \"badhost.com\",\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\n\t\ttls := &tls.Config{\n\t\t\tRootCAs:            cas,\n\t\t\tServerName:         tt.tls,\n\t\t\tInsecureSkipVerify: tt.insecureSkipVerify,\n\t\t}\n\n\t\tvar gotAddr string\n\t\tdialer := Dialer{\n\t\t\tNetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\tgotAddr = addr\n\t\t\t\treturn net.Dial(network, addrs[tt.server])\n\t\t\t},\n\t\t\tTLSClientConfig: tls,\n\t\t}\n\n\t\t// Test websocket dial\n\n\t\th := http.Header{}\n\t\tif tt.header != \"\" {\n\t\t\th.Set(\"Host\", tt.header)\n\t\t}\n\t\tc, resp, err := dialer.Dial(wsProtos[tt.server]+tt.url+\"/\", h)\n\t\tif err == nil {\n\t\t\tc.Close()\n\t\t}\n\n\t\tcheck := func(protos map[*httptest.Server]string) {\n\t\t\tname := fmt.Sprintf(\"%d: %s%s/ header[Host]=%q, tls.ServerName=%q\", i+1, protos[tt.server], tt.url, tt.header, tt.tls)\n\t\t\tif gotAddr != tt.wantAddr {\n\t\t\t\tt.Errorf(\"%s: got addr %s, want %s\", name, gotAddr, tt.wantAddr)\n\t\t\t}\n\t\t\tswitch {\n\t\t\tcase tt.fail && err == nil:\n\t\t\t\tt.Errorf(\"%s: unexpected success\", name)\n\t\t\tcase !tt.fail && err != nil:\n\t\t\t\tt.Errorf(\"%s: unexpected error %v\", name, err)\n\t\t\tcase !tt.fail && err == nil:\n\t\t\t\tif gotHost := resp.Header.Get(\"X-Test-Host\"); gotHost != tt.wantHeader {\n\t\t\t\t\tt.Errorf(\"%s: got host %s, want %s\", name, gotHost, tt.wantHeader)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcheck(wsProtos)\n\n\t\t// Confirm that net/http has same result\n\n\t\ttransport := &http.Transport{\n\t\t\tDial:            dialer.NetDial,\n\t\t\tTLSClientConfig: dialer.TLSClientConfig,\n\t\t}\n\t\treq, _ := http.NewRequest(http.MethodGet, httpProtos[tt.server]+tt.url+\"/\", nil)\n\t\tif tt.header != \"\" {\n\t\t\treq.Host = tt.header\n\t\t}\n\t\tclient := &http.Client{Transport: transport}\n\t\tresp, err = client.Do(req)\n\t\tif err == nil {\n\t\t\tresp.Body.Close()\n\t\t}\n\t\ttransport.CloseIdleConnections()\n\t\tcheck(httpProtos)\n\t}\n}\n\nfunc TestDialCompression(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tdialer := cstDialer\n\tdialer.EnableCompression = true\n\tws, _, err := dialer.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestSocksProxyDial(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\tproxyListener, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"listen failed: %v\", err)\n\t}\n\tdefer proxyListener.Close()\n\tgo func() {\n\t\tc1, err := proxyListener.Accept()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"proxy accept failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer c1.Close()\n\n\t\t_ = c1.SetDeadline(time.Now().Add(30 * time.Second))\n\n\t\tbuf := make([]byte, 32)\n\t\tif _, err := io.ReadFull(c1, buf[:3]); err != nil {\n\t\t\tt.Errorf(\"read failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif want := []byte{5, 1, 0}; !bytes.Equal(want, buf[:len(want)]) {\n\t\t\tt.Errorf(\"read %x, want %x\", buf[:len(want)], want)\n\t\t}\n\t\tif _, err := c1.Write([]byte{5, 0}); err != nil {\n\t\t\tt.Errorf(\"write failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif _, err := io.ReadFull(c1, buf[:10]); err != nil {\n\t\t\tt.Errorf(\"read failed: %v\", err)\n\t\t\treturn\n\t\t}\n\t\tif want := []byte{5, 1, 0, 1}; !bytes.Equal(want, buf[:len(want)]) {\n\t\t\tt.Errorf(\"read %x, want %x\", buf[:len(want)], want)\n\t\t\treturn\n\t\t}\n\t\tbuf[1] = 0\n\t\tif _, err := c1.Write(buf[:10]); err != nil {\n\t\t\tt.Errorf(\"write failed: %v\", err)\n\t\t\treturn\n\t\t}\n\n\t\tip := net.IP(buf[4:8])\n\t\tport := binary.BigEndian.Uint16(buf[8:10])\n\n\t\tc2, err := net.DialTCP(\"tcp\", nil, &net.TCPAddr{IP: ip, Port: int(port)})\n\t\tif err != nil {\n\t\t\tt.Errorf(\"dial failed; %v\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer c2.Close()\n\t\tdone := make(chan struct{})\n\t\tgo func() {\n\t\t\t_, _ = io.Copy(c1, c2)\n\t\t\tclose(done)\n\t\t}()\n\t\t_, _ = io.Copy(c2, c1)\n\t\t<-done\n\t}()\n\n\tpurl, err := url.Parse(\"socks5://\" + proxyListener.Addr().String())\n\tif err != nil {\n\t\tt.Fatalf(\"parse failed: %v\", err)\n\t}\n\n\tcstDialer := cstDialer // make local copy for modification on next line.\n\tcstDialer.Proxy = http.ProxyURL(purl)\n\n\tws, _, err := cstDialer.Dial(s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestTracingDialWithContext(t *testing.T) {\n\n\tvar headersWrote, requestWrote, getConn, gotConn, connectDone, gotFirstResponseByte bool\n\ttrace := &httptrace.ClientTrace{\n\t\tWroteHeaders: func() {\n\t\t\theadersWrote = true\n\t\t},\n\t\tWroteRequest: func(httptrace.WroteRequestInfo) {\n\t\t\trequestWrote = true\n\t\t},\n\t\tGetConn: func(hostPort string) {\n\t\t\tgetConn = true\n\t\t},\n\t\tGotConn: func(info httptrace.GotConnInfo) {\n\t\t\tgotConn = true\n\t\t},\n\t\tConnectDone: func(network, addr string, err error) {\n\t\t\tconnectDone = true\n\t\t},\n\t\tGotFirstResponseByte: func() {\n\t\t\tgotFirstResponseByte = true\n\t\t},\n\t}\n\tctx := httptrace.WithClientTrace(context.Background(), trace)\n\n\ts := newTLSServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, s.Server)}\n\n\tws, _, err := d.DialContext(ctx, s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\n\tif !headersWrote {\n\t\tt.Fatal(\"Headers was not written\")\n\t}\n\tif !requestWrote {\n\t\tt.Fatal(\"Request was not written\")\n\t}\n\tif !getConn {\n\t\tt.Fatal(\"getConn was not called\")\n\t}\n\tif !gotConn {\n\t\tt.Fatal(\"gotConn was not called\")\n\t}\n\tif !connectDone {\n\t\tt.Fatal(\"connectDone was not called\")\n\t}\n\tif !gotFirstResponseByte {\n\t\tt.Fatal(\"GotFirstResponseByte was not called\")\n\t}\n\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\nfunc TestEmptyTracingDialWithContext(t *testing.T) {\n\n\ttrace := &httptrace.ClientTrace{}\n\tctx := httptrace.WithClientTrace(context.Background(), trace)\n\n\ts := newTLSServer(t)\n\tdefer s.Close()\n\n\td := cstDialer\n\td.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, s.Server)}\n\n\tws, _, err := d.DialContext(ctx, s.URL, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"Dial: %v\", err)\n\t}\n\n\tdefer ws.Close()\n\tsendRecv(t, ws)\n}\n\n// TestNetDialConnect tests selection of dial method between NetDial, NetDialContext, NetDialTLS or NetDialTLSContext\nfunc TestNetDialConnect(t *testing.T) {\n\n\tupgrader := Upgrader{}\n\thandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif IsWebSocketUpgrade(r) {\n\t\t\tc, err := upgrader.Upgrade(w, r, http.Header{\"X-Test-Host\": {r.Host}})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tc.Close()\n\t\t} else {\n\t\t\tw.Header().Set(\"X-Test-Host\", r.Host)\n\t\t}\n\t})\n\n\tserver := httptest.NewServer(handler)\n\tdefer server.Close()\n\n\ttlsServer := httptest.NewTLSServer(handler)\n\tdefer tlsServer.Close()\n\n\ttestUrls := map[*httptest.Server]string{\n\t\tserver:    \"ws://\" + server.Listener.Addr().String() + \"/\",\n\t\ttlsServer: \"wss://\" + tlsServer.Listener.Addr().String() + \"/\",\n\t}\n\n\tcas := rootCAs(t, tlsServer)\n\ttlsConfig := &tls.Config{\n\t\tRootCAs:            cas,\n\t\tServerName:         \"example.com\",\n\t\tInsecureSkipVerify: false,\n\t}\n\n\ttests := []struct {\n\t\tname              string\n\t\tserver            *httptest.Server // server to use\n\t\tnetDial           func(network, addr string) (net.Conn, error)\n\t\tnetDialContext    func(ctx context.Context, network, addr string) (net.Conn, error)\n\t\tnetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)\n\t\ttlsClientConfig   *tls.Config\n\t}{\n\n\t\t{\n\t\t\tname:   \"HTTP server, all NetDial* defined, shall use NetDialContext\",\n\t\t\tserver: server,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDial should not be called\")\n\t\t\t},\n\t\t\tnetDialContext: func(_ context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t\tnetDialTLSContext: func(_ context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDialTLSContext should not be called\")\n\t\t\t},\n\t\t\ttlsClientConfig: nil,\n\t\t},\n\t\t{\n\t\t\tname:              \"HTTP server, all NetDial* undefined\",\n\t\t\tserver:            server,\n\t\t\tnetDial:           nil,\n\t\t\tnetDialContext:    nil,\n\t\t\tnetDialTLSContext: nil,\n\t\t\ttlsClientConfig:   nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"HTTP server, NetDialContext undefined, shall fallback to NetDial\",\n\t\t\tserver: server,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t\tnetDialContext: nil,\n\t\t\tnetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDialTLSContext should not be called\")\n\t\t\t},\n\t\t\ttlsClientConfig: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"HTTPS server, all NetDial* defined, shall use NetDialTLSContext\",\n\t\t\tserver: tlsServer,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDial should not be called\")\n\t\t\t},\n\t\t\tnetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDialContext should not be called\")\n\t\t\t},\n\t\t\tnetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\tnetConn, err := net.Dial(network, addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\ttlsConn := tls.Client(netConn, tlsConfig)\n\t\t\t\terr = tlsConn.Handshake()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn tlsConn, nil\n\t\t\t},\n\t\t\ttlsClientConfig: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"HTTPS server, NetDialTLSContext undefined, shall fallback to NetDialContext and do handshake\",\n\t\t\tserver: tlsServer,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDial should not be called\")\n\t\t\t},\n\t\t\tnetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t\tnetDialTLSContext: nil,\n\t\t\ttlsClientConfig:   tlsConfig,\n\t\t},\n\t\t{\n\t\t\tname:   \"HTTPS server, NetDialTLSContext and NetDialContext undefined, shall fallback to NetDial and do handshake\",\n\t\t\tserver: tlsServer,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn net.Dial(network, addr)\n\t\t\t},\n\t\t\tnetDialContext:    nil,\n\t\t\tnetDialTLSContext: nil,\n\t\t\ttlsClientConfig:   tlsConfig,\n\t\t},\n\t\t{\n\t\t\tname:              \"HTTPS server, all NetDial* undefined\",\n\t\t\tserver:            tlsServer,\n\t\t\tnetDial:           nil,\n\t\t\tnetDialContext:    nil,\n\t\t\tnetDialTLSContext: nil,\n\t\t\ttlsClientConfig:   tlsConfig,\n\t\t},\n\t\t{\n\t\t\tname:   \"HTTPS server, all NetDialTLSContext defined, dummy TlsClientConfig defined, shall not do handshake\",\n\t\t\tserver: tlsServer,\n\t\t\tnetDial: func(network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDial should not be called\")\n\t\t\t},\n\t\t\tnetDialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn nil, errors.New(\"NetDialContext should not be called\")\n\t\t\t},\n\t\t\tnetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\tnetConn, err := net.Dial(network, addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\ttlsConn := tls.Client(netConn, tlsConfig)\n\t\t\t\terr = tlsConn.Handshake()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn tlsConn, nil\n\t\t\t},\n\t\t\ttlsClientConfig: &tls.Config{\n\t\t\t\tRootCAs:            nil,\n\t\t\t\tServerName:         \"badserver.com\",\n\t\t\t\tInsecureSkipVerify: false,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\tdialer := Dialer{\n\t\t\tNetDial:           tc.netDial,\n\t\t\tNetDialContext:    tc.netDialContext,\n\t\t\tNetDialTLSContext: tc.netDialTLSContext,\n\t\t\tTLSClientConfig:   tc.tlsClientConfig,\n\t\t}\n\n\t\t// Test websocket dial\n\t\tc, _, err := dialer.Dial(testUrls[tc.server], nil)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"FAILED %s, err: %s\", tc.name, err.Error())\n\t\t} else {\n\t\t\tc.Close()\n\t\t}\n\t}\n}\nfunc TestNextProtos(t *testing.T) {\n\tts := httptest.NewUnstartedServer(\n\t\thttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),\n\t)\n\tts.EnableHTTP2 = true\n\tts.StartTLS()\n\tdefer ts.Close()\n\n\td := Dialer{\n\t\tTLSClientConfig: ts.Client().Transport.(*http.Transport).TLSClientConfig,\n\t}\n\n\tr, err := ts.Client().Get(ts.URL)\n\tif err != nil {\n\t\tt.Fatalf(\"Get: %v\", err)\n\t}\n\tr.Body.Close()\n\n\t// Asserts that Dialer.TLSClientConfig.NextProtos contains \"h2\"\n\t// after the Client.Get call from net/http above.\n\tvar containsHTTP2 bool = false\n\tfor _, proto := range d.TLSClientConfig.NextProtos {\n\t\tif proto == \"h2\" {\n\t\t\tcontainsHTTP2 = true\n\t\t}\n\t}\n\tif !containsHTTP2 {\n\t\tt.Fatalf(\"Dialer.TLSClientConfig.NextProtos does not contain \\\"h2\\\"\")\n\t}\n\n\t_, _, err = d.Dial(makeWsProto(ts.URL), nil)\n\tif err == nil {\n\t\tt.Fatalf(\"Dial succeeded, expect fail \")\n\t}\n}\n\ntype dataBeforeHandshakeResponseWriter struct {\n\thttp.ResponseWriter\n}\n\ntype dataBeforeHandshakeConnection struct {\n\tnet.Conn\n\tio.Reader\n}\n\nfunc (c *dataBeforeHandshakeConnection) Read(p []byte) (int, error) {\n\treturn c.Reader.Read(p)\n}\n\nfunc (w dataBeforeHandshakeResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\t// Example single-frame masked text message from section 5.7 of the RFC.\n\tmessage := []byte{0x81, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58}\n\tn := len(message) / 2\n\n\tc, rw, err := http.NewResponseController(w.ResponseWriter).Hijack()\n\tif rw != nil {\n\t\t// Load first part of message into bufio.Reader. If the websocket\n\t\t// connection reads more than n bytes from the bufio.Reader, then the\n\t\t// test will fail with an unexpected EOF error.\n\t\trw.Reader.Reset(bytes.NewReader(message[:n]))\n\t\trw.Reader.Peek(n)\n\t}\n\tif c != nil {\n\t\t// Inject second part of message before data read from the network connection.\n\t\tc = &dataBeforeHandshakeConnection{\n\t\t\tConn:   c,\n\t\t\tReader: io.MultiReader(bytes.NewReader(message[n:]), c),\n\t\t}\n\t}\n\treturn c, rw, err\n}\n\nfunc TestDataReceivedBeforeHandshake(t *testing.T) {\n\ts := newServer(t)\n\tdefer s.Close()\n\n\torigHandler := s.Server.Config.Handler\n\ts.Server.Config.Handler = http.HandlerFunc(\n\t\tfunc(w http.ResponseWriter, r *http.Request) {\n\t\t\torigHandler.ServeHTTP(dataBeforeHandshakeResponseWriter{w}, r)\n\t\t})\n\n\tfor _, readBufferSize := range []int{0, 1024} {\n\t\tt.Run(fmt.Sprintf(\"ReadBufferSize=%d\", readBufferSize), func(t *testing.T) {\n\t\t\tdialer := cstDialer\n\t\t\tdialer.ReadBufferSize = readBufferSize\n\t\t\tws, _, err := cstDialer.Dial(s.URL, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Dial: %v\", err)\n\t\t\t}\n\t\t\tdefer ws.Close()\n\t\t\t_, m, err := ws.ReadMessage()\n\t\t\tif err != nil || string(m) != \"Hello\" {\n\t\t\t\tt.Fatalf(\"ReadMessage() = %q, %v, want \\\"Hello\\\", nil\", m, err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "client_test.go",
    "content": "// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n)\n\nvar hostPortNoPortTests = []struct {\n\tu                    *url.URL\n\thostPort, hostNoPort string\n}{\n\t{&url.URL{Scheme: \"ws\", Host: \"example.com\"}, \"example.com:80\", \"example.com\"},\n\t{&url.URL{Scheme: \"wss\", Host: \"example.com\"}, \"example.com:443\", \"example.com\"},\n\t{&url.URL{Scheme: \"ws\", Host: \"example.com:7777\"}, \"example.com:7777\", \"example.com\"},\n\t{&url.URL{Scheme: \"wss\", Host: \"example.com:7777\"}, \"example.com:7777\", \"example.com\"},\n}\n\nfunc TestHostPortNoPort(t *testing.T) {\n\tfor _, tt := range hostPortNoPortTests {\n\t\thostPort, hostNoPort := hostPortNoPort(tt.u)\n\t\tif hostPort != tt.hostPort {\n\t\t\tt.Errorf(\"hostPortNoPort(%v) returned hostPort %q, want %q\", tt.u, hostPort, tt.hostPort)\n\t\t}\n\t\tif hostNoPort != tt.hostNoPort {\n\t\t\tt.Errorf(\"hostPortNoPort(%v) returned hostNoPort %q, want %q\", tt.u, hostNoPort, tt.hostNoPort)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "compression.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"compress/flate\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n)\n\nconst (\n\tminCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6\n\tmaxCompressionLevel     = flate.BestCompression\n\tdefaultCompressionLevel = 1\n)\n\nvar (\n\tflateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool\n\tflateReaderPool  = sync.Pool{New: func() interface{} {\n\t\treturn flate.NewReader(nil)\n\t}}\n)\n\nfunc decompressNoContextTakeover(r io.Reader) io.ReadCloser {\n\tconst tail =\n\t// Add four bytes as specified in RFC\n\t\"\\x00\\x00\\xff\\xff\" +\n\t\t// Add final block to squelch unexpected EOF error from flate reader.\n\t\t\"\\x01\\x00\\x00\\xff\\xff\"\n\n\tfr, _ := flateReaderPool.Get().(io.ReadCloser)\n\tmr := io.MultiReader(r, strings.NewReader(tail))\n\tif err := fr.(flate.Resetter).Reset(mr, nil); err != nil {\n\t\t// Reset never fails, but handle error in case that changes.\n\t\tfr = flate.NewReader(mr)\n\t}\n\treturn &flateReadWrapper{fr}\n}\n\nfunc isValidCompressionLevel(level int) bool {\n\treturn minCompressionLevel <= level && level <= maxCompressionLevel\n}\n\nfunc compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {\n\tp := &flateWriterPools[level-minCompressionLevel]\n\ttw := &truncWriter{w: w}\n\tfw, _ := p.Get().(*flate.Writer)\n\tif fw == nil {\n\t\tfw, _ = flate.NewWriter(tw, level)\n\t} else {\n\t\tfw.Reset(tw)\n\t}\n\treturn &flateWriteWrapper{fw: fw, tw: tw, p: p}\n}\n\n// truncWriter is an io.Writer that writes all but the last four bytes of the\n// stream to another io.Writer.\ntype truncWriter struct {\n\tw io.WriteCloser\n\tn int\n\tp [4]byte\n}\n\nfunc (w *truncWriter) Write(p []byte) (int, error) {\n\tn := 0\n\n\t// fill buffer first for simplicity.\n\tif w.n < len(w.p) {\n\t\tn = copy(w.p[w.n:], p)\n\t\tp = p[n:]\n\t\tw.n += n\n\t\tif len(p) == 0 {\n\t\t\treturn n, nil\n\t\t}\n\t}\n\n\tm := len(p)\n\tif m > len(w.p) {\n\t\tm = len(w.p)\n\t}\n\n\tif nn, err := w.w.Write(w.p[:m]); err != nil {\n\t\treturn n + nn, err\n\t}\n\n\tcopy(w.p[:], w.p[m:])\n\tcopy(w.p[len(w.p)-m:], p[len(p)-m:])\n\tnn, err := w.w.Write(p[:len(p)-m])\n\treturn n + nn, err\n}\n\ntype flateWriteWrapper struct {\n\tfw *flate.Writer\n\ttw *truncWriter\n\tp  *sync.Pool\n}\n\nfunc (w *flateWriteWrapper) Write(p []byte) (int, error) {\n\tif w.fw == nil {\n\t\treturn 0, errWriteClosed\n\t}\n\treturn w.fw.Write(p)\n}\n\nfunc (w *flateWriteWrapper) Close() error {\n\tif w.fw == nil {\n\t\treturn errWriteClosed\n\t}\n\terr1 := w.fw.Flush()\n\tw.p.Put(w.fw)\n\tw.fw = nil\n\tif w.tw.p != [4]byte{0, 0, 0xff, 0xff} {\n\t\treturn errors.New(\"websocket: internal error, unexpected bytes at end of flate stream\")\n\t}\n\terr2 := w.tw.w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\ntype flateReadWrapper struct {\n\tfr io.ReadCloser\n}\n\nfunc (r *flateReadWrapper) Read(p []byte) (int, error) {\n\tif r.fr == nil {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\tn, err := r.fr.Read(p)\n\tif err == io.EOF {\n\t\t// Preemptively place the reader back in the pool. This helps with\n\t\t// scenarios where the application does not call NextReader() soon after\n\t\t// this final read.\n\t\tr.Close()\n\t}\n\treturn n, err\n}\n\nfunc (r *flateReadWrapper) Close() error {\n\tif r.fr == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\terr := r.fr.Close()\n\tflateReaderPool.Put(r.fr)\n\tr.fr = nil\n\treturn err\n}\n"
  },
  {
    "path": "compression_test.go",
    "content": "package websocket\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n)\n\ntype nopCloser struct{ io.Writer }\n\nfunc (nopCloser) Close() error { return nil }\n\nfunc TestTruncWriter(t *testing.T) {\n\tconst data = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijlkmnopqrstuvwxyz987654321\"\n\tfor n := 1; n <= 10; n++ {\n\t\tvar b bytes.Buffer\n\t\tw := &truncWriter{w: nopCloser{&b}}\n\t\tp := []byte(data)\n\t\tfor len(p) > 0 {\n\t\t\tm := len(p)\n\t\t\tif m > n {\n\t\t\t\tm = n\n\t\t\t}\n\t\t\t_, _ = w.Write(p[:m])\n\t\t\tp = p[m:]\n\t\t}\n\t\tif b.String() != data[:len(data)-len(w.p)] {\n\t\t\tt.Errorf(\"%d: %q\", n, b.String())\n\t\t}\n\t}\n}\n\nfunc textMessages(num int) [][]byte {\n\tmessages := make([][]byte, num)\n\tfor i := 0; i < num; i++ {\n\t\tmsg := fmt.Sprintf(\"planet: %d, country: %d, city: %d, street: %d\", i, i, i, i)\n\t\tmessages[i] = []byte(msg)\n\t}\n\treturn messages\n}\n\nfunc BenchmarkWriteNoCompression(b *testing.B) {\n\tw := io.Discard\n\tc := newTestConn(nil, w, false)\n\tmessages := textMessages(100)\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_ = c.WriteMessage(TextMessage, messages[i%len(messages)])\n\t}\n\tb.ReportAllocs()\n}\n\nfunc BenchmarkWriteWithCompression(b *testing.B) {\n\tw := io.Discard\n\tc := newTestConn(nil, w, false)\n\tmessages := textMessages(100)\n\tc.enableWriteCompression = true\n\tc.newCompressionWriter = compressNoContextTakeover\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\t_ = c.WriteMessage(TextMessage, messages[i%len(messages)])\n\t}\n\tb.ReportAllocs()\n}\n\nfunc TestValidCompressionLevel(t *testing.T) {\n\tc := newTestConn(nil, nil, false)\n\tfor _, level := range []int{minCompressionLevel - 1, maxCompressionLevel + 1} {\n\t\tif err := c.SetCompressionLevel(level); err == nil {\n\t\t\tt.Errorf(\"no error for level %d\", level)\n\t\t}\n\t}\n\tfor _, level := range []int{minCompressionLevel, maxCompressionLevel} {\n\t\tif err := c.SetCompressionLevel(level); err != nil {\n\t\t\tt.Errorf(\"error for level %d\", level)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "conn.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\t// Frame header byte 0 bits from Section 5.2 of RFC 6455\n\tfinalBit = 1 << 7\n\trsv1Bit  = 1 << 6\n\trsv2Bit  = 1 << 5\n\trsv3Bit  = 1 << 4\n\n\t// Frame header byte 1 bits from Section 5.2 of RFC 6455\n\tmaskBit = 1 << 7\n\n\tmaxFrameHeaderSize         = 2 + 8 + 4 // Fixed header + length + mask\n\tmaxControlFramePayloadSize = 125\n\n\twriteWait = time.Second\n\n\tdefaultReadBufferSize  = 4096\n\tdefaultWriteBufferSize = 4096\n\n\tcontinuationFrame = 0\n\tnoFrame           = -1\n)\n\n// Close codes defined in RFC 6455, section 11.7.\nconst (\n\tCloseNormalClosure           = 1000\n\tCloseGoingAway               = 1001\n\tCloseProtocolError           = 1002\n\tCloseUnsupportedData         = 1003\n\tCloseNoStatusReceived        = 1005\n\tCloseAbnormalClosure         = 1006\n\tCloseInvalidFramePayloadData = 1007\n\tClosePolicyViolation         = 1008\n\tCloseMessageTooBig           = 1009\n\tCloseMandatoryExtension      = 1010\n\tCloseInternalServerErr       = 1011\n\tCloseServiceRestart          = 1012\n\tCloseTryAgainLater           = 1013\n\tCloseTLSHandshake            = 1015\n)\n\n// The message types are defined in RFC 6455, section 11.8.\nconst (\n\t// TextMessage denotes a text data message. The text message payload is\n\t// interpreted as UTF-8 encoded text data.\n\tTextMessage = 1\n\n\t// BinaryMessage denotes a binary data message.\n\tBinaryMessage = 2\n\n\t// CloseMessage denotes a close control message. The optional message\n\t// payload contains a numeric code and text. Use the FormatCloseMessage\n\t// function to format a close message payload.\n\tCloseMessage = 8\n\n\t// PingMessage denotes a ping control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPingMessage = 9\n\n\t// PongMessage denotes a pong control message. The optional message payload\n\t// is UTF-8 encoded text.\n\tPongMessage = 10\n)\n\n// ErrCloseSent is returned when the application writes a message to the\n// connection after sending a close message.\nvar ErrCloseSent = errors.New(\"websocket: close sent\")\n\n// ErrReadLimit is returned when reading a message that is larger than the\n// read limit set for the connection.\nvar ErrReadLimit = errors.New(\"websocket: read limit exceeded\")\n\n// netError satisfies the net Error interface.\ntype netError struct {\n\tmsg       string\n\ttemporary bool\n\ttimeout   bool\n}\n\nfunc (e *netError) Error() string   { return e.msg }\nfunc (e *netError) Temporary() bool { return e.temporary }\nfunc (e *netError) Timeout() bool   { return e.timeout }\n\n// CloseError represents a close message.\ntype CloseError struct {\n\t// Code is defined in RFC 6455, section 11.7.\n\tCode int\n\n\t// Text is the optional text payload.\n\tText string\n}\n\nfunc (e *CloseError) Error() string {\n\ts := []byte(\"websocket: close \")\n\ts = strconv.AppendInt(s, int64(e.Code), 10)\n\tswitch e.Code {\n\tcase CloseNormalClosure:\n\t\ts = append(s, \" (normal)\"...)\n\tcase CloseGoingAway:\n\t\ts = append(s, \" (going away)\"...)\n\tcase CloseProtocolError:\n\t\ts = append(s, \" (protocol error)\"...)\n\tcase CloseUnsupportedData:\n\t\ts = append(s, \" (unsupported data)\"...)\n\tcase CloseNoStatusReceived:\n\t\ts = append(s, \" (no status)\"...)\n\tcase CloseAbnormalClosure:\n\t\ts = append(s, \" (abnormal closure)\"...)\n\tcase CloseInvalidFramePayloadData:\n\t\ts = append(s, \" (invalid payload data)\"...)\n\tcase ClosePolicyViolation:\n\t\ts = append(s, \" (policy violation)\"...)\n\tcase CloseMessageTooBig:\n\t\ts = append(s, \" (message too big)\"...)\n\tcase CloseMandatoryExtension:\n\t\ts = append(s, \" (mandatory extension missing)\"...)\n\tcase CloseInternalServerErr:\n\t\ts = append(s, \" (internal server error)\"...)\n\tcase CloseTLSHandshake:\n\t\ts = append(s, \" (TLS handshake error)\"...)\n\t}\n\tif e.Text != \"\" {\n\t\ts = append(s, \": \"...)\n\t\ts = append(s, e.Text...)\n\t}\n\treturn string(s)\n}\n\n// IsCloseError returns boolean indicating whether the error is a *CloseError\n// with one of the specified codes.\nfunc IsCloseError(err error, codes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range codes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// IsUnexpectedCloseError returns boolean indicating whether the error is a\n// *CloseError with a code not in the list of expected codes.\nfunc IsUnexpectedCloseError(err error, expectedCodes ...int) bool {\n\tif e, ok := err.(*CloseError); ok {\n\t\tfor _, code := range expectedCodes {\n\t\t\tif e.Code == code {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\nvar (\n\terrWriteTimeout        = &netError{msg: \"websocket: write timeout\", timeout: true, temporary: true}\n\terrUnexpectedEOF       = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}\n\terrBadWriteOpCode      = errors.New(\"websocket: bad write message type\")\n\terrWriteClosed         = errors.New(\"websocket: write closed\")\n\terrInvalidControlFrame = errors.New(\"websocket: invalid control frame\")\n)\n\n// maskRand is an io.Reader for generating mask bytes. The reader is initialized\n// to crypto/rand Reader. Tests swap the reader to a math/rand reader for\n// reproducible results.\nvar maskRand = rand.Reader\n\n// newMaskKey returns a new 32 bit value for masking client frames.\nfunc newMaskKey() [4]byte {\n\tvar k [4]byte\n\t_, _ = io.ReadFull(maskRand, k[:])\n\treturn k\n}\n\nfunc isControl(frameType int) bool {\n\treturn frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage\n}\n\nfunc isData(frameType int) bool {\n\treturn frameType == TextMessage || frameType == BinaryMessage\n}\n\nvar validReceivedCloseCodes = map[int]bool{\n\t// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number\n\n\tCloseNormalClosure:           true,\n\tCloseGoingAway:               true,\n\tCloseProtocolError:           true,\n\tCloseUnsupportedData:         true,\n\tCloseNoStatusReceived:        false,\n\tCloseAbnormalClosure:         false,\n\tCloseInvalidFramePayloadData: true,\n\tClosePolicyViolation:         true,\n\tCloseMessageTooBig:           true,\n\tCloseMandatoryExtension:      true,\n\tCloseInternalServerErr:       true,\n\tCloseServiceRestart:          true,\n\tCloseTryAgainLater:           true,\n\tCloseTLSHandshake:            false,\n}\n\nfunc isValidReceivedCloseCode(code int) bool {\n\treturn validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)\n}\n\n// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this\n// interface.  The type of the value stored in a pool is not specified.\ntype BufferPool interface {\n\t// Get gets a value from the pool or returns nil if the pool is empty.\n\tGet() interface{}\n\t// Put adds a value to the pool.\n\tPut(interface{})\n}\n\n// writePoolData is the type added to the write buffer pool. This wrapper is\n// used to prevent applications from peeking at and depending on the values\n// added to the pool.\ntype writePoolData struct{ buf []byte }\n\n// The Conn type represents a WebSocket connection.\ntype Conn struct {\n\tconn        net.Conn\n\tisServer    bool\n\tsubprotocol string\n\n\t// Write fields\n\tmu            chan struct{} // used as mutex to protect write to conn\n\twriteBuf      []byte        // frame is constructed in this buffer.\n\twritePool     BufferPool\n\twriteBufSize  int\n\twriteDeadline time.Time\n\twriter        io.WriteCloser // the current writer returned to the application\n\tisWriting     bool           // for best-effort concurrent write detection\n\n\twriteErrMu sync.Mutex\n\twriteErr   error\n\n\tenableWriteCompression bool\n\tcompressionLevel       int\n\tnewCompressionWriter   func(io.WriteCloser, int) io.WriteCloser\n\n\t// Read fields\n\treader  io.ReadCloser // the current reader returned to the application\n\treadErr error\n\tbr      *bufio.Reader\n\t// bytes remaining in current frame.\n\t// set setReadRemaining to safely update this value and prevent overflow\n\treadRemaining int64\n\treadFinal     bool  // true the current message has more frames.\n\treadLength    int64 // Message size.\n\treadLimit     int64 // Maximum message size.\n\treadMaskPos   int\n\treadMaskKey   [4]byte\n\thandlePong    func(string) error\n\thandlePing    func(string) error\n\thandleClose   func(int, string) error\n\treadErrCount  int\n\tmessageReader *messageReader // the current low-level reader\n\n\treadDecompress         bool // whether last read frame had RSV1 set\n\tnewDecompressionReader func(io.Reader) io.ReadCloser\n}\n\nfunc newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn {\n\n\tif br == nil {\n\t\tif readBufferSize == 0 {\n\t\t\treadBufferSize = defaultReadBufferSize\n\t\t} else if readBufferSize < maxControlFramePayloadSize {\n\t\t\t// must be large enough for control frame\n\t\t\treadBufferSize = maxControlFramePayloadSize\n\t\t}\n\t\tbr = bufio.NewReaderSize(conn, readBufferSize)\n\t}\n\n\tif writeBufferSize <= 0 {\n\t\twriteBufferSize = defaultWriteBufferSize\n\t}\n\twriteBufferSize += maxFrameHeaderSize\n\n\tif writeBuf == nil && writeBufferPool == nil {\n\t\twriteBuf = make([]byte, writeBufferSize)\n\t}\n\n\tmu := make(chan struct{}, 1)\n\tmu <- struct{}{}\n\tc := &Conn{\n\t\tisServer:               isServer,\n\t\tbr:                     br,\n\t\tconn:                   conn,\n\t\tmu:                     mu,\n\t\treadFinal:              true,\n\t\twriteBuf:               writeBuf,\n\t\twritePool:              writeBufferPool,\n\t\twriteBufSize:           writeBufferSize,\n\t\tenableWriteCompression: true,\n\t\tcompressionLevel:       defaultCompressionLevel,\n\t}\n\tc.SetCloseHandler(nil)\n\tc.SetPingHandler(nil)\n\tc.SetPongHandler(nil)\n\treturn c\n}\n\n// setReadRemaining tracks the number of bytes remaining on the connection. If n\n// overflows, an ErrReadLimit is returned.\nfunc (c *Conn) setReadRemaining(n int64) error {\n\tif n < 0 {\n\t\treturn ErrReadLimit\n\t}\n\n\tc.readRemaining = n\n\treturn nil\n}\n\n// Subprotocol returns the negotiated protocol for the connection.\nfunc (c *Conn) Subprotocol() string {\n\treturn c.subprotocol\n}\n\n// Close closes the underlying network connection without sending or waiting\n// for a close message.\nfunc (c *Conn) Close() error {\n\treturn c.conn.Close()\n}\n\n// LocalAddr returns the local network address.\nfunc (c *Conn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *Conn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\n// Write methods\n\nfunc (c *Conn) writeFatal(err error) error {\n\tc.writeErrMu.Lock()\n\tif c.writeErr == nil {\n\t\tc.writeErr = err\n\t}\n\tc.writeErrMu.Unlock()\n\treturn err\n}\n\nfunc (c *Conn) read(n int) ([]byte, error) {\n\tp, err := c.br.Peek(n)\n\tif err == io.EOF {\n\t\terr = errUnexpectedEOF\n\t}\n\t// Discard is guaranteed to succeed because the number of bytes to discard\n\t// is less than or equal to the number of bytes buffered.\n\t_, _ = c.br.Discard(len(p))\n\treturn p, err\n}\n\nfunc (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {\n\t<-c.mu\n\tdefer func() { c.mu <- struct{}{} }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := c.conn.SetWriteDeadline(deadline); err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif len(buf1) == 0 {\n\t\t_, err = c.conn.Write(buf0)\n\t} else {\n\t\terr = c.writeBufs(buf0, buf1)\n\t}\n\tif err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif frameType == CloseMessage {\n\t\t_ = c.writeFatal(ErrCloseSent)\n\t}\n\treturn nil\n}\n\nfunc (c *Conn) writeBufs(bufs ...[]byte) error {\n\tb := net.Buffers(bufs)\n\t_, err := b.WriteTo(c.conn)\n\treturn err\n}\n\n// WriteControl writes a control message with the given deadline. The allowed\n// message types are CloseMessage, PingMessage and PongMessage.\nfunc (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {\n\tif !isControl(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\tif len(data) > maxControlFramePayloadSize {\n\t\treturn errInvalidControlFrame\n\t}\n\n\tb0 := byte(messageType) | finalBit\n\tb1 := byte(len(data))\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\tbuf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)\n\tbuf = append(buf, b0, b1)\n\n\tif c.isServer {\n\t\tbuf = append(buf, data...)\n\t} else {\n\t\tkey := newMaskKey()\n\t\tbuf = append(buf, key[:]...)\n\t\tbuf = append(buf, data...)\n\t\tmaskBytes(key, 0, buf[6:])\n\t}\n\n\tif deadline.IsZero() {\n\t\t// No timeout for zero time.\n\t\t<-c.mu\n\t} else {\n\t\td := time.Until(deadline)\n\t\tif d < 0 {\n\t\t\treturn errWriteTimeout\n\t\t}\n\t\tselect {\n\t\tcase <-c.mu:\n\t\tdefault:\n\t\t\ttimer := time.NewTimer(d)\n\t\t\tselect {\n\t\t\tcase <-c.mu:\n\t\t\t\ttimer.Stop()\n\t\t\tcase <-timer.C:\n\t\t\t\treturn errWriteTimeout\n\t\t\t}\n\t\t}\n\t}\n\n\tdefer func() { c.mu <- struct{}{} }()\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := c.conn.SetWriteDeadline(deadline); err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif _, err = c.conn.Write(buf); err != nil {\n\t\treturn c.writeFatal(err)\n\t}\n\tif messageType == CloseMessage {\n\t\t_ = c.writeFatal(ErrCloseSent)\n\t}\n\treturn err\n}\n\n// beginMessage prepares a connection and message writer for a new message.\nfunc (c *Conn) beginMessage(mw *messageWriter, messageType int) error {\n\t// Close previous writer if not already closed by the application. It's\n\t// probably better to return an error in this situation, but we cannot\n\t// change this without breaking existing applications.\n\tif c.writer != nil {\n\t\tc.writer.Close()\n\t\tc.writer = nil\n\t}\n\n\tif !isControl(messageType) && !isData(messageType) {\n\t\treturn errBadWriteOpCode\n\t}\n\n\tc.writeErrMu.Lock()\n\terr := c.writeErr\n\tc.writeErrMu.Unlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmw.c = c\n\tmw.frameType = messageType\n\tmw.pos = maxFrameHeaderSize\n\n\tif c.writeBuf == nil {\n\t\twpd, ok := c.writePool.Get().(writePoolData)\n\t\tif ok {\n\t\t\tc.writeBuf = wpd.buf\n\t\t} else {\n\t\t\tc.writeBuf = make([]byte, c.writeBufSize)\n\t\t}\n\t}\n\treturn nil\n}\n\n// NextWriter returns a writer for the next message to send. The writer's Close\n// method flushes the complete message to the network.\n//\n// There can be at most one open writer on a connection. NextWriter closes the\n// previous writer if the application has not already done so.\n//\n// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and\n// PongMessage) are supported.\nfunc (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {\n\tvar mw messageWriter\n\tif err := c.beginMessage(&mw, messageType); err != nil {\n\t\treturn nil, err\n\t}\n\tc.writer = &mw\n\tif c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {\n\t\tw := c.newCompressionWriter(c.writer, c.compressionLevel)\n\t\tmw.compress = true\n\t\tc.writer = w\n\t}\n\treturn c.writer, nil\n}\n\ntype messageWriter struct {\n\tc         *Conn\n\tcompress  bool // whether next call to flushFrame should set RSV1\n\tpos       int  // end of data in writeBuf.\n\tframeType int  // type of the current frame.\n\terr       error\n}\n\nfunc (w *messageWriter) endMessage(err error) error {\n\tif w.err != nil {\n\t\treturn err\n\t}\n\tc := w.c\n\tw.err = err\n\tc.writer = nil\n\tif c.writePool != nil {\n\t\tc.writePool.Put(writePoolData{buf: c.writeBuf})\n\t\tc.writeBuf = nil\n\t}\n\treturn err\n}\n\n// flushFrame writes buffered data and extra as a frame to the network. The\n// final argument indicates that this is the last frame in the message.\nfunc (w *messageWriter) flushFrame(final bool, extra []byte) error {\n\tc := w.c\n\tlength := w.pos - maxFrameHeaderSize + len(extra)\n\n\t// Check for invalid control frames.\n\tif isControl(w.frameType) &&\n\t\t(!final || length > maxControlFramePayloadSize) {\n\t\treturn w.endMessage(errInvalidControlFrame)\n\t}\n\n\tb0 := byte(w.frameType)\n\tif final {\n\t\tb0 |= finalBit\n\t}\n\tif w.compress {\n\t\tb0 |= rsv1Bit\n\t}\n\tw.compress = false\n\n\tb1 := byte(0)\n\tif !c.isServer {\n\t\tb1 |= maskBit\n\t}\n\n\t// Assume that the frame starts at beginning of c.writeBuf.\n\tframePos := 0\n\tif c.isServer {\n\t\t// Adjust up if mask not included in the header.\n\t\tframePos = 4\n\t}\n\n\tswitch {\n\tcase length >= 65536:\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 127\n\t\tbinary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))\n\tcase length > 125:\n\t\tframePos += 6\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | 126\n\t\tbinary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))\n\tdefault:\n\t\tframePos += 8\n\t\tc.writeBuf[framePos] = b0\n\t\tc.writeBuf[framePos+1] = b1 | byte(length)\n\t}\n\n\tif !c.isServer {\n\t\tkey := newMaskKey()\n\t\tcopy(c.writeBuf[maxFrameHeaderSize-4:], key[:])\n\t\tmaskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])\n\t\tif len(extra) > 0 {\n\t\t\treturn w.endMessage(c.writeFatal(errors.New(\"websocket: internal error, extra used in client mode\")))\n\t\t}\n\t}\n\n\t// Write the buffers to the connection with best-effort detection of\n\t// concurrent writes. See the concurrency section in the package\n\t// documentation for more info.\n\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\n\terr := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)\n\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\n\tif err != nil {\n\t\treturn w.endMessage(err)\n\t}\n\n\tif final {\n\t\t_ = w.endMessage(errWriteClosed)\n\t\treturn nil\n\t}\n\n\t// Setup for next frame.\n\tw.pos = maxFrameHeaderSize\n\tw.frameType = continuationFrame\n\treturn nil\n}\n\nfunc (w *messageWriter) ncopy(max int) (int, error) {\n\tn := len(w.c.writeBuf) - w.pos\n\tif n <= 0 {\n\t\tif err := w.flushFrame(false, nil); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tn = len(w.c.writeBuf) - w.pos\n\t}\n\tif n > max {\n\t\tn = max\n\t}\n\treturn n, nil\n}\n\nfunc (w *messageWriter) Write(p []byte) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tif len(p) > 2*len(w.c.writeBuf) && w.c.isServer {\n\t\t// Don't buffer large messages.\n\t\terr := w.flushFrame(false, p)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn len(p), nil\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) WriteString(p string) (int, error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\n\tnn := len(p)\n\tfor len(p) > 0 {\n\t\tn, err := w.ncopy(len(p))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tcopy(w.c.writeBuf[w.pos:], p[:n])\n\t\tw.pos += n\n\t\tp = p[n:]\n\t}\n\treturn nn, nil\n}\n\nfunc (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {\n\tif w.err != nil {\n\t\treturn 0, w.err\n\t}\n\tfor {\n\t\tif w.pos == len(w.c.writeBuf) {\n\t\t\terr = w.flushFrame(false, nil)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tvar n int\n\t\tn, err = r.Read(w.c.writeBuf[w.pos:])\n\t\tw.pos += n\n\t\tnn += int64(n)\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nn, err\n}\n\nfunc (w *messageWriter) Close() error {\n\tif w.err != nil {\n\t\treturn w.err\n\t}\n\treturn w.flushFrame(true, nil)\n}\n\n// WritePreparedMessage writes prepared message into connection.\nfunc (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {\n\tframeType, frameData, err := pm.frame(prepareKey{\n\t\tisServer:         c.isServer,\n\t\tcompress:         c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),\n\t\tcompressionLevel: c.compressionLevel,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tif c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = true\n\terr = c.write(frameType, c.writeDeadline, frameData, nil)\n\tif !c.isWriting {\n\t\tpanic(\"concurrent write to websocket connection\")\n\t}\n\tc.isWriting = false\n\treturn err\n}\n\n// WriteMessage is a helper method for getting a writer using NextWriter,\n// writing the message and closing the writer.\nfunc (c *Conn) WriteMessage(messageType int, data []byte) error {\n\n\tif c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {\n\t\t// Fast path with no allocations and single frame.\n\n\t\tvar mw messageWriter\n\t\tif err := c.beginMessage(&mw, messageType); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn := copy(c.writeBuf[mw.pos:], data)\n\t\tmw.pos += n\n\t\tdata = data[n:]\n\t\treturn mw.flushFrame(true, data)\n\t}\n\n\tw, err := c.NextWriter(messageType)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif _, err = w.Write(data); err != nil {\n\t\treturn err\n\t}\n\treturn w.Close()\n}\n\n// SetWriteDeadline sets the write deadline on the underlying network\n// connection. After a write has timed out, the websocket state is corrupt and\n// all future writes will return an error. A zero value for t means writes will\n// not time out.\nfunc (c *Conn) SetWriteDeadline(t time.Time) error {\n\tc.writeDeadline = t\n\treturn nil\n}\n\n// Read methods\n\nfunc (c *Conn) advanceFrame() (int, error) {\n\t// 1. Skip remainder of previous frame.\n\n\tif c.readRemaining > 0 {\n\t\tif _, err := io.CopyN(io.Discard, c.br, c.readRemaining); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t}\n\n\t// 2. Read and parse first two bytes of frame header.\n\t// To aid debugging, collect and report all errors in the first two bytes\n\t// of the header.\n\n\tvar errors []string\n\n\tp, err := c.read(2)\n\tif err != nil {\n\t\treturn noFrame, err\n\t}\n\n\tframeType := int(p[0] & 0xf)\n\tfinal := p[0]&finalBit != 0\n\trsv1 := p[0]&rsv1Bit != 0\n\trsv2 := p[0]&rsv2Bit != 0\n\trsv3 := p[0]&rsv3Bit != 0\n\tmask := p[1]&maskBit != 0\n\t_ = c.setReadRemaining(int64(p[1] & 0x7f)) // will not fail because argument is >= 0\n\n\tc.readDecompress = false\n\tif rsv1 {\n\t\tif c.newDecompressionReader != nil {\n\t\t\tc.readDecompress = true\n\t\t} else {\n\t\t\terrors = append(errors, \"RSV1 set\")\n\t\t}\n\t}\n\n\tif rsv2 {\n\t\terrors = append(errors, \"RSV2 set\")\n\t}\n\n\tif rsv3 {\n\t\terrors = append(errors, \"RSV3 set\")\n\t}\n\n\tswitch frameType {\n\tcase CloseMessage, PingMessage, PongMessage:\n\t\tif c.readRemaining > maxControlFramePayloadSize {\n\t\t\terrors = append(errors, \"len > 125 for control\")\n\t\t}\n\t\tif !final {\n\t\t\terrors = append(errors, \"FIN not set on control\")\n\t\t}\n\tcase TextMessage, BinaryMessage:\n\t\tif !c.readFinal {\n\t\t\terrors = append(errors, \"data before FIN\")\n\t\t}\n\t\tc.readFinal = final\n\tcase continuationFrame:\n\t\tif c.readFinal {\n\t\t\terrors = append(errors, \"continuation after FIN\")\n\t\t}\n\t\tc.readFinal = final\n\tdefault:\n\t\terrors = append(errors, \"bad opcode \"+strconv.Itoa(frameType))\n\t}\n\n\tif mask != c.isServer {\n\t\terrors = append(errors, \"bad MASK\")\n\t}\n\n\tif len(errors) > 0 {\n\t\treturn noFrame, c.handleProtocolError(strings.Join(errors, \", \"))\n\t}\n\n\t// 3. Read and parse frame length as per\n\t// https://tools.ietf.org/html/rfc6455#section-5.2\n\t//\n\t// The length of the \"Payload data\", in bytes: if 0-125, that is the payload\n\t// length.\n\t// - If 126, the following 2 bytes interpreted as a 16-bit unsigned\n\t// integer are the payload length.\n\t// - If 127, the following 8 bytes interpreted as\n\t// a 64-bit unsigned integer (the most significant bit MUST be 0) are the\n\t// payload length. Multibyte length quantities are expressed in network byte\n\t// order.\n\n\tswitch c.readRemaining {\n\tcase 126:\n\t\tp, err := c.read(2)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\n\t\tif err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase 127:\n\t\tp, err := c.read(8)\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\n\t\tif err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t}\n\n\t// 4. Handle frame masking.\n\n\tif mask {\n\t\tc.readMaskPos = 0\n\t\tp, err := c.read(len(c.readMaskKey))\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tcopy(c.readMaskKey[:], p)\n\t}\n\n\t// 5. For text and binary messages, enforce read limit and return.\n\n\tif frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {\n\n\t\tc.readLength += c.readRemaining\n\t\t// Don't allow readLength to overflow in the presence of a large readRemaining\n\t\t// counter.\n\t\tif c.readLength < 0 {\n\t\t\treturn noFrame, ErrReadLimit\n\t\t}\n\n\t\tif c.readLimit > 0 && c.readLength > c.readLimit {\n\t\t\t// Make a best effort to send a close message describing the problem.\n\t\t\t_ = c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, \"\"), time.Now().Add(writeWait))\n\t\t\treturn noFrame, ErrReadLimit\n\t\t}\n\n\t\treturn frameType, nil\n\t}\n\n\t// 6. Read control frame payload.\n\n\tvar payload []byte\n\tif c.readRemaining > 0 {\n\t\tpayload, err = c.read(int(c.readRemaining))\n\t\t_ = c.setReadRemaining(0) // will not fail because argument is >= 0\n\t\tif err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\tif c.isServer {\n\t\t\tmaskBytes(c.readMaskKey, 0, payload)\n\t\t}\n\t}\n\n\t// 7. Process control frame payload.\n\n\tswitch frameType {\n\tcase PongMessage:\n\t\tif err := c.handlePong(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase PingMessage:\n\t\tif err := c.handlePing(string(payload)); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\tcase CloseMessage:\n\t\tcloseCode := CloseNoStatusReceived\n\t\tcloseText := \"\"\n\t\tif len(payload) >= 2 {\n\t\t\tcloseCode = int(binary.BigEndian.Uint16(payload))\n\t\t\tif !isValidReceivedCloseCode(closeCode) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"bad close code \" + strconv.Itoa(closeCode))\n\t\t\t}\n\t\t\tcloseText = string(payload[2:])\n\t\t\tif !utf8.ValidString(closeText) {\n\t\t\t\treturn noFrame, c.handleProtocolError(\"invalid utf8 payload in close frame\")\n\t\t\t}\n\t\t}\n\t\tif err := c.handleClose(closeCode, closeText); err != nil {\n\t\t\treturn noFrame, err\n\t\t}\n\t\treturn noFrame, &CloseError{Code: closeCode, Text: closeText}\n\t}\n\n\treturn frameType, nil\n}\n\nfunc (c *Conn) handleProtocolError(message string) error {\n\tdata := FormatCloseMessage(CloseProtocolError, message)\n\tif len(data) > maxControlFramePayloadSize {\n\t\tdata = data[:maxControlFramePayloadSize]\n\t}\n\t// Make a best effor to send a close message describing the problem.\n\t_ = c.WriteControl(CloseMessage, data, time.Now().Add(writeWait))\n\treturn errors.New(\"websocket: \" + message)\n}\n\n// NextReader returns the next data message received from the peer. The\n// returned messageType is either TextMessage or BinaryMessage.\n//\n// There can be at most one open reader on a connection. NextReader discards\n// the previous message if the application has not already consumed it.\n//\n// Applications must break out of the application's read loop when this method\n// returns a non-nil error value. Errors returned from this method are\n// permanent. Once this method returns a non-nil error, all subsequent calls to\n// this method return the same error.\nfunc (c *Conn) NextReader() (messageType int, r io.Reader, err error) {\n\t// Close previous reader, only relevant for decompression.\n\tif c.reader != nil {\n\t\tc.reader.Close()\n\t\tc.reader = nil\n\t}\n\n\tc.messageReader = nil\n\tc.readLength = 0\n\n\tfor c.readErr == nil {\n\t\tframeType, err := c.advanceFrame()\n\t\tif err != nil {\n\t\t\tc.readErr = err\n\t\t\tbreak\n\t\t}\n\n\t\tif frameType == TextMessage || frameType == BinaryMessage {\n\t\t\tc.messageReader = &messageReader{c}\n\t\t\tc.reader = c.messageReader\n\t\t\tif c.readDecompress {\n\t\t\t\tc.reader = c.newDecompressionReader(c.reader)\n\t\t\t}\n\t\t\treturn frameType, c.reader, nil\n\t\t}\n\t}\n\n\t// Applications that do handle the error returned from this method spin in\n\t// tight loop on connection failure. To help application developers detect\n\t// this error, panic on repeated reads to the failed connection.\n\tc.readErrCount++\n\tif c.readErrCount >= 1000 {\n\t\tpanic(\"repeated read on failed websocket connection\")\n\t}\n\n\treturn noFrame, nil, c.readErr\n}\n\ntype messageReader struct{ c *Conn }\n\nfunc (r *messageReader) Read(b []byte) (int, error) {\n\tc := r.c\n\tif c.messageReader != r {\n\t\treturn 0, io.EOF\n\t}\n\n\tfor c.readErr == nil {\n\n\t\tif c.readRemaining > 0 {\n\t\t\tif int64(len(b)) > c.readRemaining {\n\t\t\t\tb = b[:c.readRemaining]\n\t\t\t}\n\t\t\tn, err := c.br.Read(b)\n\t\t\tc.readErr = err\n\t\t\tif c.isServer {\n\t\t\t\tc.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])\n\t\t\t}\n\t\t\trem := c.readRemaining\n\t\t\trem -= int64(n)\n\t\t\t_ = c.setReadRemaining(rem) // rem is guaranteed to be >= 0\n\t\t\tif c.readRemaining > 0 && c.readErr == io.EOF {\n\t\t\t\tc.readErr = errUnexpectedEOF\n\t\t\t}\n\t\t\treturn n, c.readErr\n\t\t}\n\n\t\tif c.readFinal {\n\t\t\tc.messageReader = nil\n\t\t\treturn 0, io.EOF\n\t\t}\n\n\t\tframeType, err := c.advanceFrame()\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\tc.readErr = err\n\t\tcase frameType == TextMessage || frameType == BinaryMessage:\n\t\t\tc.readErr = errors.New(\"websocket: internal error, unexpected text or binary in Reader\")\n\t\t}\n\t}\n\n\terr := c.readErr\n\tif err == io.EOF && c.messageReader == r {\n\t\terr = errUnexpectedEOF\n\t}\n\treturn 0, err\n}\n\nfunc (r *messageReader) Close() error {\n\treturn nil\n}\n\n// ReadMessage is a helper method for getting a reader using NextReader and\n// reading from that reader to a buffer.\nfunc (c *Conn) ReadMessage() (messageType int, p []byte, err error) {\n\tvar r io.Reader\n\tmessageType, r, err = c.NextReader()\n\tif err != nil {\n\t\treturn messageType, nil, err\n\t}\n\tp, err = io.ReadAll(r)\n\treturn messageType, p, err\n}\n\n// SetReadDeadline sets the read deadline on the underlying network connection.\n// After a read has timed out, the websocket connection state is corrupt and\n// all future reads will return an error. A zero value for t means reads will\n// not time out.\nfunc (c *Conn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\n// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a\n// message exceeds the limit, the connection sends a close message to the peer\n// and returns ErrReadLimit to the application.\nfunc (c *Conn) SetReadLimit(limit int64) {\n\tc.readLimit = limit\n}\n\n// CloseHandler returns the current close handler\nfunc (c *Conn) CloseHandler() func(code int, text string) error {\n\treturn c.handleClose\n}\n\n// SetCloseHandler sets the handler for close messages received from the peer.\n// The code argument to h is the received close code or CloseNoStatusReceived\n// if the close message is empty. The default close handler sends a close\n// message back to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// close messages as described in the section on Control Messages above.\n//\n// The connection read methods return a CloseError when a close message is\n// received. Most applications should handle close messages as part of their\n// normal error handling. Applications should only set a close handler when the\n// application must perform some action before sending a close message back to\n// the peer.\nfunc (c *Conn) SetCloseHandler(h func(code int, text string) error) {\n\tif h == nil {\n\t\th = func(code int, text string) error {\n\t\t\tmessage := FormatCloseMessage(code, \"\")\n\t\t\t// Make a best effor to send the close message.\n\t\t\t_ = c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))\n\t\t\treturn nil\n\t\t}\n\t}\n\tc.handleClose = h\n}\n\n// PingHandler returns the current ping handler\nfunc (c *Conn) PingHandler() func(appData string) error {\n\treturn c.handlePing\n}\n\n// SetPingHandler sets the handler for ping messages received from the peer.\n// The appData argument to h is the PING message application data. The default\n// ping handler sends a pong to the peer.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// ping messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPingHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(message string) error {\n\t\t\t// Make a best effort to send the pong message.\n\t\t\t_ = c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))\n\t\t\treturn nil\n\t\t}\n\t}\n\tc.handlePing = h\n}\n\n// PongHandler returns the current pong handler\nfunc (c *Conn) PongHandler() func(appData string) error {\n\treturn c.handlePong\n}\n\n// SetPongHandler sets the handler for pong messages received from the peer.\n// The appData argument to h is the PONG message application data. The default\n// pong handler does nothing.\n//\n// The handler function is called from the NextReader, ReadMessage and message\n// reader Read methods. The application must read the connection to process\n// pong messages as described in the section on Control Messages above.\nfunc (c *Conn) SetPongHandler(h func(appData string) error) {\n\tif h == nil {\n\t\th = func(string) error { return nil }\n\t}\n\tc.handlePong = h\n}\n\n// NetConn returns the underlying connection that is wrapped by c.\n// Note that writing to or reading from this connection directly will corrupt the\n// WebSocket connection.\nfunc (c *Conn) NetConn() net.Conn {\n\treturn c.conn\n}\n\n// UnderlyingConn returns the internal net.Conn. This can be used to further\n// modifications to connection specific flags.\n// Deprecated: Use the NetConn method.\nfunc (c *Conn) UnderlyingConn() net.Conn {\n\treturn c.conn\n}\n\n// EnableWriteCompression enables and disables write compression of\n// subsequent text and binary messages. This function is a noop if\n// compression was not negotiated with the peer.\nfunc (c *Conn) EnableWriteCompression(enable bool) {\n\tc.enableWriteCompression = enable\n}\n\n// SetCompressionLevel sets the flate compression level for subsequent text and\n// binary messages. This function is a noop if compression was not negotiated\n// with the peer. See the compress/flate package for a description of\n// compression levels.\nfunc (c *Conn) SetCompressionLevel(level int) error {\n\tif !isValidCompressionLevel(level) {\n\t\treturn errors.New(\"websocket: invalid compression level\")\n\t}\n\tc.compressionLevel = level\n\treturn nil\n}\n\n// FormatCloseMessage formats closeCode and text as a WebSocket close message.\n// An empty message is returned for code CloseNoStatusReceived.\nfunc FormatCloseMessage(closeCode int, text string) []byte {\n\tif closeCode == CloseNoStatusReceived {\n\t\t// Return empty message because it's illegal to send\n\t\t// CloseNoStatusReceived. Return non-nil value in case application\n\t\t// checks for nil.\n\t\treturn []byte{}\n\t}\n\tbuf := make([]byte, 2+len(text))\n\tbinary.BigEndian.PutUint16(buf, uint16(closeCode))\n\tcopy(buf[2:], text)\n\treturn buf\n}\n"
  },
  {
    "path": "conn_broadcast_test.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"io\"\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\n// broadcastBench allows to run broadcast benchmarks.\n// In every broadcast benchmark we create many connections, then send the same\n// message into every connection and wait for all writes complete. This emulates\n// an application where many connections listen to the same data - i.e. PUB/SUB\n// scenarios with many subscribers in one channel.\ntype broadcastBench struct {\n\tw           io.Writer\n\tcloseCh     chan struct{}\n\tdoneCh      chan struct{}\n\tcount       int32\n\tconns       []*broadcastConn\n\tcompression bool\n\tusePrepared bool\n}\n\ntype broadcastMessage struct {\n\tpayload  []byte\n\tprepared *PreparedMessage\n}\n\ntype broadcastConn struct {\n\tconn  *Conn\n\tmsgCh chan *broadcastMessage\n}\n\nfunc newBroadcastConn(c *Conn) *broadcastConn {\n\treturn &broadcastConn{\n\t\tconn:  c,\n\t\tmsgCh: make(chan *broadcastMessage, 1),\n\t}\n}\n\nfunc newBroadcastBench(usePrepared, compression bool) *broadcastBench {\n\tbench := &broadcastBench{\n\t\tw:           io.Discard,\n\t\tdoneCh:      make(chan struct{}),\n\t\tcloseCh:     make(chan struct{}),\n\t\tusePrepared: usePrepared,\n\t\tcompression: compression,\n\t}\n\tbench.makeConns(10000)\n\treturn bench\n}\n\nfunc (b *broadcastBench) makeConns(numConns int) {\n\tconns := make([]*broadcastConn, numConns)\n\n\tfor i := 0; i < numConns; i++ {\n\t\tc := newTestConn(nil, b.w, true)\n\t\tif b.compression {\n\t\t\tc.enableWriteCompression = true\n\t\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\t}\n\t\tconns[i] = newBroadcastConn(c)\n\t\tgo func(c *broadcastConn) {\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase msg := <-c.msgCh:\n\t\t\t\t\tif msg.prepared != nil {\n\t\t\t\t\t\t_ = c.conn.WritePreparedMessage(msg.prepared)\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_ = c.conn.WriteMessage(TextMessage, msg.payload)\n\t\t\t\t\t}\n\t\t\t\t\tval := atomic.AddInt32(&b.count, 1)\n\t\t\t\t\tif val%int32(numConns) == 0 {\n\t\t\t\t\t\tb.doneCh <- struct{}{}\n\t\t\t\t\t}\n\t\t\t\tcase <-b.closeCh:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}(conns[i])\n\t}\n\tb.conns = conns\n}\n\nfunc (b *broadcastBench) close() {\n\tclose(b.closeCh)\n}\n\nfunc (b *broadcastBench) broadcastOnce(msg *broadcastMessage) {\n\tfor _, c := range b.conns {\n\t\tc.msgCh <- msg\n\t}\n\t<-b.doneCh\n}\n\nfunc BenchmarkBroadcast(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tname        string\n\t\tusePrepared bool\n\t\tcompression bool\n\t}{\n\t\t{\"NoCompression\", false, false},\n\t\t{\"Compression\", false, true},\n\t\t{\"NoCompressionPrepared\", true, false},\n\t\t{\"CompressionPrepared\", true, true},\n\t}\n\tpayload := textMessages(1)[0]\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tbench := newBroadcastBench(bm.usePrepared, bm.compression)\n\t\t\tdefer bench.close()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tmessage := &broadcastMessage{\n\t\t\t\t\tpayload: payload,\n\t\t\t\t}\n\t\t\t\tif bench.usePrepared {\n\t\t\t\t\tpm, _ := NewPreparedMessage(TextMessage, message.payload)\n\t\t\t\t\tmessage.prepared = pm\n\t\t\t\t}\n\t\t\t\tbench.broadcastOnce(message)\n\t\t\t}\n\t\t\tb.ReportAllocs()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "conn_test.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"testing/iotest\"\n\t\"time\"\n)\n\nvar _ net.Error = errWriteTimeout\n\ntype fakeNetConn struct {\n\tio.Reader\n\tio.Writer\n}\n\nfunc (c fakeNetConn) Close() error                       { return nil }\nfunc (c fakeNetConn) LocalAddr() net.Addr                { return localAddr }\nfunc (c fakeNetConn) RemoteAddr() net.Addr               { return remoteAddr }\nfunc (c fakeNetConn) SetDeadline(t time.Time) error      { return nil }\nfunc (c fakeNetConn) SetReadDeadline(t time.Time) error  { return nil }\nfunc (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil }\n\ntype fakeAddr int\n\nvar (\n\tlocalAddr  = fakeAddr(1)\n\tremoteAddr = fakeAddr(2)\n)\n\nfunc (a fakeAddr) Network() string {\n\treturn \"net\"\n}\n\nfunc (a fakeAddr) String() string {\n\treturn \"str\"\n}\n\n// newTestConn creates a connection backed by a fake network connection using\n// default values for buffering.\nfunc newTestConn(r io.Reader, w io.Writer, isServer bool) *Conn {\n\treturn newConn(fakeNetConn{Reader: r, Writer: w}, isServer, 1024, 1024, nil, nil, nil)\n}\n\nfunc TestFraming(t *testing.T) {\n\tframeSizes := []int{\n\t\t0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535,\n\t\t// 65536, 65537\n\t}\n\tvar readChunkers = []struct {\n\t\tname string\n\t\tf    func(io.Reader) io.Reader\n\t}{\n\t\t{\"half\", iotest.HalfReader},\n\t\t{\"one\", iotest.OneByteReader},\n\t\t{\"asis\", func(r io.Reader) io.Reader { return r }},\n\t}\n\twriteBuf := make([]byte, 65537)\n\tfor i := range writeBuf {\n\t\twriteBuf[i] = byte(i)\n\t}\n\tvar writers = []struct {\n\t\tname string\n\t\tf    func(w io.Writer, n int) (int, error)\n\t}{\n\t\t{\"iocopy\", func(w io.Writer, n int) (int, error) {\n\t\t\tnn, err := io.Copy(w, bytes.NewReader(writeBuf[:n]))\n\t\t\treturn int(nn), err\n\t\t}},\n\t\t{\"write\", func(w io.Writer, n int) (int, error) {\n\t\t\treturn w.Write(writeBuf[:n])\n\t\t}},\n\t\t{\"string\", func(w io.Writer, n int) (int, error) {\n\t\t\treturn io.WriteString(w, string(writeBuf[:n]))\n\t\t}},\n\t}\n\n\tfor _, compress := range []bool{false, true} {\n\t\tfor _, isServer := range []bool{true, false} {\n\t\t\tfor _, chunker := range readChunkers {\n\n\t\t\t\tvar connBuf bytes.Buffer\n\t\t\t\twc := newTestConn(nil, &connBuf, isServer)\n\t\t\t\trc := newTestConn(chunker.f(&connBuf), nil, !isServer)\n\t\t\t\tif compress {\n\t\t\t\t\twc.newCompressionWriter = compressNoContextTakeover\n\t\t\t\t\trc.newDecompressionReader = decompressNoContextTakeover\n\t\t\t\t}\n\t\t\t\tfor _, n := range frameSizes {\n\t\t\t\t\tfor _, writer := range writers {\n\t\t\t\t\t\tname := fmt.Sprintf(\"z:%v, s:%v, r:%s, n:%d w:%s\", compress, isServer, chunker.name, n, writer.name)\n\n\t\t\t\t\t\tw, err := wc.NextWriter(TextMessage)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"%s: wc.NextWriter() returned %v\", name, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tnn, err := writer.f(w, n)\n\t\t\t\t\t\tif err != nil || nn != n {\n\t\t\t\t\t\t\tt.Errorf(\"%s: w.Write(writeBuf[:n]) returned %d, %v\", name, nn, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\terr = w.Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"%s: w.Close() returned %v\", name, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\topCode, r, err := rc.NextReader()\n\t\t\t\t\t\tif err != nil || opCode != TextMessage {\n\t\t\t\t\t\t\tt.Errorf(\"%s: NextReader() returned %d, r, %v\", name, opCode, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tt.Logf(\"frame size: %d\", n)\n\t\t\t\t\t\trbuf, err := io.ReadAll(r)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"%s: ReadFull() returned rbuf, %v\", name, err)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif len(rbuf) != n {\n\t\t\t\t\t\t\tt.Errorf(\"%s: len(rbuf) is %d, want %d\", name, len(rbuf), n)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor i, b := range rbuf {\n\t\t\t\t\t\t\tif byte(i) != b {\n\t\t\t\t\t\t\t\tt.Errorf(\"%s: bad byte at offset %d\", name, i)\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestWriteControlDeadline(t *testing.T) {\n\tt.Parallel()\n\tmessage := []byte(\"hello\")\n\tvar connBuf bytes.Buffer\n\tc := newTestConn(nil, &connBuf, true)\n\tif err := c.WriteControl(PongMessage, message, time.Time{}); err != nil {\n\t\tt.Errorf(\"WriteControl(..., zero deadline) = %v, want nil\", err)\n\t}\n\tif err := c.WriteControl(PongMessage, message, time.Now().Add(time.Second)); err != nil {\n\t\tt.Errorf(\"WriteControl(..., future deadline) = %v, want nil\", err)\n\t}\n\tif err := c.WriteControl(PongMessage, message, time.Now().Add(-time.Second)); err == nil {\n\t\tt.Errorf(\"WriteControl(..., past deadline) = nil, want timeout error\")\n\t}\n}\n\nfunc TestConcurrencyWriteControl(t *testing.T) {\n\tconst message = \"this is a ping/pong messsage\"\n\tloop := 10\n\tworkers := 10\n\tfor i := 0; i < loop; i++ {\n\t\tvar connBuf bytes.Buffer\n\n\t\twg := sync.WaitGroup{}\n\t\twc := newTestConn(nil, &connBuf, true)\n\n\t\tfor i := 0; i < workers; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tif err := wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second)); err != nil {\n\t\t\t\t\tt.Errorf(\"concurrently wc.WriteControl() returned %v\", err)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\twg.Wait()\n\t\twc.Close()\n\t}\n}\n\nfunc TestControl(t *testing.T) {\n\tconst message = \"this is a ping/pong message\"\n\tfor _, isServer := range []bool{true, false} {\n\t\tfor _, isWriteControl := range []bool{true, false} {\n\t\t\tname := fmt.Sprintf(\"s:%v, wc:%v\", isServer, isWriteControl)\n\t\t\tvar connBuf bytes.Buffer\n\t\t\twc := newTestConn(nil, &connBuf, isServer)\n\t\t\trc := newTestConn(&connBuf, nil, !isServer)\n\t\t\tif isWriteControl {\n\t\t\t\t_ = wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second))\n\t\t\t} else {\n\t\t\t\tw, err := wc.NextWriter(PongMessage)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"%s: wc.NextWriter() returned %v\", name, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, err := w.Write([]byte(message)); err != nil {\n\t\t\t\t\tt.Errorf(\"%s: w.Write() returned %v\", name, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif err := w.Close(); err != nil {\n\t\t\t\t\tt.Errorf(\"%s: w.Close() returned %v\", name, err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvar actualMessage string\n\t\t\t\trc.SetPongHandler(func(s string) error { actualMessage = s; return nil })\n\t\t\t\t_, _, _ = rc.NextReader()\n\t\t\t\tif actualMessage != message {\n\t\t\t\t\tt.Errorf(\"%s: pong=%q, want %q\", name, actualMessage, message)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// simpleBufferPool is an implementation of BufferPool for TestWriteBufferPool.\ntype simpleBufferPool struct {\n\tv interface{}\n}\n\nfunc (p *simpleBufferPool) Get() interface{} {\n\tv := p.v\n\tp.v = nil\n\treturn v\n}\n\nfunc (p *simpleBufferPool) Put(v interface{}) {\n\tp.v = v\n}\n\nfunc TestWriteBufferPool(t *testing.T) {\n\tconst message = \"Now is the time for all good people to come to the aid of the party.\"\n\n\tvar buf bytes.Buffer\n\tvar pool simpleBufferPool\n\trc := newTestConn(&buf, nil, false)\n\n\t// Specify writeBufferSize smaller than message size to ensure that pooling\n\t// works with fragmented messages.\n\twc := newConn(fakeNetConn{Writer: &buf}, true, 1024, len(message)-1, &pool, nil, nil)\n\n\tif wc.writeBuf != nil {\n\t\tt.Fatal(\"writeBuf not nil after create\")\n\t}\n\n\t// Part 1: test NextWriter/Write/Close\n\n\tw, err := wc.NextWriter(TextMessage)\n\tif err != nil {\n\t\tt.Fatalf(\"wc.NextWriter() returned %v\", err)\n\t}\n\n\tif wc.writeBuf == nil {\n\t\tt.Fatal(\"writeBuf is nil after NextWriter\")\n\t}\n\n\twriteBufAddr := &wc.writeBuf[0]\n\n\tif _, err := io.WriteString(w, message); err != nil {\n\t\tt.Fatalf(\"io.WriteString(w, message) returned %v\", err)\n\t}\n\n\tif err := w.Close(); err != nil {\n\t\tt.Fatalf(\"w.Close() returned %v\", err)\n\t}\n\n\tif wc.writeBuf != nil {\n\t\tt.Fatal(\"writeBuf not nil after w.Close()\")\n\t}\n\n\tif wpd, ok := pool.v.(writePoolData); !ok || len(wpd.buf) == 0 || &wpd.buf[0] != writeBufAddr {\n\t\tt.Fatal(\"writeBuf not returned to pool\")\n\t}\n\n\topCode, p, err := rc.ReadMessage()\n\tif opCode != TextMessage || err != nil {\n\t\tt.Fatalf(\"ReadMessage() returned %d, p, %v\", opCode, err)\n\t}\n\n\tif s := string(p); s != message {\n\t\tt.Fatalf(\"message is %s, want %s\", s, message)\n\t}\n\n\t// Part 2: Test WriteMessage.\n\n\tif err := wc.WriteMessage(TextMessage, []byte(message)); err != nil {\n\t\tt.Fatalf(\"wc.WriteMessage() returned %v\", err)\n\t}\n\n\tif wc.writeBuf != nil {\n\t\tt.Fatal(\"writeBuf not nil after wc.WriteMessage()\")\n\t}\n\n\tif wpd, ok := pool.v.(writePoolData); !ok || len(wpd.buf) == 0 || &wpd.buf[0] != writeBufAddr {\n\t\tt.Fatal(\"writeBuf not returned to pool after WriteMessage\")\n\t}\n\n\topCode, p, err = rc.ReadMessage()\n\tif opCode != TextMessage || err != nil {\n\t\tt.Fatalf(\"ReadMessage() returned %d, p, %v\", opCode, err)\n\t}\n\n\tif s := string(p); s != message {\n\t\tt.Fatalf(\"message is %s, want %s\", s, message)\n\t}\n}\n\n// TestWriteBufferPoolSync ensures that *sync.Pool works as a buffer pool.\nfunc TestWriteBufferPoolSync(t *testing.T) {\n\tvar buf bytes.Buffer\n\tvar pool sync.Pool\n\twc := newConn(fakeNetConn{Writer: &buf}, true, 1024, 1024, &pool, nil, nil)\n\trc := newTestConn(&buf, nil, false)\n\n\tconst message = \"Hello World!\"\n\tfor i := 0; i < 3; i++ {\n\t\tif err := wc.WriteMessage(TextMessage, []byte(message)); err != nil {\n\t\t\tt.Fatalf(\"wc.WriteMessage() returned %v\", err)\n\t\t}\n\t\topCode, p, err := rc.ReadMessage()\n\t\tif opCode != TextMessage || err != nil {\n\t\t\tt.Fatalf(\"ReadMessage() returned %d, p, %v\", opCode, err)\n\t\t}\n\t\tif s := string(p); s != message {\n\t\t\tt.Fatalf(\"message is %s, want %s\", s, message)\n\t\t}\n\t}\n}\n\n// errorWriter is an io.Writer than returns an error on all writes.\ntype errorWriter struct{}\n\nfunc (ew errorWriter) Write(p []byte) (int, error) { return 0, errors.New(\"error\") }\n\n// TestWriteBufferPoolError ensures that buffer is returned to pool after error\n// on write.\nfunc TestWriteBufferPoolError(t *testing.T) {\n\n\t// Part 1: Test NextWriter/Write/Close\n\n\tvar pool simpleBufferPool\n\twc := newConn(fakeNetConn{Writer: errorWriter{}}, true, 1024, 1024, &pool, nil, nil)\n\n\tw, err := wc.NextWriter(TextMessage)\n\tif err != nil {\n\t\tt.Fatalf(\"wc.NextWriter() returned %v\", err)\n\t}\n\n\tif wc.writeBuf == nil {\n\t\tt.Fatal(\"writeBuf is nil after NextWriter\")\n\t}\n\n\twriteBufAddr := &wc.writeBuf[0]\n\n\tif _, err := io.WriteString(w, \"Hello\"); err != nil {\n\t\tt.Fatalf(\"io.WriteString(w, message) returned %v\", err)\n\t}\n\n\tif err := w.Close(); err == nil {\n\t\tt.Fatalf(\"w.Close() did not return error\")\n\t}\n\n\tif wpd, ok := pool.v.(writePoolData); !ok || len(wpd.buf) == 0 || &wpd.buf[0] != writeBufAddr {\n\t\tt.Fatal(\"writeBuf not returned to pool\")\n\t}\n\n\t// Part 2: Test WriteMessage\n\n\twc = newConn(fakeNetConn{Writer: errorWriter{}}, true, 1024, 1024, &pool, nil, nil)\n\n\tif err := wc.WriteMessage(TextMessage, []byte(\"Hello\")); err == nil {\n\t\tt.Fatalf(\"wc.WriteMessage did not return error\")\n\t}\n\n\tif wpd, ok := pool.v.(writePoolData); !ok || len(wpd.buf) == 0 || &wpd.buf[0] != writeBufAddr {\n\t\tt.Fatal(\"writeBuf not returned to pool\")\n\t}\n}\n\nfunc TestCloseFrameBeforeFinalMessageFrame(t *testing.T) {\n\tconst bufSize = 512\n\n\texpectedErr := &CloseError{Code: CloseNormalClosure, Text: \"hello\"}\n\n\tvar b1, b2 bytes.Buffer\n\twc := newConn(&fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize, nil, nil, nil)\n\trc := newTestConn(&b1, &b2, true)\n\n\tw, _ := wc.NextWriter(BinaryMessage)\n\t_, _ = w.Write(make([]byte, bufSize+bufSize/2))\n\t_ = wc.WriteControl(CloseMessage, FormatCloseMessage(expectedErr.Code, expectedErr.Text), time.Now().Add(10*time.Second))\n\tw.Close()\n\n\top, r, err := rc.NextReader()\n\tif op != BinaryMessage || err != nil {\n\t\tt.Fatalf(\"NextReader() returned %d, %v\", op, err)\n\t}\n\t_, err = io.Copy(io.Discard, r)\n\tif !reflect.DeepEqual(err, expectedErr) {\n\t\tt.Fatalf(\"io.Copy() returned %v, want %v\", err, expectedErr)\n\t}\n\t_, _, err = rc.NextReader()\n\tif !reflect.DeepEqual(err, expectedErr) {\n\t\tt.Fatalf(\"NextReader() returned %v, want %v\", err, expectedErr)\n\t}\n}\n\nfunc TestEOFWithinFrame(t *testing.T) {\n\tconst bufSize = 64\n\n\tfor n := 0; ; n++ {\n\t\tvar b bytes.Buffer\n\t\twc := newTestConn(nil, &b, false)\n\t\trc := newTestConn(&b, nil, true)\n\n\t\tw, _ := wc.NextWriter(BinaryMessage)\n\t\t_, _ = w.Write(make([]byte, bufSize))\n\t\tw.Close()\n\n\t\tif n >= b.Len() {\n\t\t\tbreak\n\t\t}\n\t\tb.Truncate(n)\n\n\t\top, r, err := rc.NextReader()\n\t\tif err == errUnexpectedEOF {\n\t\t\tcontinue\n\t\t}\n\t\tif op != BinaryMessage || err != nil {\n\t\t\tt.Fatalf(\"%d: NextReader() returned %d, %v\", n, op, err)\n\t\t}\n\t\t_, err = io.Copy(io.Discard, r)\n\t\tif err != errUnexpectedEOF {\n\t\t\tt.Fatalf(\"%d: io.Copy() returned %v, want %v\", n, err, errUnexpectedEOF)\n\t\t}\n\t\t_, _, err = rc.NextReader()\n\t\tif err != errUnexpectedEOF {\n\t\t\tt.Fatalf(\"%d: NextReader() returned %v, want %v\", n, err, errUnexpectedEOF)\n\t\t}\n\t}\n}\n\nfunc TestEOFBeforeFinalFrame(t *testing.T) {\n\tconst bufSize = 512\n\n\tvar b1, b2 bytes.Buffer\n\twc := newConn(&fakeNetConn{Writer: &b1}, false, 1024, bufSize, nil, nil, nil)\n\trc := newTestConn(&b1, &b2, true)\n\n\tw, _ := wc.NextWriter(BinaryMessage)\n\t_, _ = w.Write(make([]byte, bufSize+bufSize/2))\n\n\top, r, err := rc.NextReader()\n\tif op != BinaryMessage || err != nil {\n\t\tt.Fatalf(\"NextReader() returned %d, %v\", op, err)\n\t}\n\t_, err = io.Copy(io.Discard, r)\n\tif err != errUnexpectedEOF {\n\t\tt.Fatalf(\"io.Copy() returned %v, want %v\", err, errUnexpectedEOF)\n\t}\n\t_, _, err = rc.NextReader()\n\tif err != errUnexpectedEOF {\n\t\tt.Fatalf(\"NextReader() returned %v, want %v\", err, errUnexpectedEOF)\n\t}\n}\n\nfunc TestWriteAfterMessageWriterClose(t *testing.T) {\n\twc := newTestConn(nil, &bytes.Buffer{}, false)\n\tw, _ := wc.NextWriter(BinaryMessage)\n\t_, _ = io.WriteString(w, \"hello\")\n\tif err := w.Close(); err != nil {\n\t\tt.Fatalf(\"unexpected error closing message writer, %v\", err)\n\t}\n\n\tif _, err := io.WriteString(w, \"world\"); err == nil {\n\t\tt.Fatalf(\"no error writing after close\")\n\t}\n\n\tw, _ = wc.NextWriter(BinaryMessage)\n\t_, _ = io.WriteString(w, \"hello\")\n\n\t// close w by getting next writer\n\t_, err := wc.NextWriter(BinaryMessage)\n\tif err != nil {\n\t\tt.Fatalf(\"unexpected error getting next writer, %v\", err)\n\t}\n\n\tif _, err := io.WriteString(w, \"world\"); err == nil {\n\t\tt.Fatalf(\"no error writing after close\")\n\t}\n}\n\nfunc TestReadLimit(t *testing.T) {\n\tt.Run(\"Test ReadLimit is enforced\", func(t *testing.T) {\n\t\tconst readLimit = 512\n\t\tmessage := make([]byte, readLimit+1)\n\n\t\tvar b1, b2 bytes.Buffer\n\t\twc := newConn(&fakeNetConn{Writer: &b1}, false, 1024, readLimit-2, nil, nil, nil)\n\t\trc := newTestConn(&b1, &b2, true)\n\t\trc.SetReadLimit(readLimit)\n\n\t\t// Send message at the limit with interleaved pong.\n\t\tw, _ := wc.NextWriter(BinaryMessage)\n\t\t_, _ = w.Write(message[:readLimit-1])\n\t\t_ = wc.WriteControl(PongMessage, []byte(\"this is a pong\"), time.Now().Add(10*time.Second))\n\t\t_, _ = w.Write(message[:1])\n\t\tw.Close()\n\n\t\t// Send message larger than the limit.\n\t\t_ = wc.WriteMessage(BinaryMessage, message[:readLimit+1])\n\n\t\top, _, err := rc.NextReader()\n\t\tif op != BinaryMessage || err != nil {\n\t\t\tt.Fatalf(\"1: NextReader() returned %d, %v\", op, err)\n\t\t}\n\t\top, r, err := rc.NextReader()\n\t\tif op != BinaryMessage || err != nil {\n\t\t\tt.Fatalf(\"2: NextReader() returned %d, %v\", op, err)\n\t\t}\n\t\t_, err = io.Copy(io.Discard, r)\n\t\tif err != ErrReadLimit {\n\t\t\tt.Fatalf(\"io.Copy() returned %v\", err)\n\t\t}\n\t})\n\n\tt.Run(\"Test that ReadLimit cannot be overflowed\", func(t *testing.T) {\n\t\tconst readLimit = 1\n\n\t\tvar b1, b2 bytes.Buffer\n\t\trc := newTestConn(&b1, &b2, true)\n\t\trc.SetReadLimit(readLimit)\n\n\t\t// First, send a non-final binary message\n\t\tb1.Write([]byte(\"\\x02\\x81\"))\n\n\t\t// Mask key\n\t\tb1.Write([]byte(\"\\x00\\x00\\x00\\x00\"))\n\n\t\t// First payload\n\t\tb1.Write([]byte(\"A\"))\n\n\t\t// Next, send a negative-length, non-final continuation frame\n\t\tb1.Write([]byte(\"\\x00\\xFF\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"))\n\n\t\t// Mask key\n\t\tb1.Write([]byte(\"\\x00\\x00\\x00\\x00\"))\n\n\t\t// Next, send a too long, final continuation frame\n\t\tb1.Write([]byte(\"\\x80\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\"))\n\n\t\t// Mask key\n\t\tb1.Write([]byte(\"\\x00\\x00\\x00\\x00\"))\n\n\t\t// Too-long payload\n\t\tb1.Write([]byte(\"BCDEF\"))\n\n\t\top, r, err := rc.NextReader()\n\t\tif op != BinaryMessage || err != nil {\n\t\t\tt.Fatalf(\"1: NextReader() returned %d, %v\", op, err)\n\t\t}\n\n\t\tvar buf [10]byte\n\t\tvar read int\n\t\tn, err := r.Read(buf[:])\n\t\tif err != nil && err != ErrReadLimit {\n\t\t\tt.Fatalf(\"unexpected error testing read limit: %v\", err)\n\t\t}\n\t\tread += n\n\n\t\tn, err = r.Read(buf[:])\n\t\tif err != nil && err != ErrReadLimit {\n\t\t\tt.Fatalf(\"unexpected error testing read limit: %v\", err)\n\t\t}\n\t\tread += n\n\n\t\tif err == nil && read > readLimit {\n\t\t\tt.Fatalf(\"read limit exceeded: limit %d, read %d\", readLimit, read)\n\t\t}\n\t})\n}\n\nfunc TestAddrs(t *testing.T) {\n\tc := newTestConn(nil, nil, true)\n\tif c.LocalAddr() != localAddr {\n\t\tt.Errorf(\"LocalAddr = %v, want %v\", c.LocalAddr(), localAddr)\n\t}\n\tif c.RemoteAddr() != remoteAddr {\n\t\tt.Errorf(\"RemoteAddr = %v, want %v\", c.RemoteAddr(), remoteAddr)\n\t}\n}\n\nfunc TestDeprecatedUnderlyingConn(t *testing.T) {\n\tvar b1, b2 bytes.Buffer\n\tfc := fakeNetConn{Reader: &b1, Writer: &b2}\n\tc := newConn(fc, true, 1024, 1024, nil, nil, nil)\n\tul := c.UnderlyingConn()\n\tif ul != fc {\n\t\tt.Fatalf(\"Underlying conn is not what it should be.\")\n\t}\n}\n\nfunc TestNetConn(t *testing.T) {\n\tvar b1, b2 bytes.Buffer\n\tfc := fakeNetConn{Reader: &b1, Writer: &b2}\n\tc := newConn(fc, true, 1024, 1024, nil, nil, nil)\n\tul := c.NetConn()\n\tif ul != fc {\n\t\tt.Fatalf(\"Underlying conn is not what it should be.\")\n\t}\n}\n\nfunc TestBufioReadBytes(t *testing.T) {\n\t// Test calling bufio.ReadBytes for value longer than read buffer size.\n\n\tm := make([]byte, 512)\n\tm[len(m)-1] = '\\n'\n\n\tvar b1, b2 bytes.Buffer\n\twc := newConn(fakeNetConn{Writer: &b1}, false, len(m)+64, len(m)+64, nil, nil, nil)\n\trc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, len(m)-64, len(m)-64, nil, nil, nil)\n\n\tw, _ := wc.NextWriter(BinaryMessage)\n\t_, _ = w.Write(m)\n\tw.Close()\n\n\top, r, err := rc.NextReader()\n\tif op != BinaryMessage || err != nil {\n\t\tt.Fatalf(\"NextReader() returned %d, %v\", op, err)\n\t}\n\n\tbr := bufio.NewReader(r)\n\tp, err := br.ReadBytes('\\n')\n\tif err != nil {\n\t\tt.Fatalf(\"ReadBytes() returned %v\", err)\n\t}\n\tif len(p) != len(m) {\n\t\tt.Fatalf(\"read returned %d bytes, want %d bytes\", len(p), len(m))\n\t}\n}\n\nvar closeErrorTests = []struct {\n\terr   error\n\tcodes []int\n\tok    bool\n}{\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, true},\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, false},\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, true},\n\t{errors.New(\"hello\"), []int{CloseNormalClosure}, false},\n}\n\nfunc TestCloseError(t *testing.T) {\n\tfor _, tt := range closeErrorTests {\n\t\tok := IsCloseError(tt.err, tt.codes...)\n\t\tif ok != tt.ok {\n\t\t\tt.Errorf(\"IsCloseError(%#v, %#v) returned %v, want %v\", tt.err, tt.codes, ok, tt.ok)\n\t\t}\n\t}\n}\n\nvar unexpectedCloseErrorTests = []struct {\n\terr   error\n\tcodes []int\n\tok    bool\n}{\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, false},\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, true},\n\t{&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, false},\n\t{errors.New(\"hello\"), []int{CloseNormalClosure}, false},\n}\n\nfunc TestUnexpectedCloseErrors(t *testing.T) {\n\tfor _, tt := range unexpectedCloseErrorTests {\n\t\tok := IsUnexpectedCloseError(tt.err, tt.codes...)\n\t\tif ok != tt.ok {\n\t\t\tt.Errorf(\"IsUnexpectedCloseError(%#v, %#v) returned %v, want %v\", tt.err, tt.codes, ok, tt.ok)\n\t\t}\n\t}\n}\n\ntype blockingWriter struct {\n\tc1, c2 chan struct{}\n}\n\nfunc (w blockingWriter) Write(p []byte) (int, error) {\n\t// Allow main to continue\n\tclose(w.c1)\n\t// Wait for panic in main\n\t<-w.c2\n\treturn len(p), nil\n}\n\nfunc TestConcurrentWritePanic(t *testing.T) {\n\tw := blockingWriter{make(chan struct{}), make(chan struct{})}\n\tc := newTestConn(nil, w, false)\n\tgo func() {\n\t\t_ = c.WriteMessage(TextMessage, []byte{})\n\t}()\n\n\t// wait for goroutine to block in write.\n\t<-w.c1\n\n\tdefer func() {\n\t\tclose(w.c2)\n\t\tif v := recover(); v != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\n\t_ = c.WriteMessage(TextMessage, []byte{})\n\tt.Fatal(\"should not get here\")\n}\n\ntype failingReader struct{}\n\nfunc (r failingReader) Read(p []byte) (int, error) {\n\treturn 0, io.EOF\n}\n\nfunc TestFailedConnectionReadPanic(t *testing.T) {\n\tc := newTestConn(failingReader{}, nil, false)\n\n\tdefer func() {\n\t\tif v := recover(); v != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\n\tfor i := 0; i < 20000; i++ {\n\t\t_, _, _ = c.ReadMessage()\n\t}\n\tt.Fatal(\"should not get here\")\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package websocket implements the WebSocket protocol defined in RFC 6455.\n//\n// Overview\n//\n// The Conn type represents a WebSocket connection. A server application calls\n// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:\n//\n//  var upgrader = websocket.Upgrader{\n//      ReadBufferSize:  1024,\n//      WriteBufferSize: 1024,\n//  }\n//\n//  func handler(w http.ResponseWriter, r *http.Request) {\n//      conn, err := upgrader.Upgrade(w, r, nil)\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      ... Use conn to send and receive messages.\n//  }\n//\n// Call the connection's WriteMessage and ReadMessage methods to send and\n// receive messages as a slice of bytes. This snippet of code shows how to echo\n// messages using these methods:\n//\n//  for {\n//      messageType, p, err := conn.ReadMessage()\n//      if err != nil {\n//          log.Println(err)\n//          return\n//      }\n//      if err := conn.WriteMessage(messageType, p); err != nil {\n//          log.Println(err)\n//          return\n//      }\n//  }\n//\n// In above snippet of code, p is a []byte and messageType is an int with value\n// websocket.BinaryMessage or websocket.TextMessage.\n//\n// An application can also send and receive messages using the io.WriteCloser\n// and io.Reader interfaces. To send a message, call the connection NextWriter\n// method to get an io.WriteCloser, write the message to the writer and close\n// the writer when done. To receive a message, call the connection NextReader\n// method to get an io.Reader and read until io.EOF is returned. This snippet\n// shows how to echo messages using the NextWriter and NextReader methods:\n//\n//  for {\n//      messageType, r, err := conn.NextReader()\n//      if err != nil {\n//          return\n//      }\n//      w, err := conn.NextWriter(messageType)\n//      if err != nil {\n//          return err\n//      }\n//      if _, err := io.Copy(w, r); err != nil {\n//          return err\n//      }\n//      if err := w.Close(); err != nil {\n//          return err\n//      }\n//  }\n//\n// Data Messages\n//\n// The WebSocket protocol distinguishes between text and binary data messages.\n// Text messages are interpreted as UTF-8 encoded text. The interpretation of\n// binary messages is left to the application.\n//\n// This package uses the TextMessage and BinaryMessage integer constants to\n// identify the two data message types. The ReadMessage and NextReader methods\n// return the type of the received message. The messageType argument to the\n// WriteMessage and NextWriter methods specifies the type of a sent message.\n//\n// It is the application's responsibility to ensure that text messages are\n// valid UTF-8 encoded text.\n//\n// Control Messages\n//\n// The WebSocket protocol defines three types of control messages: close, ping\n// and pong. Call the connection WriteControl, WriteMessage or NextWriter\n// methods to send a control message to the peer.\n//\n// Connections handle received close messages by calling the handler function\n// set with the SetCloseHandler method and by returning a *CloseError from the\n// NextReader, ReadMessage or the message Read method. The default close\n// handler sends a close message to the peer.\n//\n// Connections handle received ping messages by calling the handler function\n// set with the SetPingHandler method. The default ping handler sends a pong\n// message to the peer.\n//\n// Connections handle received pong messages by calling the handler function\n// set with the SetPongHandler method. The default pong handler does nothing.\n// If an application sends ping messages, then the application should set a\n// pong handler to receive the corresponding pong.\n//\n// The control message handler functions are called from the NextReader,\n// ReadMessage and message reader Read methods. The default close and ping\n// handlers can block these methods for a short time when the handler writes to\n// the connection.\n//\n// The application must read the connection to process close, ping and pong\n// messages sent from the peer. If the application is not otherwise interested\n// in messages from the peer, then the application should start a goroutine to\n// read and discard messages from the peer. A simple example is:\n//\n//  func readLoop(c *websocket.Conn) {\n//      for {\n//          if _, _, err := c.NextReader(); err != nil {\n//              c.Close()\n//              break\n//          }\n//      }\n//  }\n//\n// Concurrency\n//\n// Connections support one concurrent reader and one concurrent writer.\n//\n// Applications are responsible for ensuring that no more than one goroutine\n// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,\n// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and\n// that no more than one goroutine calls the read methods (NextReader,\n// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)\n// concurrently.\n//\n// The Close and WriteControl methods can be called concurrently with all other\n// methods.\n//\n// Origin Considerations\n//\n// Web browsers allow Javascript applications to open a WebSocket connection to\n// any host. It's up to the server to enforce an origin policy using the Origin\n// request header sent by the browser.\n//\n// The Upgrader calls the function specified in the CheckOrigin field to check\n// the origin. If the CheckOrigin function returns false, then the Upgrade\n// method fails the WebSocket handshake with HTTP status 403.\n//\n// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail\n// the handshake if the Origin request header is present and the Origin host is\n// not equal to the Host request header.\n//\n// The deprecated package-level Upgrade function does not perform origin\n// checking. The application is responsible for checking the Origin header\n// before calling the Upgrade function.\n//\n// Buffers\n//\n// Connections buffer network input and output to reduce the number\n// of system calls when reading or writing messages.\n//\n// Write buffers are also used for constructing WebSocket frames. See RFC 6455,\n// Section 5 for a discussion of message framing. A WebSocket frame header is\n// written to the network each time a write buffer is flushed to the network.\n// Decreasing the size of the write buffer can increase the amount of framing\n// overhead on the connection.\n//\n// The buffer sizes in bytes are specified by the ReadBufferSize and\n// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default\n// size of 4096 when a buffer size field is set to zero. The Upgrader reuses\n// buffers created by the HTTP server when a buffer size field is set to zero.\n// The HTTP server buffers have a size of 4096 at the time of this writing.\n//\n// The buffer sizes do not limit the size of a message that can be read or\n// written by a connection.\n//\n// Buffers are held for the lifetime of the connection by default. If the\n// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the\n// write buffer only when writing a message.\n//\n// Applications should tune the buffer sizes to balance memory use and\n// performance. Increasing the buffer size uses more memory, but can reduce the\n// number of system calls to read or write the network. In the case of writing,\n// increasing the buffer size can reduce the number of frame headers written to\n// the network.\n//\n// Some guidelines for setting buffer parameters are:\n//\n// Limit the buffer sizes to the maximum expected message size. Buffers larger\n// than the largest message do not provide any benefit.\n//\n// Depending on the distribution of message sizes, setting the buffer size to\n// a value less than the maximum expected message size can greatly reduce memory\n// use with a small impact on performance. Here's an example: If 99% of the\n// messages are smaller than 256 bytes and the maximum message size is 512\n// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls\n// than a buffer size of 512 bytes. The memory savings is 50%.\n//\n// A write buffer pool is useful when the application has a modest number\n// writes over a large number of connections. when buffers are pooled, a larger\n// buffer size has a reduced impact on total memory use and has the benefit of\n// reducing system calls and frame overhead.\n//\n// Compression EXPERIMENTAL\n//\n// Per message compression extensions (RFC 7692) are experimentally supported\n// by this package in a limited capacity. Setting the EnableCompression option\n// to true in Dialer or Upgrader will attempt to negotiate per message deflate\n// support.\n//\n//  var upgrader = websocket.Upgrader{\n//      EnableCompression: true,\n//  }\n//\n// If compression was successfully negotiated with the connection's peer, any\n// message received in compressed form will be automatically decompressed.\n// All Read methods will return uncompressed bytes.\n//\n// Per message compression of messages written to a connection can be enabled\n// or disabled by calling the corresponding Conn method:\n//\n//  conn.EnableWriteCompression(false)\n//\n// Currently this package does not support compression with \"context takeover\".\n// This means that messages must be compressed and decompressed in isolation,\n// without retaining sliding window or dictionary state across messages. For\n// more details refer to RFC 7692.\n//\n// Use of compression is experimental and may result in decreased performance.\npackage websocket\n"
  },
  {
    "path": "example_test.go",
    "content": "// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket_test\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar (\n\tc   *websocket.Conn\n\treq *http.Request\n)\n\n// The websocket.IsUnexpectedCloseError function is useful for identifying\n// application and protocol errors.\n//\n// This server application works with a client application running in the\n// browser. The client application does not explicitly close the websocket. The\n// only expected close message from the client has the code\n// websocket.CloseGoingAway. All other close messages are likely the\n// result of an application or protocol error and are logged to aid debugging.\nfunc ExampleIsUnexpectedCloseError() {\n\tfor {\n\t\tmessageType, p, err := c.ReadMessage()\n\t\tif err != nil {\n\t\t\tif websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {\n\t\t\t\tlog.Printf(\"error: %v, user-agent: %v\", err, req.Header.Get(\"User-Agent\"))\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tprocessMessage(messageType, p)\n\t}\n}\n\nfunc processMessage(mt int, p []byte) {}\n\n// TestX prevents godoc from showing this entire file in the example. Remove\n// this function when a second example is added.\nfunc TestX(t *testing.T) {}\n"
  },
  {
    "path": "examples/autobahn/README.md",
    "content": "# Test Server\n\nThis package contains a server for the [Autobahn WebSockets Test Suite](https://github.com/crossbario/autobahn-testsuite).\n\nTo test the server, run\n\n    go run server.go\n\nand start the client test driver\n\n    mkdir -p reports\n    docker run -it --rm \\\n        -v ${PWD}/config:/config \\\n        -v ${PWD}/reports:/reports \\\n        crossbario/autobahn-testsuite \\\n        wstest -m fuzzingclient -s /config/fuzzingclient.json\n\nWhen the client completes, it writes a report to reports/index.html.\n"
  },
  {
    "path": "examples/autobahn/config/fuzzingclient.json",
    "content": "{\n  \"cases\": [\"*\"],\n  \"exclude-cases\": [],\n  \"exclude-agent-cases\": {},\n  \"outdir\": \"/reports\",\n  \"options\": {\"failByDrop\": false},\n  \"servers\": [\n    {\n      \"agent\": \"ReadAllWriteMessage\",\n      \"url\": \"ws://host.docker.internal:9000/m\"\n    },\n    {\n      \"agent\": \"ReadAllWritePreparedMessage\",\n      \"url\": \"ws://host.docker.internal:9000/p\"\n    },\n    {\n      \"agent\": \"CopyFull\",\n      \"url\": \"ws://host.docker.internal:9000/f\"\n    },\n    {\n      \"agent\": \"ReadAllWrite\",\n      \"url\": \"ws://host.docker.internal:9000/r\"\n    },\n    {\n      \"agent\": \"CopyWriterOnly\",\n      \"url\": \"ws://host.docker.internal:9000/c\"\n    }\n  ]\n}\n"
  },
  {
    "path": "examples/autobahn/server.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Command server is a test server for the Autobahn WebSockets Test Suite.\npackage main\n\nimport (\n\t\"errors\"\n\t\"flag\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar upgrader = websocket.Upgrader{\n\tReadBufferSize:    4096,\n\tWriteBufferSize:   4096,\n\tEnableCompression: true,\n\tCheckOrigin: func(r *http.Request) bool {\n\t\treturn true\n\t},\n}\n\n// echoCopy echoes messages from the client using io.Copy.\nfunc echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) {\n\tconn, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Println(\"Upgrade:\", err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\tfor {\n\t\tmt, r, err := conn.NextReader()\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tlog.Println(\"NextReader:\", err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif mt == websocket.TextMessage {\n\t\t\tr = &validator{r: r}\n\t\t}\n\t\tw, err := conn.NextWriter(mt)\n\t\tif err != nil {\n\t\t\tlog.Println(\"NextWriter:\", err)\n\t\t\treturn\n\t\t}\n\t\tif mt == websocket.TextMessage {\n\t\t\tr = &validator{r: r}\n\t\t}\n\t\tif writerOnly {\n\t\t\t_, err = io.Copy(struct{ io.Writer }{w}, r)\n\t\t} else {\n\t\t\t_, err = io.Copy(w, r)\n\t\t}\n\t\tif err != nil {\n\t\t\tif err == errInvalidUTF8 {\n\t\t\t\tconn.WriteControl(websocket.CloseMessage,\n\t\t\t\t\twebsocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, \"\"),\n\t\t\t\t\ttime.Time{})\n\t\t\t}\n\t\t\tlog.Println(\"Copy:\", err)\n\t\t\treturn\n\t\t}\n\t\terr = w.Close()\n\t\tif err != nil {\n\t\t\tlog.Println(\"Close:\", err)\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) {\n\techoCopy(w, r, true)\n}\n\nfunc echoCopyFull(w http.ResponseWriter, r *http.Request) {\n\techoCopy(w, r, false)\n}\n\n// echoReadAll echoes messages from the client by reading the entire message\n// with io.ReadAll.\nfunc echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) {\n\tconn, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Println(\"Upgrade:\", err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\tfor {\n\t\tmt, b, err := conn.ReadMessage()\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tlog.Println(\"NextReader:\", err)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif mt == websocket.TextMessage {\n\t\t\tif !utf8.Valid(b) {\n\t\t\t\tconn.WriteControl(websocket.CloseMessage,\n\t\t\t\t\twebsocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, \"\"),\n\t\t\t\t\ttime.Time{})\n\t\t\t\tlog.Println(\"ReadAll: invalid utf8\")\n\t\t\t}\n\t\t}\n\t\tif writeMessage {\n\t\t\tif !writePrepared {\n\t\t\t\terr = conn.WriteMessage(mt, b)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"WriteMessage:\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpm, err := websocket.NewPreparedMessage(mt, b)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"NewPreparedMessage:\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\terr = conn.WritePreparedMessage(pm)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Println(\"WritePreparedMessage:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tw, err := conn.NextWriter(mt)\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"NextWriter:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif _, err := w.Write(b); err != nil {\n\t\t\t\tlog.Println(\"Writer:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := w.Close(); err != nil {\n\t\t\t\tlog.Println(\"Close:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc echoReadAllWriter(w http.ResponseWriter, r *http.Request) {\n\techoReadAll(w, r, false, false)\n}\n\nfunc echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {\n\techoReadAll(w, r, true, false)\n}\n\nfunc echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) {\n\techoReadAll(w, r, true, true)\n}\n\nfunc serveHome(w http.ResponseWriter, r *http.Request) {\n\tif r.URL.Path != \"/\" {\n\t\thttp.Error(w, \"Not found.\", http.StatusNotFound)\n\t\treturn\n\t}\n\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"Method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\tw.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tio.WriteString(w, \"<html><body>Echo Server</body></html>\")\n}\n\nvar addr = flag.String(\"addr\", \":9000\", \"http service address\")\n\nfunc main() {\n\tflag.Parse()\n\thttp.HandleFunc(\"/\", serveHome)\n\thttp.HandleFunc(\"/c\", echoCopyWriterOnly)\n\thttp.HandleFunc(\"/f\", echoCopyFull)\n\thttp.HandleFunc(\"/r\", echoReadAllWriter)\n\thttp.HandleFunc(\"/m\", echoReadAllWriteMessage)\n\thttp.HandleFunc(\"/p\", echoReadAllWritePreparedMessage)\n\terr := http.ListenAndServe(*addr, nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err)\n\t}\n}\n\ntype validator struct {\n\tstate int\n\tx     rune\n\tr     io.Reader\n}\n\nvar errInvalidUTF8 = errors.New(\"invalid utf8\")\n\nfunc (r *validator) Read(p []byte) (int, error) {\n\tn, err := r.r.Read(p)\n\tstate := r.state\n\tx := r.x\n\tfor _, b := range p[:n] {\n\t\tstate, x = decode(state, x, b)\n\t\tif state == utf8Reject {\n\t\t\tbreak\n\t\t}\n\t}\n\tr.state = state\n\tr.x = x\n\tif state == utf8Reject || (err == io.EOF && state != utf8Accept) {\n\t\treturn n, errInvalidUTF8\n\t}\n\treturn n, err\n}\n\n// UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n//\n// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to\n// deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n// sell copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n// IN THE SOFTWARE.\nvar utf8d = [...]byte{\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f\n\t1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f\n\t7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf\n\t8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df\n\t0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef\n\t0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff\n\t0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n\t1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n\t1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n\t1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n\t1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8\n}\n\nconst (\n\tutf8Accept = 0\n\tutf8Reject = 1\n)\n\nfunc decode(state int, x rune, b byte) (int, rune) {\n\tt := utf8d[b]\n\tif state != utf8Accept {\n\t\tx = rune(b&0x3f) | (x << 6)\n\t} else {\n\t\tx = rune((0xff >> t) & b)\n\t}\n\tstate = int(utf8d[256+state*16+int(t)])\n\treturn state, x\n}\n"
  },
  {
    "path": "examples/chat/README.md",
    "content": "# Chat Example\n\nThis application shows how to use the\n[websocket](https://github.com/gorilla/websocket) package to implement a simple\nweb chat application.\n\n## Running the example\n\nThe example requires a working Go development environment. The [Getting\nStarted](http://golang.org/doc/install) page describes how to install the\ndevelopment environment.\n\nOnce you have Go up and running, you can download, build and run the example\nusing the following commands.\n\n    $ go get github.com/gorilla/websocket\n    $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat`\n    $ go run *.go\n\nTo use the chat example, open http://localhost:8080/ in your browser.\n\n## Server\n\nThe server application defines two types, `Client` and `Hub`. The server\ncreates an instance of the `Client` type for each websocket connection. A\n`Client` acts as an intermediary between the websocket connection and a single\ninstance of the `Hub` type. The `Hub` maintains a set of registered clients and\nbroadcasts messages to the clients.\n\nThe application runs one goroutine for the `Hub` and two goroutines for each\n`Client`. The goroutines communicate with each other using channels. The `Hub`\nhas channels for registering clients, unregistering clients and broadcasting\nmessages. A `Client` has a buffered channel of outbound messages. One of the\nclient's goroutines reads messages from this channel and writes the messages to\nthe websocket. The other client goroutine reads messages from the websocket and\nsends them to the hub.\n\n### Hub \n\nThe code for the `Hub` type is in\n[hub.go](https://github.com/gorilla/websocket/blob/main/examples/chat/hub.go).\nThe application's `main` function starts the hub's `run` method as a goroutine.\nClients send requests to the hub using the `register`, `unregister` and\n`broadcast` channels.\n\nThe hub registers clients by adding the client pointer as a key in the\n`clients` map. The map value is always true.\n\nThe unregister code is a little more complicated. In addition to deleting the\nclient pointer from the `clients` map, the hub closes the clients's `send`\nchannel to signal the client that no more messages will be sent to the client.\n\nThe hub handles messages by looping over the registered clients and sending the\nmessage to the client's `send` channel. If the client's `send` buffer is full,\nthen the hub assumes that the client is dead or stuck. In this case, the hub\nunregisters the client and closes the websocket.\n\n### Client\n\nThe code for the `Client` type is in [client.go](https://github.com/gorilla/websocket/blob/main/examples/chat/client.go).\n\nThe `serveWs` function is registered by the application's `main` function as\nan HTTP handler. The handler upgrades the HTTP connection to the WebSocket\nprotocol, creates a client, registers the client with the hub and schedules the\nclient to be unregistered using a defer statement.\n\nNext, the HTTP handler starts the client's `writePump` method as a goroutine.\nThis method transfers messages from the client's send channel to the websocket\nconnection. The writer method exits when the channel is closed by the hub or\nthere's an error writing to the websocket connection.\n\nFinally, the HTTP handler calls the client's `readPump` method. This method\ntransfers inbound messages from the websocket to the hub.\n\nWebSocket connections [support one concurrent reader and one concurrent\nwriter](https://godoc.org/github.com/gorilla/websocket#hdr-Concurrency). The\napplication ensures that these concurrency requirements are met by executing\nall reads from the `readPump` goroutine and all writes from the `writePump`\ngoroutine.\n\nTo improve efficiency under high load, the `writePump` function coalesces\npending chat messages in the `send` channel to a single WebSocket message. This\nreduces the number of system calls and the amount of data sent over the\nnetwork.\n\n## Frontend\n\nThe frontend code is in [home.html](https://github.com/gorilla/websocket/blob/main/examples/chat/home.html).\n\nOn document load, the script checks for websocket functionality in the browser.\nIf websocket functionality is available, then the script opens a connection to\nthe server and registers a callback to handle messages from the server. The\ncallback appends the message to the chat log using the appendLog function.\n\nTo allow the user to manually scroll through the chat log without interruption\nfrom new messages, the `appendLog` function checks the scroll position before\nadding new content. If the chat log is scrolled to the bottom, then the\nfunction scrolls new content into view after adding the content. Otherwise, the\nscroll position is not changed.\n\nThe form handler writes the user input to the websocket and clears the input\nfield.\n"
  },
  {
    "path": "examples/chat/client.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nconst (\n\t// Time allowed to write a message to the peer.\n\twriteWait = 10 * time.Second\n\n\t// Time allowed to read the next pong message from the peer.\n\tpongWait = 60 * time.Second\n\n\t// Send pings to peer with this period. Must be less than pongWait.\n\tpingPeriod = (pongWait * 9) / 10\n\n\t// Maximum message size allowed from peer.\n\tmaxMessageSize = 512\n)\n\nvar (\n\tnewline = []byte{'\\n'}\n\tspace   = []byte{' '}\n)\n\nvar upgrader = websocket.Upgrader{\n\tReadBufferSize:  1024,\n\tWriteBufferSize: 1024,\n}\n\n// Client is a middleman between the websocket connection and the hub.\ntype Client struct {\n\thub *Hub\n\n\t// The websocket connection.\n\tconn *websocket.Conn\n\n\t// Buffered channel of outbound messages.\n\tsend chan []byte\n}\n\n// readPump pumps messages from the websocket connection to the hub.\n//\n// The application runs readPump in a per-connection goroutine. The application\n// ensures that there is at most one reader on a connection by executing all\n// reads from this goroutine.\nfunc (c *Client) readPump() {\n\tdefer func() {\n\t\tc.hub.unregister <- c\n\t\tc.conn.Close()\n\t}()\n\tc.conn.SetReadLimit(maxMessageSize)\n\tc.conn.SetReadDeadline(time.Now().Add(pongWait))\n\tc.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })\n\tfor {\n\t\t_, message, err := c.conn.ReadMessage()\n\t\tif err != nil {\n\t\t\tif websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {\n\t\t\t\tlog.Printf(\"error: %v\", err)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tmessage = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))\n\t\tc.hub.broadcast <- message\n\t}\n}\n\n// writePump pumps messages from the hub to the websocket connection.\n//\n// A goroutine running writePump is started for each connection. The\n// application ensures that there is at most one writer to a connection by\n// executing all writes from this goroutine.\nfunc (c *Client) writePump() {\n\tticker := time.NewTicker(pingPeriod)\n\tdefer func() {\n\t\tticker.Stop()\n\t\tc.conn.Close()\n\t}()\n\tfor {\n\t\tselect {\n\t\tcase message, ok := <-c.send:\n\t\t\tc.conn.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\tif !ok {\n\t\t\t\t// The hub closed the channel.\n\t\t\t\tc.conn.WriteMessage(websocket.CloseMessage, []byte{})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw, err := c.conn.NextWriter(websocket.TextMessage)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.Write(message)\n\n\t\t\t// Add queued chat messages to the current websocket message.\n\t\t\tn := len(c.send)\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\tw.Write(newline)\n\t\t\t\tw.Write(<-c.send)\n\t\t\t}\n\n\t\t\tif err := w.Close(); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase <-ticker.C:\n\t\t\tc.conn.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\tif err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// serveWs handles websocket requests from the peer.\nfunc serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {\n\tconn, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tclient := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}\n\tclient.hub.register <- client\n\n\t// Allow collection of memory referenced by the caller by doing all work in\n\t// new goroutines.\n\tgo client.writePump()\n\tgo client.readPump()\n}\n"
  },
  {
    "path": "examples/chat/home.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<title>Chat Example</title>\n<script type=\"text/javascript\">\nwindow.onload = function () {\n    var conn;\n    var msg = document.getElementById(\"msg\");\n    var log = document.getElementById(\"log\");\n\n    function appendLog(item) {\n        var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;\n        log.appendChild(item);\n        if (doScroll) {\n            log.scrollTop = log.scrollHeight - log.clientHeight;\n        }\n    }\n\n    document.getElementById(\"form\").onsubmit = function () {\n        if (!conn) {\n            return false;\n        }\n        if (!msg.value) {\n            return false;\n        }\n        conn.send(msg.value);\n        msg.value = \"\";\n        return false;\n    };\n\n    if (window[\"WebSocket\"]) {\n        conn = new WebSocket(\"ws://\" + document.location.host + \"/ws\");\n        conn.onclose = function (evt) {\n            var item = document.createElement(\"div\");\n            item.innerHTML = \"<b>Connection closed.</b>\";\n            appendLog(item);\n        };\n        conn.onmessage = function (evt) {\n            var messages = evt.data.split('\\n');\n            for (var i = 0; i < messages.length; i++) {\n                var item = document.createElement(\"div\");\n                item.innerText = messages[i];\n                appendLog(item);\n            }\n        };\n    } else {\n        var item = document.createElement(\"div\");\n        item.innerHTML = \"<b>Your browser does not support WebSockets.</b>\";\n        appendLog(item);\n    }\n};\n</script>\n<style type=\"text/css\">\nhtml {\n    overflow: hidden;\n}\n\nbody {\n    overflow: hidden;\n    padding: 0;\n    margin: 0;\n    width: 100%;\n    height: 100%;\n    background: gray;\n}\n\n#log {\n    background: white;\n    margin: 0;\n    padding: 0.5em 0.5em 0.5em 0.5em;\n    position: absolute;\n    top: 0.5em;\n    left: 0.5em;\n    right: 0.5em;\n    bottom: 3em;\n    overflow: auto;\n}\n\n#form {\n    padding: 0 0.5em 0 0.5em;\n    margin: 0;\n    position: absolute;\n    bottom: 1em;\n    left: 0px;\n    width: 100%;\n    overflow: hidden;\n}\n\n</style>\n</head>\n<body>\n<div id=\"log\"></div>\n<form id=\"form\">\n    <input type=\"submit\" value=\"Send\" />\n    <input type=\"text\" id=\"msg\" size=\"64\" autofocus />\n</form>\n</body>\n</html>\n"
  },
  {
    "path": "examples/chat/hub.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\n// Hub maintains the set of active clients and broadcasts messages to the\n// clients.\ntype Hub struct {\n\t// Registered clients.\n\tclients map[*Client]bool\n\n\t// Inbound messages from the clients.\n\tbroadcast chan []byte\n\n\t// Register requests from the clients.\n\tregister chan *Client\n\n\t// Unregister requests from clients.\n\tunregister chan *Client\n}\n\nfunc newHub() *Hub {\n\treturn &Hub{\n\t\tbroadcast:  make(chan []byte),\n\t\tregister:   make(chan *Client),\n\t\tunregister: make(chan *Client),\n\t\tclients:    make(map[*Client]bool),\n\t}\n}\n\nfunc (h *Hub) run() {\n\tfor {\n\t\tselect {\n\t\tcase client := <-h.register:\n\t\t\th.clients[client] = true\n\t\tcase client := <-h.unregister:\n\t\t\tif _, ok := h.clients[client]; ok {\n\t\t\t\tdelete(h.clients, client)\n\t\t\t\tclose(client.send)\n\t\t\t}\n\t\tcase message := <-h.broadcast:\n\t\t\tfor client := range h.clients {\n\t\t\t\tselect {\n\t\t\t\tcase client.send <- message:\n\t\t\t\tdefault:\n\t\t\t\t\tclose(client.send)\n\t\t\t\t\tdelete(h.clients, client)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/chat/main.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"net/http\"\n)\n\nvar addr = flag.String(\"addr\", \":8080\", \"http service address\")\n\nfunc serveHome(w http.ResponseWriter, r *http.Request) {\n\tlog.Println(r.URL)\n\tif r.URL.Path != \"/\" {\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"Method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\thttp.ServeFile(w, r, \"home.html\")\n}\n\nfunc main() {\n\tflag.Parse()\n\thub := newHub()\n\tgo hub.run()\n\thttp.HandleFunc(\"/\", serveHome)\n\thttp.HandleFunc(\"/ws\", func(w http.ResponseWriter, r *http.Request) {\n\t\tserveWs(hub, w, r)\n\t})\n\terr := http.ListenAndServe(*addr, nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err)\n\t}\n}\n"
  },
  {
    "path": "examples/command/README.md",
    "content": "# Command example\n\nThis example connects a websocket connection to stdin and stdout of a command.\nReceived messages are written to stdin followed by a `\\n`. Each line read from\nstandard out is sent as a message to the client.\n\n    $ go get github.com/gorilla/websocket\n    $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command`\n    $ go run main.go <command and arguments to run>\n    # Open http://localhost:8080/ .\n\nTry the following commands.\n\n    # Echo sent messages to the output area.\n    $ go run main.go cat\n\n    # Run a shell.Try sending \"ls\" and \"cat main.go\".\n    $ go run main.go sh\n\n"
  },
  {
    "path": "examples/command/home.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<title>Command Example</title>\n<script type=\"text/javascript\">\nwindow.onload = function () {\n    var conn;\n    var msg = document.getElementById(\"msg\");\n    var log = document.getElementById(\"log\");\n\n    function appendLog(item) {\n        var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;\n        log.appendChild(item);\n        if (doScroll) {\n            log.scrollTop = log.scrollHeight - log.clientHeight;\n        }\n    }\n\n    document.getElementById(\"form\").onsubmit = function () {\n        if (!conn) {\n            return false;\n        }\n        if (!msg.value) {\n            return false;\n        }\n        conn.send(msg.value);\n        msg.value = \"\";\n        return false;\n    };\n\n    if (window[\"WebSocket\"]) {\n        conn = new WebSocket(\"ws://\" + document.location.host + \"/ws\");\n        conn.onclose = function (evt) {\n            var item = document.createElement(\"div\");\n            item.innerHTML = \"<b>Connection closed.</b>\";\n            appendLog(item);\n        };\n        conn.onmessage = function (evt) {\n            var messages = evt.data.split('\\n');\n            for (var i = 0; i < messages.length; i++) {\n                var item = document.createElement(\"div\");\n                item.innerText = messages[i];\n                appendLog(item);\n            }\n        };\n    } else {\n        var item = document.createElement(\"div\");\n        item.innerHTML = \"<b>Your browser does not support WebSockets.</b>\";\n        appendLog(item);\n    }\n};\n</script>\n<style type=\"text/css\">\nhtml {\n    overflow: hidden;\n}\n\nbody {\n    overflow: hidden;\n    padding: 0;\n    margin: 0;\n    width: 100%;\n    height: 100%;\n    background: gray;\n}\n\n#log {\n    background: white;\n    margin: 0;\n    padding: 0.5em 0.5em 0.5em 0.5em;\n    position: absolute;\n    top: 0.5em;\n    left: 0.5em;\n    right: 0.5em;\n    bottom: 3em;\n    overflow: auto;\n}\n\n#log pre {\n  margin: 0;\n}\n\n#form {\n    padding: 0 0.5em 0 0.5em;\n    margin: 0;\n    position: absolute;\n    bottom: 1em;\n    left: 0px;\n    width: 100%;\n    overflow: hidden;\n}\n\n</style>\n</head>\n<body>\n<div id=\"log\"></div>\n<form id=\"form\">\n    <input type=\"submit\" value=\"Send\" />\n    <input type=\"text\" id=\"msg\" size=\"64\"/>\n</form>\n</body>\n</html>\n"
  },
  {
    "path": "examples/command/main.go",
    "content": "// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar (\n\taddr    = flag.String(\"addr\", \"127.0.0.1:8080\", \"http service address\")\n\tcmdPath string\n)\n\nconst (\n\t// Time allowed to write a message to the peer.\n\twriteWait = 10 * time.Second\n\n\t// Maximum message size allowed from peer.\n\tmaxMessageSize = 8192\n\n\t// Time allowed to read the next pong message from the peer.\n\tpongWait = 60 * time.Second\n\n\t// Send pings to peer with this period. Must be less than pongWait.\n\tpingPeriod = (pongWait * 9) / 10\n\n\t// Time to wait before force close on connection.\n\tcloseGracePeriod = 10 * time.Second\n)\n\nfunc pumpStdin(ws *websocket.Conn, w io.Writer) {\n\tdefer ws.Close()\n\tws.SetReadLimit(maxMessageSize)\n\tws.SetReadDeadline(time.Now().Add(pongWait))\n\tws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })\n\tfor {\n\t\t_, message, err := ws.ReadMessage()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tmessage = append(message, '\\n')\n\t\tif _, err := w.Write(message); err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {\n\tdefer func() {\n\t}()\n\ts := bufio.NewScanner(r)\n\tfor s.Scan() {\n\t\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\t\tif err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {\n\t\t\tws.Close()\n\t\t\tbreak\n\t\t}\n\t}\n\tif s.Err() != nil {\n\t\tlog.Println(\"scan:\", s.Err())\n\t}\n\tclose(done)\n\n\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\tws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, \"\"))\n\ttime.Sleep(closeGracePeriod)\n\tws.Close()\n}\n\nfunc ping(ws *websocket.Conn, done chan struct{}) {\n\tticker := time.NewTicker(pingPeriod)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tif err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {\n\t\t\t\tlog.Println(\"ping:\", err)\n\t\t\t}\n\t\tcase <-done:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc internalError(ws *websocket.Conn, msg string, err error) {\n\tlog.Println(msg, err)\n\tws.WriteMessage(websocket.TextMessage, []byte(\"Internal server error.\"))\n}\n\nvar upgrader = websocket.Upgrader{}\n\nfunc serveWs(w http.ResponseWriter, r *http.Request) {\n\tws, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Println(\"upgrade:\", err)\n\t\treturn\n\t}\n\n\tdefer ws.Close()\n\n\toutr, outw, err := os.Pipe()\n\tif err != nil {\n\t\tinternalError(ws, \"stdout:\", err)\n\t\treturn\n\t}\n\tdefer outr.Close()\n\tdefer outw.Close()\n\n\tinr, inw, err := os.Pipe()\n\tif err != nil {\n\t\tinternalError(ws, \"stdin:\", err)\n\t\treturn\n\t}\n\tdefer inr.Close()\n\tdefer inw.Close()\n\n\tproc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{\n\t\tFiles: []*os.File{inr, outw, outw},\n\t})\n\tif err != nil {\n\t\tinternalError(ws, \"start:\", err)\n\t\treturn\n\t}\n\n\tinr.Close()\n\toutw.Close()\n\n\tstdoutDone := make(chan struct{})\n\tgo pumpStdout(ws, outr, stdoutDone)\n\tgo ping(ws, stdoutDone)\n\n\tpumpStdin(ws, inw)\n\n\t// Some commands will exit when stdin is closed.\n\tinw.Close()\n\n\t// Other commands need a bonk on the head.\n\tif err := proc.Signal(os.Interrupt); err != nil {\n\t\tlog.Println(\"inter:\", err)\n\t}\n\n\tselect {\n\tcase <-stdoutDone:\n\tcase <-time.After(time.Second):\n\t\t// A bigger bonk on the head.\n\t\tif err := proc.Signal(os.Kill); err != nil {\n\t\t\tlog.Println(\"term:\", err)\n\t\t}\n\t\t<-stdoutDone\n\t}\n\n\tif _, err := proc.Wait(); err != nil {\n\t\tlog.Println(\"wait:\", err)\n\t}\n}\n\nfunc serveHome(w http.ResponseWriter, r *http.Request) {\n\tif r.URL.Path != \"/\" {\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"Method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\thttp.ServeFile(w, r, \"home.html\")\n}\n\nfunc main() {\n\tflag.Parse()\n\tif len(flag.Args()) < 1 {\n\t\tlog.Fatal(\"must specify at least one argument\")\n\t}\n\tvar err error\n\tcmdPath, err = exec.LookPath(flag.Args()[0])\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\thttp.HandleFunc(\"/\", serveHome)\n\thttp.HandleFunc(\"/ws\", serveWs)\n\tlog.Fatal(http.ListenAndServe(*addr, nil))\n}\n"
  },
  {
    "path": "examples/echo/README.md",
    "content": "# Client and server example\n\nThis example shows a simple client and server.\n\nThe server echoes messages sent to it. The client sends a message every second\nand prints all messages received.\n\nTo run the example, start the server:\n\n    $ go run server.go\n\nNext, start the client:\n\n    $ go run client.go\n\nThe server includes a simple web client. To use the client, open\nhttp://127.0.0.1:8080 in the browser and follow the instructions on the page.\n"
  },
  {
    "path": "examples/echo/client.go",
    "content": "// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build ignore\n// +build ignore\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/signal\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar addr = flag.String(\"addr\", \"localhost:8080\", \"http service address\")\n\nfunc main() {\n\tflag.Parse()\n\tlog.SetFlags(0)\n\n\tinterrupt := make(chan os.Signal, 1)\n\tsignal.Notify(interrupt, os.Interrupt)\n\n\tu := url.URL{Scheme: \"ws\", Host: *addr, Path: \"/echo\"}\n\tlog.Printf(\"connecting to %s\", u.String())\n\n\tc, _, err := websocket.DefaultDialer.Dial(u.String(), nil)\n\tif err != nil {\n\t\tlog.Fatal(\"dial:\", err)\n\t}\n\tdefer c.Close()\n\n\tdone := make(chan struct{})\n\n\tgo func() {\n\t\tdefer close(done)\n\t\tfor {\n\t\t\t_, message, err := c.ReadMessage()\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"read:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"recv: %s\", message)\n\t\t}\n\t}()\n\n\tticker := time.NewTicker(time.Second)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-done:\n\t\t\treturn\n\t\tcase t := <-ticker.C:\n\t\t\terr := c.WriteMessage(websocket.TextMessage, []byte(t.String()))\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"write:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\tcase <-interrupt:\n\t\t\tlog.Println(\"interrupt\")\n\n\t\t\t// Cleanly close the connection by sending a close message and then\n\t\t\t// waiting (with timeout) for the server to close the connection.\n\t\t\terr := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, \"\"))\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(\"write close:\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\tcase <-time.After(time.Second):\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "examples/echo/server.go",
    "content": "// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n//go:build ignore\n// +build ignore\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"html/template\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar addr = flag.String(\"addr\", \"localhost:8080\", \"http service address\")\n\nvar upgrader = websocket.Upgrader{} // use default options\n\nfunc echo(w http.ResponseWriter, r *http.Request) {\n\tc, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Print(\"upgrade:\", err)\n\t\treturn\n\t}\n\tdefer c.Close()\n\tfor {\n\t\tmt, message, err := c.ReadMessage()\n\t\tif err != nil {\n\t\t\tlog.Println(\"read:\", err)\n\t\t\tbreak\n\t\t}\n\t\tlog.Printf(\"recv: %s\", message)\n\t\terr = c.WriteMessage(mt, message)\n\t\tif err != nil {\n\t\t\tlog.Println(\"write:\", err)\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc home(w http.ResponseWriter, r *http.Request) {\n\thomeTemplate.Execute(w, \"ws://\"+r.Host+\"/echo\")\n}\n\nfunc main() {\n\tflag.Parse()\n\tlog.SetFlags(0)\n\thttp.HandleFunc(\"/echo\", echo)\n\thttp.HandleFunc(\"/\", home)\n\tlog.Fatal(http.ListenAndServe(*addr, nil))\n}\n\nvar homeTemplate = template.Must(template.New(\"\").Parse(`\n<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<script>  \nwindow.addEventListener(\"load\", function(evt) {\n\n    var output = document.getElementById(\"output\");\n    var input = document.getElementById(\"input\");\n    var ws;\n\n    var print = function(message) {\n        var d = document.createElement(\"div\");\n        d.textContent = message;\n        output.appendChild(d);\n        output.scroll(0, output.scrollHeight);\n    };\n\n    document.getElementById(\"open\").onclick = function(evt) {\n        if (ws) {\n            return false;\n        }\n        ws = new WebSocket(\"{{.}}\");\n        ws.onopen = function(evt) {\n            print(\"OPEN\");\n        }\n        ws.onclose = function(evt) {\n            print(\"CLOSE\");\n            ws = null;\n        }\n        ws.onmessage = function(evt) {\n            print(\"RESPONSE: \" + evt.data);\n        }\n        ws.onerror = function(evt) {\n            print(\"ERROR: \" + evt.data);\n        }\n        return false;\n    };\n\n    document.getElementById(\"send\").onclick = function(evt) {\n        if (!ws) {\n            return false;\n        }\n        print(\"SEND: \" + input.value);\n        ws.send(input.value);\n        return false;\n    };\n\n    document.getElementById(\"close\").onclick = function(evt) {\n        if (!ws) {\n            return false;\n        }\n        ws.close();\n        return false;\n    };\n\n});\n</script>\n</head>\n<body>\n<table>\n<tr><td valign=\"top\" width=\"50%\">\n<p>Click \"Open\" to create a connection to the server, \n\"Send\" to send a message to the server and \"Close\" to close the connection. \nYou can change the message and send multiple times.\n<p>\n<form>\n<button id=\"open\">Open</button>\n<button id=\"close\">Close</button>\n<p><input id=\"input\" type=\"text\" value=\"Hello world!\">\n<button id=\"send\">Send</button>\n</form>\n</td><td valign=\"top\" width=\"50%\">\n<div id=\"output\" style=\"max-height: 70vh;overflow-y: scroll;\"></div>\n</td></tr></table>\n</body>\n</html>\n`))\n"
  },
  {
    "path": "examples/filewatch/README.md",
    "content": "# File Watch example.\n\nThis example sends a file to the browser client for display whenever the file is modified.\n\n    $ go get github.com/gorilla/websocket\n    $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch`\n    $ go run main.go <name of file to watch>\n    # Open http://localhost:8080/ .\n    # Modify the file to see it update in the browser.\n"
  },
  {
    "path": "examples/filewatch/main.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"html/template\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nconst (\n\t// Time allowed to write the file to the client.\n\twriteWait = 10 * time.Second\n\n\t// Time allowed to read the next pong message from the client.\n\tpongWait = 60 * time.Second\n\n\t// Send pings to client with this period. Must be less than pongWait.\n\tpingPeriod = (pongWait * 9) / 10\n\n\t// Poll file for changes with this period.\n\tfilePeriod = 10 * time.Second\n)\n\nvar (\n\taddr      = flag.String(\"addr\", \":8080\", \"http service address\")\n\thomeTempl = template.Must(template.New(\"\").Parse(homeHTML))\n\tfilename  string\n\tupgrader  = websocket.Upgrader{\n\t\tReadBufferSize:  1024,\n\t\tWriteBufferSize: 1024,\n\t}\n)\n\nfunc readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {\n\tfi, err := os.Stat(filename)\n\tif err != nil {\n\t\treturn nil, lastMod, err\n\t}\n\tif !fi.ModTime().After(lastMod) {\n\t\treturn nil, lastMod, nil\n\t}\n\tp, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, fi.ModTime(), err\n\t}\n\treturn p, fi.ModTime(), nil\n}\n\nfunc reader(ws *websocket.Conn) {\n\tdefer ws.Close()\n\tws.SetReadLimit(512)\n\tws.SetReadDeadline(time.Now().Add(pongWait))\n\tws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })\n\tfor {\n\t\t_, _, err := ws.ReadMessage()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc writer(ws *websocket.Conn, lastMod time.Time) {\n\tlastError := \"\"\n\tpingTicker := time.NewTicker(pingPeriod)\n\tfileTicker := time.NewTicker(filePeriod)\n\tdefer func() {\n\t\tpingTicker.Stop()\n\t\tfileTicker.Stop()\n\t\tws.Close()\n\t}()\n\tfor {\n\t\tselect {\n\t\tcase <-fileTicker.C:\n\t\t\tvar p []byte\n\t\t\tvar err error\n\n\t\t\tp, lastMod, err = readFileIfModified(lastMod)\n\n\t\t\tif err != nil {\n\t\t\t\tif s := err.Error(); s != lastError {\n\t\t\t\t\tlastError = s\n\t\t\t\t\tp = []byte(lastError)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlastError = \"\"\n\t\t\t}\n\n\t\t\tif p != nil {\n\t\t\t\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\t\tif err := ws.WriteMessage(websocket.TextMessage, p); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase <-pingTicker.C:\n\t\t\tws.SetWriteDeadline(time.Now().Add(writeWait))\n\t\t\tif err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc serveWs(w http.ResponseWriter, r *http.Request) {\n\tws, err := upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tif _, ok := err.(websocket.HandshakeError); !ok {\n\t\t\tlog.Println(err)\n\t\t}\n\t\treturn\n\t}\n\n\tvar lastMod time.Time\n\tif n, err := strconv.ParseInt(r.FormValue(\"lastMod\"), 16, 64); err == nil {\n\t\tlastMod = time.Unix(0, n)\n\t}\n\n\tgo writer(ws, lastMod)\n\treader(ws)\n}\n\nfunc serveHome(w http.ResponseWriter, r *http.Request) {\n\tif r.URL.Path != \"/\" {\n\t\thttp.Error(w, \"Not found\", http.StatusNotFound)\n\t\treturn\n\t}\n\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"Method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\tw.Header().Set(\"Content-Type\", \"text/html; charset=utf-8\")\n\tp, lastMod, err := readFileIfModified(time.Time{})\n\tif err != nil {\n\t\tp = []byte(err.Error())\n\t\tlastMod = time.Unix(0, 0)\n\t}\n\tvar v = struct {\n\t\tHost    string\n\t\tData    string\n\t\tLastMod string\n\t}{\n\t\tr.Host,\n\t\tstring(p),\n\t\tstrconv.FormatInt(lastMod.UnixNano(), 16),\n\t}\n\thomeTempl.Execute(w, &v)\n}\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() != 1 {\n\t\tlog.Fatal(\"filename not specified\")\n\t}\n\tfilename = flag.Args()[0]\n\thttp.HandleFunc(\"/\", serveHome)\n\thttp.HandleFunc(\"/ws\", serveWs)\n\tif err := http.ListenAndServe(*addr, nil); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nconst homeHTML = `<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <title>WebSocket Example</title>\n    </head>\n    <body>\n        <pre id=\"fileData\">{{.Data}}</pre>\n        <script type=\"text/javascript\">\n            (function() {\n                var data = document.getElementById(\"fileData\");\n                var conn = new WebSocket(\"ws://{{.Host}}/ws?lastMod={{.LastMod}}\");\n                conn.onclose = function(evt) {\n                    data.textContent = 'Connection closed';\n                }\n                conn.onmessage = function(evt) {\n                    console.log('file updated');\n                    data.textContent = evt.data;\n                }\n            })();\n        </script>\n    </body>\n</html>\n`\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/gorilla/websocket\n\ngo 1.20\n\nretract (\n    v1.5.2 // tag accidentally overwritten\n)\n\nrequire golang.org/x/net v0.26.0\n"
  },
  {
    "path": "go.sum",
    "content": "golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=\ngolang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=\n"
  },
  {
    "path": "join.go",
    "content": "// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"io\"\n\t\"strings\"\n)\n\n// JoinMessages concatenates received messages to create a single io.Reader.\n// The string term is appended to each message. The returned reader does not\n// support concurrent calls to the Read method.\nfunc JoinMessages(c *Conn, term string) io.Reader {\n\treturn &joinReader{c: c, term: term}\n}\n\ntype joinReader struct {\n\tc    *Conn\n\tterm string\n\tr    io.Reader\n}\n\nfunc (r *joinReader) Read(p []byte) (int, error) {\n\tif r.r == nil {\n\t\tvar err error\n\t\t_, r.r, err = r.c.NextReader()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif r.term != \"\" {\n\t\t\tr.r = io.MultiReader(r.r, strings.NewReader(r.term))\n\t\t}\n\t}\n\tn, err := r.r.Read(p)\n\tif err == io.EOF {\n\t\terr = nil\n\t\tr.r = nil\n\t}\n\treturn n, err\n}\n"
  },
  {
    "path": "join_test.go",
    "content": "// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestJoinMessages(t *testing.T) {\n\tmessages := []string{\"a\", \"bc\", \"def\", \"ghij\", \"klmno\", \"0\", \"12\", \"345\", \"6789\"}\n\tfor _, readChunk := range []int{1, 2, 3, 4, 5, 6, 7} {\n\t\tfor _, term := range []string{\"\", \",\"} {\n\t\t\tvar connBuf bytes.Buffer\n\t\t\twc := newTestConn(nil, &connBuf, true)\n\t\t\trc := newTestConn(&connBuf, nil, false)\n\t\t\tfor _, m := range messages {\n\t\t\t\t_ = wc.WriteMessage(BinaryMessage, []byte(m))\n\t\t\t}\n\n\t\t\tvar result bytes.Buffer\n\t\t\t_, err := io.CopyBuffer(&result, JoinMessages(rc, term), make([]byte, readChunk))\n\t\t\tif IsUnexpectedCloseError(err, CloseAbnormalClosure) {\n\t\t\t\tt.Errorf(\"readChunk=%d, term=%q: unexpected error %v\", readChunk, term, err)\n\t\t\t}\n\t\t\twant := strings.Join(messages, term) + term\n\t\t\tif result.String() != want {\n\t\t\t\tt.Errorf(\"readChunk=%d, term=%q, got %q, want %q\", readChunk, term, result.String(), want)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "json.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// Deprecated: Use c.WriteJSON instead.\nfunc WriteJSON(c *Conn, v interface{}) error {\n\treturn c.WriteJSON(v)\n}\n\n// WriteJSON writes the JSON encoding of v as a message.\n//\n// See the documentation for encoding/json Marshal for details about the\n// conversion of Go values to JSON.\nfunc (c *Conn) WriteJSON(v interface{}) error {\n\tw, err := c.NextWriter(TextMessage)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr1 := json.NewEncoder(w).Encode(v)\n\terr2 := w.Close()\n\tif err1 != nil {\n\t\treturn err1\n\t}\n\treturn err2\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// Deprecated: Use c.ReadJSON instead.\nfunc ReadJSON(c *Conn, v interface{}) error {\n\treturn c.ReadJSON(v)\n}\n\n// ReadJSON reads the next JSON-encoded message from the connection and stores\n// it in the value pointed to by v.\n//\n// See the documentation for the encoding/json Unmarshal function for details\n// about the conversion of JSON to a Go value.\nfunc (c *Conn) ReadJSON(v interface{}) error {\n\t_, r, err := c.NextReader()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = json.NewDecoder(r).Decode(v)\n\tif err == io.EOF {\n\t\t// One value is expected in the message.\n\t\terr = io.ErrUnexpectedEOF\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "json_test.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestJSON(t *testing.T) {\n\tvar buf bytes.Buffer\n\twc := newTestConn(nil, &buf, true)\n\trc := newTestConn(&buf, nil, false)\n\n\tvar actual, expect struct {\n\t\tA int\n\t\tB string\n\t}\n\texpect.A = 1\n\texpect.B = \"hello\"\n\n\tif err := wc.WriteJSON(&expect); err != nil {\n\t\tt.Fatal(\"write\", err)\n\t}\n\n\tif err := rc.ReadJSON(&actual); err != nil {\n\t\tt.Fatal(\"read\", err)\n\t}\n\n\tif !reflect.DeepEqual(&actual, &expect) {\n\t\tt.Fatal(\"equal\", actual, expect)\n\t}\n}\n\nfunc TestPartialJSONRead(t *testing.T) {\n\tvar buf0, buf1 bytes.Buffer\n\twc := newTestConn(nil, &buf0, true)\n\trc := newTestConn(&buf0, &buf1, false)\n\n\tvar v struct {\n\t\tA int\n\t\tB string\n\t}\n\tv.A = 1\n\tv.B = \"hello\"\n\n\tmessageCount := 0\n\n\t// Partial JSON values.\n\n\tdata, err := json.Marshal(v)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor i := len(data) - 1; i >= 0; i-- {\n\t\tif err := wc.WriteMessage(TextMessage, data[:i]); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tmessageCount++\n\t}\n\n\t// Whitespace.\n\n\tif err := wc.WriteMessage(TextMessage, []byte(\" \")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tmessageCount++\n\n\t// Close.\n\n\tif err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, \"\")); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor i := 0; i < messageCount; i++ {\n\t\terr := rc.ReadJSON(&v)\n\t\tif err != io.ErrUnexpectedEOF {\n\t\t\tt.Error(\"read\", i, err)\n\t\t}\n\t}\n\n\terr = rc.ReadJSON(&v)\n\tif _, ok := err.(*CloseError); !ok {\n\t\tt.Error(\"final\", err)\n\t}\n}\n\nfunc TestDeprecatedJSON(t *testing.T) {\n\tvar buf bytes.Buffer\n\twc := newTestConn(nil, &buf, true)\n\trc := newTestConn(&buf, nil, false)\n\n\tvar actual, expect struct {\n\t\tA int\n\t\tB string\n\t}\n\texpect.A = 1\n\texpect.B = \"hello\"\n\n\tif err := WriteJSON(wc, &expect); err != nil {\n\t\tt.Fatal(\"write\", err)\n\t}\n\n\tif err := ReadJSON(rc, &actual); err != nil {\n\t\tt.Fatal(\"read\", err)\n\t}\n\n\tif !reflect.DeepEqual(&actual, &expect) {\n\t\tt.Fatal(\"equal\", actual, expect)\n\t}\n}\n"
  },
  {
    "path": "mask.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n//go:build !appengine\n// +build !appengine\n\npackage websocket\n\nimport \"unsafe\"\n\nconst wordSize = int(unsafe.Sizeof(uintptr(0)))\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\t// Mask one byte at a time for small buffers.\n\tif len(b) < 2*wordSize {\n\t\tfor i := range b {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\treturn pos & 3\n\t}\n\n\t// Mask one byte at a time to word boundary.\n\tif n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {\n\t\tn = wordSize - n\n\t\tfor i := range b[:n] {\n\t\t\tb[i] ^= key[pos&3]\n\t\t\tpos++\n\t\t}\n\t\tb = b[n:]\n\t}\n\n\t// Create aligned word size key.\n\tvar k [wordSize]byte\n\tfor i := range k {\n\t\tk[i] = key[(pos+i)&3]\n\t}\n\tkw := *(*uintptr)(unsafe.Pointer(&k))\n\n\t// Mask one word at a time.\n\tn := (len(b) / wordSize) * wordSize\n\tfor i := 0; i < n; i += wordSize {\n\t\t*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw\n\t}\n\n\t// Mask one byte at a time for remaining bytes.\n\tb = b[n:]\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\n\treturn pos & 3\n}\n"
  },
  {
    "path": "mask_safe.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n//go:build appengine\n// +build appengine\n\npackage websocket\n\nfunc maskBytes(key [4]byte, pos int, b []byte) int {\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\treturn pos & 3\n}\n"
  },
  {
    "path": "mask_test.go",
    "content": "// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.  Use of\n// this source code is governed by a BSD-style license that can be found in the\n// LICENSE file.\n\n// !appengine\n\npackage websocket\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc maskBytesByByte(key [4]byte, pos int, b []byte) int {\n\tfor i := range b {\n\t\tb[i] ^= key[pos&3]\n\t\tpos++\n\t}\n\treturn pos & 3\n}\n\nfunc notzero(b []byte) int {\n\tfor i := range b {\n\t\tif b[i] != 0 {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc TestMaskBytes(t *testing.T) {\n\tkey := [4]byte{1, 2, 3, 4}\n\tfor size := 1; size <= 1024; size++ {\n\t\tfor align := 0; align < wordSize; align++ {\n\t\t\tfor pos := 0; pos < 4; pos++ {\n\t\t\t\tb := make([]byte, size+align)[align:]\n\t\t\t\tmaskBytes(key, pos, b)\n\t\t\t\tmaskBytesByByte(key, pos, b)\n\t\t\t\tif i := notzero(b); i >= 0 {\n\t\t\t\t\tt.Errorf(\"size:%d, align:%d, pos:%d, offset:%d\", size, align, pos, i)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkMaskBytes(b *testing.B) {\n\tfor _, size := range []int{2, 4, 8, 16, 32, 512, 1024} {\n\t\tb.Run(fmt.Sprintf(\"size-%d\", size), func(b *testing.B) {\n\t\t\tfor _, align := range []int{wordSize / 2} {\n\t\t\t\tb.Run(fmt.Sprintf(\"align-%d\", align), func(b *testing.B) {\n\t\t\t\t\tfor _, fn := range []struct {\n\t\t\t\t\t\tname string\n\t\t\t\t\t\tfn   func(key [4]byte, pos int, b []byte) int\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\"byte\", maskBytesByByte},\n\t\t\t\t\t\t{\"word\", maskBytes},\n\t\t\t\t\t} {\n\t\t\t\t\t\tb.Run(fn.name, func(b *testing.B) {\n\t\t\t\t\t\t\tkey := newMaskKey()\n\t\t\t\t\t\t\tdata := make([]byte, size+align)[align:]\n\t\t\t\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\t\t\t\tfn.fn(key, 0, data)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tb.SetBytes(int64(len(data)))\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "prepared.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\n// PreparedMessage caches on the wire representations of a message payload.\n// Use PreparedMessage to efficiently send a message payload to multiple\n// connections. PreparedMessage is especially useful when compression is used\n// because the CPU and memory expensive compression operation can be executed\n// once for a given set of compression options.\ntype PreparedMessage struct {\n\tmessageType int\n\tdata        []byte\n\tmu          sync.Mutex\n\tframes      map[prepareKey]*preparedFrame\n}\n\n// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.\ntype prepareKey struct {\n\tisServer         bool\n\tcompress         bool\n\tcompressionLevel int\n}\n\n// preparedFrame contains data in wire representation.\ntype preparedFrame struct {\n\tonce sync.Once\n\tdata []byte\n}\n\n// NewPreparedMessage returns an initialized PreparedMessage. You can then send\n// it to connection using WritePreparedMessage method. Valid wire\n// representation will be calculated lazily only once for a set of current\n// connection options.\nfunc NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {\n\tpm := &PreparedMessage{\n\t\tmessageType: messageType,\n\t\tframes:      make(map[prepareKey]*preparedFrame),\n\t\tdata:        data,\n\t}\n\n\t// Prepare a plain server frame.\n\t_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// To protect against caller modifying the data argument, remember the data\n\t// copied to the plain server frame.\n\tpm.data = frameData[len(frameData)-len(data):]\n\treturn pm, nil\n}\n\nfunc (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {\n\tpm.mu.Lock()\n\tframe, ok := pm.frames[key]\n\tif !ok {\n\t\tframe = &preparedFrame{}\n\t\tpm.frames[key] = frame\n\t}\n\tpm.mu.Unlock()\n\n\tvar err error\n\tframe.once.Do(func() {\n\t\t// Prepare a frame using a 'fake' connection.\n\t\t// TODO: Refactor code in conn.go to allow more direct construction of\n\t\t// the frame.\n\t\tmu := make(chan struct{}, 1)\n\t\tmu <- struct{}{}\n\t\tvar nc prepareConn\n\t\tc := &Conn{\n\t\t\tconn:                   &nc,\n\t\t\tmu:                     mu,\n\t\t\tisServer:               key.isServer,\n\t\t\tcompressionLevel:       key.compressionLevel,\n\t\t\tenableWriteCompression: true,\n\t\t\twriteBuf:               make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),\n\t\t}\n\t\tif key.compress {\n\t\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\t}\n\t\terr = c.WriteMessage(pm.messageType, pm.data)\n\t\tframe.data = nc.buf.Bytes()\n\t})\n\treturn pm.messageType, frame.data, err\n}\n\ntype prepareConn struct {\n\tbuf bytes.Buffer\n\tnet.Conn\n}\n\nfunc (pc *prepareConn) Write(p []byte) (int, error)        { return pc.buf.Write(p) }\nfunc (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }\n"
  },
  {
    "path": "prepared_test.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"math/rand\"\n\t\"testing\"\n)\n\nvar preparedMessageTests = []struct {\n\tmessageType            int\n\tisServer               bool\n\tenableWriteCompression bool\n\tcompressionLevel       int\n}{\n\t// Server\n\t{TextMessage, true, false, flate.BestSpeed},\n\t{TextMessage, true, true, flate.BestSpeed},\n\t{TextMessage, true, true, flate.BestCompression},\n\t{PingMessage, true, false, flate.BestSpeed},\n\t{PingMessage, true, true, flate.BestSpeed},\n\n\t// Client\n\t{TextMessage, false, false, flate.BestSpeed},\n\t{TextMessage, false, true, flate.BestSpeed},\n\t{TextMessage, false, true, flate.BestCompression},\n\t{PingMessage, false, false, flate.BestSpeed},\n\t{PingMessage, false, true, flate.BestSpeed},\n}\n\nfunc TestPreparedMessage(t *testing.T) {\n\ttestRand := rand.New(rand.NewSource(99))\n\tprevMaskRand := maskRand\n\tmaskRand = testRand\n\tdefer func() { maskRand = prevMaskRand }()\n\n\tfor _, tt := range preparedMessageTests {\n\t\tvar data = []byte(\"this is a test\")\n\t\tvar buf bytes.Buffer\n\t\tc := newTestConn(nil, &buf, tt.isServer)\n\t\tif tt.enableWriteCompression {\n\t\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\t}\n\t\tif err := c.SetCompressionLevel(tt.compressionLevel); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\t// Seed random number generator for consistent frame mask.\n\t\ttestRand.Seed(1234)\n\n\t\tif err := c.WriteMessage(tt.messageType, data); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\twant := buf.String()\n\n\t\tpm, err := NewPreparedMessage(tt.messageType, data)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\t// Scribble on data to ensure that NewPreparedMessage takes a snapshot.\n\t\tcopy(data, \"hello world\")\n\n\t\t// Seed random number generator for consistent frame mask.\n\t\ttestRand.Seed(1234)\n\n\t\tbuf.Reset()\n\t\tif err := c.WritePreparedMessage(pm); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tgot := buf.String()\n\n\t\tif got != want {\n\t\t\tt.Errorf(\"write message != prepared message for %+v\", tt)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "proxy.go",
    "content": "// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"golang.org/x/net/proxy\"\n)\n\ntype netDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error)\n\nfunc (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {\n\treturn fn(context.Background(), network, addr)\n}\n\nfunc (fn netDialerFunc) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {\n\treturn fn(ctx, network, addr)\n}\n\nfunc proxyFromURL(proxyURL *url.URL, forwardDial netDialerFunc) (netDialerFunc, error) {\n\tif proxyURL.Scheme == \"http\" || proxyURL.Scheme == \"https\" {\n\t\treturn (&httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDial}).DialContext, nil\n\t}\n\tdialer, err := proxy.FromURL(proxyURL, forwardDial)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif d, ok := dialer.(proxy.ContextDialer); ok {\n\t\treturn d.DialContext, nil\n\t}\n\treturn func(ctx context.Context, net, addr string) (net.Conn, error) {\n\t\treturn dialer.Dial(net, addr)\n\t}, nil\n}\n\ntype httpProxyDialer struct {\n\tproxyURL    *url.URL\n\tforwardDial netDialerFunc\n}\n\nfunc (hpd *httpProxyDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {\n\thostPort, _ := hostPortNoPort(hpd.proxyURL)\n\tconn, err := hpd.forwardDial(ctx, network, hostPort)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconnectHeader := make(http.Header)\n\tif user := hpd.proxyURL.User; user != nil {\n\t\tproxyUser := user.Username()\n\t\tif proxyPassword, passwordSet := user.Password(); passwordSet {\n\t\t\tcredential := base64.StdEncoding.EncodeToString([]byte(proxyUser + \":\" + proxyPassword))\n\t\t\tconnectHeader.Set(\"Proxy-Authorization\", \"Basic \"+credential)\n\t\t}\n\t}\n\tconnectReq := &http.Request{\n\t\tMethod: http.MethodConnect,\n\t\tURL:    &url.URL{Opaque: addr},\n\t\tHost:   addr,\n\t\tHeader: connectHeader,\n\t}\n\n\tif err := connectReq.Write(conn); err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\t// Read response. It's OK to use and discard buffered reader here because\n\t// the remote server does not speak until spoken to.\n\tbr := bufio.NewReader(conn)\n\tresp, err := http.ReadResponse(br, connectReq)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\t// Close the response body to silence false positives from linters. Reset\n\t// the buffered reader first to ensure that Close() does not read from\n\t// conn.\n\t// Note: Applications must call resp.Body.Close() on a response returned\n\t// http.ReadResponse to inspect trailers or read another response from the\n\t// buffered reader. The call to resp.Body.Close() does not release\n\t// resources.\n\tbr.Reset(bytes.NewReader(nil))\n\t_ = resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\t_ = conn.Close()\n\t\tf := strings.SplitN(resp.Status, \" \", 2)\n\t\treturn nil, errors.New(f[1])\n\t}\n\treturn conn, nil\n}\n"
  },
  {
    "path": "server.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\n// HandshakeError describes an error with the handshake from the peer.\ntype HandshakeError struct {\n\tmessage string\n}\n\nfunc (e HandshakeError) Error() string { return e.message }\n\n// Upgrader specifies parameters for upgrading an HTTP connection to a\n// WebSocket connection.\n//\n// It is safe to call Upgrader's methods concurrently.\ntype Upgrader struct {\n\t// HandshakeTimeout specifies the duration for the handshake to complete.\n\tHandshakeTimeout time.Duration\n\n\t// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer\n\t// size is zero, then buffers allocated by the HTTP server are used. The\n\t// I/O buffer sizes do not limit the size of the messages that can be sent\n\t// or received.\n\tReadBufferSize, WriteBufferSize int\n\n\t// WriteBufferPool is a pool of buffers for write operations. If the value\n\t// is not set, then write buffers are allocated to the connection for the\n\t// lifetime of the connection.\n\t//\n\t// A pool is most useful when the application has a modest volume of writes\n\t// across a large number of connections.\n\t//\n\t// Applications should use a single pool for each unique value of\n\t// WriteBufferSize.\n\tWriteBufferPool BufferPool\n\n\t// Subprotocols specifies the server's supported protocols in order of\n\t// preference. If this field is not nil, then the Upgrade method negotiates a\n\t// subprotocol by selecting the first match in this list with a protocol\n\t// requested by the client. If there's no match, then no protocol is\n\t// negotiated (the Sec-Websocket-Protocol header is not included in the\n\t// handshake response).\n\tSubprotocols []string\n\n\t// Error specifies the function for generating HTTP error responses. If Error\n\t// is nil, then http.Error is used to generate the HTTP response.\n\tError func(w http.ResponseWriter, r *http.Request, status int, reason error)\n\n\t// CheckOrigin returns true if the request Origin header is acceptable. If\n\t// CheckOrigin is nil, then a safe default is used: return false if the\n\t// Origin request header is present and the origin host is not equal to\n\t// request Host header.\n\t//\n\t// A CheckOrigin function should carefully validate the request origin to\n\t// prevent cross-site request forgery.\n\tCheckOrigin func(r *http.Request) bool\n\n\t// EnableCompression specify if the server should attempt to negotiate per\n\t// message compression (RFC 7692). Setting this value to true does not\n\t// guarantee that compression will be supported. Currently only \"no context\n\t// takeover\" modes are supported.\n\tEnableCompression bool\n}\n\nfunc (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {\n\terr := HandshakeError{reason}\n\tif u.Error != nil {\n\t\tu.Error(w, r, status, err)\n\t} else {\n\t\tw.Header().Set(\"Sec-Websocket-Version\", \"13\")\n\t\thttp.Error(w, http.StatusText(status), status)\n\t}\n\treturn nil, err\n}\n\n// checkSameOrigin returns true if the origin is not set or is equal to the request host.\nfunc checkSameOrigin(r *http.Request) bool {\n\torigin := r.Header[\"Origin\"]\n\tif len(origin) == 0 {\n\t\treturn true\n\t}\n\tu, err := url.Parse(origin[0])\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn equalASCIIFold(u.Host, r.Host)\n}\n\nfunc (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {\n\tif u.Subprotocols != nil {\n\t\tclientProtocols := Subprotocols(r)\n\t\tfor _, clientProtocol := range clientProtocols {\n\t\t\tfor _, serverProtocol := range u.Subprotocols {\n\t\t\t\tif clientProtocol == serverProtocol {\n\t\t\t\t\treturn clientProtocol\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if responseHeader != nil {\n\t\treturn responseHeader.Get(\"Sec-Websocket-Protocol\")\n\t}\n\treturn \"\"\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie). To specify\n// subprotocols supported by the server, set Upgrader.Subprotocols directly.\n//\n// If the upgrade fails, then Upgrade replies to the client with an HTTP error\n// response.\nfunc (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {\n\tconst badHandshake = \"websocket: the client is not using the websocket protocol: \"\n\n\tif !tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, badHandshake+\"'upgrade' token not found in 'Connection' header\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Upgrade\", \"websocket\") {\n\t\tw.Header().Set(\"Upgrade\", \"websocket\")\n\t\treturn u.returnError(w, r, http.StatusUpgradeRequired, badHandshake+\"'websocket' token not found in 'Upgrade' header\")\n\t}\n\n\tif r.Method != http.MethodGet {\n\t\treturn u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+\"request method is not GET\")\n\t}\n\n\tif !tokenListContainsValue(r.Header, \"Sec-Websocket-Version\", \"13\") {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header\")\n\t}\n\n\tif _, ok := responseHeader[\"Sec-Websocket-Extensions\"]; ok {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError, \"websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported\")\n\t}\n\n\tcheckOrigin := u.CheckOrigin\n\tif checkOrigin == nil {\n\t\tcheckOrigin = checkSameOrigin\n\t}\n\tif !checkOrigin(r) {\n\t\treturn u.returnError(w, r, http.StatusForbidden, \"websocket: request origin not allowed by Upgrader.CheckOrigin\")\n\t}\n\n\tchallengeKey := r.Header.Get(\"Sec-Websocket-Key\")\n\tif !isValidChallengeKey(challengeKey) {\n\t\treturn u.returnError(w, r, http.StatusBadRequest, \"websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length\")\n\t}\n\n\tsubprotocol := u.selectSubprotocol(r, responseHeader)\n\n\t// Negotiate PMCE\n\tvar compress bool\n\tif u.EnableCompression {\n\t\tfor _, ext := range parseExtensions(r.Header) {\n\t\t\tif ext[\"\"] != \"permessage-deflate\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcompress = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tnetConn, brw, err := http.NewResponseController(w).Hijack()\n\tif err != nil {\n\t\treturn u.returnError(w, r, http.StatusInternalServerError,\n\t\t\t\"websocket: hijack: \"+err.Error())\n\t}\n\n\t// Close the network connection when returning an error. The variable\n\t// netConn is set to nil before the success return at the end of the\n\t// function.\n\tdefer func() {\n\t\tif netConn != nil {\n\t\t\t// It's safe to ignore the error from Close() because this code is\n\t\t\t// only executed when returning a more important error to the\n\t\t\t// application.\n\t\t\t_ = netConn.Close()\n\t\t}\n\t}()\n\n\tvar br *bufio.Reader\n\tif u.ReadBufferSize == 0 && brw.Reader.Size() > 256 {\n\t\t// Use hijacked buffered reader as the connection reader.\n\t\tbr = brw.Reader\n\t} else if brw.Reader.Buffered() > 0 {\n\t\t// Wrap the network connection to read buffered data in brw.Reader\n\t\t// before reading from the network connection. This should be rare\n\t\t// because a client must not send message data before receiving the\n\t\t// handshake response.\n\t\tnetConn = &brNetConn{br: brw.Reader, Conn: netConn}\n\t}\n\n\tbuf := brw.Writer.AvailableBuffer()\n\n\tvar writeBuf []byte\n\tif u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {\n\t\t// Reuse hijacked write buffer as connection buffer.\n\t\twriteBuf = buf\n\t}\n\n\tc := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)\n\tc.subprotocol = subprotocol\n\n\tif compress {\n\t\tc.newCompressionWriter = compressNoContextTakeover\n\t\tc.newDecompressionReader = decompressNoContextTakeover\n\t}\n\n\t// Use larger of hijacked buffer and connection write buffer for header.\n\tp := buf\n\tif len(c.writeBuf) > len(p) {\n\t\tp = c.writeBuf\n\t}\n\tp = p[:0]\n\n\tp = append(p, \"HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: \"...)\n\tp = append(p, computeAcceptKey(challengeKey)...)\n\tp = append(p, \"\\r\\n\"...)\n\tif c.subprotocol != \"\" {\n\t\tp = append(p, \"Sec-WebSocket-Protocol: \"...)\n\t\tp = append(p, c.subprotocol...)\n\t\tp = append(p, \"\\r\\n\"...)\n\t}\n\tif compress {\n\t\tp = append(p, \"Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\\r\\n\"...)\n\t}\n\tfor k, vs := range responseHeader {\n\t\tif k == \"Sec-Websocket-Protocol\" {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, v := range vs {\n\t\t\tp = append(p, k...)\n\t\t\tp = append(p, \": \"...)\n\t\t\tfor i := 0; i < len(v); i++ {\n\t\t\t\tb := v[i]\n\t\t\t\tif b <= 31 {\n\t\t\t\t\t// prevent response splitting.\n\t\t\t\t\tb = ' '\n\t\t\t\t}\n\t\t\t\tp = append(p, b)\n\t\t\t}\n\t\t\tp = append(p, \"\\r\\n\"...)\n\t\t}\n\t}\n\tp = append(p, \"\\r\\n\"...)\n\n\tif u.HandshakeTimeout > 0 {\n\t\tif err := netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\t// Clear deadlines set by HTTP server.\n\t\tif err := netConn.SetDeadline(time.Time{}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif _, err = netConn.Write(p); err != nil {\n\t\treturn nil, err\n\t}\n\tif u.HandshakeTimeout > 0 {\n\t\tif err := netConn.SetWriteDeadline(time.Time{}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Success! Set netConn to nil to stop the deferred function above from\n\t// closing the network connection.\n\tnetConn = nil\n\n\treturn c, nil\n}\n\n// Upgrade upgrades the HTTP server connection to the WebSocket protocol.\n//\n// Deprecated: Use websocket.Upgrader instead.\n//\n// Upgrade does not perform origin checking. The application is responsible for\n// checking the Origin header before calling Upgrade. An example implementation\n// of the same origin policy check is:\n//\n//\tif req.Header.Get(\"Origin\") != \"http://\"+req.Host {\n//\t\thttp.Error(w, \"Origin not allowed\", http.StatusForbidden)\n//\t\treturn\n//\t}\n//\n// If the endpoint supports subprotocols, then the application is responsible\n// for negotiating the protocol used on the connection. Use the Subprotocols()\n// function to get the subprotocols requested by the client. Use the\n// Sec-Websocket-Protocol response header to specify the subprotocol selected\n// by the application.\n//\n// The responseHeader is included in the response to the client's upgrade\n// request. Use the responseHeader to specify cookies (Set-Cookie) and the\n// negotiated subprotocol (Sec-Websocket-Protocol).\n//\n// The connection buffers IO to the underlying network connection. The\n// readBufSize and writeBufSize parameters specify the size of the buffers to\n// use. Messages can be larger than the buffers.\n//\n// If the request is not a valid WebSocket handshake, then Upgrade returns an\n// error of type HandshakeError. Applications should handle this error by\n// replying to the client with an HTTP error response.\nfunc Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {\n\tu := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}\n\tu.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {\n\t\t// don't return errors to maintain backwards compatibility\n\t}\n\tu.CheckOrigin = func(r *http.Request) bool {\n\t\t// allow all connections by default\n\t\treturn true\n\t}\n\treturn u.Upgrade(w, r, responseHeader)\n}\n\n// Subprotocols returns the subprotocols requested by the client in the\n// Sec-Websocket-Protocol header.\nfunc Subprotocols(r *http.Request) []string {\n\th := strings.TrimSpace(r.Header.Get(\"Sec-Websocket-Protocol\"))\n\tif h == \"\" {\n\t\treturn nil\n\t}\n\tprotocols := strings.Split(h, \",\")\n\tfor i := range protocols {\n\t\tprotocols[i] = strings.TrimSpace(protocols[i])\n\t}\n\treturn protocols\n}\n\n// IsWebSocketUpgrade returns true if the client requested upgrade to the\n// WebSocket protocol.\nfunc IsWebSocketUpgrade(r *http.Request) bool {\n\treturn tokenListContainsValue(r.Header, \"Connection\", \"upgrade\") &&\n\t\ttokenListContainsValue(r.Header, \"Upgrade\", \"websocket\")\n}\n\ntype brNetConn struct {\n\tbr *bufio.Reader\n\tnet.Conn\n}\n\nfunc (b *brNetConn) Read(p []byte) (n int, err error) {\n\tif b.br != nil {\n\t\t// Limit read to buferred data.\n\t\tif n := b.br.Buffered(); len(p) > n {\n\t\t\tp = p[:n]\n\t\t}\n\t\tn, err = b.br.Read(p)\n\t\tif b.br.Buffered() == 0 {\n\t\t\tb.br = nil\n\t\t}\n\t\treturn n, err\n\t}\n\treturn b.Conn.Read(p)\n}\n\n// NetConn returns the underlying connection that is wrapped by b.\nfunc (b *brNetConn) NetConn() net.Conn {\n\treturn b.Conn\n}\n\n"
  },
  {
    "path": "server_test.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nvar subprotocolTests = []struct {\n\th         string\n\tprotocols []string\n}{\n\t{\"\", nil},\n\t{\"foo\", []string{\"foo\"}},\n\t{\"foo,bar\", []string{\"foo\", \"bar\"}},\n\t{\"foo, bar\", []string{\"foo\", \"bar\"}},\n\t{\" foo, bar\", []string{\"foo\", \"bar\"}},\n\t{\" foo, bar \", []string{\"foo\", \"bar\"}},\n}\n\nfunc TestSubprotocols(t *testing.T) {\n\tfor _, st := range subprotocolTests {\n\t\tr := http.Request{Header: http.Header{\"Sec-Websocket-Protocol\": {st.h}}}\n\t\tprotocols := Subprotocols(&r)\n\t\tif !reflect.DeepEqual(st.protocols, protocols) {\n\t\t\tt.Errorf(\"SubProtocols(%q) returned %#v, want %#v\", st.h, protocols, st.protocols)\n\t\t}\n\t}\n}\n\nvar isWebSocketUpgradeTests = []struct {\n\tok bool\n\th  http.Header\n}{\n\t{false, http.Header{\"Upgrade\": {\"websocket\"}}},\n\t{false, http.Header{\"Connection\": {\"upgrade\"}}},\n\t{true, http.Header{\"Connection\": {\"upgRade\"}, \"Upgrade\": {\"WebSocket\"}}},\n}\n\nfunc TestIsWebSocketUpgrade(t *testing.T) {\n\tfor _, tt := range isWebSocketUpgradeTests {\n\t\tok := IsWebSocketUpgrade(&http.Request{Header: tt.h})\n\t\tif tt.ok != ok {\n\t\t\tt.Errorf(\"IsWebSocketUpgrade(%v) returned %v, want %v\", tt.h, ok, tt.ok)\n\t\t}\n\t}\n}\n\nfunc TestSubProtocolSelection(t *testing.T) {\n\tupgrader := Upgrader{\n\t\tSubprotocols: []string{\"foo\", \"bar\", \"baz\"},\n\t}\n\n\tr := http.Request{Header: http.Header{\"Sec-Websocket-Protocol\": {\"foo\", \"bar\"}}}\n\ts := upgrader.selectSubprotocol(&r, nil)\n\tif s != \"foo\" {\n\t\tt.Errorf(\"Upgrader.selectSubprotocol returned %v, want %v\", s, \"foo\")\n\t}\n\n\tr = http.Request{Header: http.Header{\"Sec-Websocket-Protocol\": {\"bar\", \"foo\"}}}\n\ts = upgrader.selectSubprotocol(&r, nil)\n\tif s != \"bar\" {\n\t\tt.Errorf(\"Upgrader.selectSubprotocol returned %v, want %v\", s, \"bar\")\n\t}\n\n\tr = http.Request{Header: http.Header{\"Sec-Websocket-Protocol\": {\"baz\"}}}\n\ts = upgrader.selectSubprotocol(&r, nil)\n\tif s != \"baz\" {\n\t\tt.Errorf(\"Upgrader.selectSubprotocol returned %v, want %v\", s, \"baz\")\n\t}\n\n\tr = http.Request{Header: http.Header{\"Sec-Websocket-Protocol\": {\"quux\"}}}\n\ts = upgrader.selectSubprotocol(&r, nil)\n\tif s != \"\" {\n\t\tt.Errorf(\"Upgrader.selectSubprotocol returned %v, want %v\", s, \"empty string\")\n\t}\n}\n\nvar checkSameOriginTests = []struct {\n\tok bool\n\tr  *http.Request\n}{\n\t{false, &http.Request{Host: \"example.org\", Header: map[string][]string{\"Origin\": {\"https://other.org\"}}}},\n\t{true, &http.Request{Host: \"example.org\", Header: map[string][]string{\"Origin\": {\"https://example.org\"}}}},\n\t{true, &http.Request{Host: \"Example.org\", Header: map[string][]string{\"Origin\": {\"https://example.org\"}}}},\n}\n\nfunc TestCheckSameOrigin(t *testing.T) {\n\tfor _, tt := range checkSameOriginTests {\n\t\tok := checkSameOrigin(tt.r)\n\t\tif tt.ok != ok {\n\t\t\tt.Errorf(\"checkSameOrigin(%+v) returned %v, want %v\", tt.r, ok, tt.ok)\n\t\t}\n\t}\n}\n\ntype reuseTestResponseWriter struct {\n\tbrw *bufio.ReadWriter\n\thttp.ResponseWriter\n}\n\nfunc (resp *reuseTestResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\treturn fakeNetConn{strings.NewReader(\"\"), &bytes.Buffer{}}, resp.brw, nil\n}\n\nvar bufioReuseTests = []struct {\n\tn     int\n\treuse bool\n}{\n\t{4096, true},\n\t{128, false},\n}\n\nfunc xTestBufioReuse(t *testing.T) {\n\tfor i, tt := range bufioReuseTests {\n\t\tbr := bufio.NewReaderSize(strings.NewReader(\"\"), tt.n)\n\t\tbw := bufio.NewWriterSize(&bytes.Buffer{}, tt.n)\n\t\tresp := &reuseTestResponseWriter{\n\t\t\tbrw: bufio.NewReadWriter(br, bw),\n\t\t}\n\t\tupgrader := Upgrader{}\n\t\tc, err := upgrader.Upgrade(resp, &http.Request{\n\t\t\tMethod: http.MethodGet,\n\t\t\tHeader: http.Header{\n\t\t\t\t\"Upgrade\":               []string{\"websocket\"},\n\t\t\t\t\"Connection\":            []string{\"upgrade\"},\n\t\t\t\t\"Sec-Websocket-Key\":     []string{\"dGhlIHNhbXBsZSBub25jZQ==\"},\n\t\t\t\t\"Sec-Websocket-Version\": []string{\"13\"},\n\t\t\t}}, nil)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif reuse := c.br == br; reuse != tt.reuse {\n\t\t\tt.Errorf(\"%d: buffered reader reuse=%v, want %v\", i, reuse, tt.reuse)\n\t\t}\n\t\twriteBuf := bw.AvailableBuffer()\n\t\tif reuse := &c.writeBuf[0] == &writeBuf[0]; reuse != tt.reuse {\n\t\t\tt.Errorf(\"%d: write buffer reuse=%v, want %v\", i, reuse, tt.reuse)\n\t\t}\n\t}\n}\n\nfunc TestHijack_NotSupported(t *testing.T) {\n\tt.Parallel()\n\n\treq := httptest.NewRequest(http.MethodGet, \"http://example.com\", nil)\n\treq.Header.Set(\"Upgrade\", \"websocket\")\n\treq.Header.Set(\"Connection\", \"upgrade\")\n\treq.Header.Set(\"Sec-Websocket-Key\", \"dGhlIHNhbXBsZSBub25jZQ==\")\n\treq.Header.Set(\"Sec-Websocket-Version\", \"13\")\n\n\trecorder := httptest.NewRecorder()\n\n\tupgrader := Upgrader{}\n\t_, err := upgrader.Upgrade(recorder, req, nil)\n\n\tif want := (HandshakeError{}); !errors.As(err, &want) || recorder.Code != http.StatusInternalServerError {\n\t\tt.Errorf(\"want %T and status_code=%d\", want, http.StatusInternalServerError)\n\t\tt.Fatalf(\"got err=%T and status_code=%d\", err, recorder.Code)\n\t}\n}\n"
  },
  {
    "path": "util.go",
    "content": "// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nvar keyGUID = []byte(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")\n\nfunc computeAcceptKey(challengeKey string) string {\n\th := sha1.New()\n\th.Write([]byte(challengeKey))\n\th.Write(keyGUID)\n\treturn base64.StdEncoding.EncodeToString(h.Sum(nil))\n}\n\nfunc generateChallengeKey() (string, error) {\n\tp := make([]byte, 16)\n\tif _, err := io.ReadFull(rand.Reader, p); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(p), nil\n}\n\n// Token octets per RFC 2616.\nvar isTokenOctet = [256]bool{\n\t'!':  true,\n\t'#':  true,\n\t'$':  true,\n\t'%':  true,\n\t'&':  true,\n\t'\\'': true,\n\t'*':  true,\n\t'+':  true,\n\t'-':  true,\n\t'.':  true,\n\t'0':  true,\n\t'1':  true,\n\t'2':  true,\n\t'3':  true,\n\t'4':  true,\n\t'5':  true,\n\t'6':  true,\n\t'7':  true,\n\t'8':  true,\n\t'9':  true,\n\t'A':  true,\n\t'B':  true,\n\t'C':  true,\n\t'D':  true,\n\t'E':  true,\n\t'F':  true,\n\t'G':  true,\n\t'H':  true,\n\t'I':  true,\n\t'J':  true,\n\t'K':  true,\n\t'L':  true,\n\t'M':  true,\n\t'N':  true,\n\t'O':  true,\n\t'P':  true,\n\t'Q':  true,\n\t'R':  true,\n\t'S':  true,\n\t'T':  true,\n\t'U':  true,\n\t'W':  true,\n\t'V':  true,\n\t'X':  true,\n\t'Y':  true,\n\t'Z':  true,\n\t'^':  true,\n\t'_':  true,\n\t'`':  true,\n\t'a':  true,\n\t'b':  true,\n\t'c':  true,\n\t'd':  true,\n\t'e':  true,\n\t'f':  true,\n\t'g':  true,\n\t'h':  true,\n\t'i':  true,\n\t'j':  true,\n\t'k':  true,\n\t'l':  true,\n\t'm':  true,\n\t'n':  true,\n\t'o':  true,\n\t'p':  true,\n\t'q':  true,\n\t'r':  true,\n\t's':  true,\n\t't':  true,\n\t'u':  true,\n\t'v':  true,\n\t'w':  true,\n\t'x':  true,\n\t'y':  true,\n\t'z':  true,\n\t'|':  true,\n\t'~':  true,\n}\n\n// skipSpace returns a slice of the string s with all leading RFC 2616 linear\n// whitespace removed.\nfunc skipSpace(s string) (rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif b := s[i]; b != ' ' && b != '\\t' {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[i:]\n}\n\n// nextToken returns the leading RFC 2616 token of s and the string following\n// the token.\nfunc nextToken(s string) (token, rest string) {\n\ti := 0\n\tfor ; i < len(s); i++ {\n\t\tif !isTokenOctet[s[i]] {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn s[:i], s[i:]\n}\n\n// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616\n// and the string following the token or quoted string.\nfunc nextTokenOrQuoted(s string) (value string, rest string) {\n\tif !strings.HasPrefix(s, \"\\\"\") {\n\t\treturn nextToken(s)\n\t}\n\ts = s[1:]\n\tfor i := 0; i < len(s); i++ {\n\t\tswitch s[i] {\n\t\tcase '\"':\n\t\t\treturn s[:i], s[i+1:]\n\t\tcase '\\\\':\n\t\t\tp := make([]byte, len(s)-1)\n\t\t\tj := copy(p, s[:i])\n\t\t\tescape := true\n\t\t\tfor i = i + 1; i < len(s); i++ {\n\t\t\t\tb := s[i]\n\t\t\t\tswitch {\n\t\t\t\tcase escape:\n\t\t\t\t\tescape = false\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\tcase b == '\\\\':\n\t\t\t\t\tescape = true\n\t\t\t\tcase b == '\"':\n\t\t\t\t\treturn string(p[:j]), s[i+1:]\n\t\t\t\tdefault:\n\t\t\t\t\tp[j] = b\n\t\t\t\t\tj++\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", \"\"\n\t\t}\n\t}\n\treturn \"\", \"\"\n}\n\n// equalASCIIFold returns true if s is equal to t with ASCII case folding as\n// defined in RFC 4790.\nfunc equalASCIIFold(s, t string) bool {\n\tfor s != \"\" && t != \"\" {\n\t\tsr, size := utf8.DecodeRuneInString(s)\n\t\ts = s[size:]\n\t\ttr, size := utf8.DecodeRuneInString(t)\n\t\tt = t[size:]\n\t\tif sr == tr {\n\t\t\tcontinue\n\t\t}\n\t\tif 'A' <= sr && sr <= 'Z' {\n\t\t\tsr = sr + 'a' - 'A'\n\t\t}\n\t\tif 'A' <= tr && tr <= 'Z' {\n\t\t\ttr = tr + 'a' - 'A'\n\t\t}\n\t\tif sr != tr {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn s == t\n}\n\n// tokenListContainsValue returns true if the 1#token header with the given\n// name contains a token equal to value with ASCII case folding.\nfunc tokenListContainsValue(header http.Header, name string, value string) bool {\nheaders:\n\tfor _, s := range header[name] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = skipSpace(s)\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tif equalASCIIFold(t, value) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn false\n}\n\n// parseExtensions parses WebSocket extensions from a header.\nfunc parseExtensions(header http.Header) []map[string]string {\n\t// From RFC 6455:\n\t//\n\t//  Sec-WebSocket-Extensions = extension-list\n\t//  extension-list = 1#extension\n\t//  extension = extension-token *( \";\" extension-param )\n\t//  extension-token = registered-token\n\t//  registered-token = token\n\t//  extension-param = token [ \"=\" (token | quoted-string) ]\n\t//     ;When using the quoted-string syntax variant, the value\n\t//     ;after quoted-string unescaping MUST conform to the\n\t//     ;'token' ABNF.\n\n\tvar result []map[string]string\nheaders:\n\tfor _, s := range header[\"Sec-Websocket-Extensions\"] {\n\t\tfor {\n\t\t\tvar t string\n\t\t\tt, s = nextToken(skipSpace(s))\n\t\t\tif t == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\text := map[string]string{\"\": t}\n\t\t\tfor {\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tif !strings.HasPrefix(s, \";\") {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvar k string\n\t\t\t\tk, s = nextToken(skipSpace(s[1:]))\n\t\t\t\tif k == \"\" {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\ts = skipSpace(s)\n\t\t\t\tvar v string\n\t\t\t\tif strings.HasPrefix(s, \"=\") {\n\t\t\t\t\tv, s = nextTokenOrQuoted(skipSpace(s[1:]))\n\t\t\t\t\ts = skipSpace(s)\n\t\t\t\t}\n\t\t\t\tif s != \"\" && s[0] != ',' && s[0] != ';' {\n\t\t\t\t\tcontinue headers\n\t\t\t\t}\n\t\t\t\text[k] = v\n\t\t\t}\n\t\t\tif s != \"\" && s[0] != ',' {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\tresult = append(result, ext)\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue headers\n\t\t\t}\n\t\t\ts = s[1:]\n\t\t}\n\t}\n\treturn result\n}\n\n// isValidChallengeKey checks if the argument meets RFC6455 specification.\nfunc isValidChallengeKey(s string) bool {\n\t// From RFC6455:\n\t//\n\t// A |Sec-WebSocket-Key| header field with a base64-encoded (see\n\t// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in\n\t// length.\n\n\tif s == \"\" {\n\t\treturn false\n\t}\n\tdecoded, err := base64.StdEncoding.DecodeString(s)\n\treturn err == nil && len(decoded) == 16\n}\n"
  },
  {
    "path": "util_test.go",
    "content": "// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage websocket\n\nimport (\n\t\"net/http\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar equalASCIIFoldTests = []struct {\n\tt, s string\n\teq   bool\n}{\n\t{\"WebSocket\", \"websocket\", true},\n\t{\"websocket\", \"WebSocket\", true},\n\t{\"Öyster\", \"öyster\", false},\n\t{\"WebSocket\", \"WetSocket\", false},\n}\n\nfunc TestEqualASCIIFold(t *testing.T) {\n\tfor _, tt := range equalASCIIFoldTests {\n\t\teq := equalASCIIFold(tt.s, tt.t)\n\t\tif eq != tt.eq {\n\t\t\tt.Errorf(\"equalASCIIFold(%q, %q) = %v, want %v\", tt.s, tt.t, eq, tt.eq)\n\t\t}\n\t}\n}\n\nvar tokenListContainsValueTests = []struct {\n\tvalue string\n\tok    bool\n}{\n\t{\"WebSocket\", true},\n\t{\"WEBSOCKET\", true},\n\t{\"websocket\", true},\n\t{\"websockets\", false},\n\t{\"x websocket\", false},\n\t{\"websocket x\", false},\n\t{\"other,websocket,more\", true},\n\t{\"other, websocket, more\", true},\n}\n\nfunc TestTokenListContainsValue(t *testing.T) {\n\tfor _, tt := range tokenListContainsValueTests {\n\t\th := http.Header{\"Upgrade\": {tt.value}}\n\t\tok := tokenListContainsValue(h, \"Upgrade\", \"websocket\")\n\t\tif ok != tt.ok {\n\t\t\tt.Errorf(\"tokenListContainsValue(h, n, %q) = %v, want %v\", tt.value, ok, tt.ok)\n\t\t}\n\t}\n}\n\nvar isValidChallengeKeyTests = []struct {\n\tkey string\n\tok  bool\n}{\n\t{\"dGhlIHNhbXBsZSBub25jZQ==\", true},\n\t{\"\", false},\n\t{\"InvalidKey\", false},\n\t{\"WHQ4eXhscUtKYjBvOGN3WEdtOEQ=\", false},\n}\n\nfunc TestIsValidChallengeKey(t *testing.T) {\n\tfor _, tt := range isValidChallengeKeyTests {\n\t\tok := isValidChallengeKey(tt.key)\n\t\tif ok != tt.ok {\n\t\t\tt.Errorf(\"isValidChallengeKey returns %v, want %v\", ok, tt.ok)\n\t\t}\n\t}\n}\n\nvar parseExtensionTests = []struct {\n\tvalue      string\n\textensions []map[string]string\n}{\n\t{`foo`, []map[string]string{{\"\": \"foo\"}}},\n\t{`foo, bar; baz=2`, []map[string]string{\n\t\t{\"\": \"foo\"},\n\t\t{\"\": \"bar\", \"baz\": \"2\"}}},\n\t{`foo; bar=\"b,a;z\"`, []map[string]string{\n\t\t{\"\": \"foo\", \"bar\": \"b,a;z\"}}},\n\t{`foo , bar; baz = 2`, []map[string]string{\n\t\t{\"\": \"foo\"},\n\t\t{\"\": \"bar\", \"baz\": \"2\"}}},\n\t{`foo, bar; baz=2 junk`, []map[string]string{\n\t\t{\"\": \"foo\"}}},\n\t{`foo junk, bar; baz=2 junk`, nil},\n\t{`mux; max-channels=4; flow-control, deflate-stream`, []map[string]string{\n\t\t{\"\": \"mux\", \"max-channels\": \"4\", \"flow-control\": \"\"},\n\t\t{\"\": \"deflate-stream\"}}},\n\t{`permessage-foo; x=\"10\"`, []map[string]string{\n\t\t{\"\": \"permessage-foo\", \"x\": \"10\"}}},\n\t{`permessage-foo; use_y, permessage-foo`, []map[string]string{\n\t\t{\"\": \"permessage-foo\", \"use_y\": \"\"},\n\t\t{\"\": \"permessage-foo\"}}},\n\t{`permessage-deflate; client_max_window_bits; server_max_window_bits=10 , permessage-deflate; client_max_window_bits`, []map[string]string{\n\t\t{\"\": \"permessage-deflate\", \"client_max_window_bits\": \"\", \"server_max_window_bits\": \"10\"},\n\t\t{\"\": \"permessage-deflate\", \"client_max_window_bits\": \"\"}}},\n\t{\"permessage-deflate; server_no_context_takeover; client_max_window_bits=15\", []map[string]string{\n\t\t{\"\": \"permessage-deflate\", \"server_no_context_takeover\": \"\", \"client_max_window_bits\": \"15\"},\n\t}},\n}\n\nfunc TestParseExtensions(t *testing.T) {\n\tfor _, tt := range parseExtensionTests {\n\t\th := http.Header{http.CanonicalHeaderKey(\"Sec-WebSocket-Extensions\"): {tt.value}}\n\t\textensions := parseExtensions(h)\n\t\tif !reflect.DeepEqual(extensions, tt.extensions) {\n\t\t\tt.Errorf(\"parseExtensions(%q)\\n    = %v,\\nwant %v\", tt.value, extensions, tt.extensions)\n\t\t}\n\t}\n}\n"
  }
]