diff --git a/.circleci/config.yml b/.circleci/config.yml index 196ec6714366370a6bb462d114cb702f2f6770bd..ec061964339261324f163cd9c2b28a5622b033a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: fmt: docker: - - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945 + - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435 steps: - checkout - restore_cache: @@ -19,7 +19,7 @@ jobs: lint: docker: - - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945 + - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435 steps: - checkout - restore_cache: @@ -36,7 +36,7 @@ jobs: test: docker: - - image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945 + - image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435 steps: - checkout - restore_cache: diff --git a/README.md b/README.md index 4ee0a30e80b0bdaa86450785e114d74d5ffff9c3..4091736d230dfc1e362c1c98425482fc5d5194cd 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,17 @@ websocket is a minimal and idiomatic WebSocket library for Go. ## Install ```bash -go get nhooyr.io/websocket@v1.5.0 +go get nhooyr.io/websocket@v1.5.1 ``` ## Features - Minimal and idiomatic API - Tiny codebase at 1700 lines -- First class context.Context support +- First class [context.Context](https://blog.golang.org/context) support - Thorough tests, fully passes the [autobahn-testsuite](https://github.com/crossbario/autobahn-testsuite) -- Zero dependencies outside of the stdlib for the core library -- JSON and ProtoBuf helpers in the wsjson and wspb subpackages +- [Zero 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 - Highly optimized by default - Concurrent writes out of the box @@ -32,7 +32,7 @@ go get nhooyr.io/websocket@v1.5.0 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). -Please use the [golang.org/x/xerrors.As](https://godoc.org/golang.org/x/xerrors#As) package to check for [websocket.CloseError](https://godoc.org/nhooyr.io/websocket#CloseError). See the [CloseError godoc example](https://godoc.org/nhooyr.io/websocket#example-CloseError). +Please 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). See the [CloseError godoc example](https://godoc.org/nhooyr.io/websocket#example-CloseError). ### Server @@ -172,4 +172,4 @@ This is a list of companies or projects that use this library. - [Coder](https://github.com/cdr) -If your company or project is using this library, please feel free to open a PR to amend the list. +If your company or project is using this library, please feel free to open an issue or PR to amend the list. diff --git a/accept.go b/accept.go index afad1be2ec4771e6405688af706b9019fee9701e..7ef72ad2045502b9ffc0e6cc75ecddffeaf00809 100644 --- a/accept.go +++ b/accept.go @@ -4,14 +4,13 @@ import ( "bytes" "crypto/sha1" "encoding/base64" + "errors" + "fmt" "io" "net/http" "net/textproto" "net/url" "strings" - - "golang.org/x/net/http/httpguts" - "golang.org/x/xerrors" ) // AcceptOptions represents the options available to pass to Accept. @@ -43,31 +42,31 @@ type AcceptOptions struct { func verifyClientRequest(w http.ResponseWriter, r *http.Request) error { if !headerValuesContainsToken(r.Header, "Connection", "Upgrade") { - err := xerrors.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection")) + err := fmt.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", r.Header.Get("Connection")) http.Error(w, err.Error(), http.StatusBadRequest) return err } if !headerValuesContainsToken(r.Header, "Upgrade", "WebSocket") { - err := xerrors.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade")) + err := fmt.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", r.Header.Get("Upgrade")) http.Error(w, err.Error(), http.StatusBadRequest) return err } if r.Method != "GET" { - err := xerrors.Errorf("websocket protocol violation: handshake request method is not GET but %q", r.Method) + err := fmt.Errorf("websocket protocol violation: handshake request method is not GET but %q", r.Method) http.Error(w, err.Error(), http.StatusBadRequest) return err } if r.Header.Get("Sec-WebSocket-Version") != "13" { - err := xerrors.Errorf("unsupported websocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version")) + err := fmt.Errorf("unsupported websocket protocol version (only 13 is supported): %q", r.Header.Get("Sec-WebSocket-Version")) http.Error(w, err.Error(), http.StatusBadRequest) return err } if r.Header.Get("Sec-WebSocket-Key") == "" { - err := xerrors.New("websocket protocol violation: missing Sec-WebSocket-Key") + err := errors.New("websocket protocol violation: missing Sec-WebSocket-Key") http.Error(w, err.Error(), http.StatusBadRequest) return err } @@ -87,7 +86,7 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) error { func Accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, error) { c, err := accept(w, r, opts) if err != nil { - return nil, xerrors.Errorf("failed to accept websocket connection: %w", err) + return nil, fmt.Errorf("failed to accept websocket connection: %w", err) } return c, nil } @@ -112,7 +111,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, hj, ok := w.(http.Hijacker) if !ok { - err = xerrors.New("passed ResponseWriter does not implement http.Hijacker") + err = errors.New("passed ResponseWriter does not implement http.Hijacker") http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented) return nil, err } @@ -131,7 +130,7 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, netConn, brw, err := hj.Hijack() if err != nil { - err = xerrors.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) return nil, err } @@ -151,9 +150,29 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (*Conn, return c, nil } -func headerValuesContainsToken(h http.Header, key, val string) bool { +func headerValuesContainsToken(h http.Header, key, token string) bool { key = textproto.CanonicalMIMEHeaderKey(key) - return httpguts.HeaderValuesContainsToken(h[key], val) + + for _, val2 := range h[key] { + if headerValueContainsToken(val2, token) { + return true + } + } + + return false +} + +func headerValueContainsToken(val2, token string) bool { + val2 = strings.TrimSpace(val2) + + for _, val2 := range strings.Split(val2, ",") { + val2 = strings.TrimSpace(val2) + if strings.EqualFold(val2, token) { + return true + } + } + + return false } func selectSubprotocol(r *http.Request, subprotocols []string) string { @@ -187,10 +206,10 @@ func authenticateOrigin(r *http.Request) error { } u, err := url.Parse(origin) if err != nil { - return xerrors.Errorf("failed to parse Origin header %q: %w", origin, err) + return fmt.Errorf("failed to parse Origin header %q: %w", origin, err) } if strings.EqualFold(u.Host, r.Host) { return nil } - return xerrors.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host) + return fmt.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host) } diff --git a/dial.go b/dial.go index 461817f60999744598ed243398b12a44b009a3ca..2ed836bd4422156ae0fb20ec626c36335338cb75 100644 --- a/dial.go +++ b/dial.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "encoding/base64" + "fmt" "io" "io/ioutil" "math/rand" @@ -12,8 +13,6 @@ import ( "net/url" "strings" "sync" - - "golang.org/x/xerrors" ) // DialOptions represents the options available to pass to Dial. @@ -44,7 +43,7 @@ type DialOptions struct { func Dial(ctx context.Context, u string, opts *DialOptions) (*Conn, *http.Response, error) { c, r, err := dial(ctx, u, opts) if err != nil { - return nil, r, xerrors.Errorf("failed to websocket dial: %w", err) + return nil, r, fmt.Errorf("failed to websocket dial: %w", err) } return c, r, nil } @@ -62,7 +61,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re opts.HTTPClient = http.DefaultClient } if opts.HTTPClient.Timeout > 0 { - return nil, nil, xerrors.Errorf("please use context for cancellation instead of http.Client.Timeout; see https://github.com/nhooyr/websocket/issues/67") + return nil, nil, fmt.Errorf("please use context for cancellation instead of http.Client.Timeout; see https://github.com/nhooyr/websocket/issues/67") } if opts.HTTPHeader == nil { opts.HTTPHeader = http.Header{} @@ -70,7 +69,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re parsedURL, err := url.Parse(u) if err != nil { - return nil, nil, xerrors.Errorf("failed to parse url: %w", err) + return nil, nil, fmt.Errorf("failed to parse url: %w", err) } switch parsedURL.Scheme { @@ -79,7 +78,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re case "wss": parsedURL.Scheme = "https" default: - return nil, nil, xerrors.Errorf("unexpected url scheme: %q", parsedURL.Scheme) + return nil, nil, fmt.Errorf("unexpected url scheme: %q", parsedURL.Scheme) } req, _ := http.NewRequest("GET", parsedURL.String(), nil) @@ -95,7 +94,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re resp, err := opts.HTTPClient.Do(req) if err != nil { - return nil, nil, xerrors.Errorf("failed to send handshake request: %w", err) + return nil, nil, fmt.Errorf("failed to send handshake request: %w", err) } defer func() { if err != nil { @@ -114,7 +113,7 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re rwc, ok := resp.Body.(io.ReadWriteCloser) if !ok { - return nil, resp, xerrors.Errorf("response body is not a io.ReadWriteCloser: %T", rwc) + return nil, resp, fmt.Errorf("response body is not a io.ReadWriteCloser: %T", rwc) } c := &Conn{ @@ -132,19 +131,19 @@ func dial(ctx context.Context, u string, opts *DialOptions) (_ *Conn, _ *http.Re func verifyServerResponse(r *http.Request, resp *http.Response) error { if resp.StatusCode != http.StatusSwitchingProtocols { - return xerrors.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode) + return fmt.Errorf("expected handshake response status code %v but got %v", http.StatusSwitchingProtocols, resp.StatusCode) } if !headerValuesContainsToken(resp.Header, "Connection", "Upgrade") { - return xerrors.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection")) + return fmt.Errorf("websocket protocol violation: Connection header %q does not contain Upgrade", resp.Header.Get("Connection")) } if !headerValuesContainsToken(resp.Header, "Upgrade", "WebSocket") { - return xerrors.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade")) + return fmt.Errorf("websocket protocol violation: Upgrade header %q does not contain websocket", resp.Header.Get("Upgrade")) } if resp.Header.Get("Sec-WebSocket-Accept") != secWebSocketAccept(r.Header.Get("Sec-WebSocket-Key")) { - return xerrors.Errorf("websocket protocol violation: invalid Sec-WebSocket-Accept %q, key %q", + return fmt.Errorf("websocket protocol violation: invalid Sec-WebSocket-Accept %q, key %q", resp.Header.Get("Sec-WebSocket-Accept"), r.Header.Get("Sec-WebSocket-Key"), ) diff --git a/example_echo_test.go b/example_echo_test.go index 3e7e7f9da7a711238ad4f216e2cde9d5087cfce7..aad326756e03a362d4001b41e266a31e6723c447 100644 --- a/example_echo_test.go +++ b/example_echo_test.go @@ -10,7 +10,6 @@ import ( "time" "golang.org/x/time/rate" - "golang.org/x/xerrors" "nhooyr.io/websocket" "nhooyr.io/websocket/wsjson" @@ -78,14 +77,14 @@ func echoServer(w http.ResponseWriter, r *http.Request) error { if c.Subprotocol() != "echo" { c.Close(websocket.StatusPolicyViolation, "client must speak the echo subprotocol") - return xerrors.Errorf("client does not speak echo sub protocol") + return fmt.Errorf("client does not speak echo sub protocol") } l := rate.NewLimiter(rate.Every(time.Millisecond*100), 10) for { err = echo(r.Context(), c, l) if err != nil { - return xerrors.Errorf("failed to echo with %v: %w", r.RemoteAddr, err) + return fmt.Errorf("failed to echo with %v: %w", r.RemoteAddr, err) } } } @@ -114,7 +113,7 @@ func echo(ctx context.Context, c *websocket.Conn, l *rate.Limiter) error { _, err = io.Copy(w, r) if err != nil { - return xerrors.Errorf("failed to io.Copy: %w", err) + return fmt.Errorf("failed to io.Copy: %w", err) } err = w.Close() diff --git a/example_test.go b/example_test.go index 797658b47059e4e6ba152eebbfbe4972886fa253..36cab2bd6b3698c9bd97f1781e6eb37347437899 100644 --- a/example_test.go +++ b/example_test.go @@ -2,12 +2,11 @@ package websocket_test import ( "context" + "errors" "log" "net/http" "time" - "golang.org/x/xerrors" - "nhooyr.io/websocket" "nhooyr.io/websocket/wsjson" ) @@ -76,7 +75,7 @@ func ExampleCloseError() { _, _, err = c.Reader(ctx) var cerr websocket.CloseError - if !xerrors.As(err, &cerr) || cerr.Code != websocket.StatusNormalClosure { + if !errors.As(err, &cerr) || cerr.Code != websocket.StatusNormalClosure { log.Fatalf("expected to be disconnected with StatusNormalClosure but got: %+v", err) return } diff --git a/export_test.go b/export_test.go index 811bf800b94011740d6a9f491f9ff8f9bfd8be8b..5a0d1c32482aee5d69f9c6362c46ce3a1bcb1340 100644 --- a/export_test.go +++ b/export_test.go @@ -3,8 +3,7 @@ package websocket import ( "bufio" "context" - - "golang.org/x/xerrors" + "fmt" ) type ( @@ -65,7 +64,7 @@ func (c *Conn) WriteHeader(ctx context.Context, h Header) error { }) _, err := c.bw.Write(headerBytes) if err != nil { - return xerrors.Errorf("failed to write header: %w", err) + return fmt.Errorf("failed to write header: %w", err) } if h.Fin { err = c.Flush() diff --git a/go.mod b/go.mod index 70fe1d4c111e94136ef01f194866cf2e86228d77..b59397c1ece37f41d008c5ac3b777e22f130fa7e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module nhooyr.io/websocket -go 1.12 +go 1.13 require ( github.com/fatih/color v1.7.0 // indirect @@ -18,12 +18,9 @@ require ( go.uber.org/atomic v1.4.0 // indirect go.uber.org/multierr v1.1.0 golang.org/x/lint v0.0.0-20190409202823-959b441ac422 - golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0 // indirect - golang.org/x/text v0.3.2 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/tools v0.0.0-20190830223141-573d9926052a - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gotest.tools v2.2.0+incompatible // indirect gotest.tools/gotestsum v0.3.6-0.20190825182939-fc6cb5870c52 diff --git a/go.sum b/go.sum index 906f9c38cb955e94ef5e56445b685b9959199d7f..18b3dd70751a82d53d34685dde090197e2663d98 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,6 @@ golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -93,11 +91,8 @@ golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0 h1:7z820YPX9pxWR59qM7BE5+fgl golang.org/x/sys v0.0.0-20190830142957-1e83adbbebd0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190830223141-573d9926052a h1:XAHT1kdPpnU8Hk+FPi42KZFhtNFEk4vBg1U4OmIeHTU= golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/header.go b/header.go index 16ab6474e69c46b10bea83df98828c35482da032..6eb8610f5a65c067a581afab3214dd3ee79a57e4 100644 --- a/header.go +++ b/header.go @@ -5,8 +5,6 @@ import ( "fmt" "io" "math" - - "golang.org/x/xerrors" ) // First byte contains fin, rsv1, rsv2, rsv3. @@ -145,7 +143,7 @@ func readHeader(b []byte, r io.Reader) (header, error) { case payloadLength == 127: h.payloadLength = int64(binary.BigEndian.Uint64(b)) if h.payloadLength < 0 { - return header{}, xerrors.Errorf("header with negative payload length: %v", h.payloadLength) + return header{}, fmt.Errorf("header with negative payload length: %v", h.payloadLength) } b = b[8:] } diff --git a/netconn.go b/netconn.go index a7c9bf7fcf6a444deac547fa84ab89e3ee28ca82..20b99c2a69d531245263235fe36db69f26659a0a 100644 --- a/netconn.go +++ b/netconn.go @@ -2,13 +2,12 @@ package websocket import ( "context" + "errors" "fmt" "io" "math" "net" "time" - - "golang.org/x/xerrors" ) // NetConn converts a *websocket.Conn into a net.Conn. @@ -97,7 +96,7 @@ func (c *netConn) Read(p []byte) (int, error) { typ, r, err := c.c.Reader(c.readContext) if err != nil { var ce CloseError - if xerrors.As(err, &ce) && (ce.Code == StatusNormalClosure) || (ce.Code == StatusGoingAway) { + if errors.As(err, &ce) && (ce.Code == StatusNormalClosure) || (ce.Code == StatusGoingAway) { c.eofed = true return 0, io.EOF } diff --git a/statuscode.go b/statuscode.go index 558610d4a9bf9d571d6efd5a34315c649b1b97ff..d2a64d62e8be495b779e9241dadc0995abde98c3 100644 --- a/statuscode.go +++ b/statuscode.go @@ -3,8 +3,6 @@ package websocket import ( "encoding/binary" "fmt" - - "golang.org/x/xerrors" ) // StatusCode represents a WebSocket status code. @@ -43,7 +41,8 @@ const ( // CloseError represents a WebSocket close frame. // It is returned by Conn's methods when a WebSocket close frame is received from // the peer. -// You will need to use https://godoc.org/golang.org/x/xerrors#As to check for this error. +// You will need to use the https://golang.org/pkg/errors/#As function, new in Go 1.13, +// to check for this error. See the CloseError example. type CloseError struct { Code StatusCode Reason string @@ -61,7 +60,7 @@ func parseClosePayload(p []byte) (CloseError, error) { } if len(p) < 2 { - return CloseError{}, xerrors.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p) + return CloseError{}, fmt.Errorf("close payload %q too small, cannot even contain the 2 byte status code", p) } ce := CloseError{ @@ -70,7 +69,7 @@ func parseClosePayload(p []byte) (CloseError, error) { } if !validWireCloseCode(ce.Code) { - return CloseError{}, xerrors.Errorf("invalid status code %v", ce.Code) + return CloseError{}, fmt.Errorf("invalid status code %v", ce.Code) } return ce, nil @@ -98,10 +97,10 @@ const maxControlFramePayload = 125 func (ce CloseError) bytes() ([]byte, error) { if len(ce.Reason) > maxControlFramePayload-2 { - return nil, xerrors.Errorf("reason string max is %v but got %q with length %v", maxControlFramePayload-2, ce.Reason, len(ce.Reason)) + return nil, fmt.Errorf("reason string max is %v but got %q with length %v", maxControlFramePayload-2, ce.Reason, len(ce.Reason)) } if !validWireCloseCode(ce.Code) { - return nil, xerrors.Errorf("status code %v cannot be set", ce.Code) + return nil, fmt.Errorf("status code %v cannot be set", ce.Code) } buf := make([]byte, 2+len(ce.Reason)) diff --git a/websocket.go b/websocket.go index 7dabfa25f686df3de346a2dcddb86dc527bdd315..56aca7051712d0fb330a7a33a432ab4fcc2ab00b 100644 --- a/websocket.go +++ b/websocket.go @@ -4,6 +4,7 @@ import ( "bufio" "context" cryptorand "crypto/rand" + "errors" "fmt" "io" "io/ioutil" @@ -14,8 +15,6 @@ import ( "sync" "sync/atomic" "time" - - "golang.org/x/xerrors" ) // Conn represents a WebSocket connection. @@ -107,7 +106,7 @@ func (c *Conn) init() { c.controlPayloadBuf = make([]byte, maxControlFramePayload) runtime.SetFinalizer(c, func(c *Conn) { - c.close(xerrors.New("connection garbage collected")) + c.close(errors.New("connection garbage collected")) }) go c.timeoutLoop() @@ -121,7 +120,7 @@ func (c *Conn) Subprotocol() string { func (c *Conn) setCloseErr(err error) { c.closeErrOnce.Do(func() { - c.closeErr = xerrors.Errorf("websocket closed: %w", err) + c.closeErr = fmt.Errorf("websocket closed: %w", err) }) } @@ -166,9 +165,9 @@ func (c *Conn) timeoutLoop() { case readCtx = <-c.setReadTimeout: case <-readCtx.Done(): - c.close(xerrors.Errorf("read timed out: %w", readCtx.Err())) + c.close(fmt.Errorf("read timed out: %w", readCtx.Err())) case <-writeCtx.Done(): - c.close(xerrors.Errorf("write timed out: %w", writeCtx.Err())) + c.close(fmt.Errorf("write timed out: %w", writeCtx.Err())) } } } @@ -179,9 +178,9 @@ func (c *Conn) acquireLock(ctx context.Context, lock chan struct{}) error { var err error switch lock { case c.writeFrameLock, c.writeMsgLock: - err = xerrors.Errorf("could not acquire write lock: %v", ctx.Err()) + err = fmt.Errorf("could not acquire write lock: %v", ctx.Err()) case c.readFrameLock: - err = xerrors.Errorf("could not acquire read lock: %v", ctx.Err()) + err = fmt.Errorf("could not acquire read lock: %v", ctx.Err()) default: panic(fmt.Sprintf("websocket: failed to acquire unknown lock: %v", ctx.Err())) } @@ -217,7 +216,7 @@ func (c *Conn) readTillMsg(ctx context.Context) (header, error) { if h.opcode.controlOp() { err = c.handleControl(ctx, h) if err != nil { - return header{}, xerrors.Errorf("failed to handle control frame: %w", err) + return header{}, fmt.Errorf("failed to handle control frame: %w", err) } continue } @@ -254,7 +253,7 @@ func (c *Conn) readFrameHeader(ctx context.Context) (header, error) { err = ctx.Err() default: } - err := xerrors.Errorf("failed to read header: %w", err) + err := fmt.Errorf("failed to read header: %w", err) c.releaseLock(c.readFrameLock) c.close(err) return header{}, err @@ -307,14 +306,14 @@ func (c *Conn) handleControl(ctx context.Context, h header) error { case opClose: ce, err := parseClosePayload(b) if err != nil { - err = xerrors.Errorf("received invalid close payload: %w", err) + err = fmt.Errorf("received invalid close payload: %w", err) c.Close(StatusProtocolError, err.Error()) return c.closeErr } // This ensures the closeErr of the Conn is always the received CloseError // in case the echo close frame write fails. // See https://github.com/nhooyr/websocket/issues/109 - c.setCloseErr(xerrors.Errorf("received close frame: %w", ce)) + c.setCloseErr(fmt.Errorf("received close frame: %w", ce)) c.writeClose(b, nil) return c.closeErr default: @@ -347,12 +346,12 @@ func (c *Conn) handleControl(ctx context.Context, h header) error { // Most users should not need this. func (c *Conn) Reader(ctx context.Context) (MessageType, io.Reader, error) { if atomic.LoadInt64(&c.readClosed) == 1 { - return 0, nil, xerrors.Errorf("websocket connection read closed") + return 0, nil, fmt.Errorf("websocket connection read closed") } typ, r, err := c.reader(ctx) if err != nil { - return 0, nil, xerrors.Errorf("failed to get reader: %w", err) + return 0, nil, fmt.Errorf("failed to get reader: %w", err) } return typ, r, nil } @@ -363,7 +362,7 @@ func (c *Conn) reader(ctx context.Context) (MessageType, io.Reader, error) { // if there is an active frame not yet fully read. // Otherwise, a user may have read the last byte but not the EOF if the EOF // is in the next frame so we check for that below. - return 0, nil, xerrors.Errorf("previous message not read to completion") + return 0, nil, fmt.Errorf("previous message not read to completion") } h, err := c.readTillMsg(ctx) @@ -378,7 +377,7 @@ func (c *Conn) reader(ctx context.Context) (MessageType, io.Reader, error) { } if !h.fin || h.payloadLength > 0 { - return 0, nil, xerrors.Errorf("previous message not read to completion") + return 0, nil, fmt.Errorf("previous message not read to completion") } c.activeReader = nil @@ -441,17 +440,17 @@ func (r *messageReader) Read(p []byte) (int, error) { if err != nil { // Have to return io.EOF directly for now, we cannot wrap as xerrors // isn't used in stdlib. - if xerrors.Is(err, io.EOF) { + if errors.Is(err, io.EOF) { return n, io.EOF } - return n, xerrors.Errorf("failed to read: %w", err) + return n, fmt.Errorf("failed to read: %w", err) } return n, nil } func (r *messageReader) read(p []byte) (int, error) { if r.eof() { - return 0, xerrors.Errorf("cannot use EOFed reader") + return 0, fmt.Errorf("cannot use EOFed reader") } if r.c.readMsgLeft <= 0 { @@ -531,7 +530,7 @@ func (c *Conn) readFramePayload(ctx context.Context, p []byte) (int, error) { err = ctx.Err() default: } - err = xerrors.Errorf("failed to read frame payload: %w", err) + err = fmt.Errorf("failed to read frame payload: %w", err) c.releaseLock(c.readFrameLock) c.close(err) return n, err @@ -580,7 +579,7 @@ func (c *Conn) Read(ctx context.Context) (MessageType, []byte, error) { func (c *Conn) Writer(ctx context.Context, typ MessageType) (io.WriteCloser, error) { wc, err := c.writer(ctx, typ) if err != nil { - return nil, xerrors.Errorf("failed to get writer: %w", err) + return nil, fmt.Errorf("failed to get writer: %w", err) } return wc, nil } @@ -605,7 +604,7 @@ func (c *Conn) writer(ctx context.Context, typ MessageType) (io.WriteCloser, err func (c *Conn) Write(ctx context.Context, typ MessageType, p []byte) error { _, err := c.write(ctx, typ, p) if err != nil { - return xerrors.Errorf("failed to write msg: %w", err) + return fmt.Errorf("failed to write msg: %w", err) } return nil } @@ -634,18 +633,18 @@ func (w *messageWriter) closed() bool { func (w *messageWriter) Write(p []byte) (int, error) { n, err := w.write(p) if err != nil { - return n, xerrors.Errorf("failed to write: %w", err) + return n, fmt.Errorf("failed to write: %w", err) } return n, nil } func (w *messageWriter) write(p []byte) (int, error) { if w.closed() { - return 0, xerrors.Errorf("cannot use closed writer") + return 0, fmt.Errorf("cannot use closed writer") } n, err := w.c.writeFrame(w.c.writeMsgCtx, false, w.c.writeMsgOpcode, p) if err != nil { - return n, xerrors.Errorf("failed to write data frame: %w", err) + return n, fmt.Errorf("failed to write data frame: %w", err) } w.c.writeMsgOpcode = opContinuation return n, nil @@ -656,20 +655,20 @@ func (w *messageWriter) write(p []byte) (int, error) { func (w *messageWriter) Close() error { err := w.close() if err != nil { - return xerrors.Errorf("failed to close writer: %w", err) + return fmt.Errorf("failed to close writer: %w", err) } return nil } func (w *messageWriter) close() error { if w.closed() { - return xerrors.Errorf("cannot use closed writer") + return fmt.Errorf("cannot use closed writer") } w.c.activeWriter = nil _, err := w.c.writeFrame(w.c.writeMsgCtx, true, w.c.writeMsgOpcode, nil) if err != nil { - return xerrors.Errorf("failed to write fin frame: %w", err) + return fmt.Errorf("failed to write fin frame: %w", err) } w.c.releaseLock(w.c.writeMsgLock) @@ -679,7 +678,7 @@ func (w *messageWriter) close() error { func (c *Conn) writeControl(ctx context.Context, opcode opcode, p []byte) error { _, err := c.writeFrame(ctx, true, opcode, p) if err != nil { - return xerrors.Errorf("failed to write control frame: %w", err) + return fmt.Errorf("failed to write control frame: %w", err) } return nil } @@ -706,7 +705,7 @@ func (c *Conn) writeFrame(ctx context.Context, fin bool, opcode opcode, p []byte if c.client { _, err := io.ReadFull(cryptorand.Reader, c.writeHeader.maskKey[:]) if err != nil { - return 0, xerrors.Errorf("failed to generate masking key: %w", err) + return 0, fmt.Errorf("failed to generate masking key: %w", err) } } @@ -737,7 +736,7 @@ func (c *Conn) realWriteFrame(ctx context.Context, h header, p []byte) (n int, e default: } - err = xerrors.Errorf("failed to write %v frame: %w", h.opcode, err) + err = fmt.Errorf("failed to write %v frame: %w", h.opcode, err) // We need to release the lock first before closing the connection to ensure // the lock can be acquired inside close to ensure no one can access c.bw. c.releaseLock(c.writeFrameLock) @@ -821,7 +820,7 @@ func (c *Conn) writePong(p []byte) error { func (c *Conn) Close(code StatusCode, reason string) error { err := c.exportedClose(code, reason) if err != nil { - return xerrors.Errorf("failed to close websocket connection: %w", err) + return fmt.Errorf("failed to close websocket connection: %w", err) } return nil } @@ -846,13 +845,13 @@ func (c *Conn) exportedClose(code StatusCode, reason string) error { // CloseErrors sent are made opaque to prevent applications from thinking // they received a given status. - sentErr := xerrors.Errorf("sent close frame: %v", ce) + sentErr := fmt.Errorf("sent close frame: %v", ce) err = c.writeClose(p, sentErr) if err != nil { return err } - if !xerrors.Is(c.closeErr, sentErr) { + if !errors.Is(c.closeErr, sentErr) { return c.closeErr } @@ -891,7 +890,7 @@ func (c *Conn) Ping(ctx context.Context) error { err := c.ping(ctx, p) if err != nil { - return xerrors.Errorf("failed to ping: %w", err) + return fmt.Errorf("failed to ping: %w", err) } return nil } @@ -918,7 +917,7 @@ func (c *Conn) ping(ctx context.Context, p string) error { case <-c.closed: return c.closeErr case <-ctx.Done(): - err := xerrors.Errorf("failed to wait for pong: %w", ctx.Err()) + err := fmt.Errorf("failed to wait for pong: %w", ctx.Err()) c.close(err) return err case <-pong: diff --git a/websocket_test.go b/websocket_test.go index ef6ae926878385da26d707386aa81e7188b8b778..1aa8b201a5a6a9d2f3dabb23207533cafdd9a1d7 100644 --- a/websocket_test.go +++ b/websocket_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -24,7 +25,6 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/timestamp" "go.uber.org/multierr" - "golang.org/x/xerrors" "nhooyr.io/websocket" "nhooyr.io/websocket/wsjson" @@ -45,7 +45,7 @@ func TestHandshake(t *testing.T) { c, err := websocket.Accept(w, r, nil) if err == nil { c.Close(websocket.StatusInternalError, "") - return xerrors.New("expected error regarding bad origin") + return errors.New("expected error regarding bad origin") } return assertErrorContains(err, "not authorized") }, @@ -57,7 +57,7 @@ func TestHandshake(t *testing.T) { }) if err == nil { c.Close(websocket.StatusInternalError, "") - return xerrors.New("expected handshake failure") + return errors.New("expected handshake failure") } return assertErrorContains(err, "403") }, @@ -115,7 +115,7 @@ func TestHandshake(t *testing.T) { server: func(w http.ResponseWriter, r *http.Request) error { cookie, err := r.Cookie("mycookie") if err != nil { - return xerrors.Errorf("request is missing mycookie: %w", err) + return fmt.Errorf("request is missing mycookie: %w", err) } err = assertEqualf("myvalue", cookie.Value, "unexpected cookie value") if err != nil { @@ -131,11 +131,11 @@ func TestHandshake(t *testing.T) { client: func(ctx context.Context, u string) error { jar, err := cookiejar.New(nil) if err != nil { - return xerrors.Errorf("failed to create cookie jar: %w", err) + return fmt.Errorf("failed to create cookie jar: %w", err) } parsedURL, err := url.Parse(u) if err != nil { - return xerrors.Errorf("failed to parse url: %w", err) + return fmt.Errorf("failed to parse url: %w", err) } parsedURL.Scheme = "http" jar.SetCookies(parsedURL, []*http.Cookie{ @@ -410,7 +410,7 @@ func TestConn(t *testing.T) { case err = <-pingErrc: return err case <-ctx2.Done(): - return xerrors.Errorf("failed to wait for pong: %w", ctx2.Err()) + return fmt.Errorf("failed to wait for pong: %w", ctx2.Err()) } }, }, @@ -576,15 +576,15 @@ func TestConn(t *testing.T) { } _, _, err = c.Read(ctx) cerr := &websocket.CloseError{} - if !xerrors.As(err, cerr) || cerr.Code != websocket.StatusProtocolError { - return xerrors.Errorf("expected close error with StatusProtocolError: %+v", err) + if !errors.As(err, cerr) || cerr.Code != websocket.StatusProtocolError { + return fmt.Errorf("expected close error with StatusProtocolError: %+v", err) } return nil }, client: func(ctx context.Context, c *websocket.Conn) error { _, _, err := c.Read(ctx) if err == nil || !strings.Contains(err.Error(), "rsv") { - return xerrors.Errorf("expected error that contains rsv: %+v", err) + return fmt.Errorf("expected error that contains rsv: %+v", err) } return nil }, @@ -684,19 +684,19 @@ func TestConn(t *testing.T) { } _, err = w.Write([]byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } err = c.Flush() if err != nil { - return xerrors.Errorf("failed to flush: %w", err) + return fmt.Errorf("failed to flush: %w", err) } _, err = w.Write([]byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } err = c.Flush() if err != nil { - return xerrors.Errorf("failed to flush: %w", err) + return fmt.Errorf("failed to flush: %w", err) } _, _, err = c.Read(ctx) return assertCloseStatus(err, websocket.StatusInternalError) @@ -724,15 +724,15 @@ func TestConn(t *testing.T) { } _, err = w.Write([]byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } err = c.Flush() if err != nil { - return xerrors.Errorf("failed to flush: %w", err) + return fmt.Errorf("failed to flush: %w", err) } _, err = c.WriteFrame(ctx, true, websocket.OpBinary, []byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } _, _, err = c.Read(ctx) return assertErrorContains(err, "received new data message without finishing") @@ -798,15 +798,15 @@ func TestConn(t *testing.T) { } _, err = w.Write([]byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } err = c.Flush() if err != nil { - return xerrors.Errorf("failed to flush: %w", err) + return fmt.Errorf("failed to flush: %w", err) } _, err = c.WriteFrame(ctx, true, websocket.OpBinary, []byte(strings.Repeat("x", 10))) if err != nil { - return xerrors.Errorf("expected non nil error") + return fmt.Errorf("expected non nil error") } _, _, err = c.Read(ctx) return assertCloseStatus(err, websocket.StatusProtocolError) @@ -1890,8 +1890,8 @@ func echoLoop(ctx context.Context, c *websocket.Conn) { func assertCloseStatus(err error, code websocket.StatusCode) error { var cerr websocket.CloseError - if !xerrors.As(err, &cerr) { - return xerrors.Errorf("no websocket close error in error chain: %+v", err) + if !errors.As(err, &cerr) { + return fmt.Errorf("no websocket close error in error chain: %+v", err) } return assertEqualf(code, cerr.Code, "unexpected status code") } @@ -1949,7 +1949,7 @@ func assertSubprotocol(c *websocket.Conn, exp string) error { func assertEqualf(exp, act interface{}, f string, v ...interface{}) error { if diff := cmpDiff(exp, act); diff != "" { - return xerrors.Errorf(f+": %v", append(v, diff)...) + return fmt.Errorf(f+": %v", append(v, diff)...) } return nil } @@ -1965,14 +1965,14 @@ func assertNetConnRead(r io.Reader, exp string) error { func assertErrorContains(err error, exp string) error { if err == nil || !strings.Contains(err.Error(), exp) { - return xerrors.Errorf("expected error that contains %q but got: %+v", exp, err) + return fmt.Errorf("expected error that contains %q but got: %+v", exp, err) } return nil } func assertErrorIs(exp, act error) error { - if !xerrors.Is(act, exp) { - return xerrors.Errorf("expected error %+v to be in %+v", exp, act) + if !errors.Is(act, exp) { + return fmt.Errorf("expected error %+v to be in %+v", exp, act) } return nil } @@ -2000,7 +2000,7 @@ func assertReadCloseFrame(ctx context.Context, c *websocket.Conn, code websocket } ce, err := websocket.ParseClosePayload(actP) if err != nil { - return xerrors.Errorf("failed to parse close frame payload: %w", err) + return fmt.Errorf("failed to parse close frame payload: %w", err) } return assertEqualf(ce.Code, code, "unexpected frame close frame code with payload %q", actP) } diff --git a/wsjson/wsjson.go b/wsjson/wsjson.go index fda429acb7a27b002e3b757bb6cfd439b6554bab..1e63f940ea7948c7267b36495e20c37e4e0f8588 100644 --- a/wsjson/wsjson.go +++ b/wsjson/wsjson.go @@ -4,8 +4,7 @@ package wsjson // import "nhooyr.io/websocket/wsjson" import ( "context" "encoding/json" - - "golang.org/x/xerrors" + "fmt" "nhooyr.io/websocket" "nhooyr.io/websocket/internal/bpool" @@ -16,7 +15,7 @@ import ( func Read(ctx context.Context, c *websocket.Conn, v interface{}) error { err := read(ctx, c, v) if err != nil { - return xerrors.Errorf("failed to read json: %w", err) + return fmt.Errorf("failed to read json: %w", err) } return nil } @@ -29,7 +28,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error { if typ != websocket.MessageText { c.Close(websocket.StatusUnsupportedData, "can only accept text messages") - return xerrors.Errorf("unexpected frame type for json (expected %v): %v", websocket.MessageText, typ) + return fmt.Errorf("unexpected frame type for json (expected %v): %v", websocket.MessageText, typ) } b := bpool.Get() @@ -45,7 +44,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error { err = json.Unmarshal(b.Bytes(), v) if err != nil { c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal JSON") - return xerrors.Errorf("failed to unmarshal json: %w", err) + return fmt.Errorf("failed to unmarshal json: %w", err) } return nil @@ -56,7 +55,7 @@ func read(ctx context.Context, c *websocket.Conn, v interface{}) error { func Write(ctx context.Context, c *websocket.Conn, v interface{}) error { err := write(ctx, c, v) if err != nil { - return xerrors.Errorf("failed to write json: %w", err) + return fmt.Errorf("failed to write json: %w", err) } return nil } @@ -72,7 +71,7 @@ func write(ctx context.Context, c *websocket.Conn, v interface{}) error { e := json.NewEncoder(w) err = e.Encode(v) if err != nil { - return xerrors.Errorf("failed to encode json: %w", err) + return fmt.Errorf("failed to encode json: %w", err) } err = w.Close() diff --git a/wspb/wspb.go b/wspb/wspb.go index dfc407cc3f3b17a2399f20e977c14646edfbcb37..8613a08093bdc2a68195f5cea55a1b84284a6a61 100644 --- a/wspb/wspb.go +++ b/wspb/wspb.go @@ -4,10 +4,10 @@ package wspb // import "nhooyr.io/websocket/wspb" import ( "bytes" "context" + "fmt" "sync" "github.com/golang/protobuf/proto" - "golang.org/x/xerrors" "nhooyr.io/websocket" "nhooyr.io/websocket/internal/bpool" @@ -18,7 +18,7 @@ import ( func Read(ctx context.Context, c *websocket.Conn, v proto.Message) error { err := read(ctx, c, v) if err != nil { - return xerrors.Errorf("failed to read protobuf: %w", err) + return fmt.Errorf("failed to read protobuf: %w", err) } return nil } @@ -31,7 +31,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error { if typ != websocket.MessageBinary { c.Close(websocket.StatusUnsupportedData, "can only accept binary messages") - return xerrors.Errorf("unexpected frame type for protobuf (expected %v): %v", websocket.MessageBinary, typ) + return fmt.Errorf("unexpected frame type for protobuf (expected %v): %v", websocket.MessageBinary, typ) } b := bpool.Get() @@ -47,7 +47,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error { err = proto.Unmarshal(b.Bytes(), v) if err != nil { c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal protobuf") - return xerrors.Errorf("failed to unmarshal protobuf: %w", err) + return fmt.Errorf("failed to unmarshal protobuf: %w", err) } return nil @@ -58,7 +58,7 @@ func read(ctx context.Context, c *websocket.Conn, v proto.Message) error { func Write(ctx context.Context, c *websocket.Conn, v proto.Message) error { err := write(ctx, c, v) if err != nil { - return xerrors.Errorf("failed to write protobuf: %w", err) + return fmt.Errorf("failed to write protobuf: %w", err) } return nil } @@ -74,7 +74,7 @@ func write(ctx context.Context, c *websocket.Conn, v proto.Message) error { err := pb.Marshal(v) if err != nil { - return xerrors.Errorf("failed to marshal protobuf: %w", err) + return fmt.Errorf("failed to marshal protobuf: %w", err) } return c.Write(ctx, websocket.MessageBinary, pb.Bytes())