The WebSocket protocol establishes a two-way communication channel via TCP.
Messages are made up of one or more dataframes, and are delineated by
frames with the fin
bit set.
This page hosts a formal specification of WebSocket using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.bin", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
websocket_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.initial_frame() // => get initial frame
#pragma once
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* The WebSocket protocol establishes a two-way communication channel via TCP.
* Messages are made up of one or more dataframes, and are delineated by
* frames with the `fin` bit set.
*/
class websocket_t : public kaitai::kstruct {
public:
class frame_header_t;
class initial_frame_t;
class dataframe_t;
enum opcode_t {
OPCODE_CONTINUATION = 0,
OPCODE_TEXT = 1,
OPCODE_BINARY = 2,
OPCODE_RESERVED_3 = 3,
OPCODE_RESERVED_4 = 4,
OPCODE_RESERVED_5 = 5,
OPCODE_RESERVED_6 = 6,
OPCODE_RESERVED_7 = 7,
OPCODE_CLOSE = 8,
OPCODE_PING = 9,
OPCODE_PONG = 10,
OPCODE_RESERVED_CONTROL_B = 11,
OPCODE_RESERVED_CONTROL_C = 12,
OPCODE_RESERVED_CONTROL_D = 13,
OPCODE_RESERVED_CONTROL_E = 14,
OPCODE_RESERVED_CONTROL_F = 15
};
websocket_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, websocket_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~websocket_t();
class frame_header_t : public kaitai::kstruct {
public:
frame_header_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, websocket_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~frame_header_t();
private:
bool f_len_payload;
int32_t m_len_payload;
public:
int32_t len_payload();
private:
bool m_finished;
uint64_t m_reserved;
opcode_t m_opcode;
bool m_is_masked;
uint64_t m_len_payload_primary;
uint16_t m_len_payload_extended_1;
bool n_len_payload_extended_1;
public:
bool _is_null_len_payload_extended_1() { len_payload_extended_1(); return n_len_payload_extended_1; };
private:
uint32_t m_len_payload_extended_2;
bool n_len_payload_extended_2;
public:
bool _is_null_len_payload_extended_2() { len_payload_extended_2(); return n_len_payload_extended_2; };
private:
uint32_t m_mask_key;
bool n_mask_key;
public:
bool _is_null_mask_key() { mask_key(); return n_mask_key; };
private:
websocket_t* m__root;
kaitai::kstruct* m__parent;
public:
bool finished() const { return m_finished; }
uint64_t reserved() const { return m_reserved; }
opcode_t opcode() const { return m_opcode; }
bool is_masked() const { return m_is_masked; }
uint64_t len_payload_primary() const { return m_len_payload_primary; }
uint16_t len_payload_extended_1() const { return m_len_payload_extended_1; }
uint32_t len_payload_extended_2() const { return m_len_payload_extended_2; }
uint32_t mask_key() const { return m_mask_key; }
websocket_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
class initial_frame_t : public kaitai::kstruct {
public:
initial_frame_t(kaitai::kstream* p__io, websocket_t* p__parent = nullptr, websocket_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~initial_frame_t();
private:
std::unique_ptr<frame_header_t> m_header;
std::string m_payload_bytes;
bool n_payload_bytes;
public:
bool _is_null_payload_bytes() { payload_bytes(); return n_payload_bytes; };
private:
std::string m_payload_text;
bool n_payload_text;
public:
bool _is_null_payload_text() { payload_text(); return n_payload_text; };
private:
websocket_t* m__root;
websocket_t* m__parent;
public:
frame_header_t* header() const { return m_header.get(); }
std::string payload_bytes() const { return m_payload_bytes; }
std::string payload_text() const { return m_payload_text; }
websocket_t* _root() const { return m__root; }
websocket_t* _parent() const { return m__parent; }
};
class dataframe_t : public kaitai::kstruct {
public:
dataframe_t(kaitai::kstream* p__io, websocket_t* p__parent = nullptr, websocket_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~dataframe_t();
private:
std::unique_ptr<frame_header_t> m_header;
std::string m_payload_bytes;
bool n_payload_bytes;
public:
bool _is_null_payload_bytes() { payload_bytes(); return n_payload_bytes; };
private:
std::string m_payload_text;
bool n_payload_text;
public:
bool _is_null_payload_text() { payload_text(); return n_payload_text; };
private:
websocket_t* m__root;
websocket_t* m__parent;
public:
frame_header_t* header() const { return m_header.get(); }
std::string payload_bytes() const { return m_payload_bytes; }
std::string payload_text() const { return m_payload_text; }
websocket_t* _root() const { return m__root; }
websocket_t* _parent() const { return m__parent; }
};
private:
std::unique_ptr<initial_frame_t> m_initial_frame;
std::unique_ptr<std::vector<std::unique_ptr<dataframe_t>>> m_trailing_frames;
bool n_trailing_frames;
public:
bool _is_null_trailing_frames() { trailing_frames(); return n_trailing_frames; };
private:
websocket_t* m__root;
kaitai::kstruct* m__parent;
public:
initial_frame_t* initial_frame() const { return m_initial_frame.get(); }
std::vector<std::unique_ptr<dataframe_t>>* trailing_frames() const { return m_trailing_frames.get(); }
websocket_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "websocket.h"
websocket_t::websocket_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, websocket_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_initial_frame = nullptr;
m_trailing_frames = nullptr;
_read();
}
void websocket_t::_read() {
m_initial_frame = std::unique_ptr<initial_frame_t>(new initial_frame_t(m__io, this, m__root));
n_trailing_frames = true;
if (initial_frame()->header()->finished() != true) {
n_trailing_frames = false;
m_trailing_frames = std::unique_ptr<std::vector<std::unique_ptr<dataframe_t>>>(new std::vector<std::unique_ptr<dataframe_t>>());
{
int i = 0;
dataframe_t* _;
do {
_ = new dataframe_t(m__io, this, m__root);
m_trailing_frames->push_back(std::move(std::unique_ptr<dataframe_t>(_)));
i++;
} while (!(_->header()->finished()));
}
}
}
websocket_t::~websocket_t() {
_clean_up();
}
void websocket_t::_clean_up() {
if (!n_trailing_frames) {
}
}
websocket_t::frame_header_t::frame_header_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, websocket_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_len_payload = false;
_read();
}
void websocket_t::frame_header_t::_read() {
m_finished = m__io->read_bits_int_be(1);
m_reserved = m__io->read_bits_int_be(3);
m_opcode = static_cast<websocket_t::opcode_t>(m__io->read_bits_int_be(4));
m_is_masked = m__io->read_bits_int_be(1);
m_len_payload_primary = m__io->read_bits_int_be(7);
m__io->align_to_byte();
n_len_payload_extended_1 = true;
if (len_payload_primary() == 126) {
n_len_payload_extended_1 = false;
m_len_payload_extended_1 = m__io->read_u2be();
}
n_len_payload_extended_2 = true;
if (len_payload_primary() == 127) {
n_len_payload_extended_2 = false;
m_len_payload_extended_2 = m__io->read_u4be();
}
n_mask_key = true;
if (is_masked()) {
n_mask_key = false;
m_mask_key = m__io->read_u4be();
}
}
websocket_t::frame_header_t::~frame_header_t() {
_clean_up();
}
void websocket_t::frame_header_t::_clean_up() {
if (!n_len_payload_extended_1) {
}
if (!n_len_payload_extended_2) {
}
if (!n_mask_key) {
}
}
int32_t websocket_t::frame_header_t::len_payload() {
if (f_len_payload)
return m_len_payload;
m_len_payload = ((len_payload_primary() <= 125) ? (len_payload_primary()) : (((len_payload_primary() == 126) ? (len_payload_extended_1()) : (len_payload_extended_2()))));
f_len_payload = true;
return m_len_payload;
}
websocket_t::initial_frame_t::initial_frame_t(kaitai::kstream* p__io, websocket_t* p__parent, websocket_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_header = nullptr;
_read();
}
void websocket_t::initial_frame_t::_read() {
m_header = std::unique_ptr<frame_header_t>(new frame_header_t(m__io, this, m__root));
n_payload_bytes = true;
if (header()->opcode() != websocket_t::OPCODE_TEXT) {
n_payload_bytes = false;
m_payload_bytes = m__io->read_bytes(header()->len_payload());
}
n_payload_text = true;
if (header()->opcode() == websocket_t::OPCODE_TEXT) {
n_payload_text = false;
m_payload_text = kaitai::kstream::bytes_to_str(m__io->read_bytes(header()->len_payload()), std::string("UTF-8"));
}
}
websocket_t::initial_frame_t::~initial_frame_t() {
_clean_up();
}
void websocket_t::initial_frame_t::_clean_up() {
if (!n_payload_bytes) {
}
if (!n_payload_text) {
}
}
websocket_t::dataframe_t::dataframe_t(kaitai::kstream* p__io, websocket_t* p__parent, websocket_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_header = nullptr;
_read();
}
void websocket_t::dataframe_t::_read() {
m_header = std::unique_ptr<frame_header_t>(new frame_header_t(m__io, this, m__root));
n_payload_bytes = true;
if (_root()->initial_frame()->header()->opcode() != websocket_t::OPCODE_TEXT) {
n_payload_bytes = false;
m_payload_bytes = m__io->read_bytes(header()->len_payload());
}
n_payload_text = true;
if (_root()->initial_frame()->header()->opcode() == websocket_t::OPCODE_TEXT) {
n_payload_text = false;
m_payload_text = kaitai::kstream::bytes_to_str(m__io->read_bytes(header()->len_payload()), std::string("UTF-8"));
}
}
websocket_t::dataframe_t::~dataframe_t() {
_clean_up();
}
void websocket_t::dataframe_t::_clean_up() {
if (!n_payload_bytes) {
}
if (!n_payload_text) {
}
}