Compare commits

...

13 Commits

30 changed files with 2003 additions and 1119 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ build
gpp1
watcher
commit.h
compile_flags.txt
bundle.zip

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "md5-c"]
path = md5-c
url = https://github.com/Zunawe/md5-c.git
[submodule "zip"]
path = zip
url = https://github.com/kuba--/zip.git

View File

@ -3,3 +3,6 @@
./gpp1
./watcher
./mongoose.o
./bundle.zip
./compile_flags.txt
./commit.h

View File

@ -7,4 +7,6 @@
# define CONFIG_LISTEN_URL "http://localhost:5000"
#endif
#define BUNDLE_ZIP_COMPRESSION 10
#endif // CONFIG_H_

90
baked.c
View File

@ -3,44 +3,16 @@
#include "gebs/gebs.h"
#include "incbin/incbin.h"
#include "stb/stb_ds.h"
#include "zip.h"
#include "baked.h"
#include "locked.h"
#include "CONFIG.h"
INCBIN(gpp1, "./gpp1");
INCBIN(home_html, "./tmpls/home.html");
INCBIN(page_missing_html, "./tmpls/page-missing.html");
INCBIN(template_blog_html, "./tmpls/template-blog.html");
INCBIN(blog_html, "./tmpls/blog.html");
INCBIN(simple_css, "./etc/simple.css");
INCBIN(favicon_ico, "./etc/favicon.ico");
#if MY_DEBUG
INCBIN(hotreload_js, "./etc/hotreload.js");
#endif
INCBIN(theme_js, "./etc/theme.js");
INCBIN(me_jpg, "./etc/me.jpg");
INCBIN(tmoa_engine_jpg, "./etc/tmoa-engine.jpg");
INCBIN(tmoa_garbage_jpg, "./etc/tmoa-garbage.jpg");
INCBIN(blog_welcome_md, "./blog/welcome.md");
INCBIN(blog_weird_page_md, "./blog/weird-page.md");
INCBIN(blog_curious_case_of_gebs_md, "./blog/curious-case-of-gebs.md");
INCBIN(blog_the_making_of_aboba_md, "./blog/the-making-of-aboba.md");
INCBIN(bundle_zip, "./bundle.zip");
static locked(Baked_Resource *) baked_resources = locked_init(nil);
void lock_baked_resources(void)
{
lockx(&baked_resources);
}
void unlock_baked_resources(void)
{
unlockx(&baked_resources);
}
void add_baked_resource(char *key, const uchar *data, size_t size)
{
int fd = memfd_create(key, 0);
@ -49,51 +21,55 @@ void add_baked_resource(char *key, const uchar *data, size_t size)
abort();
}
write(fd, data, size);
shput(baked_resources.value, key, fd);
shput(baked_resources.value, key, ((Baked_Resource_Value){ .memfd = fd, .bufptr = (void *)data }));
LOGI("Added baked resource: %s\n", key);
}
void init_baked_resources(void)
{
lockx(&baked_resources);
add_baked_resource("home.html", home_html_data, home_html_size);
add_baked_resource("page-missing.html", page_missing_html_data, page_missing_html_size);
add_baked_resource("template-blog.html", template_blog_html_data, template_blog_html_size);
add_baked_resource("blog.html", blog_html_data, blog_html_size);
add_baked_resource("gpp1", gpp1_data, gpp1_size);
add_baked_resource("simple.css", simple_css_data, simple_css_size);
add_baked_resource("favicon.ico", favicon_ico_data, favicon_ico_size);
#if MY_DEBUG
add_baked_resource("hotreload.js", hotreload_js_data, hotreload_js_size);
#endif
add_baked_resource("theme.js", theme_js_data, theme_js_size);
add_baked_resource("me.jpg", me_jpg_data, me_jpg_size);
add_baked_resource("tmoa-engine.jpg", tmoa_engine_jpg_data, tmoa_engine_jpg_size);
add_baked_resource("tmoa-garbage.jpg", tmoa_garbage_jpg_data, tmoa_garbage_jpg_size);
add_baked_resource("blog-welcome.md", blog_welcome_md_data, blog_welcome_md_size);
add_baked_resource("blog-weird-page.md", blog_weird_page_md_data, blog_weird_page_md_size);
add_baked_resource("blog-curious-case-of-gebs.md", blog_curious_case_of_gebs_md_data, blog_curious_case_of_gebs_md_size);
add_baked_resource("blog-the-making-of-aboba.md", blog_the_making_of_aboba_md_data, blog_the_making_of_aboba_md_size);
unlockx(&baked_resources);
LOGI("Initializing baked resources...\n");
struct zip_t *zip = zip_stream_open(bundle_zip_data, bundle_zip_size, BUNDLE_ZIP_COMPRESSION, 'r');
size_t n = zip_entries_total(zip);
for (size_t i = 0; i < n; i++) {
zip_entry_openbyindex(zip, i);
const char *name = strdup(zip_entry_name(zip));
size_t size = zip_entry_size(zip);
char *buf = malloc(size);
zip_entry_noallocread(zip, buf, size);
add_baked_resource((char *)name, buf, size);
zip_entry_close(zip);
}
zip_stream_close(zip);
LOGI("baked resources done\n");
}
void free_baked_resources(void)
{
lockx(&baked_resources);
LOGI("freeing baked resouces\n");
for (size_t i = 0; i < shlen(baked_resources.value); i++) {
close(baked_resources.value[i].value);
close(baked_resources.value[i].value.memfd);
free(baked_resources.value[i].key);
free(baked_resources.value[i].value.bufptr);
}
shfree(baked_resources.value);
unlockx(&baked_resources);
LOGI("baked resources done\n");
}
bool get_baked_resource_path(char *key, char *buf, size_t size)
{
LOGI("Request for baked resource path: %s\n", key);
lockx(&baked_resources);
if (shgeti(baked_resources.value, key) != -1) {
int fd = shget(baked_resources.value, key);
snprintf(buf, size, "/proc/%d/fd/%d", getpid(), fd);
Baked_Resource_Value brv = shget(baked_resources.value, key);
snprintf(buf, size, "/proc/%d/fd/%d", getpid(), brv.memfd);
LOGI("Memfd path %s\n", buf);
unlockx(&baked_resources);
return true;
}
unlockx(&baked_resources);
return false;
}

31
baked.h
View File

@ -1,40 +1,19 @@
#ifndef BAKED_H_
#define BAKED_H_
#include "incbin/incbin.h"
INCBIN_EXTERN(gpp1);
INCBIN_EXTERN(home_html);
INCBIN_EXTERN(page_missing_html);
INCBIN_EXTERN(template_blog_html);
INCBIN_EXTERN(blog_html);
INCBIN_EXTERN(simple_css);
INCBIN_EXTERN(favicon_ico);
#if MY_DEBUG
INCBIN_EXTERN(hotreload_js);
#endif
INCBIN_EXTERN(theme_js);
INCBIN_EXTERN(me_jpg);
INCBIN_EXTERN(tmoa_engine_jpg);
INCBIN_EXTERN(tmoa_garbage_jpg);
INCBIN_EXTERN(blog_welcome_md);
INCBIN_EXTERN(blog_weird_page_md);
INCBIN_EXTERN(blog_curious_case_of_gebs_md);
INCBIN_EXTERN(blog_the_making_of_aboba_md);
typedef struct {
int memfd;
void *bufptr;
} Baked_Resource_Value;
typedef struct {
char *key; // path
int value; // memfd
Baked_Resource_Value value;
} Baked_Resource;
void init_baked_resources(void);
void free_baked_resources(void);
bool get_baked_resource_path(char *key, char *buf, size_t size);
void baked_resource_each(void (*f)(Baked_Resource *resource, void *udata), void *udata);
void lock_baked_resources(void);
void unlock_baked_resources(void);
#endif // BAKED_H_

