diff options
| author | spl3g <spleefer6@yandex.ru> | 2025-03-22 21:41:16 +0300 |
|---|---|---|
| committer | spl3g <spleefer6@yandex.ru> | 2025-03-22 21:41:16 +0300 |
| commit | 62454373db4cd2add82ded0cf7b21f7d69ade597 (patch) | |
| tree | 1627133fe4567e9a3abb707af5e7289353410d5f /lib/http.c | |
| parent | ef5f52fb739c9f0d71c579f08363ef0dfd5c227d (diff) | |
Reorganize the lib
Diffstat (limited to 'lib/http.c')
| -rw-r--r-- | lib/http.c | 586 |
1 files changed, 0 insertions, 586 deletions
diff --git a/lib/http.c b/lib/http.c deleted file mode 100644 index 64b357b..0000000 --- a/lib/http.c +++ /dev/null @@ -1,586 +0,0 @@ -#include "http.h" - -#define MAX_DATA_SIZE 256 -#define BUFFER_SIZE 4096 -#define REQUEST_ARENA_SIZE 1024 - -int volatile keepRunning = 1; - -struct { - http_response_code code; - const_string string; -} response_strings [] = { - {100, CS_STATIC("Continue")}, - {101, CS_STATIC("Switching Protocols")}, - {102, CS_STATIC("Processing")}, - {103, CS_STATIC("Early Hints")}, - {200, CS_STATIC("OK")}, - {201, CS_STATIC("Created")}, - {202, CS_STATIC("Accepted")}, - {204, CS_STATIC("No Content")}, - {300, CS_STATIC("Multiple Choices")}, - {301, CS_STATIC("Moved Permanently")}, - {302, CS_STATIC("Found")}, - {303, CS_STATIC("See Other")}, - {304, CS_STATIC("Not Modified")}, - {307, CS_STATIC("Temporary Redirect")}, - {308, CS_STATIC("Permanent Redirect")}, - {400, CS_STATIC("Bad Request")}, - {401, CS_STATIC("Unauthorized")}, - {402, CS_STATIC("Payment Required")}, - {403, CS_STATIC("Forbidden")}, - {404, CS_STATIC("Not Found")}, - {405, CS_STATIC("Method Not Allowed")}, - {406, CS_STATIC("Not Acceptable")}, - {408, CS_STATIC("Request Timeout")}, - {418, CS_STATIC("Im A Teepot")}, - {500, CS_STATIC("Internal Server Error")}, - {501, CS_STATIC("Not Implemented")}, - {502, CS_STATIC("Bad Gateway")}, - {503, CS_STATIC("Service Unavailable")}, - {505, CS_STATIC("Http Version Not Suppported")}, -}; - -const_string get_response_string(http_response_code code) { - for (int i = 0; sizeof(response_strings)/sizeof(response_strings[0]) ;i++) { - if (response_strings[i].code == code) { - return response_strings[i].string; - } - } - return CS(""); -} - - -void sigchld_handler() { - // waitpid() might overwrite errno, so we save and restore it: - int saved_errno = errno; - - while(waitpid(-1, NULL, WNOHANG) > 0); - - errno = saved_errno; -} - -void http_log(http_log_level log, char *fmt, ...) { - switch (log) { - case HTTP_INFO: - fprintf(stderr, "[INFO] "); - break; - - case HTTP_WARNING: - fprintf(stderr, "[WARNING] "); - break; - - case HTTP_ERROR: - fprintf(stderr, "[ERROR] "); - break; - } - - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - - -void *get_in_addr(struct sockaddr *sa) { - if (sa->sa_family == AF_INET) { - return &(((struct sockaddr_in*)sa)->sin_addr); - } - - return &(((struct sockaddr_in6*)sa)->sin6_addr); -} - -int get_in_port(struct sockaddr *sa) { - if (sa->sa_family == AF_INET) { - return ntohs((((struct sockaddr_in*)sa)->sin_port)); - } - - return (((struct sockaddr_in6*)sa)->sin6_port); -} - -void set_context_value(arena *arena, http_context *ctx, http_context_value kv) { - for (size_t i = 0; i < ctx->len; i++) { - http_context_value *ctx_val = (ctx->data + i); - if (cs_eq(kv.key, ctx_val->key)) { - ctx_val->val = kv.val; - return; - } - } - arena_da_append(arena, ctx, kv); -} - -void *get_context_value(http_context *ctx, char* key) { - if (ctx == NULL) { - return NULL; - } - - const_string key_cs = cs_from_cstr(key); - for (size_t i = 0; i < ctx->len; i++) { - http_context_value *ctx_val = (ctx->data + i); - if (cs_eq(key_cs, ctx_val->key)) { - return ctx_val->val; - } - } - return NULL; -} - -void http_internal_server_error_handler(http_request req) { - const_string *error = get_context_value(req.ctx, "error"); - req.resp->code = INTERNAL_SERVER_ERROR; - req.resp->body = *error; - http_send(req); -} - -void http_not_found_handler(http_request req) { - req.resp->code = NOT_FOUND; - http_send(req); -} - -void http_run_next(http_middleware *self, http_request req) { - if (self->next == NULL) { - (self->handler)(req); - return; - } - - (self->next->func)(self->next, req); -}; - -void http_register_handler_middleware(arena *arena, http_handler *hand, http_middleware_func func) { - http_middleware *middleware = arena_alloc(arena, sizeof(http_middleware)); - middleware->func = func; - middleware->handler = hand->func; - if (hand->middleware == NULL) { - hand->middleware = middleware; - return; - } - - http_middleware **prev = &hand->middleware; - http_middleware *next = hand->middleware; - while (next->next != NULL) { - prev = &next; - next = next->next; - } - middleware->next = next; - *prev = middleware; -} - -void http_register_global_middleware(http_server *serv, http_middleware_func func) { - arena *arena = serv->arena; - - http_middleware *middleware = arena_alloc(arena, sizeof(http_middleware)); - http_handler_func *handler = arena_alloc(arena, sizeof(http_handler_func)); - middleware->func = func; - middleware->handler = *handler; - if (serv->global_middleware == NULL) { - http_global_middleware *global_middleware = arena_alloc(arena, sizeof(http_global_middleware)); - global_middleware->start = middleware; - global_middleware->end = middleware; - serv->global_middleware = global_middleware; - return; - } - - serv->global_middleware->end->next = middleware; - serv->global_middleware->end = middleware; -} - -int init_server(arena *arena, http_server *serv, char *addr, char *port) { - // serv initialization - struct addrinfo hints, *res; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - int status = getaddrinfo(addr, port, &hints, &res); - if (status != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); - return 1; - } - - int sockfd = socket(res->ai_family, res->ai_socktype, 0); - if (sockfd < 0) { - perror("socket"); - return 1; - } - - int yes = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) { - perror("setsockopt"); - return 1; - } - - if (bind(sockfd, res->ai_addr, res->ai_addrlen)) { - perror("bind"); - return 1; - } - - serv->arena = arena; - serv->addr = (struct sockaddr *)res->ai_addr; - serv->sockfd = sockfd; - - freeaddrinfo(res); - - struct sigaction sa; - sa.sa_handler = sigchld_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - if (sigaction(SIGCHLD, &sa, NULL) == -1) { - perror("sigaction"); - return 1; - } - - return 0; -} - -http_handler *http_handle_path(http_server *serv, char* method, char* path, http_handler_func handler) { - http_handler hand = { - .method = cs_from_cstr(method), - .path = cs_from_cstr(path), - .func = handler, - }; - - arena_da_append(serv->arena, &serv->handlers, hand); - return &serv->handlers.data[serv->handlers.len-1]; -} - -const_string http_compose_response(arena *arena, http_response *resp) { - const_string_da header_da = {0}; - arena_da_append(arena, &header_da, CS("HTTP/1.1")); - - if (resp->code != 0) { - char buf[4]; - sprintf(buf, "%ld", resp->code); - arena_da_append(arena, &header_da, cs_from_cstr(buf)); - arena_da_append(arena, &header_da, get_response_string(resp->code)); - } else { - printf("Response is handeled, but no code is given\n"); - arena_da_append(arena, &header_da, CS("500")); - arena_da_append(arena, &header_da, get_response_string(INTERNAL_SERVER_ERROR)); - } - - const_string_da resp_da= {0}; - - const_string http_header = arena_cs_concat(arena, header_da, CS(" ")); - arena_da_append(arena, &resp_da, http_header); - - for (size_t i = 0; i < resp->headers.len; i++) { - KV *header = (resp->headers.data + i); - const_string header_str = {0}; - header_str = arena_cs_append(arena, header_str, header->key); - header_str = arena_cs_append(arena, header_str, CS(": ")); - header_str = arena_cs_append(arena, header_str, header->value); - arena_da_append(arena, &resp_da, header_str); - } - - arena_da_append(arena, &resp_da, CS("")); - if (resp->body.len != 0) { - arena_da_append(arena, &resp_da, resp->body); - } - - const_string str = arena_cs_concat(arena, resp_da, CS("\r\n")); - - return str; -} - - -void http_send_response(size_t inc_fd, const_string resp) { - const char *resp_cstr = resp.data; - int resp_len = resp.len; - - int sent = send(inc_fd, resp_cstr, resp_len, 0); - while (sent > 0 && resp_len - sent > 0) { - resp_len -= sent; - sent = send(inc_fd, resp_cstr, resp_len, 0); - } - - if (sent < 0) { - perror("send"); - } -} - -void http_send(http_request req) { - arena *arena = req.arena; - - assert(req.resp->code); - const_string response = http_compose_response(arena, req.resp); - http_send_response(req.inc_fd, response); - req.resp->sent = true; -} - -void http_send_json(http_request req, const_string json) { - arena *arena = req.arena; - - int num_str_len = snprintf(NULL, 0, "%d", json.len); - char* len_str = arena_alloc(arena, snprintf(NULL, 0, "%d", json.len)); - sprintf(len_str, "%d", json.len); - const_string len_cs = {len_str, num_str_len}; - - arena_da_append(arena, &req.resp->headers, ((KV){CS("Content-Length"), len_cs})); - arena_da_append(arena, &req.resp->headers, ((KV){CS("Content-Type"), CS("application/json")})); - req.resp->body = json; - - http_send(req); -} -void http_send_body(http_request req, const_string body) { - arena *arena = req.arena; - - int num_str_len = snprintf(NULL, 0, "%d", body.len+2); - char* len_str = arena_alloc(arena, snprintf(NULL, 0, "%d", body.len)); - sprintf(len_str, "%d", body.len); - const_string len_cs = {len_str, num_str_len}; - - arena_da_append(arena, &req.resp->headers, ((KV){CS("Content-Length"), len_cs})); - arena_da_append(arena, &req.resp->headers, ((KV){CS("Content-Type"), CS("application/text")})); - req.resp->body = body; - - http_send(req); -} - -int chop_query(arena *arena, const_string *path, http_query_da *queries) { - const_string queries_str = *path; - int query_count = 0; - - if (!cs_try_chop_delim(&queries_str, '?', path)) { - return 0; - } - - const_string query_str; - - if (!cs_try_chop_delim(&queries_str, '&', &query_str)) { - query_str = queries_str; - } - - do { - KV query = {0}; - const_string key; - if (!cs_try_chop_delim(&query_str, '=', &key)) { - return -1; - } - - query.key = key; - query.value = query_str; - arena_da_append(arena, queries, query); - query_count++; - } while (cs_try_chop_delim(&queries_str, '&', &query_str)); - - return query_count; -} - -const_string chop_request(const_string *req_str, char delim, bool *parse_err) { - if (*parse_err) { - return CS(""); - } - const_string str; - if (!cs_try_chop_delim(req_str, delim, &str)) { - *parse_err = true; - return CS(""); - } - return str; -} - -int parse_request(arena *arena, size_t inc_fd, http_request *req) { - char buf[MAX_DATA_SIZE]; - const_string req_str = {0}; - - int got = recv(inc_fd, &buf, MAX_DATA_SIZE - 1, 0); - if (got < 0) { - perror("recv"); - return REQUEST_TIMEOUT; - } - - buf[got] = '\0'; - - req_str = arena_cs_append(arena, req_str, cs_from_cstr(buf)); - - while (got == MAX_DATA_SIZE - 1) { - got = recv(inc_fd, &buf, MAX_DATA_SIZE - 1, 0); - if (got < 0) { - perror("recv"); - return REQUEST_TIMEOUT; - } - - buf[got] = '\0'; - - const_string tmp = cs_from_parts(buf, got); - req_str = arena_cs_append(arena, req_str, tmp); - } - - bool parse_err = false; - - const_string method = chop_request(&req_str, ' ', &parse_err); - const_string path = chop_request(&req_str, ' ', &parse_err); - chop_request(&req_str, '\r', &parse_err); - req_str.data += 1; - - header_da headers = {0}; - while (!parse_err && req_str.data[0] != '\r') { - const_string header_str = chop_request(&req_str, '\r', &parse_err); - req_str.data += 1; - - KV header; - header.key = chop_request(&header_str, ':', &parse_err); - header_str.data++; - header.value = header_str; - - arena_da_append(arena, &headers, header); - } - chop_request(&req_str, '\n', &parse_err); - - if (parse_err) { - return BAD_REQUEST; - } - - http_query_da queries = {0}; - - if (chop_query(arena, &path, &queries) < 0) { - return BAD_REQUEST; - } - - - req->method = method; - req->path = path; - req->query = queries; - req->headers = headers; - req->body = req_str; - - return 0; -} - -bool check_handler(http_handler hand, http_request req) { - arena *arena = req.arena; - http_context *ctx = req.ctx; - - const_string handler_path = hand.path; - const_string req_path = req.path; - if (!(cs_eq(hand.method, req.method) || - (cs_eq(hand.method, CS("GET")) && cs_eq(req.method, CS("HEAD"))))) { - return false; - } - - const_string path; - bool entered = false; - - while (cs_try_chop_delim(&handler_path, ':', &path)) { - entered = true; - - const_string req_byte = {req_path.data, path.len}; - if (!cs_eq(path, req_byte)) { - return false; - } - - req_path.data += path.len; - req_path.len -= path.len; - - const_string name = cs_chop_delim(&handler_path, '/'); - const_string tmp = cs_chop_delim(&req_path, '/'); - const_string *value = arena_alloc(arena, sizeof(const_string)); - *value = tmp; - set_context_value(arena, ctx, (http_context_value){name, (void *)value}); - } - - if (!entered && !cs_eq(handler_path, req_path)) { - return false; - } - - return true; -} - -struct process_request_args { - http_server serv; - size_t inc_fd; -}; - -void *process_request(void *args) { - struct process_request_args *pargs = args; - http_server serv = pargs->serv; - size_t inc_fd = pargs->inc_fd; - - arena req_arena = {0}; - - http_request req = {0}; - req.inc_fd = inc_fd; - req.arena = &req_arena; - - http_response resp = {0}; - req.resp = &resp; - - int parse_res = parse_request(&req_arena, inc_fd, &req); - if (parse_res != 0) { - resp.code = parse_res; - http_send(req); - } - - http_context req_context = {0}; - for (size_t i = 0; i < serv.global_ctx.len; i++) { - set_context_value(&req_arena, &req_context, *(serv.global_ctx.data + i)); - } - req.ctx = &req_context; - - http_global_middleware *g_mid = serv.global_middleware; - if (!resp.sent) { - for (size_t i = 0; i < serv.handlers.len; i++) { - http_handler handler = serv.handlers.data[i]; - if (check_handler(handler, req)) { - if (serv.global_middleware != NULL) { - g_mid->end->next = handler.middleware; - g_mid->end->handler = handler.func; - g_mid->start->func(g_mid->start, req); - } else if (handler.middleware != NULL) { - handler.middleware->func(handler.middleware, req); - } else { - handler.func(req); - } - - if (!resp.sent) { - const_string error = CS("Server handled the request but did not send anything\n"); - http_log(HTTP_ERROR, "%s", error.data); - set_context_value(&req_arena, &req_context, (http_context_value){CS("error"), (void *)&error}); - g_mid->end->handler = http_internal_server_error_handler; - g_mid->start->func(g_mid->start, req); - } - break; - } - } - } - - if (!resp.code) { - g_mid->end->handler = http_not_found_handler; - g_mid->start->func(g_mid->start, req); - } - - close(inc_fd); - arena_free(&req_arena); - - pthread_exit(NULL); -} - -int listen_and_serve(http_server *serv) { - if (listen(serv->sockfd, 10) != 0) { - perror("listen"); - return 1; - } - - char my_ipstr[INET6_ADDRSTRLEN]; - inet_ntop(serv->addr->sa_family, get_in_addr(serv->addr), my_ipstr, sizeof my_ipstr); - http_log(HTTP_INFO, "Listening on %s:%d\n", my_ipstr, get_in_port(serv->addr)); - - while (keepRunning) { - struct sockaddr inc_addr; - socklen_t addr_size = sizeof inc_addr; - int inc_fd = accept(serv->sockfd, &inc_addr, &addr_size); - if (inc_fd < 0) { - perror("accept"); - return 1; - } - - struct process_request_args args = { - .serv = *serv, - .inc_fd = inc_fd, - }; - - pthread_t thread_id; - pthread_create(&thread_id, NULL, process_request, (void *)&args); - } - return 0; -} |
