From 02d02ed4ddba4d66d3f1d5ec92bfe9ec4ca182d0 Mon Sep 17 00:00:00 2001 From: Soikk Date: Sat, 14 Jun 2025 02:48:33 +0200 Subject: Reworked config. Added mime module. Reworked mime related functions in net module. --- Makefile | 2 +- src/config/config.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++------ src/config/config.h | 38 +++++-- src/mime/mime.c | 59 +++++++++++ src/mime/mime.h | 21 ++++ src/net/net.c | 55 +--------- src/net/net.h | 11 +- 7 files changed, 372 insertions(+), 99 deletions(-) create mode 100644 src/mime/mime.c create mode 100644 src/mime/mime.h diff --git a/Makefile b/Makefile index 2446219..fef954a 100755 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ else RUNCMD = ./server.exe endif -OBJS = $(addprefix $(OBJDIR)/, bit.o files.o str.o log.o list.o crc64.o net.o ipc.o config.o) +OBJS = $(addprefix $(OBJDIR)/, bit.o files.o str.o log.o list.o crc64.o net.o ipc.o mime.o config.o) INCL = -I$(LIBDIR) -I$(SRCDIR) diff --git a/src/config/config.c b/src/config/config.c index 5b4c6cc..3582e60 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -1,54 +1,281 @@ #include "config.h" -config read_config(str cfg){ - config conf = {0}; +static void read_logs(str logs){ int off = 0; - while(off < cfg.len){ - while(charisspace(cfg.ptr[off])) off++; - if(cfg.ptr[off] == '#'){ - while(!charislinebreak(cfg.ptr[off])) off++; + while(off < logs.len){ + while(off < logs.len && charisspace(logs.ptr[off])) off++; + if(logs.ptr[off] == '#'){ + while(off < logs.len && !charislinebreak(logs.ptr[off])) off++; continue; } - str key = sread_delim_f(cfg.ptr + off, charisspace, true); - off += key.len + 1; - while(charisspace(cfg.ptr[off])) off++; + int level; + str slevel = sread_delim_f(logs.ptr + off, charisspace, true); + off += slevel.len; + if(streq(slevel, sstr("DEBUG"))){ + level = LOG_DEBUG; + }else if(streq(slevel, sstr("INFO"))){ + level = LOG_INFO; + }else if(streq(slevel, sstr("WARN"))){ + level = LOG_WARN; + }else if(streq(slevel, sstr("ERROR"))){ + level = LOG_ERROR; + }else{ + if(slevel.len != 0){ + log_warn("Unexpected logging level in 'log' configuration: '%.*s'", slevel.len, slevel.ptr); + } + while(off < logs.len && !charislinebreak(logs.ptr[off])) off++; + continue; + } + if(log_get_files(level) >= MAX_LOGFILES){ + log_warn("Cannot add any more files to logging level %.*s", slevel.len, slevel.ptr); + while(off < logs.len && !charislinebreak(logs.ptr[off])) off++; + continue; + } + while(off < logs.len && charisspace(logs.ptr[off])) off++; + str file = read_delim_f(logs.ptr + off, charisspace, true); + off += file.len; + while(off < logs.len && charisspace(logs.ptr[off])) off++; + str mode = read_delim_f(logs.ptr + off, charisspace, true); + off += mode.len; + if(streq(file, sstr("stderr"))){ + int set = strtou(mode); + log_set_stderr(level, set); + }else if(streq(mode, sstr("w")) || streq(mode, sstr("a"))){ + FILE *fp = fopen(file.ptr, mode.ptr); + if(fp == NULL){ + log_warn("Error opening file '%.*s': %s", file.len, file.ptr, strerror(errno)); + }else{ + log_add_fp(level, fp); + } + }else{ + log_warn("Invalid read mode for logging file '%.*s': '%.*s'. Only 'w' or 'a' permitted", + file.len, file.ptr, mode.len, mode.ptr); + } + free_str(&file); + free_str(&mode); + while(off < logs.len && !charislinebreak(logs.ptr[off])) off++; + } +} - if(streq(key, sstr("port"))){ - str val = sread_delim_f(cfg.ptr + off, charisspace, true); +void rotate_logs(str logs){ + for(int i = 0; i < LOG_LEVEL_COUNT; i++){ + log_remove_fps(i); + log_set_stderr(i, 1); + } + read_logs(logs); +} + +config_m master_config(char *filename){ + config_m conf = {0}; + conf.file = map_file(filename); + int off = 0; + while(off < conf.file.len){ + while(off < conf.file.len && charisspace(conf.file.ptr[off])) off++; + if(conf.file.ptr[off] == '#'){ + while(off < conf.file.len && !charislinebreak(conf.file.ptr[off])) off++; + continue; + } + str key = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += key.len; + while(off < conf.file.len && charisspace(conf.file.ptr[off]) && !charislinebreak(conf.file.ptr[off])) off++; + + if(streq(key, sstr("name"))){ + conf.name = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += conf.name.len; + }else if(streq(key, sstr("port"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); off += val.len; conf.port = (int)strtou(val); + }else if(streq(key, sstr("backlog"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += val.len; + conf.backlog = (int)strtou(val); + }else if(streq(key, sstr("logs"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += val.len; + str logs; + if(val.ptr[0] != '{'){ + str logfile = dup_str(val); + logs = file_to_str(logfile.ptr); + free_str(&logfile); + }else{ + logs = read_delim(conf.file.ptr + off, '}'); + off += logs.len; + } + rotate_logs(logs); + free_str(&logs); + }else if(streq(key, sstr("worker"))){ + int bcount = 1; + while(off < conf.file.len && bcount > 0){ + if(conf.file.ptr[off] == '{') bcount++; + if(conf.file.ptr[off] == '}') bcount--; + off++; + } + }else if(key.len != 0){ + log_warn("Unexpected entry in configuration: '%.*s'", key.len, key.ptr); + } + while(off < conf.file.len && !charislinebreak(conf.file.ptr[off])) off++; + off++; + }; + + return conf; +} + +config_w worker_config(char *filename){ + config_w conf = {0}; + conf.file = map_file(filename); + init_nlist(conf.files); + int off = 0; + while(off < conf.file.len){ + while(off < conf.file.len && charisspace(conf.file.ptr[off])) off++; + if(conf.file.ptr[off] == '#'){ + while(off < conf.file.len && !charislinebreak(conf.file.ptr[off])) off++; + continue; + } + str key = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += key.len; + while(off < conf.file.len && charisspace(conf.file.ptr[off]) && !charislinebreak(conf.file.ptr[off])) off++; + + if(streq(key, sstr("name"))){ + conf.name = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += conf.name.len; + }else if(streq(key, sstr("root"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += val.len; + str trailslash = val.ptr[val.len-1] == '/' ? sstr("") : sstr("/"); + conf.root = dup_strs(val, trailslash); + }else if(streq(key, sstr("bundle"))){ + conf.bundle = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += conf.bundle.len; + }else if(streq(key, sstr("cert"))){ + conf.cert = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += conf.cert.len; + }else if(streq(key, sstr("key"))){ + conf.key = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += conf.key.len; + }else if(streq(key, sstr("https"))){ + conf.secure = 1; + }else if(streq(key, sstr("http"))){ + conf.secure = 0; }else if(streq(key, sstr("ipv4"))){ conf.ipv4 = 1; }else if(streq(key, sstr("ipv6"))){ conf.ipv6 = 1; - }else if(streq(key, sstr("root"))){ - str val = sread_delim_f(cfg.ptr + off, charisspace, true); - str trailslash = val.ptr[val.len-1] == '/' ? sstr("") : sstr("/"); - conf.root = dup_strs(val, trailslash); - off += conf.root.len; - }else{ - log_warn("Unexpected entry in configuration: '%.*s'", key.len, key.ptr); + }else if(streq(key, sstr("types"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += val.len; + str types; + if(val.ptr[0] != '{'){ + str typesfile = dup_str(val); + types = map_file(typesfile.ptr); + list_push(conf.files, types); + free_str(&typesfile); + }else{ + types = sread_delim(conf.file.ptr + off, '}'); + off += types.len; + } + read_mime_types(types); + }else if(streq(key, sstr("logs"))){ + str val = sread_delim_f(conf.file.ptr + off, charisspace, true); + off += val.len; + str logs; + if(val.ptr[0] != '{'){ + str logfile = dup_str(val); + logs = file_to_str(logfile.ptr); + free_str(&logfile); + }else{ + logs = read_delim(conf.file.ptr + off, '}'); + off += logs.len; + } + rotate_logs(logs); + free_str(&logs); } - while(!charislinebreak(cfg.ptr[off])) off++; + while(off < conf.file.len && !charislinebreak(conf.file.ptr[off])) off++; off++; }; + return conf; +} + +void free_master_config(config_m *conf){ + conf->name = (str){0}; + conf->port = 0; + conf->backlog = 0; + unmap_file(&conf->file); +} + +void free_worker_config(config_w *conf){ + conf->name = (str){0}; + conf->root = (str){0}; + conf->bundle = (str){0}; + conf->cert = (str){0}; + conf->key = (str){0}; + conf->secure = 0; + conf->ipv4 = 0; + conf->ipv6 = 0; + free_mime_types(); + for(int i = 0; i < list_size(conf->files); i++){ + unmap_file(&conf->files[i]); + } + list_free(conf->files); + unmap_file(&conf->file); +} + +void print_master_config(config_m conf){ printf( - "CONFIGURATION:\n" + "MASTER CONFIGURATION:\n" + "\t- name: %.*s\n" "\t- port: %d\n" + "\t- backlog: %d\n" + "\t- logs: {\n", + conf.name.len, conf.name.ptr, + conf.port, + conf.backlog + ); + for(int i = 0; i < LOG_LEVEL_COUNT; i++){ + switch(i){ + case LOG_DEBUG: printf("\t\tDEBUG:\t"); break; + case LOG_INFO: printf("\t\tINFO:\t"); break; + case LOG_WARN: printf("\t\tWARN:\t"); break; + case LOG_ERROR: printf("\t\tERROR:\t"); break; + } + printf("%d files%s\n", log_get_files(i), log_get_stderr(i) ? " + stderr" : ""); + } + printf("\t}\n"); +} + +void print_worker_config(config_w conf){ + printf( + "WORKER CONFIGURATION:\n" + "\t- name: %.*s\n" + "\t- root: %.*s\n" + "\t- bundle: %.*s\n" + "\t- cert: %.*s\n" + "\t- key: %.*s\n" "\t- secure: %s\n" "\t- ipv4: %s\n" - "\t- ipv6: %s\n" - "\t- workers: %d\n" - "\t- root: %s\n", - conf.port, + "\t- ipv6: %s\n", + conf.name.len, conf.name.ptr, + conf.root.len, conf.root.ptr, + conf.bundle.len, conf.bundle.ptr, + conf.cert.len, conf.cert.ptr, + conf.key.len, conf.key.ptr, conf.secure ? "yes" : "no", conf.ipv4 ? "yes" : "no", - conf.ipv6 ? "yes" : "no", - conf.workers, - conf.root.ptr + conf.ipv6 ? "yes" : "no" ); - - return conf; + print_mime_types(); + printf("\t- logs: {\n"); + for(int i = 0; i < LOG_LEVEL_COUNT; i++){ + switch(i){ + case LOG_DEBUG: printf("\t\tDEBUG:\t"); break; + case LOG_INFO: printf("\t\tINFO:\t"); break; + case LOG_WARN: printf("\t\tWARN:\t"); break; + case LOG_ERROR: printf("\t\tERROR:\t"); break; + } + printf("%d files%s\n", log_get_files(i), log_get_stderr(i) ? " + stderr" : ""); + } + printf("\t}\n"); } + diff --git a/src/config/config.h b/src/config/config.h index 4f04459..9a8f058 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -3,17 +3,39 @@ #include "str/str.h" #include "log/log.h" +#include "list/list.h" +#include "mime/mime.h" +#include -typedef struct config { + +typedef struct config_m { + str file; + str name; int port; - int secure : 1; - int ipv4 : 1; - int ipv6 : 1; - int workers; + int backlog; +} config_m; + +typedef struct config_w { + str file; + str name; str root; - -} config; + str bundle; + str cert; + str key; + uint secure : 1; + uint ipv4 : 1; + uint ipv6 : 1; + str *files; +} config_w; + + +config_m master_config(char *filename); +config_w worker_config(char *filename); + +void free_master_config(config_m *conf); +void free_worker_config(config_w *conf); -config read_config(str cfg); +void print_master_config(config_m conf); +void print_worker_config(config_w conf); #endif diff --git a/src/mime/mime.c b/src/mime/mime.c new file mode 100644 index 0000000..99250ea --- /dev/null +++ b/src/mime/mime.c @@ -0,0 +1,59 @@ +#include "mime.h" + + +// look into making this a BST or something more efficient +static mime_type *types; + +void add_mime_type(mime_type mt){ + list_push(types, mt); +} + +void read_mime_types(str file){ + if(types == NULL){ + init_nlist(types); + } + int off = 0; + while(off < file.len){ + while(off < file.len && charisspace(file.ptr[off])) off++; + if(file.ptr[off] == '#'){ + while(off < file.len && !charislinebreak(file.ptr[off])) off++; + continue; + } + mime_type mt; + mt.desc = sread_delim_f(file.ptr + off, charisspace, true); + off += mt.desc.len; + while(off < file.len && charisspace(file.ptr[off])) off++; + mt.ext = sread_delim_f(file.ptr + off, charisspace, true); + off += mt.ext.len; + while(off < file.len && !charislinebreak(file.ptr[off])) off++; + if(mt.desc.len == 0 || mt.ext.len == 0){ + continue; + } + add_mime_type(mt); + } +} + +str get_mime_type(str ext){ + int size = list_size(types); + for(int i = 0; i < size; i++){ + if(streq(types[i].ext, ext)){ + return types[i].desc; + } + } + return (str){0}; +} + +void free_mime_types(void){ + list_free(types); +} + +void print_mime_types(void){ + int size = list_size(types); + printf("\t- types: {\n"); + for(int i = 0; i < size; i++){ + printf("\t\t%.*s\t%.*s\n", + types[i].desc.len, types[i].desc.ptr, types[i].ext.len, types[i].ext.ptr); + } + printf("\t}\n"); +} + diff --git a/src/mime/mime.h b/src/mime/mime.h new file mode 100644 index 0000000..dada0a3 --- /dev/null +++ b/src/mime/mime.h @@ -0,0 +1,21 @@ +#ifndef MIME_H +#define MIME_H + +#include "str/str.h" +#include "list/list.h" + + +typedef struct mime_type { + str desc; + str ext; +} mime_type; + + +void add_mime_type(mime_type mt); +void read_mime_types(str file); +str get_mime_type(str ext); +void free_mime_types(void); + +void print_mime_types(void); + +#endif diff --git a/src/net/net.c b/src/net/net.c index e7c5981..3e70cef 100755 --- a/src/net/net.c +++ b/src/net/net.c @@ -12,47 +12,6 @@ str response_headers[] = { sstr("Transfer-Encoding"), }; -struct { - str fmt; - str type; -} mime_types[] = { - {.fmt = sstr("avif"), .type = sstr("image/avif")}, - {.fmt = sstr("bmp"), .type = sstr("image/bmp")}, - {.fmt = sstr("css"), .type = sstr("text/css")}, - {.fmt = sstr("csv"), .type = sstr("text/csv")}, - {.fmt = sstr("eot"), .type = sstr("application/vnd.ms-fontobject")}, - {.fmt = sstr("gz"), .type = sstr("application/gzip")}, - {.fmt = sstr("gif"), .type = sstr("image/gif")}, - {.fmt = sstr("html"), .type = sstr("text/html")}, - {.fmt = sstr("ico"), .type = sstr("image/vnd.microsoft.icon")}, - {.fmt = sstr("jpg"), .type = sstr("image/jpeg")}, - {.fmt = sstr("jpeg"), .type = sstr("image/jpeg")}, - {.fmt = sstr("js"), .type = sstr("text/javascript")}, - {.fmt = sstr("json"), .type = sstr("application/json")}, - {.fmt = sstr("midi"), .type = sstr("audio/midi")}, - {.fmt = sstr("mp3"), .type = sstr("audio/mpeg")}, - {.fmt = sstr("mp4"), .type = sstr("video/mp4")}, - {.fmt = sstr("mpeg"), .type = sstr("video/mpeg")}, - {.fmt = sstr("png"), .type = sstr("image/png")}, - {.fmt = sstr("pdf"), .type = sstr("application/pdf")}, - {.fmt = sstr("php"), .type = sstr("application/x-httpd-php")}, - {.fmt = sstr("rar"), .type = sstr("application/vnd.rar")}, - {.fmt = sstr("svg"), .type = sstr("image/svg+xml")}, - {.fmt = sstr("tiff"), .type = sstr("image/tiff")}, - {.fmt = sstr("ts"), .type = sstr("video/mp2t")}, - {.fmt = sstr("ttf"), .type = sstr("font/ttf")}, - {.fmt = sstr("txt"), .type = sstr("text/plain")}, - {.fmt = sstr("wav"), .type = sstr("audio/wav")}, - {.fmt = sstr("weba"), .type = sstr("audio/webm")}, - {.fmt = sstr("webm"), .type = sstr("video/webm")}, - {.fmt = sstr("webp"), .type = sstr("image/webp")}, - {.fmt = sstr("woff"), .type = sstr("font/woff")}, - {.fmt = sstr("woff2"), .type = sstr("font/woff2")}, - {.fmt = sstr("xml"), .type = sstr("application/xml")}, - {.fmt = sstr("zip"), .type = sstr("application/zip")}, - {.fmt = sstr("7z"), .type = sstr("application/x-7z-compressed")}, -}; - LIST(struct uri_mod) uri_rewrites = NULL; static int pleasesslgivemetheerror(int ssl_get_error){ @@ -597,20 +556,14 @@ void send_file(http_worker *hw, str filename){ } log_info("sending '%.*s'", filename.len, filename.ptr); - enum mime_type type = TXT; - str fmt = dstr(get_extension(filename.ptr)); - for(int i = 0; i < sizeof(mime_types)/sizeof(mime_types[0]); i++){ - if(strncmp(fmt.ptr, mime_types[i].fmt.ptr, fmt.len) == 0){ - type = i; - break; - } - } - free_str(&fmt); + str ext = dsstr(get_extension(filename.ptr)); + str type = get_mime_type(ext); + if(type.len == 0) type = sstr("text/plain"); struct http_message hm = { .resp_ver = sstr("HTTP/1.1"), .status = sstr("200"), .reason = sstr("OK"), .hlen = 2, .headers = { - { .name = response_headers[CONTENT_TYPE], .value = mime_types[type].type }, + { .name = response_headers[CONTENT_TYPE], .value = type }, { .name = response_headers[CONTENT_LENGTH], .value = utostr(fsize, 10) } }, .body = {0}, diff --git a/src/net/net.h b/src/net/net.h index 79886df..dc7c147 100755 --- a/src/net/net.h +++ b/src/net/net.h @@ -19,6 +19,7 @@ #include "list/list.h" #include "files/files.h" #include "log/log.h" +#include "mime/mime.h" #include #include #include @@ -34,16 +35,6 @@ typedef enum http_method { } http_method; - -enum mime_type { - AVIF, BMP, CSS, CSV, GZ, GIF, HTML, - ICO, JPG, JPEG, JS, JSON, MIDI, MP3, - MP4, MPEG, PNG, PDF, PHP, RAR, TIFF, TS, - TXT, WAV, WEBA, WEBM, WEBP, XML, ZIP, - _7Z, -}; - - struct uri { str path; str query; -- cgit v1.2.3