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
  • dev
  • docs
  • master
  • v0.1.0
  • v0.2.0
  • v1.0.0
  • v1.1.0
  • v1.1.1
  • v1.10.0
  • v1.2.0
  • v1.2.1
  • v1.3.0
  • v1.3.1
  • v1.3.2
  • v1.3.3
  • v1.4.0
  • v1.5.0
  • v1.5.1
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.6.3
  • v1.6.4
  • v1.6.5
  • v1.7.0
  • v1.7.1
  • v1.7.2
  • v1.7.3
  • v1.7.4
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.8.3
  • v1.8.4
  • v1.8.5
  • v1.8.6
  • v1.8.7
  • v1.9.0
  • v1.9.1
  • v1.9.2
40 results

Target

Select target project
  • github/nhooyr/websocket
  • open/websocket
2 results
Select Git revision
  • dependabot/go_modules/internal/examples/golang.org/x/time-0.6.0
  • dependabot/go_modules/internal/examples/golang.org/x/time-0.7.0
  • dependabot/go_modules/internal/examples/internal-deps-022ca1aea3
  • dependabot/go_modules/internal/examples/internal-deps-46eeb9c117
  • dependabot/go_modules/internal/examples/internal-deps-4cadc2be3d
  • dependabot/go_modules/internal/thirdparty/github.com/gin-gonic/gin-1.10.0
  • dependabot/go_modules/internal/thirdparty/github.com/gobwas/ws-1.4.0
  • dependabot/go_modules/internal/thirdparty/github.com/gorilla/websocket-1.5.3
  • dependabot/go_modules/internal/thirdparty/github.com/lesismal/nbio-1.5.10
  • dependabot/go_modules/internal/thirdparty/github.com/lesismal/nbio-1.5.11
  • dependabot/go_modules/internal/thirdparty/github.com/lesismal/nbio-1.5.12
  • dev
  • docs
  • ethan/close-order
  • fix-license-1
  • mafredri/build-add-makefile
  • mafredri/build-update-to-go1.22
  • mafredri/chore-remove-funding
  • mafredri/chore-update-dependabot
  • mafredri/fix-ci
  • mafredri/fix-ci-script-tool-version
  • mafredri/fix-coverage
  • mafredri/fix-coverage2
  • mafredri/fix-coverage3
  • mafredri/fix-coverage4
  • mafredri/fix-coverage5
  • mafredri/r-context-docs
  • mafredri/support-http-responsecontroller
  • master
  • matifali/enable-dependabot
  • matifali/optimize-CI
  • readme-update
  • v0.1.0
  • v0.2.0
  • v1.0.0
  • v1.1.0
  • v1.1.1
  • v1.2.0
  • v1.2.1
  • v1.3.0
  • v1.3.1
  • v1.3.2
  • v1.3.3
  • v1.4.0
  • v1.5.0
  • v1.5.1
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.6.3
  • v1.6.4
  • v1.6.5
  • v1.7.0
  • v1.7.1
  • v1.7.2
  • v1.7.3
  • v1.7.4
  • v1.8.0
  • v1.8.1
  • v1.8.10
  • v1.8.11
  • v1.8.12
  • v1.8.13
  • v1.8.2
  • v1.8.3
  • v1.8.4
  • v1.8.5
  • v1.8.6
  • v1.8.7
  • v1.8.8
  • v1.8.9
71 results
Show changes

Commits on Source 16

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"