View File

@ -0,0 +1,273 @@
# Asset packing with [zip.c](https://raw.githubusercontent.com/kuba--/zip/refs/heads/master/src/zip.c)
One of the technical decisions that I had to make when writing this website is the way that I'm going to
distribute the assets (images, GPP, page templates, etc.). I've decided to go with packing or baking-in
the assets into the main binary, so that I don't have to worry about updating an asset folder alongside
the application.
One flaw of this approach is that as the website gets bigger, the binary will too. Fortunately I don't
have to worry about this too much right now, since I've reimplemented asset packing with the excellent
zip library by \`kuba--\` (Polska GUROM!!!111!!). In this article I'd like to demonstrate how I've
minimised the size of our assets using \`zip\` and \`incbin\`.
![zip file](/bakedres/apwz-zip-icon.png)
## References
- zip: https://raw.githubusercontent.com/kuba--/zip/refs/heads/master/src/zip.c
- incbin: https://github.com/graphitemaster/incbin
- zip format: https://en.wikipedia.org/wiki/ZIP_(file_format)
- the source code: https://git.kamkow1lair.pl/kamkow1/aboba
## Generating the bundle - \`bundle.zip\`
To compress our assets, first we need to generate a bundle file. I've decided to call it bundle.zip - simple
and descriptive.
Generating the bundle requires some changes to our build program. See https://git.kamkow1lair.pl/kamkow1/aboba/src/branch/master/build.c for reference.
\`\`\`
#define BUNDLED_FILES \\
"./gpp1", \\
"./tmpls/home.html", \\
"./tmpls/page-missing.html", \\
"./tmpls/template-blog.html", \\
"./tmpls/blog.html", \\
"./etc/hotreload.js", \\
"./etc/theme.js", \\
"./etc/simple.css", \\
"./etc/highlight.js", \\
"./etc/hljs-rainbow.css", \\
"./etc/marked.js", \\
"./etc/favicon.ico", \\
"./etc/me.jpg", \\
"./etc/tmoa-engine.jpg", \\
"./etc/tmoa-garbage.jpg", \\
"./blog/blog-welcome.md", \\
"./blog/blog-weird-page.md", \\
"./blog/blog-curious-case-of-gebs.md", \\
"./blog/blog-the-making-of-aboba.md", \\
"./blog/blog-asset-packing-with-zip.c.md"
const char *bundle_zip_deps[] = { BUNDLED_FILES };
RULE_ARRAY("./bundle.zip", bundle_zip_deps) {
RULE("./gpp1", "./gpp/gpp.c") {
CMD("cc", "-DHAVE_STRDUP", "-DHAVE_FNMATCH_H", "-o", "gpp1", "gpp/gpp.c");
}
struct zip_t *zip = zip_open("./bundle.zip", BUNDLE_ZIP_COMPRESSION, 'w');
defer { zip_close(zip); }
for (size_t i = 0; i < sizeof(bundle_zip_deps)/sizeof(bundle_zip_deps[0]); i++) {
char *copy = strdup(bundle_zip_deps[i]);
defer { free(copy); }
char *name = basename(copy);
String_Builder sb = {0};
defer { sb_free(&sb); }
sb_read_file(&sb, bundle_zip_deps[i]);
zip_entry_open(zip, name);
zip_entry_write(zip, sb.items, sb.count);
zip_entry_close(zip);
}
LOGI("Generated bundle.zip\\n");
}
RULE("./aboba",
"./main.c",
"./routes.c",
"./routes.h",
"./baked.c",
"./baked.h",
"./commit.h",
"./timer.c",
"./timer.h",
"./CONFIG.h",
"./locked.h",
"./mongoose.o",
"./bundle.zip",
BUNDLED_FILES
) {
// build mongoose.o - skipped
// Generate commit.h - skipped
#define CC "cc"
#define TARGET "-o", "aboba"
#if MY_DEBUG
#define CFLAGS "-fsanitize=address", "-fPIC", "-ggdb"
#define DEFINES "-DMY_DEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", "-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", \\
"-DGEBS_ENABLE_PTHREAD_FEATURES"
#define EXTRA_SOURCES "./cJSON/cJSON.c", "./zip/src/zip.c", "./md5-c/md5.c"
#else
#define CFLAGS "-fPIC"
#define DEFINES "-DMY_DEBUG=0", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", "-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", \\
"-DGEBS_ENABLE_PTHREAD_FEATURES"
#define EXTRA_SOURCES "./cJSON/cJSON.c", "./zip/src/zip.c"
#endif
#define SOURCES "./main.c", "./routes.c", "./baked.c", "./timer.c"
#define OBJECTS "./mongoose.o"
#define LINK_FLAGS "-Wl,-z,execstack", "-lpthread"
#define INC_FLAGS "-I.", "-I./zip/src"
CMD(CC, TARGET, CFLAGS, DEFINES, INC_FLAGS, SOURCES, OBJECTS, EXTRA_SOURCES, LINK_FLAGS);
// generate compile_flags.txt - skipped
// #undef macros - skipped
}
\`\`\`
If you go through the commit history, you'll see that apart from just generating the bundle file, I've also cleaned up the
build commands a bit with \`#define\`s. Let's take a closer look at the bundle generation code.
\`\`\`
const char *bundle_zip_deps[] = { BUNDLED_FILES };
RULE_ARRAY("./bundle.zip", bundle_zip_deps) {
RULE("./gpp1", "./gpp/gpp.c") {
CMD("cc", "-DHAVE_STRDUP", "-DHAVE_FNMATCH_H", "-o", "gpp1", "gpp/gpp.c");
}
struct zip_t *zip = zip_open("./bundle.zip", BUNDLE_ZIP_COMPRESSION, 'w');
defer { zip_close(zip); }
for (size_t i = 0; i < sizeof(bundle_zip_deps)/sizeof(bundle_zip_deps[0]); i++) {
char *copy = strdup(bundle_zip_deps[i]);
defer { free(copy); }
char *name = basename(copy);
String_Builder sb = {0};
defer { sb_free(&sb); }
sb_read_file(&sb, bundle_zip_deps[i]);
zip_entry_open(zip, name);
zip_entry_write(zip, sb.items, sb.count);
zip_entry_close(zip);
}
LOGI("Generated bundle.zip\\n");
}
\`\`\`
We declare a dependency (using \`RULE\*()\` macro), which says that \`./bundle.zip\` depends on files defined by \`BUNDLED_FILES\`.
To generate the bundle we use \`zip_open()\` \`zip_close()\`. To call \`zip_open()\`, we have to provide a so called "compression level".
The zip library provides us only with \`ZIP_DEFAULT_COMPRESSION_LEVEL\`, which is a macro that evaluates to 6. I wasn't satisfied with
it, so after looking at \`miniz.h\` (a backing library that zip uses), I've found that zip uses \`MZ_DEFAULT_COMPRESSION_LEVEL\`, which
is 6, but we can use the value of \`MZ_UBER_COMPRESSION\`, which is 10. This way we can achieve the most size-efficient compression.
I've decided to \`#define\` the compression level to avoid using arbitrary magic numbers and we can also use that definition both in
the application and in the build program. Here's how \`CONFIG.h\` looks like now:
\`\`\`
#ifndef CONFIG_H_
#define CONFIG_H_
#if MY_DEBUG
# define CONFIG_LISTEN_URL "http://localhost:8080"
#else
# define CONFIG_LISTEN_URL "http://localhost:5000"
#endif
#define BUNDLE_ZIP_COMPRESSION 10
#endif // CONFIG_H_
\`\`\`
The only "downside" here is that, since we're compressing so hard, it's going to take more time to generate the bundle. I've put "downside"
in quotes for purpose, because this does not apply in our case. The files that we're packing are quite small already and there aren't
many of them. We're just doing this to sqeeze out *extra* spacial performance.
Previously we were baking-in the assets like so:
from https://git.kamkow1lair.pl/kamkow1/aboba/src/commit/447362c74dda85838d37d6ee3cb218697abf6106/baked.c
\`\`\`
INCBIN(gpp1, "./gpp1");
INCBIN(home_html, "./tmpls/home.html");
INCBIN(page_missing_html, "./tmpls/page-missing.html");
INCBIN(template_blog_html, "./tmpls/template-blog.html");
INCBIN(blog_html, "./tmpls/blog.html");
INCBIN(simple_css, "./etc/simple.css");
INCBIN(favicon_ico, "./etc/favicon.ico");
#if MY_DEBUG
INCBIN(hotreload_js, "./etc/hotreload.js");
#endif
INCBIN(theme_js, "./etc/theme.js");
INCBIN(highlight_js, "./etc/highlight.js");
INCBIN(hljs_rainbow_css, "./etc/hljs-rainbow.css");
INCBIN(marked_js, "./etc/marked.js");
INCBIN(me_jpg, "./etc/me.jpg");
INCBIN(tmoa_engine_jpg, "./etc/tmoa-engine.jpg");
INCBIN(tmoa_garbage_jpg, "./etc/tmoa-garbage.jpg");
INCBIN(blog_welcome_md, "./blog/welcome.md");
INCBIN(blog_weird_page_md, "./blog/weird-page.md");
INCBIN(blog_curious_case_of_gebs_md, "./blog/curious-case-of-gebs.md");
INCBIN(blog_the_making_of_aboba_md, "./blog/the-making-of-aboba.md");
\`\`\`
Now that we have our \`bundle.zip\`, we do it like this:
\`\`\`
INCBIN(bundle_zip, "./bundle.zip");
\`\`\`
And there we go, we have our bundle!
I've also had to slightly change the way we add the assets to the resource hash table:
\`\`\`
void add_baked_resource(char *key, const uchar *data, size_t size)
{
int fd = memfd_create(key, 0);
if (fd < 0) {
LOGE("Could not create resource %s. Aborting...\n", key);
abort();
}
write(fd, data, size);
shput(baked_resources.value, key, ((Baked_Resource_Value){ .memfd = fd, .bufptr = (void *)data }));
}
void init_baked_resources(void)
{
struct zip_t *zip = zip_stream_open(bundle_zip_data, bundle_zip_size, BUNDLE_ZIP_COMPRESSION, 'r');
size_t n = zip_entries_total(zip);
for (size_t i = 0; i < n; i++) {
zip_entry_openbyindex(zip, i);
const char *name = strdup(zip_entry_name(zip));
size_t size = zip_entry_size(zip);
char *buf = malloc(size);
zip_entry_noallocread(zip, buf, size);
add_baked_resource((char *)name, buf, size);
zip_entry_close(zip);
}
zip_stream_close(zip);
}
\`\`\`
\`add_baked_resource()\` hasn't changed here, but \`init_baked_resources()\` has. Here we use one of zip's abilities,
which is unpacking an in-memory .zip file. We iterate each entry in the bundle, get the name and size, preallocate
a buffer and read said entry into the buffer. We can then add the resource to the hash table as we did previously.
## Conclusion
![xkcd](/bakedres/apwz-xkcd1.png)
One question you may ask is, how much space are we saving? I don't remeber the exact sizes of the binary, but
I remember that before compression it was \`~1.2M\` and now after compression is implemented, it's \`~1.6M\`. How is
that an improvement if the binary gained weight? That \`~.4M\` is likely due to zip format overhead - metadata, file and
directory headers and so on. What we gain here is that the binary hasn't changed in size much despite adding more files,
like for eg. this article that I'm writing right now. It has stayed at \`~1.6M\` so far and the size doesn't go up.
*(So far)* We aren't even making byte-sized gains, which can be checked with \`stat --printf "%s" ./aboba\`. What
we're gaining here is slowed down size increase. Previously if I wanted to add a 30K jpeg, the binary would literally
go up by 30K.

View File

@ -5,7 +5,7 @@ how the code is architectured and some cool tricks that are used throughout the
## Our "engine"
![the engine](/etc/tmoa-engine.jpg)
![the engine](/bakedres/tmoa-engine.jpg)
This image is a joke, obviously.
@ -133,7 +133,7 @@ void *route_thread_function(void *param)
// Unparsable HTTP request
}
if (mg_match(http_msg.uri, mg_str("/etc/*"), nil)) {
if (mg_match(http_msg.uri, mg_str("/bakedres/*"), nil)) {
// Request for static resource
}
@ -147,7 +147,7 @@ this functionality comes with mongoose built-in. God bless you mongoose!
## Dynamic pages and static assets.
![the assets](/etc/tmoa-garbage.jpg)
![the assets](/bakedres/tmoa-garbage.jpg)
Let's stop here now and talk about something different entirely (dont' worry, we'll come back later).
I'd like to show you how assets/pages are implemented in aboba, so we get the whole picture.

120
build.c
View File

@ -1,6 +1,9 @@
#include <libgen.h>
#define GEBS_NO_PREFIX
#define GEBS_IMPLEMENTATION
#include "gebs/gebs.h"
#include "./zip/src/zip.c"
#include "CONFIG.h"
char *prog = NULL;
@ -15,13 +18,62 @@ int main(int argc, char ** argv)
prog = SHIFT(&argc, &argv);
char *cmd = SHIFT(&argc, &argv);
if (strcmp(cmd, "make") == 0) {
#define BUNDLED_FILES \
"./gpp1", \
"./tmpls/home.html", \
"./tmpls/page-missing.html", \
"./tmpls/template-blog.html", \
"./tmpls/blog.html", \
"./etc/hotreload.js", \
"./etc/theme.js", \
"./etc/simple.css", \
"./etc/highlight.js", \
"./etc/hljs-rainbow.css", \
"./etc/marked.js", \
"./etc/favicon.ico", \
"./etc/me.jpg", \
"./etc/tmoa-engine.jpg", \
"./etc/tmoa-garbage.jpg", \
"./etc/apwz-zip-icon.png", \
"./etc/apwz-xkcd1.png", \
"./blog/blog-welcome.md", \
"./blog/blog-weird-page.md", \
"./blog/blog-curious-case-of-gebs.md", \
"./blog/blog-the-making-of-aboba.md", \
"./blog/blog-asset-packing-with-zip.c.md"
const char *bundle_zip_deps[] = { BUNDLED_FILES };
RULE_ARRAY("./bundle.zip", bundle_zip_deps) {
RULE("./gpp1", "./gpp/gpp.c") {
CMD("cc", "-DHAVE_STRDUP", "-DHAVE_FNMATCH_H", "-o", "gpp1", "gpp/gpp.c");
}
struct zip_t *zip = zip_open("./bundle.zip", BUNDLE_ZIP_COMPRESSION, 'w');
defer { zip_close(zip); }
for (size_t i = 0; i < sizeof(bundle_zip_deps)/sizeof(bundle_zip_deps[0]); i++) {
char *copy = strdup(bundle_zip_deps[i]);
defer { free(copy); }
char *name = basename(copy);
String_Builder sb = {0};
defer { sb_free(&sb); }
sb_read_file(&sb, bundle_zip_deps[i]);
zip_entry_open(zip, name);
zip_entry_write(zip, sb.items, sb.count);
zip_entry_close(zip);
}
LOGI("Generated bundle.zip\n");
}
RULE("./aboba",
"./main.c",
"./routes.c",
"./routes.h",
"./baked.c",
"./baked.h",
"./clonestr.h",
"./commit.h",
"./timer.c",
"./timer.h",
@ -29,25 +81,9 @@ int main(int argc, char ** argv)
"./locked.h",
"./mongoose.o",
"./gpp1",
"./tmpls/home.html",
"./tmpls/page-missing.html",
"./tmpls/template-blog.html",
"./tmpls/blog.html",
"./etc/hotreload.js",
"./etc/theme.js",
"./etc/simple.css",
"./etc/favicon.ico",
"./etc/me.jpg",
"./etc/tmoa-engine.jpg",
"./etc/tmoa-garbage.jpg",
"./blog/welcome.md",
"./blog/weird-page.md",
"./blog/curious-case-of-gebs.md",
"./blog/the-making-of-aboba.md"
"./bundle.zip",
BUNDLED_FILES
) {
RULE("./mongoose.o", "./mongoose/mongoose.c") {
@ -60,10 +96,6 @@ int main(int argc, char ** argv)
#endif
}
RULE("./gpp1", "./gpp/gpp.c") {
CMD("cc", "-DHAVE_STRDUP", "-DHAVE_FNMATCH_H", "-o", "gpp1", "gpp/gpp.c");
}
RULE("./commit.h") {
String_Builder commit = {0};
defer { sb_free(&commit); }
@ -92,18 +124,39 @@ int main(int argc, char ** argv)
}
}
#define CC "cc"
#define TARGET "-o", "aboba"
#if MY_DEBUG
CMD("cc", "-fsanitize=address", "-fPIC", "-ggdb", "-I.", "-DMY_DEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX",
"-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", "-Wl,-z,execstack", "-o", "./aboba",
"./main.c", "./routes.c", "./baked.c", "./timer.c", "./mongoose.o", "./cJSON/cJSON.c",
"./md5-c/md5.c",
"-lpthread");
#define CFLAGS "-fsanitize=address", "-fPIC", "-ggdb"
#define DEFINES "-DMY_DEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", "-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", \
"-DGEBS_ENABLE_PTHREAD_FEATURES"
#define EXTRA_SOURCES "./cJSON/cJSON.c", "./zip/src/zip.c", "./md5-c/md5.c"
#else
CMD("cc", "-fPIC", "-I.", "-DMY_DEBUG=0", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX",
"-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", "-Wl,-z,execstack", "-o", "./aboba",
"./main.c", "./routes.c", "./baked.c", "./timer.c", "./mongoose.o", "./cJSON/cJSON.c",
"-lpthread");
#define CFLAGS "-fPIC"
#define DEFINES "-DMY_DEBUG=0", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", "-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", \
"-DGEBS_ENABLE_PTHREAD_FEATURES"
#define EXTRA_SOURCES "./cJSON/cJSON.c", "./zip/src/zip.c"
#endif
#define SOURCES "./main.c", "./routes.c", "./baked.c", "./timer.c"
#define OBJECTS "./mongoose.o"
#define LINK_FLAGS "-Wl,-z,execstack", "-lpthread"
#define INC_FLAGS "-I.", "-I./zip/src"
CMD(CC, TARGET, CFLAGS, DEFINES, INC_FLAGS, SOURCES, OBJECTS, EXTRA_SOURCES, LINK_FLAGS);
RULE("./compile_flags.txt") {
make_compile_flags(TARGET, CFLAGS, DEFINES, INC_FLAGS, SOURCES, OBJECTS, EXTRA_SOURCES, LINK_FLAGS);
}
#undef CC
#undef TARGET
#undef CFLAGS
#undef DEFINES
#undef EXTRA_SOURCES
#undef SOURCES
#undef OBJECTS
#undef LINK_FLAGS
#undef INC_FLAGS
}
} else if (strcmp(cmd, "clean") == 0) {
remove1("./build");
@ -111,6 +164,9 @@ int main(int argc, char ** argv)
remove1("./mongoose.o");
remove1("./gpp1");
remove1("./watcher");
remove1("./compile_flags.txt");
remove1("./bundle.zip");
remove1("./commit.h");
} else if (strcmp(cmd, "make-watcher") == 0) {
CMD("cc", "-o", "watcher", "watcher.c");
} else if (strcmp(cmd, "watch") == 0) {

View File

@ -1,11 +1,5 @@
#ifndef CLONESTR_H_
#define CLONESTR_H_
#define clonestr(str) \
({ \
char *__p = malloc(strlen((str))+1); \
strcpy(__p, (str)); \
(__p); \
})
#endif // CLONESTR_H_

BIN
etc/apwz-xkcd1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
etc/apwz-zip-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

1244
etc/highlight.js Normal file

File diff suppressed because one or more lines are too long

1
etc/hljs-rainbow.css Normal file
View File

@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#474949;color:#d1d9e1}.hljs-comment,.hljs-quote{color:#969896;font-style:italic}.hljs-addition,.hljs-keyword,.hljs-literal,.hljs-selector-tag,.hljs-type{color:#c9c}.hljs-number,.hljs-selector-attr,.hljs-selector-pseudo{color:#f99157}.hljs-doctag,.hljs-regexp,.hljs-string{color:#8abeb7}.hljs-built_in,.hljs-name,.hljs-section,.hljs-title{color:#b5bd68}.hljs-class .hljs-title,.hljs-selector-id,.hljs-template-variable,.hljs-title.class_,.hljs-variable{color:#fc6}.hljs-name,.hljs-section,.hljs-strong{font-weight:700}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-subst,.hljs-symbol{color:#f99157}.hljs-deletion{color:#dc322f}.hljs-formula{background:#eee8d5}.hljs-attr,.hljs-attribute{color:#81a2be}.hljs-emphasis{font-style:italic}

69
etc/marked.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -82,12 +82,29 @@ function neon_cyber()
document.documentElement.style.setProperty("--disabled", "#333333");
}
function dark_easy()
{
document.documentElement.style.setProperty("--bg", "#1A1A1A");
document.documentElement.style.setProperty("--accent-bg", "#2E2E2E");
document.documentElement.style.setProperty("--text", "#DCDCDC");
document.documentElement.style.setProperty("--text-light", "#A3A3A3");
document.documentElement.style.setProperty("--border", "#585858");
document.documentElement.style.setProperty("--accent", "#FF5722");
document.documentElement.style.setProperty("--accent-hover", "#F4511E");
document.documentElement.style.setProperty("--accent-text", "#1A1A1A");
document.documentElement.style.setProperty("--code", "#8BC34A");
document.documentElement.style.setProperty("--preformatted", "#A3A3A3");
document.documentElement.style.setProperty("--marked", "#FF7043");
document.documentElement.style.setProperty("--disabled", "#383838");
}
const themes = {
"light-breeze": light_breeze,
"plan9-acme": plan9_acme,
"bold-navy": bold_navy,
"soft-lavander": soft_lavander,
"neon-cyber": neon_cyber,
"dark-easy": dark_easy,
};

2
gebs

Submodule gebs updated: 6db36114d5...db8389f7d5

View File

@ -3,7 +3,7 @@
#include <pthread.h>
#define locked_init(x) { .value = (x), .lock = PTHREAD_MUTEX_INITIALIZER }
#define locked_init(x) { .value = x, .lock = PTHREAD_MUTEX_INITIALIZER }
#define locked(T) struct { T value; pthread_mutex_t lock; }
#define lockx(x) pthread_mutex_lock(&(x)->lock)

238
main.c
View File

@ -9,23 +9,39 @@
#include "mongoose/mongoose.h"
#define STB_DS_IMPLEMENTATION
#include "stb/stb_ds.h"
#include "md5-c/md5.h"
#include "routes.h"
#include "baked.h"
#include "timer.h"
#include "CONFIG.h"
#include "clonestr.h"
#include "locked.h"
static locked(Route *) route_hashtable = locked_init(nil);
static locked(char *) etc_dump_path = locked_init(nil);
static locked(char *) baked_dump_path = locked_init(nil);
char *clonestr_alloc(Allocator *alloc, char *s)
{
char *p = gebs_malloc(alloc, strlen(s)+1);
strcpy(p, s);
return p;
}
void *zmalloc_alloc(Allocator *alloc, size_t size)
{
void *p = gebs_malloc(alloc, size);
memset(p, 0, size);
return p;
}
void run_in_thread(void *(*f)(void *), void *p)
{
pthread_t tid = 0;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, f, p);
pthread_attr_destroy(&attr);
}
@ -33,91 +49,91 @@ void run_in_thread(void *(*f)(void *), void *p)
void *route_thread_function(void *param)
{
Route_Thread_Data *data = (Route_Thread_Data *)param;
Arena *arena = data->arena;
struct mg_http_message http_msg = {0};
int r = mg_http_parse(data->message.buf, data->message.len, &http_msg);
if (r <= 0) {
Route_Result result = {0};
result.status_code = 400;
result.type = ROUTE_RESULT_DYNAMIC;
/* list_append(&result.headers, clonestr("Content-Type: text/plain")); */
sb_append_nstr(&result.body, "Could not parse HTTP request");
sb_finish(&result.body);
Route_Result *result = zmalloc_alloc((Allocator *)arena, sizeof(*result));
result->arena = arena;
result->status_code = 400;
result->type = ROUTE_RESULT_DYNAMIC;
sb_append_nstr_alloc(arena, &result->body, "Could not parse HTTP request");
sb_finish_alloc(arena, &result->body);
mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result));
free(data->message.buf);
free(data);
return nil;
pthread_exit(nil);
} else {
if (mg_match(http_msg.uri, mg_str("/bakedres/*"), nil)) {
Route_Result *result = zmalloc_alloc((Allocator *)arena, sizeof(*result));
result->arena = arena;
result->type = ROUTE_RESULT_STATIC;
char *p = gebs_malloc(arena, http_msg.uri.len+1);
strncpy(p, http_msg.uri.buf, http_msg.uri.len);
p[http_msg.uri.len] = 0;
char *file = basename(p);
char *full_path = gebs_malloc(arena, PATH_MAX);
lockx(&baked_dump_path);
snprintf(full_path, PATH_MAX, "%s/%s", baked_dump_path.value, file);
unlockx(&baked_dump_path);
sb_append_nstr_alloc(arena, &result->body, full_path);
sb_finish_alloc(arena, &result->body);
mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result));
free(data->message.buf);
free(data);
pthread_exit(nil);
} else {
char key[MG_PATH_MAX] = {0};
strncpy(key, http_msg.uri.buf, http_msg.uri.len);
lockx(&route_hashtable);
ssize_t idx = shgeti(route_hashtable.value, key);
Route_Result *result = zmalloc_alloc((Allocator *)arena, sizeof(*result));
result->arena = arena;
route_hashtable.value[idx].value((Allocator *)arena, &http_msg, result, route_hashtable.value[idx].context_data);
unlockx(&route_hashtable);
mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result));
free(data->message.buf);
free(data);
pthread_exit(nil);
}
}
if (mg_match(http_msg.uri, mg_str("/etc/*"), nil)) {
Route_Result result = {0};
result.type = ROUTE_RESULT_STATIC;
char *p = malloc(http_msg.uri.len+1);
strncpy(p, http_msg.uri.buf, http_msg.uri.len);
p[http_msg.uri.len] = 0;
char *file = basename(p);
char *full_path = malloc(PATH_MAX);
lockx(&etc_dump_path);
snprintf(full_path, PATH_MAX, "%s/%s", etc_dump_path.value, file);
unlockx(&etc_dump_path);
sb_append_nstr(&result.body, full_path);
sb_finish(&result.body);
free(full_path);
free(p);
mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result));
free(data->message.buf);
free(data);
return nil;
}
char key[MG_PATH_MAX] = {0};
strncpy(key, http_msg.uri.buf, http_msg.uri.len);
lockx(&route_hashtable);
ssize_t idx = shgeti(route_hashtable.value, key);
Route_Result result = {0};
route_hashtable.value[idx].value(&http_msg, &result, route_hashtable.value[idx].context_data);
unlockx(&route_hashtable);
mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result));
free(data->message.buf);
free(data);
return nil;
}
void event_handler(struct mg_connection *conn, int ev, void *ev_data)
{
if (ev == MG_EV_HTTP_MSG) {
LOGI("HTTP EVENT\n");
struct mg_http_message *msg = (struct mg_http_message *)ev_data;
LOGI("Route: %.*s\n", msg->uri.len, msg->uri.buf);
Route_Thread_Data *data = calloc(1, sizeof(*data));
data->message = mg_strdup(msg->message);
data->conn_id = conn->id;
data->mgr = conn->mgr;
data->arena = malloc(sizeof(*data->arena));
*data->arena = arena_get();
LOGI("starting handler thread...\n");
run_in_thread(&route_thread_function, data);
} else if (ev == MG_EV_WAKEUP) {
LOGI("WAKEUP EVENT\n");
struct mg_str *data = (struct mg_str *)ev_data;
Route_Result *result = (Route_Result *)data->buf;
defer {
for (size_t i = 0; i < result->headers.count; i++) {
free(result->headers.items[i]);
}
list_free(&result->headers);
sb_free(&result->body);
}
Route_Result *result = *(Route_Result **)data->buf;
Arena *arena = result->arena;
Gebs_String_Builder sb = {0};
defer { sb_free(&sb); }
nsl_join(&result->headers, &sb, "\r\n");
nsl_join_alloc(arena, &result->headers, &sb, "\r\n");
if (result->headers.count > 0) {
sb_append_nstr(&sb, "\r\n");
sb_append_nstr_alloc(arena, &sb, "\r\n");
}
sb_finish(&sb);
sb_finish_alloc(arena, &sb);
if (result->type == ROUTE_RESULT_DYNAMIC) {
mg_http_reply(conn, result->status_code, sb.items, "%s", result->body.items);
@ -125,6 +141,11 @@ void event_handler(struct mg_connection *conn, int ev, void *ev_data)
char *path = result->body.items;
mg_http_serve_file(conn, ev_data, path, &(struct mg_http_serve_opts){0});
}
arena_destroy(arena);
free(arena);
LOGI("WAKEUP done, arena cleaned\n");
}
}
@ -142,46 +163,42 @@ void route_hashtable_put_blogs(Baked_Resource *resource, void *udata)
void init_route_hashtable(void)
{
lockx(&route_hashtable);
shdefault(route_hashtable.value, &route_page_not_found);
shput(route_hashtable.value, clonestr("/"), &route_home);
shput(route_hashtable.value, clonestr("/page-missing"), &route_page_not_found);
shput(route_hashtable.value, clonestr("/blog"), &route_blog);
shput(route_hashtable.value, clonestr_alloc(&default_allocator, "/"), &route_home);
shput(route_hashtable.value, clonestr_alloc(&default_allocator, "/page-missing"), &route_page_not_found);
shput(route_hashtable.value, clonestr_alloc(&default_allocator, "/blog"), &route_blog);
#if MY_DEBUG
shput(route_hashtable.value, clonestr("/build-id"), &route_build_id);
shput(route_hashtable.value, clonestr_alloc(&default_allocator, "/build-id"), &route_build_id);
#endif
baked_resource_each(&route_hashtable_put_blogs, nil);
unlockx(&route_hashtable);
}
void free_route_hashtable(void)
{
lockx(&route_hashtable);
for (size_t i = 0; i < shlen(route_hashtable.value); i++) {
free(route_hashtable.value[i].key);
}
shfree(route_hashtable.value);
unlockx(&route_hashtable);
}
char *init_etc_dump(void)
char *init_baked_dump(void)
{
char template[] = "/tmp/aboba-etc.XXXXXX";
char *etc_dump1 = mkdtemp(template);
char *etc_dump = malloc(strlen(etc_dump1)+1);
strcpy(etc_dump, etc_dump1);
char template[] = "/tmp/aboba-bakedres.XXXXXX";
char *baked_dump1 = mkdtemp(template);
char *baked_dump = malloc(strlen(baked_dump1)+1);
strcpy(baked_dump, baked_dump1);
if (etc_dump == nil) {
LOGE("Could not create etc dump\n");
if (baked_dump == nil) {
LOGE("Could not create bakedres dump\n");
return nil;
}
LOGI("etc dump dir is %s\n", etc_dump);
LOGI("bakedres dump dir is %s\n", baked_dump);
return etc_dump;
return baked_dump;
}
int etc_dump_rm_cb(const char *path,
int baked_dump_rm_cb(const char *path,
discard const struct stat *st,
discard int type,
discard struct FTW *ftw)
@ -189,14 +206,14 @@ int etc_dump_rm_cb(const char *path,
return remove(path);
}
bool free_etc_dump(char *etc_dump)
bool free_baked_dump(char *baked_dump)
{
LOGI("Removing etc dump %s\n", etc_dump);
bool ok = nftw(etc_dump, etc_dump_rm_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) != -1;
LOGI("Removing bakedres dump %s\n", baked_dump);
bool ok = nftw(baked_dump, baked_dump_rm_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) != -1;
if (!ok) {
LOGE("Could not remove %s\n", etc_dump);
LOGE("Could not remove %s\n", baked_dump);
}
free(etc_dump);
free(baked_dump);
return ok;
}
@ -234,47 +251,40 @@ int cp(const char* source, const char* destination)
return result;
}
void populate_etc_dump(char *etc_dump)
void copy_baked_resources_to_baked_dump(Baked_Resource *resource, void *udata)
{
static char *files[] = {
"favicon.ico",
#if MY_DEBUG
"hotreload.js",
#endif
"theme.js",
"simple.css",
"me.jpg",
"tmoa-engine.jpg",
"tmoa-garbage.jpg",
};
lock_baked_resources();
for (size_t i = 0; i < sizeof(files)/sizeof(files[0]); i++) {
char path[PATH_MAX];
get_baked_resource_path(files[i], path, sizeof(path));
char dest[PATH_MAX];
snprintf(dest, sizeof(dest), "%s/%s", etc_dump, files[i]);
cp(path, dest);
}
unlock_baked_resources();
char *baked_dump = (char *)udata;
char path[PATH_MAX];
get_baked_resource_path(resource->key, path, sizeof(path));
char dest[PATH_MAX];
snprintf(dest, sizeof(dest), "%s/%s", baked_dump, resource->key);
cp(path, dest);
}
volatile bool alive = true;
void populate_baked_dump(char *baked_dump)
{
baked_resource_each(&copy_baked_resources_to_baked_dump, baked_dump);
}
volatile sig_atomic_t alive = true;
void graceful_shutdown(int no) { alive = false; }
int main(int argc, char ** argv)
{
signal(SIGINT, &graceful_shutdown);
struct sigaction sa;
sa.sa_handler = &graceful_shutdown;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, nil);
start_timer();
init_baked_resources();
lockx(&etc_dump_path);
if ((etc_dump_path.value = init_etc_dump()) == nil) {
if ((baked_dump_path.value = init_baked_dump()) == nil) {
return 1;
}
populate_etc_dump(etc_dump_path.value);
unlockx(&etc_dump_path);
populate_baked_dump(baked_dump_path.value);
mg_log_set(MG_LL_DEBUG);
struct mg_mgr mgr;
@ -292,9 +302,7 @@ int main(int argc, char ** argv)
mg_mgr_free(&mgr);
lockx(&etc_dump_path);
free_etc_dump(etc_dump_path.value);
unlockx(&etc_dump_path);
free_baked_dump(baked_dump_path.value);
free_baked_resources();
return 0;

226
routes.c
View File

@ -7,10 +7,11 @@
#include "routes.h"
#include "baked.h"
#include "clonestr.h"
#include "commit.h"
#include "timer.h"
extern char *clonestr_alloc(Allocator *alloc, char *s);
#define INTERNAL_SERVER_ERROR_MSG "Internal server error ;(. Try again later..."
#define META_TAGS \
@ -19,65 +20,65 @@
"<meta name=\"author\" content=\"kamkow1\">" \
"<meta name=\"application-name\" content=\"aboba\">"
void make_internal_server_error(Route_Result *result)
void make_application_json(Allocator *alloc, Route_Result *result, int code, cJSON *root)
{
result->status_code = 500;
result->type = ROUTE_RESULT_DYNAMIC;
list_append(&result->headers, "Content-Type: text/plain");
sb_append_nstr(&result->body, INTERNAL_SERVER_ERROR_MSG);
sb_finish(&result->body);
}
size_t buf_size = 512;
char *buf = gebs_malloc(alloc, buf_size);
void make_application_json(Route_Result *result, int code, cJSON *root)
{
char *root_text = cJSON_PrintUnformatted(root);
defer { free(root_text); }
cJSON_PrintPreallocated(root, buf, buf_size, false);
result->status_code = code;
result->type = ROUTE_RESULT_DYNAMIC;
list_append(&result->headers, clonestr("Content-Type: application/json"));
sb_append_nstr(&result->body, root_text);
sb_finish(&result->body);
list_append_alloc(alloc, &result->headers, clonestr_alloc(alloc, "Content-Type: application/json"));
sb_append_nstr_alloc(alloc, &result->body, buf);
sb_finish_alloc(alloc, &result->body);
}
void make_text(Route_Result *result, char *subtype, int code, char *in)
void make_text(Allocator *alloc, Route_Result *result, char *subtype, int code, char *in)
{
char type[100];
snprintf(type, sizeof(type), "Content-Type: text/%s", subtype);
result->status_code = code;
result->type = ROUTE_RESULT_DYNAMIC;
list_append(&result->headers, clonestr(type));
sb_append_nstr(&result->body, in);
list_append_alloc(alloc, &result->headers,
clonestr_alloc(alloc, fmt("Content-Type: text/%s", subtype)));
sb_append_nstr_alloc(alloc, &result->body, in);
sb_finish(&result->body);
}
bool gpp_run(char *path, NString_List *env, String_Builder *out)
void make_internal_server_error(Allocator *alloc, Route_Result *result)
{
make_text(alloc, result, "plain", 500, INTERNAL_SERVER_ERROR_MSG);
}
bool gpp_run(Allocator *alloc, char *path, NString_List *env, String_Builder *out)
{
Cmd cmd = {0};
defer { cmd_free(&cmd); }
char gpp1[PATH_MAX];
if (!get_baked_resource_path("gpp1", gpp1, sizeof(gpp1))) {
return false;
}
cmd_append(&cmd, gpp1);
cmd_append(&cmd, "-H");
cmd_append(&cmd, "-x");
cmd_append(&cmd, "--nostdinc");
cmd_append(&cmd, path);
cmd_append_alloc(alloc, &cmd, gpp1);
cmd_append_alloc(alloc, &cmd, "-H");
cmd_append_alloc(alloc, &cmd, "-x");
cmd_append_alloc(alloc, &cmd, "--nostdinc");
cmd_append_alloc(alloc, &cmd, path);
for (size_t i = 0; i < env->count; i++) {
cmd_append(&cmd, env->items[i]);
cmd_append_alloc(alloc, &cmd, env->items[i]);
}
return cmd_run_collect(&cmd, out) == 0;
bool ok = cmd_run_collect_alloc(alloc, &cmd, out) == 0;
size_t len = out->count > 100 ? 100 : out->count;
LOGI("Output (first 100): \n-------------------------------\n%.*s\n-------------------------------\n", len, out->items);
return ok;
}
#if MY_DEBUG
void route_build_id(struct mg_http_message *msg, Route_Result *result, void *context_data)
void route_build_id(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data)
{
LOGI("handler build_id (%.*s)\n", msg->uri.len, msg->uri.buf);
// TODO: Somehow use our arena in here. This will require a slight rework of the way cJSON works.
cJSON *root = cJSON_CreateObject();
defer { cJSON_Delete(root); }
@ -85,226 +86,217 @@ void route_build_id(struct mg_http_message *msg, Route_Result *result, void *con
uchar md5_buf[16];
md5String(time, md5_buf);
String_Builder sb = {0};
defer { sb_free(&sb); }
for (size_t i = 0; i < 16; i++) {
sb_append_nstr(&sb, fmt("%02x", md5_buf[i]));
sb_append_nstr_alloc(alloc, &sb, clonestr_alloc(alloc, fmt("%02x", md5_buf[i])));
}
sb_finish(&sb);
sb_finish_alloc(alloc, &sb);
cJSON_AddItemToObject(root, "build_id", cJSON_CreateString(sb.items));
make_application_json(result, 200, root);
make_application_json(alloc, result, 200, root);
LOGI("handler build_id done\n");
}
#endif
void route_page_not_found(struct mg_http_message *msg, Route_Result *result, void *context_data)
void route_page_not_found(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data)
{
lock_baked_resources();
defer { unlock_baked_resources(); }
LOGI("handler page not found (%.*s)\n", msg->uri.len, msg->uri.buf);
NString_List env = {0};
defer { list_free(&env); }
list_append(&env, fmt("-DURL=%.*s", msg->uri.len, msg->uri.buf));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DURL=%.*s", msg->uri.len, msg->uri.buf)));
String_Builder out = {0};
defer { sb_free(&out); }
char path[PATH_MAX] = {0};
if (!get_baked_resource_path("page-missing.html", path, sizeof(path))) {
make_internal_server_error(result);
LOGE("Failed to get baked resource page-missing.html\n");
make_internal_server_error(alloc, result);
return;
}
list_append(&env, fmt("-DCOMMIT=%s", COMMIT_STRING));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DCOMMIT=%s", COMMIT_STRING)));
char timer[100];
get_timer_string(timer, sizeof(timer));
list_append(&env, fmt("-DRUNNING_SINCE=%s", timer));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DRUNNING_SINCE=%s", timer)));
list_append(&env, fmt("-DMETA_TAGS=%s", META_TAGS));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DMETA_TAGS=%s", META_TAGS)));
#if MY_DEBUG
list_append(&env, "-DHOTRELOAD");
list_append_alloc(alloc, &env, "-DHOTRELOAD");
#endif
bool ok = gpp_run(path, &env, &out);
sb_finish(&out);
bool ok = gpp_run(alloc, path, &env, &out);
sb_finish_alloc(alloc, &out);
if (!ok) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
} else {
make_text(result, "html", 200, out.items);
make_text(alloc, result, "html", 200, out.items);
}
LOGI("handler page not found done\n");
}
void route_home(struct mg_http_message *msg, Route_Result *result, void *context_data)
void route_home(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data)
{
lock_baked_resources();
defer { unlock_baked_resources(); }
LOGI("handler home (%.*s)\n", msg->uri.len, msg->uri.buf);
NString_List env = {0};
defer { list_free(&env); }
String_Builder out = {0};
defer { sb_free(&out); }
char path[PATH_MAX] = {0};
if (!get_baked_resource_path("home.html", path, sizeof(path))) {
make_internal_server_error(result);
LOGE("Failed to get baked resource home.html\n");
make_internal_server_error(alloc, result);
return;
}
list_append(&env, fmt("-DCOMMIT=%s", COMMIT_STRING));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DCOMMIT=%s", COMMIT_STRING)));
char timer[100];
get_timer_string(timer, sizeof(timer));
list_append(&env, fmt("-DRUNNING_SINCE=%s", timer));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DRUNNING_SINCE=%s", timer)));
list_append(&env, fmt("-DMETA_TAGS=%s", META_TAGS));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DMETA_TAGS=%s", META_TAGS)));
#if MY_DEBUG
list_append(&env, "-DHOTRELOAD");
list_append_alloc(alloc, &env, "-DHOTRELOAD");
#endif
bool ok = gpp_run(path, &env, &out);
sb_finish(&out);
bool ok = gpp_run(alloc, path, &env, &out);
sb_finish_alloc(alloc, &out);
if (!ok) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
} else {
make_text(result, "html", 200, out.items);
make_text(alloc, result, "html", 200, out.items);
}
LOGI("handler home done\n");
}
void route_generic_blog(struct mg_http_message *msg, Route_Result *result, void *context_data)
void route_generic_blog(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data)
{
lock_baked_resources();
defer { unlock_baked_resources(); }
LOGI("handler generic blog (%.*s)\n", msg->uri.len, msg->uri.buf);
NString_List env = {0};
defer { list_free(&env); }
String_Builder out = {0};
defer { sb_free(&out); }
Baked_Resource *resource = (Baked_Resource *)context_data;
char md_path[PATH_MAX] = {0};
if (!get_baked_resource_path(resource->key, md_path, sizeof(md_path))) {
make_internal_server_error(result);
LOGE("Failed to get baked resource %s\n", resource->key);
make_internal_server_error(alloc, result);
return;
}
list_append(&env, fmt("-DCOMMIT=%s", COMMIT_STRING));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DCOMMIT=%s", COMMIT_STRING)));
char timer[100];
get_timer_string(timer, sizeof(timer));
list_append(&env, fmt("-DRUNNING_SINCE=%s", timer));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DRUNNING_SINCE=%s", timer)));
list_append(&env, fmt("-DMETA_TAGS=%s", META_TAGS));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DMETA_TAGS=%s", META_TAGS)));
#if MY_DEBUG
list_append(&env, "-DHOTRELOAD");
list_append_alloc(alloc, &env, "-DHOTRELOAD");
#endif
String_Builder md = {0};
defer { sb_free(&md); }
if (!sb_read_file(&md, md_path)) {
make_internal_server_error(result);
if (!sb_read_file_alloc(alloc, &md, md_path)) {
make_internal_server_error(alloc, result);
return;
}
String_Builder md_prepared = {0};
defer { sb_free(&md_prepared); }
for (size_t i = 0; i < md.count; i++) {
char c = md.items[i];
switch (c) {
case '\"': sb_append_nstr(&md_prepared, "\\\""); break;
case '\'': sb_append_nstr(&md_prepared, "\\\'"); break;
case '\\': sb_append_nstr(&md_prepared, "\\\\"); break;
case '\a': sb_append_nstr(&md_prepared, "\\a"); break;
case '\b': sb_append_nstr(&md_prepared, "\\b"); break;
case '\r': sb_append_nstr(&md_prepared, "\\r"); break;
case '\n': sb_append_nstr(&md_prepared, "\\n"); break;
case '\t': sb_append_nstr(&md_prepared, "\\t"); break;
case '\"': sb_append_nstr_alloc(alloc, &md_prepared, "\\\""); break;
case '\'': sb_append_nstr_alloc(alloc, &md_prepared, "\\\'"); break;
case '\\': sb_append_nstr_alloc(alloc, &md_prepared, "\\\\"); break;
case '\a': sb_append_nstr_alloc(alloc, &md_prepared, "\\a"); break;
case '\b': sb_append_nstr_alloc(alloc, &md_prepared, "\\b"); break;
case '\r': sb_append_nstr_alloc(alloc, &md_prepared, "\\r"); break;
case '\n': sb_append_nstr_alloc(alloc, &md_prepared, "\\n"); break;
case '\t': sb_append_nstr_alloc(alloc, &md_prepared, "\\t"); break;
default:
if (iscntrl(md.items[i])) {
sb_append_nstr(&md_prepared, fmt("\\%03o", c));
sb_append_nstr_alloc(alloc, &md_prepared, clonestr_alloc(alloc, fmt("\\%03o", c)));
} else {
sb_append_char(&md_prepared, c);
sb_append_char_alloc(alloc, &md_prepared, c);
}
break;
}
}
sb_finish(&md_prepared);
sb_finish_alloc(alloc, &md_prepared);
list_append(&env, fmt("-DBLOG_POST_TITLE=%s", resource->key));
list_append(&env, fmt("-DBLOG_POST_MARKDOWN=%s", md_prepared.items));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DBLOG_POST_TITLE=%s", resource->key)));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DBLOG_POST_MARKDOWN=%s", md_prepared.items)));
char tmpl_file[PATH_MAX] = {0};
if (!get_baked_resource_path("template-blog.html", tmpl_file, sizeof(tmpl_file))) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
return;
}
bool ok = gpp_run(tmpl_file, &env, &out);
sb_finish(&out);
bool ok = gpp_run(alloc, tmpl_file, &env, &out);
sb_finish_alloc(alloc, &out);
if (!ok) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
} else {
make_text(result, "html", 200, out.items);
make_text(alloc, result, "html", 200, out.items);
}
LOGI("handler generic blog done\n");
}
void collect_blogs(Baked_Resource *resource, void *udata)
{
struct { Arena *tmp; String_Builder *sb; } *ud = udata;
struct { Allocator *alloc; String_Builder *sb; } *ud = udata;
if ((strlen(resource->key) >= strlen("blog-"))
&& strncmp(resource->key, "blog-", strlen("blog-")) == 0) {
sb_append_nstr_alloc(ud->tmp, ud->sb, fmt("<li><a href=\"/%s\">%s</a></li>", resource->key, resource->key));
sb_append_nstr_alloc(ud->alloc, ud->sb,
clonestr_alloc(ud->alloc, fmt("<li><a href=\"/%s\">%s</a></li>", resource->key, resource->key)));
}
}
void route_blog(struct mg_http_message *msg, Route_Result *result, void *context_data)
void route_blog(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data)
{
NString_List env = {0};
defer { list_free(&env); }
String_Builder out = {0};
defer { sb_free(&out); }
Arena tmp = arena_get();
defer { arena_destroy(&tmp); }
String_Builder sb = {0};
struct { Arena *tmp; String_Builder *sb; } collect_blogs_data = {
.tmp = &tmp,
struct { Allocator *alloc; String_Builder *sb; } collect_blogs_data = {
.alloc = alloc,
.sb = &sb,
};
baked_resource_each(&collect_blogs, &collect_blogs_data);
sb_finish_alloc(&tmp, &sb);
list_append(&env, fmt("-DBLOG_POSTS=%s", sb.items));
sb_finish_alloc(alloc, &sb);
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DBLOG_POSTS=%s", sb.items)));
char path[PATH_MAX] = {0};
if (!get_baked_resource_path("blog.html", path, sizeof(path))) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
return;
}
list_append(&env, fmt("-DCOMMIT=%s", COMMIT_STRING));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DCOMMIT=%s", COMMIT_STRING)));
char timer[100];
get_timer_string(timer, sizeof(timer));
list_append(&env, fmt("-DRUNNING_SINCE=%s", timer));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DRUNNING_SINCE=%s", timer)));
list_append(&env, fmt("-DMETA_TAGS=%s", META_TAGS));
list_append_alloc(alloc, &env, clonestr_alloc(alloc, fmt("-DMETA_TAGS=%s", META_TAGS)));
#if MY_DEBUG
list_append(&env, "-DHOTRELOAD");
list_append_alloc(alloc, &env, "-DHOTRELOAD");
#endif
bool ok = gpp_run(path, &env, &out);
sb_finish(&out);
bool ok = gpp_run(alloc, path, &env, &out);
sb_finish_alloc(alloc, &out);
if (!ok) {
make_internal_server_error(result);
make_internal_server_error(alloc, result);
} else {
make_text(result, "html", 200, out.items);
make_text(alloc, result, "html", 200, out.items);
}
}

View File

@ -12,15 +12,17 @@ typedef struct {
int status_code;
NString_List headers;
String_Builder body;
Arena *arena;
} Route_Result;
typedef struct {
struct mg_mgr *mgr;
ulong conn_id;
struct mg_str message;
Arena *arena;
} Route_Thread_Data;
typedef void (*Route_Handler)(struct mg_http_message *msg, Route_Result *result, void *context_data);
typedef void (*Route_Handler)(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
typedef struct {
char *key; // path
@ -29,11 +31,11 @@ typedef struct {
} Route;
#if MY_DEBUG
void route_build_id(struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_build_id(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
#endif
void route_page_not_found(struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_home(struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_generic_blog(struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_blog(struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_page_not_found(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_home(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_generic_blog(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
void route_blog(Allocator *alloc, struct mg_http_message *msg, Route_Result *result, void *context_data);
#endif // ROUTES_H_

View File

@ -3,7 +3,7 @@
<head>
<title>Blog</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/etc/simple.css" />
<link rel="stylesheet" href="/bakedres/simple.css" />
<#META_TAGS>
</head>
<body>
@ -24,8 +24,8 @@
</footer>
<#ifdef HOTRELOAD>
<script src="/etc/hotreload.js"></script>
<script src="/bakedres/hotreload.js"></script>
<#endif>
<script src="/etc/theme.js"></script>
<script src="/bakedres/theme.js"></script>
</body>
</html>

View File

@ -3,7 +3,7 @@
<head>
<title>Kamil's personal website</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/etc/simple.css" />
<link rel="stylesheet" href="/bakedres/simple.css" />
<#META_TAGS>
</head>
<body>
@ -54,7 +54,7 @@
</ul>
</section>
<section>
<img src="/etc/me.jpg" alt="literally me" />
<img src="/bakedres/me.jpg" alt="literally me" />
</section>
<footer>
<div style="float: left;">
@ -65,8 +65,8 @@
</footer>
<#ifdef HOTRELOAD>
<script src="/etc/hotreload.js"></script>
<script src="/bakedres/hotreload.js"></script>
<#endif>
<script src="/etc/theme.js"></script>
<script src="/bakedres/theme.js"></script>
</body>
</html>

View File

@ -2,7 +2,7 @@
<html>
<head>
<title>404 - Page not found</title>
<link rel="stylesheet" href="/etc/simple.css" />
<link rel="stylesheet" href="/bakedres/simple.css" />
<#META_TAGS>
</head>
<body>
@ -19,8 +19,8 @@
</footer>
<#ifdef HOTRELOAD>
<script src="/etc/hotreload.js"></script>
<script src="/bakedres/hotreload.js"></script>
<#endif>
<script src="/etc/theme.js"></script>
<script src="/bakedres/theme.js"></script>
</body>
</html>

View File

@ -3,10 +3,10 @@
<head>
<meta charset="utf-8" />
<title><#BLOG_POST_TITLE></title>
<link rel="stylesheet" href="/etc/simple.css" />
<link rel="stylesheet" href="/bakedres/simple.css" />
<#META_TAGS>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/rainbow.min.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
<link rel="stylesheet" href="/bakedres/hljs-rainbow.css">
<script src="/bakedres/highlight.js"></script>
</head>
<body>
<div id="content"></div>
@ -17,15 +17,15 @@
<span>Running since: <#RUNNING_SINCE></span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="/bakedres/marked.js"></script>
<script>
document.getElementById("content").innerHTML = marked.parse(`<#BLOG_POST_MARKDOWN>`);
</script>
<#ifdef HOTRELOAD>
<script src="/etc/hotreload.js"></script>
<script src="/bakedres/hotreload.js"></script>
<#endif>
<script src="/etc/theme.js"></script>
<script src="/bakedres/theme.js"></script>
<script>hljs.highlightAll();</script>
</body>
</html>

1
zip Submodule

Submodule zip added at 649a16c5ea