From f8100ac74e4371a088d6ac85587f37d3a8ab6f24 Mon Sep 17 00:00:00 2001
From: Robert Knight <robertknight@gmail.com>
Date: Thu, 31 Mar 2022 08:01:42 +0100
Subject: [PATCH] Add mechanism to specify custom headers for HTML test pages

Add support for specifying custom headers to serve test pages with via
inline `<!-- Header: <Key>:<Value> -->` comments in the HTML/XML, and
change the existing XHTML test case to use it.
---
 dev-server/documents/html/xhtml-test.mustache |  7 ++++
 dev-server/serve-dev.js                       | 38 ++++++++++++++-----
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/dev-server/documents/html/xhtml-test.mustache b/dev-server/documents/html/xhtml-test.mustache
index 7508be6e946..924d8dfee35 100644
--- a/dev-server/documents/html/xhtml-test.mustache
+++ b/dev-server/documents/html/xhtml-test.mustache
@@ -1,4 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  The dev server serves all HTML content with the text/html MIME type
+  by default. Much (most?) "XHTML" content on the web is served this way.
+  However serving with the correct MIME type affects browser behavior, so
+  it is important to use that here.
+-->
+<!-- Header: Content-Type: application/xhtml+xml -->
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>XHTML test document</title>
diff --git a/dev-server/serve-dev.js b/dev-server/serve-dev.js
index 12b4167075c..29cccfa09e9 100644
--- a/dev-server/serve-dev.js
+++ b/dev-server/serve-dev.js
@@ -41,6 +41,27 @@ function renderScript(context) {
   return Mustache.render(scriptTemplate, context);
 }
 
+/**
+ * Read tags in test pages specifying custom headers to serve the content with.
+ *
+ * These tags look like `<!-- Header: <Key>: <Value> -->`.
+ *
+ * @param {string} content
+ * @return {[key: string, value: string][]}
+ */
+function readCustomHeaderTags(content) {
+  return content
+    .split('\n')
+    .map(line => {
+      const keyValue = line.match(/<!--\s*Header:\s*([A-Za-z-]+):(.*)-->/);
+      if (!keyValue) {
+        return null;
+      }
+      return [keyValue[1], keyValue[2]];
+    })
+    .filter(kv => kv !== null);
+}
+
 /**
  * Build context for rendering templates in the defined views directory.
  *
@@ -106,16 +127,13 @@ function serveDev(port, config) {
 
   // Serve HTML documents with injected client script
   app.get('/document/:document', (req, res, next) => {
-    // All HTML documents are served with the `text/html` mime type by default,
-    // even though some declare themselves to be XHTML. This mirrors how most
-    // content on the web is served. However we do have some test pages that
-    // need to be served with the correct XHTML mime type, as that alters
-    // browser behavior. See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/XHTML.
-    if (req.params.document.startsWith('xhtml-')) {
-      res.set('Content-Type', 'application/xhtml+xml');
-    }
-
-    if (fs.existsSync(`${HTML_PATH}${req.params.document}.mustache`)) {
+    const path = `${HTML_PATH}${req.params.document}.mustache`;
+    if (fs.existsSync(path)) {
+      const content = fs.readFileSync(path, 'utf8');
+      const headers = readCustomHeaderTags(content);
+      for (let [key, value] of headers) {
+        res.set(key, value);
+      }
       res.render(req.params.document, templateContext(config));
     } else {
       next();