From 42e8b189dcbd1f2b0b38180fe36ec9cc0c944dec Mon Sep 17 00:00:00 2001
From: Andy Green <andy@warmcat.com>
Date: Fri, 22 Apr 2016 08:53:49 +0800
Subject: [PATCH] http cache policy

This allows mounts to define the caching policy of the files inside them.

Support is added in lwsws for controlling it from the config files.

The api for serializing a mount struct opaquely is removed and lws_http_mount struct
made public... it was getting out of control trying to hide the options.

Signed-off-by: Andy Green <andy@warmcat.com>
---
 CMakeLists.txt                           |   2 +-
 README.lwsws.md                          |  21 +++-
 lib/context.c                            |  44 --------
 lib/header.c                             |   7 +-
 lib/libwebsockets.c                      |  12 ++-
 lib/libwebsockets.h                      |  28 +++--
 lib/private-libwebsockets.h              |  18 +---
 lib/server.c                             |  76 ++++++++++++--
 lwsws/conf.c                             |  82 ++++++++++-----
 lwsws/etc-lwsws-conf-EXAMPLE             |   1 +
 lwsws/etc-lwsws-conf.d-localhost-EXAMPLE | 125 +++++++++++++++++++----
 plugins/lwsws-logo.png                   | Bin 0 -> 5172 bytes
 plugins/server-status.html               |  24 +++--
 13 files changed, 303 insertions(+), 137 deletions(-)
 create mode 100644 plugins/lwsws-logo.png

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 66126012..0b244a7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1405,7 +1405,7 @@ if (LWS_WITH_PLUGINS)
 		DESTINATION share/libwebsockets-test-server/plugins
 		COMPONENT plugins)
 if (LWS_WITH_SERVER_STATUS)
