Giter Club home page Giter Club logo

websocket's Introduction

websocket

Go Reference Go Coverage

websocket is a minimal and idiomatic WebSocket library for Go.

Install

go get nhooyr.io/websocket

Highlights

Roadmap

See GitHub issues for minor issues but the major future enhancements are:

  • Perfect examples #217
  • wstest.Pipe for in memory testing #340
  • Ping pong heartbeat helper #267
  • Ping pong instrumentation callbacks #246
  • Graceful shutdown helpers #209
  • Assembly for WebSocket masking #16
    • WIP at #326, about 3x faster
  • HTTP/2 #4
  • The holy grail #402

Examples

For a production quality example that demonstrates the complete API, see the echo example.

For a full stack example, see the chat example.

Server

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

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:

  • Mature and widely used
  • Prepared writes
  • Configurable buffer sizes
  • No extra goroutine per connection to support cancellation with context.Context. This costs nhooyr.io/websocket 2 KB of memory per connection.

Advantages of nhooyr.io/websocket:

golang.org/x/net/websocket

golang.org/x/net/websocket is deprecated. See golang/go/issues/18152.

The net.Conn can help in transitioning to nhooyr.io/websocket.

gobwas/ws

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.

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 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.

websocket's People

Contributors

abursavich avatar alixander avatar arthmis avatar bendiscz avatar coadler avatar cristaloleg avatar emersion avatar emyrk avatar guseggert avatar jacalz avatar keystroke3 avatar kunalsin9h avatar nhooyr avatar photostorm avatar swithek avatar typeless avatar univerio avatar vaelatern avatar wdvxdr1123 avatar x0wllaar avatar zamiell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

websocket's Issues

WASM

golang/go#27311

Can't really support the current API, browser API is different and much less expressive.

More pure go tests

Using wstest adds like 50s to the tests right now.

Its not a good experience and the code using it is very janky.

I'd rather write pure Go tests now that the library is fairly stable.

Always Compress text frames by default

If compression is negotiated, I think we should compress all text frames by default. Of course if the frame is small, we will not compress it.

So the compress function on the writer would not be exposed.

Slice conn with faster write and faster read

Should we add a code path for fast reads and writes without allocating a reader or writer?

like:

type frame struct {
	opcode opcode
	p      []byte
}

func (c *Conn) Write2(ctx context.Context, dataType DataType, msg []byte) error {
	err := c.write2(ctx, dataType, msg)
	if err != nil {
		return xerrors.Errorf("failed to write message: %w", err)
	}
	return nil
}

func (c *Conn) write2(ctx context.Context, dataType DataType, msg []byte) error {
	select {
	case <-c.closed:
		return c.closeErr
	case <-ctx.Done():
		return ctx.Err()
	case c.write <- frame{
		opcode: opcode(dataType),
		p:      msg,
	}:
		select {
		case <-c.closed:
			return c.closeErr
		case <-ctx.Done():
			return ctx.Err()
		case <-c.writeDone:
			return nil
		}
	}
}

func (c *Conn) Read2(ctx context.Context, msg []byte) (DataType, int, error) {
	dataType, n, err := c.read2(ctx, msg)
	if err != nil {
		return 0, 0, xerrors.Errorf("failed to read message: %w", err)
	}
	return dataType, n, nil

}

func (c *Conn) read2(ctx context.Context, msg []byte) (DataType, int, error) {
	select {
	case <-c.closed:
		return 0, 0, c.closeErr
	case <-ctx.Done():
		return 0, 0, xerrors.Errorf("failed to read: %w", ctx.Err())
	case c.read <- msg:
		select {
		case <-c.closed:
			return 0, 0, c.closeErr
		case <-ctx.Done():
			return 0, 0, xerrors.Errorf("failed to read: %w", ctx.Err())
		case r := <-c.readDone:
			return r.dataType, r.n, nil
		}
	}
}

Would actually be a wrapper around the Conn like JSONConn though.

Performance

Need to make sure the API/lib is as simple as possible while not sacrificing any reasonable performance.

  • Consider struct padding
  • Pooling of buffers
  • Give gobwas/ws a through read

Allow single frame writes

See #57

