aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arena.c84
-rw-r--r--src/arena.obin0 -> 3592 bytes
-rw-r--r--src/arena_strings.c53
-rw-r--r--src/arena_strings.obin0 -> 2544 bytes
-rw-r--r--src/const_strings.c86
-rw-r--r--src/const_strings.obin0 -> 3048 bytes
-rw-r--r--src/hashmap.c43
-rw-r--r--src/hashmap.obin0 -> 2296 bytes
-rw-r--r--src/http.c586
-rw-r--r--src/http.obin0 -> 23744 bytes
10 files changed, 852 insertions, 0 deletions
diff --git a/src/arena.c b/src/arena.c
new file mode 100644
index 0000000..8b4828f
--- /dev/null
+++ b/src/arena.c
@@ -0,0 +1,84 @@
+#include <chttp/arena.h>
+
+region *new_region(size_t cap) {
+ size_t size_bytes = sizeof(region) + sizeof(uintptr_t)*cap;
+ region *r = (region *)malloc(size_bytes);
+ assert(r);
+ r->next = NULL;
+ r->len = 0;
+ r->cap = cap;
+ return r;
+}
+
+arena arena_init_cap(size_t cap) {
+ arena a = {0};
+ a.region_cap = cap;
+ return a;
+}
+
+void *arena_alloc(arena *a, size_t size_bytes) {
+ size_t size = (size_bytes + sizeof(uintptr_t)-1)/sizeof(uintptr_t);
+
+ if (a->end == NULL) {
+ assert(a->beg == NULL);
+ size_t capacity = a->region_cap ? a->region_cap : ARENA_REGION_DEFAULT_CAPACITY;
+ if (capacity < size) capacity = size;
+ a->beg = new_region(capacity);
+ a->end = a->beg;
+ }
+
+ while (a->end->len + size < a->end->cap && a->end->next != NULL) {
+ a->end = a->end->next;
+ }
+
+ if (a->end->len + size > a->end->cap) {
+ size_t capacity = a->region_cap ? a->region_cap : ARENA_REGION_DEFAULT_CAPACITY;
+ if (capacity < size) capacity = size;
+ a->beg = new_region(capacity);
+ a->end->next = new_region(capacity);
+ a->end = a->end->next;
+ }
+
+ void *p = &a->end->data[a->end->len];
+ a->end->len += size;
+ return memset(p, 0, size);
+}
+
+void arena_free(arena *a) {
+ region *r = a->beg;
+ while (r != NULL) {
+ region *r0 = r;
+ r = r0->next;
+ free(r0);
+ }
+}
+
+void *arena_realloc(arena *a, void *oldptr, size_t oldsz, size_t newsz) {
+ if (newsz <= oldsz) return oldptr;
+ void *newptr = arena_alloc(a, newsz);
+ char *newptr_char = (char*)newptr;
+ char *oldptr_char = (char*)oldptr;
+ for (size_t i = 0; i < oldsz; ++i) {
+ newptr_char[i] = oldptr_char[i];
+ }
+ return newptr;
+}
+
+void grow_da(void *slice, size_t size, arena *arena) {
+ struct {
+ void *data;
+ size_t len;
+ size_t cap;
+ } replica;
+ memcpy(&replica, slice, sizeof(replica));
+
+ replica.cap = replica.cap ? replica.cap : 1;
+ void *data = arena_alloc(arena, size*2*replica.cap);
+ replica.cap *= 2;
+ if (replica.len) {
+ memcpy(data, replica.data, size*replica.len);
+ }
+ replica.data = data;
+
+ memcpy(slice, &replica, sizeof(replica));
+}
diff --git a/src/arena.o b/src/arena.o
new file mode 100644
index 0000000..845d142
--- /dev/null
+++ b/src/arena.o
Binary files differ
diff --git a/src/arena_strings.c b/src/arena_strings.c
new file mode 100644
index 0000000..78f98a1
--- /dev/null
+++ b/src/arena_strings.c
@@ -0,0 +1,53 @@
+#include <chttp/arena_strings.h>
+
+const_string arena_cs_init(arena *a, int len) {
+ char *str = new(a, char, len+1);
+ const_string cs = {0};
+ cs.data = str;
+ cs.len = len;
+ return cs;
+}
+
+const_string arena_cs_append(arena *a, const_string dst, const_string src) {
+ char *str = new(a, char, dst.len+src.len + 1);
+ memcpy(str, dst.data, dst.len);
+ memcpy(str + dst.len, src.data, src.len + 1);
+ const_string cs = {0};
+ cs.data = str;
+ cs.len = dst.len + src.len;
+ return cs;
+}
+
+const_string arena_cs_concat(arena *a, const_string_da strings, const_string sep) {
+ if (strings.len == 0) {
+ return CS("");
+ } else if (strings.len == 0) {
+ return *strings.data;
+ }
+
+ int len = sep.len * (strings.len - 1);
+ for (size_t i = 0; i < strings.len; i++) {
+ len += (strings.data + i)->len;
+ };
+
+ char* str = arena_alloc(a, len);
+ int offset = 0;
+
+ for (size_t i = 0; i < strings.len; i++) {
+ int curr_strlen = (strings.data + i)->len;
+ memcpy(str + offset,
+ (strings.data + i)->data,
+ curr_strlen);
+
+ offset += curr_strlen;
+
+ if (i != strings.len - 1) {
+ memcpy(str + offset,
+ sep.data, sep.len);
+ offset += sep.len;
+ }
+ };
+ *(str + len + 1) = '\0';
+
+ return (const_string){str, len};
+};
diff --git a/src/arena_strings.o b/src/arena_strings.o
new file mode 100644
index 0000000..b16bcec
--- /dev/null
+++ b/src/arena_strings.o
Binary files differ
diff --git a/src/const_strings.c b/src/const_strings.c
new file mode 100644
index 0000000..7712eac
--- /dev/null
+++ b/src/const_strings.c
@@ -0,0 +1,86 @@
+#include <chttp/const_strings.h>
+
+const_string cs_from_parts(const char* data, int len) {
+ const_string str;
+ str.data = data;
+ str.len = len;
+
+ return str;
+}
+
+const_string cs_from_cstr(const char* cstr) {
+ return cs_from_parts(cstr, strlen(cstr));
+}
+
+const_string cs_slice(const_string src, int from, int to) {
+ if (from > src.len) {
+ return src;
+ }
+
+ int new_from;
+ if (from <= 0) {
+ new_from = 1;
+ }
+ new_from -= 1;
+
+ int new_len;
+ if (to > src.len) {
+ new_len = src.len;
+ } else {
+ new_len = to;
+ }
+
+ return cs_from_parts(src.data + new_from, new_len);
+}
+
+const_string cs_chop_delim(const_string *str, char delim) {
+ int n;
+ for (n = 0; n < str->len && str->data[n] != delim; n++) {}
+
+ const_string result = cs_from_parts(str->data, n);
+
+ if (n < str->len) {
+ str->len -= n + 1;
+ str->data += n + 1;
+ } else {
+ str->len -= n;
+ str->data += n;
+ }
+
+ return result;
+}
+
+bool cs_try_chop_delim(const_string *str, char delim, const_string *dst) {
+ const_string str_copy = *str;
+ const_string tmp = cs_chop_delim(&str_copy, delim);
+ if (str_copy.len == 0) {
+ return false;
+ }
+ *str = str_copy;
+ *dst = tmp;
+ return true;
+}
+
+int cs_find_delim(const_string str, char delim) {
+ int n;
+ for (n = 0; n < str.len && str.data[n] != delim; n++) {}
+
+ if (n < str.len) {
+ return n;
+ }
+ return -1;
+}
+
+bool cs_eq(const_string str1, const_string str2) {
+ if (str1.len != str2.len) {
+ return false;
+ }
+ return memcmp(str1.data, str2.data, str1.len) == 0;
+}
+
+void cs_print(char *format, const_string str) {
+ char buf[str.len + 1];
+ memcpy(buf, str.data, str.len);
+ buf[str.len] = '\0';
+ printf(format, buf);
+}
diff --git a/src/const_strings.o b/src/const_strings.o
new file mode 100644
index 0000000..7154db9
--- /dev/null
+++ b/src/const_strings.o
Binary files differ
diff --git a/src/hashmap.c b/src/hashmap.c
new file mode 100644
index 0000000..c68d110
--- /dev/null
+++ b/src/hashmap.c
@@ -0,0 +1,43 @@
+#include <chttp/hashmap.h>
+
+size_t hash_str(const_string str, size_t cap) {
+ size_t hash = 0;
+ for (int i = 0; i < str.len; i++) {
+ hash = (HASHMAP_DEFAULT_R * hash + str.data[i]) % cap;
+ }
+ return hash;
+}
+
+hashmap *hashmap_init(arena *a, size_t cap) {
+ hashmap *map = arena_alloc(a, sizeof(hashmap) + sizeof(ptrdiff_t) * cap);
+ map->cap = cap;
+ return map;
+}
+
+size_t hashmap_kstr_insert(arena *a, hashmap* map, const_string key, void* val) {
+ size_t hash = hash_str(key, map->cap);
+ if (map->data[hash].key.len > 0) {
+ map->data[hash].next = arena_alloc(a, sizeof(hashitem));
+ map->data[hash].next->key = key;
+ map->data[hash].next->val = val;
+ } else {
+ map->data[hash].key = key;
+ map->data[hash].val = val;
+ }
+
+ return hash;
+}
+
+void *hashmap_kstr_get(hashmap *map, const_string key) {
+ size_t hash = hash_str(key, map->cap);
+ hashitem *item = &map->data[hash];
+ while (item->next != NULL && !cs_eq(item->key, key)) {
+ item = item->next;
+ }
+
+ if (!cs_eq(item->key, key)) {
+ return NULL;
+ }
+
+ return item->val;
+}
diff --git a/src/hashmap.o b/src/hashmap.o
new file mode 100644
index 0000000..5dd78ec
--- /dev/null
+++ b/src/hashmap.o
Binary files differ
diff --git a/src/http.c b/src/http.c
new file mode 100644
index 0000000..db497d5
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,586 @@
+#include <chttp/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;
+}
diff --git a/src/http.o b/src/http.o
new file mode 100644
index 0000000..9d4103c
--- /dev/null
+++ b/src/http.o
Binary files differ