snac2

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

commit 63ce6eaeeeea10a855bd1695a87a344eb3346692
parent 309e46917a00d220dea4aff0b19ce38660459fff
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date:   Thu, 27 Feb 2025 17:46:25 +0200

mmappy things

Diffstat:
MMakefile | 2+-
Mdata.c | 28+++++++++++++++++++---------
Mhtml.c | 24+++++++++++++++++-------
Mhttpd.c | 22++++++++++++++++++----
Msnac.h | 7++++---
Mxs_fcgi.h | 29+++++++++++++++++++----------
6 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ .POSIX: PREFIX?=/usr/local PREFIX_MAN=$(PREFIX)/man -CFLAGS=-std=c99 -Os -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 -DWITHOUT_TIMEGM +CFLAGS=-std=c99 -O -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 -DWITHOUT_TIMEGM all: snac diff --git a/data.c b/data.c @@ -21,6 +21,7 @@ #include <sys/stat.h> #include <sys/file.h> #include <sys/time.h> +#include <sys/mman.h> #include <fcntl.h> #include <pthread.h> #include <dirent.h> @@ -193,6 +194,8 @@ void srv_free(void) xs_free(srv_basedir); xs_free(srv_config); xs_free(srv_baseurl); + xs_free(srv_langs); + xs_free(srv_proxy_token_seed); pthread_mutex_destroy(&data_mutex); } @@ -2622,10 +2625,11 @@ void list_distribute(snac *user, const char *who, const xs_dict *post) /** static data **/ static int _load_raw_file(FILE *f, xs_val **data, int *size, - const char *inm, xs_str **etag) + const char *inm, xs_str **etag, int *mmapped) /* loads a cached file */ { int status = HTTP_STATUS_NOT_FOUND; + *mmapped = 0; if (f) { double tm = mtime_f(f); @@ -2640,11 +2644,16 @@ static int _load_raw_file(FILE *f, xs_val **data, int *size, status = HTTP_STATUS_NOT_MODIFIED; } else { + struct stat st; + if (fstat(fileno(f), &st) == 0 && (*data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fileno(f), 0)) != MAP_FAILED) { + *mmapped = 1; + *size = st.st_size; + } + else { + *size = XS_ALL; + *data = xs_read(f, size); + } /* newer or never downloaded; read the full file */ - *size = XS_ALL; - *data = xs_read(f, size); - fclose(f); - status = HTTP_STATUS_OK; } @@ -2652,6 +2661,7 @@ static int _load_raw_file(FILE *f, xs_val **data, int *size, if (etag != NULL) *etag = xs_dup(e); } + fclose(f); } return status; @@ -2731,10 +2741,10 @@ xs_str *_static_fn(snac *snac, const char *id) int static_get(snac *snac, const char *id, xs_val **data, int *size, - const char *inm, xs_str **etag) + const char *inm, xs_str **etag, int *mmapped) /* returns static content */ { - return _load_raw_file(user_open_subfile(snac, "static", id, 0), data, size, inm, etag); + return _load_raw_file(user_open_subfile(snac, "static", id, 0), data, size, inm, etag, mmapped); } int static_get_partial(snac *snac, const char *id, xs_val **data, int *size, @@ -2843,9 +2853,9 @@ void history_add(snac *snac, const char *id, const char *content, int size, int history_get(snac *snac, const char *id, xs_str **content, int *size, - const char *inm, xs_str **etag) + const char *inm, xs_str **etag, int *mmapped) { - return _load_raw_file(user_open_subfile(snac, "history", id, 0), content, size, inm, etag); + return _load_raw_file(user_open_subfile(snac, "history", id, 0), content, size, inm, etag, mmapped); } diff --git a/html.c b/html.c @@ -17,6 +17,7 @@ #include "xs_random.h" #include "snac.h" +#include <sys/mman.h> int login(snac *user, const xs_dict *headers) /* tries a login */ @@ -760,6 +761,7 @@ static xs_html *html_instance_body(void) xs_html *html_user_head(snac *user, const char *desc, const char *url) { xs_html *head = html_base_head(); + int mmapped = 0; /* add the user CSS */ { @@ -767,7 +769,7 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) int size; /* try to open the user css */ - if (!valid_status(static_get(user, "style.css", &css, &size, NULL, NULL))) { + if (!valid_status(static_get(user, "style.css", &css, &size, NULL, NULL, &mmapped))) { /* it's not there; try to open the server-wide css */ FILE *f; xs *g_css_fn = xs_fmt("%s/style.css", srv_basedir); @@ -783,6 +785,11 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) xs_html_tag("style", xs_html_raw(css))); } + + if (mmapped) { + munmap(css, size); + css = NULL; + } } /* title */ @@ -3601,7 +3608,7 @@ void set_user_lang(snac *user) int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag, xs_str **last_modified, - xs_dict **headers) + xs_dict **headers, int *mmapped) { const char *accept = xs_dict_get(req, "accept"); int status = HTTP_STATUS_NOT_FOUND; @@ -3616,6 +3623,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, const xs_dict *q_vars = xs_dict_get(req, "q_vars"); + *mmapped = 0; + xs *l = xs_split_n(q_path, "/", 2); v = xs_list_get(l, 1); @@ -3732,7 +3741,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, snac_debug(&snac, 1, xs_fmt("serving cached local timeline")); status = history_get(&snac, h, body, b_size, - xs_dict_get(req, "if-none-match"), etag); + xs_dict_get(req, "if-none-match"), etag, mmapped); } else { xs *list = NULL; @@ -3744,7 +3753,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, list = timeline_list(&snac, "public", skip, show, &more); xs *pins = pinned_list(&snac); - pins = xs_list_cat(pins, list); + if (xs_is_list(list)) + pins = xs_list_cat(pins, list); *body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error); @@ -3912,7 +3922,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, snac_debug(&snac, 1, xs_fmt("serving cached timeline")); status = history_get(&snac, "timeline.html_", body, b_size, - xs_dict_get(req, "if-none-match"), etag); + xs_dict_get(req, "if-none-match"), etag, mmapped); } else { int more = 0; @@ -4127,7 +4137,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else { status = static_get(&snac, id, body, &sz, - xs_dict_get(req, "if-none-match"), etag); + xs_dict_get(req, "if-none-match"), etag, mmapped); } if (valid_status(status) && status != HTTP_STATUS_PARTIAL_CONTENT) { @@ -4155,7 +4165,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } else status = history_get(&snac, id, body, b_size, - xs_dict_get(req, "if-none-match"), etag); + xs_dict_get(req, "if-none-match"), etag, mmapped); } } else diff --git a/httpd.c b/httpd.c @@ -392,6 +392,7 @@ void httpd_connection(FILE *f) int p_size = 0; const char *p; int fcgi_id; + int mmapped = 0; if (p_state->use_fcgi) req = xs_fcgi_request(f, &payload, &p_size, &fcgi_id); @@ -440,7 +441,7 @@ void httpd_connection(FILE *f) #endif /* NO_MASTODON_API */ if (status == 0) - status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag, &last_modified, &headers); + status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag, &last_modified, &headers, &mmapped); } else if (strcmp(method, "POST") == 0) { @@ -552,8 +553,15 @@ void httpd_connection(FILE *f) b_size = strlen(body); /* if it was a HEAD, no body will be sent */ - if (strcmp(method, "HEAD") == 0) - body = xs_free(body); + if (strcmp(method, "HEAD") == 0) { + if (mmapped) { + munmap(body, b_size); + body = NULL; + mmapped = 0; + } + else + body = xs_free(body); + } headers = xs_dict_append(headers, "access-control-allow-origin", "*"); headers = xs_dict_append(headers, "access-control-allow-headers", "*"); @@ -581,7 +589,13 @@ void httpd_connection(FILE *f) } } - xs_free(body); + if (mmapped) { + munmap(body, b_size); + body = NULL; + mmapped = 0; + } + else + body = xs_free(body); } diff --git a/snac.h b/snac.h @@ -244,7 +244,7 @@ int actor_add(const char *actor, const xs_dict *msg); int actor_get(const char *actor, xs_dict **data); int actor_get_refresh(snac *user, const char *actor, xs_dict **data); -int static_get(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag); +int static_get(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag, int *mmapped); int static_get_partial(snac *snac, const char *id, xs_val **data, int *size, const char *inm, xs_str **etag, int start, int *end); void static_put(snac *snac, const char *id, const char *data, int size); void static_put_meta(snac *snac, const char *id, const char *str); @@ -254,7 +254,7 @@ double history_mtime(snac *snac, const char *id); void history_add(snac *snac, const char *id, const char *content, int size, xs_str **etag); int history_get(snac *snac, const char *id, xs_str **content, int *size, - const char *inm, xs_str **etag); + const char *inm, xs_str **etag, int *mmapped); int history_del(snac *snac, const char *id); xs_list *history_list(snac *snac); @@ -401,7 +401,8 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, int html_get_handler(const xs_dict *req, const char *q_path, char **body, int *b_size, char **ctype, xs_str **etag, xs_str **last_modified, - xs_dict **headers); + xs_dict **headers, + int *mmapped); int html_post_handler(const xs_dict *req, const char *q_path, char *payload, int p_size, diff --git a/xs_fcgi.h b/xs_fcgi.h @@ -322,26 +322,24 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b out = xs_str_cat(out, s1); } + if (body == NULL) + b_size = 0; + out = xs_str_cat(out, "\r\n"); /* everything is text by now */ int size = strlen(out); - /* add the body */ - if (body != NULL && b_size > 0) { - out = xs_append_m(out, body, b_size); - size += b_size; - } - /* now send all the STDOUT in packets */ hdr.version = FCGI_VERSION_1; hdr.type = FCGI_STDOUT; hdr.id = fcgi_id; - int offset = 0; + int offset; + size_t sz; - while (offset < size) { - size_t sz = size - offset; + for (offset = 0; offset < size; offset += sz) { + sz = size - offset; if (sz > 0xffff) sz = 0xffff; @@ -350,10 +348,21 @@ void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b /* write or fail */ if (!fwrite(&hdr, sizeof(hdr), 1, f) || fwrite(out + offset, 1, sz, f) != sz) return; + } + + for (offset = 0; offset < b_size; offset += sz) { + sz = b_size - offset; + if (sz > 0xffff) + sz = 0xffff; + + hdr.content_len = htons(sz); - offset += sz; + /* write or fail */ + if (!fwrite(&hdr, sizeof(hdr), 1, f) || fwrite(body + offset, 1, sz, f) != sz) + return; } + /* final STDOUT packet with 0 size */ hdr.content_len = 0; if (!fwrite(&hdr, sizeof(hdr), 1, f))