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:
M | httpd.c | | | 3 | +-- |
M | xs.h | | | 37 | +++++++++++++++++++++++++------------ |
M | xs_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);