{"id":38505788,"url":"https://github.com/shiguredo/websocket-rs","last_synced_at":"2026-04-02T13:55:49.524Z","repository":{"id":332923008,"uuid":"1135506496","full_name":"shiguredo/websocket-rs","owner":"shiguredo","description":"WebSocket Library for Rust","archived":false,"fork":false,"pushed_at":"2026-02-25T12:24:54.000Z","size":408,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-02-25T12:43:38.834Z","etag":null,"topics":["rust","sans-io","websocket"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shiguredo.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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-16T07:36:05.000Z","updated_at":"2026-02-25T12:36:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shiguredo/websocket-rs","commit_stats":null,"previous_names":["shiguredo/websocket-rs"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/shiguredo/websocket-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiguredo%2Fwebsocket-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiguredo%2Fwebsocket-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiguredo%2Fwebsocket-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiguredo%2Fwebsocket-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shiguredo","download_url":"https://codeload.github.com/shiguredo/websocket-rs/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiguredo%2Fwebsocket-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30101996,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T23:59:36.199Z","status":"ssl_error","status_checked_at":"2026-03-04T23:56:48.556Z","response_time":59,"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":["rust","sans-io","websocket"],"created_at":"2026-01-17T06:10:08.976Z","updated_at":"2026-04-02T13:55:49.518Z","avatar_url":"https://github.com/shiguredo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# websocket-rs\n\n[![crates.io](https://img.shields.io/crates/v/shiguredo_websocket.svg)](https://crates.io/crates/shiguredo_websocket)\n[![docs.rs](https://docs.rs/shiguredo_websocket/badge.svg)](https://docs.rs/shiguredo_websocket)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![GitHub Actions](https://github.com/shiguredo/websocket-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/shiguredo/websocket-rs/actions/workflows/ci.yml)\n[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord\u0026logoColor=white)](https://discord.gg/shiguredo)\n\n## About Shiguredo's open source software\n\nWe will not respond to PRs or issues that have not been discussed on Discord. Also, Discord is only available in Japanese.\n\nPlease read \u003chttps://github.com/shiguredo/oss\u003e before use.\n\n## 時雨堂のオープンソースソフトウェアについて\n\n利用前に \u003chttps://github.com/shiguredo/oss\u003e をお読みください。\n\n## 概要\n\nRust で実装された Sans I/O な WebSocket ライブラリです。\n\n## 特徴\n\n- Sans I/O\n  - \u003chttps://sans-io.readthedocs.io/index.html\u003e\n- HTTP/1.1 対応\n\n## 使い方\n\n### クライアント (WebSocket 接続)\n\n```rust\nuse std::io::{Read, Write};\nuse std::net::TcpStream;\nuse shiguredo_websocket::{\n    ClientConnectionOptions, ConnectionEvent, ConnectionOutput,\n    RandomSource, WebSocketClientConnection, Timestamp,\n};\n\n// 乱数ソースの実装 (getrandom を使う場合)\nstruct SecureRandom;\n\nimpl RandomSource for SecureRandom {\n    fn masking_key(\u0026mut self) -\u003e [u8; 4] {\n        let mut key = [0u8; 4];\n        getrandom::fill(\u0026mut key).expect(\"failed to generate masking key\");\n        key\n    }\n\n    fn nonce(\u0026mut self) -\u003e [u8; 16] {\n        let mut nonce = [0u8; 16];\n        getrandom::fill(\u0026mut nonce).expect(\"failed to generate nonce\");\n        nonce\n    }\n}\n\n// 乱数ソースの実装 (aws-lc-rs を使う場合)\nstruct AwsLcRandom;\n\nimpl RandomSource for AwsLcRandom {\n    fn masking_key(\u0026mut self) -\u003e [u8; 4] {\n        let mut key = [0u8; 4];\n        aws_lc_rs::rand::fill(\u0026mut key).expect(\"failed to generate masking key\");\n        key\n    }\n\n    fn nonce(\u0026mut self) -\u003e [u8; 16] {\n        let mut nonce = [0u8; 16];\n        aws_lc_rs::rand::fill(\u0026mut nonce).expect(\"failed to generate nonce\");\n        nonce\n    }\n}\n\n// TCP ソケット接続\nlet mut socket = TcpStream::connect(\"echo.websocket.org:80\")?;\n\n// WebSocket 接続オプション\nlet options = ClientConnectionOptions::new(\"echo.websocket.org\", \"/\");\n\n// WebSocket 接続作成・開始\nlet mut ws = WebSocketClientConnection::new(options, SecureRandom);\nws.connect()?;\n\n// HTTP Upgrade リクエスト送信\nwhile let Some(output) = ws.poll_output() {\n    if let ConnectionOutput::SendData(data) = output {\n        socket.write_all(\u0026data)?;\n    }\n}\n\n// 現在時刻を取得するヘルパー関数\nfn now() -\u003e Timestamp {\n    use std::time::{SystemTime, UNIX_EPOCH};\n    let millis = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .unwrap()\n        .as_millis() as u64;\n    Timestamp::from_millis(millis)\n}\n\n// ハンドシェイクレスポンス受信\nlet mut buf = [0u8; 4096];\nloop {\n    let n = socket.read(\u0026mut buf)?;\n    ws.feed_recv_buf(\u0026buf[..n], now())?;\n\n    while let Some(event) = ws.poll_event() {\n        match event {\n            ConnectionEvent::Connected { protocol, extensions } =\u003e {\n                println!(\"Connected! protocol={:?}\", protocol);\n            }\n            ConnectionEvent::TextMessage(text) =\u003e {\n                println!(\"Received: {}\", text);\n            }\n            ConnectionEvent::BinaryMessage(data) =\u003e {\n                println!(\"Received: {} bytes\", data.len());\n            }\n            ConnectionEvent::Close { code, reason } =\u003e {\n                println!(\"Closed: {:?} {}\", code, reason);\n                break;\n            }\n            _ =\u003e {}\n        }\n    }\n\n    // 送信データがあれば送信\n    while let Some(output) = ws.poll_output() {\n        if let ConnectionOutput::SendData(data) = output {\n            socket.write_all(\u0026data)?;\n        }\n    }\n}\n```\n\n### サーバー (WebSocket 接続受付)\n\n```rust\nuse shiguredo_websocket::{\n    ConnectionEvent, ConnectionOutput, ConnectionState,\n    ServerConnectionOptions, WebSocketServerConnection,\n};\n\n// WebSocketServerConnection の初期化\nlet options = ServerConnectionOptions::new();\nlet mut ws = WebSocketServerConnection::new(options);\n\n// 受信データをフィード\n// ws.feed_recv_buf(\u0026received_data)?;\n\n// ハンドシェイクの自動受諾\n// if ws.state() == ConnectionState::Connecting {\n//     ws.accept_handshake_auto()?;\n// }\n\n// イベント処理\n// while let Some(event) = ws.poll_event() { ... }\n\n// 出力処理\n// while let Some(output) = ws.poll_output() { ... }\n```\n\n### メッセージ送信 (クライアント)\n\n```rust\nuse shiguredo_websocket::CloseCode;\n\n// テキストメッセージ送信\nws.send_text(\"Hello, WebSocket!\").unwrap();\n\n// バイナリメッセージ送信\nws.send_binary(\u0026[0x01, 0x02, 0x03]).unwrap();\n\n// Ping 送信\nws.send_ping(\u0026[]).unwrap();\n\n// 接続を閉じる\nws.close(CloseCode::NORMAL, \"Goodbye\").unwrap();\n```\n\n### メッセージ送信 (サーバー)\n\n```rust\nuse shiguredo_websocket::CloseCode;\n\n// テキストメッセージ送信\nws.send_text(\"Hello, WebSocket!\").unwrap();\n\n// バイナリメッセージ送信\nws.send_binary(\u0026[0x01, 0x02, 0x03]).unwrap();\n\n// Ping 送信\nws.send_ping(\u0026[]).unwrap();\n\n// 接続を閉じる\nws.close(CloseCode::NORMAL, \"Goodbye\").unwrap();\n```\n\n### フレームの直接操作 (低レベル API)\n\n```rust\nuse shiguredo_websocket::{Frame, FrameDecoder, Opcode};\n\n// フレーム作成\nlet frame = Frame::text(\"Hello\");\nlet masking_key = [0x12, 0x34, 0x56, 0x78];\nlet encoded = frame.encode(masking_key);\n\n// フレームデコード\nlet mut decoder = FrameDecoder::new();\ndecoder.feed(\u0026encoded);\n\nwhile let Some(frame) = decoder.decode().unwrap() {\n    match frame.opcode {\n        Opcode::Text =\u003e {\n            let text = String::from_utf8_lossy(\u0026frame.payload);\n            println!(\"Text: {}\", text);\n        }\n        Opcode::Binary =\u003e {\n            println!(\"Binary: {} bytes\", frame.payload.len());\n        }\n        Opcode::Ping =\u003e {\n            // Pong を返す\n        }\n        Opcode::Close =\u003e {\n            // 接続を閉じる\n        }\n        _ =\u003e {}\n    }\n}\n```\n\n## WebSocket\n\nこのライブラリが対応している WebSocket の仕組みです。\n\n### フレーム\n\n- テキストフレーム / バイナリフレーム\n- 制御フレーム (Ping, Pong, Close)\n- フラグメンテーション (継続フレーム)\n- マスキング (クライアント→サーバー)\n\n### ハンドシェイク\n\n- HTTP/1.1 Upgrade リクエスト/レスポンス\n- Sec-WebSocket-Key / Sec-WebSocket-Accept の検証\n- サブプロトコルネゴシエーション (Sec-WebSocket-Protocol)\n- 拡張ネゴシエーション (Sec-WebSocket-Extensions)\n\n### 拡張\n\n- permessage-deflate (RFC 7692)\n  - server_no_context_takeover\n  - client_no_context_takeover\n  - server_max_window_bits\n  - client_max_window_bits\n\n### 接続管理\n\n- 自動 Ping/Pong 応答\n- 定期的な Ping 送信 (設定可能)\n- Close ハンドシェイク\n- 状態管理 (Connecting, Connected, Closing, Closed)\n\n### セキュリティ\n\n- マスキングキーの検証\n- フレームサイズ制限\n- UTF-8 検証 (テキストメッセージ)\n\n### 制限 (DoS 対策)\n\nデフォルト値:\n\n- 最大フレームサイズ: 64MB\n- 最大メッセージサイズ: 64MB\n- 最大解凍サイズ (Zip Bomb 対策): 16MB\n\n`ClientConnectionOptions` / `ServerConnectionOptions` で各制限値をカスタマイズ可能です。\n\n## サンプル\n\nサンプルは [Tokio](https://github.com/tokio-rs/tokio) と [Rustls](https://github.com/rustls/rustls) を利用しています。引数のライブラリには [noargs](https://github.com/sile/noargs) を利用しています。\n\n### websocket_client\n\nWS/WSS クライアントの例です。\n\n```bash\ncargo run -p websocket_client -- wss://localhost:8443/\ncargo run -p websocket_client -- ws://localhost:8080/\ncargo run -p websocket_client -- wss://localhost:8443/ --insecure\n```\n\n**オプション:**\n\n- `\u003cURL\u003e`: 接続先 URL (ws:// または wss://)\n- `--insecure`: 自己署名証明書を許可 (WSS のみ)\n\n**機能:**\n\n- WS/WSS リクエスト送信\n- エコーメッセージ受信\n- permessage-deflate 対応\n- rustls-platform-verifier による TLS 検証\n\n### websocket_server\n\nWS/WSS エコーサーバーの例です。\n\n```bash\ncargo run -p websocket_server\ncargo run -p websocket_server -- --tls --cert cert.pem --key key.pem\n```\n\n**オプション:**\n\n- `-p, --port \u003cPORT\u003e`: リッスンポート (デフォルト: 8080, TLS 有効時: 8443)\n- `--tls`: WSS 有効化\n- `--cert \u003cPATH\u003e`: 証明書ファイル (PEM 形式)\n- `--key \u003cPATH\u003e`: 秘密鍵ファイル (PEM 形式)\n\n**機能:**\n\n- WS/WSS 接続受付\n- テキスト/バイナリメッセージのエコーバック\n- 自動 Pong 応答\n\n### websocket_reverse_proxy\n\nWS/WSS リバースプロキシの例です。\n\n```bash\n# WS -\u003e WS\ncargo run -p websocket_reverse_proxy -- --upstream ws://localhost:8080/\n\n# WS -\u003e WSS\ncargo run -p websocket_reverse_proxy -- --upstream wss://echo.websocket.org/\n\n# WSS -\u003e WSS (TLS 終端)\ncargo run -p websocket_reverse_proxy -- --tls --cert cert.pem --key key.pem --upstream wss://echo.websocket.org/\n```\n\n**オプション:**\n\n- `-p, --port \u003cPORT\u003e`: リッスンポート (デフォルト: 8080, TLS 有効時: 8443)\n- `-u, --upstream \u003cURL\u003e`: 転送先 URL (ws:// または wss://)\n- `--tls`: フロントエンドで WSS 有効化\n- `--cert \u003cPATH\u003e`: 証明書ファイル (PEM 形式)\n- `--key \u003cPATH\u003e`: 秘密鍵ファイル (PEM 形式)\n- `--insecure`: アップストリームの自己署名証明書を許可\n- `--debug`: デバッグログを有効化\n\n**機能:**\n\n- WS/WSS 接続の双方向プロキシ\n- テキスト/バイナリ/Ping/Close メッセージの中継\n- permessage-deflate 対応\n\n## 規格書\n\nこのライブラリが準拠している RFC 一覧です。\n\n- RFC 6455 - The WebSocket Protocol\n  - \u003chttps://datatracker.ietf.org/doc/html/rfc6455\u003e\n- RFC 7692 - Compression Extensions for WebSocket\n  - \u003chttps://datatracker.ietf.org/doc/html/rfc7692\u003e\n\n## ライセンス\n\nApache License 2.0\n\n```text\nCopyright 2026-2026, Shiguredo Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshiguredo%2Fwebsocket-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshiguredo%2Fwebsocket-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshiguredo%2Fwebsocket-rs/lists"}