Tiny WebSocket library for Go.

Overview

ws

GoDoc CI

RFC6455 WebSocket implementation in Go.

Features

  • Zero-copy upgrade
  • No intermediate allocations during I/O
  • Low-level API which allows to build your own logic of packet handling and buffers reuse
  • High-level wrappers and helpers around API in wsutil package, which allow to start fast without digging the protocol internals

Documentation

GoDoc.

Why

Existing WebSocket implementations do not allow users to reuse I/O buffers between connections in clear way. This library aims to export efficient low-level interface for working with the protocol without forcing only one way it could be used.

By the way, if you want get the higher-level tools, you can use wsutil package.

Status

Library is tagged as v1* so its API must not be broken during some improvements or refactoring.

This implementation of RFC6455 passes Autobahn Test Suite and currently has about 78% coverage.

Examples

Example applications using ws are developed in separate repository ws-examples.

Usage

The higher-level example of WebSocket echo server:

package main

import (
	"net/http"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

func main() {
	http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		conn, _, _, err := ws.UpgradeHTTP(r, w)
		if err != nil {
			// handle error
		}
		go func() {
			defer conn.Close()

			for {
				msg, op, err := wsutil.ReadClientData(conn)
				if err != nil {
					// handle error
				}
				err = wsutil.WriteServerMessage(conn, op, msg)
				if err != nil {
					// handle error
				}
			}
		}()
	}))
}

Lower-level, but still high-level example:

import (
	"net/http"
	"io"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

func main() {
	http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		conn, _, _, err := ws.UpgradeHTTP(r, w)
		if err != nil {
			// handle error
		}
		go func() {
			defer conn.Close()

			var (
				state  = ws.StateServerSide
				reader = wsutil.NewReader(conn, state)
				writer = wsutil.NewWriter(conn, state, ws.OpText)
			)
			for {
				header, err := reader.NextFrame()
				if err != nil {
					// handle error
				}

				// Reset writer to write frame with right operation code.
				writer.Reset(conn, state, header.OpCode)

				if _, err = io.Copy(writer, reader); err != nil {
					// handle error
				}
				if err = writer.Flush(); err != nil {
					// handle error
				}
			}
		}()
	}))
}

We can apply the same pattern to read and write structured responses through a JSON encoder and decoder.:

	...
	var (
		r = wsutil.NewReader(conn, ws.StateServerSide)
		w = wsutil.NewWriter(conn, ws.StateServerSide, ws.OpText)
		decoder = json.NewDecoder(r)
		encoder = json.NewEncoder(w)
	)
	for {
		hdr, err = r.NextFrame()
		if err != nil {
			return err
		}
		if hdr.OpCode == ws.OpClose {
			return io.EOF
		}
		var req Request
		if err := decoder.Decode(&req); err != nil {
			return err
		}
		var resp Response
		if err := encoder.Encode(&resp); err != nil {
			return err
		}
		if err = w.Flush(); err != nil {
			return err
		}
	}
	...

The lower-level example without wsutil:

package main

import (
	"net"
	"io"

	"github.com/gobwas/ws"
)

func main() {
	ln, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
		}
		_, err = ws.Upgrade(conn)
		if err != nil {
			// handle error
		}

		go func() {
			defer conn.Close()

			for {
				header, err := ws.ReadHeader(conn)
				if err != nil {
					// handle error
				}

				payload := make([]byte, header.Length)
				_, err = io.ReadFull(conn, payload)
				if err != nil {
					// handle error
				}
				if header.Masked {
					ws.Cipher(payload, header.Mask, 0)
				}

				// Reset the Masked flag, server frames must not be masked as
				// RFC6455 says.
				header.Masked = false

				if err := ws.WriteHeader(conn, header); err != nil {
					// handle error
				}
				if _, err := conn.Write(payload); err != nil {
					// handle error
				}

				if header.OpCode == ws.OpClose {
					return
				}
			}
		}()
	}
}

Zero-copy upgrade

Zero-copy upgrade helps to avoid unnecessary allocations and copying while handling HTTP Upgrade request.

