Skip to content

Commit b961007

Browse files
authored
Merge pull request #163 from nhooyr/compress
Rewrite with compression support
2 parents 449143b + 1bc100d commit b961007

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+4213
-5328
lines changed

Diff for: .github/CODEOWNERS

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

Diff for: .github/CONTRIBUTING.md

-45
This file was deleted.

Diff for: .github/ISSUE_TEMPLATE.md

-4
This file was deleted.

Diff for: .github/PULL_REQUEST_TEMPLATE.md

-4
This file was deleted.

Diff for: .github/workflows/ci.yml

+33-7
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,50 @@ on: [push, pull_request]
44
jobs:
55
fmt:
66
runs-on: ubuntu-latest
7-
container: nhooyr/websocket-ci@sha256:8a8fd73fdea33585d50a33619c4936adfd016246a2ed6bbfbf06def24b518a6a
87
steps:
98
- uses: actions/checkout@v1
10-
- run: make fmt
9+
- uses: actions/cache@v1
10+
with:
11+
path: ~/go/pkg/mod
12+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
13+
restore-keys: |
14+
${{ runner.os }}-go-
15+
- name: Run make fmt
16+
uses: ./ci/image
17+
with:
18+
args: make fmt
19+
1120
lint:
1221
runs-on: ubuntu-latest
13-
container: nhooyr/websocket-ci@sha256:8a8fd73fdea33585d50a33619c4936adfd016246a2ed6bbfbf06def24b518a6a
1422
steps:
1523
- uses: actions/checkout@v1
16-
- run: make lint
24+
- uses: actions/cache@v1
25+
with:
26+
path: ~/go/pkg/mod
27+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
28+
restore-keys: |
29+
${{ runner.os }}-go-
30+
- name: Run make lint
31+
uses: ./ci/image
32+
with:
33+
args: make lint
34+
1735
test:
1836
runs-on: ubuntu-latest
19-
container: nhooyr/websocket-ci@sha256:8a8fd73fdea33585d50a33619c4936adfd016246a2ed6bbfbf06def24b518a6a
2037
steps:
2138
- uses: actions/checkout@v1
22-
- run: make test
39+
- uses: actions/cache@v1
40+
with:
41+
path: ~/go/pkg/mod
42+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
43+
restore-keys: |
44+
${{ runner.os }}-go-
45+
- name: Run make test
46+
uses: ./ci/image
47+
with:
48+
args: make test
2349
env:
24-
COVERALLS_TOKEN: ${{ secrets.github_token }}
50+
COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
2551
- name: Upload coverage.html
2652
uses: actions/upload-artifact@master
2753
with:

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
websocket.test

Diff for: Makefile

-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,3 @@ SHELL = bash
1111
include ci/fmt.mk
1212
include ci/lint.mk
1313
include ci/test.mk
14-
15-
ci-image:
16-
docker build -f ./ci/Dockerfile -t nhooyr/websocket-ci .
17-
docker push nhooyr/websocket-ci

Diff for: README.md

+56-105
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# websocket
22

