good morning!!!!

Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • github/nhooyr/websocket
  • open/websocket
2 results
Select Git revision
Loading items
Show changes

Commits on Source 16

  • a's avatar
    remove pb and gin · ddf6627d
    a authored
    ddf6627d
  • a's avatar
    simplify deps · 37bcdcad
    a authored
    37bcdcad
  • a's avatar
    change name · 21b8b462
    a authored
    21b8b462
  • a's avatar
    change name 2 · 2f469c2f
    a authored
    2f469c2f
  • a's avatar
    reduce deps · 3c5e0cef
    a authored
    3c5e0cef
  • a's avatar
    ok · ea4aa1f7
    a authored
    ea4aa1f7
  • a's avatar
    ok · 7d5cd8e5
    a authored
    7d5cd8e5
  • a's avatar
    remove ioutil · 38cf41b1
    a authored
    38cf41b1
  • a's avatar
    autobahn · 2b5be501
    a authored
    2b5be501
  • a's avatar
    ab · 3b5743ca
    a authored
    3b5743ca
  • a's avatar
    ok · 0f329b63
    a authored
    0f329b63
  • a's avatar
    save me god · 79c91080
    a authored
    79c91080
  • a's avatar
    ok · 07547c42
    a authored
    07547c42
  • a's avatar
    a · 22e221f2
    a authored
    22e221f2
  • a's avatar
    a · 8e42981e
    a authored
    8e42981e
  • a's avatar
    a · 912d3de7
    a authored
    912d3de7