Some websocket implementations cannot handle fragmented messages and the current API only allows writing fragmented messages because you write all your data first to a writer and then call close which writes a continuation fin frame.

It may also be good for performance.

I'm not going to expose an API for this right now, opening this issue to see how badly its wanted.

Super fast mask/unmask

Don't want to have to copy this code from gobwas/ws or gorilla/websocket as then there are licensing issues and I do not want to write and maintain my own version.

I've reported golang/go#31586

Strong CI

Get gometalinter running on CI, ensure we're caching stuff and everything is fast.

Maybe want to use Circle CI instead, their docs for this sort of thing were much better and they allow running gometalinter concurrently.

Confirm AcceptOrigins API

I think it's a little awkward and inflexible. Maybe its better to have devs verify the origin themselves if its not equal to r.Host and then pass an option like websocket.AcceptInsecureOrigin()

Read after write fails (Chrome DevTools Protocol server)

Using this simple program:

package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"log"
	"time"

	"nhooyr.io/websocket"
)

var flagRemote = flag.String("r", "", "remote url")

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatalf("error: %v", err)
	}
}

func run() error {
	if *flagRemote == "" {
		return errors.New("must provide remote url via -r")
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	c, _, err := websocket.Dial(ctx, *flagRemote)
	if err != nil {
		return err
	}
	defer c.Close(websocket.StatusInternalError, "")

	// write initial message
	w, err := c.Write(ctx, websocket.MessageText)
	if err != nil {
		return err
	}
	if _, err = w.Write([]byte(`{"id":1,"method":"Target.getTargets","params":{}}`)); err != nil {
		return err
	}
	if err = w.Close(); err != nil {
		return err
	}
	log.Printf("wrote Target.getTargets message")

	// read response
	jc := websocket.JSONConn{
		Conn: c,
	}
	var v interface{}
	err = jc.Read(ctx, &v)
	if err != nil {
		log.Fatalf("failed to read json: %v", err)
	}
	buf, err := json.Marshal(v)
	if err != nil {
		return err
	}
	log.Printf("received %s", string(buf))

	return c.Close(websocket.StatusNormalClosure, "")
}

And using it with Google Chrome's DevTools Protocol, on sending a simple message via this command:

Starting Chrome:

ken@ken-desktop:~/src/go/src/github.com/kenshaw/wstest$ google-chrome --headless --remote-debugging-port=0 --disable-gpu

DevTools listening on ws://127.0.0.1:32831/devtools/browser/4f7e048b-1c84-4f05-a7fe-39fd624fb07e

And then running the simple Go test program above:

ken@ken-desktop:~/src/go/src/github.com/kenshaw/wstest$ go build && ./wstest -r 'ws://127.0.0.1:32831/devtools/browser/4f7e048b-1c84-4f05-a7fe-39fd624fb07e'
2019/04/20 20:10:49 wrote Target.getTargets message
2019/04/20 20:10:49 failed to read json: failed to read json: websocket: failed to read message: websocket: connection broken: failed to read header: EOF
ken@ken-desktop:~/src/go/src/github.com/kenshaw/wstest$

Expected output: the response from Chrome, looking something like the following:

{"id":1,"result":{"targetInfos":[{"targetId":"D18C239D31BAB5964945BD536DD9C987","type":"page","title":"","url":"about:blank","attached":false,"browserContextId":"FECF894940A790B796F6A86E836A8242"}]}}

Note: both the gobwas/ws and gorilla/websocket packages work with Chrome's DevTools Protocol.

I've attempted to look through the nhooyr.io/websocket package to determine what the cause is, but have not been able to track down what the issue is. My guess is that it's a non-standard header or some such that Chrome is returning.

Perhaps I'm not using the package properly, if so, I apologize in advance, however I believe I correctly followed the examples provided in the package. Appreciate any pointers to doing this properly -- I'm also willing to help debug this issue. Thanks in advance!

Ammar review issues

  • document why r.Limit
  • client side autobahn tests
  • fix go:generate comments in docs

Cleanup websocket.go

In interest of making tests pass, I didn't write a lot of the code in websocket.go as nice as I would have wanted.

Will need to fix this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.