Minimal and idiomatic WebSocket library for Go

Overview

websocket

godoc coverage

websocket is a minimal and idiomatic WebSocket library for Go.

Install

go get nhooyr.io/websocket

Highlights

Roadmap

  • HTTP/2 #4

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.Close(websocket.StatusInternalError, "the sky is falling")

	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.Close(websocket.StatusInternalError, "the sky is falling")

err = wsjson.Write(ctx, c, "hi")
if err != nil {
	// ...
}

c.Close(websocket.StatusNormalClosure, "")

Comparison

gorilla/websocket

Advantages of gorilla/websocket:

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 when writing idiomatic Go, nhooyr.io/websocket will be faster and easier to use.

Comments
  • Gin compatabilty

    Gin compatabilty

    I'm trying to integrate this into my existing app which is using the Gin framework, but I'm unable to establish a connection from the browser. Any ideas how I can get this working?

    Server

    package main
    
    import (
    	"context"
    	"log"
    	"net/http"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	"nhooyr.io/websocket"
    	"nhooyr.io/websocket/wsjson"
    )
    
    func main() {
    	router := gin.New()
    
    	router.GET("/ws", handler)
    
    	router.Run(":7000")
    }
    
    func handler(c *gin.Context) {
    	log.Println("upgrading")
    	conn, wsErr := websocket.Accept(c.Writer, c.Request, &websocket.AcceptOptions{
    		InsecureSkipVerify: true,
    	})
    
    	if wsErr != nil {
    		c.AbortWithError(http.StatusInternalServerError, wsErr)
    		return
    	}
    
    	defer conn.Close(websocket.StatusInternalError, "Closed unexepetedly")
    
    	log.Println("ready")
    
    	ctx, cancel := context.WithTimeout(c.Request.Context(), time.Minute)
    	defer cancel()
    	ctx = conn.CloseRead(ctx)
    
    	tick := time.NewTicker(time.Second * 5)
    	defer tick.Stop()
    
    	for {
    		select {
    		case <-ctx.Done():
    			log.Println("done")
    			conn.Close(websocket.StatusNormalClosure, "")
    			return
    		case <-tick.C:
    			writeErr := wsjson.Write(ctx, conn, map[string]interface{}{
    				"msg": "hello",
    			})
    			if writeErr != nil {
    				log.Println(writeErr)
    				return
    			}
    		}
    	}
    }
    

    Client

    const ws = new WebSocket('ws://localhost:7000/ws');
    ws.onopen = console.log;
    ws.onmessage = console.log;
    

    JS Error WebSocket connection to 'ws://localhost:7000/ws' failed: Error during WebSocket handshake: net::ERR_INVALID_HTTP_RESPONSE

    Server logs

    [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
     - using env:   export GIN_MODE=release
     - using code:  gin.SetMode(gin.ReleaseMode)
    
    [GIN-debug] GET    /ws                       --> main.handler (1 handlers)
    [GIN-debug] Listening and serving HTTP on :7000
    2019/10/25 15:17:49 upgrading
    2019/10/25 15:17:49 ready
    2019/10/25 15:17:54 done
    
    enhancement 
    opened by nmec 21
  • Weird behaviour when channelling go-capnproto2 over websocket

    Weird behaviour when channelling go-capnproto2 over websocket

    My setup is a little convoluted. I use capnproto rpc over websockets (to simplify deployment) between my programs. However, the websocket server keeps failing to read a header somehow and crashes.

    rpc: writing return: failed to write msg: websocket closed: failed to read header: EOF
    

    and on the Dialer side:

    rpc: writing finish: failed to write msg: context canceled
    

    Sometimes both ends cancel their contexts at the same time. I'm not sure why.

    bug 
    opened by Vaelatern 14
  • Improve CloseError API

    Improve CloseError API

    I think the current API is a little obnoxious since it forces you to use xerrors.As to get the close error code which is very annoying.

    Also people forget to use xerrors.As, see https://github.com/sumorf/deribit-api/blob/6b581052e02d0808bed0bdc7a04f3dc4582e70d3/stream.go#L30

    @sumorf

    I don't like the ceremony involved with xerrors.As so I'd like to provide some sort of convenience API to get the error code.

    opened by nhooyr 13
  • Allow single frame writes

    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.

    enhancement 
    opened by nhooyr 13
  • Add chat example

    Add chat example

    Hello @nhooyr, maybe you could add chat example something like https://github.com/gorilla/websocket/tree/master/examples/chat would be easier to compare both implementations and also would serve as documentation.

    docs 
    opened by svycka 12
  • Documentation should note that package ignores protection against untrusted client

    Documentation should note that package ignores protection against untrusted client

    The client does not mask sent messages or verify the the Sec-WebSocket-Accept header. Because these features are required by the protocol and related to security, the documentation should note the omission of the features.

    It's unlikely that the package will be used in an application running untrusted code, but the package might be used in a websocket proxy that forwards requests from untrusted code.

    docs 
    opened by propertyscott 12
  • forceLock hanging in v1.8.3 and causing a goroutine leak

    forceLock hanging in v1.8.3 and causing a goroutine leak

    I've only just noticed this and have only been able to confirm it was introduced in v1.8.3 I have not been able to determine if it's a mistake in my using of the library or even make a good test case yet.

    upgrading to v1.8.3 has introduced an issue where I end up with a bunch of goroutines that seem to be blocking in forceLock in conn_notjs.go. The following is my pprof output

    48 @ 0x436c00 0x4050bd 0x404e85 0x9f2324 0x9f2302 0x9f7ed7 0x466611
    #	0x9f2323	nhooyr.io/websocket.(*mu).forceLock+0x43	nhooyr.io/[email protected]/conn_notjs.go:234
    #	0x9f2301	nhooyr.io/websocket.(*msgReader).close+0x21	nhooyr.io/[email protected]/read.go:112
    #	0x9f7ed6	nhooyr.io/websocket.(*Conn).close.func1+0x46	nhooyr.io/[email protected]/conn_notjs.go:144
    
    bug 
    opened by jacobalberty 11
  • NetConn adapter

    NetConn adapter

    Was going through some random reading and I noticed this is an issue that appeared multiple times on the gorilla/websocket tracker:

    • https://github.com/gorilla/websocket/issues/282#issuecomment-327764790
    • https://github.com/gorilla/websocket/issues/441

    People using nhooyr/websocket also have ran into this: #80

    With my solution in #87, this is relatively simple to implement.

    I'm thinking websocket.NetConn(c *websocket.Conn) net.Conn

    enhancement 
    opened by nhooyr 11
  • Compile for WebAssembly

    Compile for WebAssembly

    @albrow I saw you in issue https://github.com/gorilla/websocket/issues/432#issuecomment-518355272

    If you'd like to contribute the necessary improvements to this library, I'd be more than happy to help, review and merge your changes.

    enhancement 
    opened by nhooyr 10
  • Read after write fails (Chrome DevTools Protocol server)

    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:

    [email protected]:~/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:

    [email protected]:~/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
    [email protected]:~/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!

    opened by kenshaw 10
  • Confirm AcceptOrigins API

    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()

    opened by nhooyr 10
  • Example compiled on windows flags as malware on virustotal.

    Example compiled on windows flags as malware on virustotal.

    Just built the example on windows with go 1.19.3

    cd examples\echo
    go build ./
    

    ... and then uploaded the resulting echo.exe to virustotal.com and it gets multiple errors.

    https://www.virustotal.com/gui/file/dec851d0df6d845f3b47ffbcba7ee66a5144cb06f406616b1cce21d3067085a1/detection

    I first noticed it because my local eset antivirus blocked files that used this websocket module during compile

    PS D:\Code\kmpm\websocket\examples\echo> go build ./
    open C:\Users\peter\AppData\Local\Temp\go-build3248632192\b001\exe\a.out.exe: Access denied.
    

    Some examples of what virustotal thinks.

    | vendor | status | |------------------|-------------------------------------| | eset-nod32 | A Variant Of WinGo/Agent.KN | | McAffe | Artemis!BFA767614E38 | | Microsoft | Trojan:Win32/Wacatac.B!ml | | Fortinet | W32/Agent.KN!tr |

    opened by kmpm 1
  • Update the chat example to publish messages on the websocket connection

    Update the chat example to publish messages on the websocket connection

    I think the chat example would be a lot more valuable if the messages were published via the websocket connection instead of as separate HTTP request. I'm trying to migrate from gorilla/websockets but having a hard time figuring out how to both read from and write to a client from different threads. If not publishing the messages themselves, maybe a "typing" indicator when a user is typing in the input? Anything that demonstrates concurrent full duplex communication over websockets really.

    opened by stefan-toubia 0
  • How to cleanly disconnect?

    How to cleanly disconnect?

    Problem: If I have an application that waits for data over the websocket, how do I cleanly shut down the websocket? Here, cleanly means:

    1. stop waiting for new data
    2. perform the closing handshake
    3. verify that these things happened with no errors

    Our first attempt at this calls (*Conn).Read(ctx) in a loop on one goroutine, and then when it was time to shut down, calls (*Conn).Close() from a different goroutine.

    However, this would often result in close errors reading the response packet. Investigating, I found that calling Close() concurrently with Read() from different goroutines exposed a race condition where Read() would read the frame header, and then Close() would read the packet payload as if it were a header, and throw an error.

    Would you consider this a bug? That is to say, are you meant to be able to call Read() and Close() from different goroutines concurrently?

    My next attempt cancels the Context passed to Read(), and then calls Close() from the same goroutine after Read() returns. There are a couple problems with this approach. Firstly, canceling the Read() context seems to trigger an opClose with status PolicyViolation. Ideally I'd be sending a normal status on disconnect. Secondly, Close() returns a couple different error messages that seem to depend on some races in the library, including "already wrote close" and "WebSocket closed", so it's really hard to verify that the close handshake completed correctly.

    opened by spikecurtis 0
  • Can not do Zero alloc reads and writes.

    Can not do Zero alloc reads and writes.

    I wrote some tests, here is my code:

    package main
    
    import (
    	"net/http"
    	"io"
    	"fmt"
    	"nhooyr.io/websocket"
    	"context"
    	"time"
    	"bytes"
    	"runtime"
    
    )
    
    
    func main(){
    	go func(){
    		http.ListenAndServe(`127.0.0.1:9098`,echoServer{})
    	}()
    	time.Sleep(time.Millisecond*10)
    	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    	defer cancel()
    	c, _, err := websocket.Dial(ctx, `http://127.0.0.1:9098`, &websocket.DialOptions{
    		Subprotocols: []string{"echo"},
    	})
    	defer c.Close(websocket.StatusNormalClosure,``)
    	ctx = context.Background()
    	inList:=[]byte{1}
    	buf:=make([]byte,1024)
    	c2:=websocket.NetConn(ctx,c,websocket.MessageBinary)
    	runLoopOnceFn:=func(){
    		_,err=c2.Write(inList)
    		if err!=nil{
    			panic(err)
    		}
    		for {
    			nr,err := c2.Read(buf)
    			if err!=nil{
    				panic(err)
    			}
    			if nr==0{
    				continue
    			}
    			if nr!=1 || bytes.Equal(buf[:nr],inList)==false{
    				panic(`b7wdgy9dyz`)
    			}
    			return
    		}
    	}
    	for i:=0;i<1024;i++{
    		runLoopOnceFn()
    	}
    	var m runtime.MemStats
    	runtime.ReadMemStats(&m)
    	startAllocSize:=m.TotalAlloc
    	startAllocNum:=m.Mallocs
    	const loopNum = 1024
    	for i:=0;i<loopNum;i++{
    		runLoopOnceFn()
    	}
    	runtime.ReadMemStats(&m)
    	endAllocSize:=m.TotalAlloc
    	endAllocNum:=m.Mallocs
    	fmt.Println("AllocSize",float64(endAllocSize-startAllocSize)/float64(loopNum))
    	fmt.Println("AllocNum",float64(endAllocNum-startAllocNum)/float64(loopNum))
    
    }
    
    // echoServer is the WebSocket echo server implementation.
    // It ensures the client speaks the echo subprotocol and
    // only allows one message every 100ms with a 10 message burst.
    type echoServer struct {
    	// logf controls where logs are sent.
    	logf func(f string, v ...interface{})
    }
    
    func (s echoServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
    		Subprotocols: []string{"echo"},
    	})
    	if err != nil {
    		s.logf("%v", err)
    		return
    	}
    	defer c.Close(websocket.StatusInternalError, "the sky is falling")
    	if c.Subprotocol() != "echo" {
    		c.Close(websocket.StatusPolicyViolation, "client must speak the echo subprotocol")
    		return
    	}
    	for {
    		err = echo(r.Context(), c)
    		if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
    			return
    		}
    		if err != nil {
    			s.logf("failed to echo with %v: %v", r.RemoteAddr, err)
    			return
    		}
    	}
    }
    
    // echo reads from the WebSocket connection and then writes
    // the received message back to it.
    // The entire function has 10s to complete.
    func echo(ctx context.Context, c *websocket.Conn) error {
    	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
    	defer cancel()
    
    	typ, r, err := c.Reader(ctx)
    	if err != nil {
    		return err
    	}
    
    	w, err := c.Writer(ctx, typ)
    	if err != nil {
    		return err
    	}
    
    	_, err = io.Copy(w, r)
    	if err != nil {
    		return fmt.Errorf("failed to io.Copy: %w", err)
    	}
    
    	err = w.Close()
    	return err
    }
    
    • I got result:
    AllocSize 33131.9296875
    AllocNum 9.0244140625
    
    • here is alloc place list in one runLoopOnceFn call (with my internal tool, may open source soon)
    =========================================================
    allocSize: 32.00KB allocNum: 1.0000B percentSize: 99.83%
    type: uint8
    /usr/local/go/src/runtime/malloc.go:1121
    /usr/local/go/src/runtime/slice.go:103
    /usr/local/go/src/io/io.go:424
    /usr/local/go/src/io/io.go:386
    main.go:119
    main.go:91
    /usr/local/go/src/net/http/server.go:2947
    /usr/local/go/src/net/http/server.go:1991
    /usr/local/go/src/net/http/server.go:3102
    /usr/local/go/src/runtime/asm_amd64.s:1594
    /usr/local/go/src/net/http/server.go:3102
    =========================================================
    allocSize: 24.000B allocNum: 1.0000B percentSize:  0.07%
    type: websocket.CloseError
    /usr/local/go/src/runtime/malloc.go:1121
    /usr/local/go/src/runtime/malloc.go:1192
    src/nhooyr.io/websocket/close.go:71
    main.go:92
    /usr/local/go/src/net/http/server.go:2947
    /usr/local/go/src/net/http/server.go:1991
    /usr/local/go/src/net/http/server.go:3102
    /usr/local/go/src/runtime/asm_amd64.s:1594
    /usr/local/go/src/net/http/server.go:3102
    =========================================================
    allocSize: 16.000B allocNum: 1.0000B percentSize:  0.05%
    type: websocket.msgWriter
    /usr/local/go/src/runtime/malloc.go:1121
    /usr/local/go/src/runtime/malloc.go:1192
    src/nhooyr.io/websocket/write.go:115
    src/nhooyr.io/websocket/write.go:122
    src/nhooyr.io/websocket/write.go:42
    src/nhooyr.io/websocket/netconn.go:84
    main.go:32
    main.go:66
    /usr/local/go/src/runtime/proc.go:250
    /usr/local/go/src/runtime/asm_amd64.s:1594
    =========================================================
    allocSize: 16.000B allocNum: 1.0000B percentSize:  0.05%
    type: websocket.msgWriter
    /usr/local/go/src/runtime/malloc.go:1121
    /usr/local/go/src/runtime/malloc.go:1192
    src/nhooyr.io/websocket/write.go:115
    src/nhooyr.io/websocket/write.go:28
    main.go:114
    main.go:91
    /usr/local/go/src/net/http/server.go:2947
    /usr/local/go/src/net/http/server.go:1991
    /usr/local/go/src/net/http/server.go:3102
    /usr/local/go/src/runtime/asm_amd64.s:1594
    /usr/local/go/src/net/http/server.go:3102
    
    opened by bronze1man 4
  • [doc] Please document how to handle websocket.NetConn().Read() return zero and nil

    [doc] Please document how to handle websocket.NetConn().Read() return zero and nil

    I just wrote a demo to test it. Here is my code:

    package main
    
    import (
    	"net/http"
    	"io"
    	"fmt"
    	"nhooyr.io/websocket"
    	"context"
    	"time"
    	"strconv"
    )
    
    func main(){
    	go func(){
    		http.ListenAndServe(`127.0.0.1:9098`,echoServer{})
    	}()
    	time.Sleep(time.Millisecond*10)
    	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    	defer cancel()
    	c, _, err := websocket.Dial(ctx, `http://127.0.0.1:9098`, &websocket.DialOptions{
    		Subprotocols: []string{"echo"},
    	})
    	defer c.Close(websocket.StatusNormalClosure,``)
    	ctx = context.Background()
    	inList:=[]byte{1}
    	buf:=make([]byte,1024)
    	c2:=websocket.NetConn(ctx,c,websocket.MessageBinary)
    	for i:=0;i<10;i++{
    		_,err=c2.Write(inList)
    		if err!=nil{
    			panic(err)
    		}
    		nr,err := c2.Read(buf)
    		if err!=nil{
    			panic(err)
    		}
    		if nr==0{
    			panic(`nr==0 `+strconv.Itoa(i))
    		}
    	}
    }
    
    // echoServer is the WebSocket echo server implementation.
    // It ensures the client speaks the echo subprotocol and
    // only allows one message every 100ms with a 10 message burst.
    type echoServer struct {
    	// logf controls where logs are sent.
    	logf func(f string, v ...interface{})
    }
    
    func (s echoServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
    		Subprotocols: []string{"echo"},
    	})
    	if err != nil {
    		s.logf("%v", err)
    		return
    	}
    	defer c.Close(websocket.StatusInternalError, "the sky is falling")
    	if c.Subprotocol() != "echo" {
    		c.Close(websocket.StatusPolicyViolation, "client must speak the echo subprotocol")
    		return
    	}
    	for {
    		err = echo(r.Context(), c)
    		if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
    			return
    		}
    		if err != nil {
    			s.logf("failed to echo with %v: %v", r.RemoteAddr, err)
    			return
    		}
    	}
    }
    
    // echo reads from the WebSocket connection and then writes
    // the received message back to it.
    // The entire function has 10s to complete.
    func echo(ctx context.Context, c *websocket.Conn) error {
    	ctx, cancel := context.WithTimeout(ctx, time.Second*10)
    	defer cancel()
    
    	typ, r, err := c.Reader(ctx)
    	if err != nil {
    		return err
    	}
    
    	w, err := c.Writer(ctx, typ)
    	if err != nil {
    		return err
    	}
    
    	_, err = io.Copy(w, r)
    	if err != nil {
    		return fmt.Errorf("failed to io.Copy: %w", err)
    	}
    
    	err = w.Close()
    	return err
    }
    

    And I found that websocket.NetConn().Read() will return zero and nil . I never seen a net.Conn object work like this. I do not know how to do with it , skip? But I am afraid of unlimit loop...

    opened by bronze1man 0
