Tiny WebSocket library for Go.

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
				}
			}
		}()
	}
}
Owner
Comments
  • 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.

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

  • 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;
        }
    }
    
  • 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

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

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

  • 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!

  • 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
    	}
            ....
    }```
  • 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)

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

  • 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

  • Read and Write timeouts?

    Read and Write timeouts?

    The follow is not clear from the documentation and not immediately apparent in the code:

    Does this ws library respect the HttpServer WriteTimeout & other timeouts set on the writer that is upgraded to a gobwas/ws ?

    If not, can this be added as a feature? Implementing timeouts externally is possible but wordy.

    E.g. Specifically if WriteServerMessage() is called, will it block forever if the underlying transport is blocked?

  • Expose a func to construct header data

    Expose a func to construct header data

    Hi,your ws package helped me a lot! Thank you!

    I use this netpoll replace mailru/easygo/netpoll.

    I need a func to construct header data like this image

    Thank again

    === RUN TestWriteHeader === RUN TestWriteHeader/#0 === RUN TestWriteHeader/#1 === RUN TestWriteHeader/#2 === RUN TestWriteHeader/#3 --- PASS: TestWriteHeader (0.00s) --- PASS: TestWriteHeader/#0 (0.00s) --- PASS: TestWriteHeader/#1 (0.00s) --- PASS: TestWriteHeader/#2 (0.00s) --- PASS: TestWriteHeader/#3 (0.00s) PASS ok github.com/gobwas/ws 0.002s

  • Is it necessary to use sync.Pool to reuse Reader Object in readData?

    Is it necessary to use sync.Pool to reuse Reader Object in readData?

    Recently,I used go tool trace to test my project which uses ws v1.0.4.The log shows readData function triggers gc easily when creates "Reader".

    runtime.mallocgc:1166
    runtime.newobject:1177
    github.com/gobwas/ws/wsutil.readData:250
    github.com/gobwas/ws/wsutil.ReadData:88
    github.com/gobwas/ws/wsutil.ReadClientData:97
    

    In my project,I use a goroutine to recieve remote data.

    for {
    	select {
    	case _, ok := <-c.Stop:
    		if !ok {
    			return
    		}
    	default:
    		message, msgType, err := websocketUtil.ReadClientData(*c.Conn)
    		if err != nil {
    			break
    		}
    		c.Received <- model.ClientMessage{Type: msgType, Data: message}
    	}
    }
    

    Can optimize performance if use sync.Pool to reuse "Reader"? Reader contains many members,how to reset them properly?

  • EOF error on ws.Upgrade

    EOF error on ws.Upgrade

    I receive EOF error on ws.Upgrade during high load on a server. I took code from example and run ~2k concurrent connections to websocket server. Another thing is that I pass JWT token in URL. It seems that readLine inside Upgrade is causing error: https://github.com/gobwas/ws/blob/master/server.go#L452 and then error is returned from here: https://github.com/gobwas/ws/blob/master/util.go#L183

    Example code to reproduce the issue: Server:

    package main
    
    import (
    	"log"
    	"net"
    
    	"github.com/gobwas/ws"
    )
    
    func main() {
    	ln, err := net.Listen("tcp", ":8080")
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	for {
    		conn, err := ln.Accept()
    		if err != nil {
    			log.Fatal("accept err", err)
    			continue
    		}
    		_, err = ws.Upgrade(conn)
    		if err != nil {
    			log.Fatal("upgrade err", err)
    			continue
    		}
    	}
    }
    

    client:

    package main
    
    import (
    	"fmt"
    	"log"
    	"sync"
    
    	"github.com/gorilla/websocket"
    )
    
    func main() {
    	for {
    		var wg sync.WaitGroup
    		for i := 0; i < 2000; i++ {
    			wg.Add(1)
    			go func(i int) {
    				defer wg.Done()
    				url := "ws://localhost:8080/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
    				_, _, err := websocket.DefaultDialer.Dial(url, nil)
    				if err != nil {
    					err = fmt.Errorf("cannot dial, err: %w", err)
    					log.Println(err)
    					return
    				}
    				return
    			}(i)
    		}
    		wg.Wait()
    		fmt.Println("done")
    	}
    }
    
  • 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
    }
    
Encrypted-websocket-chat - Encrypted websocket chat using golang

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

Sep 15, 2022
Websocket-chat - A simple websocket chat application
Websocket-chat - A simple websocket chat application

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

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

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

Dec 31, 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

Aug 1, 2022
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

Jan 2, 2023
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

Dec 31, 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 (

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

Dec 7, 2022
Chat bots (& more) for Zoom by figuring out their websocket protocol
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

Dec 14, 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.

Dec 22, 2022
:notes: Minimalist websocket framework for Go
: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

Dec 23, 2022
A modern, fast and scalable websocket framework with elegant API written in Go
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

Dec 29, 2022
Terminal on browser via websocket

Terminal on browser via websocket. Supportted OS Linux Mac

Dec 27, 2022
run shell scripts by websocket with go lauguage
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

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

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

Sep 21, 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

Dec 31, 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

Dec 28, 2022
An online multiplayer, websocket based, interpretation of the Tic Tac Toe minigame from "Machinarium" :D
An online multiplayer, websocket based, interpretation of the Tic Tac Toe minigame from

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

Aug 31, 2022