I wanted to have a look at adding Cache-Control headers to Bozohttpd for stupid benchmark reasons; Also because I still find this stuff interesting and a bit of a challenge.

Setting a Cache-Control header everywhere

Initially I just figured out setting a single header for all file types which was pretty much just adding into bozo_print_header in bozohttpd.c:

if (httpd->cache_control_headers)
	bozo_printf(httpd, "Cache-Control: %s\r\n", httpd->cache_control_headers);

which just checks if a header has been set in the preferences and if so prints it. The header is set via an additional command line argument in main.c:

bozowarn(httpd, "   -h string\t\tCache-Control headers");

and

case 'h':
	bozo_set_pref(&httpd, &prefs, "Cache-Control headers",
		optarg);
	break;

so you could specify -h max-age=86400. The bit that threw me here was getopt - I missed the significance of the trailing colon on the letter:

while ((c = getopt(argc, argv,
	"C:EGHI:L:M:P:S:U:VXZ:bc:defh:i:np:st:uv:x:z:")) != -1) {}}

An option character in this string can be followed by a colon (‘:’) to indicate that it takes a required argument

It merrily builds and runs if you miss that out until you try to supply that argument when it’ll segfault.

Setting a Cache-Control header on images

That was a good proof of concept, but I didn’t really want to set one Cache-Control header everywhere for everything so next I tried a bit of hard-coded logic that set the header only where the content-type matched “image”:

if (httpd->cache_control_headers && (strstr(type, "image") != NULL))
	bozo_printf(httpd, "Cache-Control: %s\r\n", httpd->cache_control_headers);

I like this idea. I still think it makes sense to be able to match a string based on the content-type as then you can match a lot of different files in one fell swoop. I.e. “image” matches image/jpeg, image/png, image/gif, etc; However I can’t really hard code the match in. Really we need to be able to supply multiple options like:

-h "image" "max-age=604800" -h "css" "max-age=86400"

Which led to…

Setting a Cache-Control header per file type

Bozohttpd was already setup for this approach as it has an existing content map for file suffixes which is used to set content-types. The cgi capabilities also piggyback on this same map so it just made sense to also jump on this too.

This basically just adds another column into the existing map (filled with NULLs):

static bozo_content_map_t static_content_map[] = {
	{ ".html",	"text/html",			"",		"", NULL, NULL},
	{ ".htm",	"text/html",			"",		"", NULL, NULL},
	{ ".gif",	"image/gif",			"",		"", NULL, NULL},

which can be used to set a Cache-Control header based on the file extension. I added a minimal cache-bozo.c file that copies how the cgi-bozo.c piggybacks on the content map. When it comes to setting headers it’s a simple as copying how headers are set for encodings and file-types:

if (cacheheader && *cacheheader)
	bozo_printf(httpd, "Cache-Control: %s\r\n", cacheheader);
if (type && *type)
	bozo_printf(httpd, "Content-Type: %s\r\n", type);
if (encoding && *encoding)
	bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding);

there’s obviously a bit more to it than that, but that’s it in principle. Then you can supply arguments like:

-h .jpg 'private, max-age=604800' -h .png 'private, max-age=604800'

This approach is good enough for me so I’ll likely leave my changes like this.


I’ve published my “fork”/changes here: Self-hosted repos. I might propose upstream, but they’ll probably want other changes (really it’d be better to at least set any header type) I’ll not have the impetus to make.

Woo! Another part of the pretending I know how to program in C series.