github: nhooyr
websocket.test
out
_autobahn
reports
/ci/out
.go-cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
before_script:
- mkdir -p .go
cache:
paths:
- .go/pkg/mod/
stages:
- test
test:
services:
- name: crossbario/autobahn-testsuite
command: wstest -m fuzzingserver -s $CI_PROJECT_DIR/fuzzingserver.json
alias: wstest
image: golang:1.21-alpine
stage: test
extends: .go-cache
script:
- AUTOBAHN_TEST=yes WSTEST_URL=ws://wstest:9001 go test -race -run=Autobahn
test:
image: golang:1.21-alpine
stage: test
extends: .go-cache
script:
- go test -race ./...
lint:
image: registry.gitlab.com/gitlab-org/gitlab-build-images:golangci-lint-alpine
stage: test
extends: .go-cache
script:
# Use default .golangci.yml file from the image if one is not present in the project root.
- "[ -e .golangci.yml ] || cp /golangci/.golangci.yml ."
# Write the code coverage report to gl-code-quality-report.json
# and print linting issues to stdout in the format: path/to/file:line description
# remove `--issues-exit-code 0` or set to non-zero to fail the job if linting issues are detected
- golangci-lint run --issues-exit-code 0 --print-issued-lines=false --out-format code-climate:gl-code-quality-report.json,line-number
artifacts:
when: always
reports:
codequality: gl-code-quality-report.json
paths:
- gl-code-quality-report.json
coverage:
stage: test
image: golang:1.21-alpine
coverage: '/\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/'
extends: .go-cache
script:
- go run gotest.tools/gotestsum@latest --junitfile report.xml --format testname -- -coverprofile=coverage.txt -covermode count ./...
- go tool cover -func=coverage.txt
- go run github.com/boumenot/gocover-cobertura@master < coverage.txt > coverage.xml
artifacts:
reports:
junit: report.xml
coverage_report:
coverage_format: cobertura
path: coverage.xml
SHELL := /bin/bash
autobahn:
docker run -it --rm \
-v "${PWD}/fuzzingserver.json:/config/fuzzingserver.json" \
-v "${PWD}/reports:/reports" \
-p 9001:9001 \
--name fuzzingserver \
crossbario/autobahn-testsuite
# websocket # websocket
[![Go Reference](https://pkg.go.dev/badge/nhooyr.io/websocket.svg)](https://pkg.go.dev/nhooyr.io/websocket) this is a fork of github.com/hnooyr/websocket
[![Go Coverage](https://img.shields.io/badge/coverage-91%25-success)](https://nhooyr.io/websocket/coverage.html)
websocket is a minimal and idiomatic WebSocket library for Go.
## Install
```sh
go get nhooyr.io/websocket
```
## Highlights
- Minimal and idiomatic API
- First class [context.Context](https://blog.golang.org/context) support
- Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite)
- [Zero dependencies](https://pkg.go.dev/nhooyr.io/websocket?tab=imports)
- JSON helpers in the [wsjson](https://pkg.go.dev/nhooyr.io/websocket/wsjson) subpackage
- Zero alloc reads and writes
- Concurrent writes
- [Close handshake](https://pkg.go.dev/nhooyr.io/websocket#Conn.Close)
- [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) wrapper
- [Ping pong](https://pkg.go.dev/nhooyr.io/websocket#Conn.Ping) API
- [RFC 7692](https://tools.ietf.org/html/rfc7692) permessage-deflate compression
- [CloseRead](https://pkg.go.dev/nhooyr.io/websocket#Conn.CloseRead) helper for write only connections
- Compile to [Wasm](https://pkg.go.dev/nhooyr.io/websocket#hdr-Wasm)
## Roadmap
See GitHub issues for minor issues but the major future enhancements are:
- [ ] Perfect examples [#217](https://github.com/nhooyr/websocket/issues/217)
- [ ] wstest.Pipe for in memory testing [#340](https://github.com/nhooyr/websocket/issues/340)
- [ ] Ping pong heartbeat helper [#267](https://github.com/nhooyr/websocket/issues/267)
- [ ] Ping pong instrumentation callbacks [#246](https://github.com/nhooyr/websocket/issues/246)
- [ ] Graceful shutdown helpers [#209](https://github.com/nhooyr/websocket/issues/209)
- [ ] Assembly for WebSocket masking [#16](https://github.com/nhooyr/websocket/issues/16)
- WIP at [#326](https://github.com/nhooyr/websocket/pull/326), about 3x faster
- [ ] HTTP/2 [#4](https://github.com/nhooyr/websocket/issues/4)
- [ ] The holy grail [#402](https://github.com/nhooyr/websocket/issues/402)
## Examples
For a production quality example that demonstrates the complete API, see the
[echo example](./internal/examples/echo).
For a full stack example, see the [chat example](./internal/examples/chat).
### Server
```go
http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
c, err := websocket.Accept(w, r, nil)
if err != nil {
// ...
}
defer c.CloseNow()
ctx, cancel := context.WithTimeout(r.Context(), time.Second*10)
defer cancel()
var v interface{}
err = wsjson.Read(ctx, c, &v)
if err != nil {
// ...
}
log.Printf("received: %v", v)
c.Close(websocket.StatusNormalClosure, "")
})
```
### Client
```go
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
c, _, err := websocket.Dial(ctx, "ws://localhost:8080", nil)
if err != nil {
// ...
}
defer c.CloseNow()
err = wsjson.Write(ctx, c, "hi")
if err != nil {
// ...
}
c.Close(websocket.StatusNormalClosure, "")
```
## Comparison
### gorilla/websocket
Advantages of [gorilla/websocket](https://github.com/gorilla/websocket):
- Mature and widely used
- [Prepared writes](https://pkg.go.dev/github.com/gorilla/websocket#PreparedMessage)
- Configurable [buffer sizes](https://pkg.go.dev/github.com/gorilla/websocket#hdr-Buffers)
- No extra goroutine per connection to support cancellation with context.Context. This costs nhooyr.io/websocket 2 KB of memory per connection.
- Will be removed soon with [context.AfterFunc](https://github.com/golang/go/issues/57928). See [#411](https://github.com/nhooyr/websocket/issues/411)
Advantages of nhooyr.io/websocket:
- Minimal and idiomatic API
- Compare godoc of [nhooyr.io/websocket](https://pkg.go.dev/nhooyr.io/websocket) with [gorilla/websocket](https://pkg.go.dev/github.com/gorilla/websocket) side by side.
- [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) wrapper
- Zero alloc reads and writes ([gorilla/websocket#535](https://github.com/gorilla/websocket/issues/535))
- Full [context.Context](https://blog.golang.org/context) support
- Dial uses [net/http.Client](https://golang.org/pkg/net/http/#Client)
- Will enable easy HTTP/2 support in the future
- Gorilla writes directly to a net.Conn and so duplicates features of net/http.Client.
- Concurrent writes
- Close handshake ([gorilla/websocket#448](https://github.com/gorilla/websocket/issues/448))
- Idiomatic [ping pong](https://pkg.go.dev/nhooyr.io/websocket#Conn.Ping) API
- Gorilla requires registering a pong callback before sending a Ping
- Can target Wasm ([gorilla/websocket#432](https://github.com/gorilla/websocket/issues/432))
- Transparent message buffer reuse with [wsjson](https://pkg.go.dev/nhooyr.io/websocket/wsjson) subpackage
- [1.75x](https://github.com/nhooyr/websocket/releases/tag/v1.7.4) faster WebSocket masking implementation in pure Go
- Gorilla's implementation is slower and uses [unsafe](https://golang.org/pkg/unsafe/).
Soon we'll have assembly and be 3x faster [#326](https://github.com/nhooyr/websocket/pull/326)
- Full [permessage-deflate](https://tools.ietf.org/html/rfc7692) compression extension support
- Gorilla only supports no context takeover mode
- [CloseRead](https://pkg.go.dev/nhooyr.io/websocket#Conn.CloseRead) helper for write only connections ([gorilla/websocket#492](https://github.com/gorilla/websocket/issues/492))
#### golang.org/x/net/websocket
[golang.org/x/net/websocket](https://pkg.go.dev/golang.org/x/net/websocket) is deprecated.
See [golang/go/issues/18152](https://github.com/golang/go/issues/18152).
The [net.Conn](https://pkg.go.dev/nhooyr.io/websocket#NetConn) can help in transitioning
to nhooyr.io/websocket.
#### gobwas/ws
[gobwas/ws](https://github.com/gobwas/ws) has an extremely flexible API that allows it to be used
in an event driven style for performance. See the author's [blog post](https://medium.freecodecamp.org/million-websockets-and-go-cc58418460bb).
However it is quite bloated. See https://pkg.go.dev/github.com/gobwas/ws
When writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use.
#### lesismal/nbio
[lesismal/nbio](https://github.com/lesismal/nbio) is similar to gobwas/ws in that the API is
event driven for performance reasons.
However it is quite bloated. See https://pkg.go.dev/github.com/lesismal/nbio
When writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use.
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"nhooyr.io/websocket/internal/errd" "gfx.cafe/open/websocket/internal/errd"
) )
// AcceptOptions represents Accept's options. // AcceptOptions represents Accept's options.
...@@ -105,13 +105,6 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con ...@@ -105,13 +105,6 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
} }
} }
hj, ok := w.(http.Hijacker)
if !ok {
err = errors.New("http.ResponseWriter does not implement http.Hijacker")
http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
return nil, err
}
w.Header().Set("Upgrade", "websocket") w.Header().Set("Upgrade", "websocket")
w.Header().Set("Connection", "Upgrade") w.Header().Set("Connection", "Upgrade")
...@@ -136,7 +129,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con ...@@ -136,7 +129,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
ginWriter.WriteHeaderNow() ginWriter.WriteHeaderNow()
} }
netConn, brw, err := hj.Hijack() netConn, brw, err := http.NewResponseController(w).Hijack()
if err != nil { if err != nil {
err = fmt.Errorf("failed to hijack connection: %w", err) err = fmt.Errorf("failed to hijack connection: %w", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
......
...@@ -12,8 +12,8 @@ import ( ...@@ -12,8 +12,8 @@ import (
"strings" "strings"
"testing" "testing"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
"nhooyr.io/websocket/internal/test/xrand" "gfx.cafe/open/websocket/internal/test/xrand"
) )
func TestAccept(t *testing.T) { func TestAccept(t *testing.T) {
...@@ -120,7 +120,7 @@ func TestAccept(t *testing.T) { ...@@ -120,7 +120,7 @@ func TestAccept(t *testing.T) {
r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16)) r.Header.Set("Sec-WebSocket-Key", xrand.Base64(16))
_, err := Accept(w, r, nil) _, err := Accept(w, r, nil)
assert.Contains(t, err, `http.ResponseWriter does not implement http.Hijacker`) assert.Contains(t, err, `failed to hijack connection`)
}) })
t.Run("badHijack", func(t *testing.T) { t.Run("badHijack", func(t *testing.T) {
......
...@@ -17,11 +17,11 @@ import ( ...@@ -17,11 +17,11 @@ import (
"testing" "testing"
"time" "time"
"nhooyr.io/websocket" "gfx.cafe/open/websocket"
"nhooyr.io/websocket/internal/errd" "gfx.cafe/open/websocket/internal/errd"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
"nhooyr.io/websocket/internal/test/wstest" "gfx.cafe/open/websocket/internal/test/wstest"
"nhooyr.io/websocket/internal/util" "gfx.cafe/open/websocket/internal/util"
) )
var excludedAutobahnCases = []string{ var excludedAutobahnCases = []string{
......
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
go test --run=^$ --bench=. --benchmem --memprofile ci/out/prof.mem --cpuprofile ci/out/prof.cpu -o ci/out/websocket.test "$@" .
(
cd ./internal/thirdparty
go test --run=^$ --bench=. --benchmem --memprofile ../../ci/out/prof-thirdparty.mem --cpuprofile ../../ci/out/prof-thirdparty.cpu -o ../../ci/out/thirdparty.test "$@" .
)
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
go mod tidy
(cd ./internal/thirdparty && go mod tidy)
(cd ./internal/examples && go mod tidy)
gofmt -w -s .
go run golang.org/x/tools/cmd/goimports@latest -w "-local=$(go list -m)" .
npx prettier@3.0.3 \
--write \
--log-level=warn \
--print-width=90 \
--no-semi \
--single-quote \
--arrow-parens=avoid \
$(git ls-files "*.yml" "*.md" "*.js" "*.css" "*.html")
go run golang.org/x/tools/cmd/stringer@latest -type=opcode,MessageType,StatusCode -output=stringer.go
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
go vet ./...
GOOS=js GOARCH=wasm go vet ./...
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
GOOS=js GOARCH=wasm staticcheck ./...
govulncheck() {
tmpf=$(mktemp)
if ! command govulncheck "$@" >"$tmpf" 2>&1; then
cat "$tmpf"
fi
}
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
GOOS=js GOARCH=wasm govulncheck ./...
(
cd ./internal/examples
go vet ./...
staticcheck ./...
govulncheck ./...
)
(
cd ./internal/thirdparty
go vet ./...
staticcheck ./...
govulncheck ./...
)
*
#!/bin/sh
set -eu
cd -- "$(dirname "$0")/.."
(
cd ./internal/examples
go test "$@" ./...
)
(
cd ./internal/thirdparty
go test "$@" ./...
)
go install github.com/agnivade/wasmbrowsertest@latest
go test --race --bench=. --timeout=1h --covermode=atomic --coverprofile=ci/out/coverage.prof --coverpkg=./... "$@" ./...
sed -i.bak '/stringer\.go/d' ci/out/coverage.prof
sed -i.bak '/nhooyr.io\/websocket\/internal\/test/d' ci/out/coverage.prof
sed -i.bak '/examples/d' ci/out/coverage.prof
# Last line is the total coverage.
go tool cover -func ci/out/coverage.prof | tail -n1
go tool cover -html=ci/out/coverage.prof -o=ci/out/coverage.html
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"net" "net"
"time" "time"
"nhooyr.io/websocket/internal/errd" "gfx.cafe/open/websocket/internal/errd"
) )
// StatusCode represents a WebSocket status code. // StatusCode represents a WebSocket status code.
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"strings" "strings"
"testing" "testing"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
) )
func TestCloseError(t *testing.T) { func TestCloseError(t *testing.T) {
......
...@@ -10,8 +10,8 @@ import ( ...@@ -10,8 +10,8 @@ import (
"strings" "strings"
"testing" "testing"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
"nhooyr.io/websocket/internal/test/xrand" "gfx.cafe/open/websocket/internal/test/xrand"
) )
func Test_slidingWindow(t *testing.T) { func Test_slidingWindow(t *testing.T) {
......
//go:build !js //go:build !js
// +build !js
package websocket_test package websocket_test
...@@ -16,13 +17,13 @@ import ( ...@@ -16,13 +17,13 @@ import (
"testing" "testing"
"time" "time"
"nhooyr.io/websocket" "gfx.cafe/open/websocket"
"nhooyr.io/websocket/internal/errd" "gfx.cafe/open/websocket/internal/errd"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
"nhooyr.io/websocket/internal/test/wstest" "gfx.cafe/open/websocket/internal/test/wstest"
"nhooyr.io/websocket/internal/test/xrand" "gfx.cafe/open/websocket/internal/test/xrand"
"nhooyr.io/websocket/internal/xsync" "gfx.cafe/open/websocket/internal/xsync"
"nhooyr.io/websocket/wsjson" "gfx.cafe/open/websocket/wsjson"
) )
func TestConn(t *testing.T) { func TestConn(t *testing.T) {
......
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
"sync" "sync"
"time" "time"
"nhooyr.io/websocket/internal/errd" "gfx.cafe/open/websocket/internal/errd"
) )
// DialOptions represents Dial's options. // DialOptions represents Dial's options.
......
...@@ -15,10 +15,10 @@ import ( ...@@ -15,10 +15,10 @@ import (
"testing" "testing"
"time" "time"
"nhooyr.io/websocket" "gfx.cafe/open/websocket"
"nhooyr.io/websocket/internal/test/assert" "gfx.cafe/open/websocket/internal/test/assert"
"nhooyr.io/websocket/internal/util" "gfx.cafe/open/websocket/internal/util"
"nhooyr.io/websocket/internal/xsync" "gfx.cafe/open/websocket/internal/xsync"
) )
func TestBadDials(t *testing.T) { func TestBadDials(t *testing.T) {
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
// //
// The wsjson subpackage contain helpers for JSON and protobuf messages. // The wsjson subpackage contain helpers for JSON and protobuf messages.
// //
// More documentation at https://nhooyr.io/websocket. // More documentation at https://gfx.cafe/open/websocket.
// //
// # Wasm // # Wasm
// //
...@@ -25,10 +25,9 @@ ...@@ -25,10 +25,9 @@
// See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket // See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
// //
// Some important caveats to be aware of: // Some important caveats to be aware of:
//
// - Accept always errors out // - Accept always errors out
// - Conn.Ping is no-op // - Conn.Ping is no-op
// - Conn.CloseNow is Close(StatusGoingAway, "") // - Conn.CloseNow is Close(StatusGoingAway, "")
// - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op // - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op
// - *http.Response from Dial is &http.Response{} with a 101 status code on success // - *http.Response from Dial is &http.Response{} with a 101 status code on success
package websocket // import "nhooyr.io/websocket" package websocket // import "gfx.cafe/open/websocket"