{"id":17438611,"url":"https://github.com/blixt/geomys","last_synced_at":"2026-01-22T14:59:59.549Z","repository":{"id":29599844,"uuid":"33139927","full_name":"blixt/geomys","owner":"blixt","description":"A simple framework for writing servers handling many persistent clients.","archived":false,"fork":false,"pushed_at":"2015-04-07T16:25:48.000Z","size":160,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-11T15:58:39.761Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blixt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-30T18:19:19.000Z","updated_at":"2015-04-07T16:25:48.000Z","dependencies_parsed_at":"2022-08-22T09:10:13.656Z","dependency_job_id":null,"html_url":"https://github.com/blixt/geomys","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/blixt/geomys","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fgeomys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fgeomys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fgeomys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fgeomys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blixt","download_url":"https://codeload.github.com/blixt/geomys/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blixt%2Fgeomys/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28664827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T14:01:31.714Z","status":"ssl_error","status_checked_at":"2026-01-22T13:59:23.143Z","response_time":144,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":[],"created_at":"2024-10-17T12:38:30.833Z","updated_at":"2026-01-22T14:59:59.529Z","avatar_url":"https://github.com/blixt.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# geomys\nA simple framework for writing servers handling many persistent clients.\n\n## Installation\n\n```bash\ngo get github.com/blixt/geomys\n```\n\n## Example\n\nThis example shows how to listen for web sockets and broadcasting their `Chat` messages to all other connected\nsockets.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n\n    \"github.com/blixt/geomys\"\n    \"golang.org/x/net/websocket\"\n)\n\ntype Chat struct {\n    Text string\n}\n\ntype Example struct {\n    geomys.WebSocketServerBase\n    Server *geomys.Server\n}\n\n// The handler for all connected clients.\nfunc (e *Example) BroadcastHandler(i *geomys.Interface, event *geomys.Event) error {\n    if event.Type == \"message\" {\n        // Broadcast the message to all interfaces.\n        e.Server.SendAll(event.Value)\n    }\n    return nil\n}\n\n// Handles an incoming socket by returning an interface to the server.\nfunc (e *Example) GetInterface(ws *websocket.Conn) *geomys.Interface {\n    // We pass in nil as the context here, but it can be anything you want.\n    i := e.Server.NewInterface(nil)\n    // Handle all client messages with the broadcast handler.\n    i.PushHandler(e.BroadcastHandler)\n    return i\n}\n\n// Returns an empty struct for the provided type name.\nfunc (e *Example) GetMessage(msgType string) (interface{}, error) {\n    switch msgType {\n    case \"Chat\":\n        return new(Chat), nil\n    default:\n        return nil, fmt.Errorf(\"Unsupported message type %s\", msgType)\n    }\n}\n\nfunc main() {\n    example := \u0026Example{Server: geomys.NewServer()}\n\n    fmt.Println(\"Starting server on port 1337...\")\n    // Example implements geomys.WebSocketServer which makes web sockets easy.\n    http.Handle(\"/socket\", geomys.WebSocketHandler(example))\n    http.ListenAndServe(\":1337\", nil)\n}\n```\n\n## Concepts\n\n### Message\n\nA simple typed data structure (a simple Go struct / JSON object).\n\n### Interface\n\nAn interface between clients and the server. Usually there will be one `Interface` instance per client. The\ninterface holds a stack of one or more handlers which handle events such as incoming messages from the client. The\nhandlers may send messages back to the client using the interface's `Send` method.\n\nThe interface also has a `Context` field which can be set to any value. This can for example be set to a session\nobject representing the user that the interface communicates with.\n\n### Handler\n\nA function which handles an event, such as a message coming from the client. The handler has access to the interface\nand may send a message to the client using the `Send` method or dispatch more events with the `Dispatch` method.\n\n### Event\n\nAn event that can be handled by a handler. The event will bubble through the handlers unless stopped by calling the\n`StopPropagation` method or returning an error in a handler. By default there is only the `\"message\"` event which is\ndispatched whenever a client sends a message.\n\n### Server\n\nA very simple wrapper for a list of `Interface` instances, allowing sending to all interfaces simultaneously and\ncleaning up when an interface has been closed.\n\n## WebSocket support\n\nThis library is built with the web in mind, so it makes WebSocket connections easier. It uses a simple protocol for\ntranslating Go structs to JSON objects and back. Here's what a WebSocket message looks like in Go and as JSON:\n\n**Go:** `\u0026MyDataType{Hello: \"World\", Answer: 42}`\n\n**JSON:** `{\"Type\": \"MyDataType\", \"Value\": {\"Hello\": \"World\", \"Answer\": 42}}`\n\nIn order to convert the data back to Go, *geomys* needs to know which struct type to unmarshal the JSON into, which\nit figures out by calling `GetMessage(\"MyDataType\")` on your `WebSocketServer` implementation, expecting an empty\nstruct back which the `Value` field will be unmarshaled into.\n\n## Design patterns\n\nHere are some ideas for how to use interfaces and handlers. These examples skip the bootstrapping code for the sake\nof brevity.\n\n### Identification\n\nThis will require the users to identify themselves before letting them chat with each other. The chat client can\nleave out the `Name` field in their `Chat` messages, as it will be replaced with the name provided in the `Ident`\nmessage before being broadcasted.\n\n```go\ntype Chat struct {\n    Name string\n    Text string\n}\n\ntype Ident struct {\n    Name string\n}\n\nfunc (e *Example) IdentifyHandler(i *geomys.Interface, event *geomys.Event) error {\n    switch msg := event.Value.(type) {\n    case *Ident:\n        if msg.Name == \"\" {\n            i.Close()\n            return errors.New(\"Client did not provide a name\")\n        }\n        // Remember the user's ident.\n        i.Context = msg\n        // Stop looking for ident messages.\n        i.RemoveHandler()\n    default:\n        return errors.New(\"Expected an Ident message\")\n    }\n    // Prevent this event from bubbling to the broadcast handler.\n    event.StopPropagation()\n    return nil\n}\n\nfunc (e *Example) BroadcastHandler(i *geomys.Interface, event *geomys.Event) error {\n    switch msg := event.Value.(type) {\n    case *Chat:\n        // Fill in the name in the chat message.\n        msg.Name = i.Context.(*Ident).Name\n        e.Server.SendAll(msg)\n    default:\n        return errors.New(\"Expected a Chat message\")\n    }\n    return nil\n}\n\nfunc (e *Example) GetInterface(ws *websocket.Conn) *geomys.Interface {\n    i := e.Server.NewInterface(nil)\n    i.PushHandler(e.BroadcastHandler)\n    // This handler will have control first, identifying the user.\n    i.PushHandler(e.IdentifyHandler)\n    return i\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblixt%2Fgeomys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblixt%2Fgeomys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblixt%2Fgeomys/lists"}