# websocket **Repository Path**: shadowyuan/websocket ## Basic Information - **Project Name**: websocket - **Description**: websocket protocol library - **Primary Language**: C++ - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-06-03 - **Last Updated**: 2026-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # websocket A standalone RFC 6455 WebSocket protocol library for C11/C++11. ## Features - RFC 6455 compliant WebSocket frame parsing and building - Client and server handshake handling - Message reassembly with fragmentation support - Streaming UTF-8 validation (RFC 3629) - All opcodes: TEXT, BINARY, CONTINUATION, PING, PONG, CLOSE - Zero external dependencies - Protocol-only codec layer (no I/O) -- works with any TCP transport ## Requirements - C11/C++11 compatible compiler - CMake 3.13+ ## Building ```bash mkdir build && cd build cmake .. [options] cmake --build . --config Release ``` ### CMake Options | Option | Default | Description | |--------|---------|-------------| | `BUILD_SHARED` | OFF | Build shared library (SO/DYLIB). Not recommended on Windows due to STL ABI instability across MSVC versions. | | `BUILD_DEMO` | OFF | Build demo executable | | `BUILD_TESTS` | OFF | Build test suite | ### Quick Build with Tests ```bash cmake .. -DBUILD_TESTS=ON -DBUILD_DEMO=ON cmake --build . --config RelWithDebInfo ctest ``` ## Usage ### Client: Build Handshake and Send Frames ```cpp #include "websocket/websocket.h" // Build handshake request std::string key; std::string request = websocket::client::BuildHandshakeRequest( &key, "/chat", "example.com", "", ""); // Build and send a text frame (client frames are masked automatically) std::string frame = websocket::client::BuildSingleDataFrame("Hello, World!", websocket::DataType::Text); // Build control frames std::string ping = websocket::client::BuildPingFrame("ping data"); std::string close = websocket::client::BuildCloseFrame( websocket::Status::NORMAL, "bye"); ``` ### Server: Accept Connection and Send Frames ```cpp #include "websocket/websocket.h" // Parse client handshake websocket::Handshaker handshaker(request_data, request_size); if (handshaker.IsValidClientRequest()) { auto key = handshaker.FindField("Sec-WebSocket-Key"); std::string response = websocket::server::BuildHandshakeSuccessReply( key.second, ""); } // Build and send a text frame (server frames are unmasked) std::string frame = websocket::server::BuildSingleDataFrame("Hello, Client!", websocket::DataType::Text); ``` ### Message Assembly ```cpp #include "websocket/assembler.h" class MyHandler : public websocket::MessageHandler { void OnMessage(const websocket::WebSocketMessage& msg) override { // Handle complete (possibly reassembled) message if (msg.is_text) { // process text } else { // process binary } } void OnControlFrame(const websocket::ControlFrame& frame) override { // Handle PING, PONG, or CLOSE control frames } void OnError(const std::string& reason) override { // Handle protocol errors; assembler resets state automatically } }; MyHandler handler; websocket::MessageAssembler assembler(&handler); // Optional tuning assembler.SetMaxBufferSize(16 * 1024 * 1024); // 16 MB default assembler.SetMaxFragmentsPerMessage(1000); // 0 = unlimited (default) // Feed raw TCP data assembler.Feed(raw_data, data_size); // Reset after disconnect assembler.Reset(); ``` ### Fragmented Messages ```cpp #include "websocket/websocket.h" #include #include // Split a large payload into multiple frames (max 1024 bytes each) std::string data = "large payload..."; std::vector frames; websocket::server::BuildConsecutiveFrames( &frames, data, 1024, websocket::DataType::Text); // On the receiving end, MessageAssembler reassembles them automatically ``` ### Frame Parsing ```cpp #include "websocket/websocket.h" // Check how many bytes a complete frame needs int64_t frame_len = websocket::FrameContext::CheckFrameLength(data, size); if (frame_len == 0) { // incomplete -- need more data } else if (frame_len < 0) { // error (ParseError value) } else { // parse the frame (buffer must be writable for unmasking) websocket::FrameContext ctx(const_cast(data), static_cast(frame_len)); if (ctx.ProcessData()) { bool fin = ctx.Fin(); int opcode = ctx.OpCode(); bool masked = ctx.Mask(); const uint8_t* payload = ctx.Payload(); uint32_t payload_len = ctx.PayloadSize(); } } // Determine if incoming data is a handshake or a websocket frame int32_t pkg_len = websocket::CheckPackageLength(data, len); ``` ## Architecture | Layer | Components | Purpose | |-------|-----------|---------| | L1 -- Cryptography | `sha1.cc`, `base64.cc` (internal) | Handshake key computation (SHA-1, Base64) | | L2 -- Frame Protocol | `FrameContext`, `BuildFrame*`, `CheckPackageLength` | Single frame parse/build, byte-order helpers | | L3 -- Handshake | `Handshaker` | HTTP Upgrade request/response validation | | L4 -- Message Assembly | `MessageAssembler`, `Utf8Validator` (internal) | Fragment reassembly and UTF-8 validation | ## API Reference ### Core Types - `websocket::OpCode` -- WebSocket frame opcodes (`WS_CONTINUATION`, `WS_TEXT`, `WS_BINARY`, `WS_CLOSE`, `WS_PING`, `WS_PONG`) - `websocket::DataType` -- `Text` or `Binary` - `websocket::Status` -- Close status codes (RFC 6455 Section 7.4): `NORMAL` (1000), `GOING_AWAY` (1001), `PROTOCOL_ERROR` (1002), etc. - `websocket::ParseError` -- Frame parsing error codes: `OK`, `INSUFFICIENT_DATA`, `INVALID_LENGTH`, `PAYLOAD_TOO_LARGE` ### Handshake - `websocket::Handshaker` -- Parse and validate HTTP upgrade requests/responses - `websocket::Handshaker::CheckPackageLength()` -- Check if a complete handshake is present - `websocket::Handshaker::IsValidClientRequest()` -- Validate an incoming client request - `websocket::Handshaker::IsValidServerResponse()` -- Validate a server response against the original key - `websocket::Handshaker::FindField()` -- Case-insensitive header field lookup - `websocket::client::BuildHandshakeRequest()` -- Generate client handshake HTTP request - `websocket::server::BuildHandshakeSuccessReply()` -- Generate server 101 response - `websocket::server::BuildHandshakeFailedReply()` -- Generate non-101 error response ### Frame Building - `websocket::client::Build*()` -- Client frames (payload is masked) - `websocket::server::Build*()` -- Server frames (unmasked) - `websocket::BuildSingleFrame()` -- Low-level frame builder - `websocket::BuildFramePrefix()` -- Build frame header bytes - `websocket::BuildFrameWithPayload()` -- Build frame with masked payload - `websocket::MaskingPayload()` -- Apply XOR mask to payload bytes ### Frame Parsing - `websocket::FrameContext` -- Parse a single frame from raw bytes - `websocket::FrameContext::CheckFrameLength()` -- Determine complete frame size - `websocket::CheckPackageLength()` -- Determine if data is a handshake or frame, and its length - `websocket::GetCloseInfo()` -- Extract close status code and reason from a close frame ### Message Assembly - `websocket::MessageAssembler` -- Reassemble fragmented messages from raw TCP stream - `websocket::MessageHandler` -- Interface for receiving reassembled messages (`OnMessage`, `OnControlFrame`, `OnError`) - `websocket::WebSocketMessage` -- Complete message with `is_text` flag and `payload` - `websocket::ControlFrame` -- Control frame with `opcode` and `payload` ## Testing ```bash cd build ctest ``` Individual test executables: | Test | Description | |------|-------------| | `test_base64` | Base64 encode/decode | | `test_handshake` | Handshake request/response validation | | `test_frame` | Frame build/parse roundtrip | | `test_assembler` | Fragmented message reassembly | | `test_sha1` | SHA-1 known-answer vectors (FIPS 180-4) | | `test_utf8` | UTF-8 validation | ## License Apache License 2.0 -- see [LICENSE](LICENSE) ## Third-Party Code - `src/sha1.cc`, `src/sha1.h` -- Based on SHA-1 implementation by Paul E. Jones