Single-header C++17 library for fast parsing and constructing of Websocket frames.
Disclaimer: this readme is largely AI generated, so mistakes may exist.
Note:
- This library does not implement the full WebSocket handshake (HTTP Upgrade, etc.).
- It also does not handle TLS/SSL or fragmentation of large messages.
- The parser, as written, does not automatically unmask frames—if you receive a masked frame (client-to-server), you’ll see the payload in its masked form unless you unmask it manually.
Below is an overview of how to build frames with FrameFactory
and parse them incrementally with FrameParser
.
- Frame Construction: Easily build text, binary, ping, pong, and close frames.
- Incremental Parsing: Pass partial data to
FrameParser::update(...)
repeatedly. The parser accumulates data in a buffer until a complete frame is recognized. - Payload Size Handling: Supports extended payload lengths (16-bit and 64-bit).
- Masking:
FrameFactory
can generate random 4-byte masking keys and XOR the payload for client-to-server frames.
Since wsframe is a header-only library, you can simply copy wsframe.hpp
into your project’s include path (or just use the included CMakeLists.txt
). There are no external dependencies beyond the C++ standard library (and std::random_device
, std::mt19937
).
git clone https://github.com/yourname/wsframe.git
# or copy wsframe.hpp into your include folder
Then in your C++ files:
#include "path/to/wsframe.hpp"
Compile with a C++17 (or higher) compiler:
g++ -std=c++17 main.cpp -o myapp
Use the FrameFactory
to build a single WebSocket frame with a specific opcode and optional payload. For example, to construct a text frame with the message "Hello World"
:
#include <wsframe/wsframe.hpp>
#include <iostream>
int main() {
wsframe::FrameFactory factory;
// Build a FIN=1, opcode=TEXT, mask=false frame with payload
std::string_view payload = "Hello World";
std::string_view rawFrame = factory.text(/*fin=*/true, /*mask=*/false, payload);
// rawFrame now references the serialized bytes in factory's internal buffer.
// You can send rawFrame.data() over a socket (e.g. using send() or SSL_write()).
std::cout << "Constructed frame has length: " << rawFrame.size() << std::endl;
return 0;
}
Other frame types:
factory.binary(fin, mask, payload)
factory.ping(mask, payload)
factory.pong(mask, payload)
factory.close(mask, payload)
Note:
- Control frames (
ping
,pong
,close
) must have payload ≤ 125 bytes (RFC requirement).mask=true
is typically used by clients sending data to a server. If you’re writing server code, you usually setmask=false
for server-to-client frames.
Use FrameParser
to incrementally parse frames. Call update(...)
with chunks of data (e.g., from recv()
or SSL_read()
). The parser buffers partial data until a full frame is recognized, then returns a std::optional<Frame>
.
#include <wsframe/wsframe.hpp>
#include <iostream>
int main() {
wsframe::FrameParser parser;
// Suppose you receive some bytes from the network...
// e.g. data from a non-blocking recv or SSL_read call
std::string_view chunk1 = "\x81\x02\x48"; // partial data
auto frameOpt1 = parser.update(chunk1);
// Because it might be incomplete, we check if a frame was parsed
if (!frameOpt1.has_value()) {
std::cout << "No complete frame yet (partial read)..." << std::endl;
}
// Then you receive more bytes...
std::string_view chunk2 = "\x69"; // the rest
auto frameOpt2 = parser.update(chunk2);
if (frameOpt2.has_value()) {
wsframe::Frame frame = frameOpt2.value();
std::cout << "Parsed a frame! Opcode="
<< wsframe::Frame::opcode_to_string(frame.opcode)
<< ", payload=" << frame.payload << std::endl;
} else {
std::cout << "Still incomplete..." << std::endl;
}
}
parser.clear()
can be used to clear the internal buffer and prepare for a fresh parse if you detect a protocol error or want to discard leftover data.- If the parser completes a frame, any leftover bytes remain in the buffer, and can be used to parse subsequent frames.
-
XorShift128Plus
- A fast, non-cryptographic pseudo-random number generator used to generate masking keys.
-
FrameBuffer
- A resizable buffer that stores raw bytes. It provides methods like
push_back(...)
andget_space(...)
to append data efficiently. - Can produce views (
FrameBuffer::View
orstd::string_view
) referencing the internal buffer.
- A resizable buffer that stores raw bytes. It provides methods like
-
Frame
- Represents a single WebSocket frame with fields:
fin
,mask
,opcode
,masking_key
, andpayload
. Frame::construct()
writes its data into aFrameBuffer
.
- Represents a single WebSocket frame with fields:
-
FrameFactory
- Wraps a
FrameBuffer
and a small random cache. - Provides high-level methods like
text(...)
,binary(...)
,ping(...)
, etc. to build a frame and return astd::string_view
of the serialized bytes.
- Wraps a
-
FrameParser
- An incremental parser that accumulates data from
update(...)
calls. - Once enough data is present to form a full frame, it returns
std::optional<Frame>
. Otherwise, it returns an emptystd::optional
.
- An incremental parser that accumulates data from
- No Handshake Layer: This is not a full WebSocket client/server library. It only handles binary framing once the handshake is done.
- No Automatic Unmasking: The parser does not unmask inbound frames. If
mask=true
, you’ll see masked bytes inFrame::payload
. - No Fragmentation Support: The code does not handle multi-frame fragmentation (FIN=0, continuation frames). For production usage, you’d need to handle or reassemble fragments.
- No TLS: The code does not manage TLS sockets; you’d wrap it in your own SSL/TCP logic.