Releases(v1.8.7)
  • v1.8.7(Apr 7, 2021)

  • v1.8.6(May 18, 2020)

    • Fix deadlock introduced in v1.8.5 #231
      • Occured when you'd write at the same time as close
    • Add support for gin-gonic/gin
    • wsjson does not require that the message type read is text anymore #233
    • Add automated test to echo example #224
    • Undeprecate InsecureSkipVerify #225
      • It's more clear than * as an origin pattern.

    In light of #231 and #205, I've opened #239 which will track a complete review to ensure there are no more such bugs from my rewrite in #163.

    Source code(tar.gz)
    Source code(zip)
  • v1.8.5(Apr 14, 2020)

    • Improve cross origin API and add example #194 #198
    • Add automated test for chat example #201
    • You can now pass http:// and https:// URLs to Dial
    • Documentation fixes #212 #208
    • Prevent all writes after close frame is written #213
    • Ensure writes on a closed connection return the close error #215
    • Disable compression for Safari due to bugs in its implementation #218
    Source code(tar.gz)
    Source code(zip)
  • v1.8.4(Feb 28, 2020)

  • v1.8.3(Feb 21, 2020)

  • v1.8.2(Feb 16, 2020)

  • v1.8.1(Feb 16, 2020)

  • v1.8.0(Feb 16, 2020)

  • v1.7.4(Nov 9, 2019)

    • Dramatically optimize WebSocket masking algorithm implementation #171
      • We are now 1.75x faster than gorilla/websocket and gobwas/ws all while using pure safe Go
      • Thanks @renthraysk
    Source code(tar.gz)
    Source code(zip)
  • v1.7.3(Nov 5, 2019)

  • v1.7.2(Oct 23, 2019)

  • v1.7.1(Oct 11, 2019)

    • Minor documentation improvements
    • Improved close handshake behaviour https://github.com/nhooyr/websocket/commit/bc4fce01803c504367a6996b2bb66aee1eb5a143
    • Removed the typescript CI in favour of make https://github.com/nhooyr/websocket/commit/4d4a02d4bf9b8336272f6dc075f0e0a77566192d
    Source code(tar.gz)
    Source code(zip)
  • v1.7.0(Oct 9, 2019)

  • v1.6.5(Sep 29, 2019)

  • v1.6.4(Sep 27, 2019)

    • Fix improper use of sync/atomic on 32 bit systems causing panics #154
      • Thanks @andersfylling for reporting this issue.
    • Improve godoc and fix code style (#149, #150, #151)
      • Thanks @cristaloleg
    • Allows concurrent use of SetReadLimit in respect to the Read goroutine #154
      • Reasoning behind this change is that if you are handling messages async to the read goroutine, you might want to adjust the read limit in those instead co-ordinating explicitly with the read goroutine.
    Source code(tar.gz)
    Source code(zip)
  • v1.6.3(Sep 24, 2019)

  • v1.6.2(Sep 23, 2019)

  • v1.6.1(Sep 22, 2019)

  • v1.6.0(Sep 22, 2019)

  • v1.5.1(Sep 4, 2019)

  • v1.5.0(Sep 2, 2019)

    • Rewrote autobahn test suite in pure Go (#129)
      • The entire test suite now runs in 2.8s 🎉
    • Improved test coverage to 91% (#123, #126)
    • Modified NetConn wrapper to take context.Context as first argument (#131)
      • This is a breaking change but relatively minor so I don't think it's worth a major release.
    • Modified Dial/Accept to accept a pointer to option structs (#122, #123)
      • This is a breaking change but relatively minor so I don't think it's worth a major release.
    • Reduced Reader/Writer allocations to only 8 bytes (#116, #118)
    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Aug 16, 2019)

  • v1.3.3(Aug 7, 2019)

  • v1.3.2(Jul 24, 2019)

  • v1.3.1(Jul 13, 2019)

  • v1.3.0(Jul 3, 2019)

  • v1.2.1(Jun 23, 2019)

  • v1.2.0(Jun 11, 2019)

  • v1.1.1(Jun 4, 2019)

  • v1.1.0(Jun 2, 2019)

Owner
Anmol Sethi
Anmol Sethi
Fast and idiomatic client-driven REST APIs.

Vulcain is a brand new protocol using HTTP/2 Server Push to create fast and idiomatic client-driven REST APIs. An open source gateway server which you

Kévin Dunglas 3.3k Nov 27, 2022
protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript clients that connect the web frontend and golang backend fronted by grpc-gateway.

protoc-gen-grpc-gateway-ts protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript

gRPC Ecosystem 91 Nov 18, 2022
A minimal filecoin client library

filclient A standalone client library for interacting with the filecoin storage network Features Make storage deals with miners Query storage ask pric

Application Research Group 38 Sep 8, 2022
PlanB: a HTTP and websocket proxy backed by Redis and inspired by Hipache.

PlanB: a distributed HTTP and websocket proxy What Is It? PlanB is a HTTP and websocket proxy backed by Redis and inspired by Hipache. It aims to be f

vinay badhan 1 Mar 20, 2022
A modern, fast and scalable websocket framework with elegant API written in Go

About neffos Neffos is a cross-platform real-time framework with expressive, elegant API written in Go. Neffos takes the pain out of development by ea

Gerasimos (Makis) Maropoulos 477 Nov 22, 2022
go websocket, a better way to buid your IM server

Your star is my power!! ?? ⭐ ⭐ ⭐ ⭐ ⭐ Discribe lhttp is a http like protocol using websocket to provide long live, build your IM service quickly scalab

中弈 678 Nov 9, 2022
开箱即用的基于命令的消息处理框架,让 websocket 和 tcp 开发就像 http 那样简单

Cmd Srv 开箱即用的基于命令的消息处理框架,让 websocket 和 tcp 开发就像 http 那样简单

Eyas 20 Sep 25, 2022
HTTP, HTTP2, HTTPS, Websocket debugging proxy

English | 简体中文 We recommend updating whistle and Node to ensure that you receive important features, bugfixes and performance improvements. Some versi

avenwu 11.5k Dec 1, 2022
WebSocket Connection Smuggler

ws-smuggler ws-smuggler is websocket connection smuggling testing tool. It is similar to the this project, but it has been rewritten based on the web

HAHWUL 41 Nov 7, 2022
This package helps establish a websocket connection to the bilibili streaming server.

biliStreamClient This package helps establish a websocket connection to the bilibili streaming server. bilibili直播弹幕的WebSocket协议分析请参考:https://blog.csdn

JINGWEI ZHANG 24 Oct 25, 2022
websocket proxy,简单的websocket反向代理实现,支持ws、wss

websocket proxy 100行代码实现轻量的websocket代理库,不依赖其他三方库,支持ws、wss代理 使用示例 Install go get github.com/pretty66/websocketproxy import ( "github.com/pretty66/w

null 36 Nov 24, 2022
High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

lesismal 1k Nov 22, 2022
Websocket proxy component

Proxy server component Task description Task description is in DESCRIPTION.md Issues found in task description and fixed signal.Notify wasn't cathing

Alex 2 May 16, 2022
NotifyTool - A message forwarding service for http to websocket

notifyTool this is a message forwarding service for http to websocket task webso

zhou_chengfei 1 Jan 3, 2022
Pubsub-go - Go-redis pubsub with websocket

go-redis pubsub with websocket # start a local dev server $ make dev

null 0 Jan 28, 2022
Um chat feito em go utilizando gorilla/websocket, go-redis/redis,golang-jwt/jwte labstack/echo.

go-chat Um chat feito em go utilizando gorilla/websocket, go-redis/redis,golang-jwt/jwte labstack/echo. Why Eu gostaria de aprender algumas ferramenta

Kevin Souza 0 Jul 14, 2022
HTTP tunnel over Websocket

WS PROXY This is a reverse HTTP proxy over websockets. The aim is to securely make call to internal APIs from outside. How does it works a WSP client

root.gg 83 Nov 12, 2022
Designed to support DNS brute-forcing with a minimal number of network connections

Fast Use of DNS Resolvers Designed to support DNS brute-forcing with a minimal number of network connections. Installation go get -v -u github.com/caf

Jeff Foley 33 Nov 6, 2022
A minimal IPFS replacement for P2P IPLD apps

IPFS-Nucleus IPFS-Nucleus is a minimal block daemon for IPLD based services. You could call it an IPLDaemon. It implements the following http api call

Peergos 27 Nov 25, 2022