{"id":44562216,"url":"https://github.com/dgrr/websocket","last_synced_at":"2026-02-13T23:43:19.648Z","repository":{"id":46542022,"uuid":"382922766","full_name":"dgrr/websocket","owner":"dgrr","description":"WebSocket for fasthttp","archived":false,"fork":false,"pushed_at":"2025-03-10T16:59:37.000Z","size":61,"stargazers_count":68,"open_issues_count":2,"forks_count":14,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-11-23T02:21:27.506Z","etag":null,"topics":["fast","fasthttp","golang","high-performance","websocket"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dgrr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-04T18:32:29.000Z","updated_at":"2025-11-23T00:07:33.000Z","dependencies_parsed_at":"2024-06-18T17:08:37.171Z","dependency_job_id":"0430f8cc-7161-40b0-98d6-22ffa18dd7e3","html_url":"https://github.com/dgrr/websocket","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/dgrr/websocket","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgrr%2Fwebsocket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgrr%2Fwebsocket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgrr%2Fwebsocket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgrr%2Fwebsocket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dgrr","download_url":"https://codeload.github.com/dgrr/websocket/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgrr%2Fwebsocket/sbom","scorecard":{"id":339720,"data":{"date":"2025-08-11","repo":{"name":"github.com/dgrr/websocket","commit":"cd225e71ed3303e19bf7d232e4b61a5d41a135cb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 4/25 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 9 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0355 / GHSA-fx95-883v-4q4h"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T05:30:15.474Z","repository_id":46542022,"created_at":"2025-08-18T05:30:15.474Z","updated_at":"2025-08-18T05:30:15.474Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29423542,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T22:20:51.549Z","status":"ssl_error","status_checked_at":"2026-02-13T22:20:49.838Z","response_time":78,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fast","fasthttp","golang","high-performance","websocket"],"created_at":"2026-02-13T23:43:18.394Z","updated_at":"2026-02-13T23:43:19.632Z","avatar_url":"https://github.com/dgrr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# websocket\n\nWebSocket library for [fasthttp](https://github.com/valyala/fasthttp) and [net/http](https://pkg.go.dev/net/http).\n\nCheckout [examples](https://github.com/dgrr/websocket/blob/master/examples) to inspire yourself.\n\n# Install\n\n```bash\ngo get github.com/dgrr/websocket\n```\n\n# Why another WebSocket package?\n\n**Other WebSocket packages DON'T** allow concurrent Read/Write operations on servers\nand they do not provide low level access to WebSocket packet crafting.\nThose WebSocket packages try to emulate the Golang API by implementing\nio.Reader and io.Writer interfaces on their connections. io.Writer might be a\ngood idea to use it, but no io.Reader, given that WebSocket is an async protocol\nby nature (all protocols are (?)).\n\nSometimes, WebSocket servers are just cumbersome when we want to handle a lot of\nclients in an async manner. For example, **in other WebSocket packages to broadcast\na message generated internally we'll need to do the following**:\n\n```go\ntype MyWebSocketService struct {\n    clients sync.Map\n}\n\ntype BlockingConn struct {\n\tlck sync.Mutex\n\tc websocketPackage.Conn\n}\n\nfunc (ws *MyWebSocketService) Broadcaster() {\n\tfor msg := range messageProducerChannel {\n        ws.clients.Range(func(_, v interface{}) bool {\n            c := v.(*BlockingConn)\n            c.lck.Lock() // oh, we need to block, otherwise we can break the program\n            err := c.Write(msg)\n            c.lck.Unlock()\n            \n            if err != nil {\n                // we have an error, what can we do? Log it?\n            \t// if the connection has been closed we'll receive that on\n            \t// the Read call, so the connection will close automatically.\n            }\n            \n            return true\n        })\n    }\n}\n\nfunc (ws *MyWebSocketService) Handle(request, response) {\n\tc, err := websocketPackage.Upgrade(request, response)\n\tif err != nil {\n\t\t// then it's clearly an error! Report back\n    }\n    \n    bc := \u0026BlockingConn{\n        c: c,\n    }   \n    \n\tws.clients.Store(bc, struct{}{})\n    \n\t// even though I just want to write, I need to block somehow\n    for {\n    \tcontent, err := bc.Read()\n    \tif err != nil {\n            // handle the error\n            break\n        }\n    }\n    \n    ws.clients.Delete(bc)\n}\n```\n\nFirst, we need to store every client upon connection,\nand whenever we want to send data we need to iterate over a list, and send the message.\nIf while, writing we get an error, then we need to handle that client's error\nWhat if the writing operation is happening at the same time in 2 different coroutines?\nThen we need a sync.Mutex and block until we finish writing.\n\nTo solve most of those problems [websocket](https://github.com/dgrr/websocket)\nuses channels and separated coroutines, one for reading and another one for writing.\nBy following the [sharing principle](https://golang.org/doc/effective_go#sharing).\n\n\u003e Do not communicate by sharing memory; instead, share memory by communicating.\n\nFollowing the fasthttp philosophy this library tries to take as much advantage\nof the Golang's multi-threaded model as possible,\nwhile keeping your code concurrently safe.\n\nTo see an example of what this package CAN do that others DONT checkout [the broadcast example](https://github.com/dgrr/websocket/blob/master/examples/broadcast/main.go).\n\n# Server\n\n## How can I launch a server?\n\nIt's quite easy. You only need to create a [Server](https://pkg.go.dev/github.com/dgrr/websocket?utm_source=godoc#Server),\nset your callbacks by calling the [Handle*](https://pkg.go.dev/github.com/dgrr/websocket?utm_source=godoc#Server.HandleClose) methods\nand then specify your fasthttp handler as [Server.Upgrade](https://pkg.go.dev/github.com/dgrr/websocket?utm_source=godoc#Server.Upgrade).\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\n\t\"github.com/valyala/fasthttp\"\n\t\"github.com/dgrr/websocket\"\n)\n\nfunc main() {\n\tws := websocket.Server{}\n\tws.HandleData(OnMessage)\n\t\n\tfasthttp.ListenAndServe(\":8080\", ws.Upgrade)\n}\n\nfunc OnMessage(c *websocket.Conn, isBinary bool, data []byte) {\n\tfmt.Printf(\"Received data from %s: %s\\n\", c.RemoteAddr(), data)\n}\n```\n\n## How can I launch a server if I use net/http?\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\n\t\"github.com/dgrr/websocket\"\n)\n\nfunc main() {\n\tws := websocket.Server{}\n\tws.HandleData(OnMessage)\n\t\n\thttp.HandleFunc(\"/\", ws.NetUpgrade)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n\nfunc OnMessage(c *websocket.Conn, isBinary bool, data []byte) {\n\tfmt.Printf(\"Received data from %s: %s\\n\", c.RemoteAddr(), data)\n}\n```\n\n## How can I handle pings?\n\nPings are handle automatically by the library, but you can get the content of\nthose pings setting the callback using [HandlePing](https://pkg.go.dev/github.com/dgrr/websocket?utm_source=godoc#Server.HandlePing).\n\nFor example, let's try to get the round trip time to a client by using\nthe PING frame. The website [http2.gofiber.io](https://http2.gofiber.io)\nuses this method to measure the round trip time displayed at the bottom of the webpage.\n\n```go\npackage main\n\nimport (\n\t\"sync\"\n\t\"encoding/binary\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/valyala/fasthttp\"\n\t\"github.com/dgrr/websocket\"\n)\n\n// Struct to keep the clients connected\n//\n// it should be safe to access the clients concurrently from Open and Close.\ntype RTTMeasure struct {\n\tclients sync.Map\n}\n\n// just trigger the ping sender\nfunc (rtt *RTTMeasure) Start() {\n\ttime.AfterFunc(time.Second * 2, rtt.sendPings)\n}\n\nfunc (rtt *RTTMeasure) sendPings() {\n\tvar data [8]byte\n\n\tbinary.BigEndian.PutUint64(data[:], uint64(\n\t\ttime.Now().UnixNano()),\n\t)\n\n\trtt.clients.Range(func(_, v interface{}) bool {\n\t\tc := v.(*websocket.Conn)\n\t\tc.Ping(data[:])\n\t\treturn true\n\t})\n\n\trtt.Start()\n}\n\n// register a connection when it's open\nfunc (rtt *RTTMeasure) RegisterConn(c *websocket.Conn) {\n\trtt.clients.Store(c.ID(), c)\n\tlog.Printf(\"Client %s connected\\n\", c.RemoteAddr())\n}\n\n// remove the connection when receiving the close\nfunc (rtt *RTTMeasure) RemoveConn(c *websocket.Conn, err error) {\n\trtt.clients.Delete(c.ID())\n\tlog.Printf(\"Client %s disconnected\\n\", c.RemoteAddr())\n}\n\nfunc main() {\n\trtt := RTTMeasure{}\n\n\tws := websocket.Server{}\n\tws.HandleOpen(rtt.RegisterConn)\n\tws.HandleClose(rtt.RemoveConn)\n\tws.HandlePong(OnPong)\n\n\t// schedule the timer\n\trtt.Start()\n\n\tfasthttp.ListenAndServe(\":8080\", ws.Upgrade)\n}\n\n// handle the pong message\nfunc OnPong(c *websocket.Conn, data []byte) {\n\tif len(data) == 8 {\n\t\tn := binary.BigEndian.Uint64(data)\n\t\tts := time.Unix(0, int64(n))\n\n\t\tlog.Printf(\"RTT with %s is %s\\n\", c.RemoteAddr(), time.Now().Sub(ts))\n\t}\n}\n```\n\n# websocket vs gorilla vs nhooyr vs gobwas\n\n| Features | [websocket](https://github.com/dgrr/websocket) | [Gorilla](https://github.com/fasthttp/websocket)| [Nhooyr](https://github.com/nhooyr/websocket) | [gowabs](https://github.com/gobwas/ws) |\n| --- | --- | --- | --- | --- |\n| Concurrent R/W                          | Yes            | No           | No. Only writes | No           |\n| Passes Autobahn Test Suite              | Mostly         | Yes          | Yes             | Mostly       |    \n| Receive fragmented message              | Yes            | Yes          | Yes             | Yes          |\n| Send close message                      | Yes            | Yes          | Yes             | Yes          |\n| Send pings and receive pongs            | Yes            | Yes          | Yes             | Yes          |\n| Get the type of a received data message | Yes            | Yes          | Yes             | Yes          |\n| Compression Extensions                  | No             | Experimental | Yes             | No (?)       |\n| Read message using io.Reader            | No             | Yes          | No              | No (?)       |\n| Write message using io.WriteCloser      | Yes            | Yes          | No              | No (?)       |\n\n# Stress tests\n\nThe following stress test were performed without timeouts:\n\nExecuting `tcpkali --ws -c 100 -m 'hello world!!13212312!' -r 10k localhost:8081` the tests shows the following:\n\n### Websocket:\n```\nTotal data sent:     267.7 MiB (280678466 bytes)\nTotal data received: 229.5 MiB (240626600 bytes)\nBandwidth per channel: 4.167⇅ Mbps (520.9 kBps)\nAggregate bandwidth: 192.357↓, 224.375↑ Mbps\nPacket rate estimate: 247050.1↓, 61842.9↑ (1↓, 1↑ TCP MSS/op)\nTest duration: 10.0075 s.\n```\n\n### Websocket for net/http:\n```\nTotal data sent:     267.3 MiB (280320124 bytes)\nTotal data received: 228.3 MiB (239396374 bytes)\nBandwidth per channel: 4.156⇅ Mbps (519.5 kBps)\nAggregate bandwidth: 191.442↓, 224.168↑ Mbps\nPacket rate estimate: 188107.1↓, 52240.7↑ (1↓, 1↑ TCP MSS/op)\nTest duration: 10.0039 s.\n```\n\nEither for fasthttp and net/http should be quite close,\nthe only difference is the way they both upgrade.\n\n### Gorilla:\n```\nTotal data sent:     260.2 MiB (272886768 bytes)\nTotal data received: 109.3 MiB (114632982 bytes)\nBandwidth per channel: 3.097⇅ Mbps (387.1 kBps)\nAggregate bandwidth: 91.615↓, 218.092↑ Mbps\nPacket rate estimate: 109755.3↓, 66807.4↑ (1↓, 1↑ TCP MSS/op)\nTest duration: 10.01 s.\n```\n\n### Nhooyr: (Don't know why is that low)\n```\nTotal data sent:     224.3 MiB (235184096 bytes)\nTotal data received: 41.2 MiB (43209780 bytes)\nBandwidth per channel: 2.227⇅ Mbps (278.3 kBps)\nAggregate bandwidth: 34.559↓, 188.097↑ Mbps\nPacket rate estimate: 88474.0↓, 55256.1↑ (1↓, 1↑ TCP MSS/op)\nTest duration: 10.0027 s.\n```\n\n### Gobwas:\n```\nTotal data sent:     265.8 MiB (278718160 bytes)\nTotal data received: 117.8 MiB (123548959 bytes)\nBandwidth per channel: 3.218⇅ Mbps (402.2 kBps)\nAggregate bandwidth: 98.825↓, 222.942↑ Mbps\nPacket rate estimate: 148231.6↓, 72106.1↑ (1↓, 1↑ TCP MSS/op)\nTest duration: 10.0015 s.\n```\n\nThe source files are in [this](https://github.com/dgrr/websocket/tree/master/stress-tests/) folder.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgrr%2Fwebsocket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdgrr%2Fwebsocket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgrr%2Fwebsocket/lists"}