diff --git a/README.lwsws.md b/README.lwsws.md
index 4692a770b598c23fd5b58b3e41bdb8de66256268..a49ee01fc3fe226833da1c8d2806b8d5c5fa9bfa 100644
--- a/README.lwsws.md
+++ b/README.lwsws.md
@@ -140,8 +140,56 @@ Mounts
 Where mounts are given in the vhost definition, then directory contents may
 be auto-served if it matches the mountpoint.
 
-Currently only file:// mount protocol and a fixed set of mimetypes are
-supported.
+Mount protocols are used to control what kind of translation happens
+
+ - file://  serve the uri using the remainder of the url past the mountpoint based on the origin directory.
+
+ Eg, with this mountpoint
+
+```
+       {
+        "mountpoint": "/",
+        "origin": "file:///var/www/mysite.com",
+        "default": "/"
+       }
+```
+
+ The uri /file.jpg would serve /var/www/mysite.com/file.jpg, since / matched.
+
+ - ^http:// or ^https://  these cause any url matching the mountpoint to issue a redirect to the origin url
+
+ - cgi://   this causes any matching url to be given to the named cgi, eg
+
+```
+       {
+        "mountpoint": "/git",
+        "origin": "cgi:///var/www/cgi-bin/cgit",
+        "default": "/"
+       }, {
+        "mountpoint": "/cgit-data",
+        "origin": "file:///usr/share/cgit",
+        "default": "/"
+       },
+```
+
+ 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
+
+```
+       {
+        "mountpoint": "/git",
+        "origin": "cgi:///var/www/cgi-bin/cgit",
+        "default": "/",
+        "cgi-env": [{
+                "CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org"
+        }]
+       }
+```
+
+ This allows you to customize one cgi depending on the mountpoint (and / or vhost).
+
+Currently only a fixed set of mimetypes are supported.
 
 
 Plugins
diff --git a/lib/context.c b/lib/context.c
index fb9aabfd87ac1b3dc66144c2305856dc3c9a38af..d8a04a8c67a98588f0724f7a9acc6b81ce7ea9a4 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -52,7 +52,7 @@ 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)
+		     const char *def, struct lws_protocol_vhost_options *cgienv)
 {
 	struct lws_http_mount *m;
 	void *orig = store;
@@ -70,6 +70,7 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
 	m->mountpoint = mountpoint;
 	m->mountpoint_len = (unsigned char)strlen(mountpoint);
 	m->mount_next = NULL;
+	m->cgienv = cgienv;
 	if (next)
 		next->mount_next = m;
 
diff --git a/lib/libuv.c b/lib/libuv.c
index 89ee69025d04f802405bfc2a04a57ef2f2f729ca..6013e0905b822e4065c595e84b826d00416cd4ab 100644
--- a/lib/libuv.c
+++ b/lib/libuv.c
@@ -51,11 +51,14 @@ lws_uv_idle(uv_idle_t *handle
 		/* still somebody left who wants forced service? */
 		if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
 			/* yes... come back again later */
+			lwsl_debug("%s: done again\n", __func__);
 			return;
 	}
 
 	/* there is nobody who needs service forcing, shut down idle */
 	uv_idle_stop(handle);
+
+	lwsl_debug("%s: done stop\n", __func__);
 }
 
 static void
@@ -272,7 +275,7 @@ lws_libuv_io(struct lws *wsi, int flags)
 	if (!LWS_LIBUV_ENABLED(context))
 		return;
 
