diff --git a/README.lwsws.md b/README.lwsws.md
index a49ee01fc3fe226833da1c8d2806b8d5c5fa9bfa..da109e850f9d2d86a3bfdcc0405179a16c12a238 100644
--- a/README.lwsws.md
+++ b/README.lwsws.md
@@ -15,8 +15,9 @@ It enables libuv and plugin support automatically.
 Configuration
 -------------
 
-lwsws uses JSON config files, there is a single file intended for global
-settings
+lwsws uses JSON config files, they're pure JSON but # may be used to turn the rest of the line into a comment.
+
+There is a single file intended for global settings
 
 /etc/lwsws/conf
 
@@ -189,7 +190,14 @@ 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).
 
-Currently only a fixed set of mimetypes are supported.
+ It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this
+
+```
+	"cgi-timeout": "30"
+```
+
+
+Note: currently only a fixed set of mimetypes are supported.
 
 
 Plugins
diff --git a/lib/context.c b/lib/context.c
index d8a04a8c67a98588f0724f7a9acc6b81ce7ea9a4..85d70947d93b86880710e53d5c7eb0a9dfbdd4df 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -52,7 +52,8 @@ static const char * const mount_protocols[] = {
 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)
+		     const char *def, struct lws_protocol_vhost_options *cgienv,
+		     int cgi_timeout)
 {
 	struct lws_http_mount *m;
 	void *orig = store;
@@ -71,6 +72,8 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
 	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;
 
@@ -83,7 +86,7 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
 		}
 
 	if (n == ARRAY_SIZE(mount_protocols)) {
-		lwsl_err("unsupported protocol://\n");
+		lwsl_err("unsupported protocol:// %s\n", origin);
 		return 0; /* ie, fail */
 	}
 
@@ -586,6 +589,8 @@ lws_create_context(struct lws_context_creation_info *info)
 
 	lws_context_init_ssl_library(info);
 
+	context->user_space = info->user;
+
 	/*
 	 * if he's not saying he'll make his own vhosts later then act
 	 * compatibly and make a default vhost using the data in the info
@@ -598,8 +603,6 @@ lws_create_context(struct lws_context_creation_info *info)
 
 	lws_context_init_extensions(info, context);
 
-	context->user_space = info->user;
-
 	lwsl_notice(" mem: per-conn:        %5u bytes + protocol rx buf\n",
 		    sizeof(struct lws));
 
diff --git a/lib/daemonize.c b/lib/daemonize.c
index 5fbd60a75d4a16346e9e0b2ce949ff5f620683e1..e91629497785d3f79e4e4fd318a82b533cf18784 100644
--- a/lib/daemonize.c
+++ b/lib/daemonize.c
@@ -173,7 +173,7 @@ lws_daemonize(const char *_lock_path)
 	 * Change the current working directory.  This prevents the current
 	 * directory from being locked; hence not being able to remove it.
 	 */
-	if (chdir("/") < 0) {
+	if (chdir("/tmp") < 0) {
 		fprintf(stderr,
 			"unable to change directory to %s, code %d (%s)",
 			"/", errno, strerror(errno));
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index b85e6e9b2f874a9232ac7a3ca23b03434acb10e5..5fe0bdc8419ad8d022850a9c7f1e32768fa088cd 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -530,6 +530,17 @@ lws_close_free_wsi_final(struct lws *wsi)
 	wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
 				       wsi->user_space, NULL, 0);
 
+#ifdef LWS_WITH_CGI
+	if (wsi->cgi) {
+		for (n = 0; n < 6; n++)
+			if (wsi->cgi->pipe_fds[n / 2][n & 1] >= 0) {
+				lwsl_notice("   closing %d\n", wsi->cgi->pipe_fds[n / 2][n & 1]);
+				close(wsi->cgi->pipe_fds[n / 2][n & 1]);
+			}
+		lws_free(wsi->cgi);
+	}
+#endif
+
 	lws_free_wsi(wsi);
 }
 
@@ -1644,7 +1655,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	char *env_array[30], cgi_path[400], e[1024], *p = e,
 	     *end = p + sizeof(e) - 1, tok[256], *t;
 	struct lws_cgi *cgi;
-	int n, m, i;
+	int n, m, i, uritok = WSI_TOKEN_GET_URI;
 
 	/*
 	 * give the master wsi a cgi struct
@@ -1674,6 +1685,9 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 		cgi->stdwsi[n]->cgi_channel = n;
 		cgi->stdwsi[n]->vhost = wsi->vhost;
 
+		lwsl_err("%s: cgi %p: pipe fd %d -> fd %d / %d\n", __func__, wsi, n,
+			 cgi->pipe_fds[n][!!(n == 0)], cgi->pipe_fds[n][!(n == 0)]);
+
 		/* read side is 0, stdin we want the write side, others read */
 		cgi->stdwsi[n]->sock = cgi->pipe_fds[n][!!(n == 0)];
 		fcntl(cgi->pipe_fds[n][!!(n == 0)], F_SETFL, O_NONBLOCK);
