From a854e4cfa450590753171fbbe4dde8da4f504abd Mon Sep 17 00:00:00 2001 From: spl3g Date: Tue, 11 Mar 2025 22:37:43 +0300 Subject: Move everything to the lib folder --- Makefile | 4 +- arena.h | 108 --------------- arena_strings.c | 53 -------- arena_strings.h | 17 --- const_strings.h | 108 --------------- lib/arena.c | 67 +++++++++ lib/arena.h | 40 ++++++ lib/arena_strings.c | 53 ++++++++ lib/arena_strings.h | 17 +++ lib/const_strings.c | 80 +++++++++++ lib/const_strings.h | 29 ++++ lib/http.c | 288 +++++++++++++++++++++++++++++++++++++++ lib/http.h | 112 +++++++++++++++ main.c | 386 +--------------------------------------------------- 14 files changed, 689 insertions(+), 673 deletions(-) delete mode 100644 arena.h delete mode 100644 arena_strings.c delete mode 100644 arena_strings.h delete mode 100644 const_strings.h create mode 100644 lib/arena.c create mode 100644 lib/arena.h create mode 100644 lib/arena_strings.c create mode 100644 lib/arena_strings.h create mode 100644 lib/const_strings.c create mode 100644 lib/const_strings.h create mode 100644 lib/http.c create mode 100644 lib/http.h diff --git a/Makefile b/Makefile index ea00ee9..000af46 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ -main: main.c const_strings.h arena.h arena_strings.h arena_strings.c - cc -Wall -Wextra -ggdb -o main main.c arena_strings.c +main: main.c lib + cc -Wall -Wextra -ggdb -o main main.c lib/arena_strings.c lib/http.c lib/arena.c lib/const_strings.c diff --git a/arena.h b/arena.h deleted file mode 100644 index c99fc97..0000000 --- a/arena.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef ARENA_H_ -#define ARENA_H_ - -#include -#include -#include -#include - -typedef struct region region; - -struct region { - region *next; - size_t cap; - size_t len; - uintptr_t data[]; -}; - -typedef struct { - region *beg; - region *end; -} arena; - -void *arena_alloc(arena *a, size_t size); -void arena_free(arena *a); -void grow_da(void *slice, size_t size, arena *arena); - - -#define new(a, t, c) (t *)arena_alloc(a, sizeof(t)*c) - -#define ARENA_REGION_DEFAULT_CAPACITY (8*1024); - -// from nullprogram -#define push(slice, arena) \ - ((slice)->len >= (slice)->cap \ - ? grow(slice, sizeof(*(slice)->data), arena), \ - (slice)->data + (slice)->len++ \ - : (slice)->data + (slice)->len++) - -#endif //ARENA_H_ - -#ifdef ARENA_IMPLEMENTATION - -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; -} - -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 = 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 = 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 grow(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)); -} -#endif // ARENA_IMPLEMENTATION diff --git a/arena_strings.c b/arena_strings.c deleted file mode 100644 index 28bd81a..0000000 --- a/arena_strings.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "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; - } - - volatile int len = sep.len * (strings.len - 1); - for (size_t i = 0; i < strings.len; i++) { - len += (strings.data + i)->len; - }; - - volatile const_string str = arena_cs_init(a, len); - volatile int offset = 0; - - for (size_t i = 0; i < strings.len; i++) { - volatile int curr_strlen = (strings.data + i)->len; - memcpy(str.data + offset, - (strings.data + i)->data, - curr_strlen); - - offset += curr_strlen; - - if (i != strings.len - 1) { - memcpy(str.data + offset, - sep.data, sep.len); - offset += sep.len; - } - }; - *(str.data + len + 1) = '\0'; - - return str; -}; diff --git a/arena_strings.h b/arena_strings.h deleted file mode 100644 index 8a8add0..0000000 --- a/arena_strings.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ARENA_STRINGS_H -#define ARENA_STRINGS_H - -#include "arena.h" -#include "const_strings.h" - -typedef struct { - const_string *data; - size_t len; - size_t cap; -} const_string_da; - -const_string arena_cs_append(arena *a, const_string dst, const_string src); -const_string arena_cs_init(arena *a, int len); -const_string arena_cs_concat(arena *a, const_string_da strings, const_string sep); - -#endif // ARENA_STRINGS_H diff --git a/const_strings.h b/const_strings.h deleted file mode 100644 index 3f13b95..0000000 --- a/const_strings.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef CONST_STRINGS_H_ -#define CONST_STRINGS_H_ - -#include -#include -#include - -typedef struct { - char* data; - int len; -} const_string; - - -const_string cs_from_parts(char* data, int len); -const_string cs_from_cstr(char* cstr); -const_string cs_slice(const_string src, int from, int to); -const_string cs_chop_delim(const_string *src, char delim); -int cs_find_delim(const_string str, char delim); -void cs_print(char *format, const_string str); - -#define CS(cstr) cs_from_parts(cstr, sizeof(cstr) - 1) -#define CS_STATIC(cstr) {.data = cstr, .len = sizeof(cstr) - 1} - -#endif // STRINGS_H_ - - -#ifdef CONST_STRINGS_IMPLEMENTATION - -const_string cs_from_parts(char* data, int len) { - const_string str; - str.data = data; - str.len = len; - - return str; -} - -const_string cs_from_cstr(char* cstr) { - return cs_from_parts(cstr, strlen(cstr)); -} - -char* cs_to_cstr(const_string str) { - str.data[str.len] = '\0'; - return str.data; -} - -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; -} - -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); -} - -#endif // STRINGS_IMPLEMENTATION diff --git a/lib/arena.c b/lib/arena.c new file mode 100644 index 0000000..dd12d0c --- /dev/null +++ b/lib/arena.c @@ -0,0 +1,67 @@ +#include "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; +} + +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 = 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 = 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 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/lib/arena.h b/lib/arena.h new file mode 100644 index 0000000..1ddddac --- /dev/null +++ b/lib/arena.h @@ -0,0 +1,40 @@ +#ifndef ARENA_H_ +#define ARENA_H_ + +#include +#include +#include +#include +#include + +typedef struct region region; + +struct region { + region *next; + size_t cap; + size_t len; + uintptr_t data[]; +}; + +typedef struct { + region *beg; + region *end; +} arena; + +void *arena_alloc(arena *a, size_t size); +void arena_free(arena *a); +void grow_da(void *slice, size_t size, arena *arena); + + +#define new(a, t, c) (t *)arena_alloc(a, sizeof(t)*c) + +#define ARENA_REGION_DEFAULT_CAPACITY (8*1024); + +// from nullprogram +#define push(slice, arena) \ + ((slice)->len >= (slice)->cap \ + ? grow_da(slice, sizeof(*(slice)->data), arena), \ + (slice)->data + (slice)->len++ \ + : (slice)->data + (slice)->len++) + +#endif //ARENA_H_ diff --git a/lib/arena_strings.c b/lib/arena_strings.c new file mode 100644 index 0000000..28bd81a --- /dev/null +++ b/lib/arena_strings.c @@ -0,0 +1,53 @@ +#include "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; + } + + volatile int len = sep.len * (strings.len - 1); + for (size_t i = 0; i < strings.len; i++) { + len += (strings.data + i)->len; + }; + + volatile const_string str = arena_cs_init(a, len); + volatile int offset = 0; + + for (size_t i = 0; i < strings.len; i++) { + volatile int curr_strlen = (strings.data + i)->len; + memcpy(str.data + offset, + (strings.data + i)->data, + curr_strlen); + + offset += curr_strlen; + + if (i != strings.len - 1) { + memcpy(str.data + offset, + sep.data, sep.len); + offset += sep.len; + } + }; + *(str.data + len + 1) = '\0'; + + return str; +}; diff --git a/lib/arena_strings.h b/lib/arena_strings.h new file mode 100644 index 0000000..85ffda3 --- /dev/null +++ b/lib/arena_strings.h @@ -0,0 +1,17 @@ +#ifndef ARENA_STRINGS_H_ +#define ARENA_STRINGS_H_ + +#include "arena.h" +#include "const_strings.h" + +typedef struct { + const_string *data; + size_t len; + size_t cap; +} const_string_da; + +const_string arena_cs_append(arena *a, const_string dst, const_string src); +const_string arena_cs_init(arena *a, int len); +const_string arena_cs_concat(arena *a, const_string_da strings, const_string sep); + +#endif // ARENA_STRINGS_H_ diff --git a/lib/const_strings.c b/lib/const_strings.c new file mode 100644 index 0000000..09d2c5f --- /dev/null +++ b/lib/const_strings.c @@ -0,0 +1,80 @@ +#include "const_strings.h" + +const_string cs_from_parts(char* data, int len) { + const_string str; + str.data = data; + str.len = len; + + return str; +} + +const_string cs_from_cstr(char* cstr) { + return cs_from_parts(cstr, strlen(cstr)); +} + +char* cs_to_cstr(const_string str) { + str.data[str.len] = '\0'; + return str.data; +} + +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; +} + +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/lib/const_strings.h b/lib/const_strings.h new file mode 100644 index 0000000..987c6f6 --- /dev/null +++ b/lib/const_strings.h @@ -0,0 +1,29 @@ +#ifndef CONST_STRINGS_H_ +#define CONST_STRINGS_H_ + +#include +#include +#include + +typedef struct { + char* data; + int len; +} const_string; + + +const_string cs_from_parts(char* data, int len); +const_string cs_from_cstr(char* cstr); +const_string cs_slice(const_string src, int from, int to); +const_string cs_chop_delim(const_string *src, char delim); +int cs_find_delim(const_string str, char delim); +bool cs_eq(const_string str1, const_string str2); +void cs_print(char *format, const_string str); + +#define CS(cstr) cs_from_parts(cstr, sizeof(cstr) - 1) +#define CS_STATIC(cstr) {.data = cstr, .len = sizeof(cstr) - 1} + +#endif // STRINGS_H_ + + +#ifdef CONST_STRINGS_IMPLEMENTATION +#endif // STRINGS_IMPLEMENTATION diff --git a/lib/http.c b/lib/http.c new file mode 100644 index 0000000..7ae49a1 --- /dev/null +++ b/lib/http.c @@ -0,0 +1,288 @@ +#include "http.h" + +#define MAX_DATA_SIZE 256 +#define BUFFER_SIZE 4096 +#define REQUEST_ARENA_SIZE 16*1024 + +int volatile keepRunning = 1; + +struct { + int 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")}, + {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(int 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 *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); +} + +int init_server(arena *arena, struct 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("bind"); + 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; + } + + char my_ipstr[INET6_ADDRSTRLEN]; + inet_ntop(serv->addr->sa_family, get_in_addr(serv->addr), my_ipstr, sizeof my_ipstr); + printf("Listening on %s:%d\n", my_ipstr, get_in_port(serv->addr)); + + return 0; +} + +void handle_path(struct server *serv, const_string method, const_string path, handler_func handler) { + struct handler hand = { + .method = method, + .path = path, + .func = handler, + }; + + *push(&serv->handlers, serv->arena) = hand; +} + +void compose_response(struct response *resp, arena *perm, arena scratch) { + const_string_da header_da = {0}; + *push(&header_da, &scratch) = CS("HTTP/1.1"); + + if (resp->code != 0) { + char buf[4]; + sprintf(buf, "%ld", resp->code); + *push(&header_da, &scratch) = cs_from_cstr(buf); + *push(&header_da, &scratch) = get_response_string(resp->code); + } else { + printf("Response is handeled, but no code is given"); + *push(&header_da, &scratch) = CS("500"); + *push(&header_da, &scratch) = get_response_string(INTERNAL_SERVER_ERROR); + } + + volatile const_string header = arena_cs_concat(&scratch, header_da, CS(" ")); + + volatile const_string str; + if (resp->body.len != 0) { + const_string_da resp_da= {0}; + + *push(&resp_da, perm) = header; + *push(&resp_da, perm) = CS(""); + *push(&resp_da, perm) = resp->body; + + str = arena_cs_concat(&scratch, resp_da, CS("\r\n")); + } else { + str = arena_cs_append(perm, header, CS("\r\n\r\n")); + } + + resp->string = str; +} + +void process_request(struct server *serv, ssize_t inc_fd) { + arena req_arena = {0}; + arena req_scratch = {0}; + 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; + } + + buf[got] = '\0'; + + req_str = arena_cs_append(&req_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; + } + + buf[got] = '\0'; + + const_string tmp = cs_from_parts(buf, got); + req_str = arena_cs_append(&req_arena, req_str, tmp); + } + + const_string method = cs_chop_delim(&req_str, ' '); + const_string path = cs_chop_delim(&req_str, ' '); + cs_chop_delim(&req_str, '\r'); + req_str.data += 1; + + header_da headers = {0}; + while (req_str.data[0] != '\r') { + const_string header_str = cs_chop_delim(&req_str, '\r'); + req_str.data += 1; + + struct header header; + header.key = cs_chop_delim(&header_str, ':'); + header_str.data++; + header.value = header_str; + + *push(&headers, &req_arena) = header; + } + cs_chop_delim(&req_str, '\n'); + + struct request req = { + .method = method, + .path = path, + .headers = headers, + .body = req_str, + }; + struct response resp = {0}; + + bool handled = false; + for (size_t i = 0; i < serv->handlers.len; i++) { + struct handler handler = serv->handlers.data[i]; + if ((cs_eq(handler.path, CS("HEAD")) + || cs_eq(handler.path, path)) + && cs_eq(serv->handlers.data[i].path, path)) { + serv->handlers.data[i].func(req, &resp); + handled = true; + break; + } + } + + if (!handled) { + resp.code = 404; + } + + if (resp.string.len == 0) { + compose_response(&resp, &req_arena, req_scratch); + } + + 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) { + fprintf(stderr, "send: %s\n", strerror(errno)); + } + + close(inc_fd); + arena_free(&req_arena); +} + +int listen_and_serve(struct server *serv) { + if (listen(serv->sockfd, 10) != 0) { + fprintf(stderr, "listen: %s\n", strerror(errno)); + return 1; + } + + 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; + } + + process_request(serv, inc_fd); + close(inc_fd); + } + return 0; +} diff --git a/lib/http.h b/lib/http.h new file mode 100644 index 0000000..97d058a --- /dev/null +++ b/lib/http.h @@ -0,0 +1,112 @@ +#ifndef HTTP_H_ +#define HTTP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "const_strings.h" +#include "arena_strings.h" +#include "arena.h" + +struct header { + const_string key; + const_string value; +}; + + +typedef struct { + struct header *data; + size_t len; + size_t cap; +} header_da; + +struct request { + const_string method; + const_string path; + header_da headers; + const_string body; +}; + +struct response { + size_t code; + const_string body; + const_string string; +}; + +typedef void (*handler_func)(struct request, struct response *resp); + +struct handler { + const_string method; + const_string path; + handler_func func; +}; + +typedef struct { + struct handler *data; + size_t len; + size_t cap; +} handler_da; + +struct server { + arena *arena; + int sockfd; + struct sockaddr *addr; + handler_da handlers; +}; + +typedef enum { + CONTINUE = 100, + SWITCHING_PROTOCOLS = 101, + PROCESSING = 102, + EARLY_HINTS = 103, + OK = 200, + CREATED = 201, + ACCEPTED = 202, + NO_CONTENT = 204, + MULTIPLE_CHOICES = 300, + MOVED_PERMANENTLY = 301, + FOUND = 302, + SEE_OTHER = 303, + NOT_MODIFIED = 304, + TEMPORARY_REDIRECT = 307, + PERMANENT_REDIRECT = 308, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + PAYMENT_REQUIRED = 402, + FORBIDDEN = 403, + NOT_FOUND = 404, + METHOD_NOT_ALLOWED = 405, + NOT_ACCEPTABLE = 406, + IM_A_TEEPOT = 418, + INTERNAL_SERVER_ERROR = 500, + NOT_IMPLEMENTED = 501, + BAD_GATEWAY = 502, + SERVICE_UNAVAILABLE = 503, + HTTP_VERSION_NOT_SUPPPORTED = 505, +} response_codes; + +const_string get_response_string(int code); +void sigchld_handler(); +void *get_in_addr(struct sockaddr *sa); +int get_in_port(struct sockaddr *sa); + +int init_server(arena *arena, struct server *serv, char *addr, char *port); +void compose_response(struct response *resp, arena *perm, arena scratch); +void process_request(struct server *serv, ssize_t inc_fd); +int listen_and_serve(struct server *serv); + +void handle_path(struct server *serv, const_string method, const_string path, handler_func handler); +void compose_response(struct response *resp, arena *perm, arena scratch); +void process_request(struct server *serv, ssize_t inc_fd); +#endif // HTTP_H_ diff --git a/main.c b/main.c index 85ac157..7cea88f 100644 --- a/main.c +++ b/main.c @@ -1,388 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "arena_strings.h" - -#define CONST_STRINGS_IMPLEMENTATION -#include "const_strings.h" - -#define ARENA_IMPLEMENTATION -#include "arena.h" - -#define MAX_DATA_SIZE 256 -#define BUFFER_SIZE 4096 -#define REQUEST_ARENA_SIZE 16*1024 - -int volatile keepRunning = 1; - -struct header { - const_string key; - const_string value; -}; - - -typedef struct { - struct header *data; - size_t len; - size_t cap; -} header_da; - -struct request { - const_string method; - const_string path; - header_da headers; - const_string body; -}; - -struct response { - size_t code; - const_string body; - const_string string; -}; - -typedef void (*handler_func)(struct request, struct response *resp); - -struct handler { - const_string method; - const_string path; - handler_func func; -}; - -typedef struct { - struct handler *data; - size_t len; - size_t cap; -} handler_da; - -struct server { - arena *arena; - int sockfd; - struct sockaddr *addr; - handler_da handlers; -}; - -typedef enum { - CONTINUE = 100, - SWITCHING_PROTOCOLS = 101, - PROCESSING = 102, - EARLY_HINTS = 103, - OK = 200, - CREATED = 201, - ACCEPTED = 202, - NO_CONTENT = 204, - MULTIPLE_CHOICES = 300, - MOVED_PERMANENTLY = 301, - FOUND = 302, - SEE_OTHER = 303, - NOT_MODIFIED = 304, - TEMPORARY_REDIRECT = 307, - PERMANENT_REDIRECT = 308, - BAD_REQUEST = 400, - UNAUTHORIZED = 401, - PAYMENT_REQUIRED = 402, - FORBIDDEN = 403, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - NOT_ACCEPTABLE = 406, - IM_A_TEEPOT = 418, - INTERNAL_SERVER_ERROR = 500, - NOT_IMPLEMENTED = 501, - BAD_GATEWAY = 502, - SERVICE_UNAVAILABLE = 503, - HTTP_VERSION_NOT_SUPPPORTED = 505, -} response_codes; - -struct { - int 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")}, - {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(int 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 *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); -} - -int init_server(arena *arena, struct 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("bind"); - 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; - } - - char my_ipstr[INET6_ADDRSTRLEN]; - inet_ntop(serv->addr->sa_family, get_in_addr(serv->addr), my_ipstr, sizeof my_ipstr); - printf("Listening on %s:%d\n", my_ipstr, get_in_port(serv->addr)); - - return 0; -} - -void handle_path(struct server *serv, const_string method, const_string path, handler_func handler) { - struct handler hand = { - .method = method, - .path = path, - .func = handler, - }; - - *push(&serv->handlers, serv->arena) = hand; -} - -void compose_response(struct response *resp, arena *perm, arena scratch) { - const_string_da header_da = {0}; - *push(&header_da, &scratch) = CS("HTTP/1.1"); - - if (resp->code != 0) { - char buf[4]; - sprintf(buf, "%ld", resp->code); - *push(&header_da, &scratch) = cs_from_cstr(buf); - *push(&header_da, &scratch) = get_response_string(resp->code); - } else { - printf("Response is handeled, but no code is given"); - *push(&header_da, &scratch) = CS("500"); - *push(&header_da, &scratch) = get_response_string(INTERNAL_SERVER_ERROR); - } - - volatile const_string header = arena_cs_concat(&scratch, header_da, CS(" ")); - - volatile const_string str; - if (resp->body.len != 0) { - const_string_da resp_da= {0}; - - *push(&resp_da, perm) = header; - *push(&resp_da, perm) = CS(""); - *push(&resp_da, perm) = resp->body; - - str = arena_cs_concat(&scratch, resp_da, CS("\r\n")); - } else { - str = arena_cs_append(perm, header, CS("\r\n\r\n")); - } - - resp->string = str; -} - -void process_request(struct server *serv, ssize_t inc_fd) { - arena req_arena = {0}; - arena req_scratch = {0}; - 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; - } - - buf[got] = '\0'; - - req_str = arena_cs_append(&req_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; - } - - buf[got] = '\0'; - - const_string tmp = cs_from_parts(buf, got); - req_str = arena_cs_append(&req_arena, req_str, tmp); - } - - const_string method = cs_chop_delim(&req_str, ' '); - const_string path = cs_chop_delim(&req_str, ' '); - cs_chop_delim(&req_str, '\r'); - req_str.data += 1; - - header_da headers = {0}; - while (req_str.data[0] != '\r') { - const_string header_str = cs_chop_delim(&req_str, '\r'); - req_str.data += 1; - - struct header header; - header.key = cs_chop_delim(&header_str, ':'); - header_str.data++; - header.value = header_str; - - *push(&headers, &req_arena) = header; - } - cs_chop_delim(&req_str, '\n'); - - struct request req = { - .method = method, - .path = path, - .headers = headers, - .body = req_str, - }; - struct response resp = {0}; - - bool handled = false; - for (size_t i = 0; i < serv->handlers.len; i++) { - struct handler handler = serv->handlers.data[i]; - if ((cs_eq(handler.path, CS("HEAD")) - || cs_eq(handler.path, path)) - && cs_eq(serv->handlers.data[i].path, path)) { - serv->handlers.data[i].func(req, &resp); - handled = true; - break; - } - } - - if (!handled) { - resp.code = 404; - } - - if (resp.string.len == 0) { - compose_response(&resp, &req_arena, req_scratch); - } - - 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) { - fprintf(stderr, "send: %s\n", strerror(errno)); - } - - close(inc_fd); - arena_free(&req_arena); -} - -int listen_and_serve(struct server *serv) { - if (listen(serv->sockfd, 10) != 0) { - fprintf(stderr, "listen: %s\n", strerror(errno)); - return 1; - } - - 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; - } - - process_request(serv, inc_fd); - close(inc_fd); - } - return 0; -} +#include "lib/http.h" void success(struct request req, struct response *resp) { printf("Success handler:\n"); -- cgit v1.2.3