3-
[![GitHub 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)
5-
[![Coveralls](https://img.shields.io/coveralls/github/nhooyr/websocket?color=65d6a4)](https://coveralls.io/github/nhooyr/websocket)
6-
[![CI Status](https://github.com/nhooyr/websocket/workflows/ci/badge.svg)](https://github.com/nhooyr/websocket/actions)
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)
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)
77

88
websocket is a minimal and idiomatic WebSocket library for Go.
99

@@ -16,28 +16,25 @@ go get nhooyr.io/websocket
1616
## Features
1717

1818
- Minimal and idiomatic API
19-
- Tiny codebase at 2200 lines
2019
- First class [context.Context](https://blog.golang.org/context) support
21-
- Thorough tests, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
22-
- [Zero dependencies](https://godoc.org/nhooyr.io/websocket?imports)
23-
- JSON and ProtoBuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
24-
- Highly optimized by default
25-
- Concurrent writes out of the box
26-
- [Complete Wasm](https://godoc.org/nhooyr.io/websocket#hdr-Wasm) support
20+
- Thorough tests, fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
21+
- [Minimal 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
23+
- Zero alloc reads and writes
24+
- Concurrent writes
2725
- [Close handshake](https://godoc.org/nhooyr.io/websocket#Conn.Close)
26+
- [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper
27+
- [Ping pong](https://godoc.org/nhooyr.io/websocket#Conn.Ping) API
28+
- [RFC 7692](https://tools.ietf.org/html/rfc7692) permessage-deflate compression
29+
- Compile to [Wasm](https://godoc.org/nhooyr.io/websocket#hdr-Wasm)
2830

2931
## Roadmap
3032

31-
- [ ] Compression Extensions [#163](https://github.com/nhooyr/websocket/pull/163)
3233
- [ ] HTTP/2 [#4](https://github.com/nhooyr/websocket/issues/4)
3334

3435
## Examples
3536

36-
For a production quality example that shows off the full API, see the [echo example on the godoc](https://godoc.org/nhooyr.io/websocket#example-package--Echo). On github, the example is at [example_echo_test.go](./example_echo_test.go).
37-
38-
Use the [errors.As](https://golang.org/pkg/errors/#As) function [new in Go 1.13](https://golang.org/doc/go1.13#error_wrapping) to check for [websocket.CloseError](https://godoc.org/nhooyr.io/websocket#CloseError).
39-
There is also [websocket.CloseStatus](https://godoc.org/nhooyr.io/websocket#CloseStatus) to quickly grab the close status code out of a [websocket.CloseError](https://godoc.org/nhooyr.io/websocket#CloseError).
40-
See the [CloseStatus godoc example](https://godoc.org/nhooyr.io/websocket#example-CloseStatus).
37+
For a production quality example that demonstrates the complete API, see the [echo example](https://godoc.org/nhooyr.io/websocket#example-package--Echo).
4138

4239
### Server
4340

@@ -84,98 +81,52 @@ if err != nil {
8481
c.Close(websocket.StatusNormalClosure, "")
8582
```
8683

87-
## Design justifications
88-
89-
- A minimal API is easier to maintain due to less docs, tests and bugs
90-
- A minimal API is also easier to use and learn
91-
- Context based cancellation is more ergonomic and robust than setting deadlines
92-
- net.Conn is never exposed as WebSocket over HTTP/2 will not have a net.Conn.
93-
- Using net/http's Client for dialing means we do not have to reinvent dialing hooks
94-
and configurations like other WebSocket libraries
95-
9684
## Comparison
9785

98-
Before the comparison, I want to point out that both gorilla/websocket and gobwas/ws were
99-
extremely useful in implementing the WebSocket protocol correctly so _big thanks_ to the
100-
authors of both. In particular, I made sure to go through the issue tracker of gorilla/websocket
101-
to ensure I implemented details correctly and understood how people were using WebSockets in
102-
production.
103-
10486
### gorilla/websocket
10587

106-
https://github.com/gorilla/websocket
107-
108-
The implementation of gorilla/websocket is 6 years old. As such, it is
109-
widely used and very mature compared to nhooyr.io/websocket.
110-
111-
On the other hand, it has grown organically and now there are too many ways to do
112-
the same thing. Compare the godoc of
113-
[nhooyr/websocket](https://godoc.org/nhooyr.io/websocket) with
114-
[gorilla/websocket](https://godoc.org/github.com/gorilla/websocket) side by side.
115-
116-
The API for nhooyr.io/websocket has been designed such that there is only one way to do things.
117-
This makes it easy to use correctly. Not only is the API simpler, the implementation is
118-
only 2200 lines whereas gorilla/websocket is at 3500 lines. That's more code to maintain,
119-
more code to test, more code to document and more surface area for bugs.
120-
121-
Moreover, nhooyr.io/websocket supports newer Go idioms such as context.Context.
122-
It also uses net/http's Client and ResponseWriter directly for WebSocket handshakes.
123-
gorilla/websocket writes its handshakes to the underlying net.Conn.
124-
Thus it has to reinvent hooks for TLS and proxies and prevents support of HTTP/2.
125-
126-
Some more advantages of nhooyr.io/websocket are that it supports concurrent writes and
127-
makes it very easy to close the connection with a status code and reason. In fact,
128-
nhooyr.io/websocket even implements the complete WebSocket close handshake for you whereas
129-
with gorilla/websocket you have to perform it manually. See [gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448).
130-
131-
The ping API is also nicer. gorilla/websocket requires registering a pong handler on the Conn
132-
which results in awkward control flow. With nhooyr.io/websocket you use the Ping method on the Conn
133-
that sends a ping and also waits for the pong.
134-
135-
Additionally, nhooyr.io/websocket can compile to [Wasm](https://godoc.org/nhooyr.io/websocket#hdr-Wasm) for the browser.
136-
137-
In terms of performance, the differences mostly depend on your application code. nhooyr.io/websocket
138-
reuses message buffers out of the box if you use the wsjson and wspb subpackages.
139-
As mentioned above, nhooyr.io/websocket also supports concurrent writers.
140-
141-
The WebSocket masking algorithm used by this package is also [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4)
142-
faster than gorilla/websocket or gobwas/ws while using only pure safe Go.
88+
Advantages of [gorilla/websocket](https://github.com/gorilla/websocket):
14389

144-
The only performance con to nhooyr.io/websocket is that it uses one extra goroutine to support
145-
cancellation with context.Context. This costs 2 KB of memory which is cheap compared to
146-
the benefits.
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)
14793

148-
### x/net/websocket
94+
Advantages of nhooyr.io/websocket:
14995

150-
https://godoc.org/golang.org/x/net/websocket
151-
152-
Unmaintained and the API does not reflect WebSocket semantics. Should never be used.
153-
154-
See https://github.com/golang/go/issues/18152
155-
156-
### gobwas/ws
157-
158-
https://github.com/gobwas/ws
159-
160-
This library has an extremely flexible API but that comes at the cost of usability
161-
and clarity.
162-
163-
This library is fantastic in terms of performance. The author put in significant
164-
effort to ensure its speed and I have applied as many of its optimizations as
165-
I could into nhooyr.io/websocket. Definitely check out his fantastic [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb)
166-
about performant WebSocket servers.
167-
168-
If you want a library that gives you absolute control over everything, this is the library.
169-
But for 99.9% of use cases, nhooyr.io/websocket will fit better. It's nearly as performant
170-
but much easier to use.
171-
172-
## Contributing
173-
174-
See [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md).
175-
176-
## Users
177-
178-
If your company or project is using this library, feel free to open an issue or PR to amend this list.
179-
180-
- [Coder](https://github.com/cdr)
181-
- [Tatsu Works](https://github.com/tatsuworks) - Ingresses 20 TB in websocket data every month on their Discord bot.
96+
- Minimal and idiomatic API
97+
- Compare godoc of [nhooyr.io/websocket](https://godoc.org/nhooyr.io/websocket) with [gorilla/websocket](https://godoc.org/github.com/gorilla/websocket) side by side.
98+
- [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper
99+
- Zero alloc reads and writes ([gorilla/websocket#535](https://github.com/gorilla/websocket/issues/535))
100+
- Full [context.Context](https://blog.golang.org/context) support
101+
- Dial uses [net/http.Client](https://golang.org/pkg/net/http/#Client)
102+
- Will enable easy HTTP/2 support in the future
103+
- Gorilla writes directly to a net.Conn and so duplicates features of net/http.Client.
104+
- Concurrent writes
105+
- Close handshake ([gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448))
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))
109+
- Transparent message buffer reuse with [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages
110+
- [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4) faster WebSocket masking implementation in pure Go
111+
- Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/).
112+
- Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support
113+
- 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)
116+
- [CloseRead](https://godoc.org/nhooyr.io/websocket#Conn.CloseRead) helper ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492))
117+
- Actively maintained ([gorilla/websocket#370](https://github.com/gorilla/websocket/issues/370))
118+
119+
#### golang.org/x/net/websocket
120+
121+
[golang.org/x/net/websocket](https://godoc.org/golang.org/x/net/websocket) is deprecated.
122+
See [golang/go/issues/18152](https://github.com/golang/go/issues/18152).
123+
124+
The [net.Conn](https://godoc.org/nhooyr.io/websocket#NetConn) wrapper will ease in transitioning
125+
to nhooyr.io/websocket.
126+
127+
#### gobwas/ws
128+
129+
[gobwas/ws](https://github.com/gobwas/ws) has an extremely flexible API that allows it to be used
130+
in an event driven style for performance. See the author's [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb).
131+
132+
However when writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use.

0 commit comments

Comments
 (0)