Skip to content

Commit

Permalink
Merge pull request #135 from nhooyr/less-deps
Browse files Browse the repository at this point in the history
Remove extra dependencies
  • Loading branch information
nhooyr authored Sep 4, 2019
2 parents 793cb86 + 1ea51d3 commit 21cd45d
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 144 deletions.
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -19,7 +19,7 @@ jobs:

lint:
docker:
- image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
- image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435
steps:
- checkout
- restore_cache:
Expand All @@ -36,7 +36,7 @@ jobs:

test:
docker:
- image: nhooyr/websocket-ci@sha256:371ca985ce2548840aeb0f8434a551708cdfe0628be722c361958e65cdded945
- image: nhooyr/websocket-ci@sha256:77e37211ded3c528e947439e294fbfc03b4fb9f9537c4e5198d5b304fd1df435
steps:
- checkout
- restore_cache:
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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.
49 changes: 34 additions & 15 deletions accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
23 changes: 11 additions & 12 deletions dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"strings"
"sync"

"golang.org/x/xerrors"
)

// DialOptions represents the options available to pass to Dial.
Expand Down Expand Up @@ -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
}
Expand All @@ -62,15 +61,15 @@ 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{}
}

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 {
Expand All @@ -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)
Expand All @@ -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 {
Expand All @@ -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{
Expand All @@ -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"),
)
Expand Down
7 changes: 3 additions & 4 deletions example_echo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"time"

"golang.org/x/time/rate"
"golang.org/x/xerrors"

"nhooyr.io/websocket"
"nhooyr.io/websocket/wsjson"
Expand Down Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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()
Expand Down
5 changes: 2 additions & 3 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
}
Expand Down
5 changes: 2 additions & 3 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package websocket
import (
"bufio"
"context"

"golang.org/x/xerrors"
"fmt"
)

type (
Expand Down Expand Up @@ -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()
Expand Down
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module nhooyr.io/websocket

go 1.12
go 1.13

require (
github.com/fatih/color v1.7.0 // indirect
Expand All @@ -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
Expand Down
Loading

0 comments on commit 21cd45d

Please sign in to comment.