Skip to content

Commit dacae56

Browse files
committed
Polish for stable release
1 parent 80689e3 commit dacae56

12 files changed

+175
-99
lines changed

.gitignore

-3
This file was deleted.

README.md

+22-9
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55

66
websocket is a minimal and idiomatic WebSocket library for Go.
77

8-
This library is not final and the API is subject to change.
8+
This library is now production ready but some parts of the API are marked as experimental.
9+
10+
Please feel free to open an issue for feedback.
911

1012
## Install
1113

1214
```bash
13-
go get nhooyr.io/websocket@v0.2.0
15+
go get nhooyr.io/websocket
1416
```
1517

1618
## Features
1719

1820
- Minimal and idiomatic API
19-
- Tiny codebase at 1400 lines
21+
- Tiny codebase at 1700 lines
2022
- First class context.Context support
2123
- Thorough tests, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
2224
- Zero dependencies outside of the stdlib for the core library
2325
- JSON and ProtoBuf helpers in the wsjson and wspb subpackages
24-
- High performance
26+
- High performance, memory reuse wherever possible
2527
- Concurrent reads and writes out of the box
2628

2729
## Roadmap
@@ -88,8 +90,9 @@ c.Close(websocket.StatusNormalClosure, "")
8890
- net.Conn is never exposed as WebSocket over HTTP/2 will not have a net.Conn.
8991
- Using net/http's Client for dialing means we do not have to reinvent dialing hooks
9092
and configurations like other WebSocket libraries
91-
- We do not support the compression extension because Go's compress/flate library is very memory intensive
92-
and browsers do not handle WebSocket compression intelligently. See [#5](https://github.com/nhooyr/websocket/issues/5)
93+
- We do not support the deflate compression extension because Go's compress/flate library
94+
is very memory intensive and browsers do not handle WebSocket compression intelligently.
95+
See [#5](https://github.com/nhooyr/websocket/issues/5)
9396

9497
## Comparison
9598

@@ -111,7 +114,7 @@ Just compare the godoc of
111114

112115
The API for nhooyr/websocket has been designed such that there is only one way to do things
113116
which makes it easy to use correctly. Not only is the API simpler, the implementation is
114-
only 1400 lines whereas gorilla/websocket is at 3500 lines. That's more code to maintain,
117+
only 1700 lines whereas gorilla/websocket is at 3500 lines. That's more code to maintain,
115118
more code to test, more code to document and more surface area for bugs.
116119

117120
The future of gorilla/websocket is also uncertain. See [gorilla/websocket#370](https://github.com/gorilla/websocket/issues/370).
@@ -124,8 +127,18 @@ it has to reinvent hooks for TLS and proxies and prevents support of HTTP/2.
124127
Some more advantages of nhooyr/websocket are that it supports concurrent reads,
125128
writes and makes it very easy to close the connection with a status code and reason.
126129

127-
In terms of performance, the only difference is nhooyr/websocket is forced to use one extra
128-
goroutine for context.Context support. Otherwise, they perform identically.
130+
nhooyr/websocket also responds to pings, pongs and close frames in a separate goroutine so that
131+
your application doesn't always need to read from the connection unless it expects a data message.
132+
gorilla/websocket requires you to constantly read from the connection to respond to control frames
133+
even if you don't expect the peer to send any messages.
134+
135+
In terms of performance, the differences depend on your application code. nhooyr/websocket
136+
reuses buffers efficiently out of the box whereas gorilla/websocket does not. As mentioned
137+
above, it also supports concurrent readers and writers out of the box.
138+
139+
The only performance downside to nhooyr/websocket is that uses two extra goroutines. One for
140+
reading pings, pongs and close frames async to application code and another to support
141+
context.Context cancellation. This costs 4 KB of memory which is fairly cheap.
129142

130143
### x/net/websocket
131144

ci/bench/entrypoint.sh

+6-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22

33
source ci/lib.sh || exit 1
44

5-
mkdir -p profs
6-
7-
go test --vet=off --run=^$ -bench=. \
8-
-cpuprofile=profs/cpu \
9-
-memprofile=profs/mem \
10-
-blockprofile=profs/block \
11-
-mutexprofile=profs/mutex \
5+
go test --vet=off --run=^$ -bench=. -o=ci/out/websocket.test \
6+
-cpuprofile=ci/out/cpu.prof \
7+
-memprofile=ci/out/mem.prof \
8+
-blockprofile=ci/out/block.prof \
9+
-mutexprofile=ci/out/mutex.prof \
1210
.
1311

1412
set +x
1513
echo
16-
echo "profiles are in ./profs
14+
echo "profiles are in ./ci/out/profs
1715
keep in mind that every profiler Go provides is enabled so that may skew the benchmarks"

ci/lint/entrypoint.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ source ci/lib.sh || exit 1
77
shellcheck ./**/*.sh
88
)
99

10-
go vet -composites=false -lostcancel=false ./...
10+
go vet ./...
1111
go run golang.org/x/lint/golint -set_exit_status ./...

ci/out/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*

ci/test/entrypoint.sh

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
source ci/lib.sh || exit 1
44

5-
mkdir -p profs
6-
75
set +x
86
echo
97
echo "this step includes benchmarks for race detection and coverage purposes
@@ -12,15 +10,15 @@ accurate numbers"
1210
echo
1311
set -x
1412

15-
go test -race -coverprofile=profs/coverage --vet=off -bench=. ./...
16-
go tool cover -func=profs/coverage
13+
go test -race -coverprofile=ci/out/coverage.prof --vet=off -bench=. ./...
14+
go tool cover -func=ci/out/coverage.prof
1715

1816
if [[ $CI ]]; then
19-
bash <(curl -s https://codecov.io/bash) -f profs/coverage
17+
bash <(curl -s https://codecov.io/bash) -f ci/out/coverage.prof
2018
else
21-
go tool cover -html=profs/coverage -o=profs/coverage.html
19+
go tool cover -html=ci/out/coverage.prof -o=ci/out/coverage.html
2220

2321
set +x
2422
echo
25-
echo "please open profs/coverage.html to see detailed test coverage stats"
23+
echo "please open ci/out/coverage.html to see detailed test coverage stats"
2624
fi

example_echo_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ func Example_echo() {
5151

5252
// Now we dial the server, send the messages and echo the responses.
5353
err = client("ws://" + l.Addr().String())
54-
time.Sleep(time.Second)
5554
if err != nil {
5655
log.Fatalf("client failed: %v", err)
5756
}

limitedreader.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package websocket
2+
3+
import (
4+
"fmt"
5+
"io"
6+
7+
"golang.org/x/xerrors"
8+
)
9+
10+
type limitedReader struct {
11+
c *Conn
12+
r io.Reader
13+
left int64
14+
limit int64
15+
}
16+
17+
func (lr *limitedReader) Read(p []byte) (int, error) {
18+
if lr.limit == 0 {
19+
lr.limit = lr.left
20+
}
21+
22+
if lr.left <= 0 {
23+
msg := fmt.Sprintf("read limited at %v bytes", lr.limit)
24+
lr.c.Close(StatusPolicyViolation, msg)
25+
return 0, xerrors.Errorf(msg)
26+
}
27+
28+
if int64(len(p)) > lr.left {
29+
p = p[:lr.left]
30+
}
31+
n, err := lr.r.Read(p)
32+
lr.left -= int64(n)
33+
return n, err
34+
}

0 commit comments

Comments
 (0)