blob: af0864f760a091b14202697589b053a3416cf089 [file] [log] [blame] [raw]
/*
Copyright: Boaz segev, 2017
License: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#include "http2_parser.h"
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
#include <endian.h>
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define __BIG_ENDIAN__
#endif
#endif
#ifdef __BIG_ENDIAN__
#define b2i16(s) \
(((uint32_t)((s)[1]) << 8) | ((uint32_t)((s)[0]))
#define b2i32(s) \
(((uint32_t)((s)[3]) << 24) | ((uint32_t)((s)[2]) << 16) | \
((uint32_t)((s)[1]) << 8) | ((uint32_t)((s)[0])))
#else
#define b2i16(s) (((uint32_t)((s)[0]) << 8) | ((uint32_t)((s)[1])))
#define b2i32(s) \
(((uint32_t)((s)[0]) << 24) | ((uint32_t)((s)[1]) << 16) | \
((uint32_t)((s)[2]) << 8) | ((uint32_t)((s)[3])))
#endif
/* *****************************************************************************
Parser data structure
***************************************************************************** */
/** an opaque parser pointer type for the HTTP 2 parser. */
typedef struct http2_parser_s {
/* http2 settings. */
const struct http2_parser_create_args_s settings;
/* http2 persistent data. */
uint32_t required_stream_id;
uint32_t reserverd_local;
uint32_t reserverd_remote;
uint8_t end_of_stream;
/* frame parsing state */
struct h2parser_state_s {
size_t expecting;
size_t missing;
unsigned length : 24;
unsigned type : 8;
unsigned flags : 8;
unsigned rsv : 1;
unsigned id : 31;
} state;
uint8_t frame[];
} http2_parser_s;
typedef struct {
struct {
unsigned length : 24;
unsigned type : 8;
unsigned flags : 8;
unsigned rsv : 1;
unsigned id : 31;
} head;
uint8_t *data;
} http2_frame_s;
/* *****************************************************************************
Mock Callbacks
***************************************************************************** */
/* *****************************************************************************
Create / Destroy / initialize
***************************************************************************** */
/** resets an HTTP/2 parser (uses reference counting). */
void http2_parser_reset(http2_parser_pt p) {
*p = (http2_parser_s){.settings = p->settings};
}
/** creates an HTTP/2 parser */
#undef http2_parser_create
http2_parser_pt http2_parser_create(struct http2_parser_create_args_s args);
/** destroy an HTTP/2 parser (uses reference counting). */
void http2_parser_destroy(http2_parser_pt);
/* *****************************************************************************
Frame handling
***************************************************************************** */
static int h2p_unwrap_frame(http2_parser_pt p) {
if (p->required_stream_id && p->state.type != 0x9)
return H2ERR_PROTOCOL_ERROR;
switch (p->state.type) {
case 0x0: { /* Data frames */
if (!p->state.id)
return H2ERR_PROTOCOL_ERROR;
uint8_t *data = p->frame;
size_t len = p->state.length;
if (p->state.flags & 0x8) {
/* padding (PADDED) */
data += p->frame[0];
len -= p->frame[0];
}
p->settings.callbacks->on_body(p, p->settings.udata, p->state.id, data,
len);
if (p->state.flags & 0x1) /* final frame (END_STREAM) */
p->settings.callbacks->on_finalized(p, p->settings.udata, p->state.id);
break;
}
case 0x1: { /* Header frames */
break;
}
case 0x2: { /* PRIORITY frames */
if (!p->state.id)
return H2ERR_PROTOCOL_ERROR;
uint32_t dep_id = b2i32(p->frame);
p->settings.callbacks->on_priority(p, p->settings.udata, p->state.id,
(dep_id & 0x7FFF), (dep_id >> 31),
p->frame[5]);
break;
}
case 0x3: { /* RST_STREAM frames */
if (!p->state.id)
return H2ERR_PROTOCOL_ERROR;
p->settings.callbacks->on_reset_stream(p, p->settings.udata, p->state.id,
b2i32(p->frame));
break;
}
case 0x4: { /* SETTINGS frames */
if (p->state.id)
return H2ERR_PROTOCOL_ERROR;
p->settings.callbacks->on_settings(p, p->settings.udata, b2i16((p->frame)),
b2i32((p->frame + 2)),
p->state.flags & 0x1);
break;
}
case 0x5: { /* PUSH_PROMISE frames */
break;
}
case 0x6: { /* PING frames */
if (p->state.length != 8)
return H2ERR_FRAME_SIZE_ERROR;
if (p->state.id)
return H2ERR_PROTOCOL_ERROR;
union {
uint64_t i;
uint8_t s[8];
} payload;
for (size_t i = 0; i < 8; i++) {
payload.s[i] = p->frame[i];
}
p->settings.callbacks->on_ping(p, p->settings.udata, payload.i,
p->state.flags & 0x1);
break;
}
case 0x7: { /* GOAWAY frames */
break;
}
case 0x8: { /* WINDOW_UPDATE frames */
break;
}
case 0x9: { /* CONTINUATION frames */
break;
}
}
return 0;
}
/* *****************************************************************************
Feeding the parser (direct write + notification)
***************************************************************************** */
/**
* Gets the current position in the HTTP/2 parser's buffer.
*
* This changes with every read according to the amount of data the parser
* requires to complete it's next step.
*/
void *http2_parser_buffer(http2_parser_pt p) {
return p->frame + (p->state.expecting - p->state.missing);
}
/**
* Gets the current capacity for the HTTP/2 parser's buffer.
*
* This changes with every read according to the amount of data the parser
* requires to complete it's next step.
*/
size_t http2_parser_capacity(http2_parser_pt p) { return p->state.missing; }
/**
* Signals the parser to process incominng data `length` long.
*
* This updates the `http2_parser_buffer` and `http2_parser_capacity` values.
*/
enum http2_err_enum http2_parser_review(http2_parser_pt p, size_t length) {
struct {
union {
uint8_t len_bytes[3];
unsigned length : 24;
};
unsigned type : 8;
unsigned flags : 8;
union {
uint8_t id_bytes[4];
struct {
unsigned rsv : 1;
unsigned id : 31;
};
};
} translator;
enum http2_err_enum ret = H2ERR_PROTOCOL_ERROR;
if (p->state.missing > length)
goto error;
p->state.missing -= length;
if (p->state.missing)
return H2ERR_OK;
if (p->state.length == 0) {
/* starting a new frame */
#ifdef __BIG_ENDIAN__
translator.len_bytes[0] = p->frame[0];
translator.len_bytes[1] = p->frame[1];
translator.len_bytes[2] = p->frame[2];
translator.id_bytes[0] = p->frame[5];
translator.id_bytes[1] = p->frame[6];
translator.id_bytes[2] = p->frame[7];
translator.id_bytes[3] = p->frame[8];
#else
translator.len_bytes[0] = p->frame[2];
translator.len_bytes[1] = p->frame[1];
translator.len_bytes[2] = p->frame[0];
translator.id_bytes[0] = p->frame[8];
translator.id_bytes[1] = p->frame[7];
translator.id_bytes[2] = p->frame[6];
translator.id_bytes[3] = p->frame[5];
#endif
translator.type = p->frame[3];
translator.flags = p->frame[4];
p->state.missing = p->state.expecting = translator.length;
if (translator.length > p->settings.max_frame_size)
goto error;
if (translator.length)
return H2ERR_OK;
}
/* handle frame */
if ((ret = h2p_unwrap_frame(p)))
goto error;
/* reset parser */
p->state = (struct h2parser_state_s){.expecting = 9, .missing = 9};
return H2ERR_OK;
error:
/* reset parser */
p->state = (struct h2parser_state_s){.expecting = 9, .missing = 9};
return ret;
}