#include "http.h" #include "log.h" #include "mem.h" #include "picohttpparser.h" #include "uv.h" #include #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(_WIN32) #include #endif typedef struct _tf_http_connection_t { tf_http_t* http; uv_tcp_t tcp; char buffer[8192]; size_t buffer_length; int parsed_length; tf_http_callback_t* callback; void* user_data; } tf_http_connection_t; typedef struct _tf_http_handler_t { const char* pattern; tf_http_callback_t* callback; void* user_data; } tf_http_handler_t; typedef struct _tf_http_t { uv_tcp_t** listeners; int listeners_count; tf_http_connection_t** connections; int connections_count; tf_http_handler_t* handlers; int handlers_count; uv_loop_t* loop; } tf_http_t; tf_http_t* tf_http_create(uv_loop_t* loop) { tf_http_t* http = tf_malloc(sizeof(tf_http_t)); *http = (tf_http_t) { .loop = loop, }; return http; } void _http_allocate_buffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) { tf_http_connection_t* connection = handle->data; *buf = uv_buf_init(connection->buffer + connection->buffer_length, sizeof(connection->buffer) - connection->buffer_length); } bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, void** out_user_data) { for (int i = 0; i < http->handlers_count; i++) { if (!http->handlers[i].pattern) { *out_callback = http->handlers[i].callback; *out_user_data = http->handlers[i].user_data; return true; } } return false; } void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffer) { tf_http_connection_t* connection = stream->data; if (read_size > 0) { connection->buffer_length += read_size; const char* method = NULL; size_t method_length = 0; const char* path = NULL; size_t path_length = 0; int minor_version = 0; struct phr_header headers[100]; size_t header_count = sizeof(headers) / sizeof(*headers); int parse_result = phr_parse_request(connection->buffer, connection->buffer_length, &method, &method_length, &path, &path_length, &minor_version, headers, &header_count, connection->parsed_length); connection->parsed_length = connection->buffer_length; if (parse_result > 0) { if (_http_find_handler(connection->http, path, &connection->callback, &connection->user_data) && connection->callback) { char* method_copy = alloca(method_length + 1); memcpy(method_copy, method, method_length); method_copy[method_length] = '\0'; char* path_copy = alloca(path_length + 1); memcpy(path_copy, path, path_length); path_copy[path_length] = '\0'; tf_http_request_t request = { .connection = connection, .phase = k_http_callback_phase_headers_received, .method = method_copy, .path = path_copy, .headers = headers, .headers_count = (int)header_count, .user_data = connection->user_data, }; connection->callback(&request); } } else { tf_printf("phr_parse_request: %d\n", parse_result); } } } static void _http_on_connection(uv_stream_t* stream, int status) { tf_http_t* http = stream->data; tf_http_connection_t* connection = tf_malloc(sizeof(tf_http_connection_t)); *connection = (tf_http_connection_t) { .http = http, .tcp = { .data = connection } }; int r = uv_tcp_init(connection->http->loop, &connection->tcp); if (r) { tf_printf("uv_tcp_init: %s\n", uv_strerror(r)); tf_free(connection); return; } r = uv_accept(stream, (uv_stream_t*)&connection->tcp); if (r) { tf_printf("uv_accept: %s\n", uv_strerror(r)); uv_close((uv_handle_t*)&connection->tcp, NULL); tf_free(connection); return; } r = uv_read_start((uv_stream_t*)&connection->tcp, _http_allocate_buffer, _http_on_read); if (r) { tf_printf("uv_read-start: %s\n", uv_strerror(r)); uv_close((uv_handle_t*)&connection->tcp, NULL); tf_free(connection); return; } http->connections = tf_realloc(http->connections, http->connections_count + 1); http->connections[http->connections_count++] = connection; } void tf_http_listen(tf_http_t* http, int port) { uv_tcp_t* tcp = tf_malloc(sizeof(uv_tcp_t)); *tcp = (uv_tcp_t) { .data = http }; int r = uv_tcp_init(http->loop, tcp); if (r) { tf_printf("uv_tcp_init: %s\n", uv_strerror(r)); } if (r == 0) { struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr = { .s_addr = INADDR_ANY }, .sin_port = ntohs(port), }; r = uv_tcp_bind(tcp, (struct sockaddr*)&addr, 0); if (r) { tf_printf("uv_tcp_bind: %s\n", uv_strerror(r)); } } if (r == 0) { r = uv_listen((uv_stream_t*)tcp, 16, _http_on_connection); if (r) { tf_printf("uv_listen: %s\n", uv_strerror(r)); } } if (r == 0) { http->listeners = tf_realloc(http->listeners, sizeof(uv_tcp_t*) * (http->listeners_count + 1)); http->listeners[http->listeners_count++] = tcp; } } void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data) { http->handlers = tf_realloc(http->handlers, sizeof(tf_http_handler_t) * (http->handlers_count + 1)); http->handlers[http->handlers_count++] = (tf_http_handler_t) { .pattern = tf_strdup(pattern), .callback = callback, .user_data = user_data, }; } void tf_http_destroy(tf_http_t* http) { tf_free(http); }