diff --git a/include/libwebsockets/lws-spa.h b/include/libwebsockets/lws-spa.h
index 89627c318c4f88940d98261647899cb7487c35a7..b2f45534bd34b3296eef9004bde47f1a58e0a204 100644
--- a/include/libwebsockets/lws-spa.h
+++ b/include/libwebsockets/lws-spa.h
@@ -86,6 +86,9 @@ struct lws_spa;
  *
  * Creates a urldecode parser and initializes it.
  *
+ * It's recommended to use the newer api, lws_spa_create_via_info()
+ * instead.
+ *
  * opt_cb can be NULL if you just want normal name=value parsing, however
  * if one or more entries in your form are bulk data (file transfer), you
  * can provide this callback and filter on the name callback parameter to
@@ -97,6 +100,36 @@ lws_spa_create(struct lws *wsi, const char * const *param_names,
 	       int count_params, int max_storage, lws_spa_fileupload_cb opt_cb,
 	       void *opt_data);
 
+typedef struct lws_spa_create_info {
+	const char * const *param_names; /* array of form parameter names, like "username" */
+	int count_params; /* count of param_names */
+	int max_storage; /* total amount of form parameter values we can store */
+	lws_spa_fileupload_cb opt_cb; /* NULL, or callback to receive file upload data. */
+	void *opt_data; /* NULL, or user pointer provided to opt_cb. */
+	size_t param_names_stride; /* 0 if param_names is an array of char *.
+					Else stride to next char * */
+	struct lwsac **ac;	/* NULL, or pointer to lwsac * to contain all
+				   related heap allocations */
+	size_t ac_chunk_size;	/* 0 for default, or ac chunk size */
+} lws_spa_create_info_t;
+
+/**
+ * lws_spa_create_via_info() - create urldecode parser
+ *
+ * \param wsi: lws connection (used to find Content Type)
+ * \param info: pointer to struct defining the arguments
+ *
+ * Creates a urldecode parser and initializes it.
+ *
+ * opt_cb can be NULL if you just want normal name=value parsing, however
+ * if one or more entries in your form are bulk data (file transfer), you
+ * can provide this callback and filter on the name callback parameter to
+ * treat that urldecoded data separately.  The callback should return -1
+ * in case of fatal error, and 0 if OK.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_spa *
+lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *info);
+
 /**
  * lws_spa_process() - parses a chunk of input data
  *
diff --git a/lib/core-net/dummy-callback.c b/lib/core-net/dummy-callback.c
index fffe99b4c09d56ee4110077e55b0caee5ef4d19e..010fbb01074b257a643a697f7b40e51bf7dad5c3 100644
--- a/lib/core-net/dummy-callback.c
+++ b/lib/core-net/dummy-callback.c
@@ -261,8 +261,10 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
 #if !defined(LWS_NO_SERVER)
 	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
 #if defined(LWS_WITH_HTTP_PROXY)
-		if (wsi->child_list)
+		if (wsi->child_list) {
 			lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len);
+			break;
+		}
 #endif
 		/* fallthru */
 	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
diff --git a/lib/roles/http/server/lws-spa.c b/lib/roles/http/server/lws-spa.c
index bc8d3ce64123818433d962ba2f83ad8d3be6ce50..6300f7d836a85437d09456880ad45316a2b7bc16 100644
--- a/lib/roles/http/server/lws-spa.c
+++ b/lib/roles/http/server/lws-spa.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - Stateful urldecode for POST
  *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -45,12 +45,14 @@ static const char * const mp_hdr[] = {
 	"\x0d\x0a"
 };
 
-typedef int (*lws_urldecode_stateful_cb)(void *data,
+struct lws_spa;
+
+typedef int (*lws_urldecode_stateful_cb)(struct lws_spa *spa,
 		const char *name, char **buf, int len, int final);
 
 struct lws_urldecode_stateful {
 	char *out;
-	void *data;
+	struct lws_spa *data;
 	struct lws *wsi;
 	char name[LWS_MAX_ELEM_NAME];
 	char temp[LWS_MAX_ELEM_NAME];
@@ -74,15 +76,29 @@ struct lws_urldecode_stateful {
 	lws_urldecode_stateful_cb output;
 };
 
+struct lws_spa {
+	struct lws_urldecode_stateful *s;
+	lws_spa_create_info_t i;
+	int *param_length;
+	char finalized;
+	char **params;
+	char *storage;
+	char *end;
+};
+
 static struct lws_urldecode_stateful *
-lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data,
-		       lws_urldecode_stateful_cb output)
+lws_urldecode_s_create(struct lws_spa *spa, struct lws *wsi, char *out,
+		       int out_len, lws_urldecode_stateful_cb output)
 {
-	struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s),
-						"stateful urldecode");
+	struct lws_urldecode_stateful *s;
 	char buf[205], *p;
 	int m = 0;
 