@@ -1712,11 +1726,13 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	if (lws_is_ssl(wsi))
 		env_array[n++] = "HTTPS=ON";
 	if (wsi->u.hdr.ah) {
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+			uritok = WSI_TOKEN_POST_URI;
 		snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
-			 lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
+			 lws_hdr_simple_ptr(wsi, uritok));
 		cgi_path[sizeof(cgi_path) - 1] = '\0';
 		env_array[n++] = cgi_path;
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+		if (uritok == WSI_TOKEN_POST_URI)
 			env_array[n++] = "REQUEST_METHOD=POST";
 		else
 			env_array[n++] = "REQUEST_METHOD=GET";
@@ -1748,7 +1764,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 
 		env_array[n++] = p;
 		p += snprintf(p, end - p, "PATH_INFO=%s",
-			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI) +
+			      lws_hdr_simple_ptr(wsi, uritok) +
 			      script_uri_path_len);
 		p++;
 	}
@@ -1764,12 +1780,32 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
 		p++;
 	}
+	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
+		env_array[n++] = p;
+		p += snprintf(p, end - p, "HTTP_COOKIE=%s",
+			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COOKIE));
+		p++;
+	}
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT)) {
 		env_array[n++] = p;
 		p += snprintf(p, end - p, "USER_AGENT=%s",
 			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_USER_AGENT));
 		p++;
 	}
+	if (uritok == WSI_TOKEN_POST_URI) {
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
+			env_array[n++] = p;
+			p += snprintf(p, end - p, "CONTENT_TYPE=%s",
+				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE));
+			p++;
+		}
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
+			env_array[n++] = p;
+			p += snprintf(p, end - p, "CONTENT_LENGTH=%s",
+				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH));
+			p++;
+		}
+	}
 	env_array[n++] = p;
 	p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
 
@@ -1808,7 +1844,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 		return 0;
 
 	/* somewhere we can at least read things and enter it */
-	if (chdir("/"))
+	if (chdir("/tmp"))
 		lwsl_notice("%s: Failed to chdir\n", __func__);
 
 	/* We are the forked process, redirect and kill inherited things.
@@ -1818,13 +1854,11 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 	 * process is OK.  Stuff that happens after the execvpe() is OK.
 	 */
 
-	for (n = 0; n < 3; n++) {
+	for (n = 0; n < 3; n++)
 		if (dup2(cgi->pipe_fds[n][!(n == 0)], n) < 0) {
 			lwsl_err("%s: stdin dup2 failed\n", __func__);
 			goto bail3;
 		}
-		close(cgi->pipe_fds[n][!(n == 0)]);
-	}
 
 #if !defined(LWS_HAVE_VFORK) || !defined(LWS_HAVE_EXECVPE)
 	for (m = 0; m < n; m++) {
@@ -1872,9 +1906,13 @@ bail1:
 LWS_VISIBLE LWS_EXTERN int
 lws_cgi_write_split_stdout_headers(struct lws *wsi)
 {
-	int n, m;
+	int n, m, match = 0, lp = 0;
+	static const char * const content_length = "content-length: ";
 	char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
-	     *end = &buf[sizeof(buf) - 1 - LWS_PRE], c;
+	     *end = &buf[sizeof(buf) - 1 - LWS_PRE], c, l[12];
+
+	if (!wsi->cgi)
+		return -1;
 
 	while (wsi->hdr_state != LHCS_PAYLOAD) {
 		/* we have to separate header / finalize and
@@ -1883,20 +1921,40 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 		 */
 		n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]), &c, 1);
 		if (n < 0) {
-			if (errno != EAGAIN)
+			if (errno != EAGAIN) {
+				lwsl_debug("%s: read says %d\n", __func__, n);
 				return -1;
+			}
 			else
 				n = 0;
 		}
 		if (n) {
-			//lwsl_err("-- %c\n", c);
+			lwsl_debug("-- 0x%02X %c\n", (unsigned char)c, c);
 			switch (wsi->hdr_state) {
 			case LCHS_HEADER:
+				if (!content_length[match] &&
+				    (c >= '0' && c <= '9') &&
+				    lp < sizeof(l) - 1) {
+					l[lp++] = c;
+					l[lp] = '\0';
+					wsi->cgi->content_length = atol(l);
+				}
+				if (tolower(c) == content_length[match])
+					match++;
+				else
+					match = 0;
+
+				/* some cgi only send us \x0a for EOL */
+				if (c == '\x0a') {
+					wsi->hdr_state = LCHS_SINGLE_0A;
+					*p++ = '\x0d';
+				}
 				*p++ = c;
 				if (c == '\x0d') {
 					wsi->hdr_state = LCHS_LF1;
 					break;
 				}
+
 				break;
 			case LCHS_LF1:
 				*p++ = c;
@@ -1905,17 +1963,24 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 					break;
 				}
 				/* we got \r[^\n]... it's unreasonable */
+				lwsl_debug("%s: funny CRLF 0x%02X\n", __func__, (unsigned char)c);
 				return -1;
+
 			case LCHS_CR2:
 				if (c == '\x0d') {
 					/* drop the \x0d */
 					wsi->hdr_state = LCHS_LF2;
 					break;
 				}
+				wsi->hdr_state = LCHS_HEADER;
+				match = 0;
 				*p++ = c;
 				break;
 			case LCHS_LF2:
+			case LCHS_SINGLE_0A:
+				m = wsi->hdr_state;
 				if (c == '\x0a') {
+					lwsl_err("Content-Length: %ld\n", wsi->cgi->content_length);
 					wsi->hdr_state = LHCS_PAYLOAD;
 					/* drop the \0xa ... finalize will add it if needed */
 					lws_finalize_http_header(wsi,
@@ -1923,8 +1988,13 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 							(unsigned char *)end);
 					break;
 				}
-				/* we got \r\n\r[^\n]... it's unreasonable */
-				return -1;
+				if (m == LCHS_LF2)
+					/* we got \r\n\r[^\n]... it's unreasonable */
+					return -1;
+				/* we got \x0anext header, it's reasonable */
+				*p++ = c;
+				wsi->hdr_state = LCHS_HEADER;
+				break;
 			case LHCS_PAYLOAD:
 				break;
 			}
