{"id":13477697,"url":"https://github.com/Totodore/socketioxide","last_synced_at":"2025-03-27T06:30:46.404Z","repository":{"id":158807329,"uuid":"612811519","full_name":"Totodore/socketioxide","owner":"Totodore","description":"A socket.io server implementation in Rust that integrates with the Tower ecosystem and the Tokio stack.","archived":false,"fork":false,"pushed_at":"2025-03-24T07:48:05.000Z","size":4199,"stargazers_count":1371,"open_issues_count":11,"forks_count":61,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-26T22:01:35.747Z","etag":null,"topics":["axum","hyper","rust","socket-io","tokio","tower"],"latest_commit_sha":null,"homepage":"https://docs.rs/socketioxide","language":"Rust","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/Totodore.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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},"funding":{"github":"totodore","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2023-03-12T03:23:23.000Z","updated_at":"2025-03-26T18:18:26.000Z","dependencies_parsed_at":"2023-11-12T13:28:03.329Z","dependency_job_id":"83706141-27ea-44ed-af06-5367c0be605a","html_url":"https://github.com/Totodore/socketioxide","commit_stats":null,"previous_names":["totodore/socketioxide"],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Totodore%2Fsocketioxide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Totodore%2Fsocketioxide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Totodore%2Fsocketioxide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Totodore%2Fsocketioxide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Totodore","download_url":"https://codeload.github.com/Totodore/socketioxide/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245797218,"owners_count":20673809,"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":["axum","hyper","rust","socket-io","tokio","tower"],"created_at":"2024-07-31T16:01:46.256Z","updated_at":"2025-03-27T06:30:46.388Z","avatar_url":"https://github.com/Totodore.png","language":"Rust","readme":"# [`Socketioxide`](https://github.com/totodore/socketioxide) 🚀🦀\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/Totodore/socketioxide/refs/heads/main/.github/logo_dark.svg\"\u003e\n  \u003cimg width=150 height=150 align=left alt=\"Shows Anuraghazra's GitHub Stats.\" src=\"https://raw.githubusercontent.com/Totodore/socketioxide/refs/heads/main/.github/logo_light.svg\"\u003e\n\u003c/picture\u003e\n\nA [***`socket.io`***](https://socket.io) server implementation in Rust that integrates with the [***`Tower`***](https://tokio.rs/#tk-lib-tower) ecosystem and the [***`Tokio stack`***](https://tokio.rs). It integrates with any server framework based on tower like [***`Axum`***](https://docs.rs/axum/latest/axum/), [***`Warp`***](https://docs.rs/warp/latest/warp/), [***`Salvo`***](https://salvo.rs), [***`Viz`***](https://viz.rs) or [***`Hyper`***](https://docs.rs/hyper/latest/hyper/). Add any other tower based middleware on top of socketioxide such as CORS, authorization, compression, etc with [***`tower-http`***](https://docs.rs/tower-http/latest/tower_http/).\n\n[![Crates.io](https://img.shields.io/crates/v/socketioxide.svg)](https://crates.io/crates/socketioxide)\n[![Documentation](https://docs.rs/socketioxide/badge.svg)](https://docs.rs/socketioxide)\n[![CI](https://github.com/Totodore/socketioxide/actions/workflows/github-ci.yml/badge.svg)](https://github.com/Totodore/socketioxide/actions/workflows/github-ci.yml)\n\n\u003cimg src=\"https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/solar.png\"\u003e\n\n## Features\n* Integrates with:\n  * [Axum](https://docs.rs/axum/latest/axum/): [🏓echo example](./examples/axum-echo/axum_echo.rs)\n  * [Warp](https://docs.rs/warp/latest/warp/): [🏓echo example](https://github.com/Totodore/socketioxide/blob/v0.8.0/examples/warp-echo/warp_echo.rs) (Not supported with `socketioxide \u003e= 0.9.0` as long as warp doesn't migrate to hyper v1)\n  * [Hyper](https://docs.rs/hyper/latest/hyper/): [🏓echo example](./examples/hyper-echo/hyper_echo.rs)\n  * [Salvo](https://salvo.rs): [🏓echo example](./examples/salvo-echo/salvo_echo.rs)\n  * [Viz](https://viz.rs): [🏓echo example](./examples/viz-echo/viz_echo.rs)\n* Out of the box support for any other middleware based on tower:\n  * [🔓CORS](https://docs.rs/tower-http/latest/tower_http/cors)\n  * [📁Compression](https://docs.rs/tower-http/latest/tower_http/compression)\n  * [🔐Authorization](https://docs.rs/tower-http/latest/tower_http/auth)\n* Effortless horizontal scaling with plugable adapters:\n  * [Redis / Valkey](https://docs.rs/socketioxide-redis/latest/socketioxide-redis)\n  * More to come...\n* Remote cluster communication with [Socketioxide-emitter](https://github.com/Totodore/socketioxide-emitter)\n* Namespaces and Dynamic Namespaces\n* Rooms\n* Ack and emit with ack\n* Binary packets\n* Polling \u0026 Websocket transports\n* Common (default) \u0026 Msgpack parsers\n* Extensions to add custom data to sockets\n* Memory efficient http payload parsing with streams\n* Flexible axum-like API to handle events. With extractors to extract data from your handlers\n* Well tested with the official [end to end test-suite](https://github.com/totodore/socketioxide/actions)\n* All Socket.io versions supported :\n  * [🔌protocol v5](https://socket.io/docs/v4/) : socket.io js from v3.0.0..latest, it is enabled by default\n  * [🔌protocol v4](https://github.com/socketio/socket.io-protocol/tree/v4) : based on engine.io v3, under the feature flag `v4`, (socket.io js from v1.0.3..latest)\n\n\u003cimg src=\"https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/solar.png\"\u003e\n\n## Examples\n\u003cdetails\u003e \u003csummary\u003e\u003ccode\u003eChat app 💬 (see full example \u003ca href=\"./examples/chat\"\u003ehere\u003c/a\u003e)\u003c/code\u003e\u003c/summary\u003e\n\n```rust\nio.ns(\"/\", |s: SocketRef| {\n    s.on(\"new message\", |s: SocketRef, Data::\u003cString\u003e(msg)| {\n        let username = s.extensions.get::\u003cUsername\u003e().unwrap().clone();\n        let msg = Res::Message {\n            username,\n            message: msg,\n        };\n        s.broadcast().emit(\"new message\", msg).ok();\n    });\n\n    s.on(\n        \"add user\",\n        |s: SocketRef, Data::\u003cString\u003e(username), user_cnt: State\u003cUserCnt\u003e| {\n            if s.extensions.get::\u003cUsername\u003e().is_some() {\n                return;\n            }\n            let num_users = user_cnt.add_user();\n            s.extensions.insert(Username(username.clone()));\n            s.emit(\"login\", Res::Login { num_users }).ok();\n\n            let res = Res::UserEvent {\n                num_users,\n                username: Username(username),\n            };\n            s.broadcast().emit(\"user joined\", res).ok();\n        },\n    );\n\n    s.on(\"typing\", |s: SocketRef| {\n        let username = s.extensions.get::\u003cUsername\u003e().unwrap().clone();\n        s.broadcast()\n            .emit(\"typing\", Res::Username { username })\n            .ok();\n    });\n\n    s.on(\"stop typing\", |s: SocketRef| {\n        let username = s.extensions.get::\u003cUsername\u003e().unwrap().clone();\n        s.broadcast()\n            .emit(\"stop typing\", Res::Username { username })\n            .ok();\n    });\n\n    s.on_disconnect(|s: SocketRef, user_cnt: State\u003cUserCnt\u003e| {\n        if let Some(username) = s.extensions.get::\u003cUsername\u003e() {\n            let num_users = user_cnt.remove_user();\n            let res = Res::UserEvent {\n                num_users,\n                username: username.clone(),\n            };\n            s.broadcast().emit(\"user left\", res).ok();\n        }\n    });\n});\n\n```\n\n\u003c/details\u003e\n\u003cdetails\u003e \u003csummary\u003e\u003ccode\u003eEcho implementation with Axum 🏓\u003c/code\u003e\u003c/summary\u003e\n\n```rust\nuse axum::routing::get;\nuse serde_json::Value;\nuse socketioxide::{\n    extract::{AckSender, Bin, Data, SocketRef},\n    SocketIo,\n};\nuse tracing::info;\nuse tracing_subscriber::FmtSubscriber;\n\nfn on_connect(socket: SocketRef, Data(data): Data\u003cValue\u003e) {\n    info!(\"Socket.IO connected: {:?} {:?}\", socket.ns(), socket.id);\n    socket.emit(\"auth\", data).ok();\n\n    socket.on(\n        \"message\",\n        |socket: SocketRef, Data::\u003cValue\u003e(data), Bin(bin)| {\n            info!(\"Received event: {:?} {:?}\", data, bin);\n            socket.bin(bin).emit(\"message-back\", data).ok();\n        },\n    );\n\n    socket.on(\n        \"message-with-ack\",\n        |Data::\u003cValue\u003e(data), ack: AckSender, Bin(bin)| {\n            info!(\"Received event: {:?} {:?}\", data, bin);\n            ack.bin(bin).send(data).ok();\n        },\n    );\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    tracing::subscriber::set_global_default(FmtSubscriber::default())?;\n\n    let (layer, io) = SocketIo::new_layer();\n\n    io.ns(\"/\", on_connect);\n    io.ns(\"/custom\", on_connect);\n\n    let app = axum::Router::new()\n        .route(\"/\", get(|| async { \"Hello, World!\" }))\n        .layer(layer);\n\n    info!(\"Starting server\");\n\n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3000\").await.unwrap();\n    axum::serve(listener, app).await.unwrap();\n\n    Ok(())\n}\n```\n\u003c/details\u003e\n\u003ccode\u003eOther examples are available in the \u003ca href=\"./examples\"\u003eexample folder\u003c/a\u003e\u003c/code\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/solar.png\"\u003e\n\n## Contributions and Feedback / Questions\nAny contribution is welcome, feel free to open an issue or a PR. If you want to contribute but don't know where to start, you can check the [issues](https://github.com/totodore/socketioxide/issues).\n\nIf you have any question or feedback, please open a thread on the [discussions](https://github.com/totodore/socketioxide/discussions) page.\n\n## License 🔐\nThis project is licensed under the [MIT license](./LICENSE).\n","funding_links":["https://github.com/sponsors/totodore"],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTotodore%2Fsocketioxide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTotodore%2Fsocketioxide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTotodore%2Fsocketioxide/lists"}