+	if (spa->i.ac)
+		s = lwsac_use_zero(spa->i.ac, sizeof(*s), spa->i.ac_chunk_size);
+	else
+		s = lws_zalloc(sizeof(*s), "stateful urldecode");
+
 	if (!s)
 		return NULL;
 
@@ -94,7 +110,7 @@ lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data,
 	s->mp = 0;
 	s->state = US_NAME;
 	s->name[0] = '\0';
-	s->data = data;
+	s->data = spa;
 	s->wsi = wsi;
 
 	if (lws_hdr_copy(wsi, buf, sizeof(buf),
@@ -403,7 +419,7 @@ done:
 }
 
 static int
-lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
+lws_urldecode_s_destroy(struct lws_spa *spa, struct lws_urldecode_stateful *s)
 {
 	int ret = 0;
 
@@ -418,48 +434,40 @@ lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
 	if (s->output(s->data, s->name, NULL, 0, LWS_UFS_CLOSE))
 		return -1;
 
-	lws_free(s);
+	if (!spa->i.ac)
+		lws_free(s);
 
 	return ret;
 }
 
-struct lws_spa {
-	struct lws_urldecode_stateful *s;
-	const char * const *param_names;
-	void *opt_data;
-	lws_spa_fileupload_cb opt_cb;
-	int *param_length;
-	int count_params;
-	int max_storage;
-	char finalized;
-	char **params;
-	char *storage;
-	char *end;
-};
-
 static int
-lws_urldecode_spa_lookup(struct lws_spa *spa,
-			 const char *name)
+lws_urldecode_spa_lookup(struct lws_spa *spa, const char *name)
 {
+	const char * const *pp = spa->i.param_names;
 	int n;
 
-	for (n = 0; n < spa->count_params; n++)
-		if (!strcmp(spa->param_names[n], name))
+	for (n = 0; n < spa->i.count_params; n++) {
+		if (!strcmp(*pp, name))
 			return n;
 
+		if (spa->i.param_names_stride)
+			pp = (const char * const *)(((char *)pp) + spa->i.param_names_stride);
+		else
+			pp++;
+	}
+
 	return -1;
 }
 
 static int
-lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len,
+lws_urldecode_spa_cb(struct lws_spa *spa, const char *name, char **buf, int len,
 		     int final)
 {
-	struct lws_spa *spa = (struct lws_spa *)data;
 	int n;
 
 	if (final == LWS_UFS_CLOSE || spa->s->content_disp_filename[0]) {
-		if (spa->opt_cb) {
-			n = spa->opt_cb(spa->opt_data, name,
+		if (spa->i.opt_cb) {
+			n = spa->i.opt_cb(spa->i.opt_data, name,
 					spa->s->content_disp_filename,
 					buf ? *buf : NULL, len, final);
 
@@ -469,63 +477,88 @@ lws_urldecode_spa_cb(void *data, const char *name, char **buf, int len,
 		return 0;
 	}
 	n = lws_urldecode_spa_lookup(spa, name);
-
 	if (n == -1 || !len) /* unrecognized */
 		return 0;
 
-	if (!spa->params[n])
-		spa->params[n] = *buf;
+	if (!spa->i.ac) {
+		if (!spa->params[n])
+			spa->params[n] = *buf;
 
-	if ((*buf) + len >= spa->end) {
-		lwsl_info("%s: exceeded storage\n", __func__);
-		return -1;
-	}
+		if ((*buf) + len >= spa->end) {
+			lwsl_info("%s: exceeded storage\n", __func__);
+			return -1;
+		}
 
-	spa->param_length[n] += len;
+		/* move it on inside storage */
+		(*buf) += len;
+		*((*buf)++) = '\0';
 
-	/* move it on inside storage */
-	(*buf) += len;
-	*((*buf)++) = '\0';
+		spa->s->out_len -= len + 1;
+	} else {
+		spa->params[n] = lwsac_use(spa->i.ac, len + 1,
+					   spa->i.ac_chunk_size);
+		if (!spa->params[n])
+			return -1;
 
-	spa->s->out_len -= len + 1;
+		memcpy(spa->params[n], *buf, len);
+		spa->params[n][len] = '\0';
+	}
+
+	spa->param_length[n] += len;
 
 	return 0;
 }
 
-LWS_VISIBLE LWS_EXTERN struct lws_spa *
-lws_spa_create(struct lws *wsi, const char * const *param_names,
-	       int count_params, int max_storage,
-	       lws_spa_fileupload_cb opt_cb, void *opt_data)
+struct lws_spa *
+lws_spa_create_via_info(struct lws *wsi, const lws_spa_create_info_t *i)
 {
-	struct lws_spa *spa = lws_zalloc(sizeof(*spa), "spa");
+	struct lws_spa *spa;
+
+	if (i->ac)
+		spa = lwsac_use_zero(i->ac, sizeof(*spa), i->ac_chunk_size);
+	else
+		spa = lws_zalloc(sizeof(*spa), "spa");
 
 	if (!spa)
 		return NULL;
 
-	spa->param_names = param_names;
-	spa->count_params = count_params;
-	spa->max_storage = max_storage;
-	spa->opt_cb = opt_cb;
-	spa->opt_data = opt_data;
+	spa->i = *i;
+	if (!spa->i.max_storage)
+		spa->i.max_storage = 512;
+
+	if (i->ac)
+		spa->storage = lwsac_use(i->ac, spa->i.max_storage,
+					 i->ac_chunk_size);
+	else
+		spa->storage = lws_malloc(spa->i.max_storage, "spa");
 
-	spa->storage = lws_malloc(max_storage, "spa");
 	if (!spa->storage)
 		goto bail2;
-	spa->end = spa->storage + max_storage - 1;
 
-	if (count_params) {
-		spa->params = lws_zalloc(sizeof(char *) * count_params, "spa params");
+	spa->end = spa->storage + i->max_storage - 1;
+
+	if (i->count_params) {
+		if (i->ac)
+			spa->params = lwsac_use_zero(i->ac,
+				sizeof(char *) * i->count_params, i->ac_chunk_size);
+		else
+			spa->params = lws_zalloc(sizeof(char *) * i->count_params,
+					 "spa params");
 		if (!spa->params)
 			goto bail3;
 	}
 
-	spa->s = lws_urldecode_s_create(wsi, spa->storage, max_storage, spa,
+	spa->s = lws_urldecode_s_create(spa, wsi, spa->storage, i->max_storage,
 					lws_urldecode_spa_cb);
 	if (!spa->s)
 		goto bail4;
 
-	if (count_params) {
-		spa->param_length = lws_zalloc(sizeof(int) * count_params,
+	if (i->count_params) {
+		if (i->ac)
+			spa->param_length = lwsac_use_zero(i->ac,
+				sizeof(int) * i->count_params, i->ac_chunk_size);
+		else
+			spa->param_length = lws_zalloc(sizeof(int) * i->count_params,
 						"spa param len");
 		if (!spa->param_length)
 			goto bail5;
@@ -536,57 +569,80 @@ lws_spa_create(struct lws *wsi, const char * const *param_names,
 	return spa;
 
 bail5:
-	lws_urldecode_s_destroy(spa->s);
+	lws_urldecode_s_destroy(spa, spa->s);
 bail4:
-	lws_free(spa->params);
+	if (!i->ac)
+		lws_free(spa->params);
 bail3:
-	lws_free(spa->storage);
+	if (!i->ac)
+		lws_free(spa->storage);
 bail2:
-	lws_free(spa);
+	if (!i->ac)
+		lws_free(spa);
+
+	if (i->ac)
+		lwsac_free(i->ac);
 
 	return NULL;
 }
 
-LWS_VISIBLE LWS_EXTERN int
-lws_spa_process(struct lws_spa *ludspa, const char *in, int len)
+struct lws_spa *
+lws_spa_create(struct lws *wsi, const char * const *param_names,
+	       int count_params, int max_storage,
+	       lws_spa_fileupload_cb opt_cb, void *opt_data)
+{
+	lws_spa_create_info_t i;
+
+	memset(&i, 0, sizeof(i));
+	i.count_params = count_params;
+	i.max_storage = max_storage;
+	i.opt_cb = opt_cb;
+	i.opt_data = opt_data;
+	i.param_names = param_names;
+
+	return lws_spa_create_via_info(wsi, &i);
+}
+
+int
+lws_spa_process(struct lws_spa *spa, const char *in, int len)
 {
-	if (!ludspa) {
+	if (!spa) {
 		lwsl_err("%s: NULL spa\n", __func__);
 		return -1;
 	}
 	/* we reject any junk after the last part arrived and we finalized */
-	if (ludspa->finalized)
+	if (spa->finalized)
 		return 0;
 
-	return lws_urldecode_s_process(ludspa->s, in, len);
+	return lws_urldecode_s_process(spa->s, in, len);
 }
 
-LWS_VISIBLE LWS_EXTERN int
-lws_spa_get_length(struct lws_spa *ludspa, int n)
+int
+lws_spa_get_length(struct lws_spa *spa, int n)
 {
-	if (n >= ludspa->count_params)
+	if (n >= spa->i.count_params)
 		return 0;
 
-	return ludspa->param_length[n];
+	return spa->param_length[n];
 }
 
-LWS_VISIBLE LWS_EXTERN const char *
-lws_spa_get_string(struct lws_spa *ludspa, int n)
+const char *
+lws_spa_get_string(struct lws_spa *spa, int n)
 {
-	if (n >= ludspa->count_params)
+	if (n >= spa->i.count_params)
 		return NULL;
 
-	return ludspa->params[n];
+	return spa->params[n];
 }
 
-LWS_VISIBLE LWS_EXTERN int
+int
 lws_spa_finalize(struct lws_spa *spa)
 {
 	if (!spa)
 		return 0;
 
 	if (spa->s) {
-		lws_urldecode_s_destroy(spa->s);
+		lws_urldecode_s_destroy(spa, spa->s);
 		spa->s = NULL;
 	}
 
@@ -595,7 +651,7 @@ lws_spa_finalize(struct lws_spa *spa)
 	return 0;
 }
 
-LWS_VISIBLE LWS_EXTERN int
+int
 lws_spa_destroy(struct lws_spa *spa)
 {
 	int n = 0;
@@ -603,15 +659,16 @@ lws_spa_destroy(struct lws_spa *spa)
 	lwsl_info("%s: destroy spa %p\n", __func__, spa);
 
 	if (spa->s)
-		lws_urldecode_s_destroy(spa->s);
-
-	lwsl_debug("%s %p %p %p %p\n", __func__, spa->param_length,
-		   spa->params, spa->storage, spa);
-
-	lws_free(spa->param_length);
-	lws_free(spa->params);
-	lws_free(spa->storage);
-	lws_free(spa);
+		lws_urldecode_s_destroy(spa, spa->s);
+
+	if (spa->i.ac)
+		lwsac_free(spa->i.ac);
+	else {
+		lws_free(spa->param_length);
+		lws_free(spa->params);
+		lws_free(spa->storage);
+		lws_free(spa);
+	}
 
 	return n;
 }
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..eec5b06a8eaaed38a9dc67184ba6de956cc97ce4
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-minimal-http-server-form-post-lwsac)
+set(SRCS minimal-http-server-form-post.c)
+
+# If we are being built as part of lws, confirm current build config supports
+# reqconfig, else skip building ourselves.
+#
+# If we are being built externally, confirm installed lws was configured to
+# support reqconfig, else error out with a helpful message about the problem.
+#
+MACRO(require_lws_config reqconfig _val result)
+
+	if (DEFINED ${reqconfig})
+	if (${reqconfig})
+		set (rq 1)
+	else()
+		set (rq 0)
+	endif()
+	else()
+		set(rq 0)
+	endif()
+
+	if (${_val} EQUAL ${rq})
+		set(SAME 1)
+	else()
+		set(SAME 0)
+	endif()
+
+	if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
+		if (${_val})
+			message("${SAMP}: skipping as lws being built without ${reqconfig}")
+		else()
+			message("${SAMP}: skipping as lws built with ${reqconfig}")
+		endif()
+		set(${result} 0)
+	else()
+		if (LWS_WITH_MINIMAL_EXAMPLES)
+			set(MET ${SAME})
+		else()
+			CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
+			if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
+				set(HAS_${reqconfig} 0)
+			else()
+				set(HAS_${reqconfig} 1)
+			endif()
+			if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
+				set(MET 1)
+			else()
+				set(MET 0)
+			endif()
+		endif()
+		if (NOT MET)
+			if (${_val})
+				message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
+			else()
+				message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
+			endif()
+		endif()
+	endif()
+ENDMACRO()
+
+set(requirements 1)
+require_lws_config(LWS_ROLE_H1 1 requirements)
+require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
+
+if (requirements)
+	add_executable(${SAMP} ${SRCS})
+
+	if (websockets_shared)
+		target_link_libraries(${SAMP} websockets_shared)
+		add_dependencies(${SAMP} websockets_shared)
+	else()
+		target_link_libraries(${SAMP} websockets)
+	endif()
+endif()
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..910b4ceca3608e54fbfa1f83540f769fbe24bd0d
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/README.md
@@ -0,0 +1,23 @@
+# lws minimal http server form POST lwsac
+
+Shows how to parse the form using an lwsac to hold the form data
+
+## build
+
+```
+ $ cmake . && make
+```
+
+## usage
+
+```
+ $ ./lws-minimal-http-server-form-post-lwsac
+[2018/03/29 08:29:41:7044] USER: LWS minimal http server form POST | visit http://localhost:7681
+[2018/03/29 08:29:41:7044] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
+[2018/03/29 08:29:49:8601] USER: text1: (len 4) 'xxxx'
+[2018/03/29 08:29:49:8601] USER: send: (len 6) 'Submit'
+```
+
+Visit http://localhost:7681, submit the form.
+
+The form parameters are dumped to the log and you are redirected to a different page.
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert
new file mode 100644
index 0000000000000000000000000000000000000000..6f372db40ad29f5bc1e0463078d48f2bd8d5c60c
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.cert
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF5jCCA86gAwIBAgIJANq50IuwPFKgMA0GCSqGSIb3DQEBCwUAMIGGMQswCQYD
+VQQGEwJHQjEQMA4GA1UECAwHRXJld2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEb
+MBkGA1UECgwSbGlid2Vic29ja2V0cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3Qx
+HzAdBgkqhkiG9w0BCQEWEG5vbmVAaW52YWxpZC5vcmcwIBcNMTgwMzIwMDQxNjA3
+WhgPMjExODAyMjQwNDE2MDdaMIGGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRXJl
+d2hvbjETMBEGA1UEBwwKQWxsIGFyb3VuZDEbMBkGA1UECgwSbGlid2Vic29ja2V0
+cy10ZXN0MRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEG5vbmVA
+aW52YWxpZC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjYtuW
+aICCY0tJPubxpIgIL+WWmz/fmK8IQr11Wtee6/IUyUlo5I602mq1qcLhT/kmpoR8
+Di3DAmHKnSWdPWtn1BtXLErLlUiHgZDrZWInmEBjKM1DZf+CvNGZ+EzPgBv5nTek
+LWcfI5ZZtoGuIP1Dl/IkNDw8zFz4cpiMe/BFGemyxdHhLrKHSm8Eo+nT734tItnH
+KT/m6DSU0xlZ13d6ehLRm7/+Nx47M3XMTRH5qKP/7TTE2s0U6+M0tsGI2zpRi+m6
+jzhNyMBTJ1u58qAe3ZW5/+YAiuZYAB6n5bhUp4oFuB5wYbcBywVR8ujInpF8buWQ
+Ujy5N8pSNp7szdYsnLJpvAd0sibrNPjC0FQCNrpNjgJmIK3+mKk4kXX7ZTwefoAz
+TK4l2pHNuC53QVc/EF++GBLAxmvCDq9ZpMIYi7OmzkkAKKC9Ue6Ef217LFQCFIBK
+Izv9cgi9fwPMLhrKleoVRNsecBsCP569WgJXhUnwf2lon4fEZr3+vRuc9shfqnV0
+nPN1IMSnzXCast7I2fiuRXdIz96KjlGQpP4XfNVA+RGL7aMnWOFIaVrKWLzAtgzo
+GMTvP/AuehKXncBJhYtW0ltTioVx+5yTYSAZWl+IssmXjefxJqYi2/7QWmv1QC9p
+sNcjTMaBQLN03T1Qelbs7Y27sxdEnNUth4kI+wIDAQABo1MwUTAdBgNVHQ4EFgQU
+9mYU23tW2zsomkKTAXarjr2vjuswHwYDVR0jBBgwFoAU9mYU23tW2zsomkKTAXar
+jr2vjuswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANjIBMrow
+YNCbhAJdP7dhlhT2RUFRdeRUJD0IxrH/hkvb6myHHnK8nOYezFPjUlmRKUgNEDuA
+xbnXZzPdCRNV9V2mShbXvCyiDY7WCQE2Bn44z26O0uWVk+7DNNLH9BnkwUtOnM9P
+wtmD9phWexm4q2GnTsiL6Ul6cy0QlTJWKVLEUQQ6yda582e23J1AXqtqFcpfoE34
+H3afEiGy882b+ZBiwkeV+oq6XVF8sFyr9zYrv9CvWTYlkpTQfLTZSsgPdEHYVcjv
+xQ2D+XyDR0aRLRlvxUa9dHGFHLICG34Juq5Ai6lM1EsoD8HSsJpMcmrH7MWw2cKk
+ujC3rMdFTtte83wF1uuF4FjUC72+SmcQN7A386BC/nk2TTsJawTDzqwOu/VdZv2g
+1WpTHlumlClZeP+G/jkSyDwqNnTu1aodDmUa4xZodfhP1HWPwUKFcq8oQr148QYA
+AOlbUOJQU7QwRWd1VbnwhDtQWXC92A2w1n/xkZSR1BM/NUSDhkBSUU1WjMbWg6Gg
+mnIZLRerQCu1Oozr87rOQqQakPkyt8BUSNK3K42j2qcfhAONdRl8Hq8Qs5pupy+s
+8sdCGDlwR3JNCMv6u48OK87F4mcIxhkSefFJUFII25pCGN5WtE4p5l+9cnO1GrIX
+e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=
+-----END CERTIFICATE-----
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key
new file mode 100644
index 0000000000000000000000000000000000000000..148f8598ee1b61bcdfd35bba618fb010d8772f39
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/localhost-100y.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+fBrpEY1IATtPq1taBZZogRqI3rOkkPk=
+-----END PRIVATE KEY-----
diff --git a/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c
new file mode 100644
index 0000000000000000000000000000000000000000..1616d0718793aeb8915b0130aff79d38d9e33ade
--- /dev/null
+++ b/minimal-examples/http-server/minimal-http-server-form-post-lwsac/minimal-http-server-form-post.c
@@ -0,0 +1,217 @@
+/*
+ * lws-minimal-http-server-form-post-lwsac
+ *
+ * Copyright (C) 2018 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * This demonstrates a minimal http server that performs POST with a couple
+ * of parameters.  It dumps the parameters to the console log and redirects
+ * to another page.
+ */
+
+#include <libwebsockets.h>
+#include <string.h>
+#include <signal.h>
+
+/*
+ * Unlike ws, http is a stateless protocol.  This pss only exists for the
+ * duration of a single http transaction.  With http/1.1 keep-alive and http/2,
+ * that is unrelated to (shorter than) the lifetime of the network connection.
+ */
+struct pss {
+	struct lws_spa *spa;
+	struct lwsac *ac;
+};
+
+static int interrupted;
+
+static const char * const param_names[] = {
+	"text1",
+	"send",
+};
+
+enum enum_param_names {
+	EPN_TEXT1,
+	EPN_SEND,
+};
+
+static int
+callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
+	      void *in, size_t len)
+{
+	struct pss *pss = (struct pss *)user;
+	uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE], *start = &buf[LWS_PRE],
+		*p = start, *end = &buf[sizeof(buf) - 1];
+	int n;
+
+	switch (reason) {
+	case LWS_CALLBACK_HTTP:
+
+		/*
+		 * Manually report that our form target URL exists
+		 *
+		 * you can also do this by adding a mount for the form URL
+		 * to the protocol with type LWSMPRO_CALLBACK, then no need
+		 * to trap LWS_CALLBACK_HTTP.
+		 */
+
+		if (!strcmp((const char *)in, "/form1"))
+			/* assertively allow it to exist in the URL space */
+			return 0;
+
+		/* default to 404-ing the URL if not mounted */
+		break;
+
+	case LWS_CALLBACK_HTTP_BODY:
+
+		/* create the POST argument parser if not already existing */
+
+		if (!pss->spa) {
+			lws_spa_create_info_t i;
+
+			memset(&i, 0, sizeof(i));
+			i.param_names = param_names;
+			i.count_params = LWS_ARRAY_SIZE(param_names);
+			i.ac = &pss->ac;
+			i.ac_chunk_size = 512;
+
+			pss->spa = lws_spa_create_via_info(wsi, &i); /* no file upload */
+			if (!pss->spa)
+				return -1;
+		}
+
+		/* let it parse the POST data */
+
+		if (lws_spa_process(pss->spa, in, (int)len))
+			return -1;
+		break;
+
+	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
+
+		/* inform the spa no more payload data coming */
+
+		lwsl_user("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
+		lws_spa_finalize(pss->spa);
+
+		/* we just dump the decoded things to the log */
+
+		for (n = 0; n < (int)LWS_ARRAY_SIZE(param_names); n++) {
+			if (!lws_spa_get_string(pss->spa, n))
+				lwsl_user("%s: undefined\n", param_names[n]);
+			else
+				lwsl_user("%s: (len %d) '%s'\n",
+				    param_names[n],
+				    lws_spa_get_length(pss->spa, n),
+				    lws_spa_get_string(pss->spa, n));
+		}
+
+		lwsac_free(&pss->ac);
+
+		/*
+		 * Our response is to redirect to a static page.  We could
+		 * have generated a dynamic html page here instead.
+		 */
+
+		if (lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
+				      (unsigned char *)"after-form1.html",
+				      16, &p, end) < 0)
+			return -1;
+		break;
+
+	case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
+		/* called when our wsi user_space is going to be destroyed */
+		if (pss->spa) {
+			lws_spa_destroy(pss->spa);
+			pss->spa = NULL;
+		}
+		lwsac_free(&pss->ac);
+		break;
+
+	default:
+		break;
+	}
+
+	return lws_callback_http_dummy(wsi, reason, user, in, len);
+}
+
+static struct lws_protocols protocols[] = {
+	{ "http", callback_http, sizeof(struct pss), 0 },
+	{ NULL, NULL, 0, 0 } /* terminator */
+};
+
+/* default mount serves the URL space from ./mount-origin */
+
+static const struct lws_http_mount mount = {
+	/* .mount_next */	       NULL,		/* linked-list "next" */
+	/* .mountpoint */		"/",		/* mountpoint URL */
+	/* .origin */		"./mount-origin",	/* serve from dir */
+	/* .def */			"index.html",	/* default filename */
+	/* .protocol */			NULL,
+	/* .cgienv */			NULL,
+	/* .extra_mimetypes */		NULL,
+	/* .interpret */		NULL,
+	/* .cgi_timeout */		0,
+	/* .cache_max_age */		0,
+	/* .auth_mask */		0,
+	/* .cache_reusable */		0,
+	/* .cache_revalidate */		0,
+	/* .cache_intermediaries */	0,
+	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
+	/* .mountpoint_len */		1,		/* char count */
+	/* .basic_auth_login_file */	NULL,
+};
+
+void sigint_handler(int sig)
+{
+	interrupted = 1;
+}
+
+int main(int argc, const char **argv)
+{
+	struct lws_context_creation_info info;
+	struct lws_context *context;
+	const char *p;
+	int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
+			/* for LLL_ verbosity above NOTICE to be built into lws,
+			 * lws must have been configured and built with
+			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
+			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
+			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
+			/* | LLL_DEBUG */;
+
+	signal(SIGINT, sigint_handler);
+
+	if ((p = lws_cmdline_option(argc, argv, "-d")))
+		logs = atoi(p);
+
+	lws_set_log_level(logs, NULL);
+	lwsl_user("LWS minimal http server POST | visit http://localhost:7681\n");
+
+	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+	info.port = 7681;
+	info.protocols = protocols;
+	info.mounts = &mount;
+	info.options =
+		LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
+
+	if (lws_cmdline_option(argc, argv, "-s")) {
+		info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+		info.ssl_cert_filepath = "localhost-100y.cert";
+		info.ssl_private_key_filepath = "localhost-100y.key";
+	}
+
+	context = lws_create_context(&info);
+	if (!context) {
+		lwsl_err("lws init failed\n");
+		return 1;
+	}
+
+	while (n >= 0 && !interrupted)
+		n = lws_service(context, 1000);
+
+	lws_context_destroy(context);
+
+	return 0;
+}