@@ -1935,8 +2005,10 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 
 			m = lws_write(wsi, (unsigned char *)start,
 				      p - start, LWS_WRITE_HTTP_HEADERS);
-			if (m < 0)
+			if (m < 0) {
+				lwsl_debug("%s: write says %d\n", __func__, m);
 				return -1;
+			}
 			/* writeability becomes uncertain now we wrote
 			 * something, we must return to the event loop
 			 */
@@ -1944,18 +2016,22 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 			return 0;
 		}
 	}
-	//lwsl_err("%s: stdout\n", __func__);
+
 	n = read(lws_get_socket_fd(wsi->cgi->stdwsi[LWS_STDOUT]),
 		 start, sizeof(buf) - LWS_PRE);
 
-	if (n < 0 && errno != EAGAIN)
+	if (n < 0 && errno != EAGAIN) {
+		lwsl_debug("%s: stdout read says %d\n", __func__, n);
 		return -1;
+	}
 	if (n > 0) {
-		m = lws_write(wsi, (unsigned char *)start, n,
-			      LWS_WRITE_HTTP);
+		m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP);
 		//lwsl_notice("write %d\n", m);
-		if (m < 0)
+		if (m < 0) {
+			lwsl_debug("%s: stdout write says %d\n", __func__, m);
 			return -1;
+		}
+		wsi->cgi->content_length_seen += m;
 	}
 
 	return 0;
@@ -1979,10 +2055,6 @@ lws_cgi_kill(struct lws *wsi)
 	if (!wsi->cgi)
 		return 0;
 
-//	lwsl_notice("%s: wsi %p\n", __func__, wsi);
-
-	assert(wsi->cgi);
-
 	if (wsi->cgi->pid > 0) {
 		/* kill the process */
 		n = kill(wsi->cgi->pid, SIGTERM);
@@ -2014,16 +2086,6 @@ lws_cgi_kill(struct lws *wsi)
 		pcgi = &(*pcgi)->cgi_list;
 	}
 
-	if (!do_close)
-		for (n = 0 ; n < 3; n++) {
-			if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) {
-				close(wsi->cgi->pipe_fds[n][!!(n == 0)]);
-				wsi->cgi->pipe_fds[n][!!(n == 0)] = -1;
-			}
-		}
-
-	lws_free_set_NULL(wsi->cgi);
-
 	if (do_close) {
 		lwsl_debug("!!!!! %s: do_close\n", __func__);
 		lws_close_free_wsi(wsi, 0);
@@ -2044,8 +2106,15 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt)
 		cgi = *pcgi;
 		pcgi = &(*pcgi)->cgi_list;
 
