about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-24 21:03:31 +0200
committersternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-24 21:03:31 +0200
commit32bf1064c08c5b63522c110c9359ff393a590929 (patch)
treeeda3ecfb90ffec862e1cf5784b5c0a36de5fd47d
parent37bc9d98eeb64692eae2c44167998b5670821d60 (diff)
feat(main): add simple caching support via max-age
* remove support for RSS <ttl> element, remove BLOG_RSS_TTL
* add BLOG_CACHE_MAX_AGE, send Cache-Control headers
* TODO: more sophisticated caching support? ETag support?
-rw-r--r--TODO3
-rw-r--r--config.example.h29
-rw-r--r--main.c62
3 files changed, 51 insertions, 43 deletions
diff --git a/TODO b/TODO
index 653446f..e9ca196 100644
--- a/TODO
+++ b/TODO
@@ -1,11 +1,10 @@
-make RSS_TTL optional | id:32a848e82d8b39bb8495f49d491882da320a3c7d
-add support for caching | id:472d68f1af5489020f4c5808e805822e3da77ffc
 rethink logging / debug output | id:5734e2717f28d80ac65d3f2123a0f46c01d4a0c1
 if possible determine BLOG_SERVER_URL automatically | id:5ad1fabab42a5776ab1446d09659c7a99a104245
 rework timezone offsets without tzset() | id:64d715e826ba9fdbcc72cb1b92a77f004642d2a3
 support markdown markup via lowdown | id:90c72d144f21d78d1b56790a927f6eea52df2a2c
 get rid of as many fixed size buffers as possible | id:9281f3a25aa08f6ba5d67c02e65cd0fb7f0a6b3c
 templating: give all functions context (index or not, SCRIPT_NAME, entry struct if applicable) | id:930c2e4f2c4c9ec3efef87b6ddd50a6ba6dd4903
+update user documentation to reflect recent config changes | id:ae716d655e71edede2a763bb7c0f6e1d369de571
 improve man3 situation | id:bf918930f6361c30f288c42894cbf9b0541c3340
 use errno instead of returning HTTP status codes in make_index | id:bfa35c48dcedbecd3a951eba243c4840178289ed
 templating: unify single_entry and index_entry | id:e6dcb195d890bb0b5186716808d806101d81e4f6
diff --git a/config.example.h b/config.example.h
index 655f1b6..b089d21 100644
--- a/config.example.h
+++ b/config.example.h
@@ -88,14 +88,33 @@
  */
 
 /*!
- * @brief TTL of RSS feed
+ * @brief Time a browsers should cache a response of sternenblog
  *
- * TTL (time to live) of the RSS feed in minutes. Feed readers may use this
- * value to determine how often to refresh the RSS feed.
+ * Time in seconds that should be the value of the `max-age` field
+ * of the `Cache-Control` header. It determines the time in seconds
+ * a response of sternenblog should be considered “fresh”.
  *
- * @see https://cyber.harvard.edu/rss/rss.html#ltttlgtSubelementOfLtchannelgt
+ * During this time browsers will used the cached version of the
+ * response (if present) for a given URL. This is useful to reduce
+ * server load, since browsers will request resources less often
+ * which means they have to be generated less often as well
+ * (especially the index).
+ *
+ * The value should be at least the time you expect users to spend
+ * on your website although you might want to go lower if your
+ * website updates _very_ frequently.
+ *
+ * The header will also be set for the feeds, but the RSS
+ * `ttl` element won't be generated based on this value.
+ *
+ * Optional setting.
+ *
+ * Requires HTTP/1.1, but sternenblog won't check the used protocol version
+ * before sending this header.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
  */
-#define BLOG_RSS_TTL 60
+#define BLOG_CACHE_MAX_AGE 3600
 
 //! @}
 
diff --git a/main.c b/main.c
index 2636236..55d8000 100644
--- a/main.c
+++ b/main.c
@@ -100,6 +100,8 @@
 //      reduce memory usage by only using get_text in the inner loop
 //      and unmapping the file directly afterwards
 
