Repository: gorilla/websocket
Pull request #542 (2019-09-21 15:59:05)

Fix broadcast benchmarks
This pull request fixes broadcast benchmarks.

Before:

```
BenchmarkBroadcast/NoCompression-4         	     159	   7825192 ns/op	      22 B/op	       0 allocs/op
BenchmarkBroadcast/WithCompression-4       	      67	  17692579 ns/op	 1192272 B/op	   30004 allocs/op
BenchmarkBroadcast/NoCompressionPrepared-4 	     136	   9620360 ns/op	     224 B/op	       1 allocs/op
BenchmarkBroadcast/WithCompressionPrepared-4         	     139	   9190567 ns/op	    8922 B/op	       2 allocs/op
```

After:

```
BenchmarkBroadcast/NoCompression-4                   148           8806814 ns/op              43 B/op          1 allocs/op
BenchmarkBroadcast/Compression-4                      64          17525206 ns/op         1157741 B/op      30002 allocs/op
BenchmarkBroadcast/NoCompressionPrepared-4           136           9424210 ns/op           11492 B/op         17 allocs/op
BenchmarkBroadcast/CompressionPrepared-4             134           9622622 ns/op           47631 B/op         21 allocs/op
```

There are actually 2 fixes.

1) Fixes wrong tests semantics. These tests must benchmark writing the same message to many connections to compare effect of using `PreparedMessage` in various conditions. But on every iteration of benchmark loop tests that work with `PreparedMessage` type must create PreparedMessage from scratch. Otherwise we see strange results almost without allocations in PreparedMessage scenarios.

2) Fixes broken indentation in console because of very long name of test. Now benchmark results are properly aligned.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1240d78..a0eb0ed 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,76 +1,70 @@
-version: 2.0
+version: 2.1
 
 jobs:
-  # Base test configuration for Go library tests Each distinct version should
-  # inherit this base, and override (at least) the container image used.
-  "test": &test
+  "test":
+    parameters:
+      version:
+        type: string
+        default: "latest"
+      golint:
+        type: boolean
+        default: true
+      modules:
+        type: boolean
+        default: true
+      goproxy:
+        type: string
+        default: ""
     docker:
-      - image: circleci/golang:latest
+      - image: "circleci/golang:<< parameters.version >>"
     working_directory: /go/src/github.com/gorilla/websocket
-    steps: &steps
+    environment:
+      GO111MODULE: "on"
+      GOPROXY: "<< parameters.goproxy >>"
+    steps:
       - checkout
-      - run: go version
-      - run: go get -t -v ./...
+      - run:
+          name: "Print the Go version"
+          command: >
+            go version
+      - run:
+          name: "Fetch dependencies"
+          command: >
+            if [[ << parameters.modules >> = true ]]; then
+              go mod download
+              export GO111MODULE=on
+            else
+              go get -v ./...
+            fi
       # Only run gofmt, vet & lint against the latest Go version
-      - run: >
-          if [[ "$LATEST" = true ]]; then
-            go get -u golang.org/x/lint/golint
-            golint ./...
-          fi
-      - run: >
-          if [[ "$LATEST" = true ]]; then
-            diff -u <(echo -n) <(gofmt -d .)
-          fi
-      - run: >
-          if [[ "$LATEST" = true ]]; then
-            go vet -v .
-          fi
-      - run: if [[ "$LATEST" = true ]]; then go vet -v .; fi
-      - run: go test -v -race ./...
-
-  "latest":
-    <<: *test
-    environment:
-      LATEST: true
-
-  "1.12":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.12
-
-  "1.11":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.11
-
-  "1.10":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.10
-
-  "1.9":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.9
-
-  "1.8":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.8
-
-  "1.7":
-    <<: *test
-    docker:
-      - image: circleci/golang:1.7
+      - run:
+          name: "Run golint"
+          command: >
+            if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then
+              go get -u golang.org/x/lint/golint
+              golint ./...
+            fi
+      - run:
+          name: "Run gofmt"
+          command: >
+            if [[ << parameters.version >> = "latest" ]]; then
+              diff -u <(echo -n) <(gofmt -d -e .)
+            fi
+      - run:
+          name: "Run go vet"
+          command: >
+            if [[ << parameters.version >> = "latest" ]]; then
+              go vet -v ./...
+            fi
+      - run:
+          name: "Run go test (+ race detector)"
+          command: >
+            go test -v -race ./...
 
 workflows:
