bunkum

an old and silly c99 web server with some fun features
Log | Files | Refs | README

commit f778a27c607ba779cae44fd074cf8eec3fbda50c
parent 6798449c8145f9d1be8e4d32c362b9bbfa77ca4f
Author: quantumish <freifeld.david@gmail.com>
Date:   Mon, 15 May 2023 01:07:35 -0700

Simple logic for switch to process-based design

Diffstat:
Mhttp.c | 314++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mutils/sync.c | 35+++++++++++++++++------------------
2 files changed, 177 insertions(+), 172 deletions(-)

diff --git a/http.c b/http.c @@ -1,6 +1,5 @@ #define _GNU_SOURCE -#include <bits/time.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> @@ -9,7 +8,7 @@ #include <string.h> #include <assert.h> #include <dirent.h> - +#include <time.h> #include <signal.h> #include <errno.h> @@ -23,9 +22,6 @@ #include <arpa/inet.h> #include <pthread.h> -#include <time.h> - -#include <zlib.h> #include <libunwind.h> #include <libunwind-ptrace.h> @@ -44,13 +40,13 @@ response_t serve_error(enum StatusCode c) { response_t r = resp_new(c); resp_add_hdr(&r, "Content-Type", "text/html"); - char errt[256]; - sprintf(errt, "Error %d", c); + char errt[256]; + sprintf(errt, "Error %d", c); - html_t html = html_new(); - html_body_add(&html.body, html_h1_new(errt)); - html_body_add(&html.body, html_p_new("Hey! Don't do that.")); - char* msg = html_render(&html); + html_t html = html_new(); + html_body_add(&html.body, html_h1_new(errt)); + html_body_add(&html.body, html_p_new("Hey! Don't do that.")); + char* msg = html_render(&html); resp_add_content(&r, msg, strlen(msg)); return r; @@ -71,65 +67,65 @@ response_t serve_file(request_t* req) { strcat(path, req->path); int fd = open(path, O_RDONLY); // TODO handle response_t r = resp_new(OK); - + struct stat st; fstat(fd, &st); char* fbuf = malloc(st.st_size); // TODO what if file larger than memory? for (size_t i = 0; read(fd, fbuf+(i*4096), 4096) > 0; i++); - + char* ext = get_file_ext(req->path); - resp_set_ctype(&r, ext); + resp_set_ctype(&r, ext); - bool ok = true; + bool ok = true; char* mtype = (char*)ext_to_mtype(ext); - char* hdr; - if ((hdr = hashmap_get(&req->headers, "Accept"))) { - ok = false; - shitvec_t mtypes = hdr_parse_accept(hdr); - for (int j = 0; j < mtypes.vec_sz; j++) { - struct req_mimetype* a_mtype = shitvec_get(&mtypes, j); - // TODO doesn't handle stuff like image/* (is that even allowed?) - if (strcmp(a_mtype->item, mtype) == 0 || strcmp(a_mtype->item, "*/*") == 0) { - ok = true; - break; - } - } + char* hdr; + if ((hdr = hashmap_get(&req->headers, "Accept"))) { + ok = false; + shitvec_t mtypes = hdr_parse_accept(hdr); + for (int j = 0; j < mtypes.vec_sz; j++) { + struct req_mimetype* a_mtype = shitvec_get(&mtypes, j); + // TODO doesn't handle stuff like image/* (is that even allowed?) + if (strcmp(a_mtype->item, mtype) == 0 || strcmp(a_mtype->item, "*/*") == 0) { + ok = true; + break; + } + } } - + char* buf = fbuf; // buffer to be written size_t bufsize = st.st_size; - + if (ok && (hdr = hashmap_get(&req->headers, "Accept-Encoding"))) { - ok = false; - shitvec_t mtypes = hdr_parse_accept(hdr); // abuse of this func - for (int j = 0; j < mtypes.vec_sz; j++) { - struct req_mimetype* a_mtype = shitvec_get(&mtypes, j); + ok = false; + shitvec_t mtypes = hdr_parse_accept(hdr); // abuse of this func + for (int j = 0; j < mtypes.vec_sz; j++) { + struct req_mimetype* a_mtype = shitvec_get(&mtypes, j); if (strcmp(a_mtype->item, "gzip") == 0) { resp_add_hdr(&r, "Content-Encoding", "gzip"); buf = gzip_compress(buf, &bufsize); ok = true; - break; - } else if (strcmp(a_mtype->item, "deflate") == 0) { + break; + } else if (strcmp(a_mtype->item, "deflate") == 0) { resp_add_hdr(&r, "Content-Encoding", "deflate"); buf = zlib_compress(buf, &bufsize); ok = true; break; } else if (strcmp(a_mtype->item, "identity") == 0) { - ok = true; - break; - } - } + ok = true; + break; + } + } } /* if (!ok) return serve_error(NotAcceptable); */ - #ifndef __APPLE__ +#ifndef __APPLE__ char datebuf[64]; time_to_str(st.st_mtim.tv_sec, datebuf); - resp_add_hdr(&r, "Last-Modified", datebuf); - #endif - + resp_add_hdr(&r, "Last-Modified", datebuf); +#endif + resp_add_content(&r, buf, bufsize); free(buf); return r; @@ -141,45 +137,45 @@ response_t serve_file(request_t* req) { response_t make_response (request_t* req, int pfd) { if (req_parse(req) < 0) { log_error("Failed to parse incoming request."); - return serve_error(BadRequest); + return serve_error(BadRequest); + } + + if (hashmap_get(&req->headers, "Profile") != 0x0) { + log_debug("Got header"); + int profile = true; + if (ptrace(PTRACE_TRACEME, NULL) < 0) { + log_error("PTRACE_TRACME failed with err %d", errno); + } + if (write(pfd, "profile", 8) != 8) { + log_error("write err"); + } + usleep(100); } - if (hashmap_get(&req->headers, "Profile") != 0x0) { - log_debug("Got header"); - int profile = true; - if (ptrace(PTRACE_TRACEME, NULL) < 0) { - log_error("PTRACE_TRACME failed with err %d", errno); - } - if (write(pfd, "profile", 8) != 8) { - log_error("write err"); - } - usleep(100); - } - log_info("Got request %s %s", method_name(req->method), req->path); - char* mapped_path = hashmap_get(&path_redirs, req->path); - if (mapped_path != NULL) { - strcpy(req->path, mapped_path); - } - + char* mapped_path = hashmap_get(&path_redirs, req->path); + if (mapped_path != NULL) { + strcpy(req->path, mapped_path); + } + if (!shitvec_check(&paths, req->path, (sv_cmp_t)strcmp)) { return serve_error(NotFound); } - log_debug("method = %d", req->method); + log_debug("method = %d", req->method); switch (req->method) { - case GET: return serve_file(req); + case GET: return serve_file(req); default: return serve_error(MethodNotAllowed); - } + } } double diff_timespec(const struct timespec *time1, const struct timespec *time0) { - return (time1->tv_sec - time0->tv_sec) - + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; + return (time1->tv_sec - time0->tv_sec) + + (time1->tv_nsec - time0->tv_nsec) / 1000000000.0; } -void* handle_conn(int ns, int pfd) { +void* handle_conn(int ns, int pfd) { char msgbuf[1024] = {0}; while(true) { struct timespec before, after, tdiff; @@ -190,19 +186,19 @@ void* handle_conn(int ns, int pfd) { clock_gettime(CLOCK_MONOTONIC, &before); if (msgbuf[0] != 0) { request_t req = req_new(msgbuf, 1024); - - response_t r = make_response(&req, pfd); + + response_t r = make_response(&req, pfd); send(ns, r.content, r.sz, 0); free(r.content); - write(pfd, "stop", 5); - clock_gettime(CLOCK_MONOTONIC, &after); + write(pfd, "stop", 5); + clock_gettime(CLOCK_MONOTONIC, &after); log_info("Handled request in %f sec", diff_timespec(&after, &before)); req_free(&req); } } } -int list_files_sv(shitvec_t* sv, char* base) { +int list_files_sv(shitvec_t* sv, char* base) { char path[MAX_PATH_LEN] = "/"; struct dirent* dp; DIR* dir = opendir(base); @@ -217,124 +213,134 @@ int list_files_sv(shitvec_t* sv, char* base) { list_files_sv(sv, path+1); } } - - closedir(dir); + + closedir(dir); return 0; } channel_t listen_chan; -/* void* listen_for_conns(void* ctxt) { */ -/* int s = *(int*)ctxt; */ -/* int namelen; */ -/* struct sockaddr_in client; */ -/* while (true) { */ -/* listen(s, 1); */ -/* int ns = accept(s, (struct sockaddr*)&client, (socklen_t*)&namelen); */ -/* log_info("Handling connection from %s", inet_ntoa(client.sin_addr)); */ -/* channel_push(&listen_chan, &ns); */ -/* } */ -/* } */ - -#define MAX_SYMLEN 128 - -int profile(pid_t tid) { - errno = 0; - log_debug("Tracing %d", tid); - if (ptrace(PTRACE_ATTACH, tid) < 0) { - log_error("ptrace() fail, errno %d", errno); - } - kill(tid, SIGSTOP); - waitpid(tid, NULL, 0); - void* ui = _UPT_create(tid); - if (!ui) return -1; - unw_cursor_t c; - unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, 0); - unw_init_remote(&c, as, ui); - do { - unw_word_t offset; - char fname[MAX_SYMLEN] = {0}; - int resp = unw_get_proc_name(&c, fname, sizeof(fname), &offset); - log_trace("%s (code %d, errno %d)", fname, resp, errno); - } while(unw_step(&c) > 0); - _UPT_resume(as, &c, ui); - _UPT_destroy(ui); +void* listen_for_conns(void* ctxt) { + int s = *(int*)ctxt; + int namelen; + struct sockaddr_in client; + while (true) { + listen(s, 1); + int ns = accept(s, (struct sockaddr*)&client, (socklen_t*)&namelen); + log_info("Handling connection from %s", inet_ntoa(client.sin_addr)); + channel_push(&listen_chan, &ns); + } +} + +#define MAX_SYMLEN 32 + +shitvec_t profile(pid_t tid) { + shitvec_t stack = shitvec_new(MAX_SYMLEN); + errno = 0; + log_debug("Tracing %d", tid); + if (ptrace(PTRACE_ATTACH, tid) < 0) { + log_error("ptrace() fail, errno %d", errno); + } + kill(tid, SIGSTOP); + waitpid(tid, NULL, 0); + void* ui = _UPT_create(tid); + if (!ui) return -1; + unw_cursor_t c; + unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, 0); + unw_init_remote(&c, as, ui); + do { + unw_word_t offset; + char fname[MAX_SYMLEN] = {0}; + int resp = unw_get_proc_name(&c, fname, sizeof(fname), &offset); + log_trace("%s (code %d, errno %d)", fname, resp, errno); + shitvec_push(&stack, fname); + } while(unw_step(&c) > 0); + _UPT_resume(as, &c, ui); + _UPT_destroy(ui); kill(tid, SIGSTOP); - printf("waiting\n"); - waitpid(tid, NULL, 0); - ptrace(PTRACE_DETACH, tid, NULL, NULL); - return 0; + waitpid(tid, NULL, 0); + ptrace(PTRACE_DETACH, tid, NULL, NULL); + return stack; } -struct thread_comm { - channel_t chan; - pid_t tid; +struct tree_node { + struct tree_node* left; + struct tree_node* right; + char name[MAX_SYMLEN]; +}; + +struct conn_ctxt { + pid_t pid; + int fd; }; // TODO some sort of DDOS protection idk int main() { int s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) die("socket"); - - int portnum = 8080; + + int portnum = 8080; struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons(portnum); name.sin_addr.s_addr = htonl(INADDR_ANY); - while(bind(s, (struct sockaddr*)&name, sizeof(name)) < 0) { - portnum += 1; - name.sin_port = htons(portnum); - } - + while(bind(s, (struct sockaddr*)&name, sizeof(name)) < 0) { + portnum += 1; + name.sin_port = htons(portnum); + } + paths = shitvec_new(MAX_PATH_LEN); path_redirs = hashmap_new(MAX_PATH_LEN, MAX_PATH_LEN); path_redirs.vark = true; hashmap_set(&path_redirs, "/", "/index.html"); - + list_files_sv(&paths, "public"); int nlen; getsockname(s, (struct sockaddr*)&name, (socklen_t*)&nlen); log_debug("Open on port %d.", htons(name.sin_port)); - - listen_chan = channel_new(sizeof(int)); - /* pthread_t lthread; */ - /* pthread_create(&lthread, NULL, listen_for_conns, &s); */ + listen_chan = channel_new(sizeof(int)); - int namelen; + pthread_t lthread; + pthread_create(&lthread, NULL, listen_for_conns, &s); + + int namelen; struct sockaddr_in client; - - int* ns; - pid_t pid; + shitvec_t conns = shitvec_new(sizeof(struct conn_ctxt)); while (true) { - listen(s, 1); - int ns = accept(s, (struct sockaddr*)&client, (socklen_t*)&namelen); - log_info("Got connection"); - - int pfds[2]; - pipe(pfds); - - pid = fork(); - if (pid == 0) { - handle_conn(ns, pfds[1]); - exit(0); - } else { - /* char msg[8] = {0}; */ - /* read(pfds[0], &msg, 8); */ - /* int retval = fcntl(pfds[0], F_SETFL, fcntl(pfds[0], F_GETFL) | O_NONBLOCK); */ - - /* while(true) { */ - /* profile(pid); */ - /* usleep(10); */ - /* if (read(pfds[0], &msg, 5) == 5 && strcmp(msg, "stop") == 0) break; */ - /* } */ - } + int* ptr = channel_try_recv(&listen_chan); + if (ptr != NULL) { + int pfds[2]; + pipe(pfds); + int retval = fcntl(pfds[0], F_SETFL, fcntl(pfds[0], F_GETFL) | O_NONBLOCK); + pid_t pid = fork(); + if (pid == 0) { + handle_conn(*ptr, pfds[1]); + close(pfds[0]); + close(pfds[1]); + exit(0); + } + struct conn_ctxt ctxt = {.fd = pfds[0], .pid = pid }; + shitvec_push(&conns, &ctxt); + } + for (int i = 0; i < conns.vec_sz; i++) { + struct conn_ctxt* ctxt = shitvec_get(&conns, i); + char msg[8] = {0}; + if (read(ctxt->fd, &msg, 8) != 8 || strcmp(msg, "profile") != 0) continue; + shitvec_t nodes = shitvec_new(sizeof(struct tree_node)); + while(true) { + profile(ctxt->pid); + usleep(10); + if (read(ctxt->fd, &msg, 5) == 5 && strcmp(msg, "stop") == 0) break; + } + } } } + // meta todos: // - TODO true dependencyless (no zlib, no pthread) // - TODO HTML parsing maybe but that makes me want to cry diff --git a/utils/sync.c b/utils/sync.c @@ -8,35 +8,34 @@ channel_t channel_new(size_t msg_sz) { pthread_mutex_init(&out.mutex, NULL); out.queue = shitvec_new(msg_sz); out.sz = 0; - return out; + return out; } void channel_push(channel_t* chan, void* msg) { pthread_mutex_lock(&chan->mutex); - shitvec_push(&chan->queue, msg); - pthread_mutex_unlock(&chan->mutex); - __atomic_fetch_add(&chan->sz, 1, __ATOMIC_RELAXED); + shitvec_push(&chan->queue, msg); + pthread_mutex_unlock(&chan->mutex); + __atomic_fetch_add(&chan->sz, 1, __ATOMIC_RELAXED); } void* channel_pop(channel_t* chan) { - void* out; - pthread_mutex_lock(&chan->mutex); - out = shitvec_get(&chan->queue, chan->queue.vec_sz-1); - chan->queue.vec_sz -= 1; - pthread_mutex_unlock(&chan->mutex); - __atomic_fetch_sub(&chan->sz, 1, __ATOMIC_RELAXED); - return out; + void* out; + pthread_mutex_lock(&chan->mutex); + out = shitvec_get(&chan->queue, chan->queue.vec_sz-1); + chan->queue.vec_sz -= 1; + pthread_mutex_unlock(&chan->mutex); + __atomic_fetch_sub(&chan->sz, 1, __ATOMIC_RELAXED); + return out; } void* channel_recv(channel_t* chan) { - while(__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) == 0); - return channel_pop(chan); + while(__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) == 0); + return channel_pop(chan); } void* channel_try_recv(channel_t* chan) { - if (__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) > 0) { - return channel_pop(chan); - } - return 0x0; + if (__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) > 0) { + return channel_pop(chan); + } + return 0x0; } -