Skip to content

Commit f6137f3

Browse files
committed
Add minor improvements
Closes #179
1 parent e8dfe27 commit f6137f3

File tree

16 files changed

+359
-218
lines changed

16 files changed

+359
-218
lines changed

README.md

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# websocket
22

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

@@ -19,14 +19,14 @@ go get nhooyr.io/websocket
1919
- First class [context.Context](https://blog.golang.org/context) support
2020
- Thorough tests, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
2121
- [Zero dependencies](https://godoc.org/nhooyr.io/websocket?imports)
22-
- JSON and ProtoBuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
22+
- JSON and protobuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
2323
- Zero alloc reads and writes
2424
- Concurrent writes
2525
- [Close handshake](https://godoc.org/nhooyr.io/websocket#Conn.Close)
2626
- [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper
27-
- [Ping pong](https://godoc.org/nhooyr.io/websocket#Conn.Ping)
27+
- [Ping pong](https://godoc.org/nhooyr.io/websocket#Conn.Ping) API
2828
- [RFC 7692](https://tools.ietf.org/html/rfc7692) permessage-deflate compression
29-
- Compile to [Wasm](https://godoc.org/nhooyr.io/websocket#hdr-Wasm)
29+
- Can target [Wasm](https://godoc.org/nhooyr.io/websocket#hdr-Wasm)
3030

3131
## Roadmap
3232

@@ -85,7 +85,11 @@ c.Close(websocket.StatusNormalClosure, "")
8585

8686
### gorilla/websocket
8787

88-
[gorilla/websocket](https://github.com/gorilla/websocket) is a widely used and mature library.
88+
Advantages of [gorilla/websocket](https://github.com/gorilla/websocket):
89+
90+
- Mature and widely used
91+
- [Prepared writes](https://godoc.org/github.com/gorilla/websocket#PreparedMessage)
92+
- Configurable [buffer sizes](https://godoc.org/github.com/gorilla/websocket#hdr-Buffers)
8993

9094
Advantages of nhooyr.io/websocket:
9195

@@ -94,26 +98,26 @@ Advantages of nhooyr.io/websocket:
9498
- [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper
9599
- Zero alloc reads and writes ([gorilla/websocket#535](https://github.com/gorilla/websocket/issues/535))
96100
- Full [context.Context](https://blog.golang.org/context) support
97-
- Uses [net/http.Client](https://golang.org/pkg/net/http/#Client) for dialing
101+
- Dial uses [net/http.Client](https://golang.org/pkg/net/http/#Client)
98102
- Will enable easy HTTP/2 support in the future
99-
- Gorilla writes directly to a net.Conn and so duplicates features from net/http.Client.
103+
- Gorilla writes directly to a net.Conn and so duplicates features of net/http.Client.
100104
- Concurrent writes
101105
- Close handshake ([gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448))
102-
- Idiomatic [ping](https://godoc.org/nhooyr.io/websocket#Conn.Ping) API
103-
- gorilla/websocket requires registering a pong callback and then sending a Ping
104-
- Wasm ([gorilla/websocket#432](https://github.com/gorilla/websocket/issues/432))
106+
- Idiomatic [ping pong](https://godoc.org/nhooyr.io/websocket#Conn.Ping) API
107+
- Gorilla requires registering a pong callback before sending a Ping
108+
- Can target Wasm ([gorilla/websocket#432](https://github.com/gorilla/websocket/issues/432))
105109
- Transparent message buffer reuse with [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
106110
- [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4) faster WebSocket masking implementation in pure Go
107-
- Gorilla's implementation depends on unsafe and is slower
111+
- Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/).
108112
- Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support
109113
- Gorilla only supports no context takeover mode
110-
- [CloseRead](https://godoc.org/nhooyr.io/websocket#Conn.CloseRead) helper
114+
- [CloseRead](https://godoc.org/nhooyr.io/websocket#Conn.CloseRead) helper ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492))
111115
- Actively maintained ([gorilla/websocket#370](https://github.com/gorilla/websocket/issues/370))
112116

113117
#### golang.org/x/net/websocket
114118

115119
[golang.org/x/net/websocket](https://godoc.org/golang.org/x/net/websocket) is deprecated.
116-
See ([golang/go/issues/18152](https://github.com/golang/go/issues/18152)).
120+
See [golang/go/issues/18152](https://github.com/golang/go/issues/18152).
117121

118122
The [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper will ease in transitioning
119123
to nhooyr.io/websocket.
@@ -124,10 +128,3 @@ to nhooyr.io/websocket.
124128
in an event driven style for performance. See the author's [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb).
125129

126130
However when writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use.
127-
128-
## Users
129-
130-
If your company or project is using this library, feel free to open an issue or PR to amend this list.
131-
132-
- [Coder](https://github.com/cdr)
133-
- [Tatsu Works](https://github.com/tatsuworks) - Ingresses 20 TB in WebSocket data every month on their Discord bot.

accept_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strings"
88
"testing"
99

10-
"nhooyr.io/websocket/internal/assert"
10+
"cdr.dev/slog/sloggers/slogtest/assert"
1111
)
1212

1313
func TestAccept(t *testing.T) {
@@ -20,7 +20,7 @@ func TestAccept(t *testing.T) {
2020
r := httptest.NewRequest("GET", "/", nil)
2121

2222
_, err := Accept(w, r, nil)
23-
assert.ErrorContains(t, err, "protocol violation")
23+
assert.ErrorContains(t, "Accept", err, "protocol violation")
2424
})
2525

2626
t.Run("requireHttpHijacker", func(t *testing.T) {
@@ -34,7 +34,7 @@ func TestAccept(t *testing.T) {
3434
r.Header.Set("Sec-WebSocket-Key", "meow123")
3535

3636
_, err := Accept(w, r, nil)
37-
assert.ErrorContains(t, err, "http.ResponseWriter does not implement http.Hijacker")
37+
assert.ErrorContains(t, "Accept", err, "http.ResponseWriter does not implement http.Hijacker")
3838
})
3939
}
4040

@@ -127,9 +127,9 @@ func Test_verifyClientHandshake(t *testing.T) {
127127

128128
err := verifyClientRequest(r)
129129
if tc.success {
130-
assert.Success(t, err)
130+
assert.Success(t, "verifyClientRequest", err)
131131
} else {
132-
assert.Error(t, err)
132+
assert.Error(t, "verifyClientRequest", err)
133133
}
134134
})
135135
}
@@ -179,7 +179,7 @@ func Test_selectSubprotocol(t *testing.T) {
179179
r.Header.Set("Sec-WebSocket-Protocol", strings.Join(tc.clientProtocols, ","))
180180

181181
negotiated := selectSubprotocol(r, tc.serverProtocols)
182-
assert.Equal(t, tc.negotiated, negotiated, "negotiated")
182+
assert.Equal(t, "negotiated", tc.negotiated, negotiated)
183183
})
184184
}
185185
}
@@ -234,10 +234,14 @@ func Test_authenticateOrigin(t *testing.T) {
234234

235235
err := authenticateOrigin(r)
236236
if tc.success {
237-
assert.Success(t, err)
237+
assert.Success(t, "authenticateOrigin", err)
238238
} else {
239-
assert.Error(t, err)
239+
assert.Error(t, "authenticateOrigin", err)
240240
}
241241
})
242242
}
243243
}
244+
245+
func Test_acceptCompression(t *testing.T) {
246+
247+
}

assert_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ package websocket_test
33
import (
44
"context"
55
"crypto/rand"
6-
"io"
76
"strings"
87
"testing"
98

9+
"cdr.dev/slog/sloggers/slogtest/assert"
10+
1011
"nhooyr.io/websocket"
11-
"nhooyr.io/websocket/internal/assert"
1212
"nhooyr.io/websocket/wsjson"
1313
)
1414

1515
func randBytes(t *testing.T, n int) []byte {
1616
b := make([]byte, n)
17-
_, err := io.ReadFull(rand.Reader, b)
18-
assert.Success(t, err)
17+
_, err := rand.Reader.Read(b)
18+
assert.Success(t, "readRandBytes", err)
1919
return b
2020
}
2121

@@ -25,7 +25,7 @@ func assertJSONEcho(t *testing.T, ctx context.Context, c *websocket.Conn, n int)
2525

2626
exp := randString(t, n)
2727
err := wsjson.Write(ctx, c, exp)
28-
assert.Success(t, err)
28+
assert.Success(t, "wsjson.Write", err)
2929

3030
assertJSONRead(t, ctx, c, exp)
3131

@@ -37,9 +37,9 @@ func assertJSONRead(t *testing.T, ctx context.Context, c *websocket.Conn, exp in
3737

3838
var act interface{}
3939
err := wsjson.Read(ctx, c, &act)
40-
assert.Success(t, err)
40+
assert.Success(t, "wsjson.Read", err)
4141

42-
assert.Equal(t, exp, act, "JSON")
42+
assert.Equal(t, "json", exp, act)
4343
}
4444

4545
func randString(t *testing.T, n int) string {
@@ -60,19 +60,19 @@ func assertEcho(t *testing.T, ctx context.Context, c *websocket.Conn, typ websoc
6060

6161
p := randBytes(t, n)
6262
err := c.Write(ctx, typ, p)
63-
assert.Success(t, err)
63+
assert.Success(t, "write", err)
6464

6565
typ2, p2, err := c.Read(ctx)
66-
assert.Success(t, err)
66+
assert.Success(t, "read", err)
6767

68-
assert.Equal(t, typ, typ2, "data type")
69-
assert.Equal(t, p, p2, "payload")
68+
assert.Equal(t, "dataType", typ, typ2)
69+
assert.Equal(t, "payload", p, p2)
7070
}
7171

7272
func assertSubprotocol(t *testing.T, c *websocket.Conn, exp string) {
7373
t.Helper()
7474

75-
assert.Equal(t, exp, c.Subprotocol(), "subprotocol")
75+
assert.Equal(t, "subprotocol", exp, c.Subprotocol())
7676
}
7777

7878
func assertCloseStatus(t *testing.T, exp websocket.StatusCode, err error) {
@@ -82,5 +82,5 @@ func assertCloseStatus(t *testing.T, exp websocket.StatusCode, err error) {
8282
t.Logf("error: %+v", err)
8383
}
8484
}()
85-
assert.Equal(t, exp, websocket.CloseStatus(err), "StatusCode")
85+
assert.Equal(t, "closeStatus", exp, websocket.CloseStatus(err))
8686
}

ci/image/Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ FROM golang:1
22

33
RUN apt-get update
44
RUN apt-get install -y chromium
5-
RUN apt-get install -y npm
6-
RUN apt-get install -y jq
75

86
ENV GOFLAGS="-mod=readonly"
97
ENV PAGER=cat

ci/test.mk

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@ ci/out/coverage.html: gotest
99
coveralls: gotest
1010
# https://github.com/coverallsapp/github-action/blob/master/src/run.ts
1111
echo "--- coveralls"
12-
export GIT_BRANCH="$$GITHUB_REF"
13-
export BUILD_NUMBER="$$GITHUB_SHA"
14-
if [[ $$GITHUB_EVENT_NAME == pull_request ]]; then
15-
export CI_PULL_REQUEST="$$(jq .number "$$GITHUB_EVENT_PATH")"
16-
BUILD_NUMBER="$$BUILD_NUMBER-PR-$$CI_PULL_REQUEST"
17-
fi
18-
goveralls -coverprofile=ci/out/coverage.prof -service=github
12+
goveralls -coverprofile=ci/out/coverage.prof
1913

2014
gotest:
2115
go test -covermode=count -coverprofile=ci/out/coverage.prof -coverpkg=./... $${GOTESTFLAGS-} ./...

close.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
StatusProtocolError StatusCode = 1002
3131
StatusUnsupportedData StatusCode = 1003
3232

33-
// 1004 is reserved and so not exported.
33+
// 1004 is reserved and so unexported.
3434
statusReserved StatusCode = 1004
3535

3636
// StatusNoStatusRcvd cannot be sent in a close message.
@@ -103,7 +103,6 @@ func (c *Conn) Close(code StatusCode, reason string) error {
103103

104104
func (c *Conn) closeHandshake(code StatusCode, reason string) (err error) {
105105
defer errd.Wrap(&err, "failed to close WebSocket")
106-
defer c.close(nil)
107106

108107
err = c.writeClose(code, reason)
109108
if err != nil {
@@ -124,6 +123,14 @@ func (c *Conn) writeError(code StatusCode, err error) {
124123
}
125124

126125
func (c *Conn) writeClose(code StatusCode, reason string) error {
126+
c.closeMu.Lock()
127+
closing := c.wroteClose
128+
c.wroteClose = true
129+
c.closeMu.Unlock()
130+
if closing {
131+
return errors.New("already wrote close")
132+
}
133+
127134
ce := CloseError{
128135
Code: code,
129136
Reason: reason,

close_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"strings"
99
"testing"
1010

11-
"nhooyr.io/websocket/internal/assert"
11+
"cdr.dev/slog/sloggers/slogtest/assert"
1212
)
1313

1414
func TestCloseError(t *testing.T) {
@@ -52,9 +52,9 @@ func TestCloseError(t *testing.T) {
5252

5353
_, err := tc.ce.bytesErr()
5454
if tc.success {
55-
assert.Success(t, err)
55+
assert.Success(t, "CloseError.bytesErr", err)
5656
} else {
57-
assert.Error(t, err)
57+
assert.Error(t, "CloseError.bytesErr", err)
5858
}
5959
})
6060
}
@@ -104,10 +104,10 @@ func Test_parseClosePayload(t *testing.T) {
104104

105105
ce, err := parseClosePayload(tc.p)
106106
if tc.success {
107-
assert.Success(t, err)
108-
assert.Equal(t, tc.ce, ce, "CloseError")
107+
assert.Success(t, "parse err", err)
108+
assert.Equal(t, "ce", tc.ce, ce)
109109
} else {
110-
assert.Error(t, err)
110+
assert.Error(t, "parse err", err)
111111
}
112112
})
113113
}
@@ -153,7 +153,7 @@ func Test_validWireCloseCode(t *testing.T) {
153153
t.Run(tc.name, func(t *testing.T) {
154154
t.Parallel()
155155

156-
assert.Equal(t, tc.valid, validWireCloseCode(tc.code), "validWireCloseCode")
156+
assert.Equal(t, "valid", tc.valid, validWireCloseCode(tc.code))
157157
})
158158
}
159159
}
@@ -190,7 +190,7 @@ func TestCloseStatus(t *testing.T) {
190190
t.Run(tc.name, func(t *testing.T) {
191191
t.Parallel()
192192

193-
assert.Equal(t, tc.exp, CloseStatus(tc.in), "CloseStatus")
193+
assert.Equal(t, "closeStatus", tc.exp, CloseStatus(tc.in))
194194
})
195195
}
196196
}

conn.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ type Conn struct {
6363
closed chan struct{}
6464
closeMu sync.Mutex
6565
closeErr error
66-
wroteClose int64
66+
wroteClose bool
6767

6868
pingCounter int32
6969
activePingsMu sync.Mutex
@@ -244,7 +244,9 @@ func (m *mu) Lock(ctx context.Context) error {
244244
case <-m.c.closed:
245245
return m.c.closeErr
246246
case <-ctx.Done():
247-
return ctx.Err()
247+
err := fmt.Errorf("failed to acquire lock: %w", ctx.Err())
248+
m.c.close(err)
249+
return err
248250
case m.ch <- struct{}{}:
249251
return nil
250252
}

conn_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313
"testing"
1414
"time"
1515

16+
"cdr.dev/slog/sloggers/slogtest/assert"
17+
1618
"nhooyr.io/websocket"
17-
"nhooyr.io/websocket/internal/assert"
1819
)
1920

2021
func TestConn(t *testing.T) {
@@ -27,7 +28,7 @@ func TestConn(t *testing.T) {
2728
InsecureSkipVerify: true,
2829
CompressionMode: websocket.CompressionNoContextTakeover,
2930
})
30-
assert.Success(t, err)
31+
assert.Success(t, "accept", err)
3132
defer c.Close(websocket.StatusInternalError, "")
3233

3334
err = echoLoop(r.Context(), c)
@@ -47,7 +48,7 @@ func TestConn(t *testing.T) {
4748
opts.HTTPClient = s.Client()
4849

4950
c, _, err := websocket.Dial(ctx, wsURL, opts)
50-
assert.Success(t, err)
51+
assert.Success(t, "dial", err)
5152
assertJSONEcho(t, ctx, c, 2)
5253
})
5354
}

0 commit comments

Comments
 (0)