-  version: 2
-  build:
+  tests:
     jobs:
-      - "latest"
-      - "1.12"
-      - "1.11"
-      - "1.10"
-      - "1.9"
-      - "1.8"
-      - "1.7"
+      - test:
+          matrix:
+            parameters:
+              version: ["latest", "1.17", "1.16", "1.15", "1.14", "1.13", "1.12", "1.11"]
diff --git a/README.md b/README.md
index 0827d05..19aa2e7 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
 
 ### Documentation
 
-* [API Reference](http://godoc.org/github.com/gorilla/websocket)
+* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
 * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
 * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
 * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
diff --git a/client.go b/client.go
index 962c06a..a24c3ce 100644
--- a/client.go
+++ b/client.go
@@ -48,6 +48,8 @@ func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufS
 }
 
 // A Dialer contains options for connecting to WebSocket server.
+//
+// It is safe to call Dialer's methods concurrently.
 type Dialer struct {
 	// NetDial specifies the dial function for creating TCP connections. If
 	// NetDial is nil, net.Dial is used.
@@ -176,7 +178,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
 	}
 
 	req := &http.Request{
-		Method:     "GET",
+		Method:     http.MethodGet,
 		URL:        u,
 		Proto:      "HTTP/1.1",
 		ProtoMajor: 1,
@@ -312,11 +314,12 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
 		tlsConn := tls.Client(netConn, cfg)
 		netConn = tlsConn
 
-		var err error
-		if trace != nil {
-			err = doHandshakeWithTrace(trace, tlsConn, cfg)
-		} else {
-			err = doHandshake(tlsConn, cfg)
+		if trace != nil && trace.TLSHandshakeStart != nil {
+			trace.TLSHandshakeStart()
+		}
+		err := doHandshake(ctx, tlsConn, cfg)
+		if trace != nil && trace.TLSHandshakeDone != nil {
+			trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
 		}
 
 		if err != nil {
@@ -348,8 +351,8 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
 	}
 
 	if resp.StatusCode != 101 ||
-		!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
-		!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
+		!tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||
+		!tokenListContainsValue(resp.Header, "Connection", "upgrade") ||
 		resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
 		// Before closing the network connection on return from this
 		// function, slurp up some of the response to aid application
@@ -381,15 +384,3 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
 	netConn = nil // to avoid close in defer.
 	return conn, resp, nil
 }
-
-func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {
-	if err := tlsConn.Handshake(); err != nil {
-		return err
-	}
-	if !cfg.InsecureSkipVerify {
-		if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/client_clone.go b/client_clone.go
index 4f0d943..4179c7a 100644
--- a/client_clone.go
+++ b/client_clone.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.8
 // +build go1.8
 
 package websocket
diff --git a/client_clone_legacy.go b/client_clone_legacy.go
index babb007..7e241a8 100644
--- a/client_clone_legacy.go
+++ b/client_clone_legacy.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.8
 // +build !go1.8
 
 package websocket
diff --git a/conn.go b/conn.go
index 6f17cd2..ca46d2f 100644
--- a/conn.go
+++ b/conn.go
@@ -244,8 +244,8 @@ type Conn struct {
 	subprotocol string
 
 	// Write fields
-	mu            chan bool // used as mutex to protect write to conn
-	writeBuf      []byte    // frame is constructed in this buffer.
+	mu            chan struct{} // used as mutex to protect write to conn
+	writeBuf      []byte        // frame is constructed in this buffer.
 	writePool     BufferPool
 	writeBufSize  int
 	writeDeadline time.Time
@@ -302,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int,
 		writeBuf = make([]byte, writeBufferSize)
 	}
 
-	mu := make(chan bool, 1)
-	mu <- true
+	mu := make(chan struct{}, 1)
+	mu <- struct{}{}
 	c := &Conn{
 		isServer:               isServer,
 		br:                     br,
@@ -377,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) {
 
 func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {
 	<-c.mu
-	defer func() { c.mu <- true }()
+	defer func() { c.mu <- struct{}{} }()
 
 	c.writeErrMu.Lock()
 	err := c.writeErr
@@ -429,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
 		maskBytes(key, 0, buf[6:])
 	}
 
-	d := time.Hour * 1000
+	d := 1000 * time.Hour
 	if !deadline.IsZero() {
 		d = deadline.Sub(time.Now())
 		if d < 0 {
@@ -444,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
 	case <-timer.C:
 		return errWriteTimeout
 	}
-	defer func() { c.mu <- true }()
+	defer func() { c.mu <- struct{}{} }()
 
 	c.writeErrMu.Lock()
 	err := c.writeErr
diff --git a/conn_write.go b/conn_write.go
index a509a21..497467a 100644
--- a/conn_write.go
+++ b/conn_write.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build go1.8
 // +build go1.8
 
 package websocket
diff --git a/conn_write_legacy.go b/conn_write_legacy.go
index 37edaff..8501a23 100644
--- a/conn_write_legacy.go
+++ b/conn_write_legacy.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build !go1.8
 // +build !go1.8
 
 package websocket
diff --git a/doc.go b/doc.go
index c6f4df8..8db0cef 100644
--- a/doc.go
+++ b/doc.go
@@ -187,9 +187,9 @@
 // than the largest message do not provide any benefit.
 //
 // Depending on the distribution of message sizes, setting the buffer size to
-// to a value less than the maximum expected message size can greatly reduce
-// memory use with a small impact on performance. Here's an example: If 99% of
-// the messages are smaller than 256 bytes and the maximum message size is 512
+// a value less than the maximum expected message size can greatly reduce memory
+// use with a small impact on performance. Here's an example: If 99% of the
+// messages are smaller than 256 bytes and the maximum message size is 512
 // bytes, then a buffer size of 256 bytes will result in 1.01 more system calls
 // than a buffer size of 512 bytes. The memory savings is 50%.
 //
diff --git a/examples/autobahn/server.go b/examples/autobahn/server.go
index c2d6ee5..8b17fe3 100644
--- a/examples/autobahn/server.go
+++ b/examples/autobahn/server.go
@@ -160,7 +160,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, "Not found.", http.StatusNotFound)
 		return
 	}
-	if r.Method != "GET" {
+	if r.Method != http.MethodGet {
 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 		return
 	}
diff --git a/examples/chat/home.html b/examples/chat/home.html
index a39a0c2..bf866af 100644
--- a/examples/chat/home.html
+++ b/examples/chat/home.html
@@ -92,7 +92,7 @@ body {
 <div id="log"></div>
 <form id="form">
     <input type="submit" value="Send" />
-    <input type="text" id="msg" size="64"/>
+    <input type="text" id="msg" size="64" autofocus />
 </form>
 </body>
 </html>
diff --git a/examples/chat/main.go b/examples/chat/main.go
index 9d4737a..474709f 100644
--- a/examples/chat/main.go
+++ b/examples/chat/main.go
@@ -18,7 +18,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, "Not found", http.StatusNotFound)
 		return
 	}
-	if r.Method != "GET" {
+	if r.Method != http.MethodGet {
 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 		return
 	}
diff --git a/examples/command/main.go b/examples/command/main.go
index 304f1a5..38d9f6c 100644
--- a/examples/command/main.go
+++ b/examples/command/main.go
@@ -170,7 +170,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, "Not found", http.StatusNotFound)
 		return
 	}
-	if r.Method != "GET" {
+	if r.Method != http.MethodGet {
 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 		return
 	}
diff --git a/examples/echo/client.go b/examples/echo/client.go
index bf0e657..7d870bd 100644
--- a/examples/echo/client.go
+++ b/examples/echo/client.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 package main
diff --git a/examples/echo/server.go b/examples/echo/server.go
index ecc680c..f9a0b7b 100644
--- a/examples/echo/server.go
+++ b/examples/echo/server.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build ignore
 // +build ignore
 
 package main
@@ -67,8 +68,9 @@ window.addEventListener("load", function(evt) {
 
     var print = function(message) {
         var d = document.createElement("div");
-        d.innerHTML = message;
+        d.textContent = message;
         output.appendChild(d);
+        output.scroll(0, output.scrollHeight);
     };
 
     document.getElementById("open").onclick = function(evt) {
@@ -126,7 +128,7 @@ You can change the message and send multiple times.
 <button id="send">Send</button>
 </form>
 </td><td valign="top" width="50%">
-<div id="output"></div>
+<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div>
 </td></tr></table>
 </body>
 </html>
diff --git a/examples/filewatch/main.go b/examples/filewatch/main.go
index b834ed3..d4bf80e 100644
--- a/examples/filewatch/main.go
+++ b/examples/filewatch/main.go
@@ -133,7 +133,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, "Not found", http.StatusNotFound)
 		return
 	}
-	if r.Method != "GET" {
+	if r.Method != http.MethodGet {
 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
 		return
 	}
diff --git a/go.sum b/go.sum
index cf4fbba..e69de29 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +0,0 @@
-github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
diff --git a/mask.go b/mask.go
index 577fce9..d0742bf 100644
--- a/mask.go
+++ b/mask.go
@@ -2,6 +2,7 @@
 // this source code is governed by a BSD-style license that can be found in the
 // LICENSE file.
 
+//go:build !appengine
 // +build !appengine
 
 package websocket
diff --git a/mask_safe.go b/mask_safe.go
index 2aac060..36250ca 100644
--- a/mask_safe.go
+++ b/mask_safe.go
@@ -2,6 +2,7 @@
 // this source code is governed by a BSD-style license that can be found in the
 // LICENSE file.
 
+//go:build appengine
 // +build appengine
 
 package websocket
diff --git a/prepared.go b/prepared.go
index 74ec565..c854225 100644
--- a/prepared.go
+++ b/prepared.go
@@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
 		// Prepare a frame using a 'fake' connection.
 		// TODO: Refactor code in conn.go to allow more direct construction of
 		// the frame.
-		mu := make(chan bool, 1)
-		mu <- true
+		mu := make(chan struct{}, 1)
+		mu <- struct{}{}
 		var nc prepareConn
 		c := &Conn{
 			conn:                   &nc,
diff --git a/proxy.go b/proxy.go
index e87a8c9..e0f466b 100644
--- a/proxy.go
+++ b/proxy.go
@@ -48,7 +48,7 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
 	}
 
 	connectReq := &http.Request{
-		Method: "CONNECT",
+		Method: http.MethodConnect,
 		URL:    &url.URL{Opaque: addr},
 		Host:   addr,
 		Header: connectHeader,
diff --git a/server.go b/server.go
index 887d558..24d53b3 100644
--- a/server.go
+++ b/server.go
@@ -23,6 +23,8 @@ func (e HandshakeError) Error() string { return e.message }
 
 // Upgrader specifies parameters for upgrading an HTTP connection to a
 // WebSocket connection.
+//
+// It is safe to call Upgrader's methods concurrently.
 type Upgrader struct {
 	// HandshakeTimeout specifies the duration for the handshake to complete.
 	HandshakeTimeout time.Duration
@@ -115,8 +117,8 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
 // Upgrade upgrades the HTTP server connection to the WebSocket protocol.
 //
 // The responseHeader is included in the response to the client's upgrade
-// request. Use the responseHeader to specify cookies (Set-Cookie) and the
-// application negotiated subprotocol (Sec-WebSocket-Protocol).
+// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
+// subprotocols supported by the server, set Upgrader.Subprotocols directly.
 //
 // If the upgrade fails, then Upgrade replies to the client with an HTTP error
 // response.
@@ -131,7 +133,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
 		return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
 	}
 
-	if r.Method != "GET" {
+	if r.Method != http.MethodGet {
 		return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
 	}
 
diff --git a/tls_handshake.go b/tls_handshake.go
new file mode 100644
index 0000000..a62b68c
--- /dev/null
+++ b/tls_handshake.go
@@ -0,0 +1,21 @@
+//go:build go1.17
+// +build go1.17
+
+package websocket
+
+import (
+	"context"
+	"crypto/tls"
+)
+
+func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
+	if err := tlsConn.HandshakeContext(ctx); err != nil {
+		return err
+	}
+	if !cfg.InsecureSkipVerify {
+		if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/tls_handshake_116.go b/tls_handshake_116.go
new file mode 100644
index 0000000..e1b2b44
--- /dev/null
+++ b/tls_handshake_116.go
@@ -0,0 +1,21 @@
+//go:build !go1.17
+// +build !go1.17
+
+package websocket
+
+import (
+	"context"
+	"crypto/tls"
+)
+
+func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
+	if err := tlsConn.Handshake(); err != nil {
+		return err
+	}
+	if !cfg.InsecureSkipVerify {
+		if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/trace.go b/trace.go
deleted file mode 100644
index 834f122..0000000
--- a/trace.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// +build go1.8
-
-package websocket
-
-import (
-	"crypto/tls"
-	"net/http/httptrace"
-)
-
-func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
-	if trace.TLSHandshakeStart != nil {
-		trace.TLSHandshakeStart()
-	}
-	err := doHandshake(tlsConn, cfg)
-	if trace.TLSHandshakeDone != nil {
-		trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
-	}
-	return err
-}
diff --git a/trace_17.go b/trace_17.go
deleted file mode 100644
index 77d05a0..0000000
--- a/trace_17.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// +build !go1.8
-
-package websocket
-
-import (
-	"crypto/tls"
-	"net/http/httptrace"
-)
-
-func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
-	return doHandshake(tlsConn, cfg)
-}


Test patch:
diff --git a/client_server_test.go b/client_server_test.go
index 8c6d012..c03f2a9 100644
--- a/client_server_test.go
+++ b/client_server_test.go
@@ -163,7 +163,7 @@ func TestProxyDial(t *testing.T) {
 	// Capture the request Host header.
 	s.Server.Config.Handler = http.HandlerFunc(
 		func(w http.ResponseWriter, r *http.Request) {
-			if r.Method == "CONNECT" {
+			if r.Method == http.MethodConnect {
 				connect = true
 				w.WriteHeader(http.StatusOK)
 				return
@@ -203,7 +203,7 @@ func TestProxyAuthorizationDial(t *testing.T) {
 		func(w http.ResponseWriter, r *http.Request) {
 			proxyAuth := r.Header.Get("Proxy-Authorization")
 			expectedProxyAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte("username:password"))
-			if r.Method == "CONNECT" && proxyAuth == expectedProxyAuth {
+			if r.Method == http.MethodConnect && proxyAuth == expectedProxyAuth {
 				connect = true
 				w.WriteHeader(http.StatusOK)
 				return
@@ -463,7 +463,7 @@ func TestBadMethod(t *testing.T) {
 	}))
 	defer s.Close()
 
-	req, err := http.NewRequest("POST", s.URL, strings.NewReader(""))
+	req, err := http.NewRequest(http.MethodPost, s.URL, strings.NewReader(""))
 	if err != nil {
 		t.Fatalf("NewRequest returned error %v", err)
 	}
@@ -481,6 +481,23 @@ func TestBadMethod(t *testing.T) {
 	}
 }
 
+func TestDialExtraTokensInRespHeaders(t *testing.T) {
+	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		challengeKey := r.Header.Get("Sec-Websocket-Key")
+		w.Header().Set("Upgrade", "foo, websocket")
+		w.Header().Set("Connection", "upgrade, keep-alive")
+		w.Header().Set("Sec-Websocket-Accept", computeAcceptKey(challengeKey))
+		w.WriteHeader(101)
+	}))
+	defer s.Close()
+
+	ws, _, err := cstDialer.Dial(makeWsProto(s.URL), nil)
+	if err != nil {
+		t.Fatalf("Dial: %v", err)
+	}
+	defer ws.Close()
+}
+
 func TestHandshake(t *testing.T) {
 	s := newServer(t)
 	defer s.Close()
@@ -587,7 +604,7 @@ func TestHost(t *testing.T) {
 		server             *httptest.Server // server to use
 		url                string           // host for request URI
 		header             string           // optional request host header
-		tls                string           // optiona host for tls ServerName
+		tls                string           // optional host for tls ServerName
 		wantAddr           string           // expected host for dial
 		wantHeader         string           // expected request header on server
 		insecureSkipVerify bool
@@ -718,7 +735,7 @@ func TestHost(t *testing.T) {
 			Dial:            dialer.NetDial,
 			TLSClientConfig: dialer.TLSClientConfig,
 		}
-		req, _ := http.NewRequest("GET", httpProtos[tt.server]+tt.url+"/", nil)
+		req, _ := http.NewRequest(http.MethodGet, httpProtos[tt.server]+tt.url+"/", nil)
 		if tt.header != "" {
 			req.Host = tt.header
 		}
diff --git a/conn_broadcast_test.go b/conn_broadcast_test.go
index cb88cbb..6e744fc 100644
--- a/conn_broadcast_test.go
+++ b/conn_broadcast_test.go
@@ -18,7 +18,6 @@ import (
 // scenarios with many subscribers in one channel.
 type broadcastBench struct {
 	w           io.Writer
-	message     *broadcastMessage
 	closeCh     chan struct{}
 	doneCh      chan struct{}
 	count       int32
@@ -52,14 +51,6 @@ func newBroadcastBench(usePrepared, compression bool) *broadcastBench {
 		usePrepared: usePrepared,
 		compression: compression,
 	}
-	msg := &broadcastMessage{
-		payload: textMessages(1)[0],
-	}
-	if usePrepared {
-		pm, _ := NewPreparedMessage(TextMessage, msg.payload)
-		msg.prepared = pm
-	}
-	bench.message = msg
 	bench.makeConns(10000)
 	return bench
 }
@@ -78,7 +69,7 @@ func (b *broadcastBench) makeConns(numConns int) {
 			for {
 				select {
 				case msg := <-c.msgCh:
-					if b.usePrepared {
+					if msg.prepared != nil {
 						c.conn.WritePreparedMessage(msg.prepared)
 					} else {
 						c.conn.WriteMessage(TextMessage, msg.payload)
@@ -100,9 +91,9 @@ func (b *broadcastBench) close() {
 	close(b.closeCh)
 }
 
-func (b *broadcastBench) runOnce() {
+func (b *broadcastBench) broadcastOnce(msg *broadcastMessage) {
 	for _, c := range b.conns {
-		c.msgCh <- b.message
+		c.msgCh <- msg
 	}
 	<-b.doneCh
 }
@@ -114,17 +105,25 @@ func BenchmarkBroadcast(b *testing.B) {
 		compression bool
 	}{
 		{"NoCompression", false, false},
-		{"WithCompression", false, true},
+		{"Compression", false, true},
 		{"NoCompressionPrepared", true, false},
-		{"WithCompressionPrepared", true, true},
+		{"CompressionPrepared", true, true},
 	}
+	payload := textMessages(1)[0]
 	for _, bm := range benchmarks {
 		b.Run(bm.name, func(b *testing.B) {
 			bench := newBroadcastBench(bm.usePrepared, bm.compression)
 			defer bench.close()
 			b.ResetTimer()
 			for i := 0; i < b.N; i++ {
-				bench.runOnce()
+				message := &broadcastMessage{
+					payload: payload,
+				}
+				if bench.usePrepared {
+					pm, _ := NewPreparedMessage(TextMessage, message.payload)
+					message.prepared = pm
+				}
+				bench.broadcastOnce(message)
 			}
 			b.ReportAllocs()
 		})
diff --git a/example_test.go b/example_test.go
index 96449ea..cd1883b 100644
--- a/example_test.go
+++ b/example_test.go
@@ -23,10 +23,9 @@ var (
 // This server application works with a client application running in the
 // browser. The client application does not explicitly close the websocket. The
 // only expected close message from the client has the code
-// websocket.CloseGoingAway. All other other close messages are likely the
+// websocket.CloseGoingAway. All other close messages are likely the
 // result of an application or protocol error and are logged to aid debugging.
 func ExampleIsUnexpectedCloseError() {
-
 	for {
 		messageType, p, err := c.ReadMessage()
 		if err != nil {
@@ -35,11 +34,11 @@ func ExampleIsUnexpectedCloseError() {
 			}
 			return
 		}
-		processMesage(messageType, p)
+		processMessage(messageType, p)
 	}
 }
 
-func processMesage(mt int, p []byte) {}
+func processMessage(mt int, p []byte) {}
 
 // TestX prevents godoc from showing this entire file in the example. Remove
 // this function when a second example is added.
diff --git a/server_test.go b/server_test.go
index 456c1db..3029aa8 100644
--- a/server_test.go
+++ b/server_test.go
@@ -98,7 +98,7 @@ func TestBufioReuse(t *testing.T) {
 		}
 		upgrader := Upgrader{}
 		c, err := upgrader.Upgrade(resp, &http.Request{
-			Method: "GET",
+			Method: http.MethodGet,
 			Header: http.Header{
 				"Upgrade":               []string{"websocket"},
 				"Connection":            []string{"upgrade"},