-		if (cgi->pid > 0 &&
-		    waitpid(cgi->pid, &status, WNOHANG) > 0) {
+		if (cgi->pid <= 0)
+			continue;
+
+		/* wait for stdout to be drained */
+		if (cgi->content_length > cgi->content_length_seen)
+			continue;
+
+		if (waitpid(cgi->pid, &status, WNOHANG) > 0) {
+			lwsl_notice("content length seen: %ld\n", cgi->content_length_seen);
 			cgi->pid = 0;
 			lws_cgi_kill(cgi->wsi);
 			pcgi = &pt->cgi_list;
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 9996cf2ae7e260b84a18d5a00a4b29e1a5286510..b03ce923d692a41228337883ceadaca1b8829eb4 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1551,7 +1551,8 @@ 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);
+		     struct lws_protocol_vhost_options *cgienv,
+		     int cgi_timeout);
 
 LWS_VISIBLE LWS_EXTERN void
 lws_set_log_level(int level,
@@ -2011,6 +2012,7 @@ enum lws_cgi_hdr_state {
 	LCHS_CR2,
 	LCHS_LF2,
 	LHCS_PAYLOAD,
+	LCHS_SINGLE_0A,
 };
 
 struct lws_cgi_args {
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index cff846231cec89ef169bbde4d751b0e86e9b6cf6..c2be3e7a38eff1ee4316761ad0b1b0a4b5c7a609 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -624,6 +624,8 @@ struct lws_http_mount {
 
 	struct lws_protocol_vhost_options *cgienv;
 
+	int cgi_timeout;
+
 	unsigned char origin_protocol;
 	unsigned char mountpoint_len;
 };
@@ -1121,6 +1123,8 @@ struct lws_cgi {
 	struct lws_cgi *cgi_list;
 	struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
 	struct lws *wsi; /* owner */
+	unsigned long content_length;
+	unsigned long content_length_seen;
 	int pipe_fds[3][2];
 	int pid;
 
diff --git a/lib/server.c b/lib/server.c
index aca8ad79fcfbbdc9fad398be059d6d4fb7e753f6..f001b5ddee8eff697fb8c7c77d28926ba7c7f371 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -234,7 +234,7 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 		}
 
 		lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode);
-
+#if !defined(WIN32)
 		if ((S_IFMT & st.st_mode) == S_IFLNK) {
 			if (readlink(path, sym, sizeof(sym))) {
 				lwsl_err("Failed to read link %s\n", path);
@@ -243,7 +243,7 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 			lwsl_debug("symlink %s -> %s\n", path, sym);
 			snprintf(path, sizeof(path) - 1, "%s", sym);
 		}
-
+#endif
 		if ((S_IFMT & st.st_mode) == S_IFDIR) {
 			lwsl_debug("default filename append to dir\n");
 			snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
@@ -409,6 +409,7 @@ lws_http_action(struct lws *wsi)
 	lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
 			wsi->context->timeout_secs);
 #ifdef LWS_OPENSSL_SUPPORT
+#if 0
 	if (wsi->redirect_to_https) {
 		/*
 		 * we accepted http:// only so we could redirect to
@@ -435,6 +436,7 @@ lws_http_action(struct lws *wsi)
 
 		return lws_http_transaction_completed(wsi);
 	}
+#endif
 #endif
 
 	/* can we serve it from the mount list? */
@@ -447,7 +449,9 @@ lws_http_action(struct lws *wsi)
 		     uri_ptr[hm->mountpoint_len] == '/' ||
 		     hm->mountpoint_len == 1)
 		    ) {
-			if (hm->mountpoint_len > best) {
+			if ((hm->origin_protocol == LWSMPRO_CGI ||
+			     lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) &&
+			    hm->mountpoint_len > best) {
 				best = hm->mountpoint_len;
 				hit = hm;
 			}
@@ -476,14 +480,15 @@ lws_http_action(struct lws *wsi)
 		 * understands he is one "directory level" down.
 		 */
 		if ((hit->mountpoint_len > 1 || (hit->origin_protocol & 4)) &&
-		    (*s != '/' || (hit->origin_protocol & 4))) {
+		    (*s != '/' || (hit->origin_protocol & 4)) &&
+		    (hit->origin_protocol != LWSMPRO_CGI)) {
 			unsigned char *start = pt->serv_buf + LWS_PRE,
 					      *p = start, *end = p + 512;
 			static const char *oprot[] = {
 				"http://", "https://"
 			};
 
-			// lwsl_err("inin '%s'\n", s);
+			 lwsl_err("Doing 301 '%s' org %s\n", s, hit->origin);
 
 			if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
 				goto bail_nuke_ah;
@@ -527,7 +532,12 @@ lws_http_action(struct lws *wsi)
 
 			lwsl_debug("%s: cgi\n", __func__);
 			cmd[0] = hit->origin;
-			n = lws_cgi(wsi, cmd, hit->mountpoint_len, 5,
+
+			n = 5;
+			if (hit->cgi_timeout)
+				n = hit->cgi_timeout;
+
+			n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
 				    hit->cgienv);
 			if (n) {
 				lwsl_err("%s: cgi failed\n");
@@ -545,7 +555,7 @@ lws_http_action(struct lws *wsi)
 				      p - (buffer + LWS_PRE),
 				      LWS_WRITE_HTTP_HEADERS);
 
-			return 0;
+			goto deal_body;
 		}
 #endif
 
@@ -569,6 +579,9 @@ lws_http_action(struct lws *wsi)
 		return 1;
 	}
 
+#ifdef LWS_WITH_CGI
+deal_body:
+#endif
 	/*
 	 * If we're not issuing a file, check for content_length or
 	 * HTTP keep-alive. No keep-alive header allocation for
@@ -611,7 +624,11 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 	while (len--) {
 		wsi->more_rx_waiting = !!len;
 
-		assert(wsi->mode == LWSCM_HTTP_SERVING);
+		if (wsi->mode != LWSCM_HTTP_SERVING &&
+		    wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
+			lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
+			goto bail_nuke_ah;
+		}
 
 		if (lws_parse(wsi, *(*buf)++)) {
 			lwsl_info("lws_parse failed\n");
diff --git a/lib/service.c b/lib/service.c
index 2e66812bbc0bd0dc87023dc0353337317308c891..6ebe7140b73882208a0d6e7431bfaf5aa4b91bac 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -53,6 +53,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 #endif
 	int ret, m, n;
 
+	//lwsl_err("%s: %p\n", __func__, wsi);
+
 	/*
 	 * user callback is lowest priority to get these notifications
 	 * actually, since other pending things cannot be disordered
@@ -94,6 +96,12 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 		return 0; /* leave POLLOUT active */
 	}
 #endif
+
+#ifdef LWS_WITH_CGI
+	if (wsi->cgi)
+		goto user_service_go_again;
+#endif
+
 	/* Priority 3: pending control packets (pong or close)
 	 */
 	if ((wsi->state == LWSS_ESTABLISHED &&
@@ -239,9 +247,14 @@ user_service:
 			return 1;
 		}
 
+
 	if (!wsi->hdr_parsing_completed)
 		return 0;
 
+#ifdef LWS_WITH_CGI
+user_service_go_again:
+#endif
+
 #ifdef LWS_USE_HTTP2
 	/*
 	 * we are the 'network wsi' for potentially many muxed child wsi with
@@ -738,9 +751,17 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 	case LWSCM_HTTP_SERVING_ACCEPTED:
 	case LWSCM_SERVER_LISTENER:
 	case LWSCM_SSL_ACK_PENDING:
-		if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED) {
+		if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED)
+			goto handled;
+
+#ifdef LWS_WITH_CGI
+		if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) {
+			n = lws_handle_POLLOUT_event(wsi, pollfd);
+			if (n)
+				goto close_and_handled;
 			goto handled;
 		}
+#endif
 		n = lws_server_socket_service(context, wsi, pollfd);
 		if (n) /* closed by above */
 			return 1;
@@ -995,6 +1016,9 @@ drain:
 			args.stdwsi = &wsi->parent->cgi->stdwsi[0];
 			args.hdr_state = wsi->hdr_state;
 
+			//lwsl_err("CGI LWS_STDOUT waiting wsi %p mode %d state %d\n",
+			//	 wsi->parent, wsi->parent->mode, wsi->parent->state);
+
 			if (user_callback_handle_rxflow(
 					wsi->parent->protocol->callback,
 					wsi->parent, LWS_CALLBACK_CGI,
diff --git a/lwsws/conf.c b/lwsws/conf.c
index 1cbc5ede40aa88bba8212aa2f151f280842f0aa9..3bb1d5b9445460ce96c08ea223e0235df66f564c 100644
--- a/lwsws/conf.c
+++ b/lwsws/conf.c
@@ -48,6 +48,7 @@ static const char * const paths_vhosts[] = {
 	"vhosts[].mounts[].mountpoint",
 	"vhosts[].mounts[].origin",
 	"vhosts[].mounts[].default",
+	"vhosts[].mounts[].cgi-timeout",
 	"vhosts[].mounts[].cgi-env[].*",
 	"vhosts[].ws-protocols[].*.*",
 	"vhosts[].ws-protocols[].*",
@@ -65,6 +66,7 @@ enum lejp_vhost_paths {
 	LEJPVP_MOUNTPOINT,
 	LEJPVP_ORIGIN,
 	LEJPVP_DEFAULT,
+	LEJPVP_CGI_TIMEOUT,
 	LEJPVP_CGI_ENV,
 	LEJPVP_PROTOCOL_NAME_OPT,
 	LEJPVP_PROTOCOL_NAME,
@@ -81,6 +83,7 @@ struct jpargs {
 	char *mountpoint, *origin, *def;
 	struct lws_protocol_vhost_options *pvo;
 	struct lws_protocol_vhost_options *mp_cgienv;
+	int cgi_timeout;
 };
 
 static void *
@@ -192,6 +195,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->origin = NULL;
 		a->def = NULL;
 		a->mp_cgienv = NULL;
+		a->cgi_timeout = 0;
 	}
 
 	/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
@@ -241,7 +245,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		}
 
 		n = lws_write_http_mount(a->last, &m, a->p, a->mountpoint,
-					 a->origin, a->def, a->mp_cgienv);
+					 a->origin, a->def, a->mp_cgienv,
+					 a->cgi_timeout);
 		if (!n)
 			return 1;
 		a->p += n;
@@ -280,6 +285,9 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_DEFAULT:
 		a->def = a->p;
 		break;
+	case LEJPVP_CGI_TIMEOUT:
+		a->cgi_timeout = atoi(ctx->buf);
+		return 0;
 	case LEJPVP_CGI_ENV:
 		mp_cgienv = lwsws_align(a);
 		a->p += sizeof(*a->mp_cgienv);
diff --git a/lwsws/etc-lwsws-conf-EXAMPLE b/lwsws/etc-lwsws-conf-EXAMPLE
index ac2c9ac54382e0e248dff23cbf4bd31221098651..e6184d98eb841957f42216a76389ab40be473764 100644
--- a/lwsws/etc-lwsws-conf-EXAMPLE
+++ b/lwsws/etc-lwsws-conf-EXAMPLE
@@ -4,8 +4,8 @@
 
 {
   "global": {
-   "uid": "99",
-   "gid": "99",
+   "uid": "48",
+   "gid": "48",
    "interface": "eth0",
    "count-threads": "1",
    "init-ssl": "yes"
diff --git a/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE b/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE
deleted file mode 100644
index b4e307dbb12c26dbb6e42583106c0a1c71f69e7a..0000000000000000000000000000000000000000
--- a/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE
+++ /dev/null
@@ -1,50 +0,0 @@
-# comment
-
-{
- "vhosts": [ {
-     "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",
-     "mounts": [{
-       "mountpoint": "/",
-       "origin": "file:///var/www/libwebsockets.org",
-       "default": "index.html"
-       }, {
-        "mountpoint": "/git",
-        "origin": "http://git.warmcat.com",
-        "default": "/" 
-       }, {
-        "mountpoint": "/mailman",
-        "origin": "cgi://usr/lib/mailman/cgi-bin/",
-        "default": "/list-info"
-    }]
-   },
-    {
-    "name": "libwebsockets.org", # disambiguated by port, must be same for SNI
-    "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",
-     "ws-protocols": [{
-       "wsprotocol": "dumb-increment-protocol",
-       "wsprotocol": "lws-mirror-protocol",
-       "wsprotocol": "lws-status"
-     }],
-     "ws-extensions": [{
-       "extension": "permessage-deflate"
-     }],
-     "mounts": [{
-       "mountpoint": "/",
-       "origin": "file:///usr/local/share/libwebsockets-test-server",
-       "default": "test.html"
-     }]
-   },
-    {
-    "name": "libwebsockets.org",
-    "port": "80",
-    "global-redirect": "https://libwebsockets.org"
- }]
-}
-
diff --git a/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE
new file mode 100644
index 0000000000000000000000000000000000000000..43d0489d0b85a52b54deeedfcb3c83eae964bd08
--- /dev/null
+++ b/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE
@@ -0,0 +1,99 @@
+{
+ "vhosts": [ {
+     "name": "localhost",
+     "port": "80",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": "file:///var/www/libwebsockets.org",
+       "default": "index.html"
+       }, {
+        "mountpoint": "/git",
+        "origin": "cgi:///var/www/cgi-bin/cgit",
+        "default": "/",
+        "cgi-env": [{
+                "CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org"
+        }]
+       }, {
+        "mountpoint": "/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"
+       }, {
+        "mountpoint": "/mailman/listinfo",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/listinfo"
+       }, {
+        "mountpoint": "/mailman/admin",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/admin"
+       }, {
+        "mountpoint": "/mailman/admindb",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/admindb"
+       }, {
+        "mountpoint": "/mailman/confirm",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/confirm"
+       }, {
+        "mountpoint": "/mailman/create",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/create"
+       }, {
+        "mountpoint": "/mailman/edithtml",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/edithtml"
+       }, {
+        "mountpoint": "/mailman/options",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/options"
+       }, {
+        "mountpoint": "/mailman/private",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/private"
+       }, {
+        "mountpoint": "/mailman/rmlist",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/rmlist"
+       }, {
+        "mountpoint": "/mailman/roster",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/roster"
+       }, {
+        "mountpoint": "/mailman/subscribe",
+        "origin": "cgi:///usr/lib/mailman/cgi-bin/subscribe"
+       }, {
+        "mountpoint": "/pipermail",
+        "origin": "file:///var/lib/mailman/archives/public",
+        "default": "index.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": {
+         "status": "ok"
+       }
+     }]
+    },
+    {
+    "name": "localhost",
+    "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"
+     }]
+   },
+    {
+    "name": "localhostx",
+    "port": "80",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": ">https://localhost"
+     }]
+   }
+
+  ]
+}
+
diff --git a/lwsws/http.c b/lwsws/http.c
index ff4ca98b2afc4dd6c63a98c9c686d0fa109c9635..337981d74b01f2fe51d2908d2a75f87f2ff4e394 100644
--- a/lwsws/http.c
+++ b/lwsws/http.c
@@ -71,7 +71,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 	struct per_session_data__http *pss =
 			(struct per_session_data__http *)user;
 	unsigned char buffer[4096 + LWS_PRE];
