cMCP/http2.c

718 lines
22 KiB
C

#include "http2.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
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";
}
}