{"id":13827050,"url":"https://github.com/romshark/webwire-go","last_synced_at":"2025-12-15T02:21:54.867Z","repository":{"uuid":"121677904","full_name":"romshark/webwire-go","owner":"romshark","description":"A transport independent asynchronous duplex messaging library for Go","archived":true,"fork":false,"pushed_at":"2019-01-15T11:29:23.000Z","size":5074,"stargazers_count":216,"open_issues_count":4,"forks_count":9,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-04-16T01:43:43.450Z","etag":null,"topics":["go","golang","messaging","request-response","server-client","server-client-communication","sessions","websockets"],"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/romshark.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-15T20:16:40.000Z","updated_at":"2024-04-11T17:02:40.000Z","dependencies_parsed_at":"2022-09-05T15:41:35.292Z","dependency_job_id":null,"html_url":"https://github.com/romshark/webwire-go","commit_stats":null,"previous_names":["qbeon/webwire-go"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fwebwire-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fwebwire-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fwebwire-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romshark%2Fwebwire-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romshark","download_url":"https://codeload.github.com/romshark/webwire-go/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":196600346,"owners_count":13244182,"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","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":["go","golang","messaging","request-response","server-client","server-client-communication","sessions","websockets"],"created_at":"2024-08-04T09:01:49.202Z","updated_at":"2025-12-15T02:21:49.812Z","avatar_url":"https://github.com/romshark.png","language":"Go","funding_links":["https://opencollective.com/webwire"],"categories":["Go"],"sub_categories":[],"readme":"\u003c!-- HEADER --\u003e\n\u003ch1 align=\"center\"\u003e\n\t\u003cbr\u003e\n\t\u003ca href=\"https://github.com/qbeon/webwire-go\"\u003e\u003cimg src=\"https://cdn.rawgit.com/qbeon/webwire-go/c7c2c74e/docs/img/webwire_logo.svg\" alt=\"WebWire\" width=\"256\"\u003e\u003c/a\u003e\n\t\u003cbr\u003e\n\t\u003cbr\u003e\n\tWebWire for \u003ca href=\"https://golang.org/\"\u003eGo\u003c/a\u003e\n\t\u003cbr\u003e\n\t\u003csub\u003eAn asynchronous duplex messaging library\u003c/sub\u003e\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://travis-ci.org/qbeon/webwire-go\"\u003e\n\t\t\u003cimg src=\"https://travis-ci.org/qbeon/webwire-go.svg?branch=master\" alt=\"Travis CI: build status\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://coveralls.io/github/qbeon/webwire-go?branch=master\"\u003e\n\t\t\u003cimg src=\"https://coveralls.io/repos/github/qbeon/webwire-go/badge.svg?branch=master\" alt=\"Coveralls: Test Coverage\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://goreportcard.com/report/github.com/qbeon/webwire-go\"\u003e\n\t\t\u003cimg src=\"https://goreportcard.com/badge/github.com/qbeon/webwire-go\" alt=\"GoReportCard\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://codebeat.co/projects/github-com-qbeon-webwire-go-master\"\u003e\n\t\t\u003cimg src=\"https://codebeat.co/badges/809181da-797c-4cdd-bb23-d0324935f3b0\" alt=\"CodeBeat: Status\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://codeclimate.com/github/qbeon/webwire-go/maintainability\"\u003e\n\t\t\u003cimg src=\"https://api.codeclimate.com/v1/badges/243a45cacec7d850c64d/maintainability\" alt=\"CodeClimate: Maintainability\"\u003e\n\t\u003c/a\u003e\n\t\u003cbr\u003e\n\t\u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/License-MIT-green.svg\" alt=\"Licence: MIT\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://app.fossa.io/projects/git%2Bgithub.com%2Fqbeon%2Fwebwire-go?ref=badge_shield\" alt=\"FOSSA Status\"\u003e\n\t\t\u003cimg src=\"https://app.fossa.io/api/projects/git%2Bgithub.com%2Fqbeon%2Fwebwire-go.svg?type=shield\"/\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://godoc.org/github.com/qbeon/webwire-go\"\u003e\n\t\t\u003cimg src=\"https://godoc.org/github.com/qbeon/webwire-go?status.svg\" alt=\"GoDoc\"\u003e\n\t\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://opencollective.com/webwire\"\u003e\n\t\t\u003cimg src=\"https://opencollective.com/webwire/tiers/backer.svg?avatarHeight=64\" alt=\"OpenCollective\"\u003e\n\t\u003c/a\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\n\u003c!-- CONTENT --\u003e\nWebWire is a high-performance transport independent asynchronous duplex messaging library and an open source binary message protocol with builtin authentication and support for UTF8 and UTF16 encoding.\nThe [webwire-go](https://github.com/qbeon/webwire-go) library provides a server implementation for the Go programming language.\n\u003cbr\u003e\n\n#### Table of Contents\n- [Installation](#installation)\n\t- [Dep](#dep)\n\t- [Go Get](#go-get)\n- [Contribution](#contribution)\n\t- [Maintainers](#maintainers)\n- [WebWire Binary Protocol](#webwire-binary-protocol)\n- [Examples](#examples)\n- [Features](#features)\n\t- [Request-Reply](#request-reply)\n\t- [Client-side Signals](#client-side-signals)\n\t- [Server-side Signals](#server-side-signals)\n\t- [Namespaces](#namespaces)\n\t- [Sessions](#sessions)\n\t- [Concurrency](#concurrency)\n\t- [Hooks](#hooks)\n\t\t- [Server-side Hooks](#server-side-hooks)\n\t\t- [SessionManager Hooks](#sessionmanager-hooks)\n\t\t- [SessionKeyGenerator Hooks](#sessionkeygenerator-hooks)\n\t- [Graceful Shutdown](#graceful-shutdown)\n\t- [Multi-Language Support](#multi-language-support)\n\t- [Security](#security)\n\n\n## Installation\nChoose any stable release from [the available release tags](https://github.com/qbeon/webwire-go/releases) and copy the source code into your project's vendor directory: ```$YOURPROJECT/vendor/github.com/qbeon/webwire-go```. All necessary transitive [dependencies](https://github.com/qbeon/webwire-go#dependencies) are already embedded into the `webwire-go` repository.\n\n### Dep\nIf you're using [dep](https://github.com/golang/dep), just use [dep ensure](https://golang.github.io/dep/docs/daily-dep.html#adding-a-new-dependency) to add a specific version of webwire-go including all its transitive dependencies to your project: ```dep ensure -add github.com/qbeon/webwire-go@v1.0.0-rc1```. This will remove all embedded transitive dependencies and move them to your projects `vendor` directory.\n\n### Go Get\nYou can also use [go get](https://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependencies): ```go get github.com/qbeon/webwire-go``` but beware that this will fetch the latest commit of the [master branch](https://github.com/qbeon/webwire-go/commits/master) which is currently **not yet** considered a stable release branch. It's therefore recommended to use [dep](https://github.com/qbeon/webwire-go#dep) instead.\n\n## Contribution\nContribution of any kind is always welcome and appreciated, check out our [Contribution Guidelines](https://github.com/qbeon/webwire-go/blob/master/CONTRIBUTING.md) for more information!\n\n### Maintainers\n| Maintainer | Role | Specialization |\n| :--- | :--- | :--- |\n| **[Roman Sharkov](https://github.com/romshark)** | Core Maintainer | Dev (Go, JavaScript) |\n| **[Daniil Trishkin](https://github.com/FromZeus)** | CI Maintainer | DevOps |\n\n## WebWire Binary Protocol\nWebWire is built for speed and portability implementing an open source [binary protocol](https://github.com/qbeon/webwire-go/blob/master/docs/protocol-sequences.svg).\n![Protocol Subset Diagram](https://github.com/qbeon/webwire-go/blob/master/docs/img/wwr_msgproto_diagram.svg)\n\nThe first byte defines the [type of the message](https://github.com/qbeon/webwire-go/blob/master/message/message.go#L91). Requests and replies contain an incremental 8-byte identifier that must be unique in the context of the senders' session. A 0 to 255 bytes long 7-bit ASCII encoded name is contained in the header of a signal or request message.\nA header-padding byte is applied in case of UTF16 payload encoding to properly align the payload sequence.\nFraudulent messages are recognized by analyzing the message length, out-of-range memory access attacks are therefore prevented.\n\n## Examples\n- **[Echo](https://github.com/qbeon/webwire-go-examples/tree/master/echo)** - Demonstrates a simple request-reply implementation using the [Go client](https://github.com/qbeon/webwire-go-client).\n\n- **[Pub-Sub](https://github.com/qbeon/webwire-go-examples/tree/master/pubsub)** - Demonstrantes a simple publisher-subscriber tolopology using the [Go client](https://github.com/qbeon/webwire-go-client).\n\n- **[Chat Room](https://github.com/qbeon/webwire-go-examples/tree/master/chatroom)** - Demonstrates advanced use of the library. The corresponding [JavaScript Chat Room Client](https://github.com/qbeon/webwire-js/tree/master/examples/chatroom-client-vue) is implemented with the [Vue.js framework](https://vuejs.org/).\n\n## Features\n### Request-Reply\nClients can initiate multiple simultaneous requests and receive replies asynchronously. Requests are multiplexed through the connection similar to HTTP2 pipelining. The below examples are using the [webwire Go client](https://github.com/qbeon-webwire-go-client).\n\n```go\n// Send a request to the server,\n// this will block the goroutine until either a reply is received\n// or the default timeout triggers (if there is one)\nreply, err := client.Request(\n\tcontext.Background(), // No cancelation, default timeout\n\tnil,                  // No name\n\twwr.Payload{\n\t\tData: []byte(\"sudo rm -rf /\"), // Binary request payload\n\t},\n)\ndefer reply.Close() // Close the reply\nif err != nil {\n\t// Oh oh, the request failed for some reason!\n}\nreply.PayloadUtf8() // Here we go!\n ```\n\nRequests will respect cancelable contexts and deadlines\n\n```go\ncancelableCtx, cancel := context.WithCancel(context.Background())\ndefer cancel()\ntimedCtx, cancelTimed := context.WithTimeout(cancelableCtx, 1*time.Second)\ndefer cancelTimed()\n\n// Send a cancelable request to the server with a 1 second deadline\n// will block the goroutine for 1 second at max\nreply, err := client.Request(timedCtx, nil, wwr.Payload{\n\tEncoding: wwr.EncodingUtf8,\n\tData:     []byte(\"hurry up!\"),\n})\ndefer reply.Close()\n\n// Investigate errors manually...\nswitch err.(type) {\ncase wwr.ErrCanceled:\n\t// Request was prematurely canceled by the sender\ncase wwr.ErrDeadlineExceeded:\n\t// Request timed out, server didn't manage to reply\n\t// within the user-specified context deadline\ncase wwr.TimeoutErr:\n\t// Request timed out, server didn't manage to reply\n\t// within the specified default request timeout duration\ncase nil:\n\t// Replied successfully\n}\n\n// ... or check for a timeout error the easier way:\nif err != nil {\n\tif wwr.IsErrTimeout(err) {\n\t\t// Timed out due to deadline excess or default timeout\n\t} else {\n\t\t// Unexpected error\n\t}\n}\n\nreply // Just in time!\n```\n\n### Client-side Signals\nIndividual clients can send signals to the server. Signals are one-way messages guaranteed to arrive, though they're not guaranteed to be processed like requests are. In cases such as when the server is being shut down, incoming signals are ignored by the server and dropped while requests will acknowledge the failure. The below examples are using the [webwire Go client](https://github.com/qbeon-webwire-go-client).\n\n```go\n// Send signal to server\nerr := client.Signal(\n\t[]byte(\"eventA\"),\n\twwr.Payload{\n\t\tEncoding: wwr.EncodingUtf8,\n\t\tData:     []byte(\"something\"),\n\t},\n)\n```\n\n### Server-side Signals\nThe server also can send signals to individual connected clients.\n\n```go\nfunc OnRequest(\n  _ context.Context,\n  conn wwr.Connection,\n  _ wwr.Message,\n) (wwr.Payload, error) {\n\t// Send a signal to the client before replying to the request\n\tconn.Signal(\n\t\tnil, // No message name\n\t\twwr.Payload{\n\t\t\tEncoding: wwr.EncodingUtf8,\n\t\t\tData:     []byte(\"example\")),\n\t\t},\n\t)\n\n\t// Reply nothing\n\treturn wwr.Payload{}, nil\n}\n```\n\n### Namespaces\nDifferent kinds of requests and signals can be differentiated using the builtin namespacing feature.\n\n```go\nfunc OnRequest(\n\t_ context.Context,\n\t_ wwr.Connection,\n\tmsg wwr.Message,\n) (wwr.Payload, error) {\n\tswitch msg.Name() {\n\tcase \"auth\":\n\t\t// Authentication request\n\t\treturn wwr.Payload{\n\t\t\tEncoding: wwr.EncodingUtf8,\n      \t\t\tData:     []byte(\"this is an auth request\"),\n\t\t}\n\tcase \"query\":\n\t\t// Query request\n\t\treturn wwr.Payload{\n\t\t\tEncoding: wwr.EncodingUtf8,\n\t\t\tData:     []byte(\"this is a query request\"),\n\t\t}\n\t}\n\n\t// Otherwise return nothing\n\treturn wwr.Payload{}, nil\n}\n```\n```go\nfunc OnSignal(\n\t_ context.Context,\n\t_ wwr.Connection,\n\tmsg wwr.Message,\n) {\n\tswitch string(msg.Name()) {\n\tcase \"event A\":\n\t\t// handle event A\n\tcase \"event B\":\n\t\t// handle event B\n\t}\n}\n```\n\n### Sessions\nIndividual connections can get sessions assigned to identify them. The state of the session is automagically synchronized between the client and the server. WebWire doesn't enforce any kind of authentication technique though, it just provides a way to authenticate a connection. WebWire also doesn't enforce any kind of session storage, the user could implement a custom session manager implementing the WebWire `SessionManager` interface to use any kind of volatile or persistent session storage, be it a database or a simple in-memory map.\n\n```go\nfunc OnRequest(\n\t_ context.Context,\n\tconn wwr.Connection,\n\tmsg wwr.Message,\n) (wwr.Payload, error) {\n\t// Verify credentials\n\tif string(msg.Payload()) != \"secret:pass\" {\n\t\treturn wwr.Payload{}, wwr.ReqErr {\n\t\t\tCode:    \"WRONG_CREDENTIALS\",\n\t\t\tMessage: \"Incorrect username or password, try again\",\n\t\t}\n\t}\n\t// Create session (will automatically synchronize to the client)\n\terr := conn.CreateSession(/*something that implements wwr.SessionInfo*/)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Couldn't create session for some reason\")\n\t}\n\n\t// Complete request, reply nothing\n\treturn wwr.Payload{}, nil\n}\n```\n\nWebWire provides a basic file-based session manager implementation out of the box used by default when no custom session manager is defined. The default session manager creates a file with a .wwrsess extension for each opened session in the configured directory (which, by default, is the directory of the executable). During the restoration of a session the file is looked up by name using the session key, read and unmarshalled recreating the session object.\n\n### Concurrency\nMessages are parsed and handled concurrently in a separate goroutine by default. The total number of concurrently executed handlers can be independently throttled down for each individual connection, which is unlimited by default.\n\nAll exported interfaces provided by both the server and the client are thread safe and can thus safely be used concurrently from within multiple goroutines, the library automatically synchronizes all concurrent operations.\n\n### Hooks\nVarious hooks provide the ability to asynchronously react to different kinds of events and control the behavior of both the client and the server.\n\n#### Server-side Hooks\n- OnClientConnected\n- OnClientDisconnected\n- OnSignal\n- OnRequest\n\n#### SessionManager Hooks\n- OnSessionCreated\n- OnSessionLookup\n- OnSessionClosed\n\n#### SessionKeyGenerator Hooks\n- Generate\n\n### Graceful Shutdown\nThe server will finish processing all ongoing signals and requests before closing when asked to shut down.\n```go\n// Will block until all handlers have finished\nserver.Shutdown()\n```\nWhile the server is shutting down new connections are refused with `503 Service Unavailable` and incoming new requests from connected clients will be rejected with a special error: `RegErrSrvShutdown`. Any incoming signals from connected clients will be ignored during the shutdown.\n\nServer-side client connections also support graceful shutdown, a connection will be closed when all work on it is done,\nwhile incoming requests and signals are handled similarly to shutting down the server.\n```go\n// Will block until all work on this connection is done\nconnection.Close()\n```\n\n### Multi-Language Support\nThe following libraries provide seamless support for various development environments providing fully compliant protocol implementations supporting the latest features.\n- **Go (server \u0026 client)**: An [official Go client](https://github.com/qbeon/webwire-go-client) implementation is available.\n- **JavaScript (client)**: An [official JavaScript library](https://github.com/qbeon/webwire-js) enables seamless support for various JavaScript environments ([93% of web-browsers](https://caniuse.com/#search=websockets) \u0026 [Node.js](https://nodejs.org/en/)) providing a fully compliant client implementation (requires a websocket-based transport implementation such as [qbeon/webwire-go-gorilla](https://github.com/qbeon/webwire-go-gorilla) or [qbeon/webwire-go-fasthttp](https://github.com/qbeon/webwire-go-fasthttp)).\n\n### Security\nA webwire server can be hosted by a [TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) protected server transport implementation to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) as well as to verify the identity of the server during connection establishment. Setting up a TLS protected websocket server for example is easy:\n```go\n// Setup a secure webwire server instance\nserver, err := wwr.NewServer(\n\tserverImplementation,\n\twwr.ServerOptions{\n\t\tHost: \"localhost:443\",\n\t},\n\t// Use a TLS protected transport layer\n\t\u0026wwrgorilla.Transport{\n\t\tTLS: \u0026wwrgorilla.TLS{\n\t\t\t// Provide key and certificate\n\t\t\tCertFilePath:       \"path/to/certificate.crt\",\n\t\t\tPrivateKeyFilePath: \"path/to/private.key\",\n\t\t\t// Specify TLS configs\n\t\t\tConfig: \u0026tls.Config{\n\t\t\t\tMinVersion:               tls.VersionTLS12,\n\t\t\t\tCurvePreferences:         []tls.CurveID{tls.X25519, tls.CurveP256},\n\t\t\t\tPreferServerCipherSuites: true,\n\t\t\t\tCipherSuites: []uint16{\n\t\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,\n\t\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,\n\t\t\t\t\ttls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n)\nif err != nil {\n\tpanic(fmt.Errorf(\"failed setting up wwr server: %s\", err))\n}\n// Launch\nif err := server.Run(); err != nil {\n\tpanic(fmt.Errorf(\"wwr server failed: %s\", err))\n}\n```\nThe above code example is using the [webwire-go-gorilla](https://github.com/qbeon/webwire-go-gorilla) transport implementation.\n\n----\n\n© 2018 Roman Sharkov \u003croman.sharkov@qbeon.com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2Fwebwire-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromshark%2Fwebwire-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromshark%2Fwebwire-go/lists"}