diff --git a/README.md b/README.md index e967cd8af6f956b84e2cf07deddd6fd1e084b542..2d71ce0b23e9175202a1e0a989fd8ff1290960c2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # websocket -[](https://godoc.org/nhooyr.io/websocket) +[](https://pkg.go.dev/nhooyr.io/websocket) websocket is a minimal and idiomatic WebSocket library for Go. @@ -16,8 +16,8 @@ go get nhooyr.io/websocket - First class [context.Context](https://blog.golang.org/context) support - Fully passes the WebSocket [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite) - Thorough unit tests with [90% coverage](https://coveralls.io/github/nhooyr/websocket) -- [Minimal dependencies](https://godoc.org/nhooyr.io/websocket?imports) -- JSON and protobuf helpers in the [wsjson](https://godoc.org/nhooyr.io/websocket/wsjson) and [wspb](https://godoc.org/nhooyr.io/websocket/wspb) subpackages +- [Minimal dependencies](https://pkg.go.dev/nhooyr.io/websocket?tab=imports) +- JSON and protobuf helpers in the [wsjson](https://pkg.go.dev/nhooyr.io/websocket/wsjson?tab=doc) and [wspb](https://pkg.go.dev/nhooyr.io/websocket/wspb?tab=doc) subpackages - Zero alloc reads and writes - Concurrent writes - [Close handshake](https://godoc.org/nhooyr.io/websocket#Conn.Close) @@ -98,7 +98,7 @@ Advantages of nhooyr.io/websocket: - [net.Conn](https://godoc.org/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) +- Dials use [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 diff --git a/accept_js.go b/accept_js.go index 724b35b5bc3e4712a3a8d4299c587ba2d47e7fef..daad4b79fec613b311121eb6cc4f7beb014d9cd3 100644 --- a/accept_js.go +++ b/accept_js.go @@ -9,6 +9,7 @@ import ( type AcceptOptions struct { Subprotocols []string InsecureSkipVerify bool + OriginPatterns []string CompressionMode CompressionMode CompressionThreshold int } diff --git a/chat-example/chat_test.go b/chat-example/chat_test.go index 491499ccee6d8669f695da478736d05ca04fa2af..2cbc995eb54abe08ed815ae4c3f03f071eb4a2e7 100644 --- a/chat-example/chat_test.go +++ b/chat-example/chat_test.go @@ -61,7 +61,7 @@ func Test_chatServer(t *testing.T) { const nmessages = 128 const maxMessageSize = 128 - const nclients = 10 + const nclients = 16 url, closeFn := setupTest(t) defer closeFn() @@ -191,8 +191,7 @@ type client struct { } func newClient(ctx context.Context, url string) (*client, error) { - wsURL := strings.Replace(url, "http://", "ws://", 1) - c, _, err := websocket.Dial(ctx, wsURL+"/subscribe", nil) + c, _, err := websocket.Dial(ctx, url+"/subscribe", nil) if err != nil { return nil, err } diff --git a/close_notjs.go b/close_notjs.go index 2537299560bd2f7560a112d8c86a67b639aecf61..4f1cebcbb9ec6a096aa542ae014f828aff78f4b5 100644 --- a/close_notjs.go +++ b/close_notjs.go @@ -34,14 +34,15 @@ func (c *Conn) Close(code StatusCode, reason string) error { func (c *Conn) closeHandshake(code StatusCode, reason string) (err error) { defer errd.Wrap(&err, "failed to close WebSocket") - err = c.writeClose(code, reason) - if err != nil && CloseStatus(err) == -1 && err != errAlreadyWroteClose { - return err + writeErr := c.writeClose(code, reason) + closeHandshakeErr := c.waitCloseHandshake() + + if writeErr != nil { + return writeErr } - err = c.waitCloseHandshake() - if CloseStatus(err) == -1 { - return err + if CloseStatus(closeHandshakeErr) == -1 { + return closeHandshakeErr } return nil } @@ -50,10 +51,10 @@ var errAlreadyWroteClose = errors.New("already wrote close") func (c *Conn) writeClose(code StatusCode, reason string) error { c.closeMu.Lock() - closing := c.wroteClose + wroteClose := c.wroteClose c.wroteClose = true c.closeMu.Unlock() - if closing { + if wroteClose { return errAlreadyWroteClose } diff --git a/conn_test.go b/conn_test.go index af4fa4c0baa71c886c72aabd071595b30c62bc1e..7514540dfdb8bd66e84ee0eac33edbde68eaf627 100644 --- a/conn_test.go +++ b/conn_test.go @@ -298,7 +298,7 @@ func TestWasm(t *testing.T) { defer cancel() cmd := exec.CommandContext(ctx, "go", "test", "-exec=wasmbrowsertest", "./...") - cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", fmt.Sprintf("WS_ECHO_SERVER_URL=%v", wstest.URL(s))) + cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", fmt.Sprintf("WS_ECHO_SERVER_URL=%v", s.URL)) b, err := cmd.CombinedOutput() if err != nil { diff --git a/dial.go b/dial.go index 50a0ecce3f1aeedde43715337e2b67f85f50eb85..9ab680ebcf402431fb3a2de0e415ecd1a8d92ba4 100644 --- a/dial.go +++ b/dial.go @@ -58,6 +58,8 @@ type DialOptions struct { // This function requires at least Go 1.12 as it uses a new feature // in net/http to perform WebSocket handshakes. // See docs on the HTTPClient option and https://github.com/golang/go/issues/26937#issuecomment-415855861 +// +// URLs with http/https schemes will work and translated into ws/wss. func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) { return dial(ctx, u, opts, nil) } @@ -145,6 +147,7 @@ func handshakeRequest(ctx context.Context, urls string, opts *DialOptions, copts u.Scheme = "http" case "wss": u.Scheme = "https" + case "http", "https": default: return nil, fmt.Errorf("unexpected url scheme: %q", u.Scheme) } diff --git a/example_test.go b/example_test.go index 462de3761044d4741a77bc2bd62ed45896a6e2b9..39de0b8026888c556bc5f8e5e6af106478daf612 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,3 @@ -// +build !js - package websocket_test import ( @@ -187,3 +185,8 @@ func ExampleGrace() { s.Shutdown(ctx) g.Shutdown(ctx) } + +// This example demonstrates full stack chat with an automated test. +func Example_fullStackChat() { + // https://github.com/nhooyr/websocket/tree/master/chat-example +} diff --git a/internal/test/wstest/url.go b/internal/test/wstest/url.go deleted file mode 100644 index a11c61b46d88a2b5633d1621fbee8b93ef9fe732..0000000000000000000000000000000000000000 --- a/internal/test/wstest/url.go +++ /dev/null @@ -1,11 +0,0 @@ -package wstest - -import ( - "net/http/httptest" - "strings" -) - -// URL returns the ws url for s. -func URL(s *httptest.Server) string { - return strings.Replace(s.URL, "http", "ws", 1) -} diff --git a/ws_js.go b/ws_js.go index a8c8b77187d956b0eb27376fb1926a7973124f20..69019e61600635fd829e52a0193df2540d2b5a95 100644 --- a/ws_js.go +++ b/ws_js.go @@ -9,6 +9,7 @@ import ( "net/http" "reflect" "runtime" + "strings" "sync" "syscall/js" @@ -257,6 +258,9 @@ func dial(ctx context.Context, url string, opts *DialOptions) (*Conn, *http.Resp opts = &DialOptions{} } + url = strings.Replace(url, "http://", "ws://", 1) + url = strings.Replace(url, "https://", "wss://", 1) + ws, err := wsjs.New(url, opts.Subprotocols) if err != nil { return nil, nil, err