Processing of all non-websocket headers is made in place with use of registered user callbacks whose arguments are only valid until callback returns.

The simple example looks like this:

package main

import (
	"net"
	"log"

	"github.com/gobwas/ws"
)

func main() {
	ln, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		log.Fatal(err)
	}
	u := ws.Upgrader{
		OnHeader: func(key, value []byte) (err error) {
			log.Printf("non-websocket header: %q=%q", key, value)
			return
		},
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			// handle error
		}

		_, err = u.Upgrade(conn)
		if err != nil {
			// handle error
		}
	}
}

Usage of ws.Upgrader here brings ability to control incoming connections on tcp level and simply not to accept them by some logic.

Zero-copy upgrade is for high-load services which have to control many resources such as connections buffers.

The real life example could be like this:

package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"runtime"

	"github.com/gobwas/httphead"
	"github.com/gobwas/ws"
)

func main() {
	ln, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		// handle error
	}

	// Prepare handshake header writer from http.Header mapping.
	header := ws.HandshakeHeaderHTTP(http.Header{
		"X-Go-Version": []string{runtime.Version()},
	})

	u := ws.Upgrader{
		OnHost: func(host []byte) error {
			if string(host) == "github.com" {
				return nil
			}
			return ws.RejectConnectionError(
				ws.RejectionStatus(403),
				ws.RejectionHeader(ws.HandshakeHeaderString(
					"X-Want-Host: github.com\r\n",
				)),
			)
		},
		OnHeader: func(key, value []byte) error {
			if string(key) != "Cookie" {
				return nil
			}
			ok := httphead.ScanCookie(value, func(key, value []byte) bool {
				// Check session here or do some other stuff with cookies.
				// Maybe copy some values for future use.
				return true
			})
			if ok {
				return nil
			}
			return ws.RejectConnectionError(
				ws.RejectionReason("bad cookie"),
				ws.RejectionStatus(400),
			)
		},
		OnBeforeUpgrade: func() (ws.HandshakeHeader, error) {
			return header, nil
		},
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Fatal(err)
		}
		_, err = u.Upgrade(conn)
		if err != nil {
			log.Printf("upgrade error: %s", err)
		}
	}
}

Compression

There is a ws/wsflate package to support Permessage-Deflate Compression Extension.

