#include "http2.h" #include #include #include #include #include #include static const char *HTTP2_CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; static const size_t HTTP2_FRAME_HEADER_SIZE = 9; int http2_init(void) { return 0; } static int http2_buffer_init(HTTP2_Buffer *buf, size_t initial_capacity) { if (buf == NULL) return -1; buf->data = malloc(initial_capacity); if (buf->data == NULL) return -1; buf->size = 0; buf->capacity = initial_capacity; return 0; } static int http2_buffer_grow(HTTP2_Buffer *buf, size_t needed) { if (buf == NULL || buf->data == NULL) return -1; size_t new_cap = buf->capacity * 2; while (new_cap < buf->size + needed) { new_cap *= 2; } uint8_t *new_data = realloc(buf->data, new_cap); if (new_data == NULL) return -1; buf->data = new_data; buf->capacity = new_cap; return 0; } static void http2_buffer_free(HTTP2_Buffer *buf) { if (buf == NULL) return; free(buf->data); buf->data = NULL; buf->size = 0; buf->capacity = 0; } HTTP2_Connection *http2_connection_new(void) { HTTP2_Connection *conn = malloc(sizeof(HTTP2_Connection)); if (conn == NULL) return NULL; memset(conn, 0, sizeof(*conn)); if (http2_buffer_init(&conn->read_buffer, 4096) != 0) { free(conn); return NULL; } if (http2_buffer_init(&conn->write_buffer, 4096) != 0) { http2_buffer_free(&conn->read_buffer); free(conn); return NULL; } if (http2_hpack_encode_init(&conn->encoder, HTTP2_DEFAULT_HEADER_TABLE_SIZE) != 0) { http2_buffer_free(&conn->read_buffer); http2_buffer_free(&conn->write_buffer); free(conn); return NULL; } if (http2_hpack_decode_init(&conn->decoder, HTTP2_DEFAULT_HEADER_TABLE_SIZE) != 0) { http2_hpack_encode_free(&conn->encoder); http2_buffer_free(&conn->read_buffer); http2_buffer_free(&conn->write_buffer); free(conn); return NULL; } conn->local_settings[HTTP2_SETTINGS_HEADER_TABLE_SIZE] = HTTP2_DEFAULT_HEADER_TABLE_SIZE; conn->local_settings[HTTP2_SETTINGS_ENABLE_PUSH] = 1; conn->local_settings[HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = HTTP2_MAX_CONCURRENT_STREAMS; conn->local_settings[HTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = HTTP2_INITIAL_WINDOW_SIZE; conn->local_settings[HTTP2_SETTINGS_MAX_FRAME_SIZE] = HTTP2_MAX_FRAME_SIZE; conn->local_settings[HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE] = 65535; conn->remote_settings[HTTP2_SETTINGS_HEADER_TABLE_SIZE] = HTTP2_DEFAULT_HEADER_TABLE_SIZE; conn->remote_settings[HTTP2_SETTINGS_ENABLE_PUSH] = 1; conn->remote_settings[HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = HTTP2_MAX_CONCURRENT_STREAMS; conn->remote_settings[HTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_settings[HTTP2_SETTINGS_MAX_FRAME_SIZE] = HTTP2_MAX_FRAME_SIZE; conn->remote_settings[HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE] = 65535; conn->next_stream_id = 1; conn->local_window_size = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_window_size = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_concurrent_streams = HTTP2_MAX_CONCURRENT_STREAMS; conn->last_stream_id = 0; return conn; } int http2_connection_init(HTTP2_Connection *conn, int sockfd, int is_server) { if (conn == NULL) return -1; memset(conn, 0, sizeof(*conn)); conn->sockfd = sockfd; conn->is_server = is_server; conn->next_stream_id = is_server ? 2 : 1; conn->local_window_size = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_window_size = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_concurrent_streams = HTTP2_MAX_CONCURRENT_STREAMS; conn->local_settings[HTTP2_SETTINGS_HEADER_TABLE_SIZE] = HTTP2_DEFAULT_HEADER_TABLE_SIZE; conn->local_settings[HTTP2_SETTINGS_ENABLE_PUSH] = 1; conn->local_settings[HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = HTTP2_MAX_CONCURRENT_STREAMS; conn->local_settings[HTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = HTTP2_INITIAL_WINDOW_SIZE; conn->local_settings[HTTP2_SETTINGS_MAX_FRAME_SIZE] = HTTP2_MAX_FRAME_SIZE; conn->local_settings[HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE] = 65535; conn->remote_settings[HTTP2_SETTINGS_HEADER_TABLE_SIZE] = HTTP2_DEFAULT_HEADER_TABLE_SIZE; conn->remote_settings[HTTP2_SETTINGS_ENABLE_PUSH] = 1; conn->remote_settings[HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = HTTP2_MAX_CONCURRENT_STREAMS; conn->remote_settings[HTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = HTTP2_INITIAL_WINDOW_SIZE; conn->remote_settings[HTTP2_SETTINGS_MAX_FRAME_SIZE] = HTTP2_MAX_FRAME_SIZE; conn->remote_settings[HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE] = 65535; if (http2_buffer_init(&conn->read_buffer, 4096) != 0) return -1; if (http2_buffer_init(&conn->write_buffer, 4096) != 0) { http2_buffer_free(&conn->read_buffer); return -1; } return 0; } void http2_connection_free(HTTP2_Connection *conn) { if (conn == NULL) return; http2_buffer_free(&conn->read_buffer); http2_buffer_free(&conn->write_buffer); free(conn); } void http2_connection_close(HTTP2_Connection *conn) { if (conn == NULL) return; if (conn->sockfd >= 0) { close(conn->sockfd); conn->sockfd = -1; } } int http2_send_preface(HTTP2_Connection *conn) { if (conn == NULL || conn->sockfd < 0) return -1; if (send(conn->sockfd, HTTP2_CONNECTION_PREFACE, strlen(HTTP2_CONNECTION_PREFACE), 0) < 0) { perror("Failed to send HTTP/2 connection preface"); return -1; } uint32_t settings[12] = { HTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096, HTTP2_SETTINGS_ENABLE_PUSH, 1, HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100, HTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535, HTTP2_SETTINGS_MAX_FRAME_SIZE, 16384, HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, 65535 }; http2_encode_settings(conn, settings, 6); return 0; } int http2_recv_preface(HTTP2_Connection *conn) { if (conn == NULL || conn->sockfd < 0) return -1; char preface[24] = {0}; ssize_t received = recv(conn->sockfd, preface, sizeof(preface) - 1, MSG_PEEK); if (received < 0) { perror("Failed to recv HTTP/2 connection preface"); return -1; } if ((size_t)received < strlen(HTTP2_CONNECTION_PREFACE)) { received = recv(conn->sockfd, preface, strlen(HTTP2_CONNECTION_PREFACE), 0); if (received < 0) { perror("Failed to recv HTTP/2 connection preface"); return -1; } if (strncmp(preface, HTTP2_CONNECTION_PREFACE, strlen(HTTP2_CONNECTION_PREFACE)) != 0) { fprintf(stderr, "Invalid HTTP/2 connection preface\n"); return -1; } } return 0; } int http2_encode_frame(HTTP2_Frame *frame, HTTP2_Frame_Type type, uint32_t stream_id, uint8_t flags, const uint8_t *payload, size_t payload_len) { if (frame == NULL || payload_len > HTTP2_MAX_FRAME_SIZE) return -1; frame->type = type; frame->flags = flags; frame->stream_id = stream_id; frame->length = (uint32_t)payload_len; if (payload != NULL && payload_len > 0) { memcpy(frame->payload, payload, payload_len); } return 0; } static size_t http2_format_frame(const HTTP2_Frame *frame, uint8_t *out) { if (frame == NULL || out == NULL) return 0; out[0] = (uint8_t)((frame->length >> 16) & 0xFF); out[1] = (uint8_t)((frame->length >> 8) & 0xFF); out[2] = (uint8_t)(frame->length & 0xFF); out[3] = (uint8_t)frame->type; out[4] = frame->flags; uint32_t stream_id = frame->stream_id; out[5] = (uint8_t)((stream_id >> 24) & 0xFF); out[6] = (uint8_t)((stream_id >> 16) & 0xFF); out[7] = (uint8_t)((stream_id >> 8) & 0xFF); out[8] = (uint8_t)(stream_id & 0xFF); if (frame->length > 0) { memcpy(out + HTTP2_FRAME_HEADER_SIZE, frame->payload, frame->length); } return HTTP2_FRAME_HEADER_SIZE + frame->length; } int http2_decode_frame(HTTP2_Frame *frame, const uint8_t *data, size_t len) { if (frame == NULL || data == NULL || len < HTTP2_FRAME_HEADER_SIZE) return -1; frame->length = ((uint32_t)data[0] << 16) | ((uint32_t)data[1] << 8) | (uint32_t)data[2]; frame->type = (HTTP2_Frame_Type)data[3]; frame->flags = data[4]; frame->stream_id = ((uint32_t)data[5] << 24) | ((uint32_t)data[6] << 16) | ((uint32_t)data[7] << 8) | (uint32_t)data[8]; if (frame->length > 0 && len >= HTTP2_FRAME_HEADER_SIZE + frame->length) { memcpy(frame->payload, data + HTTP2_FRAME_HEADER_SIZE, frame->length); } else if (frame->length > 0) { return -1; } return 0; } int http2_recv_frame(HTTP2_Connection *conn, HTTP2_Frame *frame) { if (conn == NULL || frame == NULL || conn->sockfd < 0) return -1; uint8_t header[HTTP2_FRAME_HEADER_SIZE]; ssize_t received = recv(conn->sockfd, header, HTTP2_FRAME_HEADER_SIZE, 0); if (received < 0) { perror("Failed to recv HTTP/2 frame header"); return -1; } if ((size_t)received < HTTP2_FRAME_HEADER_SIZE) { return -1; } if (http2_decode_frame(frame, header, HTTP2_FRAME_HEADER_SIZE) != 0) return -1; if (frame->length > 0) { size_t total_len = HTTP2_FRAME_HEADER_SIZE + frame->length; if (conn->read_buffer.capacity < total_len) { if (http2_buffer_grow(&conn->read_buffer, total_len) != 0) return -1; } received = recv(conn->sockfd, conn->read_buffer.data, frame->length, 0); if (received < 0) { perror("Failed to recv HTTP/2 frame payload"); return -1; } memcpy(frame->payload, conn->read_buffer.data, received); frame->length = (uint32_t)received; } return 0; } int http2_send_frame(HTTP2_Connection *conn, HTTP2_Frame *frame) { if (conn == NULL || frame == NULL || conn->sockfd < 0) return -1; size_t total_len = HTTP2_FRAME_HEADER_SIZE + frame->length; if (conn->write_buffer.capacity < total_len) { if (http2_buffer_grow(&conn->write_buffer, total_len) != 0) return -1; } size_t written = http2_format_frame(frame, conn->write_buffer.data); ssize_t sent = send(conn->sockfd, conn->write_buffer.data, written, 0); if (sent < 0) { perror("Failed to send HTTP/2 frame"); return -1; } return 0; } int http2_encode_settings(HTTP2_Connection *conn, uint32_t *settings, size_t count) { if (conn == NULL || settings == NULL) return -1; uint8_t payload[256] = {0}; size_t payload_len = 0; for (size_t i = 0; i < count; i++) { uint16_t id = (uint16_t)(settings[i * 2]); uint32_t value = settings[i * 2 + 1]; payload[payload_len++] = (uint8_t)((id >> 8) & 0xFF); payload[payload_len++] = (uint8_t)(id & 0xFF); payload[payload_len++] = (uint8_t)((value >> 24) & 0xFF); payload[payload_len++] = (uint8_t)((value >> 16) & 0xFF); payload[payload_len++] = (uint8_t)((value >> 8) & 0xFF); payload[payload_len++] = (uint8_t)(value & 0xFF); if (id > 0 && id <= HTTP2_SETTINGS_MAX_PARAM_ID) { conn->local_settings[id] = value; } } HTTP2_Frame frame = {0}; http2_encode_frame(&frame, HTTP2_FRAME_SETTINGS, 0, 0, payload, payload_len); return http2_send_frame(conn, &frame); } int http2_decode_settings(const HTTP2_Frame *frame, uint32_t *settings, size_t *count) { if (frame == NULL || settings == NULL || count == NULL) return -1; if (frame->type != HTTP2_FRAME_SETTINGS) return -1; *count = 0; size_t i = 0; while (i < frame->length) { if (i + 5 >= frame->length) break; uint16_t id = ((uint16_t)frame->payload[i] << 8) | frame->payload[i + 1]; uint32_t value = ((uint32_t)frame->payload[i + 2] << 24) | ((uint32_t)frame->payload[i + 3] << 16) | ((uint32_t)frame->payload[i + 4] << 8) | frame->payload[i + 5]; settings[(*count) * 2] = id; settings[(*count) * 2 + 1] = value; (*count)++; i += 6; } return 0; } int http2_encode_priority(HTTP2_Priority_Spec *prio, uint8_t *out, size_t *out_len) { if (prio == NULL || out == NULL || out_len == NULL) return -1; uint8_t *p = out; *p++ = (uint8_t)((prio->stream_dependency >> 24) & 0xFF); *p++ = (uint8_t)((prio->stream_dependency >> 16) & 0xFF); *p++ = (uint8_t)((prio->stream_dependency >> 8) & 0xFF); *p++ = (uint8_t)(prio->stream_dependency & 0xFF); *p++ = (prio->exclusive << 7) | prio->weight; *out_len = 5; return 0; } int http2_decode_priority(const uint8_t *data, size_t len, HTTP2_Priority_Spec *prio) { if (data == NULL || prio == NULL || len < 5) return -1; prio->stream_dependency = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | ((uint32_t)data[2] << 8) | (uint32_t)data[3]; prio->exclusive = (data[4] >> 7) & 0x1; prio->weight = (data[4] & 0xFF) + 1; return 0; } int http2_hpack_encode_init(HTTP2_Encoder_Context *ctx, uint32_t max_size) { if (ctx == NULL) return -1; memset(ctx, 0, sizeof(*ctx)); ctx->max_size = max_size; ctx->current_size = 0; ctx->dynamic_table.cap = 128; ctx->dynamic_table.data = malloc(ctx->dynamic_table.cap); if (ctx->dynamic_table.data == NULL) return -1; ctx->dynamic_table.len = 0; return 0; } static uint32_t http2_hpack_table_size(uint32_t entries) { return 32 + entries * 32; } static int http2_hpack_evict(HTTP2_Encoder_Context *ctx, uint32_t amount) { if (ctx == NULL) return -1; while (ctx->current_size + amount > ctx->max_size && ctx->dynamic_table.len > 0) { ctx->current_size -= 32; ctx->dynamic_table.len--; } return 0; } static int http2_hpack_encode_indexed(uint8_t idx, uint8_t *out, size_t *out_len) { if (out == NULL || out_len == NULL) return -1; if (idx < 61) { out[0] = 0x80 | idx; *out_len = 1; } else { out[0] = 0x80 | 0x3F; out[1] = (uint8_t)(idx - 60); *out_len = 2; } return 0; } static int http2_hpack_encode_literal(const uint8_t *name, size_t name_len, const uint8_t *value, size_t value_len, uint8_t idx_mod, uint8_t *out, size_t *out_len) { if (name == NULL || value == NULL || out == NULL || out_len == NULL) return -1; size_t pos = 0; out[pos++] = idx_mod; if (name_len < 61) { out[pos++] = (uint8_t)name_len; } else { out[pos++] = 0x3F; out[pos++] = (uint8_t)(name_len - 60); } memcpy(out + pos, name, name_len); pos += name_len; if (value_len < 61) { out[pos++] = (uint8_t)value_len; } else { out[pos++] = 0x3F; out[pos++] = (uint8_t)(value_len - 60); } memcpy(out + pos, value, value_len); pos += value_len; *out_len = pos; return 0; } int http2_hpack_encode(HTTP2_Encoder_Context *ctx, const char **headers, size_t headers_len, uint8_t *out, size_t *out_len) { if (ctx == NULL || headers == NULL || out == NULL || out_len == NULL) return -1; size_t pos = 0; for (size_t i = 0; i < headers_len; i += 2) { const char *name = headers[i]; const char *value = headers[i + 1]; size_t name_len = strlen(name); size_t value_len = strlen(value); if (http2_hpack_encode_literal((const uint8_t *)name, name_len, (const uint8_t *)value, value_len, 0x40, out + pos, &pos) != 0) { continue; } } *out_len = pos; return 0; } int http2_hpack_decode_init(HTTP2_Decoder_Context *ctx, uint32_t max_size) { if (ctx == NULL) return -1; memset(ctx, 0, sizeof(*ctx)); ctx->max_size = max_size; ctx->current_size = 0; ctx->dynamic_table.cap = 128; ctx->dynamic_table.data = malloc(ctx->dynamic_table.cap); if (ctx->dynamic_table.data == NULL) return -1; ctx->dynamic_table.len = 0; ctx->mirror = 0; return 0; } void http2_hpack_encode_free(HTTP2_Encoder_Context *ctx) { if (ctx == NULL) return; free(ctx->dynamic_table.data); ctx->dynamic_table.data = NULL; ctx->dynamic_table.len = 0; } void http2_hpack_decode_free(HTTP2_Decoder_Context *ctx) { if (ctx == NULL) return; free(ctx->dynamic_table.data); ctx->dynamic_table.data = NULL; ctx->dynamic_table.len = 0; } int http2_hpack_decode(HTTP2_Decoder_Context *ctx, const uint8_t *data, size_t len, char **headers, size_t *headers_len) { if (ctx == NULL || data == NULL || headers == NULL || headers_len == NULL) return -1; *headers_len = 0; size_t pos = 0; while (pos < len) { uint8_t byte = data[pos]; if ((byte & 0x80) != 0) { pos++; continue; } if ((byte & 0x40) != 0) { pos++; continue; } pos++; } return 0; } static int http2_static_table_index(const char *name, size_t name_len) { static const char *names[] = { ":authority", ":method", ":method", ":path", ":path", ":scheme", ":status", ":status", ":status", ":status", ":status", ":status", ":status", ":status", ":status", "accept-encoding", "accept-language", "accept-ranges", "age", "allow", "authorization", "cache-control", "content-encoding", "content-language", "content-length", "content-location", "content-range", "content-type", "cookie", "date", "etag", "expect", "expires", "from", "host", "if-match", "if-modified-since", "if-none-match", "if-range", "if-unmodified-since", "last-modified", "link", "location", "max-forwards", "proxy-authenticate", "proxy-authorization", "range", "referer", "refresh", "retry-after", "server", "set-cookie", "strict-transport-security", "transfer-encoding", "user-agent", "vary", "via", "www-authenticate" }; for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) { if (strncmp(name, names[i], name_len) == 0) { return (int)(i + 1); } } return 0; } int http2_encode_headers(HTTP2_Connection *conn, const char **headers, size_t headers_len, uint8_t *out, size_t *out_len) { if (conn == NULL || headers == NULL || out == NULL || out_len == NULL) return -1; return http2_hpack_encode(&conn->encoder, headers, headers_len, out, out_len); } int http2_decode_headers(HTTP2_Connection *conn, const uint8_t *data, size_t len, char **headers, size_t *headers_len) { if (conn == NULL || data == NULL || headers == NULL || headers_len == NULL) return -1; return http2_hpack_decode(&conn->decoder, data, len, headers, headers_len); } int http2_encode_priority_frame(uint32_t stream_id, HTTP2_Priority_Spec *prio, uint8_t *out, size_t *out_len) { if (prio == NULL || out == NULL || out_len == NULL) return -1; if (stream_id == 0 || (stream_id & 1) == 0) return -1; HTTP2_Frame frame = {0}; http2_encode_priority(prio, frame.payload, out_len); http2_encode_frame(&frame, HTTP2_FRAME_PRIORITY, stream_id, 0, frame.payload, *out_len); return 0; } int http2_encode_rst_stream(uint32_t stream_id, HTTP2_Error_Code error, uint8_t *out, size_t *out_len) { if (out == NULL || out_len == NULL) return -1; if (stream_id == 0 || (stream_id & 1) == 0) return -1; out[0] = (uint8_t)((error >> 24) & 0xFF); out[1] = (uint8_t)((error >> 16) & 0xFF); out[2] = (uint8_t)((error >> 8) & 0xFF); out[3] = (uint8_t)(error & 0xFF); *out_len = 4; return 0; } int http2_encode_ping(HTTP2_Connection *conn, uint8_t *data, uint8_t flags, uint8_t *out, size_t *out_len) { if (conn == NULL || data == NULL || out == NULL || out_len == NULL) return -1; HTTP2_Frame frame = {0}; memcpy(frame.payload, data, 8); http2_encode_frame(&frame, HTTP2_FRAME_PING, 0, flags, frame.payload, 8); *out_len = HTTP2_FRAME_HEADER_SIZE + 8; return 0; } int http2_encode_goaway(uint32_t last_stream_id, HTTP2_Error_Code error, const uint8_t *additional_debug, size_t debug_len, uint8_t *out, size_t *out_len) { if (out == NULL || out_len == NULL) return -1; if (last_stream_id != 0 && (last_stream_id & 1) == 0) return -1; uint8_t *p = out; *p++ = (uint8_t)((last_stream_id >> 24) & 0xFF); *p++ = (uint8_t)((last_stream_id >> 16) & 0xFF); *p++ = (uint8_t)((last_stream_id >> 8) & 0xFF); *p++ = (uint8_t)(last_stream_id & 0xFF); *p++ = (uint8_t)((error >> 24) & 0xFF); *p++ = (uint8_t)((error >> 16) & 0xFF); *p++ = (uint8_t)((error >> 8) & 0xFF); *p++ = (uint8_t)(error & 0xFF); if (additional_debug != NULL && debug_len > 0) { memcpy(p, additional_debug, debug_len); p += debug_len; } *out_len = (size_t)(p - out); return 0; } int http2_encode_window_update(uint32_t stream_id, uint32_t increment, uint8_t *out, size_t *out_len) { if (out == NULL || out_len == NULL) return -1; if (increment == 0 || increment > 2147483647) return -1; out[0] = (uint8_t)((increment >> 24) & 0xFF); out[1] = (uint8_t)((increment >> 16) & 0xFF); out[2] = (uint8_t)((increment >> 8) & 0xFF); out[3] = (uint8_t)(increment & 0xFF); *out_len = 4; return 0; } int http2_parse_frame_type(const char *name) { if (name == NULL) return -1; if (strcmp(name, "DATA") == 0) return HTTP2_FRAME_DATA; if (strcmp(name, "HEADERS") == 0) return HTTP2_FRAME_HEADERS; if (strcmp(name, "PRIORITY") == 0) return HTTP2_FRAME_PRIORITY; if (strcmp(name, "RST_STREAM") == 0) return HTTP2_FRAME_RST_STREAM; if (strcmp(name, "SETTINGS") == 0) return HTTP2_FRAME_SETTINGS; if (strcmp(name, "PUSH_PROMISE") == 0) return HTTP2_FRAME_PUSH_PROMISE; if (strcmp(name, "PING") == 0) return HTTP2_FRAME_PING; if (strcmp(name, "GOAWAY") == 0) return HTTP2_FRAME_GOAWAY; if (strcmp(name, "WINDOW_UPDATE") == 0) return HTTP2_FRAME_WINDOW_UPDATE; if (strcmp(name, "CONTINUATION") == 0) return HTTP2_FRAME_CONTINUATION; return -1; } const char *http2_frame_type_to_str(HTTP2_Frame_Type type) { switch (type) { case HTTP2_FRAME_DATA: return "DATA"; case HTTP2_FRAME_HEADERS: return "HEADERS"; case HTTP2_FRAME_PRIORITY: return "PRIORITY"; case HTTP2_FRAME_RST_STREAM: return "RST_STREAM"; case HTTP2_FRAME_SETTINGS: return "SETTINGS"; case HTTP2_FRAME_PUSH_PROMISE: return "PUSH_PROMISE"; case HTTP2_FRAME_PING: return "PING"; case HTTP2_FRAME_GOAWAY: return "GOAWAY"; case HTTP2_FRAME_WINDOW_UPDATE: return "WINDOW_UPDATE"; case HTTP2_FRAME_CONTINUATION: return "CONTINUATION"; default: return "UNKNOWN"; } } const char *http2_error_to_str(HTTP2_Error_Code error) { switch (error) { case HTTP2_ERROR_NO_ERROR: return "NO_ERROR"; case HTTP2_ERROR_PROTOCOL_ERROR: return "PROTOCOL_ERROR"; case HTTP2_ERROR_INTERNAL_ERROR: return "INTERNAL_ERROR"; case HTTP2_ERROR_FLOW_CONTROL_ERROR: return "FLOW_CONTROL_ERROR"; case HTTP2_ERROR_SETTINGS_TIMEOUT: return "SETTINGS_TIMEOUT"; case HTTP2_ERROR_STREAM_CLOSED: return "STREAM_CLOSED"; case HTTP2_ERROR_FRAME_SIZE_ERROR: return "FRAME_SIZE_ERROR"; case HTTP2_ERROR_REFUSED_STREAM: return "REFUSED_STREAM"; case HTTP2_ERROR_CANCEL: return "CANCEL"; case HTTP2_ERROR_COMPRESSION_ERROR: return "COMPRESSION_ERROR"; case HTTP2_ERROR_CONNECT_ERROR: return "CONNECT_ERROR"; case HTTP2_ERROR_ENHANCE_YOUR_CLAIM: return "ENHANCE_YOUR_CLAIM"; case HTTP2_ERROR_INADEQUATE_SECURITY: return "INADEQUATE_SECURITY"; case HTTP2_ERROR_HTTP_1_1_REQUIRED: return "HTTP_1_1_REQUIRED"; default: return "UNKNOWN"; } }