-	install(FILES plugins/server-status.html
+	install(FILES plugins/server-status.html;plugins/lwsws-logo.png
 		DESTINATION share/libwebsockets-test-server/server-status
 			COMPONENT examples)
 endif()
diff --git a/README.lwsws.md b/README.lwsws.md
index 15a8bd31..82227fdf 100644
--- a/README.lwsws.md
+++ b/README.lwsws.md
@@ -216,7 +216,13 @@ Mount protocols are used to control what kind of translation happens
 
  would cause the url /git/myrepo to pass "myrepo" to the cgi /var/www/cgi-bin/cgit and send the results to the client.
 
- When using a cgi:// protcol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
+Note: currently only a fixed set of mimetypes are supported.
+
+
+Other mount options
+-------------------
+
+1) When using a cgi:// protcol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
 
 ```
        {
@@ -231,14 +237,23 @@ Mount protocols are used to control what kind of translation happens
 
  This allows you to customize one cgi depending on the mountpoint (and / or vhost).
 
- It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this
+2) It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this
 
 ```
 	"cgi-timeout": "30"
 ```
 
+3) Cache policy of the files in the mount can also be set.  If no
+options are given, the content is marked uncacheable.
 
-Note: currently only a fixed set of mimetypes are supported.
+       {
+        "mountpoint": "/",
+        "origin": "file:///var/www/mysite.com",
+        "cache-max-age": "60",      # seconds
+        "cache-reuse": "1",         # allow reuse at client at all
+        "cache-revalidate": "1",    # check it with server each time
+        "cache-intermediaries": "1" # allow intermediary caches to hold
+       }
 
 
 Plugins
diff --git a/lib/context.c b/lib/context.c
index 8ee5ffa7..d95bbe57 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -49,50 +49,6 @@ static const char * const mount_protocols[] = {
 	">https://",
 };
 
-LWS_VISIBLE LWS_EXTERN int
-lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
-		     void *store, const char *mountpoint, const char *origin,
-		     const char *def, struct lws_protocol_vhost_options *cgienv,
-		     int cgi_timeout)
-{
-	struct lws_http_mount *m;
-	void *orig = store;
-	unsigned long l = (unsigned long)store;
-	int n;
-
-	if (l & 15)
-		l += 16 - (l & 15);
-
-	store = (void *)l;
-	m = (struct lws_http_mount *)store;
-	*res = m;
-
-	m->def = def;
-	m->mountpoint = mountpoint;
-	m->mountpoint_len = (unsigned char)strlen(mountpoint);
-	m->mount_next = NULL;
-	m->cgienv = cgienv;
-	m->cgi_timeout = cgi_timeout;
-
-	if (next)
-		next->mount_next = m;
-
-	for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
-		if (!strncmp(origin, mount_protocols[n],
-		     strlen(mount_protocols[n]))) {
-			m->origin_protocol = n;
-			m->origin = origin + strlen(mount_protocols[n]);
-			break;
-		}
-
-	if (n == ARRAY_SIZE(mount_protocols)) {
-		lwsl_err("unsupported protocol:// %s\n", origin);
-		return 0; /* ie, fail */
-	}
-
-	return ((char *)store + sizeof(*m)) - (char *)orig;
-}
-
 LWS_VISIBLE void *
 lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot,
 			    int size)
diff --git a/lib/header.c b/lib/header.c
index 70058d19..9bc4b7df 100644
--- a/lib/header.c
+++ b/lib/header.c
@@ -167,8 +167,11 @@ lws_add_http_header_status(struct lws *wsi, unsigned int code,
 	if (code == 200)
 		description = "OK";
 
-	if (code >= 300 && code < 400)
-		description = "Redirect";
+	if (code == 304)
+		description = "Not Modified";
+	else
+		if (code >= 300 && code < 400)
+			description = "Redirect";
 
 	if (wsi->u.http.request_version < ARRAY_SIZE(hver))
 		p1 = hver[wsi->u.http.request_version];
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 43f367d6..a4b3fbc1 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -2395,11 +2395,19 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 				buf += snprintf(buf, end - buf, ",");
 			buf += snprintf(buf, end - buf,
 					"\n  {\n   \"mountpoint\":\"%s\",\n"
-					"  \"origin\":\"%s%s\"\n"
+					"  \"origin\":\"%s%s\",\n"
+					"  \"cache_max_age\":\"%d\",\n"
+					"  \"cache_reuse\":\"%d\",\n"
+					"  \"cache_revalidate\":\"%d\",\n"
+					"  \"cache_intermediaries\":\"%d\"\n"
 					,
 					m->mountpoint,
 					prots[m->origin_protocol],
-					m->origin);
+					m->origin,
+					m->cache_max_age,
+					m->cache_reusable,
+					m->cache_revalidate,
+					m->cache_intermediaries);
 			if (m->def)
 				buf += snprintf(buf, end - buf,
 						",\n  \"default\":\"%s\"",
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 6dd32a5e..78832d74 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1367,6 +1367,25 @@ struct lws_protocol_vhost_options {
 	const char *value;
 };
 
+struct lws_http_mount {
+	struct lws_http_mount *mount_next;
+	const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
+	const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
+	const char *def; /* default target, eg, "index.html" */
+
+	struct lws_protocol_vhost_options *cgienv;
+
+	int cgi_timeout;
+	int cache_max_age;
+
+	unsigned int cache_reusable:1;
+	unsigned int cache_revalidate:1;
+	unsigned int cache_intermediaries:1;
+
+	unsigned char origin_protocol;
+	unsigned char mountpoint_len;
+};
+
 /**
  * struct lws_context_creation_info - parameters to create context with
  *
@@ -1553,8 +1572,6 @@ struct lws_client_connect_info {
 	void *_unused[4];
 };
 
-struct lws_http_mount;
-
 enum {
 	LWSMPRO_HTTP,
 	LWSMPRO_HTTPS,
@@ -1564,13 +1581,6 @@ enum {
 	LWSMPRO_REDIR_HTTPS,
 };
 
-LWS_VISIBLE LWS_EXTERN int
-lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
-		     void *store, const char *mountpoint, const char *origin,
-		     const char *def,
-		     struct lws_protocol_vhost_options *cgienv,
-		     int cgi_timeout);
-
 LWS_EXTERN int
 lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len);
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 82146706..5c3e4cc9 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -619,20 +619,6 @@ struct lws_context_per_thread {
 	unsigned char tid;
 };
 
-struct lws_http_mount {
-	struct lws_http_mount *mount_next;
-	const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
-	const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
-	const char *def; /* default target, eg, "index.html" */
-
-	struct lws_protocol_vhost_options *cgienv;
-
-	int cgi_timeout;
-
-	unsigned char origin_protocol;
-	unsigned char mountpoint_len;
-};
-
 /*
  * virtual host -related context information
  *   vhostwide SSL context
@@ -1252,6 +1238,7 @@ struct lws {
 #ifndef LWS_NO_CLIENT
 	int chunk_remaining;
 #endif
+	unsigned int cache_secs;
 
 	unsigned int hdr_parsing_completed:1;
 	unsigned int http2_substream:1;
@@ -1261,6 +1248,9 @@ struct lws {
 	unsigned int rxflow_change_to:2;
 	unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
 	unsigned int conn_stat_done:1;
+	unsigned int cache_reuse:1;
+	unsigned int cache_revalidate:1;
+	unsigned int cache_intermediaries:1;
 #ifdef LWS_WITH_ACCESS_LOG
 	unsigned int access_log_pending:1;
 #endif
diff --git a/lib/server.c b/lib/server.c
index 5eafb583..94621f5c 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -133,7 +133,7 @@ lws_context_init_server(struct lws_context_creation_info *info,
 
 #if LWS_POSIX
 	listen(wsi->sock, LWS_SOMAXCONN);
-	} /* for each thread able to independently lister */
+	} /* for each thread able to independently listen */
 #else
 	mbed3_tcp_stream_bind(wsi->sock, info->port, wsi);
 #endif
@@ -231,9 +231,10 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 	const char *mimetype;
 	struct stat st;
 	char path[256], sym[256];
+	unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
+	unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
 	int n, spin = 0;
 
-	lwsl_notice("%s: %s %s\n", __func__, uri, origin);
 	snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
 
 	do {
@@ -263,17 +264,54 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 
 	} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
 
-	if (spin == 5) {
+	if (spin == 5)
 		lwsl_err("symlink loop %s \n", path);
+
+	n = sprintf(sym, "%08lX%08lX", (unsigned long)st.st_size,
+				   (unsigned long)st.st_mtime);
+
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
+		/*
+		 * he thinks he has some version of it already,
+		 * check if the tag matches
+		 */
+		if (!strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
+
+			lwsl_notice("%s: ETAG match %s %s\n", __func__,
+				    uri, origin);
+
+			/* we don't need to send the payload */
+			if (lws_add_http_header_status(wsi, 304, &p, end))
+				return -1;
+			if (lws_add_http_header_by_token(wsi,
+					WSI_TOKEN_HTTP_ETAG,
+					(unsigned char *)sym, n, &p, end))
+				return -1;
+			if (lws_finalize_http_header(wsi, &p, end))
+				return -1;
+
+			n = lws_write(wsi, start, p - start,
+					LWS_WRITE_HTTP_HEADERS);
+			if (n != (p - start)) {
+				lwsl_err("_write returned %d from %d\n", n, p - start);
+				return -1;
+			}
+
+			return lws_http_transaction_completed(wsi);
+		}
 	}
 
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
+			(unsigned char *)sym, n, &p, end))
+		return -1;
+
 	mimetype = get_mimetype(path);
 	if (!mimetype) {
 		lwsl_err("unknown mimetype for %s", path);
 		goto bail;
 	}
 
-	n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
+	n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start);
 
 	if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
 		return -1; /* error or can't reuse connection: close the socket */
@@ -620,11 +658,13 @@ lws_http_action(struct lws *wsi)
 		n = strlen(s);
 		if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
 			s = (char *)hit->def;
-
 		if (!s)
 			s = "index.html";
 
-			// lwsl_err("okok\n");
+		wsi->cache_secs = hit->cache_max_age;
+		wsi->cache_reuse = hit->cache_reusable;
+		wsi->cache_revalidate = hit->cache_revalidate;
+		wsi->cache_intermediaries = hit->cache_intermediaries;
 
 		n = lws_http_serve(wsi, s, hit->origin);
 	} else
@@ -1589,12 +1629,14 @@ LWS_VISIBLE int
 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 		    const char *other_headers, int other_headers_len)
 {
+	static const char * const intermediates[] = { "private", "public" };
 	struct lws_context *context = lws_get_context(wsi);
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+	char cache_control[50], *cc = "no-store";
 	unsigned char *response = pt->serv_buf + LWS_PRE;
 	unsigned char *p = response;
 	unsigned char *end = p + LWS_MAX_SOCKET_IO_BUF - LWS_PRE;
-	int ret = 0;
+	int ret = 0, cclen = 8;
 
 	wsi->u.http.fd = lws_plat_file_open(wsi, file, &wsi->u.http.filelen,
 					    O_RDONLY);
@@ -1608,10 +1650,6 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 
 	if (lws_add_http_header_status(wsi, 200, &p, end))
 		return -1;
-	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
-					 (unsigned char *)"libwebsockets", 13,
-					 &p, end))
-		return -1;
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
 					 (unsigned char *)content_type,
 					 strlen(content_type), &p, end))
@@ -1619,6 +1657,22 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 	if (lws_add_http_header_content_length(wsi, wsi->u.http.filelen, &p, end))
 		return -1;
 
+	if (wsi->cache_secs && wsi->cache_reuse) {
+		if (wsi->cache_revalidate) {
+			cc = cache_control;
+			cclen = sprintf(cache_control, "%s max-age: %u",
+				    intermediates[wsi->cache_intermediaries],
+				    wsi->cache_secs);
+		} else {
+			cc = "no-cache";
+			cclen = 8;
+		}
+	}
+
+	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CACHE_CONTROL,
+			(unsigned char *)cc, cclen, &p, end))
+		return -1;
+
 	if (other_headers) {
 		if ((end - p) < other_headers_len)
 			return -1;
diff --git a/lwsws/conf.c b/lwsws/conf.c
index 6a391951..e3a1ada0 100644
--- a/lwsws/conf.c
+++ b/lwsws/conf.c
@@ -54,6 +54,10 @@ static const char * const paths_vhosts[] = {
 	"vhosts[].mounts[].default",
 	"vhosts[].mounts[].cgi-timeout",
 	"vhosts[].mounts[].cgi-env[].*",
+	"vhosts[].mounts[].cache-max-age",
+	"vhosts[].mounts[].cache-reuse",
+	"vhosts[].mounts[].cache-revalidate",
+	"vhosts[].mounts[].cache-intermediaries",
 	"vhosts[].ws-protocols[].*.*",
 	"vhosts[].ws-protocols[].*",
 	"vhosts[].ws-protocols[]",
@@ -77,6 +81,10 @@ enum lejp_vhost_paths {
 	LEJPVP_DEFAULT,
 	LEJPVP_CGI_TIMEOUT,
 	LEJPVP_CGI_ENV,
+	LEJPVP_MOUNT_CACHE_MAX_AGE,
+	LEJPVP_MOUNT_CACHE_REUSE,
+	LEJPVP_MOUNT_CACHE_REVALIDATE,
+	LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
 	LEJPVP_PROTOCOL_NAME_OPT,
 	LEJPVP_PROTOCOL_NAME,
 	LEJPVP_PROTOCOL,
@@ -90,10 +98,9 @@ struct jpargs {
 	const struct lws_extension *extensions;
 	char *p, *end, valid;
 	struct lws_http_mount *head, *last;
-	char *mountpoint, *origin, *def;
+
 	struct lws_protocol_vhost_options *pvo;
-	struct lws_protocol_vhost_options *mp_cgienv;
-	int cgi_timeout;
+	struct lws_http_mount m;
 };
 
 static void *
@@ -205,13 +212,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	}
 
 	if (reason == LEJPCB_OBJECT_START &&
-	    ctx->path_match == LEJPVP_MOUNTS + 1) {
-		a->mountpoint = NULL;
-		a->origin = NULL;
-		a->def = NULL;
-		a->mp_cgienv = NULL;
-		a->cgi_timeout = 0;
-	}
+	    ctx->path_match == LEJPVP_MOUNTS + 1)
+		memset(&a->m, 0, sizeof(a->m));
 
 	/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
 	if (reason == LEJPCB_OBJECT_START &&
@@ -254,17 +256,38 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 
 	if (reason == LEJPCB_OBJECT_END &&
 	    ctx->path_match == LEJPVP_MOUNTS + 1) {
-		if (!a->mountpoint || !a->origin) {
+		static const char * const mount_protocols[] = {
+			"http://",
+			"https://",
+			"file://",
+			"cgi://",
+			">http://",
+			">https://",
+		};
+
+		if (!a->m.mountpoint || !a->m.origin) {
 			lwsl_err("mountpoint and origin required\n");
 			return 1;
 		}
+		m = lwsws_align(a);
+		memcpy(m, &a->m, sizeof(*m));
+		if (a->last)
+			a->last->mount_next = m;
 
-		n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint,
-					 a->origin, a->def, a->mp_cgienv,
-					 a->cgi_timeout);
-		if (!n)
+		for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
+			if (!strncmp(a->m.origin, mount_protocols[n],
+			     strlen(mount_protocols[n]))) {
+				m->origin_protocol = n;
+				m->origin = a->m.origin + strlen(mount_protocols[n]);
+				break;
+			}
+
+		if (n == ARRAY_SIZE(mount_protocols)) {
+			lwsl_err("unsupported protocol:// %s\n", a->m.origin);
 			return 1;
-		a->p += n;
+		}
+
+		a->p += sizeof(*m);
 		if (!a->head)
 			a->head = m;
 
@@ -310,26 +333,39 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->info->log_filepath = a->p;
 		break;
 	case LEJPVP_MOUNTPOINT:
-		a->mountpoint = a->p;
+		a->m.mountpoint = a->p;
+		a->m.mountpoint_len = strlen(ctx->buf);
 		break;
 	case LEJPVP_ORIGIN:
-		a->origin = a->p;
+		a->m.origin = a->p;
 		break;
 	case LEJPVP_DEFAULT:
-		a->def = a->p;
+		a->m.def = a->p;
 		break;
+	case LEJPVP_MOUNT_CACHE_MAX_AGE:
+		a->m.cache_max_age = atoi(ctx->buf);
+		return 0;
+	case LEJPVP_MOUNT_CACHE_REUSE:
+		a->m.cache_reusable = arg_to_bool(ctx->buf);
+		return 0;
+	case LEJPVP_MOUNT_CACHE_REVALIDATE:
+		a->m.cache_revalidate = arg_to_bool(ctx->buf);
+		return 0;
+	case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
+		a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
+		return 0;
 	case LEJPVP_CGI_TIMEOUT:
-		a->cgi_timeout = atoi(ctx->buf);
+		a->m.cgi_timeout = atoi(ctx->buf);
 		return 0;
 	case LEJPVP_KEEPALIVE_TIMEOUT:
 		a->info->keepalive_timeout = atoi(ctx->buf);
 		return 0;
 	case LEJPVP_CGI_ENV:
 		mp_cgienv = lwsws_align(a);
-		a->p += sizeof(*a->mp_cgienv);
+		a->p += sizeof(*a->m.cgienv);
 
-		mp_cgienv->next = a->mp_cgienv;
-		a->mp_cgienv = mp_cgienv;
+		mp_cgienv->next = a->m.cgienv;
+		a->m.cgienv = mp_cgienv;
 
 		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
 		mp_cgienv->name = a->p;
diff --git a/lwsws/etc-lwsws-conf-EXAMPLE b/lwsws/etc-lwsws-conf-EXAMPLE
index e6184d98..8cdf5d8a 100644
--- a/lwsws/etc-lwsws-conf-EXAMPLE
+++ b/lwsws/etc-lwsws-conf-EXAMPLE
@@ -8,6 +8,7 @@
    "gid": "48",
    "interface": "eth0",
    "count-threads": "1",
+   "server-string": "lwsws",
    "init-ssl": "yes"
  }
 }
diff --git a/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE
index 43d0489d..86fad5ec 100644
--- a/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE
+++ b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE
@@ -1,28 +1,40 @@
+# comment
+
 {
  "vhosts": [ {
-     "name": "localhost",
-     "port": "80",
+     "name": "libwebsockets.org",
+     "port": "443",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "access-log": "/var/log/httpd/lws-access-log",
+     "sts": "on",
      "mounts": [{
        "mountpoint": "/",
        "origin": "file:///var/www/libwebsockets.org",
-       "default": "index.html"
+       "default": "index.html",
+       "cache-max-age": "60",
+       "cache-reuse": "1",
+       "cache-revalidate": "1",
+       "cache-intermediaries": "0"
        }, {
+        # this hooks us up to cgit cgi part
         "mountpoint": "/git",
         "origin": "cgi:///var/www/cgi-bin/cgit",
         "default": "/",
-        "cgi-env": [{
-                "CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org"
-        }]
+	"cgi-env": [{
+		"CGIT_CONFIG": "/etc/cgitrc/git.libwebsockets.org"
+	}],
+	# we can also set up per-cgi process timeout
+	"cgi-timeout": "30"
        }, {
-        "mountpoint": "/cgit-data",
+        # this hooks us up to cgit static assets
+        "mountpoint": "/git/cgit-data",
         "origin": "file:///usr/share/cgit",
         "default": "/"
         }, {
-        "mountpoint": "/testcgi",
-        "origin": "cgi:///usr/local/share/libwebsockets-test-server/lws-cgi-test.sh"
-       }, {
         "mountpoint": "/mailman",
-        "origin": ">http://localhost/mailman/listinfo"
+        "origin": ">https://libwebsockets.org/mailman/listinfo"
        }, {
         "mountpoint": "/mailman/listinfo",
         "origin": "cgi:///usr/lib/mailman/cgi-bin/listinfo"
@@ -59,41 +71,110 @@
        }, {
         "mountpoint": "/pipermail",
         "origin": "file:///var/lib/mailman/archives/public",
-        "default": "index.html"
+	"default": "index.html"
+       }, {
+        # we used to have a trac, redirect anyone using it to github
+        "mountpoint": "/trac",
+        "origin": ">https://github.com/warmcat/libwebsockets"
+       }, {
+        "mountpoint": "/server-status",
+        "origin": "file:///usr/local/share/libwebsockets-test-server/server-status",
+        "default": "server-status.html"
        }, {
         "mountpoint": "/testserver",
         "origin": "file:///usr/local/share/libwebsockets-test-server",
         "default": "test.html"
-       }],
+       }
+     ],
      # which protocols are enabled for this vhost, and optional
      # vhost-specific config options for the protocol
      #
      "ws-protocols": [{
-       "warmcat,timezoom": {
+       "dumb-increment-protocol": {
          "status": "ok"
+       },
+       "lws-mirror-protocol": {
+         "status": "ok"
+       },
+       "lws-status": {
+         "status": "ok"
+       },
+       "lws-server-status": {
+         "status": "ok",
+	 "update-ms": "5000"
        }
+     }],
+     "ws-extensions": [{
+       "extension": "permessage-deflate"
      }]
-    },
+   },
+
+	# redirect any guys coming in on http to https
     {
-    "name": "localhost",
+     "name": "libwebsockets.org",
+     "port": "80",
+     "sts": "on",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://libwebsockets.org"
+     }]
+   },
+    {
+	# the old test server ran this on :7681, put a redirect
+	# there to take us to the new location
+    "name": "libwebsockets.org",
     "port": "7681",
      "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
      "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
      "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
      "mounts": [{
        "mountpoint": "/",
-       "origin": ">https://localhost"
+       "origin": ">https://libwebsockets.org/testserver/"
      }]
-   },
+  },
+
+	# old site for mailing list redirect to new one
     {
-    "name": "localhostx",
+    "name": "ml.libwebsockets.org",
     "port": "80",
      "mounts": [{
        "mountpoint": "/",
-       "origin": ">https://localhost"
+       "origin": ">https://libwebsockets.org/mailman"
+     }]
+   },
+	# old site for mailing list redirect to new one
+    {
+    "name": "ml.libwebsockets.org",
+    "port": "443",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://libwebsockets.org/mailman"
+     }]
+   },
+
+
+	# redirect any guys coming in on http to https
+    {
+    "name": "git.libwebsockets.org",
+    "port": "80",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://libwebsockets.org/git"
+     }]
+   },
+    {
+	# the old test server ran this on :7681, put a redirect
+	# there to take us to the new location
+    "name": "git.libwebsockets.org",
+    "port": "443",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://libwebsockets.org/git"
      }]
    }
-
-  ]
+ }]
 }
 
diff --git a/plugins/lwsws-logo.png b/plugins/lwsws-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..4e4188bc9cfdda7852c837d851c1300922f97800
GIT binary patch
literal 5172
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

literal 0
HcmV?d00001

diff --git a/plugins/server-status.html b/plugins/server-status.html
index 3ba4b9e2..155ac2c6 100644
--- a/plugins/server-status.html
+++ b/plugins/server-status.html
@@ -98,7 +98,9 @@
 <header></header>
 <article>
 
-<table><tr><td align=center>
+<table>
+<tr><td><img src="./lwsws-logo.png"></td><td><span id=title class=title>Server status</span></td></tr>
+<tr><td align=center colspan=2>
 <div id="conninfo">...</div>
 </td></tr>
 
@@ -164,7 +166,8 @@ function get_appropriate_ws_url()
 
 	try {
 		socket_status.onopen = function() {
-		} 
+			document.getElementById("title").innerHTML = "Server Status (Active)";
+		}
 
 		socket_status.onmessage =function got_packet(msg) {
 			document.getElementById("conninfo").innerHTML = "<pre>"+msg.data+"</pre>";
@@ -210,14 +213,24 @@ function get_appropriate_ws_url()
 				"total http transactions " + san(jso.vhosts[n].trans) + "<br>" +
 				"Upgrades to ws: " + san(jso.vhosts[n].ws_upg) + ", " +
 				"to http/2: " + san(jso.vhosts[n].http2_upg) + "<br>" +
-				"<table><tr><td class=t colspan=2>Mounts</td></tr>";
+				"<table><tr><td class=t>Mountpoint</td><td class=t>Origin</td><td class=t>Cache Policy</td></tr>";
 				
 				var m;
 				for (m = 0; m < jso.vhosts[n].mounts.length; m++) {
 					s = s + "<tr><td>";
 					s = s + san(jso.vhosts[n].mounts[m].mountpoint) +
 						"</td><td>" +
-						san(jso.vhosts[n].mounts[m].origin);
+						san(jso.vhosts[n].mounts[m].origin) +
+						"</td><td>";
+					if (parseInt(san(jso.vhosts[n].mounts[m].cache_max_age)))
+						s = s + "max-age: " +
+						san(jso.vhosts[n].mounts[m].cache_max_age) +
+						", reuse: " +
+						san(jso.vhosts[n].mounts[m].cache_reuse) +
+						", reval: " +
+						san(jso.vhosts[n].mounts[m].cache_revalidate) +
+						", inter: " +
+						san(jso.vhosts[n].mounts[m].cache_intermediaries);
 					s = s + "</td></tr>"
 				}
 				s = s + "</table>";
@@ -229,8 +242,7 @@ function get_appropriate_ws_url()
 		} 
 
 		socket_status.onclose = function(){
-			document.getElementById("s_statustd").style.backgroundColor = "#ff4040";
-			document.getElementById("s_status").textContent = " websocket connection CLOSED ";
+			document.getElementById("title").innerHTML = "Server Status (Disconnected)";
 		}
 	} catch(exception) {
 		alert('<p>Error' + exception);