snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

commit a5fb539ee2ccaf8053ee5503c285a47b8cbb6fe8
parent 63ce6eaeeeea10a855bd1695a87a344eb3346692
Author: Santtu Lakkala <inz@inz.fi>
Date:   Tue, 11 Mar 2025 12:46:59 +0200

Add xs_dict_set_strnn helper

Diffstat:
Mhttpd.c | 3+--
Mxs.h | 37+++++++++++++++++++++++++------------
Mxs_fcgi.h | 127+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
3 files changed, 99 insertions(+), 68 deletions(-)

diff --git a/httpd.c b/httpd.c @@ -23,7 +23,6 @@ #include <unistd.h> #include <sys/resource.h> // for getrlimit() - #include <sys/mman.h> #ifdef USE_POLL_FOR_SLEEP @@ -917,7 +916,7 @@ void httpd(void) const char *address = NULL; const char *port = NULL; xs *full_address = NULL; - int rs; + volatile int rs; pthread_t threads[MAX_THREADS] = {0}; int n; xs *sem_name = NULL; diff --git a/xs.h b/xs.h @@ -131,6 +131,7 @@ const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key); #define xs_dict_get_def(dict, key, def) xs_or(xs_dict_get(dict, key), def) xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key); xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data); +xs_dict *xs_dict_set_strnn(xs_dict *dict, const void *key, int klen, const void *val, int vlen); xs_dict *xs_dict_set_fmt(xs_dict *dict, const xs_str *key, const char *fmt, ...); xs_dict *xs_dict_gc(const xs_dict *dict); @@ -1153,10 +1154,10 @@ xs_dict *xs_dict_new(void) return d; } -static int *_xs_dict_locate(const xs_dict *dict, const char *key) +static int *_xs_dict_locate(const xs_dict *dict, const char *key, int ksz) /* locates a ditem */ { - unsigned int h = xs_hash_func(key, strlen(key)); + unsigned int h = xs_hash_func(key, ksz); /* start from the root */ dict_hdr *dh = (dict_hdr *)(dict + 1); @@ -1179,18 +1180,17 @@ static int *_xs_dict_locate(const xs_dict *dict, const char *key) return off; } -xs_dict *_xs_dict_ensure(xs_dict *dict, const xs_str *key, int vsz, int *offset) +xs_dict *_xs_dict_ensure(xs_dict *dict, const xs_str *key, int klen, int vsz, int *offset) { if (xs_type(dict) == XSTYPE_DICT) { - int *o = _xs_dict_locate(dict, key); + int *o = _xs_dict_locate(dict, key, klen); int end = xs_size(dict); if (!*o) { /* ditem does not exist yet: append to the end */ *o = end; - int ksz = xs_size(key); - int dsz = sizeof(ditem_hdr) + ksz + vsz; + int dsz = sizeof(ditem_hdr) + klen + 1 + vsz; /* open room in the dict for the full ditem */ dict = xs_expand(dict, end, dsz); @@ -1202,10 +1202,11 @@ xs_dict *_xs_dict_ensure(xs_dict *dict, const xs_str *key, int vsz, int *offset) memset(di, '\0', dsz); /* set the offset to the value */ - di->value_offset = end + sizeof(ditem_hdr) + ksz; + di->value_offset = end + sizeof(ditem_hdr) + klen + 1; /* copy the key */ - memcpy(di->key, key, ksz); + memcpy(di->key, key, klen); + di->key[klen] = '\0'; /* chain to the sequential list */ if (dh->first == 0) @@ -1267,7 +1268,7 @@ xs_dict *xs_dict_set_fmt(xs_dict *dict, const xs_str *key, const char *fmt, ...) if (vsz++ < 0) return dict; - dict = _xs_dict_ensure(dict, key, vsz, &o); + dict = _xs_dict_ensure(dict, key, strlen(key), vsz, &o); va_start(ap, fmt); vsnprintf(dict + o, vsz, fmt, ap); @@ -1286,13 +1287,25 @@ xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value) if (xs_type(dict) == XSTYPE_DICT) { int vsz = xs_size(value); int o; - dict = _xs_dict_ensure(dict, key, vsz, &o); + dict = _xs_dict_ensure(dict, key, strlen(key), vsz, &o); memcpy(dict + o, value, vsz); } return dict; } +xs_dict *xs_dict_set_strnn(xs_dict *dict, const void *key, int klen, const void *val, int vlen) +/* sets a key/value pair */ +{ + if (xs_type(dict) == XSTYPE_DICT) { + int o; + dict = _xs_dict_ensure(dict, key, klen, vlen + 1, &o); + memcpy(dict + o, val, vlen); + *(dict + o + vlen) = '\0'; + } + + return dict; +} xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) /* just an alias (for this implementation it's the same) */ @@ -1312,7 +1325,7 @@ xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key) /* deletes a key/value pair */ { if (xs_type(dict) == XSTYPE_DICT) { - int *o = _xs_dict_locate(dict, key); + int *o = _xs_dict_locate(dict, key, strlen(key)); if (*o) { /* found ditem */ @@ -1331,7 +1344,7 @@ const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key) /* gets a value by key, or NULL */ { if (xs_type(dict) == XSTYPE_DICT) { - int *o = _xs_dict_locate(dict, key); + int *o = _xs_dict_locate(dict, key, strlen(key)); if (*o) { /* found ditem */ diff --git a/xs_fcgi.h b/xs_fcgi.h @@ -18,6 +18,8 @@ #ifdef XS_IMPLEMENTATION +#include <stdint.h> + struct fcgi_record_header { unsigned char version; unsigned char type; @@ -75,6 +77,42 @@ struct fcgi_end_request { #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 +#define MATCH(a, al, b) \ + ((al) == sizeof("" b) - 1 && !memcmp((a), b, (sizeof(b) - 1))) + +const char *headerify_i(unsigned char *name, int *len) +{ + int i; + + if (MATCH(name, *len, "REQUEST_METHOD")) { + *len = sizeof("method") - 1; + return "method"; + } + if (MATCH(name, *len, "CONTENT_TYPE")) + return "content-type"; + if (MATCH(name, *len, "CONTENT_LENGTH")) + return "content-length"; + if (MATCH(name, *len, "REMOTE_ADDR")) + return "remote-addr"; + if (*len < (int)sizeof("HTTP_") || memcmp(name, "HTTP_", sizeof("HTTP_") - 1)) + return NULL; + + name += sizeof("HTTP_") - 1; + *len -= sizeof("HTTP_") - 1; + + for (i = 0; i < *len; i++) { + switch (name[i]) { + case '_': + name[i] = '-'; + break; + default: + name[i] = tolower(name[i]); + break; + } + } + + return (const char *)name; +} xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id) /* keeps receiving FCGI packets until a complete request is finished */ @@ -152,6 +190,8 @@ xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id) int offset = 0; while (offset < b_size) { unsigned int ksz = buf[offset++]; + const char *key; + int kl; if (ksz & 0x80) { ksz &= 0x7f; @@ -168,43 +208,29 @@ xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id) vsz = (vsz << 8) | buf[offset++]; } - /* get the key */ - xs *k = xs_str_new_sz((char *)&buf[offset], ksz); - offset += ksz; - - /* get the value */ - xs *v = xs_str_new_sz((char *)&buf[offset], vsz); - offset += vsz; - - if (!xs_is_string(k) || !xs_is_string(v)) + if (!xs_is_string((xs_val *)&buf[offset]) || !xs_is_string((xs_val *)&buf[offset + ksz])) continue; - cgi_vars = xs_dict_append(cgi_vars, k, v); - - if (strcmp(k, "REQUEST_METHOD") == 0) - req = xs_dict_append(req, "method", v); - else - if (strcmp(k, "REQUEST_URI") == 0) { - req = xs_dict_append(req, "raw_path", v); + cgi_vars = xs_dict_set_strnn(cgi_vars, &buf[offset], ksz, &buf[offset + ksz], vsz); - xs *pnv = xs_split_n(v, "?", 1); + if (MATCH(&buf[offset], ksz, "REQUEST_URI")) { + const unsigned char *v = &buf[offset + ksz]; + const unsigned char *q = memchr(v, '?', vsz); - /* store the path */ - req = xs_dict_append(req, "path", xs_list_get(pnv, 0)); + req = xs_dict_set_strnn(req, "raw_path", sizeof("raw_path") - 1, v, vsz); + req = xs_dict_set_strnn(req, "path", 4, v, q != NULL ? q - v : vsz); - /* get the variables */ - q_vars = xs_url_vars(xs_list_get(pnv, 1)); + if (q) + q_vars = xs_url_vars(xs_dict_get(req, "raw_path") + (q - v + 1)); } - else - if (xs_match(k, "CONTENT_TYPE|CONTENT_LENGTH|REMOTE_ADDR|HTTP_*")) { - if (xs_startswith(k, "HTTP_")) - k = xs_crop_i(k, 5, 0); - k = xs_tolower_i(k); - k = xs_replace_i(k, "_", "-"); + kl = ksz; + key = headerify_i(&buf[offset], &kl); - req = xs_dict_append(req, k, v); - } + if (key) + req = xs_dict_set_strnn(req, key, kl, &buf[offset + ksz], vsz); + + offset += ksz + vsz; } req = xs_dict_append(req, "cgi_vars", cgi_vars); @@ -296,9 +322,13 @@ end: void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size, int fcgi_id) /* writes an FCGI response */ { - struct fcgi_record_header hdr = {0}; + struct fcgi_record_header hdr = { + .version = FCGI_VERSION_1, + .type = FCGI_STDOUT, + .id = fcgi_id + }; struct fcgi_end_request ereq = {0}; - xs *out = xs_str_new(NULL); + xs_str_bld outb = {0}; const xs_str *k; const xs_str *v; @@ -307,41 +337,30 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b return; /* create the headers */ - { - xs *s1 = xs_fmt("status: %d\r\n", status); - out = xs_str_cat(out, s1); - } + xs_str_bld_cat_fmt(&outb, "status: %d\r\n", status); - xs_dict_foreach(headers, k, v) { - xs *s1 = xs_fmt("%s: %s\r\n", k, v); - out = xs_str_cat(out, s1); - } + xs_dict_foreach(headers, k, v) + xs_str_bld_cat_fmt(&outb, "%s: %s\r\n", k, v); - if (b_size > 0) { - xs *s1 = xs_fmt("content-length: %d\r\n", b_size); - out = xs_str_cat(out, s1); - } + if (b_size > 0) + xs_str_bld_cat_fmt(&outb, "content-length: %d\r\n", b_size); + + xs_str_bld_cat(&outb, "\r\n"); if (body == NULL) b_size = 0; - out = xs_str_cat(out, "\r\n"); - /* everything is text by now */ + xs *out = outb.data; int size = strlen(out); - /* now send all the STDOUT in packets */ - hdr.version = FCGI_VERSION_1; - hdr.type = FCGI_STDOUT; - hdr.id = fcgi_id; - int offset; size_t sz; for (offset = 0; offset < size; offset += sz) { sz = size - offset; - if (sz > 0xffff) - sz = 0xffff; + if (sz > UINT16_MAX) + sz = UINT16_MAX; hdr.content_len = htons(sz); @@ -352,8 +371,8 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b for (offset = 0; offset < b_size; offset += sz) { sz = b_size - offset; - if (sz > 0xffff) - sz = 0xffff; + if (sz > UINT16_MAX) + sz = UINT16_MAX; hdr.content_len = htons(sz);