-	lwsl_debug("%s: wsi: %p, flags:%d\n", __func__, wsi, flags);
+	lwsl_debug("%s: wsi: %p, flags:0x%x\n", __func__, wsi, flags);
 
 	if (!pt->io_loop_uv) {
 		lwsl_info("%s: no io loop yet\n", __func__);
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index c537c1b5087fe6e2cd6a5da04764dc44dfe3544a..b85e6e9b2f874a9232ac7a3ca23b03434acb10e5 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -176,10 +176,11 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	if (wsi->mode == LWSCM_CGI) {
 		/* we are not a network connection, but a handler for CGI io */
 		if (wsi->parent && wsi->parent->cgi)
-		/* end the binding between us and master */
-		wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
+			/* end the binding between us and master */
+			wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL;
 		wsi->socket_is_permanently_unusable = 1;
 
+		lwsl_debug("------ %s: detected cgi fdhandler wsi %p\n", __func__, wsi);
 		goto just_kill_connection;
 	}
 
@@ -496,6 +497,7 @@ just_kill_connection:
 
 #ifdef LWS_USE_LIBUV
 	if (LWS_LIBUV_ENABLED(context)) {
+		lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
 		/* libuv has to do his own close handle processing asynchronously */
 		lws_libuv_closehandle(wsi);
 
@@ -1534,7 +1536,7 @@ lws_urlencode(const char *in, int inlen, char *out, int outlen)
 	const char *hex = "0123456789ABCDEF";
 	char *start = out, *end = out + outlen;
 
-	while (inlen-- && out > end - 4) {
+	while (inlen-- && out < end - 4) {
 		if ((*in >= 'A' && *in <= 'Z') ||
 		    (*in >= 'a' && *in <= 'z') ||
 		    (*in >= '0' && *in <= '9') ||
@@ -1635,12 +1637,12 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
  */
 
 LWS_VISIBLE LWS_EXTERN int
-lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
-	int timeout_secs)
+lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len,
+	int timeout_secs, struct lws_protocol_vhost_options *mp_cgienv)
 {
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
 	char *env_array[30], cgi_path[400], e[1024], *p = e,
-	     *end = p + sizeof(e) - 1, tok[256];
+	     *end = p + sizeof(e) - 1, tok[256], *t;
 	struct lws_cgi *cgi;
 	int n, m, i;
 
@@ -1670,12 +1672,15 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
 		if (!cgi->stdwsi[n])
 			goto bail2;
 		cgi->stdwsi[n]->cgi_channel = n;
+		cgi->stdwsi[n]->vhost = wsi->vhost;
+
 		/* 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);
 	}
 
 	for (n = 0; n < 3; n++) {
+		lws_libuv_accept(cgi->stdwsi[n], cgi->stdwsi[n]->sock);
 		if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n]))
 			goto bail3;
 		cgi->stdwsi[n]->parent = wsi;
@@ -1725,9 +1730,16 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
 					     WSI_TOKEN_HTTP_URI_ARGS, m);
 			if (i < 0)
 				break;
-			i = lws_urlencode(tok, i, p, end - p);
-			p += i;
-			*p++ = '&';
+			t = tok;
+			while (*t && *t != '=' && p < end - 4)
+				*p++ = *t++;
+			if (*t == '=')
+				*p++ = *t++;
+			i = lws_urlencode(t, i- (t - tok), p, end - p);
+			if (i > 0) {
+				p += i;
+				*p++ = '&';
+			}
 			m++;
 		}
 		if (m)
@@ -1759,13 +1771,23 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
 		p++;
 	}
 	env_array[n++] = p;
-	p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[2]) + 1;
+	p += snprintf(p, end - p, "SCRIPT_PATH=%s", exec_array[0]) + 1;
+
+	while (mp_cgienv) {
+		env_array[n++] = p;
+		p += snprintf(p, end - p, "%s=%s", mp_cgienv->name,
+			      mp_cgienv->value);
+		lwsl_notice("   Applying mount-specific cgi env '%s'\n",
+			    env_array[n - 1]);
+		p++;
+		mp_cgienv = mp_cgienv->next;
+	}
 
 	env_array[n++] = "SERVER_SOFTWARE=libwebsockets";
 	env_array[n++] = "PATH=/bin:/usr/bin:/usr/local/bin:/var/www/cgi-bin";
 	env_array[n] = NULL;
 
-#if 0
+#if 1
 	for (m = 0; m < n; m++)
 		lwsl_err("    %s\n", env_array[m]);
 #endif
@@ -1785,6 +1807,10 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
 		/* we are the parent process */
 		return 0;
 
+	/* somewhere we can at least read things and enter it */
+	if (chdir("/"))
+		lwsl_notice("%s: Failed to chdir\n", __func__);
+
 	/* We are the forked process, redirect and kill inherited things.
 	 *
 	 * Because of vfork(), we cannot do anything that changes pages in
@@ -1806,9 +1832,9 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int script_uri_path_len,
 		*p++ = '\0';
 		setenv(env_array[m], p, 1);
 	}
-	execvp(exec_array[0], &exec_array[0]);
+	execvp(exec_array[0], (char * const *)&exec_array[0]);
 #else
-	execvpe(exec_array[0], &exec_array[0], &env_array[0]);
+	execvpe(exec_array[0], (char * const *)&exec_array[0], &env_array[0]);
 #endif
 
 	exit(1);
@@ -1948,6 +1974,8 @@ lws_cgi_kill(struct lws *wsi)
 	struct lws_cgi_args args;
 	int n, status, do_close = 0;
 
+	lwsl_debug("!!!!! %s: %p\n", __func__, wsi);
+
 	if (!wsi->cgi)
 		return 0;
 
@@ -1986,17 +2014,20 @@ lws_cgi_kill(struct lws *wsi)
 		pcgi = &(*pcgi)->cgi_list;
 	}
 
-	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;
+	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)
+	if (do_close) {
+		lwsl_debug("!!!!! %s: do_close\n", __func__);
 		lws_close_free_wsi(wsi, 0);
+	}
 
 	return 0;
 }
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 13d8bccc859f52debba57e47dc72bdca2dd7901f..9996cf2ae7e260b84a18d5a00a4b29e1a5286510 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1538,10 +1538,20 @@ struct lws_client_connect_info {
 
 struct lws_http_mount;
 
+enum {
+	LWSMPRO_HTTP,
+	LWSMPRO_HTTPS,
+	LWSMPRO_FILE,
+	LWSMPRO_CGI,
+	LWSMPRO_REDIR_HTTP,
+	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);
+		     const char *def,
+		     struct lws_protocol_vhost_options *cgienv);
 
 LWS_VISIBLE LWS_EXTERN void
 lws_set_log_level(int level,
@@ -2012,8 +2022,9 @@ struct lws_cgi_args {
 };
 
 LWS_VISIBLE LWS_EXTERN int
-lws_cgi(struct lws *wsi, char * const *exec_array,  int script_uri_path_len,
-	int timeout_secs);
+lws_cgi(struct lws *wsi, const char * const *exec_array,
+	int script_uri_path_len, int timeout_secs,
+	struct lws_protocol_vhost_options *mp_cgienv);
 
 LWS_VISIBLE LWS_EXTERN int
 lws_cgi_write_split_stdout_headers(struct lws *wsi);
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index eb2f76234415335394b426b4e025f0452a9cac56..880ad85da8aeafed43e17d49374d47aab4c70346 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -441,8 +441,10 @@ next:
 
 
 static void
-sigpipe_handler(int x)
+sigabrt_handler(int x)
 {
+	printf("%s\n", __func__);
+	//*(char *)0 = 0;
 }
 
 LWS_VISIBLE int
@@ -456,7 +458,9 @@ lws_plat_context_early_init(void)
 
 	sigprocmask(SIG_BLOCK, &mask, NULL);
 
-	signal(SIGPIPE, sigpipe_handler);
+	signal(SIGPIPE, SIG_IGN);
+
+	signal(SIGABRT, sigabrt_handler);
 
 	return 0;
 }
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 35f3ed6fa4c36edfd914fade6bd4dd4a271a0374..0684326d8efe2c68a408a785673028c989200426 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -621,6 +621,8 @@ struct lws_http_mount {
 	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;
+
 	unsigned char origin_protocol;
 	unsigned char mountpoint_len;
 };
diff --git a/lib/server.c b/lib/server.c
index f1ca7acde15569af1a1a07b0d8d05c0cf5e7a8ed..b28c9bbaac05b1a86e5c30551584b09dc816c41a 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -218,17 +218,48 @@ static const char * get_mimetype(const char *file)
 int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 {
 	const char *mimetype;
-	char path[256];
-	int n;
+	struct stat st;
+	char path[256], sym[256];
+	int n, spin = 0;
 
 	lwsl_notice("%s: %s %s\n", __func__, uri, origin);
 	snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
 
+	do {
+		spin++;
+
+		if (stat(path, &st)) {
+			lwsl_err("unable to stat %s\n", path);
+			goto bail;
+		}
+
+		lwsl_debug(" %s mode %d\n", path, S_IFMT & st.st_mode);
+
+		if ((S_IFMT & st.st_mode) == S_IFLNK) {
+			if (readlink(path, sym, sizeof(sym))) {
+				lwsl_err("Failed to read link %s\n", path);
+				goto bail;
+			}
+			lwsl_debug("symlink %s -> %s\n", path, sym);
+			snprintf(path, sizeof(path) - 1, "%s", sym);
+		}
+
+		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",
+				 origin, uri);
+		}
+
+	} while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
+
+	if (spin == 5) {
+		lwsl_err("symlink loop %s \n", path);
+	}
+
 	mimetype = get_mimetype(path);
 	if (!mimetype) {
 		lwsl_err("unknown mimetype for %s", path);
-		lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
-		return -1;
+		goto bail;
 	}
 
 	n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
@@ -237,6 +268,10 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
 		return -1; /* error or can't reuse connection: close the socket */
 
 	return 0;
+bail:
+	lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
+
+	return -1;
 }
 
 int
@@ -403,7 +438,11 @@ lws_http_action(struct lws *wsi)
 	hm = wsi->vhost->mount_list;
 	while (hm) {
 		if (uri_len >= hm->mountpoint_len &&
-		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len)) {
+		    !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
+		    (uri_ptr[hm->mountpoint_len] == '\0' ||
+		     uri_ptr[hm->mountpoint_len] == '/' ||
+		     hm->mountpoint_len == 1)
+		    ) {
 			if (hm->mountpoint_len > best) {
 				best = hm->mountpoint_len;
 				hit = hm;
@@ -414,7 +453,8 @@ lws_http_action(struct lws *wsi)
 	if (hit) {
 		char *s = uri_ptr + hit->mountpoint_len;
 
-		lwsl_err("*** hit %d %d %s\n", hit->mountpoint_len, hit->origin_protocol , hit->origin);
+		lwsl_debug("*** hit %d %d %s\n", hit->mountpoint_len,
+			   hit->origin_protocol , hit->origin);
 
 		/*
 		 * if we have a mountpoint like https://xxx.com/yyy
@@ -431,50 +471,87 @@ lws_http_action(struct lws *wsi)
 		 * / at the end, we must redirect to add it so the browser
 		 * understands he is one "directory level" down.
 		 */
-		if (hit->mountpoint_len > 1 || (hit->origin_protocol & 4))
-			if (*s != '/' || (hit->origin_protocol & 4)) {
-				unsigned char *start = pt->serv_buf + LWS_PRE,
+		if ((hit->mountpoint_len > 1 || (hit->origin_protocol & 4)) &&
+		    (*s != '/' || (hit->origin_protocol & 4))) {
+			unsigned char *start = pt->serv_buf + LWS_PRE,
 					      *p = start, *end = p + 512;
-				static const char *oprot[] = {
-					"http://", "https://"
-				};
-
-				if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
-					goto bail_nuke_ah;
-				if (lws_add_http_header_status(wsi, 301, &p, end))
-					goto bail_nuke_ah;
-
-				lwsl_err("**** %s", hit->origin);
-
-				/* > at start indicates deal with by redirect */
-				if (hit->origin_protocol & 4)
-					n = snprintf((char *)end, 256, "%s%s",
-						    oprot[hit->origin_protocol & 1],
-						    hit->origin);
-				else
-					n = snprintf((char *)end, 256,
-					    "https://%s/%s/",
-					    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
-					    uri_ptr);
-				if (lws_add_http_header_by_token(wsi,
-						WSI_TOKEN_HTTP_LOCATION,
-						end, n, &p, end))
-					goto bail_nuke_ah;
-				if (lws_finalize_http_header(wsi, &p, end))
-					goto bail_nuke_ah;
-				n = lws_write(wsi, start, p - start,
-						LWS_WRITE_HTTP_HEADERS);
-				if ((int)n < 0)
-					goto bail_nuke_ah;
-
-				return lws_http_transaction_completed(wsi);
+			static const char *oprot[] = {
+				"http://", "https://"
+			};
+
+			if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
+				goto bail_nuke_ah;
+			if (lws_add_http_header_status(wsi, 301, &p, end))
+				goto bail_nuke_ah;
+
+			lwsl_debug("**** %s", hit->origin);
+
+			/* > at start indicates deal with by redirect */
+			if (hit->origin_protocol & 4)
+				n = snprintf((char *)end, 256, "%s%s",
+					    oprot[hit->origin_protocol & 1],
+					    hit->origin);
+			else
+				n = snprintf((char *)end, 256,
+				    "https://%s/%s/",
+				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
+				    uri_ptr);
+			if (lws_add_http_header_by_token(wsi,
+					WSI_TOKEN_HTTP_LOCATION,
+					end, n, &p, end))
+				goto bail_nuke_ah;
+			if (lws_finalize_http_header(wsi, &p, end))
+				goto bail_nuke_ah;
+			n = lws_write(wsi, start, p - start,
+					LWS_WRITE_HTTP_HEADERS);
+			if ((int)n < 0)
+				goto bail_nuke_ah;
+
+			return lws_http_transaction_completed(wsi);
+		}
+
+#ifdef LWS_WITH_CGI
+		/* did we hit something with a cgi:// origin? */
+		if (hit->origin_protocol == LWSMPRO_CGI) {
+			const char *cmd[] = {
+				NULL, /* replace with cgi path */
+				NULL
+			};
+			unsigned char *p, *end, buffer[256];
+
+			lwsl_debug("%s: cgi\n", __func__);
+			cmd[0] = hit->origin;
+			n = lws_cgi(wsi, cmd, hit->mountpoint_len, 5,
+				    hit->cgienv);
+			if (n) {
+				lwsl_err("%s: cgi failed\n");
+				return -1;
 			}
+			p = buffer + LWS_PRE;
+			end = p + sizeof(buffer) - LWS_PRE;
+
+			if (lws_add_http_header_status(wsi, 200, &p, end))
+				return 1;
+			if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
+					(unsigned char *)"close", 5, &p, end))
+				return 1;
+			n = lws_write(wsi, buffer + LWS_PRE,
+				      p - (buffer + LWS_PRE),
+				      LWS_WRITE_HTTP_HEADERS);
+
+			return 0;
+		}
+#endif
 
-		if (s[0] == '\0' || (s[0] == '/' && s[1] == '\0'))
+		n = strlen(s);
+		if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
 			s = (char *)hit->def;
 
 		if (!s)
 			s = "index.html";
+
+
+
 		n = lws_http_serve(wsi, s, hit->origin);
 	} else
 		n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
@@ -992,18 +1069,21 @@ lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd)
 	lws_libuv_accept(new_wsi, new_wsi->sock);
 
 	if (!LWS_SSL_ENABLED(new_wsi->vhost)) {
-		if (insert_wsi_socket_into_fds(context, new_wsi))
+		if (insert_wsi_socket_into_fds(context, new_wsi)) {
+			lwsl_err("%s: fail inserting socket\n", __func__);
 			goto fail;
+		}
 	} else {
 		new_wsi->mode = LWSCM_SSL_INIT;
-		if (lws_server_socket_service_ssl(new_wsi, accept_fd))
+		if (lws_server_socket_service_ssl(new_wsi, accept_fd)) {
+			lwsl_err("%s: fail ssl negotiation\n", __func__);
 			goto fail;
+		}
 	}
 
 	return new_wsi;
 
 fail:
-	lwsl_err("%s: fail\n", __func__);
 	lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS);
 
 	return NULL;
diff --git a/lib/ssl-server.c b/lib/ssl-server.c
index 9bd2af9ad1424bdd01316563add211d1ad018131..55ff61bd58868e2f7ec04a45f88607fdef453dc2 100644
--- a/lib/ssl-server.c
+++ b/lib/ssl-server.c
@@ -178,7 +178,8 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
 	if (servername) {
 		vhost = lws_select_vhost(context, port, servername);
 		if (vhost) {
-			lwsl_notice("SNI: Found: %s (port %d)\n", servername, port);
+			lwsl_debug("SNI: Found: %s (port %d)\n",
+				   servername, port);
 			SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
 			return SSL_TLSEXT_ERR_OK;
 		}
diff --git a/lwsws/conf.c b/lwsws/conf.c
index ff4029d2af453dcca84165c057ca686bc545742d..1cbc5ede40aa88bba8212aa2f151f280842f0aa9 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-env[].*",
 	"vhosts[].ws-protocols[].*.*",
 	"vhosts[].ws-protocols[].*",
 	"vhosts[].ws-protocols[]",
@@ -64,6 +65,7 @@ enum lejp_vhost_paths {
 	LEJPVP_MOUNTPOINT,
 	LEJPVP_ORIGIN,
 	LEJPVP_DEFAULT,
+	LEJPVP_CGI_ENV,
 	LEJPVP_PROTOCOL_NAME_OPT,
 	LEJPVP_PROTOCOL_NAME,
 	LEJPVP_PROTOCOL,
@@ -78,6 +80,7 @@ struct jpargs {
 	struct lws_http_mount *head, *last;
 	char *mountpoint, *origin, *def;
 	struct lws_protocol_vhost_options *pvo;
+	struct lws_protocol_vhost_options *mp_cgienv;
 };
 
 static void *
@@ -144,13 +147,15 @@ static char
 lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 {
 	struct jpargs *a = (struct jpargs *)ctx->user;
-	struct lws_protocol_vhost_options *pvo;
+	struct lws_protocol_vhost_options *pvo, *mp_cgienv;
 	struct lws_http_mount *m;
 	int n;
 
-//	lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
-//	for (n = 0; n < ctx->wildcount; n++)
-//		lwsl_notice("    %d\n", ctx->wild[n]);
+#if 0
+	lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
+	for (n = 0; n < ctx->wildcount; n++)
+		lwsl_notice("    %d\n", ctx->wild[n]);
+#endif
 
 	if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
 		/* set the defaults for this vhost */
@@ -186,6 +191,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		a->mountpoint = NULL;
 		a->origin = NULL;
 		a->def = NULL;
+		a->mp_cgienv = NULL;
 	}
 
 	/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
@@ -235,7 +241,7 @@ 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->origin, a->def, a->mp_cgienv);
 		if (!n)
 			return 1;
 		a->p += n;
@@ -274,7 +280,25 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 	case LEJPVP_DEFAULT:
 		a->def = a->p;
 		break;
+	case LEJPVP_CGI_ENV:
+		mp_cgienv = lwsws_align(a);
+		a->p += sizeof(*a->mp_cgienv);
+
+		mp_cgienv->next = a->mp_cgienv;
+		a->mp_cgienv = mp_cgienv;
 
+		n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
+		mp_cgienv->name = a->p;
+		a->p += n;
+		mp_cgienv->value = a->p;
+		mp_cgienv->options = NULL;
+		a->p += snprintf(a->p, a->end - a->p, "%s", ctx->buf);
+		*(a->p)++ = '\0';
+
+		lwsl_notice("    adding cgi-env '%s' = '%s'\n", mp_cgienv->name,
+				mp_cgienv->value);
+
+		break;
 	case LEJPVP_PROTOCOL_NAME_OPT:
 		/* this catches, eg,
 		 * vhosts[].ws-protocols[].xxx-protocol.yyy-option
@@ -435,7 +459,7 @@ lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
 
 	a.info = info;
 	a.p = *cs;
-	a.end = a.p + *len;
+	a.end = (a.p + *len) - 1;
 	a.valid = 0;
 
 	if (lwsws_get_config(&a, "/etc/lwsws/conf", paths_global,
diff --git a/lwsws/http.c b/lwsws/http.c
index 66f398f9babf5054ceed104e32de734ddf5cbb8c..ff4ca98b2afc4dd6c63a98c9c686d0fa109c9635 100644
--- a/lwsws/http.c
+++ b/lwsws/http.c
@@ -152,42 +152,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 		}
 #endif
 
-#ifdef LWS_WITH_CGI
-		if (!strncmp(in, "/cgitest", 8)) {
-			static char *cmd[] = {
-				"/bin/sh",
-				"-c",
-				INSTALL_DATADIR"/libwebsockets-test-server/lws-cgi-test.sh",
-//				"/var/www/cgi-bin/cgit",
-				NULL
-			};
-
-			lwsl_notice("%s: cgitest\n", __func__);
-			n = lws_cgi(wsi, cmd, 8, 5);
-			if (n) {
-				lwsl_err("%s: cgi failed\n");
-				return -1;
-			}
-			p = buffer + LWS_PRE;
-			end = p + sizeof(buffer) - LWS_PRE;
-
-			if (lws_add_http_header_status(wsi, 200, &p, end))
-				return 1;
-			if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
-					(unsigned char *)"close", 5, &p, end))
-				return 1;
-			n = lws_write(wsi, buffer + LWS_PRE,
-				      p - (buffer + LWS_PRE),
-				      LWS_WRITE_HTTP_HEADERS);
-
-			/* the cgi starts by outputting headers, we can't
-			 *  finalize the headers until we see the end of that
-			 */
-
-			break;
-		}
-#endif
-
 		/* if a legal POST URL, let it continue and accept data */
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
 			return 0;
diff --git a/test-server/test-server-http.c b/test-server/test-server-http.c
index 902e94564c503ac6ad4ff5564644ddd6f936e01a..af40609aac321b11cb55bee382b46d77557bb0f6 100644
--- a/test-server/test-server-http.c
+++ b/test-server/test-server-http.c
@@ -215,42 +215,6 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 		}
 #endif
 
-#ifdef LWS_WITH_CGI
-		if (!strncmp(in, "/cgitest", 8)) {
-			static char *cmd[] = {
-				"/bin/sh",
-				"-c",
-				INSTALL_DATADIR"/libwebsockets-test-server/lws-cgi-test.sh",
-//				"/var/www/cgi-bin/cgit",
-				NULL
-			};
-
-			lwsl_notice("%s: cgitest\n", __func__);
-			n = lws_cgi(wsi, cmd, 8, 5);
-			if (n) {
-				lwsl_err("%s: cgi failed\n");
-				return -1;
-			}
-			p = buffer + LWS_PRE;
-			end = p + sizeof(buffer) - LWS_PRE;
-
-			if (lws_add_http_header_status(wsi, 200, &p, end))
-				return 1;
-			if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
-					(unsigned char *)"close", 5, &p, end))
-				return 1;
-			n = lws_write(wsi, buffer + LWS_PRE,
-				      p - (buffer + LWS_PRE),
-				      LWS_WRITE_HTTP_HEADERS);
-
-			/* the cgi starts by outputting headers, we can't
-			 *  finalize the headers until we see the end of that
-			 */
-
-			break;
-		}
-#endif
-
 		/* if a legal POST URL, let it continue and accept data */
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
 			return 0;
