aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c386
1 files changed, 1 insertions, 385 deletions
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");