This is a parser and builder for WebSocket messages (see RFC6455) written in C.
- Fast parsing and building of websocket messages
- No dependencies
- No internal buffering
- No need to buffer the whole frame — works with chunks of a data
- No syscalls
- No allocations
- It can be interrupted at anytime
Tested as part of PHP-ION extension.
Inspired by http-parser by Ryan Dahl and multipart-parser by Igor Afonov.
Production ready.
Use http-parser for parsing headers. This library parse only websocket frames.
This parser library works with several callbacks, which the user may set up at application initialization time.
websocket_parser_settings settings;
websocket_parser_settings_init(&settings);
settings.on_frame_header = websocket_frame_header;
settings.on_frame_body = websocket_frame_body;
settings.on_frame_end = websocket_frame_end;
These functions must match the signatures defined in the websocket-parser header file.
Returning a value other than 0 from the callbacks will abort message processing.
One websocket_parser object is used per TCP connection. Initialize websocket_parser
struct using websocket_parser_init()
and set callbacks:
websocket_parser_settings settings;
websocket_parser_settings_init(&settings);
settings.on_frame_header = websocket_frame_header;
settings.on_frame_body = websocket_frame_body;
settings.on_frame_end = websocket_frame_end;
parser = malloc(sizeof(websocket_parser));
websocket_parser_init(parser);
// Attention! Sets your <data> after websocket_parser_init
parser->data = my_frame_struct;
Basically, callback looks like that:
int websocket_frame_header(websocket_parser * parser) {
parser->data->opcode = parser->flags & WS_OP_MASK; // gets opcode
parser->data->is_final = parser->flags & WS_FIN; // checks is final frame
if(parser->length) {
parser->data->body = malloc(parser->length); // allocate memory for frame body, if body exists
}
return 0;
}
int ion_websocket_frame_body(websocket_parser * parser, const char *at, size_t size) {
if(parser->flags & WS_HAS_MASK) {
// if frame has mask, we have to copy and decode data via websocket_parser_copy_masked function
websocket_parser_decode(&parser->data->body[parser->offset], at, length, parser);
} else {
memcpy(&parser->data->body[parser->offset], at, length);
}
return 0;
}
int websocket_frame_end(websocket_parser * parser) {
my_app_push_frame(parser->data); // use parsed frame
}
When data is received execute the parser and check for errors.
size_t nread;
// .. init settitngs and parser ...
nread = websocket_parser_execute(parser, &settings, data, data_len);
if(nread != data_len) {
// some callback return a value other than 0
}
// ...
free(parser);
To calculate how many bytes to allocate for a frame, use the websocket_calc_frame_size
function:
size_t frame_len = websocket_calc_frame_size(WS_OP_TEXT | WS_FINAL_FRAME | WS_HAS_MASK, data_len);
char * frame = malloc(sizeof(char) * frame_len);
After that you can build a frame
websocket_build_frame(frame, WS_OP_TEXT | WS_FINAL_FRAME | WS_HAS_MASK, mask, data, data_len);
and send binary string to the socket
write(sock, frame, frame_len);
Macros WEBSOCKET_UUID contains unique ID for handshake
#define WEBSOCKET_UUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
There is binary websocket frame example:
- Raw frame:
\x81\x8Amask\x0B\x13\x12\x06\x08\x41\x17\x0A\x19\x00
- Has mask: yes
- Mask:
mask
- Payload:
frame data
- Fin: yes
- Opcode:
WS_OP_TEXT