aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspl3g <spleefer6@yandex.ru>2025-03-11 22:37:43 +0300
committerspl3g <spleefer6@yandex.ru>2025-03-11 22:37:43 +0300
commita854e4cfa450590753171fbbe4dde8da4f504abd (patch)
treefb1d91910e5a86a6460654af289fbb4b3ef707b7
parent74ab2f6b89ceb2af34524386d6003746570d2b03 (diff)
Move everything to the lib folder
-rw-r--r--Makefile4
-rw-r--r--lib/arena.c (renamed from arena.h)49
-rw-r--r--lib/arena.h40
-rw-r--r--lib/arena_strings.c (renamed from arena_strings.c)0
-rw-r--r--lib/arena_strings.h (renamed from arena_strings.h)6
-rw-r--r--lib/const_strings.c (renamed from const_strings.h)34
-rw-r--r--lib/const_strings.h29
-rw-r--r--lib/http.c288
-rw-r--r--lib/http.h112
-rw-r--r--main.c386
10 files changed, 482 insertions, 466 deletions
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/lib/arena.c
index c99fc97..dd12d0c 100644
--- a/arena.h
+++ b/lib/arena.c
@@ -1,44 +1,4 @@
-#ifndef ARENA_H_
-#define ARENA_H_
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <assert.h>
-
-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
+#include "arena.h"
region *new_region(size_t cap) {
size_t size_bytes = sizeof(region) + sizeof(uintptr_t)*cap;
@@ -52,7 +12,7 @@ region *new_region(size_t cap) {
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;
@@ -64,7 +24,7 @@ void *arena_alloc(arena *a, size_t size_bytes) {
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;
@@ -87,7 +47,7 @@ void arena_free(arena *a) {
}
}
-void grow(void *slice, size_t size, arena *arena) {
+void grow_da(void *slice, size_t size, arena *arena) {
struct {
void *data;
size_t len;
@@ -105,4 +65,3 @@ void grow(void *slice, size_t size, arena *arena) {
memcpy(slice, &replica, sizeof(replica));
}
-#endif // ARENA_IMPLEMENTATION
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 <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+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/arena_strings.c b/lib/arena_strings.c
index 28bd81a..28bd81a 100644
--- a/arena_strings.c
+++ b/lib/arena_strings.c
diff --git a/arena_strings.h b/lib/arena_strings.h
index 8a8add0..85ffda3 100644
--- a/arena_strings.h
+++ b/lib/arena_strings.h
@@ -1,5 +1,5 @@
-#ifndef ARENA_STRINGS_H
-#define ARENA_STRINGS_H
+#ifndef ARENA_STRINGS_H_
+#define ARENA_STRINGS_H_
#include "arena.h"
#include "const_strings.h"
@@ -14,4 +14,4 @@ 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
+#endif // ARENA_STRINGS_H_
diff --git a/const_strings.h b/lib/const_strings.c
index 3f13b95..09d2c5f 100644
--- a/const_strings.h
+++ b/lib/const_strings.c
@@ -1,36 +1,10 @@
-#ifndef CONST_STRINGS_H_
-#define CONST_STRINGS_H_
-
-#include <string.h>
-#include <stdio.h>
-#include <stdbool.h>
-
-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
+#include "const_strings.h"
const_string cs_from_parts(char* data, int len) {
const_string str;
str.data = data;
str.len = len;
-
+
return str;
}
@@ -77,7 +51,7 @@ const_string cs_chop_delim(const_string *str, char delim) {
str->len -= n;
str->data += n;
}
-
+
return result;
}
@@ -104,5 +78,3 @@ void cs_print(char *format, const_string str) {
buf[str.len] = '\0';
printf(format, buf);
}
-
-#endif // STRINGS_IMPLEMENTATION
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 <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+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 <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#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 <stdio.h>
-#include <string.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <stdlib.h>
-#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");