Skip to content

deanxyuan/websocket

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

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

cmake .. -DBUILD_TESTS=ON -DBUILD_DEMO=ON
cmake --build . --config RelWithDebInfo
ctest

Usage

Client: Build Handshake and Send Frames

#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

#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

#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

#include "websocket/websocket.h"
#include <vector>
#include <string>

// Split a large payload into multiple frames (max 1024 bytes each)
std::string data = "large payload...";
std::vector<std::string> frames;
websocket::server::BuildConsecutiveFrames(
    &frames, data, 1024, websocket::DataType::Text);

// On the receiving end, MessageAssembler reassembles them automatically

Frame Parsing

#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<uint8_t*>(data),
                                static_cast<uint32_t>(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

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

Third-Party Code

  • src/sha1.cc, src/sha1.h -- Based on SHA-1 implementation by Paul E. Jones

About

Standalone RFC 6455 WebSocket protocol library for C/C++ — zero dependencies, transport-agnostic codec.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors