bunkum

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

commit dbaa680f3819391269a520637d013d145dce3177
parent f34b70f63be68595fc94039a698a51c952d8b172
Author: quantumish <freifeld.david@gmail.com>
Date:   Fri, 13 Oct 2023 22:54:05 -0400

Fix hashmap bug, add test

Diffstat:
MMakefile | 2+-
Mhttp/request.c | 48++++++++++++++++++++++++++++++++++++++++++++++--
Mtest.h | 1+
Mtestutils.c | 7+++++++
Mutils/hashmap.c | 38+++++++++++++++++++++++---------------
Mutils/hashmap.h | 2++
Mutils/profile.c | 2++
Mutils/shitvec.c | 1-
Mutils/sync.c | 3+--
9 files changed, 83 insertions(+), 21 deletions(-)

diff --git a/Makefile b/Makefile @@ -8,7 +8,7 @@ OBJ = $(wildcard build/*.o) all: serv serv: $(CLIBS) http.c - $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) -O3 run: serv sudo docker build -t bunkum-dev . diff --git a/http/request.c b/http/request.c @@ -17,6 +17,24 @@ enum http_method method_enum(char* p) { return method_codes[((*(uint64_t*)p*0x1b8b6e6d) % 0x100000000) >> 28]; } + +/* enum parse_qvals_err { */ +/* PARSE_QVALS_SUCCESS, */ +/* PARSE_QVALS_MALFORMED, */ +/* }; */ + +/* struct parse_qvals_res { */ +/* enum parse_qval_err err; */ +/* union { */ +/* shitvec_t items; */ + +/* } data; */ +/* }; */ + +/* shitvec_t parse_qvalues(char* str) { */ + +/* } */ + // Be careful about passing by value! if the internal hashmap resizes // a req_free() call on the original (the one copied *from*) will be a // double free() error @@ -47,7 +65,9 @@ shitvec_t hdr_parse_accept(char* val) { next = strtok(NULL, ","); float q = 1; char* qptr = NULL; - if (next != NULL && (qptr = (char*)memchr(start, ';', next-start))) { + + char* end = next != NULL ? next : start+strlen(start); + if ((qptr = (char*)memchr(start, ';', end-start))) { sscanf(qptr, ";q=%f", &q); } struct req_mimetype mtype; @@ -62,6 +82,14 @@ shitvec_t hdr_parse_accept(char* val) { return mimetypes; } +struct mime_type parse_mimetype(char* str) { + char* slash = strchr(str, '/'); + // if (slash == NULL) handle_error(); + char* subtype = slash + 1; +} + + + int req_parse(request_t* req) { char method[8] = {0}; int matched = sscanf(req->buf, "%s %s HTTP/%f\r\n", (char*)method, (char*)req->path, &req->ver); @@ -74,7 +102,8 @@ int req_parse(request_t* req) { char name[MAX_HEADER_NAME] = {0}; char value[MAX_HEADER_VALUE] = {0}; int matched = sscanf(start, "%32[^:]: %s", name, value); - if (matched == 0) return 0; // No more headers; + // printf("%d matched. %s: %s\n", matched, name, value); + if (matched == 0) return 0; // No more headers; else if (matched == 1) { if (name[0] == '\r') return 0; // TODO sketch return -1; // Uhh... half a header. NOTE doesn't even seem to work. fun. @@ -133,4 +162,19 @@ void test_hdr_parse_accept() { assert_float_eq(0.8, mtype->q); } +void test_bad_req_total_garbage() { + request_t r = req_new("this is not a request.", 23); + assert_int_eq(-1, req_parse(&r)); +} + +/* void test_bad_req_half_header() { */ +/* char bigbuf[1024] = "GET / HTTP/1.1\r\nProfile:\n"; */ +/* request_t r = req_new(bigbuf, 1024); */ +/* assert_int_eq(-1, req_parse(&r)); */ + +/* bzero(bigbuf, 1024); */ +/* bigbuf = "GET / HTTP/1.1\r\nProfile: */ +/* } */ + + #endif diff --git a/test.h b/test.h @@ -7,6 +7,7 @@ void assert_str_eq(char* a, char* b); void assert_float_eq(float a, float b); void assert_size_eq(size_t a, size_t b); +void assert_int_eq(int a, int b); void assert_bool(bool expr); #endif diff --git a/testutils.c b/testutils.c @@ -18,6 +18,13 @@ void assert_size_eq(size_t a, size_t b) { } } +void assert_int_eq(int a, int b) { + if (a != b) { + printf("(%d != %d)", a, b); + exit(1); + } +} + void assert_float_eq(float a, float b) { if (a != b) { printf("(%f != %f)", a, b); diff --git a/utils/hashmap.c b/utils/hashmap.c @@ -44,19 +44,19 @@ hashmap_t hashmap_new(size_t ksize, size_t vsize) { h.k_sz = ksize; h.v_sz = vsize; h.len = HASHMAP_INIT_SIZE; - h.vark = false; + h.vark = false; h.filled = 0; // Store # of filled keys for load factor calculation return h; } int hashmap_kcmp(hashmap_t* h, void* a, void* b) { - if (h->vark) return strcmp(a, b); - return memcmp(a, b, h->k_sz); + if (h->vark) return strcmp(a, b); + return memcmp(a, b, h->k_sz); } void* hashmap_kcpy(hashmap_t* h, void* a, void* b) { - if (h->vark) return strcpy(a, b); - return memcpy(a, b, h->k_sz); + if (h->vark) return strcpy(a, b); + return memcpy(a, b, h->k_sz); } void hashmap_resize(hashmap_t* h) { @@ -142,7 +142,7 @@ int hashmap_del(hashmap_t* h, void* k) { int looped_once = 0; for (size_t off = index; off < h->len; off+=1) { if (hashmap_kcmp(h, h->keys + (off * h->k_sz), k) == 0) { - memset(h->keys + (off * h->v_sz), 0, h->k_sz); + memset(h->keys + (off * h->k_sz), 0, h->k_sz); // No reason to delete value, will just be overwritten next time. return 0; } @@ -164,15 +164,23 @@ void hashmap_free(hashmap_t* h) { #include "../test.h" void test_hashmap_sanity() { - hashmap_t h = hashmap_new(16, 64); - hashmap_set(&h, "whoo", "This is a test."); - hashmap_set(&h, "Accept", "image/png,text/plain;q=0.5,image/jpeg"); - hashmap_resize(&h); // make sure it doesn't explode - hashmap_set(&h, "Blah blah", "This is (another) test."); - assert_str_eq(hashmap_get(&h, "whoo"), "This is a test."); - assert_str_eq(hashmap_get(&h, "Accept"), "image/png,text/plain;q=0.5,image/jpeg"); - assert_str_eq(hashmap_get(&h, "Blah blah"), "This is (another) test."); - hashmap_free(&h); + hashmap_t h = hashmap_new(16, 64); + hashmap_set(&h, "whoo", "This is a test."); + hashmap_set(&h, "Accept", "image/png,text/plain;q=0.5,image/jpeg"); + hashmap_resize(&h); // make sure it doesn't explode + hashmap_set(&h, "Blah blah", "This is (another) test."); + assert_str_eq(hashmap_get(&h, "whoo"), "This is a test."); + assert_str_eq(hashmap_get(&h, "Accept"), "image/png,text/plain;q=0.5,image/jpeg"); + assert_str_eq(hashmap_get(&h, "Blah blah"), "This is (another) test."); + hashmap_free(&h); +} + +void test_hashmap_del() { + hashmap_t h = hashmap_new(16, 64); + assert_int_eq(hashmap_del(&h, "abc"), 1); + assert_int_eq(hashmap_set(&h, "abc", "whoo"), 0); + assert_int_eq(hashmap_del(&h, "abc"), 0); + assert_int_eq((long)hashmap_get(&h, "abc"), 0); } #endif diff --git a/utils/hashmap.h b/utils/hashmap.h @@ -17,6 +17,8 @@ typedef struct hashmap_t { // Generates a new hashmap_t. hashmap_t hashmap_new(size_t ksize, size_t vsize); +#define hashmap_init(t1, t2) hashmap_new(sizeof(t1), sizeof(t2)) + // Sets a key-value pair in the hashmap_t passed to it. Returns 0x0 for success, 0x1 for failure. int hashmap_set(hashmap_t* h, void* k, void* v); diff --git a/utils/profile.c b/utils/profile.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include <string.h> #include <errno.h> #include <stdio.h> diff --git a/utils/shitvec.c b/utils/shitvec.c @@ -1,4 +1,3 @@ - #include <stdlib.h> #include <string.h> diff --git a/utils/sync.c b/utils/sync.c @@ -1,4 +1,3 @@ - #include "log.h" #include "sync.h" @@ -29,7 +28,7 @@ void* channel_pop(channel_t* chan) { } void* channel_recv(channel_t* chan) { - while(__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) == 0); + while(__atomic_load_n(&chan->sz, __ATOMIC_RELAXED) == 0); return channel_pop(chan); }