10000 Merge pull request #187 from nhooyr/release-v1.8.0 · coder/websocket@94f9b71 · GitHub
[go: up one dir, main page]

Skip to content

Commit 94f9b71

Browse files
authored
Merge pull request #187 from nhooyr/release-v1.8.0
Release v1.8.0
2 parents b961007 + 4735f36 commit 94f9b71

32 files changed

+313
-235
lines changed

Makefile

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ all: fmt lint test
22

33
.SILENT:
44

5-
.PHONY: *
6-
7-
.ONESHELL:
8-
SHELL = bash
9-
.SHELLFLAGS = -ceuo pipefail
10-
115
include ci/fmt.mk
126
include ci/lint.mk
137
include ci/test.mk

README.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# websocket
22

3-
[![release](https://img.shields.io/github/v/release/nhooyr/websocket?color=6b9ded&sort=semver)](https://github.com/nhooyr/websocket/releases)
43
[![godoc](https://godoc.org/nhooyr.io/websocket?status.svg)](https://godoc.org/nhooyr.io/websocket)
5-
[![coverage](https://img.shields.io/coveralls/github/nhooyr/websocket?color=65d6a4)](https://coveralls.io/github/nhooyr/websocket)
6-
[![ci](https://github.com/nhooyr/websocket/workflows/ci/badge.svg)](https://github.com/nhooyr/websocket/actions)
74

85
websocket is a minimal and idiomatic WebSocket library for Go.
96

@@ -17,7 +14,8 @@ go get nhooyr.io/websocket
1714

1815
- Minimal and idiomatic API
1916
- First class [context.Context](https://blog.golang.org/context) support
20-
- Thorough tests, fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
17+
- Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
18+
- Thorough unit tests with [90% coverage](https://coveralls.io/github/nhooyr/websocket)
2119
- [Minimal dependencies](https://godoc.org/nhooyr.io/websocket?imports)
2220
- JSON and protobuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
2321
- Zero alloc reads and writes
@@ -111,8 +109,7 @@ Advantages of nhooyr.io/websocket:
111109
- Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/).
112110
- Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support
113111
- Gorilla only supports no context takeover mode
114-
- Uses [klauspost/compress](https://github.com/klauspost/compress) for optimized compression
115-
- See [gorilla/websocket#203](https://github.com/gorilla/websocket/issues/203)
112+
- We use [klauspost/compress](https://github.com/klauspost/compress) for much lower memory usage ([gorilla/websocket#203](https://github.com/gorilla/websocket/issues/203))
116113
- [CloseRead](https://godoc.org/nhooyr.io/websocket#Conn.CloseRead) helper ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492))
117114
- Actively maintained ([gorilla/websocket#370](https://github.com/gorilla/websocket/issues/370))
118115

accept.go

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import (
66
"bytes"
77
"crypto/sha1"
88
"encoding/base64"
9+
"errors"
10+
"fmt"
911
"io"
1012
"net/http"
1113
"net/textproto"
1214
"net/url"
15+
"strconv"
1316
"strings"
1417

15-
"golang.org/x/xerrors"
16-
1718
"nhooyr.io/websocket/internal/errd"
1819
)
1920

@@ -85,7 +86,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
8586

8687
hj, ok := w.(http.Hijacker)
8788
if !ok {
88-
err = xerrors.New("http.ResponseWriter does not implement http.Hijacker")
89+
err = errors.New("http.ResponseWriter does not implement http.Hijacker")
8990
http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
9091
return nil, err
9192
}
@@ -110,7 +111,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
110111

111112
netConn, brw, err := hj.Hijack()
112113
if err != nil {
113-
err = xerrors.Errorf("failed to hijack connection: %w", err)
114+
err = fmt.Errorf("failed to hijack connection: %w", err)
114115
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
115116
return nil, err
116117
}
@@ -133,32 +134,32 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
133134

134135
func verifyClientRequest(w http.ResponseWriter, r *http.Request) (errCode int, _ error) {
135136
if !r.ProtoAtLeast(1, 1) {
136-
return http.StatusUpgradeRequired, xerrors.Errorf("WebSocket protocol violation: handshake request must be at least HTTP/1.1: %q", r.Proto)
137+
return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: handshake request must be at least HTTP/1.1: %q", r.Proto)
137138
}
138139

139140
if !headerContainsToken(r.Header, "Connection", "Upgrade") {
140141
w.Header().Set("Connection", "Upgrade")
141142
w.Header().Set("Upgrade", "websocket")
142-
return http.StatusUpgradeRequired, xerrors.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection"))
143+
return http.StatusUpgrad 10000 eRequired, fmt.Errorf("WebSocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection"))
143144
}
144145

145146
if !headerContainsToken(r.Header, "Upgrade", "websocket") {
146147
w.Header().Set("Connection", "Upgrade")
147148
w.Header().Set("Upgrade", "websocket")
148-
return http.StatusUpgradeRequired, xerrors.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade"))
149+
return http.StatusUpgradeRequired, fmt.Errorf("WebSocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade"))
149150
}
150151

151152
if r.Method != "GET" {
152-
return http.StatusMethodNotAllowed, xerrors.Errorf("WebSocket protocol violation: handshake request method is not GET but %q", r.Method)
153+
return http.StatusMethodNotAllowed, fmt.Errorf("WebSocket protocol violation: handshake request method is not GET but %q", r.Method)
153154
}
154155

155156
if r.Header.Get("Sec-WebSocket-Version") != "13" {
156157
w.Header().Set("Sec-WebSocket-Version", "13")
157-
return http.StatusBadRequest, xerrors.Errorf("unsupported WebSocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
158+
return http.StatusBadRequest, fmt.Errorf("unsupported WebSocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version"))
158159
}
159160

160161
if r.Header.Get("Sec-WebSocket-Key") == "" {
161-
return http.StatusBadRequest, xerrors.New("WebSocket protocol violation: missing Sec-WebSocket-Key")
162+
return http.StatusBadRequest, errors.New("WebSocket protocol violation: missing Sec-WebSocket-Key")
162163
}
163164

164165
return 0, nil
@@ -169,10 +170,10 @@ func authenticateOrigin(r *http.Request) error {
169170
if origin != "" {
170171
u, err := url.Parse(origin)
171172
if err != nil {
172-
return xerrors.Errorf("failed to parse Origin header %q: %w", origin, err)
173+
return fmt.Errorf("failed to parse Origin header %q: %w", origin, err)
173174
}
174175
if !strings.EqualFold(u.Host, r.Host) {
175-
return xerrors.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
176+
return fmt.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
176177
}
177178
}
178179
return nil
@@ -208,6 +209,7 @@ func acceptCompression(r *http.Request, w http.ResponseWriter, mode CompressionM
208209

209210
func acceptDeflate(w http.ResponseWriter, ext websocketExtension, mode CompressionMode) (*compressionOptions, error) {
210211
copts := mode.opts()
212+
copts.serverMaxWindowBits = 8
211213

212214
for _, p := range ext.params {
213215
switch p {
@@ -219,11 +221,31 @@ func acceptDeflate(w http.ResponseWriter, ext websocketExtension, mode Compressi
219221
continue
220222
}
221223

222-
if strings.HasPrefix(p, "client_max_window_bits") || strings.HasPrefix(p, "server_max_window_bits") {
224+
if strings.HasPrefix(p, "client_max_window_bits") {
225+
continue
226+
227+
// bits, ok := parseExtensionParameter(p, 15)
228+
// if !ok || bits < 8 || bits > 16 {
229+
// err := fmt.Errorf("invalid client_max_window_bits: %q", p)
230+
// http.Error(w, err.Error(), http.StatusBadRequest)
231+
// return nil, err
232+
// }
233+
// copts.clientMaxWindowBits = bits
234+
// continue
235+
}
236+
237+
if false && strings.HasPrefix(p, "server_max_window_bits") {
238+
// We always send back 8 but make sure to validate.
239+
bits, ok := parseExtensionParameter(p, 0)
240+
if !ok || bits < 8 || bits > 16 {
241+
err := fmt.Errorf("invalid server_max_window_bits: %q", p)
242+
http.Error(w, err.Error(), http.StatusBadRequest)
243+
return nil, err
244+
}
223245
continue
224246
}
225247

226-
err := xerrors.Errorf("unsupported permessage-deflate parameter: %q", p)
248+
err := fmt.Errorf("unsupported permessage-deflate parameter: %q", p)
227249
http.Error(w, err.Error(), http.StatusBadRequest)
228250
return nil, err
229251
}
@@ -233,6 +255,21 @@ func acceptDeflate(w http.ResponseWriter, ext websocketExtension, mode Compressi
233255
return copts, nil
234256
}
235257

258+
// parseExtensionParameter parses the value in the extension parameter p.
259+
// It falls back to defaultVal if there is no value.
260+
// If defaultVal == 0, then ok == false if there is no value.
261+
func parseExtensionParameter(p string, defaultVal int) (int, bool) {
262+
ps := strings.Split(p, "=")
263+
if len(ps) == 1 {
264+
if defaultVal > 0 {
265+
return defaultVal, true
266+
}
267+
return 0, false
268+
}
269+
i, e := strconv.Atoi(strings.Trim(ps[1], `"`))
270+
return i, e == nil
271+
}
272+
236273
func acceptWebkitDeflate(w http.ResponseWriter, ext websocketExtension, mode CompressionMode) (*compressionOptions, error) {
237274
copts := mode.opts()
238275
// The peer must explicitly request it.
@@ -253,7 +290,7 @@ func acceptWebkitDeflate(w http.ResponseWriter, ext websocketExtension, mode Com
253290
//
254291
// Either way, we're only implementing this for webkit which never sends the max_window_bits
255292
// parameter so we don't need to worry about it.
256-
err := xerrors.Errorf("unsupported x-webkit-deflate-frame parameter: %q", p)
293+
err := fmt.Errorf("unsupported x-webkit-deflate-frame parameter: %q", p)
257294
http.Error(w, err.Error(), http.StatusBadRequest)
258295
return nil, err
259296
}

accept_js.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package websocket
22

33
import (
4+
"errors"
45
"net/http"
5-
6-
"golang.org/x/xerrors"
76
)
87

98
// AcceptOptions represents Accept's options.
@@ -16,5 +15,5 @@ type AcceptOptions struct {
1615

1716
// Accept is stubbed out for Wasm.
1817
func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) {
19-
return nil, xerrors.New("unimplemented")
18+
return nil, errors.New("unimplemented")
2019
}

accept_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ package websocket
44

55
import (
66
"bufio"
7+
"errors"
78
"net"
89
"net/http"
910
"net/http/httptest"
1011
"strings"
1112
"testing"
1213

13-
"golang.org/x/xerrors"
14-
1514
"nhooyr.io/websocket/internal/test/assert"
1615
)
1716

@@ -80,7 +79,7 @@ func TestAccept(t *testing.T) {
8079
w := mockHijacker{
8180
ResponseWriter: httptest.NewRecorder(),
8281
hijack: func() (conn net.Conn, writer *bufio.ReadWriter, err error) {
83-
return nil, nil, xerrors.New("haha")
82+
return nil, nil, errors.New("haha")
8483
},
8584
}
8685

@@ -328,6 +327,7 @@ func Test_acceptCompression(t *testing.T) {
328327
expCopts: &compressionOptions{
329328
clientNoContextTakeover: true,
330329
serverNoContextTakeover: true,
330+
serverMaxWindowBits: 8,
331331
},
332332
},
333333
{

autobahn_test.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import (
1515
"testing"
1616
"time"
1717

18-
"golang.org/x/xerrors"
19-
2018
"nhooyr.io/websocket"
2119
"nhooyr.io/websocket/internal/errd"
2220
"nhooyr.io/websocket/internal/test/assert"
@@ -108,7 +106,7 @@ func wstestClientServer(ctx context.Context) (url string, closeFn func(), err er
108106
"exclude-cases": excludedAutobahnCases,
109107
})
110108
if err != nil {
111-
return "", nil, xerrors.Errorf("failed to write spec: %w", err)
109+
return "", nil, fmt.Errorf("failed to write spec: %w", err)
112110
}
113111

114112
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*15)
@@ -126,7 +124,7 @@ func wstestClientServer(ctx context.Context) (url string, closeFn func(), err er
126124
wstest := exec.CommandContext(ctx, "wstest", args...)
127125
err = wstest.Start()
128126
if err != nil {
129-
return "", nil, xerrors.Errorf("failed to start wstest: %w", err)
127+
return "", nil, fmt.Errorf("failed to start wstest: %w", err)
130128
}
131129

132130
return url, func() {
@@ -209,20 +207,20 @@ func unusedListenAddr() (_ string, err error) {
209207
func tempJSONFile(v interface{}) (string, error) {
210208
f, err := ioutil.TempFile("", "temp.json")
211209
if err != nil {
212-
return "", xerrors.Errorf("temp file: %w", err)
210+
return "", fmt.Errorf("temp file: %w", err)
213211
}
214212
defer f.Close()
215213

216214
e := json.NewEncoder(f)
217215
e.SetIndent("", "\t")
218216
err = e.Encode(v)
219217
if err != nil {
220-
return "", xerrors.Errorf("json encode: %w", err)
218+
return "", fmt.Errorf("json encode: %w", err)
221219
}
222220

223221
err = f.Close()
224222
if err != nil {
225-
return "", xerrors.Errorf("close temp file: %w", err)
223+
return "", fmt.Errorf("close temp file: %w", err)
226224
}
227225

228226
return f.Name(), nil

ci/ensure_fmt.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
main() {
6+
local files
7+
mapfile -t files < <(git ls-files --other --modified --exclude-standard)
8+
if [[ ${files[*]} == "" ]]; then
9+
return
10+
fi
11+
12+
echo "Files need generation or are formatted incorrectly:"
13+
for f in "${files[@]}"; do
14+
echo " $f"
15+
done
16+
17+
echo
18+
echo "Please run the following locally:"
19+
echo " make fmt"
20+
exit 1
21+
}
22+
23+
main "$@"

ci/fmt.mk

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
fmt: modtidy gofmt goimports prettier
1+
fmt: modtidy gofmt goimports prettier shfmt
22
ifdef CI
3-
if [[ $$(git ls-files --other --modified --exclude-standard) != "" ]]; then
4-
echo "Files need generation or are formatted incorrectly:"
5-
git -c color.ui=always status | grep --color=no '\e\[31m'
6-
echo "Please run the following locally:"
7-
echo " make fmt"
8-
exit 1
9-
fi
3+
./ci/ensure_fmt.sh
104
endif
115

126
modtidy: gen
@@ -23,3 +17,6 @@ prettier:
2317

2418
gen:
2519
stringer -type=opcode,MessageType,StatusCode -output=stringer.go
20+
21+
shfmt:
22+
shfmt -i 2 -w -s -sr $$(git ls-files "*.sh")

ci/image/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
FROM golang:1
22

33
RUN apt-get update
4-
RUN apt-get install -y chromium npm
4+
RUN apt-get install -y chromium npm shellcheck
5+
6+
ARG SHFMT_URL=https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_linux_amd64
7+
RUN curl -L $SHFMT_URL > /usr/local/bin/shfmt && chmod +x /usr/local/bin/shfmt
58

69
ENV GOFLAGS="-mod=readonly"
7-
ENV PAGER=cat
810
ENV CI=true
911
ENV MAKEFLAGS="--jobs=16 --output-sync=target"
1012

ci/lint.mk

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
lint: govet golint
1+
lint: govet golint govet-wasm golint-wasm shellcheck
22

33
govet:
44
go vet ./...
@@ -11,3 +11,6 @@ golint:
1111

1212
golint-wasm:
1313
GOOS=js GOARCH=wasm golint -set_exit_status ./...
14+
15+
shellcheck:
16+
shellcheck $$(git ls-files "*.sh")

ci/test.mk

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ ci/out/coverage.html: gotest
77
go tool cover -html=ci/out/coverage.prof -o=ci/out/coverage.html
88

99
coveralls: gotest
10-
# https://github.com/coverallsapp/github-action/blob/master/src/run.ts
1110
echo "--- coveralls"
1211
goveralls -coverprofile=ci/out/coverage.prof
1312

0 commit comments

Comments
 (0)
0