+void send_standard_headers(int status, char content_type[]);
+
 /*!
  * @brief Outputs the CGI response for the index page of the blog
  *
@@ -158,9 +160,7 @@ int main(void) {
     if(script_name == NULL) {
         fputs("Missing SCRIPT_NAME", stderr);
 
-        send_header("Content-type", "text/html");
-        send_header("Status", "500 Internal Server Error");
-        terminate_headers();
+        send_standard_headers(500, "text/html");
 
         template_header();
         template_error(500);
@@ -183,23 +183,34 @@ int main(void) {
     return EXIT_SUCCESS;
 }
 
+void send_standard_headers(int status, char content_type[]) {
+    send_header("Status", http_status_line(status));
+    send_header("Content-type", content_type);
+
+#ifdef BLOG_CACHE_MAX_AGE
+    char max_age[256];
+    int result = snprintf(max_age, sizeof max_age, "max-age=%d", BLOG_CACHE_MAX_AGE);
+    if(result > 0 && max_age[sizeof max_age - 1] == '\0') {
+        send_header("Cache-Control", max_age);
+    }
+#endif
+
+    terminate_headers();
+}
+
 void blog_index(char script_name[]) {
     struct entry *entries = NULL;
 
     int count = make_index(BLOG_DIR, script_name, 0, &entries);
 
-    send_header("Content-type", "text/html");
-
     if(count < 0) {
-        send_header("Status", "500 Internal Server Error");
-        terminate_headers();
+        send_standard_headers(500, "text/html");
 
         template_header();
         template_error(500);
         template_footer();
     } else {
-        send_header("Status", "200 OK");
-        terminate_headers();
+        send_standard_headers(200, "text/html");
 
         template_header();
 
@@ -222,8 +233,6 @@ void blog_index(char script_name[]) {
 }
 
 void blog_entry(char script_name[], char path_info[]) {
-    send_header("Content-type", "text/html");
-
     struct entry entry;
     int status = make_entry(BLOG_DIR, script_name, path_info, &entry);
 
@@ -231,17 +240,13 @@ void blog_entry(char script_name[], char path_info[]) {
         status = 500;
     }
 
-    if(status != 200) {
-        send_header("Status", http_status_line(status));
-        terminate_headers();
+    send_standard_headers(status, "text/html");
 
+    if(status != 200) {
         template_header();
         template_error(status);
         template_footer();
     } else {
-        send_header("Status", "200 OK");
-        terminate_headers();
-
         template_header();
         template_single_entry(entry);
         template_footer();
@@ -257,17 +262,13 @@ void blog_rss(char script_name[]) {
     int count = make_index(BLOG_DIR, script_name, 1, &entries);
 
     if(count < 0) {
-        send_header("Status", "500 Internal Server Error");
-        send_header("Content-type", "text/plain");
-        terminate_headers();
+        send_standard_headers(500, "text/plain");
 
         puts("Internal Server Error");
         return;
     }
 
-    send_header("Status", "200 OK");
-    send_header("Content-type", "application/rss+xml");
-    terminate_headers();
+    send_standard_headers(200, "application/rss+xml");
 
     struct xml_context ctx;
     new_xml_context(&ctx);
@@ -304,13 +305,6 @@ void blog_rss(char script_name[]) {
         }
     }
 
-    char ttl_string[32];
-    if(snprintf(ttl_string, sizeof ttl_string, "%d", BLOG_RSS_TTL) >= 0) {
-        xml_open_tag(&ctx, "ttl");
-        xml_escaped(&ctx, ttl_string);
-        xml_close_tag(&ctx, "ttl");
-    }
-
     char *rss_link = catn_alloc(3, BLOG_SERVER_URL, script_name, "/rss.xml");
     if(rss_link != NULL) {
         xml_empty_tag(&ctx, "atom:link", 3,
@@ -368,9 +362,7 @@ void blog_atom(char script_name[]) {
     int count = make_index(BLOG_DIR, script_name, 1, &entries);
 
     if(count < 0) {
-        send_header("Status", http_status_line(500));
-        send_header("Content-type", "text/plain");
-        terminate_headers();
+        send_standard_headers(500, "text/plain");
 
         puts("Internal Server Error");
 
@@ -384,9 +376,7 @@ void blog_atom(char script_name[]) {
     char *self_url = catn_alloc(3, BLOG_SERVER_URL, script_name, "/atom.xml");
     char *html_url = catn_alloc(2, BLOG_SERVER_URL, script_name);
 
-    send_header("Status", http_status_line(200));
-    send_header("Content-type", "application/atom+xml");
-    terminate_headers();
+    send_standard_headers(200, "application/atom+xml");
 
     xml_raw(&ctx, "<?xml version=\"1.0\" encoding=\"utf-8\"?>");
     xml_open_tag_attrs(&ctx, "feed", 1, "xmlns", "http://www.w3.org/2005/Atom");