diff options
| author | spl3g <spleefer6@yandex.ru> | 2025-03-21 13:38:30 +0300 |
|---|---|---|
| committer | spl3g <spleefer6@yandex.ru> | 2025-03-22 11:12:11 +0300 |
| commit | 358f08897aca0aafeed993e91ef63edc00fd288f (patch) | |
| tree | ea2b057390343be61a062d1c702df99625e7c275 | |
| parent | 11d0fe6912ca72f2ae8ab057b7e9eeb9a4b7c15e (diff) | |
Update the api and add middleware support
| -rw-r--r-- | lib/http.c | 221 | ||||
| -rw-r--r-- | lib/http.h | 62 | ||||
| -rw-r--r-- | main.c | 25 |
3 files changed, 217 insertions, 91 deletions
@@ -109,39 +109,63 @@ void set_context_value(arena *arena, context *ctx, context_value kv) { arena_da_append(arena, ctx, kv); } -void *get_context_value(context *ctx, const_string key) { +void *get_context_value(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++) { context_value *ctx_val = (ctx->data + i); - if (cs_eq(key, ctx_val->key)) { + if (cs_eq(key_cs, ctx_val->key)) { return ctx_val->val; } } return NULL; } -void resp_set_code(struct response* resp, response_code code) { - resp->code = code; +void http_handler_runner_middleware(http_middleware *self, struct request req) { + (self->handler)(req); } -void resp_set_json(arena *arena, struct response* resp, const_string json) { - int num_str_len = snprintf(NULL, 0, "%d", json.len+2); - char* len_str = arena_alloc(arena, snprintf(NULL, 0, "%d", json.len+2)); - sprintf(len_str, "%d", json.len+2); - const_string len_cs = {len_str, num_str_len}; - - arena_da_append(arena, &resp->headers, ((KV){CS("Content-Length"), len_cs})); - arena_da_append(arena, &resp->headers, ((KV){CS("Content-Type"), CS("application/json")})); - resp->body = json; +void http_register_handler_middleware(arena *arena, 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) { + http_middleware *runner_middleware = arena_alloc(arena, sizeof(http_middleware)); + runner_middleware->func = http_handler_runner_middleware; + runner_middleware->handler = hand->func; + + middleware->next = runner_middleware; + hand->middleware = middleware; + return; + } + + volatile http_middleware *next = hand->middleware; + while (next->next->next != NULL) { + next = next->next; + } + middleware->next = next->next; + next->next = middleware; } -void resp_set_body(arena *arena, struct response* resp, const_string body) { - int num_str_len = snprintf(NULL, 0, "%d", body.len+2); - char* len_str = arena_alloc(arena, snprintf(NULL, 0, "%d", body.len+2)); - sprintf(len_str, "%d", body.len+2); - const_string len_cs = {len_str, num_str_len}; - arena_da_append(arena, &resp->headers, ((KV){CS("Content-Length"), len_cs})); - arena_da_append(arena, &resp->headers, ((KV){CS("Content-Type"), CS("application/text")})); - resp->body = body; +void http_register_global_middleware(arena *arena, struct server *serv, http_middleware_func func) { + http_middleware *middleware = arena_alloc(arena, sizeof(http_middleware)); + handler_func *handler = arena_alloc(arena, sizeof(handler_func)); + middleware->func = func; + middleware->handler = *handler; + if (serv->global_middleware == NULL) { + serv->global_middleware = middleware; + return; + } + + http_middleware *next = serv->global_middleware; + while (next->next != NULL) { + next = next->next; + } + + next->next = middleware; } int init_server(arena *arena, struct server *serv, char *addr, char *port) { @@ -194,7 +218,7 @@ int init_server(arena *arena, struct server *serv, char *addr, char *port) { return 0; } -void handle_path(struct server *serv, const_string method, const_string path, handler_func handler) { +handler *handle_path(struct server *serv, const_string method, const_string path, handler_func handler) { struct handler hand = { .method = method, .path = path, @@ -202,9 +226,10 @@ void handle_path(struct server *serv, const_string method, const_string path, ha }; arena_da_append(serv->arena, &serv->handlers, hand); + return &serv->handlers.data[serv->handlers.len-1]; } -void compose_response(arena *arena, struct response *resp) { +const_string http_compose_response(arena *arena, struct response *resp) { const_string_da header_da = {0}; arena_da_append(arena, &header_da, CS("HTTP/1.1")); @@ -238,10 +263,63 @@ void compose_response(arena *arena, struct response *resp) { arena_da_append(arena, &resp_da, resp->body); } - arena_da_append(arena, &resp_da, CS("")); const_string str = arena_cs_concat(arena, resp_da, CS("\r\n")); - resp->string = str; + 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(struct 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(struct 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(struct 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, query_da *queries) { @@ -344,19 +422,24 @@ int parse_request(arena *arena, size_t inc_fd, struct request *req) { return BAD_REQUEST; } - struct request request = { - .method = method, - .path = path, - .query = queries, - .headers = headers, - .body = req_str, - }; - *req = request; + req->method = method; + req->path = path; + req->query = queries; + req->headers = headers; + req->body = req_str; + return 0; } -bool compare_paths(const_string handler_path, const_string req_path, arena* arena, context *ctx) { +bool check_handler(handler hand, struct request req, arena* arena, context *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; @@ -385,28 +468,52 @@ bool compare_paths(const_string handler_path, const_string req_path, arena* aren return true; } -void process_request(struct server *serv, size_t inc_fd) { +struct process_request_args { + struct server serv; + size_t inc_fd; +}; + +void *process_request(void *args) { + struct process_request_args *pargs = args; + struct server serv = pargs->serv; + size_t inc_fd = pargs->inc_fd; + arena req_arena = {0}; - struct request req; + struct request req = {0}; + req.inc_fd = inc_fd; + req.arena = &req_arena; + struct 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); } context req_context = {0}; - set_context_value(&req_arena, &req_context, (context_value){CS("arena"), &req_arena}); - for (size_t i = 0; i < serv->global_ctx.len; i++) { - set_context_value(&req_arena, &req_context, *(serv->global_ctx.data + i)); + for (size_t i = 0; i < serv.global_ctx.len; i++) { + set_context_value(&req_arena, &req_context, *(serv.global_ctx.data + i)); } if (!resp.code) { - for (size_t i = 0; i < serv->handlers.len; i++) { - struct handler handler = serv->handlers.data[i]; - if ((cs_eq(handler.method, req.method) || (cs_eq(handler.method, CS("GET")) && cs_eq(req.method, CS("HEAD"))) ) - && compare_paths(handler.path, req.path, &req_arena, &req_context)) { - handler.func(&req_context, req, &resp); + for (size_t i = 0; i < serv.handlers.len; i++) { + struct handler handler = serv.handlers.data[i]; + if (check_handler(handler, req, &req_arena, &req_context)) { + if (handler.middleware != NULL) { + handler.middleware->func(handler.middleware, req); + } else { + handler.func(req); + } + + if (!resp.sent) { + char *error = "Server handled the request but did not send anything"; + http_log(HTTP_ERROR, error); + resp.code = INTERNAL_SERVER_ERROR; + resp.body = CS(error); + } break; } } @@ -416,25 +523,16 @@ void process_request(struct server *serv, size_t inc_fd) { resp.code = NOT_FOUND; } - if (resp.string.len == 0) { - compose_response(&req_arena, &resp); + if (!resp.sent) { + http_send(req); } - const char *resp_cstr = resp.string.data; - int resp_len = resp.string.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"); - } close(inc_fd); arena_free(&req_arena); + + pthread_exit(NULL); } int listen_and_serve(struct server *serv) { @@ -456,8 +554,13 @@ int listen_and_serve(struct server *serv) { return 1; } - process_request(serv, inc_fd); - close(inc_fd); + 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; } @@ -15,6 +15,7 @@ #include <signal.h> #include <stdlib.h> #include <stdarg.h> +#include <pthread.h> #include "const_strings.h" #include "arena_strings.h" @@ -42,52 +43,71 @@ typedef struct { size_t cap; } query_da; +typedef struct { + const_string key; + void *val; +} context_value; + +typedef struct { + context_value *data; + size_t len; + size_t cap; +} context; + struct request { + size_t inc_fd; const_string method; const_string path; query_da query; header_da headers; const_string body; + + arena *arena; + struct response* resp; + context *ctx; }; struct response { size_t code; header_da headers; const_string body; - const_string string; + bool sent; }; -typedef struct { - const_string key; - void *val; -} context_value; +typedef void (*handler_func)(struct request); -typedef struct { - context_value *data; - size_t len; - size_t cap; -} context; +typedef struct handler_da handler_da; +typedef struct http_middleware http_middleware; +typedef struct handler handler; + +typedef void (*http_middleware_func)(http_middleware *, struct request); -typedef void (*handler_func)(context*, struct request, struct response *resp); +struct http_middleware { + http_middleware_func func; + http_middleware *next; + handler_func handler; +}; struct handler { const_string method; const_string path; + http_middleware *middleware; handler_func func; }; -typedef struct { - struct handler *data; +struct handler_da { + handler *data; size_t len; size_t cap; -} handler_da; +}; struct server { - int sockfd; + size_t sockfd; struct sockaddr *addr; arena *arena; handler_da handlers; context global_ctx; + http_middleware *global_middleware; }; typedef enum { @@ -134,13 +154,17 @@ int get_in_port(struct sockaddr *sa); void http_log(http_log_level log, char *fmt, ...); void set_context_value(arena *arena, context *ctx, context_value kv); -void *get_context_value(context *ctx, const_string key); +void *get_context_value(context *ctx, char* key); -void resp_set_code(struct response* resp, response_code code); -void resp_set_json(arena *arena, struct response* resp, const_string json); +void http_send(struct request req); +void http_send_json(struct request req, const_string json); +void http_send_body(struct request req, const_string body); int init_server(arena *arena, struct server *serv, char *addr, char *port); int listen_and_serve(struct server *serv); -void handle_path(struct server *serv, const_string method, const_string path, handler_func handler); +handler *handle_path(struct server *serv, const_string method, const_string path, handler_func handler); + +void http_register_handler_middleware(arena *arena, handler *hand, http_middleware_func func); + #endif // HTTP_H_ @@ -1,18 +1,15 @@ +#include "lib/const_strings.h" #include "lib/http.h" -void success(struct request req, struct response *resp) { - printf("Success handler:\n"); - cs_print("Method: %s\n", req.method); - cs_print("Path: %s\n", req.path); - cs_print("Body: %s\n", req.body); - - for (size_t i = 0; i < req.query.len; i++) { - cs_print("%s: ", req.query.data[i].key); - cs_print("%s\n", req.query.data[i].val); - }; +void success(struct request req) { + req.resp->code = OK; + req.resp->body = CS("Hello world!\n"); + http_send(req); +} - resp->code = OK; - resp->body = CS("Hello world!\n"); +void logging_func(http_middleware *self, struct request req) { + (self->next->func)(self->next, req); + http_log(HTTP_INFO, "[1]"CS_FMT" "CS_FMT": %ld\n", CS_ARG(req.method), CS_ARG(req.path), req.resp->code); } int main() { @@ -22,7 +19,9 @@ int main() { return 1; } - handle_path(&serv, CS("GET"), CS("/"), success); + handler *success_handler = handle_path(&serv, CS("GET"), CS("/"), success); + http_register_handler_middleware(&arena, success_handler, logging_func); + listen_and_serve(&serv); return 0; |