It provides minimalistic I/O wrappers to be used in conjunction with any deflate implementation (for example, the standard library's compress/flate.

package main

import (
	"bytes"
	"log"
	"net"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsflate"
)

func main() {
	ln, err := net.Listen("tcp", "localhost:8080")
	if err != nil {
		// handle error
	}
	e := wsflate.Extension{
		// We are using default parameters here since we use
		// wsflate.{Compress,Decompress}Frame helpers below in the code.
		// This assumes that we use standard compress/flate package as flate
		// implementation.
		Parameters: wsflate.DefaultParameters,
	}
	u := ws.Upgrader{
		Negotiate: e.Negotiate,
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Fatal(err)
		}

		// Reset extension after previous upgrades.
		e.Reset()

		_, err = u.Upgrade(conn)
		if err != nil {
			log.Printf("upgrade error: %s", err)
			continue
		}
		if _, ok := e.Accepted(); !ok {
			log.Printf("didn't negotiate compression for %s", conn.RemoteAddr())
			conn.Close()
			continue
		}

		go func() {
			defer conn.Close()
			for {
				frame, err := ws.ReadFrame(conn)
				if err != nil {
					// Handle error.
					return
				}
				frame = ws.UnmaskFrameInPlace(frame)
				frame, err = wsflate.DecompressFrame(frame)
				if err != nil {
					// Handle error.
					return
				}

				// Do something with frame...

				ack := ws.NewTextFrame([]byte("this is an acknowledgement"))
				ack, err = wsflate.CompressFrame(ack)
				if err != nil {
					// Handle error.
					return
				}
				if err = ws.WriteFrame(conn, ack); err != nil {
					// Handle error.
					return
				}
			}
		}()
	}
}
Issues
  • Multiple epoll events received for large message size, reads getting blocked after first event

    Multiple epoll events received for large message size, reads getting blocked after first event

    Hi we are implementing websocket server and using netpoll (epoll) for watching for read events on connected clients file descriptor. When the client sends large message size we are getting multiple read events for the same message. We are able to read the entire message after receiving the first event from epoll. For the subsequent events the read gets blocked on nextframe() call as there is no message on underlying connection. Any help is much appreciated.

    opened by codefetcher 26
  • Fix bugs in the Grow feature

    Fix bugs in the Grow feature

    Thanks for your excellent work!

    https://github.com/chromedp/chromedp depends on this package, and it fails to send large requests due to the lack of fragmentation support in Chrome (reported here: https://github.com/ChromeDevTools/devtools-protocol/issues/175). Now that Grow() is added to this package, I want to address this issue on the client side. It works like a charm! Thank you very much!

    I found some issues though. So here is the pull request.

    BTW, DisableFlush() is supposed to be used to enable auto growing of the buffer, right? Is it better to call it EnableAutoGrow()?

    opened by ZekeLu 22
  • wss:// and netpoll

    wss:// and netpoll

    I was having issues with my app and found out that using wss:// with nginx as a proxy leads to missing messages. I am using the chat example with netpoll.

    Running the golang code below with ws:// returns the number of sent requests, whilst using wss:// returns only first 2-3 messages.

    High-level example with wsutil works as expected, I think that might be the issue with netpoll. Hopefully, that makes more sense to you. Thanks

    slightly modified chat example from gobwas/ws-examples

    server.go

    	poller, _ := netpoll.New(nil)
    
    	pool := gopool.NewPool(128, 1, 1)
    
    	handle := func(conn net.Conn) {
    		userSeq := 0
    
    		hs, err := ws.Upgrade(conn)
    
    		if err != nil {
    			panic(err)
    		}
    
    		logger.Debugf("%s: established websocket connection: %+v", nameConn(conn), hs)
    
    		desc := netpoll.Must(netpoll.HandleRead(conn))
    
    		// Subscribe to events about conn.
    		pErr := poller.Start(desc, func(ev netpoll.Event) {
    			fmt.Println(userSeq)
    
    			userSeq++
    		})
    
    		if pErr != nil {
    			panic(pErr)
    		}
    	}
    
    	// Create incoming connections listener.
    	ln, err := net.Listen("tcp", ":8088")
    
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	log.Printf("websocket is listening on %s", ln.Addr().String())
    
    	// Create netpoll descriptor for the listener.
    	// We use OneShot here to manually resume events stream when we want to.
    	acceptDesc := netpoll.Must(netpoll.HandleListener(
    		ln, netpoll.EventRead|netpoll.EventOneShot,
    	))
    
    	// accept is a channel to signal about next incoming connection Accept()
    	// results.
    	accept := make(chan error, 1)
    
    	// Subscribe to events about listener.
    	err = poller.Start(acceptDesc, func(e netpoll.Event) {
    		err := pool.ScheduleTimeout(time.Millisecond, func() {
    			conn, err := ln.Accept()
    			if err != nil {
    				accept <- err
    				return
    			}
    
    			accept <- nil
    			handle(conn)
    		})
    		if err == nil {
    			err = <-accept
    		}
    		if err != nil {
    			if err != gopool.ErrScheduleTimeout {
    				goto cooldown
    			}
    			if ne, ok := err.(net.Error); ok && ne.Temporary() {
    				goto cooldown
    			}
    
    			log.Fatalf("accept error: %v", err)
    
    		cooldown:
    			delay := 5 * time.Millisecond
    			log.Printf("accept error: %v; retrying in %s", err, delay)
    			time.Sleep(delay)
    		}
    
    		err = poller.Resume(acceptDesc)
    
    		if err != nil {
    			log.Fatal(err)
    		}
    	})
    
    	if err != nil {
    		log.Fatal(err)
    	}
    

    client.js

    process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
    
    const WebSocket = require('ws');
    const ws = new WebSocket( 'wss://vm.dev/' );
    
    ws.on('open', function open() {
        for (let i = 0; i < 10; i++) {
            ws.send("ping");
        }
    })
    

    Nginx config

    server {
        listen 80;
        listen 443 ssl;
    
        # ... ssl config here ...
    
        location / {
            proxy_pass http://192.168.0.100:8088/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
    opened by macabre2077 20
  • fix: panic caused by uintptr

    fix: panic caused by uintptr

    When I used this library on windows, ReadHeader returns full zero Header after about 3-5 calls, and sometimes the program panic. After some debug I found it's caused by the usage of uintptr.

    According to doc of unsafe.Pointer, most of the previous uintptr usage are incorrect. This PR fixes the issue.

    Change-Id: Iddd9c07d7c1c1d3f1cf5239b6524e9399b49725d

    opened by jxskiss 19
  • Send headers with Sec-WebSocket-* capitalization

    Send headers with Sec-WebSocket-* capitalization

    According to the RFC, the Sec-WebSocket-* headers should have a capital S inside the word WebSocket. This is a shame, because most other headers are canonicalized differently.

    Some websocket servers are particularly picky about the capitalization. The agar.io server (or maybe the Cloudflare servers it's behind) will reject connections with the canonical spelling, giving a 400 in response.

    You can try it. This will succeed:

    curl -v --http1.1 'https://live-arena-1cm5igj.agar.io/'
        -H 'Host: live-arena-1cm5igj.agar.io:443'
        -H 'Upgrade: websocket'
        -H 'Connection: Upgrade'
        -H 'Sec-WebSocket-Version: 13'
        -H 'Sec-WebSocket-Key: 1u4hDlp0kUjbZafgII9lLw=='
    

    But this will fail, giving a 400 Bad Request:

    curl -v --http1.1 'https://live-arena-1cm5igj.agar.io/'
        -H 'Host: live-arena-1cm5igj.agar.io:443'
        -H 'Upgrade: websocket'
        -H 'Connection: Upgrade'
        -H 'Sec-Websocket-Version: 13'
        -H 'Sec-Websocket-Key: 1u4hDlp0kUjbZafgII9lLw=='
    

    This patch fixes this: ws will now accept any capitalization it receives, but use the RFC-correct capitalization when sending headers either as a response or as an upgrade request.

    Thanks for this library! The zero-copy functionality is really nice for us.

    opened by qguv 14
  • Compression built around flate package

    Compression built around flate package

    Simple reader and writer that support deflate compression. Can be used as the wrapper around the wsutil.Writer and wsutil.Reader structs.

    Code highly inspired by Gorilla's WebSocket approach, but I not properly sure about pools for flate.Reader/flate.Writer.

    opened by rdmrcv 14
  • Looking forward to your response

    Looking forward to your response

    Hello @gobwas - I'm Jeeva, I'm planning to bring Websocket feature on next release of aah framework. I have read this article and went through your ws codebase.

    I'm interested in integrating with aah framework, however you have mentioned in the readme "The library is not tagged as v1.0.0 yet so it can be broken during some improvements or refactoring." I think it is production ready. However I would like to hear from you and also could you please tag it?

    Thank you!

    question 
    opened by jeevatkm 14
  • Stack based slice does not work.

    Stack based slice does not work.

    func ReadHeader(r io.Reader) (h Header, err error) {
    	var b [MaxHeaderSize - 2]byte
    
           // ************ OLD CODE (Go 1.12 does not work!) ************
    	//bp := uintptr(unsafe.Pointer(&b))
    	//bh := &reflect.SliceHeader{Data: bp, Len: 2, Cap: MaxHeaderSize - 2}
    	//bts := *(*[]byte)(unsafe.Pointer(bh))
    
            // ************ NEW CODE (Go 1.12 work it!) ************
    	var bts []byte
    	bh := (*reflect.SliceHeader)(unsafe.Pointer(&bts))
    	bh.Data = uintptr(unsafe.Pointer(&b))
    	bh.Cap = MaxHeaderSize - 2
    	bh.Len = 2
    
    	// Prepare to hold first 2 bytes to choose size of next read.
    	_, err = io.ReadFull(r, bts)
    	if err != nil {
    		return
    	}
            ....
    }```
    opened by limpo1989 11
  • Fix some lint warnings

    Fix some lint warnings

    Have fixed few warnings. I haven't touched this one:

    frame.go:181:13:warning: struct of size 24 could be 16 (maligned)
    

    I'm not sure how it's safe to change order of fields for Header(this might also impact Frame)

    opened by cristaloleg 9
  • in ws.Upgrader, retrieve cookie?

    in ws.Upgrader, retrieve cookie?

    I see the example code given in Readme did check for cookies. But is there anyway to return the cookie value when calling upgrader.Upgrade? Otherwise it would be meaningless to validate the cookie other than prevent unknown requests.

    opened by joesonw 8
  • Intermittent protocol failure and malformed payload

    Intermittent protocol failure and malformed payload

    I'm willing to bet this is an oversight on my part, but here goes.

    I wrote a light weight websocket connection between two servers for work, with both client and server utilising gobwas/ws.

    Intermittently (and much more frequently on windows) I will get a read error "frames from client to server must be masked" on the server, and terminate the connection. However, all writes from my client code use wsutil.WriteClientMessage, so all messages should be masked (assuming my understanding of the library is correct). I note that this, and some other protocol errors happened early on when I was accidentally reading from the connection in two goroutines simultaneously, though I believe this has been resolved.

    I also see the occasional issue where a message payload contains additional bytes (usually ~4) on the front that look like part of the websocket frame data (payload is always JSON in current usages).

    I suspect I've overlooked something in one of the systems designs, but at this point I've run out of ideas as to the cause, so I'm appealing for assistance.

    Summary:

    • Server intermittently (and more frequently on windows) produces read error "frames from client to server must be masked"
    • client intermittently has additional bytes prefixed to payload, preventing message parsing

    the client library I wrote can be seen here: https://github.com/Maldris/evtWebsocketClient and a gist of the server connection/communication logic can be seen here: https://gist.github.com/Maldris/4f62a8b7456fd82c3de6551ef4d8d99c

    documentation 
    opened by Maldris 8
  • why does conn.Write ignoring the number bytes written?

    why does conn.Write ignoring the number bytes written?

    Hi,

    why does conn.Write ignoring the number bytes written here https://github.com/gobwas/ws/blob/master/write.go#L95 ? It seems to me that there is no guarantee net.Conn will write all the byes in one write call. if so, shouldn't the following function return both number of bytes and the error so the application can create ws continuation frames depending on the number of bytes written?

    func WriteFrame(w io.Writer, f Frame) error {
    	err := WriteHeader(w, f.Header)
    	if err != nil {
    		return err
    	}
    	_, err = w.Write(f.Payload)
    	return err
    }
    
    opened by kant777 0
  • HTTP_PROXY, HTTPS_PROXY, NO_PROXY parameters ignored

    HTTP_PROXY, HTTPS_PROXY, NO_PROXY parameters ignored

    Problem with client side of the library behind a corporate proxy. HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment parameters ignored during connection. Definitely, it is possible to make self proxy-aware Dialer implementation, but looking at the standard library it seems that default implementation should honor *_PROXY params out of the box.

    opened by e-zhydzetski 1
  • got error message

    got error message "control frame is not final" when using websocketUtil.ReadServerText

    client side:

    func startTest(c *gin.Context) {
    	type Request struct {
    		Cnt int
    		Url string
    	}
    	req := new(Request)
    	if err := c.ShouldBind(req); err != nil {
    		c.JSON(http.StatusOK, gin.H{
    			"Errcode": errno.ErrWrongParam.Code(),
    			"Errmsg":  errno.ErrWrongParam.Error(),
    		})
    		return
    	}
    	statistics := make([]chan int, req.Cnt)
    	for i := 0; i < req.Cnt; i++ {
    		statistics[i] = make(chan int)
    		go func(index int, url string, res chan int) {
    			conn, _, _, err := websocket.DefaultDialer.Dial(context.Background(), url)
    			if err != nil {
    				log.Println("dial fail index = ", index)
    				res <- 1
    				return
    			}
    			defer conn.Close()
    			text := "hello world"
    			err = websocketUtil.WriteClientMessage(conn, websocket.OpText, []byte(text))
    			if err != nil {
    				log.Println("WriteMessage failed 111:", err)
    				res <- 2
    				return
    			}
    
    			message, err := websocketUtil.ReadServerText(conn)
    			if err != nil {
    				log.Println("ReadMessage failed 222:", err)
    				res <- 3
    				return
    			}
    			log.Println("ReadMessage :", string(message))
    			sleepSec := rand.Int31n(40)
    			log.Println("start sleep =", sleepSec, " s")
    			time.Sleep(time.Duration(sleepSec) * time.Second)
    			res <- 4
    		}(i, req.Url, statistics[i])
    	}
    	dialFailCnt := float64(0)
    	writeFailCnt := float64(0)
    	readFailCnt := float64(0)
    	finishCnt := float64(0)
    	for _, ch := range statistics {
    		res := <-ch
    		switch res {
    		case 1:
    			dialFailCnt++
    		case 2:
    			writeFailCnt++
    		case 3:
    			readFailCnt++
    		case 4:
    			finishCnt++
    		}
    	}
    	floatTotal := float64(req.Cnt)
    	log.Println(fmt.Sprintf("total=%d,dialFailRate=%f,writeFailRate=%f,readFailRate=%f,successRate=%f", req.Cnt, dialFailCnt/floatTotal, writeFailCnt/floatTotal, readFailCnt/floatTotal, finishCnt/floatTotal))
    }
    

    server side:

    func readClientData(Conn *net.Conn) {
    	for {
    		message, msgType, err := websocketUtil.ReadClientData(Conn)
    		if err != nil {
    			break
    		}
            if msgType == websocket.OpText {
    			_ = websocketUtil.WriteServerMessage(Conn, msgType, []byte("send echo"))
            }
    	}
    }
    

    i start a benchmark test in my 4c8g machine with code above,(obviously i removed some project-related code) when i test 5000 connection got result total=5000,dialFailRate=0.000000,writeFailRate=0.000000,readFailRate=0.004600,successRate=0.995400 all realFail errors are control frame is not final. it's a bug or my wrong usage? besides, after upgrading websocket, i bind a unique id with each connection which has lock competition that raising high cpu load, but according to the statistical results, it seems irrelevant to the question.

    opened by Jekinnnnnn 1
  • wsutil: buffer grow leads to its reduce

    wsutil: buffer grow leads to its reduce

    Under using chromedp package wich use that lib as dependency got panic in some cases with the follow stack trace

    panic: wsutil: buffer grow leads to its reduce                              
    goroutine 42 [running]:                                                     
    github.com/gobwas/ws/wsutil.(*Writer).Grow(0xc0004c22e8, 0x1b)              
            /home/max/go/pkg/mod/github.com/gobwas/[email protected]/wsutil/writer.
    github.com/gobwas/ws/wsutil.(*Writer).Write(0xc0004c22e8, 0xc000ad4000, 0x8d
            /home/max/go/pkg/mod/github.com/gobwas/[email protected]/wsutil/writer.
    net.(*Buffers).WriteTo(0xc0328f45c0, 0xee9ec0, 0xc0004c22e8, 0x1ffad, 0x1ffa
            /usr/local/go/src/net/net.go:675 +0xd9                              
    github.com/mailru/easyjson/buffer.(*Buffer).DumpTo(0xc0004c2410, 0xee9ec0, 0
            /home/max/go/pkg/mod/github.com/mailru/[email protected]/buffer/pool.g
    github.com/mailru/easyjson/jwriter.(*Writer).DumpTo(...)                    
            /home/max/go/pkg/mod/github.com/mailru/[email protected]/jwriter/write
    github.com/chromedp/chromedp.(*Conn).Write(0xc0004c2240, 0xef56a0, 0xc000928
            /home/max/go/pkg/mod/github.com/chromedp/[email protected]/conn.go:137
    github.com/chromedp/chromedp.(*Browser).run(0xc0006bb900, 0xef56a0, 0xc00092
            /home/max/go/pkg/mod/github.com/chromedp/[email protected]/browser.go:
    created by github.com/chromedp/chromedp.NewBrowser                          
            /home/max/go/pkg/mod/github.com/chromedp/[email protected]/browser.go:
    %# v &{info systemd 45 0xc0000c8820 0xc0000c8870 0xc0006bc760 0xc000495950}
    

    I think it`s problem with call size in chromedp, but generally lib should detect grow size lower than current without panic.

    opened by maxjust 8
  • added option for dialer to follow redirects

    added option for dialer to follow redirects

    Hey guys, First of all, thank you for this package! Great job!

    This PR introduces ability for a client to follow redirects.

    Protocol wise, following redirects is acceptable. Previous behaviour is unmodified

    Usage: Simply add dialer.FollowRedirects = true

    This is quite a naive implementation. If you feel changes are required to make it more acceptable, I would happily put the time in.

    opened by NivKeidan 4
Releases(v1.1.0)
  • v1.1.0(Jul 12, 2021)

    Notes

    Compression support

    This release adds wsflate package implementing WebSocket Compression Extension logic.

    API

    • Added ws.UnmaskFrame() and ws.UnmaskFrameInPlace() helpers to be symmetrical with already existing ws.MaskFrame() and ws.MaskFrameInPlace()
    • Added ws/wsutil.Reader.MaxFrameSize field (thanks to @agnivade)
    • Added ability to obtain WebSocket handshake rejection HTTP status code from errors returned during upgrade

    Bug Fixes

    • Fixed incorrect buffer recalculation logic inside wsutil.Writer.Grow(), which was leading to panic with message "wsutil: buffer grow leads to its reduce" (thanks to @ZekeLu)

    Development

    • Added go.mod declaration which makes ws to be a Go module
    • Moved from Travis CI to GitHub Actions
    • Update a dependencies for Autobahn tests
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0-rc.6(Jul 10, 2021)

  • v1.1.0-rc.5(Feb 11, 2021)

  • v1.1.0-rc.4(Feb 6, 2021)

  • v1.1.0-rc.3(Feb 5, 2021)

  • v1.1.0-rc.2(Jan 25, 2021)

  • v1.1.0-rc.1(Dec 8, 2020)

  • v1.0.4(Sep 7, 2020)

  • v1.0.3(Mar 3, 2020)

  • v1.0.2(Jun 14, 2019)

    This release contains a patch that removes unsafe usage from many places. This is done to bring more stable and compatible behavior on other than Linux operating systems.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(May 19, 2019)

    This release contains patch that makes ws.Dial() and other client functions send headers in spec compliant capitalization (Sec-Websocket-* to Sec-WebSocket-*). See #71.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Aug 5, 2018)

    Breaking changes:

    • HTTPUpgrader: UpgradeHTTP() and HTTPUpgrader.Upgrade() accepts now only http.ResponseWriter and *http.Request without http.Header option. To add custom headers per upgrade use separate instances of HTTPUpgrader with Header field set to custom headers.

    • Upgrader:

      • Upgrader.Header field now not a callback, but HandshakeHeader interface. See the docs for more info.
      • Upgrader.OnRequest, Upgrader.OnHost, Upgrader.OnHeader and Upgrader.OnBeforeUpgrade are now return only error, without rejection code. To use rejection code use an RejectConnectionError. See the docs for more info.
    • wsutil.ClosedError: Code and Reason methods are now exported fields of a struct.

    • wsutil.ControlHandler: no more callbacks, all logic moved to the same called struct.

    Features

    • Add MustWriteFrame() and MustReadFrame() helper functions.
    • Add State helper methods: s.ClientSide(), s.ServerSide() and others.

    Bugfixes

    • wsutil.ControlHandler: now deals well with masked application data.
    • wsutil.ReadMessage (and brothers): deals well with io.ReadAll at fragmented messages.
    • wsutil.Reader: works well with io.ReadAll when body has invalid utf8 sequence.

    Improvements

    • Running autobahn testsuite on every travis ci builds.
    • More unit tests.
    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(May 27, 2018)

Owner
Sergey Kamardin
Sergey Kamardin
Encrypted-websocket-chat - Encrypted websocket chat using golang

Encrypted websocket chat First version written in python This version should be

Artyom Artamonov 11 Jun 10, 2022
Websocket-chat - A simple websocket chat application

WebSocket Chat App This is a simple chat app based on websockets. It allows user

null 1 Jan 25, 2022
A tiny command line websocket client written in Go

wsc A simplistic tool for sending and receiving websocket messages from a command line. Mainly useful to test websocket servers. Getting started: $ go

Raphael Simon 49 Jan 12, 2022
Minimal and idiomatic WebSocket library for Go

websocket websocket is a minimal and idiomatic WebSocket library for Go. Install go get nhooyr.io/websocket Highlights Minimal and idiomatic API First

Anmol Sethi 2.3k Jun 24, 2022
A go library for consuming Binance Websocket Market Streams

binancestream A go library for consuming Binance Websocket Market Streams This library handles network failures by automatically reconnecting to the w

Yasin Özel 1 Nov 1, 2021
A fast, well-tested and widely used WebSocket implementation for Go.

Gorilla WebSocket Gorilla WebSocket is a Go implementation of the WebSocket protocol. Documentation API Reference Chat example Command example Client

Gorilla Web Toolkit 17.4k Jun 26, 2022
Turn any program that uses STDIN/STDOUT into a WebSocket server. Like inetd, but for WebSockets.

websocketd websocketd is a small command-line tool that will wrap an existing command-line interface program, and allow it to be accessed via a WebSoc

Joe Walnes 16.3k Jun 27, 2022
WebSocket Command Line Client written in Go

ws-cli WebSocket Command Line Client written in Go Installation go get github.com/kseo/ws-cli Usage $ ws-cli -url ws://echo.websocket.org connected (

Kwang Yul Seo 17 Nov 12, 2021
proxy your traffic through CDN using websocket

go-cdn2proxy proxy your traffic through CDN using websocket what does it do example server client thanks what does it do you can use this as a library

jm33-ng 39 Jun 25, 2022
Chat bots (& more) for Zoom by figuring out their websocket protocol

zoomer - Bot library for Zoom meetings Good bot support is part of what makes Discord so nice to use. Unfortunately, the official Zoom API is basicall

Christopher Tarry 45 May 11, 2022
Simple example for using Turbos Streams in Go with the Gorilla WebSocket toolkit.

Go Example for TurboStreams over WebSockets Simple example for using Turbos Streams in Go with the Gorilla WebSocket toolkit.

Jan Stamer 23 Jun 8, 2022
:notes: Minimalist websocket framework for Go

melody ?? Minimalist websocket framework for Go. Melody is websocket framework based on github.com/gorilla/websocket that abstracts away the tedious p

Ola 2.5k Jun 27, 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 386 Jun 22, 2022
Terminal on browser via websocket

Terminal on browser via websocket. Supportted OS Linux Mac

skanehira 139 Jun 12, 2022
run shell scripts by websocket with go lauguage

go_shell_socket run shell scripts by websocket with go lauguage Usage pull project get gin and websocket with go get config config.json file build it

soQ 20 Mar 9, 2022
simpleChatInGo - This is a simple chat that i made for fun asnd learn more about websocket

simpleChatInGo This is a simple chat that i made for fun asnd learn more about websocket deploy For deploy this you only need to run the command : $ d

ranon rat 6 Feb 8, 2022
gatews - Gate.io WebSocket SDK

gatews - Gate.io WebSocket SDK gatews provides new Gate.io WebSocket V4 implementations. It is intended to work along with gateapi-* series to provide

gate.io 48 Jun 30, 2022
WebSocket for fasthttp

websocket WebSocket library for fasthttp and net/http. Checkout examples to inspire yourself. Install go get github.com/dgrr/websocket Why another Web

Darío 35 Jun 2, 2022
An online multiplayer, websocket based, interpretation of the Tic Tac Toe minigame from "Machinarium" :D

Tik Tak Toe An interpretation of the tic tac toe minigame from Amanita Design's Machinarium, multiplayer, online, as a website. Here's a screenshot of

null 1 Oct 28, 2021