@@ -423,15 +387,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 
 		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;
 
-			pss->reason_bf &= ~1;
-			break;
-		}
-#endif
 #ifndef LWS_NO_CLIENT
 		if (pss->reason_bf & 2) {
 			char *px = buf + LWS_PRE;
@@ -601,56 +557,6 @@ bail:
 		break;
 #endif
 
-#ifdef LWS_WITH_CGI
-	/* CGI IO events (POLLIN/OUT) appear here our demo user code policy is
-	 *
-	 *  - POST data goes on subprocess stdin
-	 *  - subprocess stdout goes on http via writeable callback
-	 *  - subprocess stderr goes to the logs
-	 */
-	case LWS_CALLBACK_CGI:
-		pss->args = *((struct lws_cgi_args *)in);
-		//lwsl_notice("LWS_CALLBACK_CGI: ch %d\n", pss->args.ch);
-		switch (pss->args.ch) { /* which of stdin/out/err ? */
-		case LWS_STDIN:
-			/* TBD stdin rx flow control */
-			break;
-		case LWS_STDOUT:
-			pss->reason_bf |= 1;
-			/* when writing to MASTER would not block */
-			lws_callback_on_writable(wsi);
-			break;
-		case LWS_STDERR:
-			n = read(lws_get_socket_fd(pss->args.stdwsi[LWS_STDERR]),
-					buf, 127);
-			//lwsl_notice("stderr reads %d\n", n);
-			if (n > 0) {
-				if (buf[n - 1] != '\n')
-					buf[n++] = '\n';
-				buf[n] = '\0';
-				lwsl_notice("CGI-stderr: %s\n", buf);
-			}
-			break;
-		}
-		break;
-
-	case LWS_CALLBACK_CGI_TERMINATED:
-		//lwsl_notice("LWS_CALLBACK_CGI_TERMINATED\n");
-		/* because we sent on openended http, close the connection */
-		return -1;
-
-	case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
-		//lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA\n");
-		pss->args = *((struct lws_cgi_args *)in);
-		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);
-		if (n < pss->args.len)
-			lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: sent %d only %d went",
-					n, pss->args.len);
-		return n;
-#endif
-
 	/*
 	 * callbacks for managing the external poll() array appear in
 	 * protocol 0 callback