{"id":13416845,"url":"https://github.com/gobwas/ws","last_synced_at":"2025-09-09T20:43:30.580Z","repository":{"id":37944539,"uuid":"79746008","full_name":"gobwas/ws","owner":"gobwas","description":"Tiny WebSocket library for Go.","archived":false,"fork":false,"pushed_at":"2025-09-07T16:51:21.000Z","size":454,"stargazers_count":6344,"open_issues_count":21,"forks_count":384,"subscribers_count":123,"default_branch":"master","last_synced_at":"2025-09-07T18:40:39.413Z","etag":null,"topics":["fast","go","golang","rfc-6455","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/gobwas.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-01-22T21:11:53.000Z","updated_at":"2025-09-07T16:51:18.000Z","dependencies_parsed_at":"2023-02-19T02:31:12.135Z","dependency_job_id":"328ccba3-d270-49d8-a788-f62a89bc8e9e","html_url":"https://github.com/gobwas/ws","commit_stats":{"total_commits":293,"total_committers":23,"mean_commits":12.73913043478261,"dds":"0.21843003412969286","last_synced_commit":"4f030072d3475c0a99f5908553997de71bb70d06"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/gobwas/ws","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobwas%2Fws","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobwas%2Fws/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobwas%2Fws/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobwas%2Fws/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gobwas","download_url":"https://codeload.github.com/gobwas/ws/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gobwas%2Fws/sbom","scorecard":{"id":433100,"data":{"date":"2025-08-11","repo":{"name":"github.com/gobwas/ws","commit":"4f030072d3475c0a99f5908553997de71bb70d06"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Code-Review","score":3,"reason":"Found 9/25 approved changesets -- score normalized to 3","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":"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":10,"reason":"no dangerous workflow patterns detected","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":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/autobahn.yml:1","Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"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":"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":"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":"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":"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":"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/autobahn.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/gobwas/ws/autobahn.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/autobahn.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/gobwas/ws/autobahn.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/autobahn.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/gobwas/ws/autobahn.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/gobwas/ws/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/gobwas/ws/main.yml/master?enable=pin","Warn: containerImage not pinned by hash: autobahn/docker/autobahn/Dockerfile:3: pin your Docker image by updating pypy:2-slim to pypy:2-slim@sha256:740256f4ff8a97e103bb68cb897d063aec290546fec22547f86d366c3bc09244","Warn: containerImage not pinned by hash: autobahn/docker/server/Dockerfile:1: pin your Docker image by updating golang:1.20.2-alpine3.17 to golang:1.20.2-alpine3.17@sha256:87734b78d26a52260f303cf1b40df45b0797f972bd0250e56937c42114bf472c","Warn: pipCommand not pinned by hash: autobahn/docker/autobahn/Dockerfile:12-13","Warn: pipCommand not pinned by hash: autobahn/docker/autobahn/Dockerfile:12-13","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 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"}}]},"last_synced_at":"2025-08-19T03:51:11.649Z","repository_id":37944539,"created_at":"2025-08-19T03:51:11.649Z","updated_at":"2025-08-19T03:51:11.649Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274166473,"owners_count":25233957,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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","go","golang","rfc-6455","websocket"],"created_at":"2024-07-30T22:00:23.480Z","updated_at":"2025-09-09T20:43:30.543Z","avatar_url":"https://github.com/gobwas.png","language":"Go","readme":"# ws\n\n[![GoDoc][godoc-image]][godoc-url]\n[![CI][ci-badge]][ci-url]\n\n\u003e [RFC6455][rfc-url] WebSocket implementation in Go.\n\n# Features\n\n- Zero-copy upgrade\n- No intermediate allocations during I/O\n- Low-level API which allows to build your own logic of packet handling and\n  buffers reuse\n- High-level wrappers and helpers around API in `wsutil` package, which allow\n  to start fast without digging the protocol internals\n\n# Documentation\n\n[GoDoc][godoc-url].\n\n# Why\n\nExisting WebSocket implementations do not allow users to reuse I/O buffers\nbetween connections in clear way. This library aims to export efficient\nlow-level interface for working with the protocol without forcing only one way\nit could be used.\n\nBy the way, if you want get the higher-level tools, you can use `wsutil`\npackage.\n\n# Status\n\nLibrary is tagged as `v1*` so its API must not be broken during some\nimprovements or refactoring.\n\nThis implementation of RFC6455 passes [Autobahn Test\nSuite](https://github.com/crossbario/autobahn-testsuite) and currently has\nabout 78% coverage.\n\n# Examples\n\nExample applications using `ws` are developed in separate repository\n[ws-examples](https://github.com/gobwas/ws-examples).\n\n# Usage\n\nThe higher-level example of WebSocket echo server:\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/gobwas/ws/wsutil\"\n)\n\nfunc main() {\n\thttp.ListenAndServe(\":8080\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tconn, _, _, err := ws.UpgradeHTTP(r, w)\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\t\tgo func() {\n\t\t\tdefer conn.Close()\n\n\t\t\tfor {\n\t\t\t\tmsg, op, err := wsutil.ReadClientData(conn)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t\terr = wsutil.WriteServerMessage(conn, op, msg)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}))\n}\n```\n\nLower-level, but still high-level example:\n\n\n```go\nimport (\n\t\"net/http\"\n\t\"io\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/gobwas/ws/wsutil\"\n)\n\nfunc main() {\n\thttp.ListenAndServe(\":8080\", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tconn, _, _, err := ws.UpgradeHTTP(r, w)\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\t\tgo func() {\n\t\t\tdefer conn.Close()\n\n\t\t\tvar (\n\t\t\t\tstate  = ws.StateServerSide\n\t\t\t\treader = wsutil.NewReader(conn, state)\n\t\t\t\twriter = wsutil.NewWriter(conn, state, ws.OpText)\n\t\t\t)\n\t\t\tfor {\n\t\t\t\theader, err := reader.NextFrame()\n\t\t\t\tif err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\n\t\t\t\t// Reset writer to write frame with right operation code.\n\t\t\t\twriter.Reset(conn, state, header.OpCode)\n\n\t\t\t\tif _, err = io.Copy(writer, reader); err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t\tif err = writer.Flush(); err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}))\n}\n```\n\nWe can apply the same pattern to read and write structured responses through a JSON encoder and decoder.:\n\n```go\n\t...\n\tvar (\n\t\tr = wsutil.NewReader(conn, ws.StateServerSide)\n\t\tw = wsutil.NewWriter(conn, ws.StateServerSide, ws.OpText)\n\t\tdecoder = json.NewDecoder(r)\n\t\tencoder = json.NewEncoder(w)\n\t)\n\tfor {\n\t\thdr, err = r.NextFrame()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif hdr.OpCode == ws.OpClose {\n\t\t\treturn io.EOF\n\t\t}\n\t\tvar req Request\n\t\tif err := decoder.Decode(\u0026req); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar resp Response\n\t\tif err := encoder.Encode(\u0026resp); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = w.Flush(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t...\n```\n\nThe lower-level example without `wsutil`:\n\n```go\npackage main\n\nimport (\n\t\"net\"\n\t\"io\"\n\n\t\"github.com/gobwas/ws\"\n)\n\nfunc main() {\n\tln, err := net.Listen(\"tcp\", \"localhost:8080\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfor {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\t\t_, err = ws.Upgrade(conn)\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\n\t\tgo func() {\n\t\t\tdefer conn.Close()\n\n\t\t\tfor {\n\t\t\t\theader, err := ws.ReadHeader(conn)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\n\t\t\t\tpayload := make([]byte, header.Length)\n\t\t\t\t_, err = io.ReadFull(conn, payload)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t\tif header.Masked {\n\t\t\t\t\tws.Cipher(payload, header.Mask, 0)\n\t\t\t\t}\n\n\t\t\t\t// Reset the Masked flag, server frames must not be masked as\n\t\t\t\t// RFC6455 says.\n\t\t\t\theader.Masked = false\n\n\t\t\t\tif err := ws.WriteHeader(conn, header); err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\t\t\t\tif _, err := conn.Write(payload); err != nil {\n\t\t\t\t\t// handle error\n\t\t\t\t}\n\n\t\t\t\tif header.OpCode == ws.OpClose {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n}\n```\n\n# Zero-copy upgrade\n\nZero-copy upgrade helps to avoid unnecessary allocations and copying while\nhandling HTTP Upgrade request.\n\nProcessing of all non-websocket headers is made in place with use of registered\nuser callbacks whose arguments are only valid until callback returns.\n\nThe simple example looks like this:\n\n```go\npackage main\n\nimport (\n\t\"net\"\n\t\"log\"\n\n\t\"github.com/gobwas/ws\"\n)\n\nfunc main() {\n\tln, err := net.Listen(\"tcp\", \"localhost:8080\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tu := ws.Upgrader{\n\t\tOnHeader: func(key, value []byte) (err error) {\n\t\t\tlog.Printf(\"non-websocket header: %q=%q\", key, value)\n\t\t\treturn\n\t\t},\n\t}\n\tfor {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\n\t\t_, err = u.Upgrade(conn)\n\t\tif err != nil {\n\t\t\t// handle error\n\t\t}\n\t}\n}\n```\n\nUsage of `ws.Upgrader` here brings ability to control incoming connections on\ntcp level and simply not to accept them by some logic.\n\nZero-copy upgrade is for high-load services which have to control many\nresources such as connections buffers.\n\nThe real life example could be like this:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net\"\n\t\"net/http\"\n\t\"runtime\"\n\n\t\"github.com/gobwas/httphead\"\n\t\"github.com/gobwas/ws\"\n)\n\nfunc main() {\n\tln, err := net.Listen(\"tcp\", \"localhost:8080\")\n\tif err != nil {\n\t\t// handle error\n\t}\n\n\t// Prepare handshake header writer from http.Header mapping.\n\theader := ws.HandshakeHeaderHTTP(http.Header{\n\t\t\"X-Go-Version\": []string{runtime.Version()},\n\t})\n\n\tu := ws.Upgrader{\n\t\tOnHost: func(host []byte) error {\n\t\t\tif string(host) == \"github.com\" {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn ws.RejectConnectionError(\n\t\t\t\tws.RejectionStatus(403),\n\t\t\t\tws.RejectionHeader(ws.HandshakeHeaderString(\n\t\t\t\t\t\"X-Want-Host: github.com\\r\\n\",\n\t\t\t\t)),\n\t\t\t)\n\t\t},\n\t\tOnHeader: func(key, value []byte) error {\n\t\t\tif string(key) != \"Cookie\" {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tok := httphead.ScanCookie(value, func(key, value []byte) bool {\n\t\t\t\t// Check session here or do some other stuff with cookies.\n\t\t\t\t// Maybe copy some values for future use.\n\t\t\t\treturn true\n\t\t\t})\n\t\t\tif ok {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn ws.RejectConnectionError(\n\t\t\t\tws.RejectionReason(\"bad cookie\"),\n\t\t\t\tws.RejectionStatus(400),\n\t\t\t)\n\t\t},\n\t\tOnBeforeUpgrade: func() (ws.HandshakeHeader, error) {\n\t\t\treturn header, nil\n\t\t},\n\t}\n\tfor {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\t_, err = u.Upgrade(conn)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"upgrade error: %s\", err)\n\t\t}\n\t}\n}\n```\n\n# Compression\n\nThere is a `ws/wsflate` package to support [Permessage-Deflate Compression\nExtension][rfc-pmce].\n\nIt provides minimalistic I/O wrappers to be used in conjunction with any\ndeflate implementation (for example, the standard library's\n[compress/flate][compress/flate]).\n\nIt is also compatible with `wsutil`'s reader and writer by providing\n`wsflate.MessageState` type, which implements `wsutil.SendExtension` and\n`wsutil.RecvExtension` interfaces.\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/gobwas/ws/wsflate\"\n)\n\nfunc main() {\n\tln, err := net.Listen(\"tcp\", \"localhost:8080\")\n\tif err != nil {\n\t\t// handle error\n\t}\n\te := wsflate.Extension{\n\t\t// We are using default parameters here since we use\n\t\t// wsflate.{Compress,Decompress}Frame helpers below in the code.\n\t\t// This assumes that we use standard compress/flate package as flate\n\t\t// implementation.\n\t\tParameters: wsflate.DefaultParameters,\n\t}\n\tu := ws.Upgrader{\n\t\tNegotiate: e.Negotiate,\n\t}\n\tfor {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\t// Reset extension after previous upgrades.\n\t\te.Reset()\n\n\t\t_, err = u.Upgrade(conn)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"upgrade error: %s\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := e.Accepted(); !ok {\n\t\t\tlog.Printf(\"didn't negotiate compression for %s\", conn.RemoteAddr())\n\t\t\tconn.Close()\n\t\t\tcontinue\n\t\t}\n\n\t\tgo func() {\n\t\t\tdefer conn.Close()\n\t\t\tfor {\n\t\t\t\tframe, err := ws.ReadFrame(conn)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// Handle error.\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tframe = ws.UnmaskFrameInPlace(frame)\n\n\t\t\t\tif wsflate.IsCompressed(frame.Header) {\n\t\t\t\t\t// Note that even after successful negotiation of\n\t\t\t\t\t// compression extension, both sides are able to send\n\t\t\t\t\t// non-compressed messages.\n\t\t\t\t\tframe, err = wsflate.DecompressFrame(frame)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t// Handle error.\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Do something with frame...\n\n\t\t\t\tack := ws.NewTextFrame([]byte(\"this is an acknowledgement\"))\n\n\t\t\t\t// Compress response unconditionally.\n\t\t\t\tack, err = wsflate.CompressFrame(ack)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// Handle error.\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif err = ws.WriteFrame(conn, ack); err != nil {\n\t\t\t\t\t// Handle error.\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n}\n```\n\nYou can use compression with `wsutil` package this way:\n\n```go\n\t// Upgrade somehow and negotiate compression to get the conn...\n\n\t// Initialize flate reader. We are using nil as a source io.Reader because\n\t// we will Reset() it in the message i/o loop below.\n\tfr := wsflate.NewReader(nil, func(r io.Reader) wsflate.Decompressor {\n\t\treturn flate.NewReader(r)\n\t})\n\t// Initialize flate writer. We are using nil as a destination io.Writer\n\t// because we will Reset() it in the message i/o loop below.\n\tfw := wsflate.NewWriter(nil, func(w io.Writer) wsflate.Compressor {\n\t\tf, _ := flate.NewWriter(w, 9)\n\t\treturn f\n\t})\n\n\t// Declare compression message state variable.\n\t//\n\t// It has two goals:\n\t// - Allow users to check whether received message is compressed or not.\n\t// - Help wsutil.Reader and wsutil.Writer to set/unset appropriate\n\t//   WebSocket header bits while writing next frame to the wire (it\n\t//   implements wsutil.RecvExtension and wsutil.SendExtension).\n\tvar msg wsflate.MessageState\n\n\t// Initialize WebSocket reader as previously. \n\t// Please note the use of Reader.Extensions field as well as\n\t// of ws.StateExtended flag.\n\trd := \u0026wsutil.Reader{\n\t\tSource:     conn,\n\t\tState:      ws.StateServerSide | ws.StateExtended,\n\t\tExtensions: []wsutil.RecvExtension{\n\t\t\t\u0026msg, \n\t\t},\n\t}\n\n\t// Initialize WebSocket writer with ws.StateExtended flag as well.\n\twr := wsutil.NewWriter(conn, ws.StateServerSide|ws.StateExtended, 0)\n\t// Use the message state as wsutil.SendExtension.\n\twr.SetExtensions(\u0026msg)\n\n\tfor {\n\t\th, err := rd.NextFrame()\n\t\tif err != nil {\n\t\t\t// handle error.\n\t\t}\n\t\tif h.OpCode.IsControl() {\n\t\t\t// handle control frame.\n\t\t}\n\t\tif !msg.IsCompressed() {\n\t\t\t// handle uncompressed frame (skipped for the sake of example\n\t\t\t// simplicity).\n\t\t}\n\n\t\t// Reset the writer to echo same op code.\n\t\twr.Reset(h.OpCode)\n\n\t\t// Reset both flate reader and writer to start the new round of i/o.\n\t\tfr.Reset(rd)\n\t\tfw.Reset(wr)\n\n\t\t// Copy whole message from reader to writer decompressing it and\n\t\t// compressing again.\n\t\tif _, err := io.Copy(fw, fr); err != nil {\n\t\t\t// handle error.\n\t\t}\n\t\t// Flush any remaining buffers from flate writer to WebSocket writer.\n\t\tif err := fw.Close(); err != nil {\n\t\t\t// handle error.\n\t\t}\n\t\t// Flush the whole WebSocket message to the wire.\n\t\tif err := wr.Flush(); err != nil {\n\t\t\t// handle error.\n\t\t}\n\t}\n```\n\n\n[rfc-url]: https://tools.ietf.org/html/rfc6455\n[rfc-pmce]: https://tools.ietf.org/html/rfc7692#section-7\n[godoc-image]: https://godoc.org/github.com/gobwas/ws?status.svg\n[godoc-url]: https://godoc.org/github.com/gobwas/ws\n[compress/flate]: https://golang.org/pkg/compress/flate/\n[ci-badge]:    https://github.com/gobwas/ws/workflows/CI/badge.svg\n[ci-url]:      https://github.com/gobwas/ws/actions?query=workflow%3ACI\n","funding_links":[],"categories":["Popular","开源类库","Go","Middlewares \u0026 framework add-ons","Open source library","Tools per Language","网络信息服务"],"sub_categories":["WebSocket","Go","网络协议"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgobwas%2Fws","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgobwas%2Fws","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgobwas%2Fws/lists"}