-	unsigned long amount, file_len, sent;
 	char leaf_path[1024];
 	const char *mimetype;
 	char *other_headers;
@@ -137,9 +136,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 				lwsl_err("proxy connect fail\n");
 				break;
 			}
-
-
-
 			break;
 		}
 #endif
@@ -156,89 +152,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
 			return 0;
 
-		/* check for the "send a big file by hand" example case */
-		lwsl_notice("%s\n", in);
-		if (!strcmp((const char *)in, "/leaf.jpg")) {
-			if (strlen(resource_path) > sizeof(leaf_path) - 10)
-				return -1;
-			sprintf(leaf_path, "%s/leaf.jpg", resource_path);
-
-			/* well, let's demonstrate how to send the hard way */
-
-			p = buffer + LWS_PRE;
-			end = p + sizeof(buffer) - LWS_PRE;
-
-			pss->fd = lws_plat_file_open(wsi, leaf_path, &file_len,
-						     LWS_O_RDONLY);
-
-			if (pss->fd == LWS_INVALID_FILE) {
-				lwsl_err("faild to open file %s\n", leaf_path);
-				return -1;
-			}
-
-			/*
-			 * we will send a big jpeg file, but it could be
-			 * anything.  Set the Content-Type: appropriately
-			 * so the browser knows what to do with it.
-			 *
-			 * Notice we use the APIs to build the header, which
-			 * will do the right thing for HTTP 1/1.1 and HTTP2
-			 * depending on what connection it happens to be working
-			 * on
-			 */
-			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 *)"image/jpeg",
-					10, &p, end))
-				return 1;
-			if (lws_add_http_header_content_length(wsi,
-							       file_len, &p,
-							       end))
-				return 1;
-			if (lws_finalize_http_header(wsi, &p, end))
-				return 1;
-
-			/*
-			 * send the http headers...
-			 * this won't block since it's the first payload sent
-			 * on the connection since it was established
-			 * (too small for partial)
-			 *
-			 * Notice they are sent using LWS_WRITE_HTTP_HEADERS
-			 * which also means you can't send body too in one step,
-			 * this is mandated by changes in HTTP2
-			 */
-
-			*p = '\0';
-			lwsl_info("%s\n", buffer + LWS_PRE);
-
-			n = lws_write(wsi, buffer + LWS_PRE,
-				      p - (buffer + LWS_PRE),
-				      LWS_WRITE_HTTP_HEADERS);
-			if (n < 0) {
-				lws_plat_file_close(wsi, pss->fd);
-				return -1;
-			}
-			/*
-			 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
-			 */
-			lws_callback_on_writable(wsi);
-			break;
-		}
-
-		/* if not, send a file the easy way */
-		if (!strncmp(in, "/cgit-data/", 11)) {
-			in = (char *)in + 11;
-			strcpy(buf, "/usr/share/cgit");
-		} else
-			strcpy(buf, resource_path);
-
+		strcpy(buf, resource_path);
 		if (strcmp(in, "/")) {
 			if (*((const char *)in) != '/')
 				strcat(buf, "/");
@@ -317,17 +231,14 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 		goto try_to_reuse;
 
 	case LWS_CALLBACK_HTTP_WRITEABLE:
-		lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n");
-
-		if (pss->client_finished)
-			return -1;
+		// lwsl_notice("LWS_CALLBACK_HTTP_WRITEABLE\n");
 
-		if (pss->fd == LWS_INVALID_FILE)
-			goto try_to_reuse;
 #ifdef LWS_WITH_CGI
 		if (pss->reason_bf & 1) {
-			if (lws_cgi_write_split_stdout_headers(wsi) < 0)
-				goto bail;
+			if (lws_cgi_write_split_stdout_headers(wsi) < 0) {
+				lwsl_debug("lws_cgi_write_split_stdout_headers says close\n");
+				return -1;
+			}
 
 			pss->reason_bf &= ~1;
 			break;
@@ -348,13 +259,20 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 			if (!wsi1)
 				break;
 			if (lws_http_client_read(wsi1, &px, &lenx) < 0)
-				goto bail;
+				return -1;
 
 			if (pss->client_finished)
 				return -1;
 			break;
 		}
 #endif
+#if 0
+		if (pss->client_finished)
+			return -1;
+
+		if (pss->fd == LWS_INVALID_FILE)
+			goto try_to_reuse;
+
 		/*
 		 * we can send more of whatever it is we were sending
 		 */
@@ -416,16 +334,7 @@ bail:
 		lws_plat_file_close(wsi, pss->fd);
 
 		return -1;
-
-	/*
-	 * callback for confirming to continue with client IP appear in
-	 * protocol 0 callback since no websocket protocol has been agreed
-	 * yet.  You can just ignore this if you won't filter on client IP
-	 * since the default unhandled callback return is 0 meaning let the
-	 * connection continue.
-	 */
-	case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-		/* if we returned non-zero from here, we kill the connection */
+#endif
 		break;
 
 #ifndef LWS_NO_CLIENT
@@ -516,7 +425,7 @@ bail:
 		case LWS_STDIN:
 			/* TBD stdin rx flow control */
 			break;
-		case LWS_STDOUT:
+		case LWS_STDOUT:;
 			pss->reason_bf |= 1;
 			/* when writing to MASTER would not block */
 			lws_callback_on_writable(wsi);
@@ -541,8 +450,10 @@ bail:
 		return -1;
 
 	case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
-		//lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
+		lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
 		pss->args = *((struct lws_cgi_args *)in);
+		pss->args.data[pss->args.len] = '\0';
+		//lwsl_err("(stdin fd = %d) %s\n", lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]), pss->args.data);
 		n = write(lws_get_socket_fd(pss->args.stdwsi[LWS_STDIN]),
 			  pss->args.data, pss->args.len);
 		//lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: write says %d", n);
@@ -558,59 +469,14 @@ bail:
 	 */
 
 	case LWS_CALLBACK_LOCK_POLL:
-		/*
-		 * lock mutex to protect pollfd state
-		 * called before any other POLL related callback
-		 * if protecting wsi lifecycle change, len == 1
-		 */
 		test_server_lock(len);
 		break;
 
 	case LWS_CALLBACK_UNLOCK_POLL:
-		/*
-		 * unlock mutex to protect pollfd state when
-		 * called after any other POLL related callback
-		 * if protecting wsi lifecycle change, len == 1
-		 */
 		test_server_unlock(len);
 		break;
 
-#ifdef EXTERNAL_POLL
-	case LWS_CALLBACK_ADD_POLL_FD:
-
-		if (count_pollfds >= max_poll_elements) {
-			lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
-			return 1;
-		}
-
-		fd_lookup[pa->fd] = count_pollfds;
-		pollfds[count_pollfds].fd = pa->fd;
-		pollfds[count_pollfds].events = pa->events;
-		pollfds[count_pollfds++].revents = 0;
-		break;
-
-	case LWS_CALLBACK_DEL_POLL_FD:
-		if (!--count_pollfds)
-			break;
-		m = fd_lookup[pa->fd];
-		/* have the last guy take up the vacant slot */
-		pollfds[m] = pollfds[count_pollfds];
-		fd_lookup[pollfds[count_pollfds].fd] = m;
-		break;
-
-	case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
-	        pollfds[fd_lookup[pa->fd]].events = pa->events;
-		break;
-#endif
-
 	case LWS_CALLBACK_GET_THREAD_ID:
-		/*
-		 * if you will call "lws_callback_on_writable"
-		 * from a different thread, return the caller thread ID
-		 * here so lws can use this information to work out if it
-		 * should signal the poll() loop to exit and restart early
-		 */
-
 		/* return pthread_getthreadid_np(); */
 
 		break;
diff --git a/lwsws/lejp.h b/lwsws/lejp.h
index 5832e998e1a15c1e908bc282150857e02d373612..bbed3005f97b9fa1ed8b2ee173fa9f21ee327b77 100644
--- a/lwsws/lejp.h
+++ b/lwsws/lejp.h
@@ -155,7 +155,7 @@ typedef char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
 #endif
 #ifndef LEJP_STRING_CHUNK
 /* must be >= 30 to assemble floats */
-#define LEJP_STRING_CHUNK 64
+#define LEJP_STRING_CHUNK 128
 #endif
 
 enum num_flags {
diff --git a/test-server/lws-cgi-test.sh b/test-server/lws-cgi-test.sh
index 914673ff918214c9504f791c4848f7a5b45fbb70..cc14af29e70c7e056d5d977cd1a049645de7dbc3 100755
--- a/test-server/lws-cgi-test.sh
+++ b/test-server/lws-cgi-test.sh
@@ -5,12 +5,16 @@ echo -e -n "\x0d\x0a"
 
 echo "<html><body>"
 echo "<h1>lwstest script stdout</h1>"
->&2 echo "lwstest script stderr"
+>&2 echo "lwstest script stderr: $REQUEST_METHOD"
 
 echo "<h2>REQUEST_METHOD=$REQUEST_METHOD</h2>"
 
 if [ "$REQUEST_METHOD" = "POST" ] ; then
-	read line
+	>&2 echo "lwstest script stderr: doing read"
+	echo "CONTENT_LENGTH=$CONTENT_LENGTH"
+	read -n $CONTENT_LENGTH line
+	>&2 echo "lwstest script stderr: done read"
+
 	echo "read=\"$line\""
 else
 	echo "<table>"