diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7149959b73e54203d80ecef23cb4ff6ebfc2f073..89372d9a508672bcc8bc4f8543ce7ed41d299db4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@ project(libwebsockets C)
 set(PACKAGE "libwebsockets")
 set(CPACK_PACKAGE_NAME "${PACKAGE}")
 set(CPACK_PACKAGE_VERSION_MAJOR "2")
-set(CPACK_PACKAGE_VERSION_MINOR "3")
+set(CPACK_PACKAGE_VERSION_MINOR "4")
 set(CPACK_PACKAGE_VERSION_PATCH "0")
 set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
 set(CPACK_PACKAGE_VENDOR "andy@warmcat.com")
@@ -96,7 +96,7 @@ option(LWS_WITH_LATENCY "Build latency measuring code into the library" OFF)
 option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON)
 option(LWS_IPV6 "Compile with support for ipv6" OFF)
 option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" OFF)
-#option(LWS_WITH_HTTP2 "Compile with support for http2" OFF)
+option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" OFF)
 option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
 option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
 option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying (requires libhubbub)" OFF)
@@ -628,7 +628,7 @@ if (NOT LWS_WITHOUT_CLIENT)
 		lib/client-parser.c)
 endif()
 
-if (LWS_WITH_MBEDTLS AND NOT LWS_WITH_ESP32)
+if (LWS_WITH_MBEDTLS)
  	set(LWS_WITH_SSL ON)
 
 	list(APPEND HDR_PRIVATE
@@ -1474,6 +1474,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
 			"${PROJECT_SOURCE_DIR}/test-apps/leaf.jpg"
 			"${PROJECT_SOURCE_DIR}/test-apps/candide.zip"
 			"${PROJECT_SOURCE_DIR}/test-apps/libwebsockets.org-logo.png"
+			"${PROJECT_SOURCE_DIR}/test-apps/http2.png"
 			"${PROJECT_SOURCE_DIR}/test-apps/lws-common.js"
 			"${PROJECT_SOURCE_DIR}/test-apps/test.html")
 
diff --git a/READMEs/README.build.md b/READMEs/README.build.md
index 811083d9a38758569af4d97a09e6d0952f071e54..d065eb61b65e96a445b7dd04e1bd89356a262b7e 100644
--- a/READMEs/README.build.md
+++ b/READMEs/README.build.md
@@ -251,6 +251,31 @@ deleting build/CMakeCache.txt may be enough.
 	$ make install
 ```
 
+@section ssllib Choosing Your TLS Poison
+
+ - If you are really restricted on memory, code size, or don't care about TLS
+   speed, mbedTLS is a good choice: `cmake .. -DLWS_WITH_MBEDTLS=1`
+ 
+ - If cpu and memory is not super restricted and you care about TLS speed,
+   OpenSSL or a directly compatible variant like Boring SSL is a good choice.
+ 
+Just building lws against stock Fedora OpenSSL or stock Fedora mbedTLS, for
+SSL handhake mbedTLS takes ~36ms and OpenSSL takes ~1ms on the same x86_64
+build machine here, with everything else the same.  Over the 144 connections of
+h2spec compliance testing for example, this ends up completing in 400ms for
+OpenSSL and 5.5sec for mbedTLS on x86_64.  In other words mbedTLS is very slow
+compared to OpenSSL under the (fairly typical) conditions I tested it.
+
+This isn't an inefficiency in the mbedtls interface implementation, it's just
+mbedTLS doing the crypto much slower than OpenSSL, which has accelerated
+versions of common crypto operations it automatically uses for platforms
+supporting it.  As of Oct 2017 mbedTLS itself has no such optimizations for any
+platform that I could find.  It's just pure C running on the CPU.
+
+Lws supports both almost the same, so instead of taking my word for it you are
+invited to try it both ways and see which the results (including, eg, binary
+size and memory usage as well as speed) suggest you use.
+
 @section optee Building for OP-TEE
 
 OP-TEE is a "Secure World" Trusted Execution Environment.
@@ -376,11 +401,14 @@ additionally, discovered plugins are not enabled automatically for security
 reasons.  You do this using info->pvo or for lwsws, in the JSON config.
 
 
-@section http2rp Reproducing HTTP2.0 tests
+@section http2rp Reproducing HTTP/2 tests
+
+Enable `-DLWS_WITH_HTTP2=1` in cmake to build with http/2 support enabled.
 
 You must have built and be running lws against a version of openssl that has
-ALPN / NPN.  Most distros still have older versions.  You'll know it's right by
-seeing
+ALPN.  At the time of writing, recent distros have started upgrading to OpenSSL
+1.1+ that supports this already.  You'll know it's right by seeing
+
 ```
 	lwsts[4752]:  Compiled with OpenSSL support
 	lwsts[4752]:  Using SSL mode
@@ -388,14 +416,29 @@ seeing
 ```
 at lws startup.
 
-For non-SSL HTTP2.0 upgrade
+Recent Firefox and Chrome also support HTTP/2 by ALPN, so these should just work
+with the test server running in -s / ssl mode.
+
+For testing with nghttp client:
+
 ```
-	$ nghttp -nvasu http://localhost:7681/test.htm
+	$ nghttp -nvas https://localhost:7681/test.html
 ```
-For SSL / ALPN HTTP2.0 upgrade
+
+Testing with h2spec (https://github.com/summerwind/h2spec)
+
 ```
-	$ nghttp -nvas https://localhost:7681/test.html
+        $ h2spec  -h 127.0.0.1 -p 7681 -t -k -v -o 1
+```
+
+At the time of writing, http/2 support is not fully complete; however all the
+h2spec tests pass.
+
 ```
+145 tests, 144 passed, 1 skipped, 0 failed
+
+```
+
 
 @section cross Cross compiling
 
@@ -537,7 +580,7 @@ chmod 644 /tmp/cross/include/zlib.h /tmp/cross/include/zconf.h
 
 3) `cd mbedtls ; mkdir build ; cd build`
 
-3) `cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile -DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross -DUSE_SHARED_MBEDTLS_LIBRARY=1`  mbedtls also uses cmake, so you can simply reuse the toolchain file you used for libwebsockets.  That is why you shouldn't put project-specific options in the toolchain file, it should just describe the toolchain.
+3) `cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile -DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross -DCMAKE_BUILD_TYPE=RELEASE -DUSE_SHARED_MBEDTLS_LIBRARY=1`  mbedtls also uses cmake, so you can simply reuse the toolchain file you used for libwebsockets.  That is why you shouldn't put project-specific options in the toolchain file, it should just describe the toolchain.
 
 4) `make && make install`
 
diff --git a/READMEs/README.coding.md b/READMEs/README.coding.md
index e6afe3fa4fc4d93e28ea2ec73ed02dece2c0abf9..7e934ba52e26ef1ce859870e792b7a3785466319 100644
--- a/READMEs/README.coding.md
+++ b/READMEs/README.coding.md
@@ -332,6 +332,75 @@ isn't processed by user code before then should be copied out for later.
 For HTTP connections that don't upgrade, header info remains available the
 whole time.
 
+@section http2compat Code Requirements for HTTP/2 compatibility
+
+Websocket connections only work over http/1, so there is nothing special to do
+when you want to enable -DLWS_WITH_HTTP2=1.
+
+The internal http apis already follow these requirements and are compatible with
+http/2 already.  So if you use stuff like mounts and serve stuff out of the
+filesystem, there's also nothing special to do.
+
+However if you are getting your hands dirty with writing response headers, or
+writing bulk data over http/2, you need to observe these rules so that it will
+work over both http/1.x and http/2 the same.
+
+1) LWS_PRE requirement applies on ALL lws_write().  For http/1, you don't have
+to take care of LWS_PRE for http data, since it is just sent straight out.
+For http/2, it will write up to LWS_PRE bytes behind the buffer start to create
+the http/2 frame header.
+
+This has implications if you treated the input buffer to lws_write() as const...
+it isn't any more with http/2, up to 9 bytes behind the buffer will be trashed.
+
+2) Headers are encoded using a sophisticated scheme in http/2.  The existing
+header access apis are already made compatible for incoming headers,
+for outgoing headers you must:
+
+ - observe the LWS_PRE buffer requirement mentioned above
+ 
+ - Use `lws_add_http_header_status()` to add the transaction status (200 etc)
+ 
+ - use lws apis `lws_add_http_header_by_name()` and `lws_add_http_header_by_token()`
+   to put the headers into the buffer (these will translate what is actually
+   written to the buffer depending on if the connection is in http/2 mode or not)
+   
+ - use the `lws api lws_finalize_http_header()` api after adding the last
+   response header
+   
+ - write the header using lws_write(..., `LWS_WRITE_HTTP_HEADERS`);
+ 
+ 3) http/2 introduces per-stream transmit credit... how much more you can send
+ on a stream is decided by the peer.  You start off with some amount, as the
+ stream sends stuff lws will reduce your credit accordingly, when it reaches
+ zero, you must not send anything further until lws receives "more credit" for
+ that stream the peer.  Lws will suppress writable callbacks if you hit 0 until
+ more credit for the stream appears, and lws built-in file serving (via mounts
+ etc) already takes care of observing the tx credit restrictions.  However if
+ you write your own code that wants to send http data, you must consult the
+ `lws_get_peer_write_allowance()` api to find out the state of your tx credit.
+ For http/1, it will always return (size_t)-1, ie, no limit.
+ 
+ This is orthogonal to the question of how much space your local side's kernel
+ will make to buffer your send data on that connection.  So although the result
+ from `lws_get_peer_write_allowance()` is "how much you can send" logically,
+ and may be megabytes if the peer allows it, you should restrict what you send
+ at one time to whatever your machine will generally accept in one go, and
+ further reduce that amount if `lws_get_peer_write_allowance()` returns
+ something smaller.  If it returns 0, you should not consume or send anything
+ and return having asked for callback on writable, it will only come back when
+ more tx credit has arrived for your stream.
+ 
+ 4) Header names with captital letters are illegal in http/2.  Header names in
+ http/1 are case insensitive.  So if you generate headers by name, change all
+ your header name strings to lower-case to be compatible both ways.
+ 
+ 5) Chunked Transfer-encoding is illegal in http/2, http/2 peers will actively
+ reject it.  Lws takes care of removing the header and converting CGIs that
+ emit chunked into unchunked automatically for http/2 connections.
+ 
+If you follow these rules, your code will automatically work with both http/1.x
+and http/2.
 
 @section ka TCP Keepalive
 
diff --git a/READMEs/mainpage.md b/READMEs/mainpage.md
index 9a427b39afbe5d89989e025261743e5d3e829d3c..33b1348cfb4f66beacd1d402e122b856f7b9b64c 100644
--- a/READMEs/mainpage.md
+++ b/READMEs/mainpage.md
@@ -2,13 +2,17 @@
 
 Libwebsockets covers a lot of interesting features for people making embedded servers or clients
 
- - http(s) serving and client operation
- - ws(s) serving and client operation
- - http(s) apis for file transfer and upload
- - http POST form handling (including multipart)
+ - HTTP(S) serving and client operation
+ - HTTP/2 support for serving
+ - WS(S) serving and client operation
+ - HTTP(S) apis for file transfer and upload
+ - HTTP 1 + 2 POST form handling (including multipart / file upload)
  - cookie-based sessions
  - account management (including registration, email verification, lost pw etc)
- - strong ssl PFS support (A+ on SSLlabs test)
+ - strong SSL / TLS  PFS support (A+ on SSLlabs test)
+ - ssh server integration
+ - serving gzipped files directly from inside zip files, without conversion
+ - support for linux, bsd, windows etc... and very small nonlinux targets like ESP32
 
 You can browse by api category <a href="modules.html">here</a>
 
diff --git a/component.mk b/component.mk
index 236395a4d63bffd880b5f761f0647cfdbc48472e..8baf3d6daa72df1814dfc760b9e74dce81fedd97 100644
--- a/component.mk
+++ b/component.mk
@@ -19,7 +19,7 @@ CROSS_PATH:= $(shell dirname $(CROSS_PATH1) )/..
 build:
 	cd $(COMPONENT_BUILD_DIR) ; \
 	echo "doing lws cmake" ; \
-	cmake $(COMPONENT_PATH)  -DLWS_C_FLAGS="$(CFLAGS) -DNDEBUG=1 " \
+	cmake $(COMPONENT_PATH)  -DLWS_C_FLAGS="$(CFLAGS) " \
 		-DIDF_PATH=$(IDF_PATH) \
 		-DCROSS_PATH=$(CROSS_PATH) \
 		-DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \
@@ -27,6 +27,7 @@ build:
 		-DCMAKE_BUILD_TYPE=RELEASE \
 		-DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \
 		-DLWS_WITH_STATS=0 \
+		-DLWS_WITH_HTTP2=1 \
 		-DZLIB_LIBRARY=$(BUILD_DIR_BASE)/zlib/libzlib.a \
 		-DZLIB_INCLUDE_DIR=$(COMPONENT_PATH)/../zlib \
 		-DLWS_WITH_ESP32=1 ;\
diff --git a/lib/alloc.c b/lib/alloc.c
index e53c356cae974af9cc674ad4a437a4b50c35abba..898db12464aa538e0e45da96c6e7667530138d36 100644
--- a/lib/alloc.c
+++ b/lib/alloc.c
@@ -51,7 +51,11 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
 static void *_realloc(void *ptr, size_t size, const char *reason)
 {
 	if (size) {
+#if defined(LWS_PLAT_ESP32)
+		lwsl_notice("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+#else
 		lwsl_debug("%s: size %lu: %s\n", __func__, (unsigned long)size, reason);
+#endif
 #if defined(LWS_PLAT_OPTEE)
 		return (void *)TEE_Realloc(ptr, size);
 #else
diff --git a/lib/client.c b/lib/client.c
index 54ffb59baa058534902f0d9d99f8a15d7f47dd57..b2a4c12f534286b93de3697232751ed22467e9fa 100755
--- a/lib/client.c
+++ b/lib/client.c
@@ -36,7 +36,7 @@ lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
 			/*
 			 * we were accepting input but now we stopped doing so
 			 */
-			if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+			if (lws_is_flowcontrolled(wsi)) {
 				lwsl_debug("%s: caching %ld\n", __func__, (long)len);
 				lws_rxflow_cache(wsi, *buf, 0, len);
 				return 0;
@@ -308,7 +308,6 @@ start_ws_handshake:
 		}
 
 		/* send our request to the server */
-
 		lws_latency_pre(context, wsi);
 
 		n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb);
@@ -350,10 +349,12 @@ client_http_body_sent:
 		break;
 
 	case LWSCM_WSCL_WAITING_SERVER_REPLY:
-
-		/* handle server hung up on us */
-
-		if (pollfd->revents & LWS_POLLHUP) {
+		/*
+		 * handle server hanging up on us...
+		 * but if there is POLLIN waiting, handle that first
+		 */
+		if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
+								LWS_POLLHUP) {
 
 			lwsl_debug("Server connection %p (fd=%d) dead\n",
 				(void *)wsi, pollfd->fd);
@@ -364,18 +365,15 @@ client_http_body_sent:
 		if (!(pollfd->revents & LWS_POLLIN))
 			break;
 
-		/* interpret the server response */
-
-		/*
+		/* interpret the server response
+		 *
 		 *  HTTP/1.1 101 Switching Protocols
 		 *  Upgrade: websocket
 		 *  Connection: Upgrade
 		 *  Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
 		 *  Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
 		 *  Sec-WebSocket-Protocol: chat
-		 */
-
-		/*
+		 *
 		 * we have to take some care here to only take from the
 		 * socket bytewise.  The browser may (and has been seen to
 		 * in the case that onopen() performs websocket traffic)
@@ -409,7 +407,6 @@ client_http_body_sent:
 		 * libwebsocket timeout still active here too, so if parsing did
 		 * not complete just wait for next packet coming in this state
 		 */
-
 		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
 			break;
 
@@ -418,7 +415,6 @@ client_http_body_sent:
 		 * packet traffic already arrived we'll trigger poll() again
 		 * right away and deal with it that way
 		 */
-
 		return lws_client_interpret_server_handshake(wsi);
 
 bail3:
@@ -480,7 +476,7 @@ lws_http_transaction_completed_client(struct lws *wsi)
 	/* otherwise set ourselves up ready to go again */
 	wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
 	wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
-	wsi->u.http.content_length = 0;
+	wsi->u.http.rx_content_length = 0;
 	wsi->hdr_parsing_completed = 0;
 
 	/* He asked for it to stay alive indefinitely */
@@ -694,12 +690,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 		}
 
 		if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
-			wsi->u.http.content_length =
+			wsi->u.http.rx_content_length =
 					atoll(lws_hdr_simple_ptr(wsi,
 						WSI_TOKEN_HTTP_CONTENT_LENGTH));
 			lwsl_notice("%s: incoming content length %llu\n", __func__,
-					(unsigned long long)wsi->u.http.content_length);
-			wsi->u.http.content_remain = wsi->u.http.content_length;
+					(unsigned long long)wsi->u.http.rx_content_length);
+			wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
 		} else /* can't do 1.1 without a content length or chunked */
 			if (!wsi->chunked)
 				wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
diff --git a/lib/context.c b/lib/context.c
index 8b5512b2ac41034605a48706234567ef50beeb81..ae96ddfea406f6bedafbc4235c059fd996b82218 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -50,6 +50,64 @@ static const char * const mount_protocols[] = {
 	"callback://"
 };
 
+#if defined(LWS_WITH_HTTP2)
+/*
+ * These are the standardized defaults.
+ * Override what actually goes in the vhost settings in platform or user code.
+ * Leave these alone because they are used to determine "what is different
+ * from the protocol defaults".
+ */
+const struct http2_settings lws_h2_defaults = { {
+	1,
+	/* H2SET_HEADER_TABLE_SIZE */			4096,
+	/* *** This controls how many entries in the dynamic table ***
+	 * Allows the sender to inform the remote endpoint of the maximum
+	 * size of the header compression table used to decode header
+	 * blocks, in octets.  The encoder can select any size equal to or
+	 * less than this value by using signaling specific to the header
+	 * compression format inside a header block (see [COMPRESSION]).
+	 * The initial value is 4,096 octets.
+	 */
+	/* H2SET_ENABLE_PUSH */				   1,
+	/* H2SET_MAX_CONCURRENT_STREAMS */	  0x7fffffff,
+	/* H2SET_INITIAL_WINDOW_SIZE */		       65535,
+	/* H2SET_MAX_FRAME_SIZE */		       16384,
+	/* H2SET_MAX_HEADER_LIST_SIZE */	  0x7fffffff,
+	/*< This advisory setting informs a peer of the maximum size of
+	 * header list that the sender is prepared to accept, in octets.
+	 * The value is based on the uncompressed size of header fields,
+	 * including the length of the name and value in octets plus an
+	 * overhead of 32 octets for each header field.
+	 */
+
+}};
+
+const struct http2_settings lws_h2_stock_settings = { {
+	1,
+	/* H2SET_HEADER_TABLE_SIZE */			 512,
+	/* *** This controls how many entries in the dynamic table ***
+	 * Allows the sender to inform the remote endpoint of the maximum
+	 * size of the header compression table used to decode header
+	 * blocks, in octets.  The encoder can select any size equal to or
+	 * less than this value by using signaling specific to the header
+	 * compression format inside a header block (see [COMPRESSION]).
+	 * The initial value is 4,096 octets.
+	 */
+	/* H2SET_ENABLE_PUSH */				   1,
+	/* H2SET_MAX_CONCURRENT_STREAMS */		  24,
+	/* H2SET_INITIAL_WINDOW_SIZE */		       65535,
+	/* H2SET_MAX_FRAME_SIZE */		       16384,
+	/* H2SET_MAX_HEADER_LIST_SIZE */	  	4096,
+	/*< This advisory setting informs a peer of the maximum size of
+	 * header list that the sender is prepared to accept, in octets.
+	 * The value is based on the uncompressed size of header fields,
+	 * including the length of the name and value in octets plus an
+	 * overhead of 32 octets for each header field.
+	 */
+
+}};
+#endif
+
 LWS_VISIBLE void *
 lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost,
 			    const struct lws_protocols *prot, int size)
@@ -268,9 +326,15 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
 		}
 
 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
-			lwsl_debug("writing chunk terminator and exiting\n");
-			n = lws_write(wsi, (unsigned char *)"0\x0d\x0a\x0d\x0a",
-				      5, LWS_WRITE_HTTP);
+			if (!wsi->http2_substream) {
+				memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
+				lwsl_debug("writing chunk terminator and exiting\n");
+				n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
+						5, LWS_WRITE_HTTP);
+			} else
+				n = lws_write(wsi, (unsigned char *)buf + LWS_PRE,
+					      0, LWS_WRITE_HTTP_FINAL);
+
 			/* always close after sending it */
 			return -1;
 		}
@@ -392,7 +456,8 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
 		lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
 				wsi->cgi->explicitly_chunked,
 				(uint64_t)wsi->cgi->content_length);
-		if (!wsi->cgi->explicitly_chunked && !wsi->cgi->content_length) {
+		if (!wsi->cgi->explicitly_chunked &&
+		    !wsi->cgi->content_length) {
 			/* send terminating chunk */
 			lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
 			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
@@ -485,6 +550,13 @@ lws_create_vhost(struct lws_context *context,
 	if (info->options & LWS_SERVER_OPTION_ONLY_RAW)
 		lwsl_info("%s set to only support RAW\n", vh->name);
 
+#if defined(LWS_WITH_HTTP2)
+	vh->set = context->set;
+	if (info->http2_settings[0])
+		for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
+			vh->set.s[n] = info->http2_settings[n];
+#endif
+
 	vh->iface = info->iface;
 #if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32)
 	vh->bind_iface = info->bind_iface;
@@ -790,6 +862,11 @@ lws_create_context(struct lws_context_creation_info *info)
 #endif
 #if LWS_POSIX
 	lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
+#endif
+#if defined(LWS_WITH_HTTP2)
+	lwsl_info(" HTTP2 support         : available\n");
+#else
+	lwsl_info(" HTTP2 support         : not configured");
 #endif
 	if (lws_plat_context_early_init())
 		return NULL;
@@ -804,6 +881,10 @@ lws_create_context(struct lws_context_creation_info *info)
 	else
 		context->pt_serv_buf_size = 4096;
 
+#if defined(LWS_WITH_HTTP2)
+	context->set = lws_h2_stock_settings;
+#endif
+
 #if LWS_MAX_SMP > 1
 	pthread_mutex_init(&context->lock, NULL);
 #endif
@@ -1023,6 +1104,15 @@ lws_create_context(struct lws_context_creation_info *info)
 	if (lws_plat_init(context, info))
 		goto bail;
 
+#if defined(LWS_WITH_HTTP2)
+	/*
+	 * let the user code see what the platform default SETTINGS were, he
+	 * can modify them when he creates the vhosts.
+	 */
+	for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
+		info->http2_settings[n] = context->set.s[n];
+#endif
+
 	lws_context_init_ssl_library(info);
 
 	context->user_space = info->user;
diff --git a/lib/handshake.c b/lib/handshake.c
index e837a18ae79aa4efde5deb1b6210394aeb9c18c5..bc7609d9208d47bf5b7805185682ba71cbaf5218 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -72,24 +72,33 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 	case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
 	case LWSS_HTTP2_ESTABLISHED:
 		n = 0;
+		//lwsl_debug("%s: starting new block of %d\n", __func__, (int)len);
+		/*
+		 * wsi here is always the network connection wsi, not a stream
+		 * wsi.
+		 */
 		while (n < len) {
 			/*
 			 * we were accepting input but now we stopped doing so
 			 */
-			if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+			if (lws_is_flowcontrolled(wsi)) {
 				lws_rxflow_cache(wsi, buf, n, len);
 
 				return 1;
 			}
 
 			/* account for what we're using in rxflow buffer */
-			if (wsi->rxflow_buffer)
+			if (wsi->rxflow_buffer) {
 				wsi->rxflow_pos++;
-			if (lws_http2_parser(wsi, buf[n++])) {
+				assert(wsi->rxflow_pos <= wsi->rxflow_len);
+			}
+
+			if (lws_h2_parser(wsi, buf[n++])) {
 				lwsl_debug("%s: http2_parser bailed\n", __func__);
 				goto bail;
 			}
 		}
+		lwsl_debug("%s: used up block of %d\n", __func__, (int)len);
 		break;
 #endif
 
@@ -111,6 +120,8 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 		}
 		lwsl_parser("issuing %d bytes to parser\n", (int)len);
 
+		lwsl_hexdump(buf, (size_t)len);
+
 		if (lws_handshake_client(wsi, &buf, (size_t)len))
 			goto bail;
 
@@ -145,9 +156,9 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 			case LWSS_HTTP_ISSUING_FILE:
 				goto read_ok;
 			case LWSS_HTTP_BODY:
-				wsi->u.http.content_remain =
-						wsi->u.http.content_length;
-				if (wsi->u.http.content_remain)
+				wsi->u.http.rx_content_remain =
+						wsi->u.http.rx_content_length;
+				if (wsi->u.http.rx_content_remain)
 					goto http_postbody;
 
 				/* there is no POST content */
@@ -159,13 +170,14 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 
 	case LWSS_HTTP_BODY:
 http_postbody:
-		while (len && wsi->u.http.content_remain) {
+		//lwsl_notice("http post body\n");
+		while (len && wsi->u.http.rx_content_remain) {
 			/* Copy as much as possible, up to the limit of:
 			 * what we have in the read buffer (len)
 			 * remaining portion of the POST body (content_remain)
 			 */
-			body_chunk_len = min(wsi->u.http.content_remain,len);
-			wsi->u.http.content_remain -= body_chunk_len;
+			body_chunk_len = min(wsi->u.http.rx_content_remain, len);
+			wsi->u.http.rx_content_remain -= body_chunk_len;
 			len -= body_chunk_len;
 #ifdef LWS_WITH_CGI
 			if (wsi->cgi) {
@@ -197,7 +209,7 @@ http_postbody:
 #endif
 			buf += n;
 
-			if (wsi->u.http.content_remain)  {
+			if (wsi->u.http.rx_content_remain)  {
 				lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
 						wsi->context->timeout_secs);
 				break;
@@ -219,11 +231,15 @@ postbody_completion:
 			if (!wsi->cgi)
 #endif
 			{
+				lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
 				n = wsi->protocol->callback(wsi,
 					LWS_CALLBACK_HTTP_BODY_COMPLETION,
 					wsi->user_space, NULL, 0);
 				if (n)
 					goto bail;
+
+				if (wsi->http2_substream)
+					wsi->state = LWSS_HTTP2_ESTABLISHED;
 			}
 
 			break;
diff --git a/lib/header.c b/lib/header.c
index 872da2061cc1c4398d2e2a1efafd4fa1167e806b..e2562cd6ea6cd3a4eab30b72558b3fd1ce08d025 100644
--- a/lib/header.c
+++ b/lib/header.c
@@ -107,8 +107,8 @@ int lws_add_http_header_content_length(struct lws *wsi,
 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
 					 (unsigned char *)b, n, p, end))
 		return 1;
-	wsi->u.http.content_length = content_length;
-	wsi->u.http.content_remain = content_length;
+	wsi->u.http.tx_content_length = content_length;
+	wsi->u.http.tx_content_remain = content_length;
 
 	return 0;
 }
@@ -228,7 +228,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 	unsigned char *p = pt->serv_buf + LWS_PRE;
 	unsigned char *start = p;
 	unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
-	int n = 0, m, len;
+	int n = 0, m = 0, len;
 	char slen[20];
 
 	if (!html_body)
@@ -254,29 +254,65 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
 		return 1;
 
 #if defined(LWS_WITH_HTTP2)
-	{
+	if (wsi->http2_substream) {
 		unsigned char *body = p + 512;
 
+		/*
+		 * for HTTP/2, the headers must be sent separately, since they
+		 * go out in their own frame.  That puts us in a bind that
+		 * we won't always be able to get away with two lws_write()s in
+		 * sequence, since the first may use up the writability due to
+		 * the pipe being choked or SSL_WANT_.
+		 *
+		 * However we do need to send the human-readable body, and the
+		 * END_STREAM.
+		 *
+		 * Solve it by writing the headers now...
+		 */
 		m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
 		if (m != (int)(p - start))
 			return 1;
 
-		len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
-		      code, html_body);
+		/*
+		 * ... but stash the body and send it as a priority next
+		 * handle_POLLOUT
+		 */
 
-		n = len;
-		m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
-	}
-#else
-	p += lws_snprintf((char *)p, end - p - 1,
-			  "<html><body><h1>%u</h1>%s</body></html>",
-			  code, html_body);
+		len = sprintf((char *)body,
+			      "<html><body><h1>%u</h1>%s</body></html>",
+			      code, html_body);
+		wsi->u.http.tx_content_length = len;
+		wsi->u.http.tx_content_remain = len;
 
-	n = (int)(p - start);
-	m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
-	if (m != n)
-		return 1;
+		wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
+							"pending status body");
+		if (!wsi->u.h2.pending_status_body)
+			return -1;
+
+		strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
+		       (const char *)body);
+		lws_callback_on_writable(wsi);
+
+		return 0;
+	} else
 #endif
+	{
+		/*
+		 * for http/1, we can just append the body after the finalized
+		 * headers and send it all in one go.
+		 */
+		p += lws_snprintf((char *)p, end - p - 1,
+				  "<html><body><h1>%u</h1>%s</body></html>",
+				  code, html_body);
+
+		n = (int)(p - start);
+
+		m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
+		if (m != n)
+			return 1;
+	}
+
+	lwsl_notice("%s: return\n", __func__);
 
 	return m != n;
 }
@@ -313,7 +349,7 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
 	if (lws_finalize_http_header(wsi, p, end))
 		return -1;
 
-	n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS);
+	n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
 
 	return n;
 }
diff --git a/lib/hpack.c b/lib/hpack.c
index a07d13e475dee10ef18ab04814aa25c40e90607e..f7d916f9318f1ec2f7e53ff21d54472ee72a15fb 100644
--- a/lib/hpack.c
+++ b/lib/hpack.c
@@ -1,7 +1,7 @@
 /*
  * lib/hpack.c
  *
- * Copyright (C) 2014 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2014-2017 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
@@ -88,6 +88,17 @@
           +-------+-----------------------------+---------------+
 */
 
+static const uint8_t static_hdr_len[] = {
+		0, /* starts at 1 */
+		10, 7, 7, 5, 5, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 14, 15, 15, 13, 6, 27,
+		3, 5, 13, 13, 19, 16, 16, 14, 16, 13,
+		12, 6, 4, 4, 6, 7, 4, 4, 8, 17,
+		13, 8, 19, 13, 4, 8, 12, 18, 19,
+		5, 7, 7, 11, 6, 10, 25, 17, 10, 4,
+		3, 16
+};
+
 static const unsigned char static_token[] = {
 	0,
 	WSI_TOKEN_HTTP_COLON_AUTHORITY,
@@ -189,15 +200,19 @@ static int huftable_decode(int pos, char c)
 	return pos + (lextable[q] << 1);
 }
 
-static int lws_hpack_update_table_size(struct lws *wsi, int idx)
-{
-	lwsl_info("hpack set table size %d\n", idx);
-	return 0;
-}
-
 static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
 {
-	struct allocated_headers * ah = wsi->u.http2.http.ah;
+	struct allocated_headers *ah = wsi->u.h2.http.ah;
+
+	if (!ah) {
+		lwsl_notice("%s: no ah\n", __func__);
+		return 1;
+	}
+
+	ah->hdr_token_idx = -1;
+
+	lwsl_header("%s: token %d ah->pos = %d, ah->nfrag = %d\n",
+		   __func__, hdr_token_idx, ah->pos, ah->nfrag);
 
 	if (!hdr_token_idx) {
 		lwsl_err("%s: zero hdr_token_idx\n", __func__);
@@ -209,9 +224,28 @@ static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
 		return 1;
 	}
 
+	if ((hdr_token_idx == WSI_TOKEN_HTTP_COLON_AUTHORITY ||
+	     hdr_token_idx == WSI_TOKEN_HTTP_COLON_METHOD ||
+	     hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH ||
+	     hdr_token_idx == WSI_TOKEN_HTTP_COLON_SCHEME) &&
+	     ah->frag_index[hdr_token_idx]) {
+		if (!(ah->frags[ah->frag_index[hdr_token_idx]].flags & 1)) {
+			lws_h2_goaway(lws_get_network_wsi(wsi),
+				      H2_ERR_PROTOCOL_ERROR,
+				      "Duplicated pseudoheader");
+			return 1;
+		}
+	}
+
+	if (ah->nfrag == 0)
+		ah->nfrag = 1;
+
 	ah->frags[ah->nfrag].offset = ah->pos;
 	ah->frags[ah->nfrag].len = 0;
 	ah->frags[ah->nfrag].nfrag = 0;
+	ah->frags[ah->nfrag].flags = 2; /* we had reason to set it */
+
+	ah->hdr_token_idx = hdr_token_idx;
 
 	ah->frag_index[hdr_token_idx] = ah->nfrag;
 
@@ -220,7 +254,7 @@ static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
 
 static int lws_frag_append(struct lws *wsi, unsigned char c)
 {
-	struct allocated_headers * ah = wsi->u.http2.http.ah;
+	struct allocated_headers * ah = wsi->u.h2.http.ah;
 
 	ah->data[ah->pos++] = c;
 	ah->frags[ah->nfrag].len++;
@@ -230,121 +264,435 @@ static int lws_frag_append(struct lws *wsi, unsigned char c)
 
 static int lws_frag_end(struct lws *wsi)
 {
+	lwsl_header("%s\n", __func__);
 	if (lws_frag_append(wsi, 0))
 		return 1;
 
-	wsi->u.http2.http.ah->nfrag++;
+	/* don't account for the terminating NUL in the logical length */
+	wsi->u.h2.http.ah->frags[wsi->u.h2.http.ah->nfrag].len--;
+
+	wsi->u.h2.http.ah->nfrag++;
 	return 0;
 }
 
+int
+lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h)
+{
+	struct allocated_headers *ah = wsi->u.h2.http.ah;
+	int n;
+
+	if (!ah)
+		return 0;
+
+	n = ah->frag_index[h];
+	if (!n)
+		return 0;
+
+	return !!(ah->frags[n].flags & 2);
+}
+
 static void lws_dump_header(struct lws *wsi, int hdr)
 {
 	char s[200];
-	int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
-	s[len] = '\0';
-	lwsl_info("  hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
+	const unsigned char *p;
+	int len;
+
+	if (hdr == LWS_HPACK_IGNORE_ENTRY) {
+		lwsl_notice("hdr tok ignored\n");
+		return;
+	}
+
+	(void)p;
+
+	len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
+	if (len < 0)
+		strcpy(s, "(too big to show)");
+	else
+		s[len] = '\0';
+	p = lws_token_to_string(hdr);
+	lwsl_header("  hdr tok %d (%s) = '%s' (len %d)\n", hdr,
+		   p ? (char *)p : (char *)"null", s, len);
 }
 
+/*
+ * dynamic table
+ *
+ *  [ 0 ....   num_entries - 1]
+ *
+ *  Starts filling at 0+
+ *
+ *  #62 is *most recently entered*
+ *
+ *  Number of entries is not restricted, but aggregated size of the entry
+ *  payloads is.  Unfortunately the way HPACK does this is specific to an
+ *  imagined implementation, and lws implementation is much more efficient
+ *  (ignoring unknown headers and using the lws token index for the header
+ *  name part).
+ */
+
+/*
+ * returns 0 if dynamic entry (arg and len are filled)
+ * returns -1 if failure
+ * returns nonzero token index if actually static token
+ */
 static int
-lws_token_from_index(struct lws *wsi, int index, char **arg, int *len)
+lws_token_from_index(struct lws *wsi, int index, const char **arg, int *len,
+		     uint32_t *hdr_len)
 {
 	struct hpack_dynamic_table *dyn;
 
+	if (index == LWS_HPACK_IGNORE_ENTRY)
+		return LWS_HPACK_IGNORE_ENTRY;
+
 	/* dynamic table only belongs to network wsi */
+	wsi = lws_get_network_wsi(wsi);
+	if (!wsi->u.h2.h2n)
+		return -1;
 
-	wsi = lws_http2_get_network_wsi(wsi);
+	dyn = &wsi->u.h2.h2n->hpack_dyn_table;
 
-	dyn = wsi->u.http2.hpack_dyn_table;
+	if (index < 0)
+		return -1;
+
+	if (index < ARRAY_SIZE(static_token)) {
+		if (arg && index < ARRAY_SIZE(http2_canned)) {
+			*arg = http2_canned[index];
+			*len = strlen(http2_canned[index]);
+		}
+		if (hdr_len)
+			*hdr_len = static_hdr_len[index];
 
-	if (index < ARRAY_SIZE(static_token))
 		return static_token[index];
+	}
 
-	if (!dyn)
-		return 0;
+	if (!dyn) {
+		lwsl_notice("no dynamic table\n");
+		return -1;
+	}
+
+	if (index < ARRAY_SIZE(static_token) ||
+	    index >= ARRAY_SIZE(static_token) + dyn->used_entries) {
+		lwsl_err("  %s: adjusted index %d >= %d\n", __func__, index,
+			    dyn->used_entries);
+		lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR,
+			      "index out of range");
+		return -1;
+	}
 
 	index -= ARRAY_SIZE(static_token);
-	if (index >= dyn->num_entries)
-		return 0;
+	index = (dyn->pos - 1 - index) % dyn->num_entries;
+	if (index < 0)
+		index += dyn->num_entries;
+
+	lwsl_header("%s: dyn index %d, tok %d\n", __func__, index, dyn->entries[index].lws_hdr_idx);
 
 	if (arg && len) {
-		*arg = dyn->args + dyn->entries[index].arg_offset;
-		*len = dyn->entries[index].arg_len;
+		*arg = dyn->entries[index].value;
+		*len = dyn->entries[index].value_len;
 	}
 
-	return dyn->entries[index].token;
+	if (hdr_len)
+		*hdr_len = dyn->entries[index].hdr_len;
+
+	return dyn->entries[index].lws_hdr_idx;
 }
 
 static int
-lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
+lws_h2_dynamic_table_dump(struct lws *wsi)
 {
+#if 0
+	struct lws *nwsi = lws_get_network_wsi(wsi);
 	struct hpack_dynamic_table *dyn;
-	int ret = 1;
-
-	wsi = lws_http2_get_network_wsi(wsi);
-	dyn = wsi->u.http2.hpack_dyn_table;
+	int n, m;
+	const char *p;
 
-	if (!dyn) {
-		dyn = lws_zalloc(sizeof(*dyn), "hpack dyn");
-		if (!dyn)
-			return 1;
-		wsi->u.http2.hpack_dyn_table = dyn;
-
-		dyn->args = lws_malloc(1024, "hpack");
-		if (!dyn->args)
-			goto bail1;
-		dyn->args_length = 1024;
-		dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20, "hpack dyn entries");
-		if (!dyn->entries)
-			goto bail2;
-		dyn->num_entries = 20;
+	if (!nwsi->u.h2.h2n)
+		return 1;
+	dyn = &nwsi->u.h2.h2n->hpack_dyn_table;
+
+	lwsl_header("Dump dyn table for nwsi %p (%d / %d members, pos = %d, start index %d, virt used %d / %d)\n", nwsi,
+			dyn->used_entries, dyn->num_entries, dyn->pos, (uint32_t)ARRAY_SIZE(static_token),
+			dyn->virtual_payload_usage, dyn->virtual_payload_max);
+
+	for (n = 0; n < dyn->used_entries; n++) {
+		m = (dyn->pos - 1 - n) % dyn->num_entries;
+		if (m < 0)
+			m += dyn->num_entries;
+		if (dyn->entries[m].lws_hdr_idx != LWS_HPACK_IGNORE_ENTRY)
+			p = (const char *)lws_token_to_string(
+					dyn->entries[m].lws_hdr_idx);
+		else
+			p = "(ignored)";
+		lwsl_header("   %3d: tok %s: (len %d) val '%s'\n",
+			    (int)(n + ARRAY_SIZE(static_token)), p, dyn->entries[m].hdr_len,
+			    dyn->entries[m].value ? dyn->entries[m].value : "null");
 	}
+#endif
+	return 0;
+}
+
+static void
+lws_dynamic_free(struct hpack_dynamic_table *dyn, int idx)
+{
+	lwsl_header("freeing %d for reuse\n", idx);
+	dyn->virtual_payload_usage -=  dyn->entries[idx].value_len +
+				dyn->entries[idx].hdr_len;
+	lws_free_set_NULL(dyn->entries[idx].value);
+	dyn->entries[idx].value = NULL;
+	dyn->entries[idx].value_len = 0;
+	dyn->entries[idx].hdr_len = 0;
+	dyn->entries[idx].lws_hdr_idx = LWS_HPACK_IGNORE_ENTRY;
+	dyn->used_entries--;
+}
+
+/*
+ * There are two address spaces, 1) internal ringbuffer and 2) HPACK indexes.
+ *
+ * Internal ringbuffer:
+ *
+ * The internal ringbuffer wraps as we keep filling it, dyn->pos points to
+ * the next index to be written.
+ *
+ * HPACK indexes:
+ *
+ * The last-written entry becomes entry 0, the previously-last-written entry
+ * becomes entry 1 etc.
+ */
+
+static int
+lws_dynamic_token_insert(struct lws *wsi, int hdr_len,
+			 int lws_hdr_index, char *arg, int len)
+{
+	struct hpack_dynamic_table *dyn;
+	int new_index, n;
 
-	if (dyn->next == dyn->num_entries)
+	/* dynamic table only belongs to network wsi */
+	wsi = lws_get_network_wsi(wsi);
+	if (!wsi->u.h2.h2n)
 		return 1;
+	dyn = &wsi->u.h2.h2n->hpack_dyn_table;
+
+	if (!dyn->entries) {
+		lwsl_err("%s: unsized dyn table\n", __func__);
 
-	if (dyn->args_length - dyn->pos < len)
 		return 1;
+	}
+	lws_h2_dynamic_table_dump(wsi);
+
+	new_index = (dyn->pos) % dyn->num_entries;
+	if (dyn->num_entries && dyn->used_entries == dyn->num_entries) {
+		if (dyn->virtual_payload_usage < dyn->virtual_payload_max)
+			lwsl_err("Dropping header content before limit!\n");
+		/* we have to drop the oldest to make space */
+		lws_dynamic_free(dyn, new_index);
+	}
 
-	dyn->entries[dyn->next].token = token;
-	dyn->entries[dyn->next].arg_offset = dyn->pos;
-	if (len)
-		memcpy(dyn->args + dyn->pos, arg, len);
-	dyn->entries[dyn->next].arg_len = len;
+	/*
+	 * evict guys to make room, allowing for some overage.  We have to
+	 * take care about getting a single huge header, and evicting
+	 * everything
+	 */
+
+	while (dyn->virtual_payload_usage &&
+	       dyn->used_entries &&
+	       dyn->virtual_payload_usage + hdr_len + len >
+				dyn->virtual_payload_max + 1024) {
+		n = (dyn->pos - dyn->used_entries) % dyn->num_entries;
+		if (n < 0)
+			n += dyn->num_entries;
+		lws_dynamic_free(dyn, n);
+	}
+
+	if (dyn->used_entries < dyn->num_entries)
+		dyn->used_entries++;
+
+	dyn->entries[new_index].value_len = 0;
+
+	if (lws_hdr_index != LWS_HPACK_IGNORE_ENTRY) {
+		dyn->entries[new_index].value = lws_malloc(len + 1, "hpack dyn");
+		if (!dyn->entries[new_index].value)
+			return 1;
+
+		memcpy(dyn->entries[new_index].value, arg, len);
+		dyn->entries[new_index].value[len] = '\0';
+		dyn->entries[new_index].value_len = len;
+	} else
+		dyn->entries[new_index].value = NULL;
+
+	dyn->entries[new_index].lws_hdr_idx = lws_hdr_index;
+	dyn->entries[new_index].hdr_len = hdr_len;
+
+	dyn->virtual_payload_usage += hdr_len + len;
+
+	lwsl_info("%s: index %ld: lws_hdr_index 0x%x, hdr len %d, '%s' len %d\n",
+		  __func__, (long)ARRAY_SIZE(static_token),
+		  lws_hdr_index, hdr_len, dyn->entries[new_index].value ?
+				 dyn->entries[new_index].value : "null", len);
+
+	dyn->pos = (dyn->pos + 1) % dyn->num_entries;
+
+	lws_h2_dynamic_table_dump(wsi);
+
+	return 0;
+}
+
+int
+lws_hpack_dynamic_size(struct lws *wsi, int size)
+{
+	struct hpack_dynamic_table *dyn;
+	struct hpack_dt_entry *dte;
+	struct lws *nwsi;
+	int min, n = 0, m;
+
+	/*
+	 * "size" here is coming from the http/2 SETTING
+	 * SETTINGS_HEADER_TABLE_SIZE.  This is a (virtual, in our case)
+	 * linear buffer containing dynamic header names and values... when it
+	 * is full, old entries are evicted.
+	 *
+	 * We encode the header as an lws_hdr_idx, which is all the rest of
+	 * lws cares about; if there is no matching header we store an empty
+	 * entry in the dyn table as a placeholder.
+	 *
+	 * So to make the two systems work together we keep an accounting of
+	 * what we are using to decide when to evict... we must only evict
+	 * things when the remote peer's accounting also makes him feel he
+	 * should evict something.
+	 */
+
+	nwsi = lws_get_network_wsi(wsi);
+	if (!nwsi->u.h2.h2n)
+		goto bail;
+
+	dyn = &nwsi->u.h2.h2n->hpack_dyn_table;
+	lwsl_info("%s: from %d to %d\n", __func__, (int)dyn->num_entries, size);
+
+	if (size > nwsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]) {
+		lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
+			"Asked for header table bigger than we told");
+		goto bail;
+	}
+
+	dyn->virtual_payload_max = size;
+
+	size = size / 8;
+	min = size;
+	if (min > dyn->used_entries)
+		min = dyn->used_entries;
+
+	if (size == dyn->num_entries)
+		return 0;
+
+	if (dyn->num_entries < min)
+		min = dyn->num_entries;
+
+	dte = lws_zalloc(sizeof(*dte) * (size + 1), "dynamic table entries");
+	if (!dte)
+		goto bail;
+
+	while (dyn->virtual_payload_usage && dyn->used_entries &&
+	       dyn->virtual_payload_usage > dyn->virtual_payload_max) {
+		n = (dyn->pos - dyn->used_entries) % dyn->num_entries;
+		if (n < 0)
+			n += dyn->num_entries;
+		lws_dynamic_free(dyn, n);
+	}
+
+	if (min > dyn->used_entries)
+		min = dyn->used_entries;
+
+	if (dyn->entries) {
+		for (n = 0; n < min; n++) {
+			m = (dyn->pos - dyn->used_entries + n) % dyn->num_entries;
+			if (m < 0)
+				m += dyn->num_entries;
+			dte[n] = dyn->entries[m];
+		}
 
-	lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
-		  __func__, dyn->next, token, lws_token_to_string(token), len);
+		lws_free(dyn->entries);
+	}
+
+	dyn->entries = dte;
+	dyn->num_entries = size;
+	dyn->used_entries = min;
+	dyn->pos = min % size;
 
-	dyn->pos += len;
-	dyn->next++;
+	lws_h2_dynamic_table_dump(wsi);
 
 	return 0;
 
-bail2:
-	lws_free(dyn->args);
-bail1:
-	lws_free(dyn);
-	wsi->u.http2.hpack_dyn_table = NULL;
+bail:
+	lwsl_info("%s: failed to resize to %d\n", __func__, size);
 
-	return ret;
+	return 1;
 }
 
-static int lws_write_indexed_hdr(struct lws *wsi, int idx)
+void
+lws_hpack_destroy_dynamic_header(struct lws *wsi)
 {
-	const char *p;
-	int tok = lws_token_from_index(wsi, idx, NULL, 0);
+	struct hpack_dynamic_table *dyn;
+	int n;
 
-	lwsl_info("writing indexed hdr %d (tok %d '%s')\n", idx, tok,
-		  lws_token_to_string(tok));
+	if (!wsi->u.h2.h2n)
+		return;
 
-	if (lws_frag_start(wsi, tok))
+	dyn = &wsi->u.h2.h2n->hpack_dyn_table;
+
+	if (!dyn->entries)
+		return;
+
+	for (n = 0; n < dyn->num_entries; n++)
+		if (dyn->entries[n].value)
+			lws_free_set_NULL(dyn->entries[n].value);
+
+	lws_free_set_NULL(dyn->entries);
+}
+
+static int
+lws_hpack_use_idx_hdr(struct lws *wsi, int idx, int known_token)
+{
+	const char *arg = NULL;
+	int len = 0;
+	const char *p = NULL;
+	int tok = lws_token_from_index(wsi, idx, &arg, &len, NULL);
+
+	if (tok == LWS_HPACK_IGNORE_ENTRY) {
+		lwsl_header("%s: lws_token says ignore, returning\n", __func__);
+		return 0;
+	}
+
+	if (tok == -1) {
+		lwsl_info("%s: idx %d mapped to tok %d\n", __func__, idx, tok);
 		return 1;
+	}
+
+	if (arg) {
+		/* dynamic result */
+		if (known_token > 0)
+			tok = known_token;
+		lwsl_header("%s: dyn: idx %d '%s' tok %d\n", __func__, idx, arg,
+			   tok);
+	} else
+		lwsl_header("writing indexed hdr %d (tok %d '%s')\n", idx, tok,
+				lws_token_to_string(tok));
+
+	if (tok == LWS_HPACK_IGNORE_ENTRY)
+		return 0;
 
-	if (idx < ARRAY_SIZE(http2_canned)) {
+	if (arg)
+		p = arg;
+
+	if (idx < ARRAY_SIZE(http2_canned))
 		p = http2_canned[idx];
-		while (*p)
+
+	if (lws_frag_start(wsi, tok))
+		return 1;
+
+	if (p)
+		while (*p && len--)
 			if (lws_frag_append(wsi, *p++))
 				return 1;
-	}
+
 	if (lws_frag_end(wsi))
 		return 1;
 
@@ -355,320 +703,590 @@ static int lws_write_indexed_hdr(struct lws *wsi, int idx)
 
 int lws_hpack_interpret(struct lws *wsi, unsigned char c)
 {
+	struct lws *nwsi = lws_get_network_wsi(wsi);
+	struct lws_h2_netconn *h2n = nwsi->u.h2.h2n;
+	struct allocated_headers *ah = wsi->u.h2.http.ah;
 	unsigned int prev;
 	unsigned char c1;
-	int n;
-
-	lwsl_debug("   state %d\n", wsi->u.http2.hpack);
-
-	switch (wsi->u.http2.hpack) {
-	case HPKS_OPT_PADDING:
-		wsi->u.http2.padding = c;
-		lwsl_info("padding %d\n", c);
-		if (wsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
-			wsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
-			wsi->u.http2.hpack_m = 4;
-		} else
-			wsi->u.http2.hpack = HPKS_TYPE;
-		break;
-	case HKPS_OPT_E_DEPENDENCY:
-		wsi->u.http2.hpack_e_dep <<= 8;
-		wsi->u.http2.hpack_e_dep |= c;
-		if (! --wsi->u.http2.hpack_m) {
-			lwsl_info("hpack_e_dep = 0x%x\n", wsi->u.http2.hpack_e_dep);
-			wsi->u.http2.hpack = HKPS_OPT_WEIGHT;
-		}
-		break;
-	case HKPS_OPT_WEIGHT:
-		/* weight */
-		wsi->u.http2.hpack = HPKS_TYPE;
-		break;
+	int n, m;
+
+	if (!h2n)
+		return -1;
+
+	/*
+	 * HPKT_INDEXED_HDR_7		  1xxxxxxx: just "header field"
+	 * HPKT_INDEXED_HDR_6_VALUE_INCR  01xxxxxx: NEW indexed hdr + val
+	 * HPKT_LITERAL_HDR_VALUE_INCR	  01000000: NEW literal hdr + val
+	 * HPKT_INDEXED_HDR_4_VALUE	  0000xxxx: indexed hdr + val
+	 * HPKT_INDEXED_HDR_4_VALUE_NEVER 0001xxxx: NEVER NEW indexed hdr + val
+	 * HPKT_LITERAL_HDR_VALUE	  00000000: literal hdr + val
+	 * HPKT_LITERAL_HDR_VALUE_NEVER	  00010000: NEVER NEW literal hdr + val
+	 */
+	switch (h2n->hpack) {
 
 	case HPKS_TYPE:
-
-		if (wsi->u.http2.count > (wsi->u.http2.length - wsi->u.http2.padding)) {
-			lwsl_info("padding eat\n");
-			break;
-		}
-
-		if (c & 0x80) { /* indexed header field only */
+		h2n->is_first_header_char = 1;
+		h2n->huff_pad = 0;
+		h2n->zero_huff_padding = 0;
+		h2n->last_action_dyntable_resize = 0;
+		h2n->ext_count = 0;
+		h2n->hpack_hdr_len = 0;
+		h2n->unknown_header = 0;
+		h2n->seen_nonpseudoheader = 0;
+
+		if (c & 0x80) { /* 1....  indexed header field only */
 			/* just a possibly-extended integer */
-			wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_7;
-			lwsl_debug("HKPS_TYPE setting header_index %d\n", c & 0x7f);
-			wsi->u.http2.header_index = c & 0x7f;
+			h2n->hpack_type = HPKT_INDEXED_HDR_7;
+			lwsl_header("HPKT_INDEXED_HDR_7 hdr %d\n", c & 0x7f);
+			lws_h2_dynamic_table_dump(wsi);
+
+			h2n->hdr_idx = c & 0x7f;
 			if ((c & 0x7f) == 0x7f) {
-				wsi->u.http2.hpack_len = c & 0x7f;
-				wsi->u.http2.hpack_m = 0;
-				wsi->u.http2.hpack = HPKS_IDX_EXT;
+				h2n->hpack_len = 0;
+				h2n->hpack_m = 0x7f;
+				h2n->hpack = HPKS_IDX_EXT;
 				break;
 			}
-			lwsl_debug("HKPS_TYPE: %d\n", c & 0x7f);
-			if (lws_write_indexed_hdr(wsi, c & 0x7f))
+			if (!h2n->hdr_idx) {
+				lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
+					      "hdr index 0 seen");
+					return 1;
+			}
+			lwsl_header("HPKT_INDEXED_HDR_7: hdr %d\n", c & 0x7f);
+			if (lws_hpack_use_idx_hdr(wsi, c & 0x7f, -1)) {
+				lwsl_header("%s: idx hdr wr fail\n", __func__);
 				return 1;
+			}
 			/* stay at same state */
 			break;
 		}
-		if (c & 0x40) { /* literal header incr idx */
+		if (c & 0x40) { /* 01.... indexed or literal header incr idx */
 			/*
-			 * [possibly-extended hdr idx (6) | new literal hdr name]
-			 * H + possibly-extended value length
+			 * [possibly-ext hdr idx (6) | new literal hdr name]
+			 * H + possibly-ext value length
 			 * literal value
 			 */
-			lwsl_debug("HKPS_TYPE 2 setting header_index %d\n", 0);
-			wsi->u.http2.header_index = 0;
-			if (c == 0x40) { /* literal name */
-				wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE_INCR;
-				wsi->u.http2.value = 0;
-				wsi->u.http2.hpack = HPKS_HLEN;
+			h2n->hdr_idx = 0;
+			if (c == 0x40) { /* literal header */
+				lwsl_header("   HPKT_LITERAL_HDR_VALUE_INCR\n");
+				h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_INCR;
+				h2n->value = 0;
+				h2n->hpack_len = 0;
+				h2n->hpack = HPKS_HLEN;
 				break;
 			}
-			/* indexed name */
-			wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR;
+			/* indexed header */
+			h2n->hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR;
+			lwsl_header("   HPKT_INDEXED_HDR_6_VALUE_INCR (hdr %d)\n",
+				   c & 0x3f);
+			h2n->hdr_idx = c & 0x3f;
 			if ((c & 0x3f) == 0x3f) {
-				wsi->u.http2.hpack_len = c & 0x3f;
-				wsi->u.http2.hpack_m = 0;
-				wsi->u.http2.hpack = HPKS_IDX_EXT;
+				h2n->hpack_m = 0x3f;
+				h2n->hpack_len = 0;
+				h2n->hpack = HPKS_IDX_EXT;
 				break;
 			}
-			lwsl_debug("HKPS_TYPE 3 setting header_index %d\n", c & 0x3f);
-			wsi->u.http2.header_index = c & 0x3f;
-			wsi->u.http2.value = 1;
-			wsi->u.http2.hpack = HPKS_HLEN;
+
+			h2n->value = 1;
+			h2n->hpack = HPKS_HLEN;
+			if (!h2n->hdr_idx) {
+				lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
+					      "hdr index 0 seen");
+					return 1;
+			}
 			break;
 		}
 		switch(c & 0xf0) {
 		case 0x10: /* literal header never index */
-		case 0: /* literal header without indexing */
+		case 0:    /* literal header without indexing */
 			/*
 			 * follows 0x40 except 4-bit hdr idx
 			 * and don't add to index
 			 */
 			if (c == 0) { /* literal name */
-				wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE;
-				wsi->u.http2.hpack = HPKS_HLEN;
-				wsi->u.http2.value = 0;
+				h2n->hpack_type = HPKT_LITERAL_HDR_VALUE;
+				lwsl_header("   HPKT_LITERAL_HDR_VALUE\n");
+				h2n->hpack = HPKS_HLEN;
+				h2n->value = 0;
+				break;
+			}
+			if (c == 0x10) { /* literal name NEVER */
+				h2n->hpack_type = HPKT_LITERAL_HDR_VALUE_NEVER;
+				lwsl_header("   HPKT_LITERAL_HDR_VALUE_NEVER\n");
+				h2n->hpack = HPKS_HLEN;
+				h2n->value = 0;
 				break;
 			}
-			//lwsl_debug("indexed\n");
+			lwsl_header("indexed\n");
 			/* indexed name */
-			wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_4_VALUE;
-			wsi->u.http2.header_index = 0;
+			if (c & 0x10) {
+				h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE_NEVER;
+				lwsl_header("   HPKT_LITERAL_HDR_4_VALUE_NEVER\n");
+			} else {
+				h2n->hpack_type = HPKT_INDEXED_HDR_4_VALUE;
+				lwsl_header("   HPKT_INDEXED_HDR_4_VALUE\n");
+			}
+			h2n->hdr_idx = 0;
 			if ((c & 0xf) == 0xf) {
-				wsi->u.http2.hpack_len = c & 0xf;
-				wsi->u.http2.hpack_m = 0;
-				wsi->u.http2.hpack = HPKS_IDX_EXT;
+				h2n->hpack_len = c & 0xf;
+				h2n->hpack_m = 0xf;
+				h2n->hpack_len = 0;
+				h2n->hpack = HPKS_IDX_EXT;
 				break;
 			}
-			//lwsl_err("HKPS_TYPE 5 setting header_index %d\n", c & 0xf);
-			wsi->u.http2.header_index = c & 0xf;
-			wsi->u.http2.value = 1;
-			wsi->u.http2.hpack = HPKS_HLEN;
+			h2n->hdr_idx = c & 0xf;
+			h2n->value = 1;
+			h2n->hpack = HPKS_HLEN;
 			break;
 
 		case 0x20:
 		case 0x30: /* header table size update */
 			/* possibly-extended size value (5) */
-			wsi->u.http2.hpack_type = HPKT_SIZE_5;
-			if ((c & 0x1f) == 0x1f) {
-				wsi->u.http2.hpack_len = c & 0x1f;
-				wsi->u.http2.hpack_m = 0;
-				wsi->u.http2.hpack = HPKS_IDX_EXT;
+			lwsl_header("HPKT_SIZE_5 %x\n", c &0x1f);
+			h2n->hpack_type = HPKT_SIZE_5;
+			h2n->hpack_len = c & 0x1f;
+			if (h2n->hpack_len == 0x1f) {
+				h2n->hpack_m = 0x1f;
+				h2n->hpack_len = 0;
+				h2n->hpack = HPKS_IDX_EXT;
 				break;
 			}
-			lws_hpack_update_table_size(wsi, c & 0x1f);
-			/* stay at HPKS_TYPE state */
+			h2n->last_action_dyntable_resize = 1;
+			if (lws_hpack_dynamic_size(wsi, h2n->hpack_len))
+				return 1;
 			break;
 		}
 		break;
 
 	case HPKS_IDX_EXT:
-		wsi->u.http2.hpack_len += (c & 0x7f) << wsi->u.http2.hpack_m;
-		wsi->u.http2.hpack_m += 7;
-		if (!(c & 0x80)) {
-			switch (wsi->u.http2.hpack_type) {
-			case HPKT_INDEXED_HDR_7:
-				//lwsl_err("HKPS_IDX_EXT hdr idx %d\n", wsi->u.http2.hpack_len);
-				if (lws_write_indexed_hdr(wsi, wsi->u.http2.hpack_len))
-					return 1;
-				wsi->u.http2.hpack = HPKS_TYPE;
-				break;
-			default:
-				// lwsl_err("HKPS_IDX_EXT setting header_index %d\n",
-				//		wsi->u.http2.hpack_len);
-				wsi->u.http2.header_index = wsi->u.http2.hpack_len;
-				wsi->u.http2.value = 1;
-				wsi->u.http2.hpack = HPKS_HLEN;
-				break;
+		h2n->hpack_len = h2n->hpack_len |
+				 ((c & 0x7f) << h2n->ext_count);
+		h2n->ext_count += 7;
+		if (c & 0x80) /* extended int not complete yet */
+			break;
+
+		/* extended integer done */
+		h2n->hpack_len += h2n->hpack_m;
+		lwsl_header("HPKS_IDX_EXT: hpack_len %d\n", h2n->hpack_len);
+
+		switch (h2n->hpack_type) {
+		case HPKT_INDEXED_HDR_7:
+			if (lws_hpack_use_idx_hdr(wsi, h2n->hpack_len,
+						  h2n->hdr_idx)) {
+				lwsl_notice("%s: hd7 use fail\n", __func__);
+				return 1;
+			}
+			h2n->hpack = HPKS_TYPE;
+			break;
+
+		case HPKT_SIZE_5:
+			h2n->last_action_dyntable_resize = 1;
+			if (lws_hpack_dynamic_size(wsi, h2n->hpack_len))
+				return 1;
+			h2n->hpack = HPKS_TYPE;
+			break;
+
+		default:
+			h2n->hdr_idx = h2n->hpack_len;
+			if (!h2n->hdr_idx) {
+				lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
+					      "extended header index was 0");
+				return 1;
 			}
+			h2n->value = 1;
+			h2n->hpack = HPKS_HLEN;
+			break;
 		}
 		break;
 
 	case HPKS_HLEN: /* [ H | 7+ ] */
-		wsi->u.http2.huff = !!(c & 0x80);
-		wsi->u.http2.hpack_pos = 0;
-		wsi->u.http2.hpack_len = c & 0x7f;
-		if (wsi->u.http2.hpack_len < 0x7f) {
+		h2n->huff = !!(c & 0x80);
+		h2n->hpack_pos = 0;
+		h2n->hpack_len = c & 0x7f;
+
+		if (h2n->hpack_len == 0x7f) {
+			h2n->hpack_m = 0x7f;
+			h2n->hpack_len = 0;
+			h2n->ext_count = 0;
+			h2n->hpack = HPKS_HLEN_EXT;
+			break;
+		}
 pre_data:
-			if (wsi->u.http2.value) {
-				if (wsi->u.http2.header_index)
-				if (lws_frag_start(wsi, lws_token_from_index(wsi,
-						   wsi->u.http2.header_index,
-						   NULL, NULL))) {
-				//	lwsl_notice("%s: hlen failed\n", __func__);
-					return 1;
-				}
+		h2n->hpack = HPKS_DATA;
+		if (!h2n->value || !h2n->hdr_idx) {
+			wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
+			wsi->u.hdr.lextable_pos = 0;
+			h2n->unknown_header = 0;
+			break;
+		}
+
+		if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) {
+			n = wsi->u.hdr.parser_state;
+			if (n == 255) {
+				n = -1;
+				h2n->hdr_idx = -1;
 			} else
-				wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
-			wsi->u.http2.hpack = HPKS_DATA;
+				h2n->hdr_idx = 1;
+		} else {
+			n = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, NULL);
+			lwsl_header("  lws_tok_from_idx(%d) says %d\n",
+				   h2n->hdr_idx, n);
+		}
+
+		if (n == LWS_HPACK_IGNORE_ENTRY || n == -1)
+			h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY;
+
+		switch (h2n->hpack_type) {
+		/*
+		 * hpack types with literal headers were parsed by the lws
+		 * header SM... on recognition of a known lws header, it does
+		 * the correct lws_frag_start() for us already.  Other types
+		 * (ie, indexed header) need us to do it here.
+		 */
+		case HPKT_LITERAL_HDR_VALUE_INCR:
+		case HPKT_LITERAL_HDR_VALUE:
+		case HPKT_LITERAL_HDR_VALUE_NEVER:
+			break;
+		default:
+			if (n != -1 && n != LWS_HPACK_IGNORE_ENTRY &&
+			    lws_frag_start(wsi, n)) {
+				lwsl_header("%s: frag start failed\n", __func__);
+				return 1;
+			}
 			break;
 		}
-		wsi->u.http2.hpack_m = 0;
-		wsi->u.http2.hpack = HPKS_HLEN_EXT;
 		break;
 
 	case HPKS_HLEN_EXT:
-		wsi->u.http2.hpack_len += (c & 0x7f) <<
-					wsi->u.http2.hpack_m;
-		wsi->u.http2.hpack_m += 7;
-		if (!(c & 0x80))
-			goto pre_data;
+		h2n->hpack_len = h2n->hpack_len |
+				 ((c & 0x7f) << h2n->ext_count);
+		h2n->ext_count += 7;
+		if (c & 0x80) /* extended integer not complete yet */
+			break;
 
-		break;
+		h2n->hpack_len += h2n->hpack_m;
+		goto pre_data;
 
 	case HPKS_DATA:
+		//lwsl_header(" 0x%02X huff %d\n", c, h2n->huff);
+			c1 = c;
+
 		for (n = 0; n < 8; n++) {
-			if (wsi->u.http2.huff) {
-				prev = wsi->u.http2.hpack_pos;
-				wsi->u.http2.hpack_pos = huftable_decode(
-						wsi->u.http2.hpack_pos,
-		     				(c >> 7) & 1);
+			if (h2n->huff) {
+				char b = (c >> 7) & 1;
+				prev = h2n->hpack_pos;
+				h2n->hpack_pos = huftable_decode(
+						h2n->hpack_pos, b);
 				c <<= 1;
-				if (wsi->u.http2.hpack_pos == 0xffff)
+				if (h2n->hpack_pos == 0xffff) {
+					lwsl_notice("Huffman err\n");
 					return 1;
-				if (!(wsi->u.http2.hpack_pos & 0x8000))
+				}
+				if (!(h2n->hpack_pos & 0x8000)) {
+					if (!b)
+						h2n->zero_huff_padding = 1;
+					h2n->huff_pad++;
 					continue;
-				c1 = wsi->u.http2.hpack_pos & 0x7fff;
-				wsi->u.http2.hpack_pos = 0;
-
-				if (!c1 && prev == HUFTABLE_0x100_PREV)
-					; /* EOT */
-			} else {
+				}
+				c1 = h2n->hpack_pos & 0x7fff;
+				h2n->hpack_pos = 0;
+				h2n->huff_pad = 0;
+				h2n->zero_huff_padding = 0;
+
+				/* EOS |11111111|11111111|11111111|111111 */
+				if (!c1 && prev == HUFTABLE_0x100_PREV) {
+					lws_h2_goaway(nwsi,
+						H2_ERR_COMPRESSION_ERROR,
+						"Huffman EOT seen");
+					return 1;
+				}
+			} else
 				n = 8;
-				c1 = c;
-			}
-			if (wsi->u.http2.value) { /* value */
-				if (wsi->u.http2.header_index)
-					if (lws_frag_append(wsi, c1))
+
+			if (h2n->value) { /* value */
+
+				if (h2n->hdr_idx &&
+				    h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY) {
+
+					if (ah->hdr_token_idx == WSI_TOKEN_HTTP_COLON_PATH) {
+
+						switch (lws_parse_urldecode(wsi, &c1)) {
+						case LPUR_CONTINUE:
+							break;
+						case LPUR_SWALLOW:
+							goto swallow;
+						case LPUR_EXCESSIVE:
+						case LPUR_FORBID:
+							lws_h2_goaway(nwsi,
+								H2_ERR_PROTOCOL_ERROR,
+								"Evil URI");
+							return 1;
+
+						default:
+							return -1;
+						}
+					}
+					if (lws_frag_append(wsi, c1)) {
+						lwsl_notice("%s: frag app fail\n",
+									__func__);
 						return 1;
-			} else { /* name */
-				if (lws_parse(wsi, c1))
+					}
+				} else
+					lwsl_header("ignoring %c\n", c1);
+			} else {
+				/*
+				 * Convert name using existing parser,
+			 	 * If h2n->unknown_header == 0, result is
+			 	 * in wsi->u.hdr.parser_state
+			 	 * using WSI_TOKEN_GET_URI.
+			 	 *
+			 	 * If unknown header h2n->unknown_header
+			 	 * will be set.
+			 	 */
+				h2n->hpack_hdr_len++;
+				if (h2n->is_first_header_char) {
+					h2n->is_first_header_char = 0;
+					h2n->first_hdr_char = c1;
+				}
+				lwsl_header("parser: %c\n", c1);
+				/* uppercase header names illegal */
+				if (c1 >= 'A' && c1 <= 'Z') {
+					lws_h2_goaway(nwsi,
+						H2_ERR_COMPRESSION_ERROR,
+						"Uppercase literal hpack hdr");
 					return 1;
+				}
+				if (!h2n->unknown_header && lws_parse(wsi, c1))
+					h2n->unknown_header = 1;
+			}
+swallow:
+			(void)n;
+		} // for n
+
+		if (--h2n->hpack_len)
+			break;
+
+		/*
+		 * The header (h2n->value = 0) or the payload (h2n->value = 1)
+		 * is complete.
+		 */
+
+		if (h2n->huff && (h2n->huff_pad > 7 ||
+		    (h2n->zero_huff_padding && h2n->huff_pad))) {
+			lwsl_notice("zero_huff_padding: %d huff_pad: %d\n",
+				    h2n->zero_huff_padding, h2n->huff_pad);
+			lws_h2_goaway(nwsi, H2_ERR_COMPRESSION_ERROR,
+				      "Huffman padding excessive or wrong");
+			return 1;
+		}
 
+		if (!h2n->value && (
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER)) {
+			h2n->hdr_idx = LWS_HPACK_IGNORE_ENTRY;
+			lwsl_header("wsi->u.hdr.parser_state: %d\n",
+				    wsi->u.hdr.parser_state);
+
+			if (wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART) {
+				/* h2 headers come without the colon */
+				n = lws_parse(wsi, ':');
+				(void)n;
 			}
+
+			if (wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART ||
+			    wsi->u.hdr.parser_state == WSI_TOKEN_SKIPPING) {
+				h2n->unknown_header = 1;
+				wsi->u.hdr.parser_state = -1;
+			}
+		}
+
+		n = 8;
+
+		/* we have the header */
+		if (!h2n->value) {
+			h2n->value = 1;
+			h2n->hpack = HPKS_HLEN;
+			h2n->huff_pad = 0;
+			h2n->zero_huff_padding = 0;
+			h2n->ext_count = 0;
+			break;
 		}
-		if (--wsi->u.http2.hpack_len == 0) {
-
-			switch (wsi->u.http2.hpack_type) {
-			case HPKT_LITERAL_HDR_VALUE_INCR:
-			case HPKT_INDEXED_HDR_6_VALUE_INCR: // !!!
-				if (lws_hpack_add_dynamic_header(wsi,
-				     lws_token_from_index(wsi,
-						 wsi->u.http2.header_index,
-						 	 NULL, NULL), NULL, 0))
+
+		/*
+		 * we have got both the header and value
+		 */
+
+		switch (h2n->hpack_type) {
+		/*
+		 * These are the only two that insert to the dyntable
+		 */
+		/* NEW indexed hdr with value */
+		case HPKT_INDEXED_HDR_6_VALUE_INCR:
+			/* header length is determined by known index */
+			m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL,
+					&h2n->hpack_hdr_len);
+			goto add_it;
+		/* NEW literal hdr with value */
+		case HPKT_LITERAL_HDR_VALUE_INCR:
+			/*
+			 * hdr is a new literal, so length is already in
+			 * h2n->hpack_hdr_len
+			 */
+			m = wsi->u.hdr.parser_state;
+			if (h2n->unknown_header ||
+			    wsi->u.hdr.parser_state == WSI_TOKEN_NAME_PART ||
+			    wsi->u.hdr.parser_state == WSI_TOKEN_SKIPPING) {
+				if (h2n->first_hdr_char == ':') {
+					lwsl_info("HPKT_LITERAL_HDR_VALUE_INCR: end parser state %d unk hdr %d\n",
+						wsi->u.hdr.parser_state,
+						h2n->unknown_header);
+					/* unknown pseudoheaders are illegal */
+					lws_h2_goaway(nwsi,
+						      H2_ERR_PROTOCOL_ERROR,
+						      "Unknown pseudoheader");
 					return 1;
-				break;
-			default:
-				break;
+				}
+				m = LWS_HPACK_IGNORE_ENTRY;
 			}
+add_it:
+			/*
+			 * mark us as having been set at the time of dynamic
+			 * token insertion.
+			 */
+			ah->frags[ah->nfrag].flags |= 1;
 
-			n = 8;
-			if (wsi->u.http2.value) {
-				if (lws_frag_end(wsi))
-					return 1;
-				// lwsl_err("data\n");
-				lws_dump_header(wsi, lws_token_from_index(
-						wsi, wsi->u.http2.header_index,
-						NULL, NULL));
-				if (wsi->u.http2.count + wsi->u.http2.padding ==
-				    wsi->u.http2.length)
-					wsi->u.http2.hpack = HKPS_OPT_DISCARD_PADDING;
-				else
-					wsi->u.http2.hpack = HPKS_TYPE;
-			} else { /* name */
-				//if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
-
-				wsi->u.http2.value = 1;
-				wsi->u.http2.hpack = HPKS_HLEN;
+			if (lws_dynamic_token_insert(wsi, h2n->hpack_hdr_len, m,
+					&ah->data[ah->frags[ah->nfrag].offset],
+					ah->frags[ah->nfrag].len)) {
+				lwsl_notice("%s: tok_insert fail\n", __func__);
+				return 1;
 			}
+			break;
+
+		default:
+			break;
 		}
-		break;
-	case HKPS_OPT_DISCARD_PADDING:
-		lwsl_info("eating padding %x\n", c);
-		if (! --wsi->u.http2.padding)
-			wsi->u.http2.hpack = HPKS_TYPE;
+
+		if (h2n->hdr_idx != LWS_HPACK_IGNORE_ENTRY && lws_frag_end(wsi))
+			return 1;
+
+		if (h2n->hpack_type == HPKT_LITERAL_HDR_VALUE ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_INCR ||
+		    h2n->hpack_type == HPKT_LITERAL_HDR_VALUE_NEVER) {
+			m = wsi->u.hdr.parser_state;
+			if (m == 255)
+				m = -1;
+		} else
+			m = lws_token_from_index(wsi, h2n->hdr_idx, NULL, NULL, NULL);
+		if (m != -1 && m != LWS_HPACK_IGNORE_ENTRY)
+			lws_dump_header(wsi, m);
+
+		if (h2n->seen_nonpseudoheader && (
+		     m == WSI_TOKEN_HTTP_COLON_AUTHORITY ||
+		     m == WSI_TOKEN_HTTP_COLON_METHOD ||
+		     m == WSI_TOKEN_HTTP_COLON_PATH ||
+		     m == WSI_TOKEN_HTTP_COLON_SCHEME)) {
+			/*
+			 * it's not legal to see a
+			 * pseudoheader after normal
+			 * headers
+			 */
+			lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR,
+				"Pseudoheader after normal hdrs");
+			return 1;
+		}
+
+		if (m != WSI_TOKEN_HTTP_COLON_AUTHORITY &&
+		    m != WSI_TOKEN_HTTP_COLON_METHOD &&
+		    m != WSI_TOKEN_HTTP_COLON_PATH &&
+		    m != WSI_TOKEN_HTTP_COLON_SCHEME)
+			h2n->seen_nonpseudoheader = 1;
+
+		h2n->is_first_header_char = 1;
+		h2n->hpack = HPKS_TYPE;
 		break;
 	}
 
 	return 0;
 }
 
-static int lws_http2_num(int starting_bits, unsigned long num,
-			 unsigned char **p, unsigned char *end)
+static int
+lws_h2_num_start(int starting_bits, unsigned long num)
 {
 	int mask = (1 << starting_bits) - 1;
 
-	if (num < mask) {
-		*((*p)++) |= num;
-		return *p >= end;
-	}
+	if (num < mask)
+		return (int)num;
 
-	*((*p)++) |= mask;
-	if (*p >= end)
-		return 1;
+	return mask;
+}
+
+static int
+lws_h2_num(int starting_bits, unsigned long num,
+			 unsigned char **p, unsigned char *end)
+{
+	int mask = (1 << starting_bits) - 1;
+
+	if (num < mask)
+		return 0;
 
 	num -= mask;
-	while (num >= 128) {
-		*((*p)++) = 0x80 | (num & 0x7f);
+	do {
+		if (num > 127)
+			*((*p)++) = 0x80 | (num & 0x7f);
+		else
+			*((*p)++) = 0x00 | (num & 0x7f);
 		if (*p >= end)
 			return 1;
 		num >>= 7;
-	}
+	} while (num);
 
 	return 0;
 }
 
-int lws_add_http2_header_by_name(struct lws *wsi,
-				 const unsigned char *name,
+int lws_add_http2_header_by_name(struct lws *wsi, const unsigned char *name,
 				 const unsigned char *value, int length,
 				 unsigned char **p, unsigned char *end)
 {
 	int len;
 
-	lwsl_info("%s: %p  %s:%s\n", __func__, *p, name, value);
+	lwsl_header("%s: %p  %s:%s\n", __func__, *p, name, value);
 
 	len = strlen((char *)name);
 	if (len)
 		if (name[len - 1] == ':')
 			len--;
 
+	if (wsi->http2_substream && !strncmp((const char *)name,
+					     "transfer-encoding", len)) {
+		lwsl_header("rejecting %s\n", name);
+
+		return 0;
+	}
+
 	if (end - *p < len + length + 8)
 		return 1;
 
-	*((*p)++) = 0; /* not indexed, literal name */
+	*((*p)++) = 0; /* literal hdr, literal name,  */
 
-	**p = 0; /* non-HUF */
-	if (lws_http2_num(7, len, p, end))
+	*((*p)++) = 0 | lws_h2_num_start(7, len); /* non-HUF */
+	if (lws_h2_num(7, len, p, end))
 		return 1;
 	memcpy(*p, name, len);
 	*p += len;
 
-	*(*p) = 0; /* non-HUF */
-	if (lws_http2_num(7, length, p, end))
+	*((*p)++) = 0 | lws_h2_num_start(7, length); /* non-HUF */
+	if (lws_h2_num(7, length, p, end))
 		return 1;
 
 	memcpy(*p, value, length);
 	*p += length;
 
+	//lwsl_hexdump(op, *p -op);
+
 	return 0;
 }
 
@@ -685,14 +1303,13 @@ int lws_add_http2_header_by_token(struct lws *wsi, enum lws_token_indexes token,
 	return lws_add_http2_header_by_name(wsi, name, value, length, p, end);
 }
 
-int lws_add_http2_header_status(struct lws *wsi,
-			        unsigned int code, unsigned char **p,
-				unsigned char *end)
+int lws_add_http2_header_status(struct lws *wsi, unsigned int code,
+				unsigned char **p, unsigned char *end)
 {
 	unsigned char status[10];
 	int n;
 
-	wsi->u.http2.send_END_STREAM = !!(code >= 400);
+	wsi->u.h2.send_END_STREAM = 0; // !!(code >= 400);
 
 	n = sprintf((char *)status, "%u", code);
 	if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS,
diff --git a/lib/http2.c b/lib/http2.c
index 0bde70040fa467c03f7a4596b888f33aad802f05..560f414ba889981292a5e9052b3915372eea15ab 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2017 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
@@ -22,141 +22,443 @@
 
 #include "private-libwebsockets.h"
 
-const struct http2_settings lws_http2_default_settings = { {
-	0,
-	/* LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE */		4096,
-	/* LWS_HTTP2_SETTINGS__ENABLE_PUSH */			   1,
-	/* LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS */	 100,
-	/* LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE */	       65535,
-	/* LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE */	       16384,
-	/* LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE */		  ~0,
-}};
+/*
+ * bitmap of control messages that are valid to receive for each http2 state
+ */
 
+static const uint16_t http2_rx_validity[] = {
+	/* LWS_H2S_IDLE */
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+//		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE)| /* ignore */
+		(1 << LWS_H2_FRAME_TYPE_HEADERS) |
+		(1 << LWS_H2_FRAME_TYPE_CONTINUATION),
+	/* LWS_H2S_RESERVED_LOCAL */
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE),
+	/* LWS_H2S_RESERVED_REMOTE */
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_HEADERS) |
+		(1 << LWS_H2_FRAME_TYPE_CONTINUATION) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY),
+	/* LWS_H2S_OPEN */
+		(1 << LWS_H2_FRAME_TYPE_DATA) |
+		(1 << LWS_H2_FRAME_TYPE_HEADERS) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM) |
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) |
+		(1 << LWS_H2_FRAME_TYPE_PING) |
+		(1 << LWS_H2_FRAME_TYPE_GOAWAY) |
+		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) |
+		(1 << LWS_H2_FRAME_TYPE_CONTINUATION),
+	/* LWS_H2S_HALF_CLOSED_REMOTE */
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM),
+	/* LWS_H2S_HALF_CLOSED_LOCAL */
+		(1 << LWS_H2_FRAME_TYPE_DATA) |
+		(1 << LWS_H2_FRAME_TYPE_HEADERS) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM) |
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_PUSH_PROMISE) |
+		(1 << LWS_H2_FRAME_TYPE_PING) |
+		(1 << LWS_H2_FRAME_TYPE_GOAWAY) |
+		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) |
+		(1 << LWS_H2_FRAME_TYPE_CONTINUATION),
+	/* LWS_H2S_CLOSED */
+		(1 << LWS_H2_FRAME_TYPE_SETTINGS) |
+		(1 << LWS_H2_FRAME_TYPE_PRIORITY) |
+		(1 << LWS_H2_FRAME_TYPE_WINDOW_UPDATE) |
+		(1 << LWS_H2_FRAME_TYPE_RST_STREAM),
+};
+
+static const char *preface = "PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a";
+
+static const char * const h2_state_names[] = {
+	"LWS_H2S_IDLE",
+	"LWS_H2S_RESERVED_LOCAL",
+	"LWS_H2S_RESERVED_REMOTE",
+	"LWS_H2S_OPEN",
+	"LWS_H2S_HALF_CLOSED_REMOTE",
+	"LWS_H2S_HALF_CLOSED_LOCAL",
+	"LWS_H2S_CLOSED",
+};
+
+#if 0
+static const char * const h2_setting_names[] = {
+	"",
+	"H2SET_HEADER_TABLE_SIZE",
+	"H2SET_ENABLE_PUSH",
+	"H2SET_MAX_CONCURRENT_STREAMS",
+	"H2SET_INITIAL_WINDOW_SIZE",
+	"H2SET_MAX_FRAME_SIZE",
+	"H2SET_MAX_HEADER_LIST_SIZE",
+};
+
+void
+lws_h2_dump_settings(struct http2_settings *set)
+{
+	int n;
 
-void lws_http2_init(struct http2_settings *settings)
+	for (n = 1; n < H2SET_COUNT; n++)
+		lwsl_notice("   %30s: %10d\n", h2_setting_names[n], set->s[n]);
+}
+#else
+void
+lws_h2_dump_settings(struct http2_settings *set)
 {
-	memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings));
 }
+#endif
 
-struct lws *
-lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
+void lws_h2_init(struct lws *wsi)
 {
-	do {
-		if (wsi->u.http2.my_stream_id == sid)
-			return wsi;
-
-		wsi = wsi->u.http2.next_child_wsi;
-	} while (wsi);
+	wsi->u.h2.h2n->set = wsi->vhost->set;
+}
 
-	return NULL;
+static void
+lws_h2_state(struct lws *wsi, enum lws_h2_states s)
+{
+	if (!wsi)
+		return;
+	lwsl_info("%s: wsi %p: state %s -> %s\n", __func__, wsi,
+			h2_state_names[wsi->u.h2.h2_state],
+			h2_state_names[s]);
+	wsi->u.h2.h2_state = (uint8_t)s;
 }
 
 struct lws *
-lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
+lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
 			    unsigned int sid)
 {
-	struct lws *wsi = lws_create_new_server_wsi(vhost);
-
-	if (!wsi)
+	struct lws *wsi;
+	struct lws *nwsi = lws_get_network_wsi(parent_wsi);
+	struct lws_h2_netconn *h2n = nwsi->u.h2.h2n;
+
+	/*
+	 * The identifier of a newly established stream MUST be numerically
+   	 * greater than all streams that the initiating endpoint has opened or
+   	 * reserved.  This governs streams that are opened using a HEADERS frame
+   	 * and streams that are reserved using PUSH_PROMISE.  An endpoint that
+   	 * receives an unexpected stream identifier MUST respond with a
+   	 * connection error (Section 5.4.1) of type PROTOCOL_ERROR.
+	 */
+	if (sid <= h2n->highest_sid_opened) {
+		lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, "Bad sid");
 		return NULL;
+	}
 
 	/* no more children allowed by parent */
-	if (parent_wsi->u.http2.child_count + 1 ==
-	    parent_wsi->u.http2.peer_settings.setting[
-			LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS])
-		goto bail;
-	lws_http2_init(&wsi->u.http2.peer_settings);
-	lws_http2_init(&wsi->u.http2.my_settings);
-	wsi->u.http2.stream_id = sid;
-	wsi->u.http2.my_stream_id = sid;
+	if (parent_wsi->u.h2.child_count + 1 >
+	    parent_wsi->u.h2.h2n->set.s[H2SET_MAX_CONCURRENT_STREAMS]) {
+		lwsl_notice("reached concurrent stream limit\n");
+		return NULL;
+	}
+	wsi = lws_create_new_server_wsi(vh);
+	if (!wsi) {
+		lwsl_notice("new server wsi failed (vh %p)\n", vh);
+		return NULL;
+	}
+
+	h2n->highest_sid_opened = sid;
+	wsi->u.h2.my_sid = sid;
+	wsi->http2_substream = 1;
 
-	wsi->u.http2.parent_wsi = parent_wsi;
-	wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi;
-	parent_wsi->u.http2.next_child_wsi = wsi;
-	parent_wsi->u.http2.child_count++;
+	wsi->u.h2.parent_wsi = parent_wsi;
+	/* new guy's sibling is whoever was the first child before */
+	wsi->u.h2.sibling_list = parent_wsi->u.h2.child_list;
+	/* first child is now the new guy */
+	parent_wsi->u.h2.child_list = wsi;
+	parent_wsi->u.h2.child_count++;
 
-	wsi->u.http2.my_priority = 16;
-	wsi->u.http2.tx_credit = 65535;
+	wsi->u.h2.my_priority = 16;
+	wsi->u.h2.tx_cr = nwsi->u.h2.h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
+	wsi->u.h2.peer_tx_cr_est = nwsi->vhost->set.s[H2SET_INITIAL_WINDOW_SIZE];
 
 	wsi->state = LWSS_HTTP2_ESTABLISHED;
 	wsi->mode = parent_wsi->mode;
 
-	wsi->protocol = &vhost->protocols[0];
+	wsi->protocol = &vh->protocols[0];
 	if (lws_ensure_user_space(wsi))
-		goto bail;
+		goto bail1;
+
+	wsi->vhost->conn_stats.h2_subs++;
 
-	lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
-		  parent_wsi, wsi, sid, wsi->user_space);
+	lwsl_info("%s: %p new ch %p, sid %d, usersp=%p, tx cr %d, peer_credit %d (nwsi tx_cr %d)\n",
+		  __func__, parent_wsi, wsi, sid, wsi->user_space,
+		  wsi->u.h2.tx_cr, wsi->u.h2.peer_tx_cr_est, nwsi->u.h2.tx_cr);
 
 	return wsi;
 
-bail:
-	vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
-			       NULL, NULL, 0);
+bail1:
+	/* undo the insert */
+	parent_wsi->u.h2.child_list = wsi->u.h2.sibling_list;
+	parent_wsi->u.h2.child_count--;
+
+	if (wsi->user_space)
+		lws_free_set_NULL(wsi->user_space);
+	vh->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0);
 	lws_free(wsi);
 
 	return NULL;
 }
 
+struct lws *
+lws_h2_wsi_from_id(struct lws *parent_wsi, unsigned int sid)
+{
+	lws_start_foreach_ll(struct lws *, wsi, parent_wsi->u.h2.child_list) {
+		if (wsi->u.h2.my_sid == sid)
+			return wsi;
+	} lws_end_foreach_ll(wsi, u.h2.sibling_list);
+
+	return NULL;
+}
+
 int lws_remove_server_child_wsi(struct lws_context *context, struct lws *wsi)
 {
-	struct lws **w = &wsi->u.http2.parent_wsi;
-	do {
+	lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) {
 		if (*w == wsi) {
-			*w = wsi->u.http2.next_child_wsi;
-			(wsi->u.http2.parent_wsi)->u.http2.child_count--;
+			*w = wsi->u.h2.sibling_list;
+			(wsi->u.h2.parent_wsi)->u.h2.child_count--;
 			return 0;
 		}
-
-		w = &((*w)->u.http2.next_child_wsi);
-	} while (*w);
+	} lws_end_foreach_llp(w, u.h2.sibling_list);
 
 	lwsl_err("%s: can't find %p\n", __func__, wsi);
+
 	return 1;
 }
 
+void
+lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pps)
+{
+	struct lws *nwsi = lws_get_network_wsi(wsi);
+	struct lws_h2_netconn *h2n = nwsi->u.h2.h2n;
+
+	pps->next = h2n->pps;
+	h2n->pps = pps;
+	lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_DISABLE |
+				 LWS_RXFLOW_REASON_H2_PPS_PENDING);
+	lws_callback_on_writable(wsi);
+}
+
+static struct lws_h2_protocol_send *
+lws_h2_new_pps(enum lws_h2_protocol_send_type type)
+{
+	struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps");
+
+	if (pps)
+		pps->type = type;
+
+	return pps;
+}
+
+int
+lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason)
+{
+	struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps;
+
+	if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
+		return 0;
+
+	pps = lws_h2_new_pps(LWS_H2_PPS_GOAWAY);
+	if (!pps)
+		return 1;
+
+	lwsl_info("%s: %p: ERR 0x%x, '%s'\n", __func__, wsi, err, reason);
+
+	pps->u.ga.err = err;
+	strncpy(pps->u.ga.str, reason, sizeof(pps->u.ga.str) - 1);
+	pps->u.ga.str[sizeof(pps->u.ga.str) - 1] = '\0';
+	lws_pps_schedule(wsi, pps);
+
+	h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
+
+	return 0;
+}
+
+int
+lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason)
+{
+	struct lws *nwsi = lws_get_network_wsi(wsi);
+	struct lws_h2_netconn *h2n = nwsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps;
+
+	if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
+		return 0;
+
+	pps = lws_h2_new_pps(LWS_H2_PPS_RST_STREAM);
+	if (!pps)
+		return 1;
+
+	lwsl_info("%s: RST_STREAM 0x%x, REASON '%s'\n", __func__, err, reason);
+
+	pps->u.rs.sid = h2n->sid;
+	pps->u.rs.err = err;
+	lws_pps_schedule(wsi, pps);
+
+	h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
+	lws_h2_state(wsi, LWS_H2_STATE_CLOSED);
+
+	return 0;
+}
+
 int
-lws_http2_interpret_settings_payload(struct http2_settings *settings,
-				     unsigned char *buf, int len)
+lws_h2_settings(struct lws *wsi, struct http2_settings *settings,
+			unsigned char *buf, int len)
 {
+	struct lws *nwsi = lws_get_network_wsi(wsi);
 	unsigned int a, b;
 
 	if (!len)
 		return 0;
 
-	if (len < LWS_HTTP2_SETTINGS_LENGTH)
+	if (len < LWS_H2_SETTINGS_LEN)
 		return 1;
 
-	while (len >= LWS_HTTP2_SETTINGS_LENGTH) {
+	while (len >= LWS_H2_SETTINGS_LEN) {
 		a = (buf[0] << 8) | buf[1];
-		if (a < LWS_HTTP2_SETTINGS__COUNT) {
-			b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5];
-			settings->setting[a] = b;
-			lwsl_info("http2 settings %d <- 0x%x\n", a, b);
+		if (!a || a >= H2SET_COUNT)
+			goto skip;
+		b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5];
+
+		switch (a) {
+		case H2SET_HEADER_TABLE_SIZE:
+			break;
+		case H2SET_ENABLE_PUSH:
+			if (b > 1) {
+				lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR,
+						"ENABLE_PUSH invalid arg");
+				return 1;
+			}
+			break;
+		case H2SET_MAX_CONCURRENT_STREAMS:
+			break;
+		case H2SET_INITIAL_WINDOW_SIZE:
+			if (b > 0x7fffffff) {
+				lws_h2_goaway(nwsi, H2_ERR_FLOW_CONTROL_ERROR,
+						"Inital Window beyond max");
+				return 1;
+			}
+			/*
+			 * In addition to changing the flow-control window for
+			 * streams that are not yet active, a SETTINGS frame
+			 * can alter the initial flow-control window size for
+			 * streams with active flow-control windows (that is,
+			 * streams in the "open" or "half-closed (remote)"
+			 * state).  When the value of
+			 * SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver
+			 * MUST adjust the size of all stream flow-control
+			 * windows that it maintains by the difference between
+			 * the new value and the old value.
+			 */
+
+			lws_start_foreach_ll(struct lws *, w,
+					     nwsi->u.h2.child_list) {
+				lwsl_info("%s: adi child tc cr %d +%d -> %d",
+					    __func__,
+					    w->u.h2.tx_cr, b - settings->s[a],
+					    w->u.h2.tx_cr + b - settings->s[a]);
+				w->u.h2.tx_cr += b - settings->s[a];
+				if (w->u.h2.tx_cr > 0 &&
+				    w->u.h2.tx_cr <= b - settings->s[a])
+					lws_callback_on_writable(w);
+			} lws_end_foreach_ll(w, u.h2.sibling_list);
+
+			break;
+		case H2SET_MAX_FRAME_SIZE:
+			if (b < wsi->vhost->set.s[H2SET_MAX_FRAME_SIZE]) {
+				lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR,
+					      "Frame size < initial");
+				return 1;
+			}
+			if (b > 0x007fffff) {
+				lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR,
+					      "Settings Frame size above max");
+				return 1;
+			}
+			break;
+		case H2SET_MAX_HEADER_LIST_SIZE:
+			break;
 		}
-		len -= LWS_HTTP2_SETTINGS_LENGTH;
-		buf += LWS_HTTP2_SETTINGS_LENGTH;
+		settings->s[a] = b;
+		lwsl_info("http2 settings %d <- 0x%x\n", a, b);
+skip:
+		len -= LWS_H2_SETTINGS_LEN;
+		buf += LWS_H2_SETTINGS_LEN;
 	}
 
 	if (len)
 		return 1;
 
+	lws_h2_dump_settings(settings);
+
 	return 0;
 }
 
-struct lws *lws_http2_get_network_wsi(struct lws *wsi)
+/* RFC7640 Sect 6.9
+ *
+ * The WINDOW_UPDATE frame can be specific to a stream or to the entire
+ * connection.  In the former case, the frame's stream identifier
+ * indicates the affected stream; in the latter, the value "0" indicates
+ * that the entire connection is the subject of the frame.
+ *
+ * ...
+ *
+ * Two flow-control windows are applicable: the stream flow-control
+ * window and the connection flow-control window.  The sender MUST NOT
+ * send a flow-controlled frame with a length that exceeds the space
+ * available in either of the flow-control windows advertised by the
+ * receiver.  Frames with zero length with the END_STREAM flag set (that
+ * is, an empty DATA frame) MAY be sent if there is no available space
+ * in either flow-control window.
+ */
+
+int
+lws_h2_tx_cr_get(struct lws *wsi)
 {
-	while (wsi->u.http2.parent_wsi)
-		wsi = wsi->u.http2.parent_wsi;
+	int c = wsi->u.h2.tx_cr;
+	struct lws *nwsi;
 
-	return wsi;
+	if (!wsi->http2_substream && !wsi->upgraded_to_http2)
+		return ~0x80000000;
+
+	nwsi = lws_get_network_wsi(wsi);
+
+	lwsl_info ("%s: %p: own tx credit %d: nwsi credit %d\n",
+		     __func__, wsi, c, nwsi->u.h2.tx_cr);
+
+	if (nwsi->u.h2.tx_cr < c)
+		c = nwsi->u.h2.tx_cr;
+
+	if (c < 0)
+		return 0;
+
+	return c;
 }
 
-int lws_http2_frame_write(struct lws *wsi, int type, int flags,
-			  unsigned int sid, unsigned int len, unsigned char *buf)
+void
+lws_h2_tx_cr_consume(struct lws *wsi, int consumed)
 {
-	struct lws *wsi_eff = lws_http2_get_network_wsi(wsi);
-	unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
+	struct lws *nwsi = lws_get_network_wsi(wsi);
+
+	wsi->u.h2.tx_cr -= consumed;
+
+	if (nwsi != wsi)
+		nwsi->u.h2.tx_cr -= consumed;
+}
+
+int lws_h2_frame_write(struct lws *wsi, int type, int flags,
+		       unsigned int sid, unsigned int len, unsigned char *buf)
+{
+	struct lws *nwsi = lws_get_network_wsi(wsi);
+	unsigned char *p = &buf[-LWS_H2_FRAME_HEADER_LENGTH];
 	int n;
 
 	*p++ = len >> 16;
@@ -169,368 +471,1103 @@ int lws_http2_frame_write(struct lws *wsi, int type, int flags,
 	*p++ = sid >> 8;
 	*p++ = sid;
 
-	lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d, tx_credit=%d\n",
-		  __func__, wsi, wsi_eff, type, flags, sid, len,
-		  wsi->u.http2.tx_credit);
+	lwsl_debug("%s: %p (eff %p). typ %d, fl 0x%x, sid=%d, len=%d, "
+		  "txcr=%d, nwsi->txcr=%d\n", __func__, wsi, nwsi, type, flags,
+		  sid, len, wsi->u.h2.tx_cr, nwsi->u.h2.tx_cr);
 
-	if (type == LWS_HTTP2_FRAME_TYPE_DATA) {
-		if (wsi->u.http2.tx_credit < len)
+	if (type == LWS_H2_FRAME_TYPE_DATA) {
+		if (wsi->u.h2.tx_cr < len)
 			lwsl_err("%s: %p: sending payload len %d"
-				 " but tx_credit only %d!\n", __func__, wsi, len,
-				 wsi->u.http2.tx_credit);
-		wsi->u.http2.tx_credit -= len;
+				 " but tx_cr only %d!\n", __func__, wsi,
+				 len, wsi->u.h2.tx_cr);
+		lws_h2_tx_cr_consume(wsi, len);
 	}
 
-	n = lws_issue_raw(wsi_eff, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH],
-			  len + LWS_HTTP2_FRAME_HEADER_LENGTH);
-	if (n >= LWS_HTTP2_FRAME_HEADER_LENGTH)
-		return n - LWS_HTTP2_FRAME_HEADER_LENGTH;
+	n = lws_issue_raw(nwsi, &buf[-LWS_H2_FRAME_HEADER_LENGTH],
+			  len + LWS_H2_FRAME_HEADER_LENGTH);
+	if (n < 0)
+		return n;
+
+	if (n >= LWS_H2_FRAME_HEADER_LENGTH)
+		return n - LWS_H2_FRAME_HEADER_LENGTH;
 
 	return n;
 }
 
-static void lws_http2_settings_write(struct lws *wsi, int n, unsigned char *buf)
+static void lws_h2_set_bin(struct lws *wsi, int n, unsigned char *buf)
 {
 	*buf++ = n >> 8;
 	*buf++ = n;
-	*buf++ = wsi->u.http2.my_settings.setting[n] >> 24;
-	*buf++ = wsi->u.http2.my_settings.setting[n] >> 16;
-	*buf++ = wsi->u.http2.my_settings.setting[n] >> 8;
-	*buf = wsi->u.http2.my_settings.setting[n];
+	*buf++ = wsi->u.h2.h2n->set.s[n] >> 24;
+	*buf++ = wsi->u.h2.h2n->set.s[n] >> 16;
+	*buf++ = wsi->u.h2.h2n->set.s[n] >> 8;
+	*buf = wsi->u.h2.h2n->set.s[n];
 }
 
-static const char * https_client_preface =
-	"PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a";
-
-int
-lws_http2_parser(struct lws *wsi, unsigned char c)
+int lws_h2_do_pps_send(struct lws *wsi)
 {
-	struct lws *swsi;
-	int n;
+	struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps = NULL;
+	struct lws *cwsi;
+	uint8_t set[LWS_PRE + 64], *p = &set[LWS_PRE], *q;
+	int n, m = 0;
 
-	switch (wsi->state) {
-	case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
-		if (https_client_preface[wsi->u.http2.count++] != c)
-			return 1;
+	if (!h2n)
+		return 1;
 
-		if (!https_client_preface[wsi->u.http2.count]) {
-			lwsl_info("http2: %p: established\n", wsi);
-			wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS;
-			wsi->u.http2.count = 0;
-			wsi->u.http2.tx_credit = 65535;
+	/* get the oldest pps */
 
+	lws_start_foreach_llp(struct lws_h2_protocol_send **, pps1, h2n->pps) {
+		if ((*pps1)->next == NULL) { /* we are the oldest in the list */
+			pps = *pps1; /* remove us from the list */
+			*pps1 = NULL;
+			continue;
+		}
+	} lws_end_foreach_llp(pps1, next);
+
+	if (!pps)
+		return 1;
+
+	lwsl_info("%s: %p: %d\n", __func__, wsi, pps->type);
+
+	switch (pps->type) {
+
+	case LWS_H2_PPS_MY_SETTINGS:
+		/*
+		 * if any of our settings varies from h2 "default defaults"
+		 * then we must inform the perr
+		 */
+		for (n = 1; n < H2SET_COUNT; n++)
+			if (h2n->set.s[n] != lws_h2_defaults.s[n]) {
+				lwsl_debug("sending SETTING %d 0x%x\n", n,
+						wsi->u.h2.h2n->set.s[n]);
+				lws_h2_set_bin(wsi, n, &set[LWS_PRE + m]);
+				m += sizeof(h2n->one_setting);
+			}
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS,
+		     		       0, LWS_H2_STREAM_ID_MASTER, m,
+		     		       &set[LWS_PRE]);
+		if (n != m) {
+			lwsl_info("send %d %d\n", n, m);
+			goto bail;
+		}
+		break;
+
+	case LWS_H2_PPS_ACK_SETTINGS:
+		/* send ack ... always empty */
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS, 1,
+				       LWS_H2_STREAM_ID_MASTER, 0, &set[LWS_PRE]);
+		if (n) {
+			lwsl_err("ack tells %d\n", n);
+			goto bail;
+		}
+		/* this is the end of the preface dance then? */
+		if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) {
+			wsi->state = LWSS_HTTP2_ESTABLISHED;
+			wsi->u.http.fop_fd = NULL;
+			if (lws_is_ssl(lws_get_network_wsi(wsi)))
+				break;
 			/*
-			 * we must send a settings frame -- empty one is OK...
-			 * that must be the first thing sent by server
-			 * and the peer must send a SETTINGS with ACK flag...
+			 * we need to treat the headers from the upgrade as the
+			 * first job.  So these need to get shifted to sid 1.
 			 */
+			h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1);
+			if (!h2n->swsi)
+				goto bail;
+
+			/* pass on the initial headers to SID 1 */
+			h2n->swsi->u.http.ah = wsi->u.http.ah;
+			wsi->u.http.ah = NULL;
 
-			lws_set_protocol_write_pending(wsi,
-						       LWS_PPS_HTTP2_MY_SETTINGS);
+			lwsl_info("%s: inherited headers %p\n", __func__,
+				  h2n->swsi->u.http.ah);
+			h2n->swsi->u.h2.tx_cr =
+				h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
+			lwsl_info("initial tx credit on conn %p: %d\n",
+				  h2n->swsi, h2n->swsi->u.h2.tx_cr);
+			h2n->swsi->u.h2.initialized = 1;
+			/* demanded by HTTP2 */
+			h2n->swsi->u.h2.END_STREAM = 1;
+			lwsl_info("servicing initial http request\n");
+
+			wsi->vhost->conn_stats.h2_trans++;
+
+			if (lws_http_action(h2n->swsi))
+				goto bail;
+
+			break;
+		}
+		break;
+	case LWS_H2_PPS_PONG:
+		lwsl_debug("sending PONG\n");
+		memcpy(&set[LWS_PRE], pps->u.ping.ping_payload, 8);
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_PING,
+		     		       LWS_H2_FLAG_SETTINGS_ACK,
+				       LWS_H2_STREAM_ID_MASTER, 8,
+				       &set[LWS_PRE]);
+		if (n != 8) {
+			lwsl_info("send %d %d\n", n, m);
+			goto bail;
 		}
 		break;
 
-	case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
-	case LWSS_HTTP2_ESTABLISHED:
-		if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { // payload
-			wsi->u.http2.count++;
-			wsi->u.http2.stream_wsi->u.http2.count = wsi->u.http2.count;
-			/* applies to wsi->u.http2.stream_wsi which may be wsi*/
-			switch(wsi->u.http2.type) {
-			case LWS_HTTP2_FRAME_TYPE_SETTINGS:
-				wsi->u.http2.stream_wsi->u.http2.one_setting[wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH] = c;
-				if (wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH == LWS_HTTP2_SETTINGS_LENGTH - 1)
-					if (lws_http2_interpret_settings_payload(
-					     &wsi->u.http2.stream_wsi->u.http2.peer_settings,
-					     wsi->u.http2.one_setting,
-					     LWS_HTTP2_SETTINGS_LENGTH))
-						return 1;
-				break;
-			case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
-			case LWS_HTTP2_FRAME_TYPE_HEADERS:
-				lwsl_info(" %02X\n", c);
-				if (!wsi->u.http2.stream_wsi->u.hdr.ah)
-					if (lws_header_table_attach(wsi->u.http2.stream_wsi, 0)) {
-						lwsl_err("%s: Failed to get ah\n", __func__);
-						return 1;
-					}
-				if (lws_hpack_interpret(wsi->u.http2.stream_wsi, c)) {
-					lwsl_notice("%s: lws_hpack_interpret failed\n", __func__);
-					return 1;
-				}
-				break;
-			case LWS_HTTP2_FRAME_TYPE_GOAWAY:
-				if (wsi->u.http2.count >= 5 && wsi->u.http2.count <= 8) {
-					wsi->u.http2.hpack_e_dep <<= 8;
-					wsi->u.http2.hpack_e_dep |= c;
-					if (wsi->u.http2.count == 8) {
-						lwsl_info("goaway err 0x%x\n", wsi->u.http2.hpack_e_dep);
-					}
-				}
-				wsi->u.http2.GOING_AWAY = 1;
-				break;
-			case LWS_HTTP2_FRAME_TYPE_DATA:
-				break;
-			case LWS_HTTP2_FRAME_TYPE_PRIORITY:
-				break;
-			case LWS_HTTP2_FRAME_TYPE_RST_STREAM:
-				break;
-			case LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE:
-				break;
-			case LWS_HTTP2_FRAME_TYPE_PING:
-				if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
-				} else { /* they're sending us a ping request */
-					if (wsi->u.http2.count > 8)
-						return 1;
-					wsi->u.http2.ping_payload[wsi->u.http2.count - 1] = c;
-				}
+	case LWS_H2_PPS_GOAWAY:
+		lwsl_info("LWS_H2_PPS_GOAWAY\n");
+		*p++ = pps->u.ga.highest_sid >> 24;
+		*p++ = pps->u.ga.highest_sid >> 16;
+		*p++ = pps->u.ga.highest_sid >> 8;
+		*p++ = pps->u.ga.highest_sid;
+		*p++ = pps->u.ga.err >> 24;
+		*p++ = pps->u.ga.err >> 16;
+		*p++ = pps->u.ga.err >> 8;
+		*p++ = pps->u.ga.err;
+		q = (unsigned char *)h2n->goaway_str;
+		n = 0;
+		while (*q && n++ < sizeof(h2n->goaway_str))
+			*p++ = *q++;
+		h2n->we_told_goaway = 1;
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_GOAWAY, 0,
+				       LWS_H2_STREAM_ID_MASTER,
+				       p - &set[LWS_PRE], &set[LWS_PRE]);
+		if (n != 4) {
+			lwsl_info("send %d %d\n", n, m);
+			goto bail;
+		}
+		goto bail;
+
+	case LWS_H2_PPS_RST_STREAM:
+		lwsl_info("LWS_H2_PPS_RST_STREAM\n");
+		*p++ = pps->u.rs.err >> 24;
+		*p++ = pps->u.rs.err >> 16;
+		*p++ = pps->u.rs.err >> 8;
+		*p++ = pps->u.rs.err;
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_RST_STREAM,
+				       0, pps->u.rs.sid, 4, &set[LWS_PRE]);
+		if (n != 4) {
+			lwsl_info("send %d %d\n", n, m);
+			goto bail;
+		}
+		cwsi = lws_h2_wsi_from_id(wsi, pps->u.rs.sid);
+		if (cwsi)
+			lws_close_free_wsi(cwsi, 0);
+		break;
+
+	case LWS_H2_PPS_UPDATE_WINDOW:
+		lwsl_notice("LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n", pps->u.update_window.sid, pps->u.update_window.credit);
+		*p++ = pps->u.update_window.credit >> 24;
+		*p++ = pps->u.update_window.credit >> 16;
+		*p++ = pps->u.update_window.credit >> 8;
+		*p++ = pps->u.update_window.credit;
+		n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
+				       0, pps->u.update_window.sid, 4, &set[LWS_PRE]);
+		if (n != 4) {
+			lwsl_info("send %d %d\n", n, m);
+			goto bail;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	lws_free(pps);
+
+	return 0;
+
+bail:
+	lws_free(pps);
+
+	return 1;
+}
+
+/*
+ * The frame header part has just completely arrived.
+ * Perform actions for frame completion.
+ */
+static int
+lws_h2_parse_frame_header(struct lws *wsi)
+{
+	struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps;
+	int n;
+
+	/*
+	 * We just got the frame header
+	 */
+	h2n->count = 0;
+	h2n->swsi = wsi;
+	/* b31 is a reserved bit */
+	h2n->sid = h2n->sid & 0x7fffffff;
+
+	if (h2n->sid && !(h2n->sid & 1)) {
+		lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Even Stream ID");
+
+		return 0;
+	}
+
+	/* let the network wsi live a bit longer if subs are active */
+	lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 10);
+
+	/* let the network wsi live a bit longer if subs are active */
+	lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 10);
+
+	if (h2n->sid)
+		h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid);
+
+	lwsl_info("%p (%p): fr hdr: typ 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n",
+		  wsi, h2n->swsi, h2n->type, h2n->flags, h2n->sid,
+		  h2n->length);
+
+	if (h2n->we_told_goaway && h2n->sid > h2n->highest_sid)
+		h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
+
+	if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
+		return 0;
+
+	if (h2n->length > h2n->set.s[H2SET_MAX_FRAME_SIZE]) {
+		/*
+		 * peer sent us something bigger than we told
+		 * it we would allow
+		 */
+		lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+			      "Peer ignored our frame size setting");
+		return 0;
+	}
+
+	if (h2n->swsi)
+		lwsl_info("%s: wsi %p, State: %s, received cmd %d\n",
+		  __func__, h2n->swsi,
+		  h2_state_names[h2n->swsi->u.h2.h2_state], h2n->type);
+	else {
+		/* if it's data, either way no swsi means CLOSED state */
+		if (h2n->type == LWS_H2_FRAME_TYPE_DATA) {
+			lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED,
+				      "Data for nonexistant sid");
+			return 0;
+		}
+		/* if the sid is credible, treat as wsi for it closed */
+		if (h2n->sid > h2n->highest_sid_opened &&
+		    h2n->type != LWS_H2_FRAME_TYPE_HEADERS &&
+		    h2n->type != LWS_H2_FRAME_TYPE_PRIORITY) {
+			/* if not credible, reject it */
+			lwsl_info("%s: wsi %p, No child for sid %d, rx cmd %d\n",
+			  __func__, h2n->swsi, h2n->sid, h2n->type);
+			lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED,
+				     "Data for nonexistant sid");
+			return 0;
+		}
+	}
+
+	if (h2n->swsi && h2n->sid &&
+	    !(http2_rx_validity[h2n->swsi->u.h2.h2_state] & (1 << h2n->type))) {
+		lwsl_info("%s: wsi %p, State: %s, ILLEGAL cmdrx %d (OK 0x%x)\n",
+			  __func__, h2n->swsi,
+			  h2_state_names[h2n->swsi->u.h2.h2_state], h2n->type,
+			  http2_rx_validity[h2n->swsi->u.h2.h2_state]);
+
+		if (h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED ||
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE)
+			n = H2_ERR_STREAM_CLOSED;
+		else
+			n = H2_ERR_PROTOCOL_ERROR;
+		lws_h2_goaway(wsi, n, "invalid rx for state");
+
+		return 0;
+	}
+
+	if (h2n->cont_exp && (h2n->cont_exp_sid != h2n->sid ||
+			      h2n->type != LWS_H2_FRAME_TYPE_CONTINUATION)) {
+		lwsl_info("%s: expected cont on sid %d (got %d on sid %d)\n",
+			  __func__, h2n->cont_exp_sid, h2n->type, h2n->sid);
+		h2n->cont_exp = 0;
+		if (h2n->cont_exp_headers)
+			n = H2_ERR_COMPRESSION_ERROR;
+		else
+			n = H2_ERR_PROTOCOL_ERROR;
+		lws_h2_goaway(wsi, n, "Continuation hdrs State");
+
+		return 0;
+	}
+
+	switch (h2n->type) {
+	case LWS_H2_FRAME_TYPE_DATA:
+		lwsl_info("seen incoming LWS_H2_FRAME_TYPE_DATA start\n");
+		if (!h2n->sid) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "DATA 0 sid");
+			break;
+		}
+		lwsl_info("Frame header DATA: sid %d\n", h2n->sid);
+
+		if (!h2n->swsi)
+			break;
+
+		h2n->swsi->u.h2.peer_tx_cr_est -= h2n->length;
+		lwsl_debug("   peer_tx_cr_est %d\n", h2n->swsi->u.h2.peer_tx_cr_est);
+		if (h2n->swsi->u.h2.peer_tx_cr_est < 32768) {
+			h2n->swsi->u.h2.peer_tx_cr_est += 65536;
+			pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW);
+			if (!pps)
+				return 1;
+			pps->u.update_window.sid = h2n->sid;
+			pps->u.update_window.credit = 65536;
+			lws_pps_schedule(wsi, pps);
+			pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW);
+			if (!pps)
+				return 1;
+			pps->u.update_window.sid = 0;
+			pps->u.update_window.credit = 65536;
+			lws_pps_schedule(wsi, pps);
+		}
+
+		if ((
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE ||
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED)) {
+			lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED, "conn closed");
+			break;
+		}
+		break;
+	case LWS_H2_FRAME_TYPE_PRIORITY:
+		lwsl_info("LWS_H2_FRAME_TYPE_PRIORITY complete frame\n");
+		if (!h2n->sid) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "Priority has 0 sid");
+			break;
+		}
+		if (h2n->length != 5) {
+			lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+				      "Priority has length other than 5");
+			break;
+		}
+		break;
+	case LWS_H2_FRAME_TYPE_PUSH_PROMISE:
+		lwsl_info("LWS_H2_FRAME_TYPE_PUSH_PROMISE complete frame\n");
+		lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "Server only");
+		break;
+
+	case LWS_H2_FRAME_TYPE_GOAWAY:
+		break;
+
+	case LWS_H2_FRAME_TYPE_RST_STREAM:
+		if (!h2n->sid)
+			return 1;
+		if (!h2n->swsi) {
+			if (h2n->sid <= h2n->highest_sid_opened)
 				break;
-			case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
-				wsi->u.http2.hpack_e_dep <<= 8;
-				wsi->u.http2.hpack_e_dep |= c;
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "crazy sid on RST_STREAM");
+			return 1;
+		}
+		if (h2n->length != 4) {
+			lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+				      "RST_STREAM can only be length 4");
+			break;
+		}
+		lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED);
+		break;
+
+	case LWS_H2_FRAME_TYPE_SETTINGS:
+		lwsl_info("LWS_H2_FRAME_TYPE_SETTINGS complete frame\n");
+		/* nonzero sid on settings is illegal */
+		if (h2n->sid) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					 "Settings has nonzero sid");
+			break;
+		}
+
+		if (!(h2n->flags & LWS_H2_FLAG_SETTINGS_ACK)) {
+			if ((!h2n->length) || h2n->length % 6) {
+				lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+						 "Settings length error");
 				break;
 			}
-			if (wsi->u.http2.count != wsi->u.http2.length)
-				break;
+			lwsl_info("scheduled settings ack PPS\n");
+			/* non-ACK coming in means we must ACK it */
+
+
+			if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
+				return 0;
+
+			pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS);
+			if (!pps)
+				return 1;
+			lws_pps_schedule(wsi, pps);
+			break;
+		}
+		/* came to us with ACK set... not allowed to have payload */
 
-			/* end of frame */
+		if (h2n->length) {
+			lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+				      "Settings with ACK not allowed payload");
+			break;
+		}
+		break;
+	case LWS_H2_FRAME_TYPE_PING:
+		if (h2n->sid) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "Ping has nonzero sid");
+			break;
+		}
+		if (h2n->length != 8) {
+			lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+				      "Ping payload can only be 8");
+			break;
+		}
+		break;
+	case LWS_H2_FRAME_TYPE_CONTINUATION:
+		lwsl_info("LWS_H2_FRAME_TYPE_CONTINUATION: sid = %d\n",
+			  h2n->sid);
+
+		if (!h2n->cont_exp ||
+		     h2n->cont_exp_sid != h2n->sid ||
+		     !h2n->sid ||
+		     !h2n->swsi) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "unexpected CONTINUATION");
+			break;
+		}
+		if (h2n->swsi->u.h2.END_HEADERS) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "END_HEADERS already seen");
+			break;
+		}
+		/* END_STREAM is in HEADERS, skip resetting it */
+		goto update_end_headers;
+
+	case LWS_H2_FRAME_TYPE_HEADERS:
+		lwsl_info("HEADERS: frame header: sid = %d\n", h2n->sid);
+		if (!h2n->sid) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "sid 0");
+			return 1;
+		}
 
-			wsi->u.http2.frame_state = 0;
-			wsi->u.http2.count = 0;
-			swsi = wsi->u.http2.stream_wsi;
-			/* set our initial window size */
-			if (!wsi->u.http2.initialized) {
-				wsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
-				lwsl_info("initial tx credit on master conn %p: %d\n", wsi, wsi->u.http2.tx_credit);
-				wsi->u.http2.initialized = 1;
+		if (!h2n->swsi) {
+			/* no more children allowed by parent */
+			if (wsi->u.h2.child_count + 1 >
+			    wsi->u.h2.h2n->set.s[H2SET_MAX_CONCURRENT_STREAMS]) {
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				"Another stream not allowed");
+
+				return 1;
 			}
-			switch (wsi->u.http2.type) {
-			case LWS_HTTP2_FRAME_TYPE_HEADERS:
-				/* service the http request itself */
-				lwsl_info("servicing initial http request, wsi=%p, stream wsi=%p\n", wsi, wsi->u.http2.stream_wsi);
-				n = lws_http_action(swsi);
-				(void)n;
-				lwsl_info("  action result %d\n", n);
-				break;
-			case LWS_HTTP2_FRAME_TYPE_PING:
-				if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
-				} else { /* they're sending us a ping request */
-					lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_PONG);
-				}
-				break;
-			case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
-				wsi->u.http2.hpack_e_dep &= ~(1 << 31);
-				if ((lws_intptr_t)swsi->u.http2.tx_credit + (lws_intptr_t)wsi->u.http2.hpack_e_dep > (~(1 << 31)))
-					return 1; /* actually need to close swsi not the whole show */
-				swsi->u.http2.tx_credit += wsi->u.http2.hpack_e_dep;
-				if (swsi->u.http2.waiting_tx_credit && swsi->u.http2.tx_credit > 0) {
-					lwsl_info("%s: %p: waiting_tx_credit -> wait on writeable\n", __func__, wsi);
-					swsi->u.http2.waiting_tx_credit = 0;
-					lws_callback_on_writable(swsi);
-				}
-				break;
+
+			h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, h2n->sid);
+			if (!h2n->swsi) {
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "OOM");
+
+				return 1;
 			}
+		}
+
+		/*
+		 * ah needs attaching to child wsi, even though
+		 * we only fill it from network wsi
+		 */
+		if (!h2n->swsi->u.hdr.ah)
+			if (lws_header_table_attach(h2n->swsi, 0)) {
+				lwsl_err("%s: Failed to get ah\n", __func__);
+				return 1;
+			}
+
+		/*
+		 * The first use of a new stream identifier implicitly closes
+		 * all streams in the "idle" state that might have been
+		 * initiated by that peer with a lower-valued stream identifier.
+		 *
+		 * For example, if a client sends a HEADERS frame on stream 7
+		 * without ever sending a frame on stream 5, then stream 5
+		 * transitions to the "closed" state when the first frame for
+		 * stream 7 is sent or received.
+		 */
+		lws_start_foreach_ll(struct lws *, w, wsi->u.h2.child_list) {
+			if (w->u.h2.my_sid < h2n->sid &&
+			    w->u.h2.h2_state == LWS_H2_STATE_IDLE)
+				lws_close_free_wsi(w, 0);
+		} lws_end_foreach_ll(w, u.h2.sibling_list);
+
+
+		/* END_STREAM means after servicing this, close the stream */
+		h2n->swsi->u.h2.END_STREAM = !!(h2n->flags & LWS_H2_FLAG_END_STREAM);
+		lwsl_info("%s: hdr END_STREAM = %d\n",__func__,
+			  h2n->swsi->u.h2.END_STREAM);
+
+		h2n->cont_exp = !(h2n->flags & LWS_H2_FLAG_END_HEADERS);
+		h2n->cont_exp_sid = h2n->sid;
+		h2n->cont_exp_headers = 1;
+		h2n->seen_nonpseudoheader = 0;
+		lws_header_table_reset(h2n->swsi, 0);
+
+update_end_headers:
+		/* no END_HEADERS means CONTINUATION must come */
+		h2n->swsi->u.h2.END_HEADERS =
+				!!(h2n->flags & LWS_H2_FLAG_END_HEADERS);
+		if (h2n->swsi->u.h2.END_HEADERS)
+			h2n->cont_exp = 0;
+		lwsl_debug("END_HEADERS %d\n", h2n->swsi->u.h2.END_HEADERS);
+		break;
+
+	case LWS_H2_FRAME_TYPE_WINDOW_UPDATE:
+		if (h2n->length != 4) {
+			lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
+				      "window update frame not 4");
 			break;
 		}
-		switch (wsi->u.http2.frame_state++) {
-		case 0:
-			wsi->u.http2.length = c;
+		lwsl_info("LWS_H2_FRAME_TYPE_WINDOW_UPDATE\n");
+		break;
+	default:
+		lwsl_info("%s: ILLEGAL FRAME TYPE %d\n", __func__, h2n->type);
+		h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
+		break;
+	}
+	if (h2n->length == 0)
+		h2n->frame_state = 0;
+
+	return 0;
+}
+
+/*
+ * The last byte of the whole frame has been handled.
+ * Perform actions for frame completion.
+ */
+static int
+lws_h2_parse_end_of_frame(struct lws *wsi)
+{
+	struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps;
+	struct lws *eff_wsi = wsi;
+	const char *p;
+	int n;
+
+	h2n->frame_state = 0;
+	h2n->count = 0;
+
+	if (h2n->sid)
+		h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid);
+
+	if (h2n->sid > h2n->highest_sid)
+		h2n->highest_sid = h2n->sid;
+
+	/* set our initial window size */
+	if (!wsi->u.h2.initialized) {
+		wsi->u.h2.tx_cr = h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
+		lwsl_info("initial tx credit on master %p: %d\n", wsi,
+			  wsi->u.h2.tx_cr);
+		wsi->u.h2.initialized = 1;
+	}
+
+	if (h2n->collected_priority && (h2n->dep & ~(1 << 31)) == h2n->sid) {
+		lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR, "depends on own sid");
+		return 0;
+	}
+
+	switch (h2n->type) {
+	case LWS_H2_FRAME_TYPE_CONTINUATION:
+	case LWS_H2_FRAME_TYPE_HEADERS:
+
+		/* service the http request itself */
+
+		if (h2n->last_action_dyntable_resize) {
+			lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR,
+				"dyntable resize last in headers");
 			break;
-		case 1:
-		case 2:
-			wsi->u.http2.length <<= 8;
-			wsi->u.http2.length |= c;
+		}
+
+		if (!h2n->swsi->u.h2.END_HEADERS) {
+			/* we are not finished yet */
+			lwsl_info("witholding http action for continuation\n");
 			break;
-		case 3:
-			wsi->u.http2.type = c;
+		}
+
+		/* confirm the hpack stream state is reasonable for finishing */
+
+		if (h2n->hpack != HPKS_TYPE) {
+			/* hpack incomplete */
+			lwsl_info("hpack incomplete %d (type %d, len %d)\n",
+				  h2n->hpack, h2n->type, h2n->hpack_len);
+			lws_h2_goaway(wsi, H2_ERR_COMPRESSION_ERROR,
+				      "hpack incomplete");
 			break;
-		case 4:
-			wsi->u.http2.flags = c;
+		}
+
+		/* this is the last part of HEADERS */
+		switch (h2n->swsi->u.h2.h2_state) {
+		case LWS_H2_STATE_IDLE:
+			lws_h2_state(h2n->swsi, LWS_H2_STATE_OPEN);
 			break;
-		case 5:
-		case 6:
-		case 7:
-		case 8:
-			wsi->u.http2.stream_id <<= 8;
-			wsi->u.http2.stream_id |= c;
+		case LWS_H2_STATE_RESERVED_REMOTE:
+			lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_LOCAL);
 			break;
 		}
-		if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { /* frame header complete */
-			lwsl_info("frame: type 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n",
-				  wsi->u.http2.type, wsi->u.http2.flags, wsi->u.http2.stream_id, wsi->u.http2.length);
-			wsi->u.http2.count = 0;
 
-			wsi->u.http2.stream_wsi = wsi;
-			if (wsi->u.http2.stream_id)
-				wsi->u.http2.stream_wsi = lws_http2_wsi_from_id(wsi, wsi->u.http2.stream_id);
+		lwsl_info("http req, wsi=%p, h2n->swsi=%p\n", wsi, h2n->swsi);
+		h2n->swsi->hdr_parsing_completed = 1;
+
+		if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
+			h2n->swsi->u.http.rx_content_length  = atoll(
+				lws_hdr_simple_ptr(h2n->swsi,
+				      WSI_TOKEN_HTTP_CONTENT_LENGTH));
+			h2n->swsi->u.http.rx_content_remain =
+					h2n->swsi->u.http.rx_content_length;
+			lwsl_info("setting rx_content_length %lld\n",
+				   (long long)h2n->swsi->u.http.rx_content_length);
+		}
 
-			switch (wsi->u.http2.type) {
-			case LWS_HTTP2_FRAME_TYPE_SETTINGS:
-				/* nonzero sid on settings is illegal */
-				if (wsi->u.http2.stream_id)
-					return 1;
+		{
+			int n = 0, len;
+			char buf[256];
+			const unsigned char *c;
 
-				if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
-				} else
-					/* non-ACK coming in means we must ACK it */
-					lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
-				break;
-			case LWS_HTTP2_FRAME_TYPE_PING:
-				if (wsi->u.http2.stream_id)
-					return 1;
-				if (wsi->u.http2.length != 8)
-					return 1;
-				break;
-			case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
-				if (wsi->u.http2.END_HEADERS)
-					return 1;
-				goto update_end_headers;
+			do {
+				c = lws_token_to_string(n);
+				if (!c) {
+					n++;
+					continue;
+				}
 
-			case LWS_HTTP2_FRAME_TYPE_HEADERS:
-				lwsl_info("LWS_HTTP2_FRAME_TYPE_HEADERS: stream_id = %d\n", wsi->u.http2.stream_id);
-				if (!wsi->u.http2.stream_id)
-					return 1;
-				if (!wsi->u.http2.stream_wsi) {
-					wsi->u.http2.stream_wsi =
-						lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id);
-					wsi->u.http2.stream_wsi->http2_substream = 1;
+				len = lws_hdr_total_length(h2n->swsi, n);
+				if (!len || len > sizeof(buf) - 1) {
+					n++;
+					continue;
 				}
 
-				/* END_STREAM means after servicing this, close the stream */
-				wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
-				lwsl_info("%s: headers END_STREAM = %d\n",__func__, wsi->u.http2.END_STREAM);
-update_end_headers:
-				/* no END_HEADERS means CONTINUATION must come */
-				wsi->u.http2.END_HEADERS = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_HEADERS);
+				lws_hdr_copy(h2n->swsi, buf, sizeof buf, n);
+				buf[sizeof(buf) - 1] = '\0';
 
-				swsi = wsi->u.http2.stream_wsi;
-				if (!swsi)
-					return 1;
+				lwsl_info("    %s = %s\n", (char *)c, buf);
+				n++;
+			} while (c);
+		}
 
+		if (h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_REMOTE ||
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_CLOSED) {
+			lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED,
+				      "Banning service on CLOSED_REMOTE");
+			break;
+		}
 
-				/* prepare the hpack parser at the right start */
+		switch (h2n->swsi->u.h2.h2_state) {
+		case LWS_H2_STATE_OPEN:
+			if (h2n->swsi->u.h2.END_STREAM)
+				lws_h2_state(h2n->swsi,
+					     LWS_H2_STATE_HALF_CLOSED_REMOTE);
+			break;
+		case LWS_H2_STATE_HALF_CLOSED_LOCAL:
+			if (h2n->swsi->u.h2.END_STREAM)
+				lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED);
+			break;
+		}
 
-				swsi->u.http2.flags = wsi->u.http2.flags;
-				swsi->u.http2.length = wsi->u.http2.length;
-				swsi->u.http2.END_STREAM = wsi->u.http2.END_STREAM;
+		if (!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_PATH) ||
+		    !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD) ||
+		    !lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_SCHEME) ||
+		     lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_STATUS) ||
+		     lws_hdr_extant(h2n->swsi, WSI_TOKEN_CONNECTION)) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "Pseudoheader checks");
+			break;
+		}
 
-				if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PADDED)
-					swsi->u.http2.hpack = HPKS_OPT_PADDING;
-				else
-					if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
-						swsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
-						swsi->u.http2.hpack_m = 4;
-					} else
-						swsi->u.http2.hpack = HPKS_TYPE;
-				lwsl_info("initial hpack state %d\n", swsi->u.http2.hpack);
-				break;
-			case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
-				if (wsi->u.http2.length != 4)
-					return 1;
+		if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_TE)) {
+			n = lws_hdr_total_length(h2n->swsi, WSI_TOKEN_TE);
+
+			if (n != 8 ||
+			    strncmp(lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_TE),
+				  "trailers", n)) {
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					      "Illegal transfer-encoding");
 				break;
 			}
-			if (wsi->u.http2.length == 0)
-				wsi->u.http2.frame_state = 0;
+		}
+
+		p = lws_hdr_simple_ptr(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD);
+		if (!strcmp(p, "POST"))
+			h2n->swsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI] =
+				h2n->swsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP_COLON_PATH];
+
+		wsi->vhost->conn_stats.h2_trans++;
+
+		lwsl_info("  action start...\n");
+		n = lws_http_action(h2n->swsi);
+		lwsl_info("  action result %d (wsi->u.http.rx_content_remain %lld)\n", n, h2n->swsi->u.http.rx_content_remain);
+
+		/*
+		 * Commonly we only managed to start a larger transfer that will
+		 * complete asynchronously.  In those cases we will hear about
+		 * END_STREAM going out in the POLLOUT handler.
+		 */
+		if (n || h2n->swsi->u.h2.send_END_STREAM) {
+			lws_close_free_wsi(h2n->swsi, 0);
+			h2n->swsi = NULL;
+			break;
+		}
+		break;
+
+	case LWS_H2_FRAME_TYPE_DATA:
+		if (!h2n->swsi)
+			break;
+
+		if (lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
+		    h2n->swsi->u.h2.END_STREAM && h2n->swsi->u.http.rx_content_length &&
+		    h2n->swsi->u.http.rx_content_remain) {
+			lws_h2_rst_stream(h2n->swsi, H2_ERR_PROTOCOL_ERROR,
+					  "Not enough rx content");
+			break;
+		}
+
+		if (h2n->swsi->u.h2.END_STREAM &&
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_OPEN)
+			lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_REMOTE);
+
+		if (h2n->swsi->u.h2.END_STREAM &&
+		    h2n->swsi->u.h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL)
+			lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED);
+		break;
+
+	case LWS_H2_FRAME_TYPE_PING:
+		if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack
+		} else {/* they're sending us a ping request */
+			lwsl_info("rx ping, preparing pong\n");
+			pps = lws_h2_new_pps(LWS_H2_PPS_PONG);
+			if (!pps)
+				return 1;
+			memcpy(pps->u.ping.ping_payload, h2n->ping_payload, 8);
+			lws_pps_schedule(wsi, pps);
+		}
+
+		break;
+
+	case LWS_H2_FRAME_TYPE_WINDOW_UPDATE:
+		h2n->hpack_e_dep &= ~(1 << 31);
+		lwsl_info("WINDOW_UPDATE: sid %d %u (0x%x)\n", h2n->sid,
+			    h2n->hpack_e_dep, h2n->hpack_e_dep);
+
+		if (h2n->sid)
+			eff_wsi = h2n->swsi;
 
+		if (!eff_wsi) {
+			if (h2n->sid > h2n->highest_sid_opened)
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					      "alien sid");
+			break; /* ignore */
 		}
+
+		if ((uint64_t)eff_wsi->u.h2.tx_cr + (uint64_t)h2n->hpack_e_dep >
+		    (uint64_t)0x7fffffff) {
+			if (h2n->sid)
+				lws_h2_rst_stream(h2n->swsi,
+						  H2_ERR_FLOW_CONTROL_ERROR,
+						  "Flow control exceeded max");
+			else
+				lws_h2_goaway(wsi, H2_ERR_FLOW_CONTROL_ERROR,
+					      "Flow control exceeded max");
+			break;
+		}
+
+		if (!h2n->hpack_e_dep) {
+			lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+				      "Zero length window update");
+			break;
+		}
+		n = eff_wsi->u.h2.tx_cr;
+		eff_wsi->u.h2.tx_cr += h2n->hpack_e_dep;
+
+		if (n <= 0 && eff_wsi->u.h2.tx_cr <= 0)
+			/* it helps, but won't change sendability for anyone */
+			break;
+
+		/*
+		 * It did change sendability... for us and any children waiting
+		 * on us... reassess blockage for all children first
+		 */
+		lws_start_foreach_ll(struct lws *, w, wsi->u.h2.child_list) {
+			lws_callback_on_writable(w);
+		} lws_end_foreach_ll(w, u.h2.sibling_list);
+
+		if (eff_wsi->u.h2.skint && lws_h2_tx_cr_get(eff_wsi)) {
+			lwsl_info("%s: %p: skint\n", __func__, wsi);
+			eff_wsi->u.h2.skint = 0;
+			lws_callback_on_writable(eff_wsi);
+		}
+		break;
+
+	case LWS_H2_FRAME_TYPE_GOAWAY:
+		lwsl_info("GOAWAY: last sid %d, error 0x%08X, string '%s'\n",
+			  h2n->goaway_last_sid, h2n->goaway_err, h2n->goaway_str);
+		wsi->u.h2.GOING_AWAY = 1;
+
+		return 1;
+
+	case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */
 		break;
 	}
 
 	return 0;
 }
 
-int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
+int
+lws_h2_parser(struct lws *wsi, unsigned char c)
 {
-	unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
-	struct lws *swsi;
-	int n, m = 0;
+	struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+	struct lws_h2_protocol_send *pps;
+	int n;
 
-	lwsl_debug("%s: %p: %d\n", __func__, wsi, wsi->pps);
+	if (!h2n)
+		return 1;
 
-	switch (wsi->pps) {
-	case LWS_PPS_HTTP2_MY_SETTINGS:
-		for (n = 1; n < LWS_HTTP2_SETTINGS__COUNT; n++)
-			if (wsi->u.http2.my_settings.setting[n] != lws_http2_default_settings.setting[n]) {
-				lws_http2_settings_write(wsi, n,
-							 &settings[LWS_PRE + m]);
-				m += sizeof(wsi->u.http2.one_setting);
-			}
-		n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
-		     			  0, LWS_HTTP2_STREAM_ID_MASTER, m,
-		     			  &settings[LWS_PRE]);
-		if (n != m) {
-			lwsl_info("send %d %d\n", n, m);
+	switch (wsi->state) {
+	case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
+		if (preface[h2n->count++] != c)
 			return 1;
-		}
-		break;
-	case LWS_PPS_HTTP2_ACK_SETTINGS:
-		/* send ack ... always empty */
-		n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
-			1, LWS_HTTP2_STREAM_ID_MASTER, 0,
-			&settings[LWS_PRE]);
-		if (n) {
-			lwsl_err("ack tells %d\n", n);
+
+		if (preface[h2n->count])
+			break;
+
+		lwsl_info("http2: %p: established\n", wsi);
+		wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS;
+		h2n->count = 0;
+		wsi->u.h2.tx_cr = 65535;
+
+		/*
+		 * we must send a settings frame -- empty one is OK...
+		 * that must be the first thing sent by server
+		 * and the peer must send a SETTINGS with ACK flag...
+		 */
+		pps = lws_h2_new_pps(LWS_H2_PPS_MY_SETTINGS);
+		if (!pps)
 			return 1;
-		}
-		/* this is the end of the preface dance then? */
-		if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) {
-			wsi->state = LWSS_HTTP2_ESTABLISHED;
+		lws_pps_schedule(wsi, pps);
+		break;
 
-			wsi->u.http.fop_fd = NULL;
+	case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
+	case LWSS_HTTP2_ESTABLISHED:
+		if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH)
+			goto try_frame_start;
 
-			if (lws_is_ssl(lws_http2_get_network_wsi(wsi))) {
-				lwsl_info("skipping nonexistent ssl upgrade headers\n");
-				break;
-			}
+		/*
+		 * post-header, preamble / payload / padding part
+		 */
+		h2n->count++;
 
+		if (h2n->flags & LWS_H2_FLAG_PADDED && !h2n->pad_length) {
 			/*
-			 * we need to treat the headers from this upgrade
-			 * as the first job.  These need to get
-			 * shifted to stream ID 1
+			 * Get the padding count... actual padding is
+			 * at the end of the frame.
 			 */
-			lwsl_info("%s: setting up sid 1\n", __func__);
+			h2n->padding = c;
+			h2n->pad_length = 1;
+			h2n->preamble++;
+
+			if (h2n->padding > h2n->length - 1)
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					      "execssive padding");
+			break; /* we consumed this */
+		}
 
-			swsi = wsi->u.http2.stream_wsi =
-					lws_create_server_child_wsi(wsi->vhost, wsi, 1);
-			/* pass on the initial headers to SID 1 */
-			swsi->u.http.ah = wsi->u.http.ah;
-			wsi->u.http.ah = NULL;
+		if (h2n->flags & LWS_H2_FLAG_PRIORITY &&
+		    !h2n->collected_priority) {
+			/* going to be 5 preamble bytes */
 
-			lwsl_info("%s: inherited headers %p\n", __func__, swsi->u.http.ah);
-			swsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
-			lwsl_info("initial tx credit on conn %p: %d\n", swsi, swsi->u.http2.tx_credit);
-			swsi->u.http2.initialized = 1;
-			/* demanded by HTTP2 */
-			swsi->u.http2.END_STREAM = 1;
-			lwsl_info("servicing initial http request\n");
-			return lws_http_action(swsi);
+			lwsl_debug("PRIORITY FLAG:  0x%x\n", c);
+
+			if (h2n->preamble++ - h2n->pad_length < 4) {
+				h2n->dep = ((h2n->dep) << 8) | c;
+				break; /* we consumed this */
+			}
+			h2n->weight_temp = c;
+			h2n->collected_priority = 1;
+			lwsl_debug("PRI FL: dep 0x%x, weight 0x%02X\n",
+				   h2n->dep, h2n->weight_temp);
+			break; /* we consumed this */
 		}
-		break;
-	case LWS_PPS_HTTP2_PONG:
-		memcpy(&settings[LWS_PRE], wsi->u.http2.ping_payload, 8);
-		n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_PING,
-		     			  LWS_HTTP2_FLAG_SETTINGS_ACK,
-			    		  LWS_HTTP2_STREAM_ID_MASTER, 8,
-		     			  &settings[LWS_PRE]);
-		if (n != 8) {
-			lwsl_info("send %d %d\n", n, m);
+		if (h2n->padding && h2n->count > (h2n->length - h2n->padding)) {
+			if (c) {
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					      "nonzero padding");
+				break;
+			}
+			goto frame_end;
+		}
+
+		/* applies to wsi->u.h2.swsi which may be wsi */
+		switch(h2n->type) {
+
+		case LWS_H2_FRAME_TYPE_SETTINGS:
+			n = (h2n->count - 1 - h2n->preamble) %
+			     LWS_H2_SETTINGS_LEN;
+			h2n->one_setting[n] = c;
+			if (n != LWS_H2_SETTINGS_LEN - 1)
+				break;
+			lws_h2_settings(wsi, &h2n->set, h2n->one_setting,
+					LWS_H2_SETTINGS_LEN);
+			break;
+
+		case LWS_H2_FRAME_TYPE_CONTINUATION:
+		case LWS_H2_FRAME_TYPE_HEADERS:
+			if (!h2n->swsi)
+				break;
+			if (lws_hpack_interpret(h2n->swsi, c)) {
+				lwsl_info("%s: hpack failed\n", __func__);
+				return 1;
+			}
+			break;
+
+		case LWS_H2_FRAME_TYPE_GOAWAY:
+			switch (h2n->inside++) {
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+				h2n->goaway_last_sid <<= 8;
+				h2n->goaway_last_sid |= c;
+				h2n->goaway_str[0] = '\0';
+				break;
+
+			case 4:
+			case 5:
+			case 6:
+			case 7:
+				h2n->goaway_err <<= 8;
+				h2n->goaway_err |= c;
+				break;
+
+			default:
+				if (h2n->inside - 9 <
+				    sizeof(h2n->goaway_str) - 1)
+					h2n->goaway_str[h2n->inside - 9] = c;
+				h2n->goaway_str[sizeof(h2n->goaway_str) - 1] = '\0';
+				break;
+			}
+			break;
+
+		case LWS_H2_FRAME_TYPE_DATA:
+			//lwsl_notice("incoming LWS_H2_FRAME_TYPE_DATA content\n");
+			if (!h2n->swsi) {
+				//lwsl_notice("data sid %d has no swsi\n", h2n->sid);
+				break;
+			}
+
+			h2n->swsi->state = LWSS_HTTP_BODY;
+
+			h2n->inside++;
+			/* because the HTTP_BODY stuff will handle it */
+			//h2n->swsi->u.http.rx_content_remain--;
+			//lwsl_info("remain %lld, %d / %d",
+			//	    (long long)h2n->swsi->u.http.rx_content_remain,
+			//	    h2n->inside, h2n->length);
+			if (lws_hdr_total_length(h2n->swsi,
+						 WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
+			    h2n->swsi->u.http.rx_content_length &&
+			    h2n->swsi->u.http.rx_content_remain == 1 && /* last byte */
+			    h2n->inside < h2n->length) { /* unread data in frame */
+				lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+					      "More rx than content_length told");
+				break;
+			}
+
+			n = lws_read(h2n->swsi, &c, 1);
+			if (n < 0) {
+			//	lws_h2_rst_stream(wsi, LWS_H2_PPS_RST_STREAM,
+			//			  "post body done");
+				break;
+			}
+			break;
+
+		case LWS_H2_FRAME_TYPE_PRIORITY:
+			if (h2n->count <= 4) {
+				h2n->dep <<= 8;
+				h2n->dep |= c;
+			} else {
+				h2n->weight_temp = c;
+				lwsl_info("PRIORITY: dep 0x%x, weight 0x%02X\n",
+					  h2n->dep, h2n->weight_temp);
+
+				if ((h2n->dep & ~(1 << 31)) == h2n->sid) {
+					lws_h2_goaway(wsi, H2_ERR_PROTOCOL_ERROR,
+						      "cant depend on own sid");
+					break;
+				}
+			}
+			break;
+
+		case LWS_H2_FRAME_TYPE_RST_STREAM:
+			break;
+
+		case LWS_H2_FRAME_TYPE_PUSH_PROMISE:
+			break;
+
+		case LWS_H2_FRAME_TYPE_PING:
+			if (h2n->flags & LWS_H2_FLAG_SETTINGS_ACK) { // ack
+			} else { /* they're sending us a ping request */
+				if (h2n->count > 8)
+					return 1;
+				h2n->ping_payload[h2n->count - 1] = c;
+			}
+			break;
+
+		case LWS_H2_FRAME_TYPE_WINDOW_UPDATE:
+			h2n->hpack_e_dep <<= 8;
+			h2n->hpack_e_dep |= c;
+			break;
+
+		case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */
+			break;
+
+		default:
+			lwsl_notice("%s: unhandled frame type %d\n",
+				    __func__, h2n->type);
+
 			return 1;
 		}
+
+frame_end:
+		if (h2n->count != h2n->length)
+			break;
+
+		/*
+		 * end of frame just happened
+		 */
+		if (lws_h2_parse_end_of_frame(wsi))
+			return 1;
 		break;
-	default:
+
+try_frame_start:
+		if (h2n->frame_state <= 8) {
+
+			switch (h2n->frame_state++) {
+			case 0:
+				h2n->pad_length = 0;
+				h2n->collected_priority = 0;
+				h2n->padding = 0;
+				h2n->preamble = 0;
+				h2n->length = c;
+				h2n->inside = 0;
+				break;
+			case 1:
+			case 2:
+				h2n->length <<= 8;
+				h2n->length |= c;
+				break;
+			case 3:
+				h2n->type = c;
+				break;
+			case 4:
+				h2n->flags = c;
+				break;
+
+			case 5:
+			case 6:
+			case 7:
+			case 8:
+				h2n->sid <<= 8;
+				h2n->sid |= c;
+				break;
+			}
+		}
+		if (h2n->frame_state == LWS_H2_FRAME_HEADER_LENGTH)
+			if (lws_h2_parse_frame_header(wsi))
+				return 1;
 		break;
 	}
 
 	return 0;
 }
 
-struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
-{
-	do {
-		wsi = wsi->u.http2.next_child_wsi;
-		if (!wsi)
-			return NULL;
-	} while (n--);
-
-	return wsi;
-}
diff --git a/lib/lejp-conf.c b/lib/lejp-conf.c
index c2f4793d1781b49bdbcba6dc0b37f7e256577e93..e84f4b24ceac6a0e48c9d3f33dd8b0d5385e7e09 100644
--- a/lib/lejp-conf.c
+++ b/lib/lejp-conf.c
@@ -477,7 +477,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
 		for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
 			if (!strncmp(a->m.origin, mount_protocols[n],
 			     strlen(mount_protocols[n]))) {
-				lwsl_err("----%s\n", a->m.origin);
+				lwsl_info("----%s\n", a->m.origin);
 				m->origin_protocol = n;
 				m->origin = a->m.origin +
 					    strlen(mount_protocols[n]);
diff --git a/lib/lextable-strings.h b/lib/lextable-strings.h
index c9fe6ff3ae77e99bd97a679a2f7ca73de6383cfa..ab42c3e476e968acfcd56144d5bab53cb33af8cb 100644
--- a/lib/lextable-strings.h
+++ b/lib/lextable-strings.h
@@ -96,6 +96,8 @@ STORE_IN_ROM static const char * const set[] = {
 
 	"x-forwarded-for",
 	"connect ",
+	"head ",
+	"te:",		/* http/2 wants it to reject it */
 
 	"", /* not matchable */
 
diff --git a/lib/lextable.h b/lib/lextable.h
index 2f4f079acfc186c54f95ad90a919a789d57aad4b..f940afd25bd164e92e3dbee0ee843f8a86e25351 100644
--- a/lib/lextable.h
+++ b/lib/lextable.h
@@ -2,39 +2,39 @@
                        0x70 /* 'p' */, 0x42, 0x00  /* (to 0x0045 state   5) */,
                        0x6F /* 'o' */, 0x51, 0x00  /* (to 0x0057 state  10) */,
                        0x68 /* 'h' */, 0x5D, 0x00  /* (to 0x0066 state  18) */,
-                       0x63 /* 'c' */, 0x66, 0x00  /* (to 0x0072 state  23) */,
-                       0x75 /* 'u' */, 0x87, 0x00  /* (to 0x0096 state  34) */,
-                       0x73 /* 's' */, 0x9D, 0x00  /* (to 0x00AF state  48) */,
-                       0x0D /* '.' */, 0xD6, 0x00  /* (to 0x00EB state  68) */,
-                       0x61 /* 'a' */, 0x2E, 0x01  /* (to 0x0146 state 129) */,
-                       0x69 /* 'i' */, 0x6D, 0x01  /* (to 0x0188 state 163) */,
-                       0x64 /* 'd' */, 0x16, 0x02  /* (to 0x0234 state 265) */,
-                       0x72 /* 'r' */, 0x1F, 0x02  /* (to 0x0240 state 270) */,
-                       0x3A /* ':' */, 0x50, 0x02  /* (to 0x0274 state 299) */,
-                       0x65 /* 'e' */, 0xDC, 0x02  /* (to 0x0303 state 409) */,
-                       0x66 /* 'f' */, 0xF8, 0x02  /* (to 0x0322 state 425) */,
-                       0x6C /* 'l' */, 0x1A, 0x03  /* (to 0x0347 state 458) */,
-                       0x6D /* 'm' */, 0x3D, 0x03  /* (to 0x036D state 484) */,
-                       0x74 /* 't' */, 0xAC, 0x03  /* (to 0x03DF state 578) */,
-                       0x76 /* 'v' */, 0xC7, 0x03  /* (to 0x03FD state 606) */,
-                       0x77 /* 'w' */, 0xD4, 0x03  /* (to 0x040D state 614) */,
-                       0x78 /* 'x' */, 0xFB, 0x03  /* (to 0x0437 state 650) */,
+                       0x63 /* 'c' */, 0x69, 0x00  /* (to 0x0075 state  23) */,
+                       0x75 /* 'u' */, 0x8A, 0x00  /* (to 0x0099 state  34) */,
+                       0x73 /* 's' */, 0xA0, 0x00  /* (to 0x00B2 state  48) */,
+                       0x0D /* '.' */, 0xD9, 0x00  /* (to 0x00EE state  68) */,
+                       0x61 /* 'a' */, 0x31, 0x01  /* (to 0x0149 state 129) */,
+                       0x69 /* 'i' */, 0x70, 0x01  /* (to 0x018B state 163) */,
+                       0x64 /* 'd' */, 0x19, 0x02  /* (to 0x0237 state 265) */,
+                       0x72 /* 'r' */, 0x22, 0x02  /* (to 0x0243 state 270) */,
+                       0x3A /* ':' */, 0x53, 0x02  /* (to 0x0277 state 299) */,
+                       0x65 /* 'e' */, 0xDF, 0x02  /* (to 0x0306 state 409) */,
+                       0x66 /* 'f' */, 0xFB, 0x02  /* (to 0x0325 state 425) */,
+                       0x6C /* 'l' */, 0x1D, 0x03  /* (to 0x034A state 458) */,
+                       0x6D /* 'm' */, 0x40, 0x03  /* (to 0x0370 state 484) */,
+                       0x74 /* 't' */, 0xAF, 0x03  /* (to 0x03E2 state 578) */,
+                       0x76 /* 'v' */, 0xD0, 0x03  /* (to 0x0406 state 606) */,
+                       0x77 /* 'w' */, 0xDD, 0x03  /* (to 0x0416 state 614) */,
+                       0x78 /* 'x' */, 0x04, 0x04  /* (to 0x0440 state 650) */,
                        0x08, /* fail */
 /* pos 0040:   1 */    0xE5 /* 'e' -> */,
 /* pos 0041:   2 */    0xF4 /* 't' -> */,
 /* pos 0042:   3 */    0xA0 /* ' ' -> */,
 /* pos 0043:   4 */    0x00, 0x00                  /* - terminal marker  0 - */,
 /* pos 0045:   5 */    0x6F /* 'o' */, 0x0D, 0x00  /* (to 0x0052 state   6) */,
-                       0x72 /* 'r' */, 0x92, 0x01  /* (to 0x01DA state 211) */,
-                       0x61 /* 'a' */, 0xD4, 0x03  /* (to 0x041F state 631) */,
-                       0x75 /* 'u' */, 0xD6, 0x03  /* (to 0x0424 state 635) */,
+                       0x72 /* 'r' */, 0x95, 0x01  /* (to 0x01DD state 211) */,
+                       0x61 /* 'a' */, 0xDD, 0x03  /* (to 0x0428 state 631) */,
+                       0x75 /* 'u' */, 0xDF, 0x03  /* (to 0x042D state 635) */,
                        0x08, /* fail */
 /* pos 0052:   6 */    0xF3 /* 's' -> */,
 /* pos 0053:   7 */    0xF4 /* 't' -> */,
 /* pos 0054:   8 */    0xA0 /* ' ' -> */,
 /* pos 0055:   9 */    0x00, 0x01                  /* - terminal marker  1 - */,
 /* pos 0057:  10 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x005E state  11) */,
-                       0x72 /* 'r' */, 0x4E, 0x00  /* (to 0x00A8 state  42) */,
+                       0x72 /* 'r' */, 0x51, 0x00  /* (to 0x00AB state  42) */,
                        0x08, /* fail */
 /* pos 005e:  11 */    0xF4 /* 't' -> */,
 /* pos 005f:  12 */    0xE9 /* 'i' -> */,
@@ -43,754 +43,763 @@
 /* pos 0062:  15 */    0xF3 /* 's' -> */,
 /* pos 0063:  16 */    0xA0 /* ' ' -> */,
 /* pos 0064:  17 */    0x00, 0x02                  /* - terminal marker  2 - */,
-/* pos 0066:  18 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x006D state  19) */,
-                       0x74 /* 't' */, 0xBC, 0x00  /* (to 0x0125 state 110) */,
-                       0x08, /* fail */
-/* pos 006d:  19 */    0xF3 /* 's' -> */,
-/* pos 006e:  20 */    0xF4 /* 't' -> */,
-/* pos 006f:  21 */    0xBA /* ':' -> */,
-/* pos 0070:  22 */    0x00, 0x03                  /* - terminal marker  3 - */,
-/* pos 0072:  23 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x0079 state  24) */,
-                       0x61 /* 'a' */, 0x72, 0x01  /* (to 0x01E7 state 217) */,
-                       0x08, /* fail */
-/* pos 0079:  24 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x0080 state  25) */,
-                       0x6F /* 'o' */, 0x87, 0x01  /* (to 0x0203 state 243) */,
-                       0x08, /* fail */
-/* pos 0080:  25 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x0087 state  26) */,
-                       0x74 /* 't' */, 0x86, 0x01  /* (to 0x0209 state 248) */,
-                       0x08, /* fail */
-/* pos 0087:  26 */    0xE5 /* 'e' -> */,
-/* pos 0088:  27 */    0xE3 /* 'c' -> */,
-/* pos 0089:  28 */    0xF4 /* 't' -> */,
-/* pos 008a:  29 */    0x69 /* 'i' */, 0x07, 0x00  /* (to 0x0091 state  30) */,
-                       0x20 /* ' ' */, 0xCC, 0x03  /* (to 0x0459 state 675) */,
-                       0x08, /* fail */
-/* pos 0091:  30 */    0xEF /* 'o' -> */,
-/* pos 0092:  31 */    0xEE /* 'n' -> */,
-/* pos 0093:  32 */    0xBA /* ':' -> */,
-/* pos 0094:  33 */    0x00, 0x04                  /* - terminal marker  4 - */,
-/* pos 0096:  34 */    0x70 /* 'p' */, 0x0A, 0x00  /* (to 0x00A0 state  35) */,
-                       0x73 /* 's' */, 0x59, 0x03  /* (to 0x03F2 state 596) */,
-                       0x72 /* 'r' */, 0x91, 0x03  /* (to 0x042D state 642) */,
-                       0x08, /* fail */
-/* pos 00a0:  35 */    0xE7 /* 'g' -> */,
-/* pos 00a1:  36 */    0xF2 /* 'r' -> */,
-/* pos 00a2:  37 */    0xE1 /* 'a' -> */,
-/* pos 00a3:  38 */    0xE4 /* 'd' -> */,
-/* pos 00a4:  39 */    0xE5 /* 'e' -> */,
-/* pos 00a5:  40 */    0xBA /* ':' -> */,
-/* pos 00a6:  41 */    0x00, 0x05                  /* - terminal marker  5 - */,
-/* pos 00a8:  42 */    0xE9 /* 'i' -> */,
-/* pos 00a9:  43 */    0xE7 /* 'g' -> */,
-/* pos 00aa:  44 */    0xE9 /* 'i' -> */,
-/* pos 00ab:  45 */    0xEE /* 'n' -> */,
-/* pos 00ac:  46 */    0xBA /* ':' -> */,
-/* pos 00ad:  47 */    0x00, 0x06                  /* - terminal marker  6 - */,
-/* pos 00af:  48 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x00B6 state  49) */,
-                       0x74 /* 't' */, 0x13, 0x03  /* (to 0x03C5 state 553) */,
-                       0x08, /* fail */
-/* pos 00b6:  49 */    0x63 /* 'c' */, 0x0A, 0x00  /* (to 0x00C0 state  50) */,
-                       0x72 /* 'r' */, 0xFC, 0x02  /* (to 0x03B5 state 539) */,
-                       0x74 /* 't' */, 0xFF, 0x02  /* (to 0x03BB state 544) */,
-                       0x08, /* fail */
-/* pos 00c0:  50 */    0xAD /* '-' -> */,
-/* pos 00c1:  51 */    0xF7 /* 'w' -> */,
-/* pos 00c2:  52 */    0xE5 /* 'e' -> */,
-/* pos 00c3:  53 */    0xE2 /* 'b' -> */,
-/* pos 00c4:  54 */    0xF3 /* 's' -> */,
-/* pos 00c5:  55 */    0xEF /* 'o' -> */,
-/* pos 00c6:  56 */    0xE3 /* 'c' -> */,
-/* pos 00c7:  57 */    0xEB /* 'k' -> */,
-/* pos 00c8:  58 */    0xE5 /* 'e' -> */,
-/* pos 00c9:  59 */    0xF4 /* 't' -> */,
-/* pos 00ca:  60 */    0xAD /* '-' -> */,
-/* pos 00cb:  61 */    0x64 /* 'd' */, 0x19, 0x00  /* (to 0x00E4 state  62) */,
-                       0x65 /* 'e' */, 0x20, 0x00  /* (to 0x00EE state  70) */,
-                       0x6B /* 'k' */, 0x29, 0x00  /* (to 0x00FA state  81) */,
-                       0x70 /* 'p' */, 0x38, 0x00  /* (to 0x010C state  88) */,
-                       0x61 /* 'a' */, 0x3F, 0x00  /* (to 0x0116 state  97) */,
-                       0x6E /* 'n' */, 0x44, 0x00  /* (to 0x011E state 104) */,
-                       0x76 /* 'v' */, 0x86, 0x01  /* (to 0x0263 state 284) */,
-                       0x6F /* 'o' */, 0x8C, 0x01  /* (to 0x026C state 292) */,
-                       0x08, /* fail */
-/* pos 00e4:  62 */    0xF2 /* 'r' -> */,
-/* pos 00e5:  63 */    0xE1 /* 'a' -> */,
-/* pos 00e6:  64 */    0xE6 /* 'f' -> */,
-/* pos 00e7:  65 */    0xF4 /* 't' -> */,
-/* pos 00e8:  66 */    0xBA /* ':' -> */,
-/* pos 00e9:  67 */    0x00, 0x07                  /* - terminal marker  7 - */,
-/* pos 00eb:  68 */    0x8A /* '.' -> */,
-/* pos 00ec:  69 */    0x00, 0x08                  /* - terminal marker  8 - */,
-/* pos 00ee:  70 */    0xF8 /* 'x' -> */,
-/* pos 00ef:  71 */    0xF4 /* 't' -> */,
-/* pos 00f0:  72 */    0xE5 /* 'e' -> */,
-/* pos 00f1:  73 */    0xEE /* 'n' -> */,
-/* pos 00f2:  74 */    0xF3 /* 's' -> */,
-/* pos 00f3:  75 */    0xE9 /* 'i' -> */,
-/* pos 00f4:  76 */    0xEF /* 'o' -> */,
-/* pos 00f5:  77 */    0xEE /* 'n' -> */,
-/* pos 00f6:  78 */    0xF3 /* 's' -> */,
-/* pos 00f7:  79 */    0xBA /* ':' -> */,
-/* pos 00f8:  80 */    0x00, 0x09                  /* - terminal marker  9 - */,
-/* pos 00fa:  81 */    0xE5 /* 'e' -> */,
-/* pos 00fb:  82 */    0xF9 /* 'y' -> */,
-/* pos 00fc:  83 */    0x31 /* '1' */, 0x0A, 0x00  /* (to 0x0106 state  84) */,
-                       0x32 /* '2' */, 0x0A, 0x00  /* (to 0x0109 state  86) */,
-                       0x3A /* ':' */, 0x5F, 0x01  /* (to 0x0261 state 283) */,
-                       0x08, /* fail */
-/* pos 0106:  84 */    0xBA /* ':' -> */,
-/* pos 0107:  85 */    0x00, 0x0A                  /* - terminal marker 10 - */,
-/* pos 0109:  86 */    0xBA /* ':' -> */,
-/* pos 010a:  87 */    0x00, 0x0B                  /* - terminal marker 11 - */,
-/* pos 010c:  88 */    0xF2 /* 'r' -> */,
-/* pos 010d:  89 */    0xEF /* 'o' -> */,
-/* pos 010e:  90 */    0xF4 /* 't' -> */,
-/* pos 010f:  91 */    0xEF /* 'o' -> */,
-/* pos 0110:  92 */    0xE3 /* 'c' -> */,
-/* pos 0111:  93 */    0xEF /* 'o' -> */,
-/* pos 0112:  94 */    0xEC /* 'l' -> */,
-/* pos 0113:  95 */    0xBA /* ':' -> */,
-/* pos 0114:  96 */    0x00, 0x0C                  /* - terminal marker 12 - */,
-/* pos 0116:  97 */    0xE3 /* 'c' -> */,
-/* pos 0117:  98 */    0xE3 /* 'c' -> */,
-/* pos 0118:  99 */    0xE5 /* 'e' -> */,
-/* pos 0119: 100 */    0xF0 /* 'p' -> */,
-/* pos 011a: 101 */    0xF4 /* 't' -> */,
-/* pos 011b: 102 */    0xBA /* ':' -> */,
-/* pos 011c: 103 */    0x00, 0x0D                  /* - terminal marker 13 - */,
-/* pos 011e: 104 */    0xEF /* 'o' -> */,
-/* pos 011f: 105 */    0xEE /* 'n' -> */,
-/* pos 0120: 106 */    0xE3 /* 'c' -> */,
-/* pos 0121: 107 */    0xE5 /* 'e' -> */,
-/* pos 0122: 108 */    0xBA /* ':' -> */,
-/* pos 0123: 109 */    0x00, 0x0E                  /* - terminal marker 14 - */,
-/* pos 0125: 110 */    0xF4 /* 't' -> */,
-/* pos 0126: 111 */    0xF0 /* 'p' -> */,
-/* pos 0127: 112 */    0x2F /* '/' */, 0x07, 0x00  /* (to 0x012E state 113) */,
-                       0x32 /* '2' */, 0x10, 0x00  /* (to 0x013A state 118) */,
-                       0x08, /* fail */
-/* pos 012e: 113 */    0xB1 /* '1' -> */,
-/* pos 012f: 114 */    0xAE /* '.' -> */,
-/* pos 0130: 115 */    0x31 /* '1' */, 0x07, 0x00  /* (to 0x0137 state 116) */,
-                       0x30 /* '0' */, 0x15, 0x03  /* (to 0x0448 state 660) */,
-                       0x08, /* fail */
-/* pos 0137: 116 */    0xA0 /* ' ' -> */,
-/* pos 0138: 117 */    0x00, 0x0F                  /* - terminal marker 15 - */,
-/* pos 013a: 118 */    0xAD /* '-' -> */,
-/* pos 013b: 119 */    0xF3 /* 's' -> */,
-/* pos 013c: 120 */    0xE5 /* 'e' -> */,
-/* pos 013d: 121 */    0xF4 /* 't' -> */,
-/* pos 013e: 122 */    0xF4 /* 't' -> */,
-/* pos 013f: 123 */    0xE9 /* 'i' -> */,
-/* pos 0140: 124 */    0xEE /* 'n' -> */,
-/* pos 0141: 125 */    0xE7 /* 'g' -> */,
-/* pos 0142: 126 */    0xF3 /* 's' -> */,
-/* pos 0143: 127 */    0xBA /* ':' -> */,
-/* pos 0144: 128 */    0x00, 0x10                  /* - terminal marker 16 - */,
-/* pos 0146: 129 */    0x63 /* 'c' */, 0x0D, 0x00  /* (to 0x0153 state 130) */,
-                       0x75 /* 'u' */, 0xAC, 0x00  /* (to 0x01F5 state 230) */,
-                       0x67 /* 'g' */, 0x7D, 0x01  /* (to 0x02C9 state 358) */,
-                       0x6C /* 'l' */, 0x7E, 0x01  /* (to 0x02CD state 361) */,
-                       0x08, /* fail */
-/* pos 0153: 130 */    0xE3 /* 'c' -> */,
-/* pos 0154: 131 */    0xE5 /* 'e' -> */,
-/* pos 0155: 132 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x015C state 133) */,
-                       0x73 /* 's' */, 0x0E, 0x00  /* (to 0x0166 state 136) */,
-                       0x08, /* fail */
-/* pos 015c: 133 */    0xF4 /* 't' -> */,
-/* pos 015d: 134 */    0x3A /* ':' */, 0x07, 0x00  /* (to 0x0164 state 135) */,
-                       0x2D /* '-' */, 0x59, 0x00  /* (to 0x01B9 state 192) */,
-                       0x08, /* fail */
-/* pos 0164: 135 */    0x00, 0x11                  /* - terminal marker 17 - */,
-/* pos 0166: 136 */    0xF3 /* 's' -> */,
-/* pos 0167: 137 */    0xAD /* '-' -> */,
-/* pos 0168: 138 */    0xE3 /* 'c' -> */,
-/* pos 0169: 139 */    0xEF /* 'o' -> */,
-/* pos 016a: 140 */    0xEE /* 'n' -> */,
-/* pos 016b: 141 */    0xF4 /* 't' -> */,
-/* pos 016c: 142 */    0xF2 /* 'r' -> */,
-/* pos 016d: 143 */    0xEF /* 'o' -> */,
-/* pos 016e: 144 */    0xEC /* 'l' -> */,
-/* pos 016f: 145 */    0xAD /* '-' -> */,
-/* pos 0170: 146 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x0177 state 147) */,
-                       0x61 /* 'a' */, 0x48, 0x01  /* (to 0x02BB state 345) */,
-                       0x08, /* fail */
-/* pos 0177: 147 */    0xE5 /* 'e' -> */,
-/* pos 0178: 148 */    0xF1 /* 'q' -> */,
-/* pos 0179: 149 */    0xF5 /* 'u' -> */,
-/* pos 017a: 150 */    0xE5 /* 'e' -> */,
-/* pos 017b: 151 */    0xF3 /* 's' -> */,
-/* pos 017c: 152 */    0xF4 /* 't' -> */,
-/* pos 017d: 153 */    0xAD /* '-' -> */,
-/* pos 017e: 154 */    0xE8 /* 'h' -> */,
-/* pos 017f: 155 */    0xE5 /* 'e' -> */,
-/* pos 0180: 156 */    0xE1 /* 'a' -> */,
-/* pos 0181: 157 */    0xE4 /* 'd' -> */,
-/* pos 0182: 158 */    0xE5 /* 'e' -> */,
-/* pos 0183: 159 */    0xF2 /* 'r' -> */,
-/* pos 0184: 160 */    0xF3 /* 's' -> */,
-/* pos 0185: 161 */    0xBA /* ':' -> */,
-/* pos 0186: 162 */    0x00, 0x12                  /* - terminal marker 18 - */,
-/* pos 0188: 163 */    0xE6 /* 'f' -> */,
-/* pos 0189: 164 */    0xAD /* '-' -> */,
-/* pos 018a: 165 */    0x6D /* 'm' */, 0x0D, 0x00  /* (to 0x0197 state 166) */,
-                       0x6E /* 'n' */, 0x20, 0x00  /* (to 0x01AD state 181) */,
-                       0x72 /* 'r' */, 0x9E, 0x01  /* (to 0x032E state 435) */,
-                       0x75 /* 'u' */, 0xA2, 0x01  /* (to 0x0335 state 441) */,
-                       0x08, /* fail */
-/* pos 0197: 166 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x019E state 167) */,
-                       0x61 /* 'a' */, 0x8E, 0x01  /* (to 0x0328 state 430) */,
-                       0x08, /* fail */
-/* pos 019e: 167 */    0xE4 /* 'd' -> */,
-/* pos 019f: 168 */    0xE9 /* 'i' -> */,
-/* pos 01a0: 169 */    0xE6 /* 'f' -> */,
-/* pos 01a1: 170 */    0xE9 /* 'i' -> */,
-/* pos 01a2: 171 */    0xE5 /* 'e' -> */,
-/* pos 01a3: 172 */    0xE4 /* 'd' -> */,
-/* pos 01a4: 173 */    0xAD /* '-' -> */,
-/* pos 01a5: 174 */    0xF3 /* 's' -> */,
-/* pos 01a6: 175 */    0xE9 /* 'i' -> */,
-/* pos 01a7: 176 */    0xEE /* 'n' -> */,
-/* pos 01a8: 177 */    0xE3 /* 'c' -> */,
-/* pos 01a9: 178 */    0xE5 /* 'e' -> */,
-/* pos 01aa: 179 */    0xBA /* ':' -> */,
-/* pos 01ab: 180 */    0x00, 0x13                  /* - terminal marker 19 - */,
-/* pos 01ad: 181 */    0xEF /* 'o' -> */,
-/* pos 01ae: 182 */    0xEE /* 'n' -> */,
-/* pos 01af: 183 */    0xE5 /* 'e' -> */,
-/* pos 01b0: 184 */    0xAD /* '-' -> */,
-/* pos 01b1: 185 */    0xED /* 'm' -> */,
-/* pos 01b2: 186 */    0xE1 /* 'a' -> */,
-/* pos 01b3: 187 */    0xF4 /* 't' -> */,
-/* pos 01b4: 188 */    0xE3 /* 'c' -> */,
-/* pos 01b5: 189 */    0xE8 /* 'h' -> */,
-/* pos 01b6: 190 */    0xBA /* ':' -> */,
-/* pos 01b7: 191 */    0x00, 0x14                  /* - terminal marker 20 - */,
-/* pos 01b9: 192 */    0x65 /* 'e' */, 0x0D, 0x00  /* (to 0x01C6 state 193) */,
-                       0x6C /* 'l' */, 0x14, 0x00  /* (to 0x01D0 state 202) */,
-                       0x63 /* 'c' */, 0xEB, 0x00  /* (to 0x02AA state 330) */,
-                       0x72 /* 'r' */, 0xF1, 0x00  /* (to 0x02B3 state 338) */,
-                       0x08, /* fail */
-/* pos 01c6: 193 */    0xEE /* 'n' -> */,
-/* pos 01c7: 194 */    0xE3 /* 'c' -> */,
-/* pos 01c8: 195 */    0xEF /* 'o' -> */,
-/* pos 01c9: 196 */    0xE4 /* 'd' -> */,
-/* pos 01ca: 197 */    0xE9 /* 'i' -> */,
-/* pos 01cb: 198 */    0xEE /* 'n' -> */,
-/* pos 01cc: 199 */    0xE7 /* 'g' -> */,
-/* pos 01cd: 200 */    0xBA /* ':' -> */,
-/* pos 01ce: 201 */    0x00, 0x15                  /* - terminal marker 21 - */,
-/* pos 01d0: 202 */    0xE1 /* 'a' -> */,
-/* pos 01d1: 203 */    0xEE /* 'n' -> */,
-/* pos 01d2: 204 */    0xE7 /* 'g' -> */,
-/* pos 01d3: 205 */    0xF5 /* 'u' -> */,
-/* pos 01d4: 206 */    0xE1 /* 'a' -> */,
-/* pos 01d5: 207 */    0xE7 /* 'g' -> */,
-/* pos 01d6: 208 */    0xE5 /* 'e' -> */,
-/* pos 01d7: 209 */    0xBA /* ':' -> */,
-/* pos 01d8: 210 */    0x00, 0x16                  /* - terminal marker 22 - */,
-/* pos 01da: 211 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x01E1 state 212) */,
-                       0x6F /* 'o' */, 0x9E, 0x01  /* (to 0x037B state 497) */,
-                       0x08, /* fail */
-/* pos 01e1: 212 */    0xE7 /* 'g' -> */,
-/* pos 01e2: 213 */    0xED /* 'm' -> */,
-/* pos 01e3: 214 */    0xE1 /* 'a' -> */,
-/* pos 01e4: 215 */    0xBA /* ':' -> */,
-/* pos 01e5: 216 */    0x00, 0x17                  /* - terminal marker 23 - */,
-/* pos 01e7: 217 */    0xE3 /* 'c' -> */,
-/* pos 01e8: 218 */    0xE8 /* 'h' -> */,
-/* pos 01e9: 219 */    0xE5 /* 'e' -> */,
-/* pos 01ea: 220 */    0xAD /* '-' -> */,
-/* pos 01eb: 221 */    0xE3 /* 'c' -> */,
-/* pos 01ec: 222 */    0xEF /* 'o' -> */,
-/* pos 01ed: 223 */    0xEE /* 'n' -> */,
-/* pos 01ee: 224 */    0xF4 /* 't' -> */,
-/* pos 01ef: 225 */    0xF2 /* 'r' -> */,
-/* pos 01f0: 226 */    0xEF /* 'o' -> */,
-/* pos 01f1: 227 */    0xEC /* 'l' -> */,
-/* pos 01f2: 228 */    0xBA /* ':' -> */,
-/* pos 01f3: 229 */    0x00, 0x18                  /* - terminal marker 24 - */,
-/* pos 01f5: 230 */    0xF4 /* 't' -> */,
-/* pos 01f6: 231 */    0xE8 /* 'h' -> */,
-/* pos 01f7: 232 */    0xEF /* 'o' -> */,
-/* pos 01f8: 233 */    0xF2 /* 'r' -> */,
-/* pos 01f9: 234 */    0xE9 /* 'i' -> */,
-/* pos 01fa: 235 */    0xFA /* 'z' -> */,
-/* pos 01fb: 236 */    0xE1 /* 'a' -> */,
-/* pos 01fc: 237 */    0xF4 /* 't' -> */,
-/* pos 01fd: 238 */    0xE9 /* 'i' -> */,
-/* pos 01fe: 239 */    0xEF /* 'o' -> */,
-/* pos 01ff: 240 */    0xEE /* 'n' -> */,
-/* pos 0200: 241 */    0xBA /* ':' -> */,
-/* pos 0201: 242 */    0x00, 0x19                  /* - terminal marker 25 - */,
-/* pos 0203: 243 */    0xEB /* 'k' -> */,
-/* pos 0204: 244 */    0xE9 /* 'i' -> */,
-/* pos 0205: 245 */    0xE5 /* 'e' -> */,
-/* pos 0206: 246 */    0xBA /* ':' -> */,
-/* pos 0207: 247 */    0x00, 0x1A                  /* - terminal marker 26 - */,
-/* pos 0209: 248 */    0xE5 /* 'e' -> */,
-/* pos 020a: 249 */    0xEE /* 'n' -> */,
-/* pos 020b: 250 */    0xF4 /* 't' -> */,
-/* pos 020c: 251 */    0xAD /* '-' -> */,
-/* pos 020d: 252 */    0x6C /* 'l' */, 0x10, 0x00  /* (to 0x021D state 253) */,
-                       0x74 /* 't' */, 0x1E, 0x00  /* (to 0x022E state 260) */,
-                       0x64 /* 'd' */, 0xC0, 0x00  /* (to 0x02D3 state 366) */,
-                       0x65 /* 'e' */, 0xCA, 0x00  /* (to 0x02E0 state 378) */,
-                       0x72 /* 'r' */, 0xE3, 0x00  /* (to 0x02FC state 403) */,
-                       0x08, /* fail */
-/* pos 021d: 253 */    0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x0227 state 254) */,
-                       0x61 /* 'a' */, 0xCA, 0x00  /* (to 0x02EA state 387) */,
-                       0x6F /* 'o' */, 0xD0, 0x00  /* (to 0x02F3 state 395) */,
-                       0x08, /* fail */
-/* pos 0227: 254 */    0xEE /* 'n' -> */,
-/* pos 0228: 255 */    0xE7 /* 'g' -> */,
-/* pos 0229: 256 */    0xF4 /* 't' -> */,
-/* pos 022a: 257 */    0xE8 /* 'h' -> */,
-/* pos 022b: 258 */    0xBA /* ':' -> */,
-/* pos 022c: 259 */    0x00, 0x1B                  /* - terminal marker 27 - */,
-/* pos 022e: 260 */    0xF9 /* 'y' -> */,
-/* pos 022f: 261 */    0xF0 /* 'p' -> */,
-/* pos 0230: 262 */    0xE5 /* 'e' -> */,
-/* pos 0231: 263 */    0xBA /* ':' -> */,
-/* pos 0232: 264 */    0x00, 0x1C                  /* - terminal marker 28 - */,
-/* pos 0234: 265 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x023B state 266) */,
-                       0x65 /* 'e' */, 0xF0, 0x01  /* (to 0x0427 state 637) */,
-                       0x08, /* fail */
-/* pos 023b: 266 */    0xF4 /* 't' -> */,
-/* pos 023c: 267 */    0xE5 /* 'e' -> */,
-/* pos 023d: 268 */    0xBA /* ':' -> */,
-/* pos 023e: 269 */    0x00, 0x1D                  /* - terminal marker 29 - */,
-/* pos 0240: 270 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x0247 state 271) */,
-                       0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x024D state 276) */,
-                       0x08, /* fail */
-/* pos 0247: 271 */    0xEE /* 'n' -> */,
-/* pos 0248: 272 */    0xE7 /* 'g' -> */,
-/* pos 0249: 273 */    0xE5 /* 'e' -> */,
-/* pos 024a: 274 */    0xBA /* ':' -> */,
-/* pos 024b: 275 */    0x00, 0x1E                  /* - terminal marker 30 - */,
-/* pos 024d: 276 */    0x66 /* 'f' */, 0x07, 0x00  /* (to 0x0254 state 277) */,
-                       0x74 /* 't' */, 0x5A, 0x01  /* (to 0x03AA state 529) */,
-                       0x08, /* fail */
-/* pos 0254: 277 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x025B state 278) */,
-                       0x72 /* 'r' */, 0x4D, 0x01  /* (to 0x03A4 state 524) */,
-                       0x08, /* fail */
-/* pos 025b: 278 */    0xF2 /* 'r' -> */,
-/* pos 025c: 279 */    0xE5 /* 'e' -> */,
-/* pos 025d: 280 */    0xF2 /* 'r' -> */,
-/* pos 025e: 281 */    0xBA /* ':' -> */,
-/* pos 025f: 282 */    0x00, 0x1F                  /* - terminal marker 31 - */,
-/* pos 0261: 283 */    0x00, 0x20                  /* - terminal marker 32 - */,
-/* pos 0263: 284 */    0xE5 /* 'e' -> */,
-/* pos 0264: 285 */    0xF2 /* 'r' -> */,
-/* pos 0265: 286 */    0xF3 /* 's' -> */,
-/* pos 0266: 287 */    0xE9 /* 'i' -> */,
-/* pos 0267: 288 */    0xEF /* 'o' -> */,
-/* pos 0268: 289 */    0xEE /* 'n' -> */,
-/* pos 0269: 290 */    0xBA /* ':' -> */,
-/* pos 026a: 291 */    0x00, 0x21                  /* - terminal marker 33 - */,
-/* pos 026c: 292 */    0xF2 /* 'r' -> */,
-/* pos 026d: 293 */    0xE9 /* 'i' -> */,
-/* pos 026e: 294 */    0xE7 /* 'g' -> */,
-/* pos 026f: 295 */    0xE9 /* 'i' -> */,
-/* pos 0270: 296 */    0xEE /* 'n' -> */,
-/* pos 0271: 297 */    0xBA /* ':' -> */,
-/* pos 0272: 298 */    0x00, 0x22                  /* - terminal marker 34 - */,
-/* pos 0274: 299 */    0x61 /* 'a' */, 0x0D, 0x00  /* (to 0x0281 state 300) */,
-                       0x6D /* 'm' */, 0x14, 0x00  /* (to 0x028B state 309) */,
-                       0x70 /* 'p' */, 0x18, 0x00  /* (to 0x0292 state 315) */,
-                       0x73 /* 's' */, 0x1A, 0x00  /* (to 0x0297 state 319) */,
-                       0x08, /* fail */
-/* pos 0281: 300 */    0xF5 /* 'u' -> */,
-/* pos 0282: 301 */    0xF4 /* 't' -> */,
-/* pos 0283: 302 */    0xE8 /* 'h' -> */,
-/* pos 0284: 303 */    0xEF /* 'o' -> */,
-/* pos 0285: 304 */    0xF2 /* 'r' -> */,
-/* pos 0286: 305 */    0xE9 /* 'i' -> */,
-/* pos 0287: 306 */    0xF4 /* 't' -> */,
-/* pos 0288: 307 */    0xF9 /* 'y' -> */,
-/* pos 0289: 308 */    0x00, 0x23                  /* - terminal marker 35 - */,
-/* pos 028b: 309 */    0xE5 /* 'e' -> */,
-/* pos 028c: 310 */    0xF4 /* 't' -> */,
-/* pos 028d: 311 */    0xE8 /* 'h' -> */,
-/* pos 028e: 312 */    0xEF /* 'o' -> */,
-/* pos 028f: 313 */    0xE4 /* 'd' -> */,
-/* pos 0290: 314 */    0x00, 0x24                  /* - terminal marker 36 - */,
-/* pos 0292: 315 */    0xE1 /* 'a' -> */,
-/* pos 0293: 316 */    0xF4 /* 't' -> */,
-/* pos 0294: 317 */    0xE8 /* 'h' -> */,
-/* pos 0295: 318 */    0x00, 0x25                  /* - terminal marker 37 - */,
-/* pos 0297: 319 */    0x63 /* 'c' */, 0x07, 0x00  /* (to 0x029E state 320) */,
-                       0x74 /* 't' */, 0x0A, 0x00  /* (to 0x02A4 state 325) */,
-                       0x08, /* fail */
-/* pos 029e: 320 */    0xE8 /* 'h' -> */,
-/* pos 029f: 321 */    0xE5 /* 'e' -> */,
-/* pos 02a0: 322 */    0xED /* 'm' -> */,
-/* pos 02a1: 323 */    0xE5 /* 'e' -> */,
-/* pos 02a2: 324 */    0x00, 0x26                  /* - terminal marker 38 - */,
-/* pos 02a4: 325 */    0xE1 /* 'a' -> */,
-/* pos 02a5: 326 */    0xF4 /* 't' -> */,
-/* pos 02a6: 327 */    0xF5 /* 'u' -> */,
-/* pos 02a7: 328 */    0xF3 /* 's' -> */,
-/* pos 02a8: 329 */    0x00, 0x27                  /* - terminal marker 39 - */,
-/* pos 02aa: 330 */    0xE8 /* 'h' -> */,
-/* pos 02ab: 331 */    0xE1 /* 'a' -> */,
-/* pos 02ac: 332 */    0xF2 /* 'r' -> */,
-/* pos 02ad: 333 */    0xF3 /* 's' -> */,
-/* pos 02ae: 334 */    0xE5 /* 'e' -> */,
-/* pos 02af: 335 */    0xF4 /* 't' -> */,
-/* pos 02b0: 336 */    0xBA /* ':' -> */,
-/* pos 02b1: 337 */    0x00, 0x28                  /* - terminal marker 40 - */,
-/* pos 02b3: 338 */    0xE1 /* 'a' -> */,
-/* pos 02b4: 339 */    0xEE /* 'n' -> */,
-/* pos 02b5: 340 */    0xE7 /* 'g' -> */,
-/* pos 02b6: 341 */    0xE5 /* 'e' -> */,
-/* pos 02b7: 342 */    0xF3 /* 's' -> */,
-/* pos 02b8: 343 */    0xBA /* ':' -> */,
-/* pos 02b9: 344 */    0x00, 0x29                  /* - terminal marker 41 - */,
-/* pos 02bb: 345 */    0xEC /* 'l' -> */,
-/* pos 02bc: 346 */    0xEC /* 'l' -> */,
-/* pos 02bd: 347 */    0xEF /* 'o' -> */,
-/* pos 02be: 348 */    0xF7 /* 'w' -> */,
-/* pos 02bf: 349 */    0xAD /* '-' -> */,
-/* pos 02c0: 350 */    0xEF /* 'o' -> */,
-/* pos 02c1: 351 */    0xF2 /* 'r' -> */,
-/* pos 02c2: 352 */    0xE9 /* 'i' -> */,
-/* pos 02c3: 353 */    0xE7 /* 'g' -> */,
-/* pos 02c4: 354 */    0xE9 /* 'i' -> */,
-/* pos 02c5: 355 */    0xEE /* 'n' -> */,
-/* pos 02c6: 356 */    0xBA /* ':' -> */,
-/* pos 02c7: 357 */    0x00, 0x2A                  /* - terminal marker 42 - */,
-/* pos 02c9: 358 */    0xE5 /* 'e' -> */,
-/* pos 02ca: 359 */    0xBA /* ':' -> */,
-/* pos 02cb: 360 */    0x00, 0x2B                  /* - terminal marker 43 - */,
-/* pos 02cd: 361 */    0xEC /* 'l' -> */,
-/* pos 02ce: 362 */    0xEF /* 'o' -> */,
-/* pos 02cf: 363 */    0xF7 /* 'w' -> */,
-/* pos 02d0: 364 */    0xBA /* ':' -> */,
-/* pos 02d1: 365 */    0x00, 0x2C                  /* - terminal marker 44 - */,
-/* pos 02d3: 366 */    0xE9 /* 'i' -> */,
-/* pos 02d4: 367 */    0xF3 /* 's' -> */,
-/* pos 02d5: 368 */    0xF0 /* 'p' -> */,
-/* pos 02d6: 369 */    0xEF /* 'o' -> */,
-/* pos 02d7: 370 */    0xF3 /* 's' -> */,
-/* pos 02d8: 371 */    0xE9 /* 'i' -> */,
-/* pos 02d9: 372 */    0xF4 /* 't' -> */,
-/* pos 02da: 373 */    0xE9 /* 'i' -> */,
-/* pos 02db: 374 */    0xEF /* 'o' -> */,
-/* pos 02dc: 375 */    0xEE /* 'n' -> */,
-/* pos 02dd: 376 */    0xBA /* ':' -> */,
-/* pos 02de: 377 */    0x00, 0x2D                  /* - terminal marker 45 - */,
-/* pos 02e0: 378 */    0xEE /* 'n' -> */,
-/* pos 02e1: 379 */    0xE3 /* 'c' -> */,
-/* pos 02e2: 380 */    0xEF /* 'o' -> */,
-/* pos 02e3: 381 */    0xE4 /* 'd' -> */,
-/* pos 02e4: 382 */    0xE9 /* 'i' -> */,
-/* pos 02e5: 383 */    0xEE /* 'n' -> */,
-/* pos 02e6: 384 */    0xE7 /* 'g' -> */,
-/* pos 02e7: 385 */    0xBA /* ':' -> */,
-/* pos 02e8: 386 */    0x00, 0x2E                  /* - terminal marker 46 - */,
-/* pos 02ea: 387 */    0xEE /* 'n' -> */,
-/* pos 02eb: 388 */    0xE7 /* 'g' -> */,
-/* pos 02ec: 389 */    0xF5 /* 'u' -> */,
-/* pos 02ed: 390 */    0xE1 /* 'a' -> */,
-/* pos 02ee: 391 */    0xE7 /* 'g' -> */,
-/* pos 02ef: 392 */    0xE5 /* 'e' -> */,
-/* pos 02f0: 393 */    0xBA /* ':' -> */,
-/* pos 02f1: 394 */    0x00, 0x2F                  /* - terminal marker 47 - */,
-/* pos 02f3: 395 */    0xE3 /* 'c' -> */,
-/* pos 02f4: 396 */    0xE1 /* 'a' -> */,
-/* pos 02f5: 397 */    0xF4 /* 't' -> */,
-/* pos 02f6: 398 */    0xE9 /* 'i' -> */,
-/* pos 02f7: 399 */    0xEF /* 'o' -> */,
-/* pos 02f8: 400 */    0xEE /* 'n' -> */,
-/* pos 02f9: 401 */    0xBA /* ':' -> */,
-/* pos 02fa: 402 */    0x00, 0x30                  /* - terminal marker 48 - */,
-/* pos 02fc: 403 */    0xE1 /* 'a' -> */,
-/* pos 02fd: 404 */    0xEE /* 'n' -> */,
-/* pos 02fe: 405 */    0xE7 /* 'g' -> */,
-/* pos 02ff: 406 */    0xE5 /* 'e' -> */,
-/* pos 0300: 407 */    0xBA /* ':' -> */,
-/* pos 0301: 408 */    0x00, 0x31                  /* - terminal marker 49 - */,
-/* pos 0303: 409 */    0x74 /* 't' */, 0x07, 0x00  /* (to 0x030A state 410) */,
-                       0x78 /* 'x' */, 0x09, 0x00  /* (to 0x030F state 414) */,
-                       0x08, /* fail */
-/* pos 030a: 410 */    0xE1 /* 'a' -> */,
-/* pos 030b: 411 */    0xE7 /* 'g' -> */,
-/* pos 030c: 412 */    0xBA /* ':' -> */,
-/* pos 030d: 413 */    0x00, 0x32                  /* - terminal marker 50 - */,
-/* pos 030f: 414 */    0xF0 /* 'p' -> */,
-/* pos 0310: 415 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x0317 state 416) */,
-                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x031C state 420) */,
-                       0x08, /* fail */
-/* pos 0317: 416 */    0xE3 /* 'c' -> */,
-/* pos 0318: 417 */    0xF4 /* 't' -> */,
-/* pos 0319: 418 */    0xBA /* ':' -> */,
-/* pos 031a: 419 */    0x00, 0x33                  /* - terminal marker 51 - */,
-/* pos 031c: 420 */    0xF2 /* 'r' -> */,
-/* pos 031d: 421 */    0xE5 /* 'e' -> */,
-/* pos 031e: 422 */    0xF3 /* 's' -> */,
-/* pos 031f: 423 */    0xBA /* ':' -> */,
-/* pos 0320: 424 */    0x00, 0x34                  /* - terminal marker 52 - */,
-/* pos 0322: 425 */    0xF2 /* 'r' -> */,
-/* pos 0323: 426 */    0xEF /* 'o' -> */,
-/* pos 0324: 427 */    0xED /* 'm' -> */,
-/* pos 0325: 428 */    0xBA /* ':' -> */,
-/* pos 0326: 429 */    0x00, 0x35                  /* - terminal marker 53 - */,
-/* pos 0328: 430 */    0xF4 /* 't' -> */,
-/* pos 0329: 431 */    0xE3 /* 'c' -> */,
-/* pos 032a: 432 */    0xE8 /* 'h' -> */,
-/* pos 032b: 433 */    0xBA /* ':' -> */,
-/* pos 032c: 434 */    0x00, 0x36                  /* - terminal marker 54 - */,
-/* pos 032e: 435 */    0xE1 /* 'a' -> */,
-/* pos 032f: 436 */    0xEE /* 'n' -> */,
-/* pos 0330: 437 */    0xE7 /* 'g' -> */,
-/* pos 0331: 438 */    0xE5 /* 'e' -> */,
-/* pos 0332: 439 */    0xBA /* ':' -> */,
-/* pos 0333: 440 */    0x00, 0x37                  /* - terminal marker 55 - */,
-/* pos 0335: 441 */    0xEE /* 'n' -> */,
-/* pos 0336: 442 */    0xED /* 'm' -> */,
-/* pos 0337: 443 */    0xEF /* 'o' -> */,
-/* pos 0338: 444 */    0xE4 /* 'd' -> */,
-/* pos 0339: 445 */    0xE9 /* 'i' -> */,
-/* pos 033a: 446 */    0xE6 /* 'f' -> */,
-/* pos 033b: 447 */    0xE9 /* 'i' -> */,
-/* pos 033c: 448 */    0xE5 /* 'e' -> */,
-/* pos 033d: 449 */    0xE4 /* 'd' -> */,
-/* pos 033e: 450 */    0xAD /* '-' -> */,
-/* pos 033f: 451 */    0xF3 /* 's' -> */,
-/* pos 0340: 452 */    0xE9 /* 'i' -> */,
-/* pos 0341: 453 */    0xEE /* 'n' -> */,
-/* pos 0342: 454 */    0xE3 /* 'c' -> */,
-/* pos 0343: 455 */    0xE5 /* 'e' -> */,
-/* pos 0344: 456 */    0xBA /* ':' -> */,
-/* pos 0345: 457 */    0x00, 0x38                  /* - terminal marker 56 - */,
-/* pos 0347: 458 */    0x61 /* 'a' */, 0x0A, 0x00  /* (to 0x0351 state 459) */,
-                       0x69 /* 'i' */, 0x15, 0x00  /* (to 0x035F state 472) */,
-                       0x6F /* 'o' */, 0x17, 0x00  /* (to 0x0364 state 476) */,
-                       0x08, /* fail */
-/* pos 0351: 459 */    0xF3 /* 's' -> */,
-/* pos 0352: 460 */    0xF4 /* 't' -> */,
-/* pos 0353: 461 */    0xAD /* '-' -> */,
-/* pos 0354: 462 */    0xED /* 'm' -> */,
-/* pos 0355: 463 */    0xEF /* 'o' -> */,
-/* pos 0356: 464 */    0xE4 /* 'd' -> */,
-/* pos 0357: 465 */    0xE9 /* 'i' -> */,
-/* pos 0358: 466 */    0xE6 /* 'f' -> */,
-/* pos 0359: 467 */    0xE9 /* 'i' -> */,
-/* pos 035a: 468 */    0xE5 /* 'e' -> */,
-/* pos 035b: 469 */    0xE4 /* 'd' -> */,
-/* pos 035c: 470 */    0xBA /* ':' -> */,
-/* pos 035d: 471 */    0x00, 0x39                  /* - terminal marker 57 - */,
-/* pos 035f: 472 */    0xEE /* 'n' -> */,
-/* pos 0360: 473 */    0xEB /* 'k' -> */,
-/* pos 0361: 474 */    0xBA /* ':' -> */,
-/* pos 0362: 475 */    0x00, 0x3A                  /* - terminal marker 58 - */,
-/* pos 0364: 476 */    0xE3 /* 'c' -> */,
-/* pos 0365: 477 */    0xE1 /* 'a' -> */,
-/* pos 0366: 478 */    0xF4 /* 't' -> */,
-/* pos 0367: 479 */    0xE9 /* 'i' -> */,
-/* pos 0368: 480 */    0xEF /* 'o' -> */,
-/* pos 0369: 481 */    0xEE /* 'n' -> */,
-/* pos 036a: 482 */    0xBA /* ':' -> */,
-/* pos 036b: 483 */    0x00, 0x3B                  /* - terminal marker 59 - */,
-/* pos 036d: 484 */    0xE1 /* 'a' -> */,
-/* pos 036e: 485 */    0xF8 /* 'x' -> */,
-/* pos 036f: 486 */    0xAD /* '-' -> */,
-/* pos 0370: 487 */    0xE6 /* 'f' -> */,
-/* pos 0371: 488 */    0xEF /* 'o' -> */,
-/* pos 0372: 489 */    0xF2 /* 'r' -> */,
-/* pos 0373: 490 */    0xF7 /* 'w' -> */,
-/* pos 0374: 491 */    0xE1 /* 'a' -> */,
-/* pos 0375: 492 */    0xF2 /* 'r' -> */,
-/* pos 0376: 493 */    0xE4 /* 'd' -> */,
-/* pos 0377: 494 */    0xF3 /* 's' -> */,
-/* pos 0378: 495 */    0xBA /* ':' -> */,
-/* pos 0379: 496 */    0x00, 0x3C                  /* - terminal marker 60 - */,
-/* pos 037b: 497 */    0xF8 /* 'x' -> */,
-/* pos 037c: 498 */    0xF9 /* 'y' -> */,
-/* pos 037d: 499 */    0x2D /* '-' */, 0x07, 0x00  /* (to 0x0384 state 500) */,
-                       0x20 /* ' ' */, 0xB5, 0x00  /* (to 0x0435 state 649) */,
-                       0x08, /* fail */
-/* pos 0384: 500 */    0xE1 /* 'a' -> */,
-/* pos 0385: 501 */    0xF5 /* 'u' -> */,
-/* pos 0386: 502 */    0xF4 /* 't' -> */,
-/* pos 0387: 503 */    0xE8 /* 'h' -> */,
-/* pos 0388: 504 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x038F state 505) */,
-                       0x6F /* 'o' */, 0x0E, 0x00  /* (to 0x0399 state 514) */,
-                       0x08, /* fail */
-/* pos 038f: 505 */    0xEE /* 'n' -> */,
-/* pos 0390: 506 */    0xF4 /* 't' -> */,
-/* pos 0391: 507 */    0xE9 /* 'i' -> */,
-/* pos 0392: 508 */    0xE3 /* 'c' -> */,
-/* pos 0393: 509 */    0xE1 /* 'a' -> */,
-/* pos 0394: 510 */    0xF4 /* 't' -> */,
-/* pos 0395: 511 */    0xE5 /* 'e' -> */,
-/* pos 0396: 512 */    0xBA /* ':' -> */,
-/* pos 0397: 513 */    0x00, 0x3D                  /* - terminal marker 61 - */,
-/* pos 0399: 514 */    0xF2 /* 'r' -> */,
-/* pos 039a: 515 */    0xE9 /* 'i' -> */,
-/* pos 039b: 516 */    0xFA /* 'z' -> */,
-/* pos 039c: 517 */    0xE1 /* 'a' -> */,
-/* pos 039d: 518 */    0xF4 /* 't' -> */,
-/* pos 039e: 519 */    0xE9 /* 'i' -> */,
-/* pos 039f: 520 */    0xEF /* 'o' -> */,
-/* pos 03a0: 521 */    0xEE /* 'n' -> */,
-/* pos 03a1: 522 */    0xBA /* ':' -> */,
-/* pos 03a2: 523 */    0x00, 0x3E                  /* - terminal marker 62 - */,
-/* pos 03a4: 524 */    0xE5 /* 'e' -> */,
-/* pos 03a5: 525 */    0xF3 /* 's' -> */,
-/* pos 03a6: 526 */    0xE8 /* 'h' -> */,
-/* pos 03a7: 527 */    0xBA /* ':' -> */,
-/* pos 03a8: 528 */    0x00, 0x3F                  /* - terminal marker 63 - */,
-/* pos 03aa: 529 */    0xF2 /* 'r' -> */,
-/* pos 03ab: 530 */    0xF9 /* 'y' -> */,
-/* pos 03ac: 531 */    0xAD /* '-' -> */,
-/* pos 03ad: 532 */    0xE1 /* 'a' -> */,
-/* pos 03ae: 533 */    0xE6 /* 'f' -> */,
-/* pos 03af: 534 */    0xF4 /* 't' -> */,
-/* pos 03b0: 535 */    0xE5 /* 'e' -> */,
-/* pos 03b1: 536 */    0xF2 /* 'r' -> */,
-/* pos 03b2: 537 */    0xBA /* ':' -> */,
-/* pos 03b3: 538 */    0x00, 0x40                  /* - terminal marker 64 - */,
-/* pos 03b5: 539 */    0xF6 /* 'v' -> */,
-/* pos 03b6: 540 */    0xE5 /* 'e' -> */,
-/* pos 03b7: 541 */    0xF2 /* 'r' -> */,
-/* pos 03b8: 542 */    0xBA /* ':' -> */,
-/* pos 03b9: 543 */    0x00, 0x41                  /* - terminal marker 65 - */,
-/* pos 03bb: 544 */    0xAD /* '-' -> */,
-/* pos 03bc: 545 */    0xE3 /* 'c' -> */,
-/* pos 03bd: 546 */    0xEF /* 'o' -> */,
-/* pos 03be: 547 */    0xEF /* 'o' -> */,
-/* pos 03bf: 548 */    0xEB /* 'k' -> */,
-/* pos 03c0: 549 */    0xE9 /* 'i' -> */,
-/* pos 03c1: 550 */    0xE5 /* 'e' -> */,
-/* pos 03c2: 551 */    0xBA /* ':' -> */,
-/* pos 03c3: 552 */    0x00, 0x42                  /* - terminal marker 66 - */,
-/* pos 03c5: 553 */    0xF2 /* 'r' -> */,
-/* pos 03c6: 554 */    0xE9 /* 'i' -> */,
-/* pos 03c7: 555 */    0xE3 /* 'c' -> */,
-/* pos 03c8: 556 */    0xF4 /* 't' -> */,
-/* pos 03c9: 557 */    0xAD /* '-' -> */,
-/* pos 03ca: 558 */    0xF4 /* 't' -> */,
-/* pos 03cb: 559 */    0xF2 /* 'r' -> */,
-/* pos 03cc: 560 */    0xE1 /* 'a' -> */,
-/* pos 03cd: 561 */    0xEE /* 'n' -> */,
-/* pos 03ce: 562 */    0xF3 /* 's' -> */,
-/* pos 03cf: 563 */    0xF0 /* 'p' -> */,
-/* pos 03d0: 564 */    0xEF /* 'o' -> */,
-/* pos 03d1: 565 */    0xF2 /* 'r' -> */,
-/* pos 03d2: 566 */    0xF4 /* 't' -> */,
-/* pos 03d3: 567 */    0xAD /* '-' -> */,
-/* pos 03d4: 568 */    0xF3 /* 's' -> */,
-/* pos 03d5: 569 */    0xE5 /* 'e' -> */,
-/* pos 03d6: 570 */    0xE3 /* 'c' -> */,
-/* pos 03d7: 571 */    0xF5 /* 'u' -> */,
-/* pos 03d8: 572 */    0xF2 /* 'r' -> */,
-/* pos 03d9: 573 */    0xE9 /* 'i' -> */,
-/* pos 03da: 574 */    0xF4 /* 't' -> */,
-/* pos 03db: 575 */    0xF9 /* 'y' -> */,
-/* pos 03dc: 576 */    0xBA /* ':' -> */,
-/* pos 03dd: 577 */    0x00, 0x43                  /* - terminal marker 67 - */,
-/* pos 03df: 578 */    0xF2 /* 'r' -> */,
-/* pos 03e0: 579 */    0xE1 /* 'a' -> */,
-/* pos 03e1: 580 */    0xEE /* 'n' -> */,
-/* pos 03e2: 581 */    0xF3 /* 's' -> */,
-/* pos 03e3: 582 */    0xE6 /* 'f' -> */,
-/* pos 03e4: 583 */    0xE5 /* 'e' -> */,
-/* pos 03e5: 584 */    0xF2 /* 'r' -> */,
-/* pos 03e6: 585 */    0xAD /* '-' -> */,
-/* pos 03e7: 586 */    0xE5 /* 'e' -> */,
-/* pos 03e8: 587 */    0xEE /* 'n' -> */,
-/* pos 03e9: 588 */    0xE3 /* 'c' -> */,
-/* pos 03ea: 589 */    0xEF /* 'o' -> */,
-/* pos 03eb: 590 */    0xE4 /* 'd' -> */,
-/* pos 03ec: 591 */    0xE9 /* 'i' -> */,
-/* pos 03ed: 592 */    0xEE /* 'n' -> */,
-/* pos 03ee: 593 */    0xE7 /* 'g' -> */,
-/* pos 03ef: 594 */    0xBA /* ':' -> */,
-/* pos 03f0: 595 */    0x00, 0x44                  /* - terminal marker 68 - */,
-/* pos 03f2: 596 */    0xE5 /* 'e' -> */,
-/* pos 03f3: 597 */    0xF2 /* 'r' -> */,
-/* pos 03f4: 598 */    0xAD /* '-' -> */,
-/* pos 03f5: 599 */    0xE1 /* 'a' -> */,
-/* pos 03f6: 600 */    0xE7 /* 'g' -> */,
-/* pos 03f7: 601 */    0xE5 /* 'e' -> */,
-/* pos 03f8: 602 */    0xEE /* 'n' -> */,
-/* pos 03f9: 603 */    0xF4 /* 't' -> */,
-/* pos 03fa: 604 */    0xBA /* ':' -> */,
-/* pos 03fb: 605 */    0x00, 0x45                  /* - terminal marker 69 - */,
-/* pos 03fd: 606 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x0404 state 607) */,
-                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x0409 state 611) */,
-                       0x08, /* fail */
-/* pos 0404: 607 */    0xF2 /* 'r' -> */,
-/* pos 0405: 608 */    0xF9 /* 'y' -> */,
-/* pos 0406: 609 */    0xBA /* ':' -> */,
-/* pos 0407: 610 */    0x00, 0x46                  /* - terminal marker 70 - */,
-/* pos 0409: 611 */    0xE1 /* 'a' -> */,
-/* pos 040a: 612 */    0xBA /* ':' -> */,
-/* pos 040b: 613 */    0x00, 0x47                  /* - terminal marker 71 - */,
-/* pos 040d: 614 */    0xF7 /* 'w' -> */,
-/* pos 040e: 615 */    0xF7 /* 'w' -> */,
-/* pos 040f: 616 */    0xAD /* '-' -> */,
-/* pos 0410: 617 */    0xE1 /* 'a' -> */,
-/* pos 0411: 618 */    0xF5 /* 'u' -> */,
-/* pos 0412: 619 */    0xF4 /* 't' -> */,
-/* pos 0413: 620 */    0xE8 /* 'h' -> */,
-/* pos 0414: 621 */    0xE5 /* 'e' -> */,
-/* pos 0415: 622 */    0xEE /* 'n' -> */,
-/* pos 0416: 623 */    0xF4 /* 't' -> */,
-/* pos 0417: 624 */    0xE9 /* 'i' -> */,
-/* pos 0418: 625 */    0xE3 /* 'c' -> */,
-/* pos 0419: 626 */    0xE1 /* 'a' -> */,
-/* pos 041a: 627 */    0xF4 /* 't' -> */,
-/* pos 041b: 628 */    0xE5 /* 'e' -> */,
-/* pos 041c: 629 */    0xBA /* ':' -> */,
-/* pos 041d: 630 */    0x00, 0x48                  /* - terminal marker 72 - */,
-/* pos 041f: 631 */    0xF4 /* 't' -> */,
-/* pos 0420: 632 */    0xE3 /* 'c' -> */,
-/* pos 0421: 633 */    0xE8 /* 'h' -> */,
-/* pos 0422: 634 */    0x00, 0x49                  /* - terminal marker 73 - */,
-/* pos 0424: 635 */    0xF4 /* 't' -> */,
-/* pos 0425: 636 */    0x00, 0x4A                  /* - terminal marker 74 - */,
-/* pos 0427: 637 */    0xEC /* 'l' -> */,
-/* pos 0428: 638 */    0xE5 /* 'e' -> */,
-/* pos 0429: 639 */    0xF4 /* 't' -> */,
-/* pos 042a: 640 */    0xE5 /* 'e' -> */,
-/* pos 042b: 641 */    0x00, 0x4B                  /* - terminal marker 75 - */,
-/* pos 042d: 642 */    0xE9 /* 'i' -> */,
-/* pos 042e: 643 */    0xAD /* '-' -> */,
-/* pos 042f: 644 */    0xE1 /* 'a' -> */,
-/* pos 0430: 645 */    0xF2 /* 'r' -> */,
-/* pos 0431: 646 */    0xE7 /* 'g' -> */,
-/* pos 0432: 647 */    0xF3 /* 's' -> */,
-/* pos 0433: 648 */    0x00, 0x4C                  /* - terminal marker 76 - */,
-/* pos 0435: 649 */    0x00, 0x4D                  /* - terminal marker 77 - */,
-/* pos 0437: 650 */    0xAD /* '-' -> */,
-/* pos 0438: 651 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x043F state 652) */,
-                       0x66 /* 'f' */, 0x10, 0x00  /* (to 0x044B state 662) */,
-                       0x08, /* fail */
-/* pos 043f: 652 */    0xE5 /* 'e' -> */,
-/* pos 0440: 653 */    0xE1 /* 'a' -> */,
-/* pos 0441: 654 */    0xEC /* 'l' -> */,
-/* pos 0442: 655 */    0xAD /* '-' -> */,
-/* pos 0443: 656 */    0xE9 /* 'i' -> */,
-/* pos 0444: 657 */    0xF0 /* 'p' -> */,
-/* pos 0445: 658 */    0xBA /* ':' -> */,
-/* pos 0446: 659 */    0x00, 0x4E                  /* - terminal marker 78 - */,
-/* pos 0448: 660 */    0xA0 /* ' ' -> */,
-/* pos 0449: 661 */    0x00, 0x4F                  /* - terminal marker 79 - */,
-/* pos 044b: 662 */    0xEF /* 'o' -> */,
-/* pos 044c: 663 */    0xF2 /* 'r' -> */,
-/* pos 044d: 664 */    0xF7 /* 'w' -> */,
-/* pos 044e: 665 */    0xE1 /* 'a' -> */,
-/* pos 044f: 666 */    0xF2 /* 'r' -> */,
-/* pos 0450: 667 */    0xE4 /* 'd' -> */,
-/* pos 0451: 668 */    0xE5 /* 'e' -> */,
-/* pos 0452: 669 */    0xE4 /* 'd' -> */,
-/* pos 0453: 670 */    0xAD /* '-' -> */,
-/* pos 0454: 671 */    0xE6 /* 'f' -> */,
-/* pos 0455: 672 */    0xEF /* 'o' -> */,
-/* pos 0456: 673 */    0xF2 /* 'r' -> */,
-/* pos 0457: 674 */    0x00, 0x50                  /* - terminal marker 80 - */,
-/* pos 0459: 675 */    0x00, 0x51                  /* - terminal marker 81 - */,
-/* total size 1115 bytes */
+/* pos 0066:  18 */    0x6F /* 'o' */, 0x0A, 0x00  /* (to 0x0070 state  19) */,
+                       0x74 /* 't' */, 0xBF, 0x00  /* (to 0x0128 state 110) */,
+                       0x65 /* 'e' */, 0xF8, 0x03  /* (to 0x0464 state 676) */,
+                       0x08, /* fail */
+/* pos 0070:  19 */    0xF3 /* 's' -> */,
+/* pos 0071:  20 */    0xF4 /* 't' -> */,
+/* pos 0072:  21 */    0xBA /* ':' -> */,
+/* pos 0073:  22 */    0x00, 0x03                  /* - terminal marker  3 - */,
+/* pos 0075:  23 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x007C state  24) */,
+                       0x61 /* 'a' */, 0x72, 0x01  /* (to 0x01EA state 217) */,
+                       0x08, /* fail */
+/* pos 007c:  24 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x0083 state  25) */,
+                       0x6F /* 'o' */, 0x87, 0x01  /* (to 0x0206 state 243) */,
+                       0x08, /* fail */
+/* pos 0083:  25 */    0x6E /* 'n' */, 0x07, 0x00  /* (to 0x008A state  26) */,
+                       0x74 /* 't' */, 0x86, 0x01  /* (to 0x020C state 248) */,
+                       0x08, /* fail */
+/* pos 008a:  26 */    0xE5 /* 'e' -> */,
+/* pos 008b:  27 */    0xE3 /* 'c' -> */,
+/* pos 008c:  28 */    0xF4 /* 't' -> */,
+/* pos 008d:  29 */    0x69 /* 'i' */, 0x07, 0x00  /* (to 0x0094 state  30) */,
+                       0x20 /* ' ' */, 0xD2, 0x03  /* (to 0x0462 state 675) */,
+                       0x08, /* fail */
+/* pos 0094:  30 */    0xEF /* 'o' -> */,
+/* pos 0095:  31 */    0xEE /* 'n' -> */,
+/* pos 0096:  32 */    0xBA /* ':' -> */,
+/* pos 0097:  33 */    0x00, 0x04                  /* - terminal marker  4 - */,
+/* pos 0099:  34 */    0x70 /* 'p' */, 0x0A, 0x00  /* (to 0x00A3 state  35) */,
+                       0x73 /* 's' */, 0x5F, 0x03  /* (to 0x03FB state 596) */,
+                       0x72 /* 'r' */, 0x97, 0x03  /* (to 0x0436 state 642) */,
+                       0x08, /* fail */
+/* pos 00a3:  35 */    0xE7 /* 'g' -> */,
+/* pos 00a4:  36 */    0xF2 /* 'r' -> */,
+/* pos 00a5:  37 */    0xE1 /* 'a' -> */,
+/* pos 00a6:  38 */    0xE4 /* 'd' -> */,
+/* pos 00a7:  39 */    0xE5 /* 'e' -> */,
+/* pos 00a8:  40 */    0xBA /* ':' -> */,
+/* pos 00a9:  41 */    0x00, 0x05                  /* - terminal marker  5 - */,
+/* pos 00ab:  42 */    0xE9 /* 'i' -> */,
+/* pos 00ac:  43 */    0xE7 /* 'g' -> */,
+/* pos 00ad:  44 */    0xE9 /* 'i' -> */,
+/* pos 00ae:  45 */    0xEE /* 'n' -> */,
+/* pos 00af:  46 */    0xBA /* ':' -> */,
+/* pos 00b0:  47 */    0x00, 0x06                  /* - terminal marker  6 - */,
+/* pos 00b2:  48 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x00B9 state  49) */,
+                       0x74 /* 't' */, 0x13, 0x03  /* (to 0x03C8 state 553) */,
+                       0x08, /* fail */
+/* pos 00b9:  49 */    0x63 /* 'c' */, 0x0A, 0x00  /* (to 0x00C3 state  50) */,
+                       0x72 /* 'r' */, 0xFC, 0x02  /* (to 0x03B8 state 539) */,
+                       0x74 /* 't' */, 0xFF, 0x02  /* (to 0x03BE state 544) */,
+                       0x08, /* fail */
+/* pos 00c3:  50 */    0xAD /* '-' -> */,
+/* pos 00c4:  51 */    0xF7 /* 'w' -> */,
+/* pos 00c5:  52 */    0xE5 /* 'e' -> */,
+/* pos 00c6:  53 */    0xE2 /* 'b' -> */,
+/* pos 00c7:  54 */    0xF3 /* 's' -> */,
+/* pos 00c8:  55 */    0xEF /* 'o' -> */,
+/* pos 00c9:  56 */    0xE3 /* 'c' -> */,
+/* pos 00ca:  57 */    0xEB /* 'k' -> */,
+/* pos 00cb:  58 */    0xE5 /* 'e' -> */,
+/* pos 00cc:  59 */    0xF4 /* 't' -> */,
+/* pos 00cd:  60 */    0xAD /* '-' -> */,
+/* pos 00ce:  61 */    0x64 /* 'd' */, 0x19, 0x00  /* (to 0x00E7 state  62) */,
+                       0x65 /* 'e' */, 0x20, 0x00  /* (to 0x00F1 state  70) */,
+                       0x6B /* 'k' */, 0x29, 0x00  /* (to 0x00FD state  81) */,
+                       0x70 /* 'p' */, 0x38, 0x00  /* (to 0x010F state  88) */,
+                       0x61 /* 'a' */, 0x3F, 0x00  /* (to 0x0119 state  97) */,
+                       0x6E /* 'n' */, 0x44, 0x00  /* (to 0x0121 state 104) */,
+                       0x76 /* 'v' */, 0x86, 0x01  /* (to 0x0266 state 284) */,
+                       0x6F /* 'o' */, 0x8C, 0x01  /* (to 0x026F state 292) */,
+                       0x08, /* fail */
+/* pos 00e7:  62 */    0xF2 /* 'r' -> */,
+/* pos 00e8:  63 */    0xE1 /* 'a' -> */,
+/* pos 00e9:  64 */    0xE6 /* 'f' -> */,
+/* pos 00ea:  65 */    0xF4 /* 't' -> */,
+/* pos 00eb:  66 */    0xBA /* ':' -> */,
+/* pos 00ec:  67 */    0x00, 0x07                  /* - terminal marker  7 - */,
+/* pos 00ee:  68 */    0x8A /* '.' -> */,
+/* pos 00ef:  69 */    0x00, 0x08                  /* - terminal marker  8 - */,
+/* pos 00f1:  70 */    0xF8 /* 'x' -> */,
+/* pos 00f2:  71 */    0xF4 /* 't' -> */,
+/* pos 00f3:  72 */    0xE5 /* 'e' -> */,
+/* pos 00f4:  73 */    0xEE /* 'n' -> */,
+/* pos 00f5:  74 */    0xF3 /* 's' -> */,
+/* pos 00f6:  75 */    0xE9 /* 'i' -> */,
+/* pos 00f7:  76 */    0xEF /* 'o' -> */,
+/* pos 00f8:  77 */    0xEE /* 'n' -> */,
+/* pos 00f9:  78 */    0xF3 /* 's' -> */,
+/* pos 00fa:  79 */    0xBA /* ':' -> */,
+/* pos 00fb:  80 */    0x00, 0x09                  /* - terminal marker  9 - */,
+/* pos 00fd:  81 */    0xE5 /* 'e' -> */,
+/* pos 00fe:  82 */    0xF9 /* 'y' -> */,
+/* pos 00ff:  83 */    0x31 /* '1' */, 0x0A, 0x00  /* (to 0x0109 state  84) */,
+                       0x32 /* '2' */, 0x0A, 0x00  /* (to 0x010C state  86) */,
+                       0x3A /* ':' */, 0x5F, 0x01  /* (to 0x0264 state 283) */,
+                       0x08, /* fail */
+/* pos 0109:  84 */    0xBA /* ':' -> */,
+/* pos 010a:  85 */    0x00, 0x0A                  /* - terminal marker 10 - */,
+/* pos 010c:  86 */    0xBA /* ':' -> */,
+/* pos 010d:  87 */    0x00, 0x0B                  /* - terminal marker 11 - */,
+/* pos 010f:  88 */    0xF2 /* 'r' -> */,
+/* pos 0110:  89 */    0xEF /* 'o' -> */,
+/* pos 0111:  90 */    0xF4 /* 't' -> */,
+/* pos 0112:  91 */    0xEF /* 'o' -> */,
+/* pos 0113:  92 */    0xE3 /* 'c' -> */,
+/* pos 0114:  93 */    0xEF /* 'o' -> */,
+/* pos 0115:  94 */    0xEC /* 'l' -> */,
+/* pos 0116:  95 */    0xBA /* ':' -> */,
+/* pos 0117:  96 */    0x00, 0x0C                  /* - terminal marker 12 - */,
+/* pos 0119:  97 */    0xE3 /* 'c' -> */,
+/* pos 011a:  98 */    0xE3 /* 'c' -> */,
+/* pos 011b:  99 */    0xE5 /* 'e' -> */,
+/* pos 011c: 100 */    0xF0 /* 'p' -> */,
+/* pos 011d: 101 */    0xF4 /* 't' -> */,
+/* pos 011e: 102 */    0xBA /* ':' -> */,
+/* pos 011f: 103 */    0x00, 0x0D                  /* - terminal marker 13 - */,
+/* pos 0121: 104 */    0xEF /* 'o' -> */,
+/* pos 0122: 105 */    0xEE /* 'n' -> */,
+/* pos 0123: 106 */    0xE3 /* 'c' -> */,
+/* pos 0124: 107 */    0xE5 /* 'e' -> */,
+/* pos 0125: 108 */    0xBA /* ':' -> */,
+/* pos 0126: 109 */    0x00, 0x0E                  /* - terminal marker 14 - */,
+/* pos 0128: 110 */    0xF4 /* 't' -> */,
+/* pos 0129: 111 */    0xF0 /* 'p' -> */,
+/* pos 012a: 112 */    0x2F /* '/' */, 0x07, 0x00  /* (to 0x0131 state 113) */,
+                       0x32 /* '2' */, 0x10, 0x00  /* (to 0x013D state 118) */,
+                       0x08, /* fail */
+/* pos 0131: 113 */    0xB1 /* '1' -> */,
+/* pos 0132: 114 */    0xAE /* '.' -> */,
+/* pos 0133: 115 */    0x31 /* '1' */, 0x07, 0x00  /* (to 0x013A state 116) */,
+                       0x30 /* '0' */, 0x1B, 0x03  /* (to 0x0451 state 660) */,
+                       0x08, /* fail */
+/* pos 013a: 116 */    0xA0 /* ' ' -> */,
+/* pos 013b: 117 */    0x00, 0x0F                  /* - terminal marker 15 - */,
+/* pos 013d: 118 */    0xAD /* '-' -> */,
+/* pos 013e: 119 */    0xF3 /* 's' -> */,
+/* pos 013f: 120 */    0xE5 /* 'e' -> */,
+/* pos 0140: 121 */    0xF4 /* 't' -> */,
+/* pos 0141: 122 */    0xF4 /* 't' -> */,
+/* pos 0142: 123 */    0xE9 /* 'i' -> */,
+/* pos 0143: 124 */    0xEE /* 'n' -> */,
+/* pos 0144: 125 */    0xE7 /* 'g' -> */,
+/* pos 0145: 126 */    0xF3 /* 's' -> */,
+/* pos 0146: 127 */    0xBA /* ':' -> */,
+/* pos 0147: 128 */    0x00, 0x10                  /* - terminal marker 16 - */,
+/* pos 0149: 129 */    0x63 /* 'c' */, 0x0D, 0x00  /* (to 0x0156 state 130) */,
+                       0x75 /* 'u' */, 0xAC, 0x00  /* (to 0x01F8 state 230) */,
+                       0x67 /* 'g' */, 0x7D, 0x01  /* (to 0x02CC state 358) */,
+                       0x6C /* 'l' */, 0x7E, 0x01  /* (to 0x02D0 state 361) */,
+                       0x08, /* fail */
+/* pos 0156: 130 */    0xE3 /* 'c' -> */,
+/* pos 0157: 131 */    0xE5 /* 'e' -> */,
+/* pos 0158: 132 */    0x70 /* 'p' */, 0x07, 0x00  /* (to 0x015F state 133) */,
+                       0x73 /* 's' */, 0x0E, 0x00  /* (to 0x0169 state 136) */,
+                       0x08, /* fail */
+/* pos 015f: 133 */    0xF4 /* 't' -> */,
+/* pos 0160: 134 */    0x3A /* ':' */, 0x07, 0x00  /* (to 0x0167 state 135) */,
+                       0x2D /* '-' */, 0x59, 0x00  /* (to 0x01BC state 192) */,
+                       0x08, /* fail */
+/* pos 0167: 135 */    0x00, 0x11                  /* - terminal marker 17 - */,
+/* pos 0169: 136 */    0xF3 /* 's' -> */,
+/* pos 016a: 137 */    0xAD /* '-' -> */,
+/* pos 016b: 138 */    0xE3 /* 'c' -> */,
+/* pos 016c: 139 */    0xEF /* 'o' -> */,
+/* pos 016d: 140 */    0xEE /* 'n' -> */,
+/* pos 016e: 141 */    0xF4 /* 't' -> */,
+/* pos 016f: 142 */    0xF2 /* 'r' -> */,
+/* pos 0170: 143 */    0xEF /* 'o' -> */,
+/* pos 0171: 144 */    0xEC /* 'l' -> */,
+/* pos 0172: 145 */    0xAD /* '-' -> */,
+/* pos 0173: 146 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x017A state 147) */,
+                       0x61 /* 'a' */, 0x48, 0x01  /* (to 0x02BE state 345) */,
+                       0x08, /* fail */
+/* pos 017a: 147 */    0xE5 /* 'e' -> */,
+/* pos 017b: 148 */    0xF1 /* 'q' -> */,
+/* pos 017c: 149 */    0xF5 /* 'u' -> */,
+/* pos 017d: 150 */    0xE5 /* 'e' -> */,
+/* pos 017e: 151 */    0xF3 /* 's' -> */,
+/* pos 017f: 152 */    0xF4 /* 't' -> */,
+/* pos 0180: 153 */    0xAD /* '-' -> */,
+/* pos 0181: 154 */    0xE8 /* 'h' -> */,
+/* pos 0182: 155 */    0xE5 /* 'e' -> */,
+/* pos 0183: 156 */    0xE1 /* 'a' -> */,
+/* pos 0184: 157 */    0xE4 /* 'd' -> */,
+/* pos 0185: 158 */    0xE5 /* 'e' -> */,
+/* pos 0186: 159 */    0xF2 /* 'r' -> */,
+/* pos 0187: 160 */    0xF3 /* 's' -> */,
+/* pos 0188: 161 */    0xBA /* ':' -> */,
+/* pos 0189: 162 */    0x00, 0x12                  /* - terminal marker 18 - */,
+/* pos 018b: 163 */    0xE6 /* 'f' -> */,
+/* pos 018c: 164 */    0xAD /* '-' -> */,
+/* pos 018d: 165 */    0x6D /* 'm' */, 0x0D, 0x00  /* (to 0x019A state 166) */,
+                       0x6E /* 'n' */, 0x20, 0x00  /* (to 0x01B0 state 181) */,
+                       0x72 /* 'r' */, 0x9E, 0x01  /* (to 0x0331 state 435) */,
+                       0x75 /* 'u' */, 0xA2, 0x01  /* (to 0x0338 state 441) */,
+                       0x08, /* fail */
+/* pos 019a: 166 */    0x6F /* 'o' */, 0x07, 0x00  /* (to 0x01A1 state 167) */,
+                       0x61 /* 'a' */, 0x8E, 0x01  /* (to 0x032B state 430) */,
+                       0x08, /* fail */
+/* pos 01a1: 167 */    0xE4 /* 'd' -> */,
+/* pos 01a2: 168 */    0xE9 /* 'i' -> */,
+/* pos 01a3: 169 */    0xE6 /* 'f' -> */,
+/* pos 01a4: 170 */    0xE9 /* 'i' -> */,
+/* pos 01a5: 171 */    0xE5 /* 'e' -> */,
+/* pos 01a6: 172 */    0xE4 /* 'd' -> */,
+/* pos 01a7: 173 */    0xAD /* '-' -> */,
+/* pos 01a8: 174 */    0xF3 /* 's' -> */,
+/* pos 01a9: 175 */    0xE9 /* 'i' -> */,
+/* pos 01aa: 176 */    0xEE /* 'n' -> */,
+/* pos 01ab: 177 */    0xE3 /* 'c' -> */,
+/* pos 01ac: 178 */    0xE5 /* 'e' -> */,
+/* pos 01ad: 179 */    0xBA /* ':' -> */,
+/* pos 01ae: 180 */    0x00, 0x13                  /* - terminal marker 19 - */,
+/* pos 01b0: 181 */    0xEF /* 'o' -> */,
+/* pos 01b1: 182 */    0xEE /* 'n' -> */,
+/* pos 01b2: 183 */    0xE5 /* 'e' -> */,
+/* pos 01b3: 184 */    0xAD /* '-' -> */,
+/* pos 01b4: 185 */    0xED /* 'm' -> */,
+/* pos 01b5: 186 */    0xE1 /* 'a' -> */,
+/* pos 01b6: 187 */    0xF4 /* 't' -> */,
+/* pos 01b7: 188 */    0xE3 /* 'c' -> */,
+/* pos 01b8: 189 */    0xE8 /* 'h' -> */,
+/* pos 01b9: 190 */    0xBA /* ':' -> */,
+/* pos 01ba: 191 */    0x00, 0x14                  /* - terminal marker 20 - */,
+/* pos 01bc: 192 */    0x65 /* 'e' */, 0x0D, 0x00  /* (to 0x01C9 state 193) */,
+                       0x6C /* 'l' */, 0x14, 0x00  /* (to 0x01D3 state 202) */,
+                       0x63 /* 'c' */, 0xEB, 0x00  /* (to 0x02AD state 330) */,
+                       0x72 /* 'r' */, 0xF1, 0x00  /* (to 0x02B6 state 338) */,
+                       0x08, /* fail */
+/* pos 01c9: 193 */    0xEE /* 'n' -> */,
+/* pos 01ca: 194 */    0xE3 /* 'c' -> */,
+/* pos 01cb: 195 */    0xEF /* 'o' -> */,
+/* pos 01cc: 196 */    0xE4 /* 'd' -> */,
+/* pos 01cd: 197 */    0xE9 /* 'i' -> */,
+/* pos 01ce: 198 */    0xEE /* 'n' -> */,
+/* pos 01cf: 199 */    0xE7 /* 'g' -> */,
+/* pos 01d0: 200 */    0xBA /* ':' -> */,
+/* pos 01d1: 201 */    0x00, 0x15                  /* - terminal marker 21 - */,
+/* pos 01d3: 202 */    0xE1 /* 'a' -> */,
+/* pos 01d4: 203 */    0xEE /* 'n' -> */,
+/* pos 01d5: 204 */    0xE7 /* 'g' -> */,
+/* pos 01d6: 205 */    0xF5 /* 'u' -> */,
+/* pos 01d7: 206 */    0xE1 /* 'a' -> */,
+/* pos 01d8: 207 */    0xE7 /* 'g' -> */,
+/* pos 01d9: 208 */    0xE5 /* 'e' -> */,
+/* pos 01da: 209 */    0xBA /* ':' -> */,
+/* pos 01db: 210 */    0x00, 0x16                  /* - terminal marker 22 - */,
+/* pos 01dd: 211 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x01E4 state 212) */,
+                       0x6F /* 'o' */, 0x9E, 0x01  /* (to 0x037E state 497) */,
+                       0x08, /* fail */
+/* pos 01e4: 212 */    0xE7 /* 'g' -> */,
+/* pos 01e5: 213 */    0xED /* 'm' -> */,
+/* pos 01e6: 214 */    0xE1 /* 'a' -> */,
+/* pos 01e7: 215 */    0xBA /* ':' -> */,
+/* pos 01e8: 216 */    0x00, 0x17                  /* - terminal marker 23 - */,
+/* pos 01ea: 217 */    0xE3 /* 'c' -> */,
+/* pos 01eb: 218 */    0xE8 /* 'h' -> */,
+/* pos 01ec: 219 */    0xE5 /* 'e' -> */,
+/* pos 01ed: 220 */    0xAD /* '-' -> */,
+/* pos 01ee: 221 */    0xE3 /* 'c' -> */,
+/* pos 01ef: 222 */    0xEF /* 'o' -> */,
+/* pos 01f0: 223 */    0xEE /* 'n' -> */,
+/* pos 01f1: 224 */    0xF4 /* 't' -> */,
+/* pos 01f2: 225 */    0xF2 /* 'r' -> */,
+/* pos 01f3: 226 */    0xEF /* 'o' -> */,
+/* pos 01f4: 227 */    0xEC /* 'l' -> */,
+/* pos 01f5: 228 */    0xBA /* ':' -> */,
+/* pos 01f6: 229 */    0x00, 0x18                  /* - terminal marker 24 - */,
+/* pos 01f8: 230 */    0xF4 /* 't' -> */,
+/* pos 01f9: 231 */    0xE8 /* 'h' -> */,
+/* pos 01fa: 232 */    0xEF /* 'o' -> */,
+/* pos 01fb: 233 */    0xF2 /* 'r' -> */,
+/* pos 01fc: 234 */    0xE9 /* 'i' -> */,
+/* pos 01fd: 235 */    0xFA /* 'z' -> */,
+/* pos 01fe: 236 */    0xE1 /* 'a' -> */,
+/* pos 01ff: 237 */    0xF4 /* 't' -> */,
+/* pos 0200: 238 */    0xE9 /* 'i' -> */,
+/* pos 0201: 239 */    0xEF /* 'o' -> */,
+/* pos 0202: 240 */    0xEE /* 'n' -> */,
+/* pos 0203: 241 */    0xBA /* ':' -> */,
+/* pos 0204: 242 */    0x00, 0x19                  /* - terminal marker 25 - */,
+/* pos 0206: 243 */    0xEB /* 'k' -> */,
+/* pos 0207: 244 */    0xE9 /* 'i' -> */,
+/* pos 0208: 245 */    0xE5 /* 'e' -> */,
+/* pos 0209: 246 */    0xBA /* ':' -> */,
+/* pos 020a: 247 */    0x00, 0x1A                  /* - terminal marker 26 - */,
+/* pos 020c: 248 */    0xE5 /* 'e' -> */,
+/* pos 020d: 249 */    0xEE /* 'n' -> */,
+/* pos 020e: 250 */    0xF4 /* 't' -> */,
+/* pos 020f: 251 */    0xAD /* '-' -> */,
+/* pos 0210: 252 */    0x6C /* 'l' */, 0x10, 0x00  /* (to 0x0220 state 253) */,
+                       0x74 /* 't' */, 0x1E, 0x00  /* (to 0x0231 state 260) */,
+                       0x64 /* 'd' */, 0xC0, 0x00  /* (to 0x02D6 state 366) */,
+                       0x65 /* 'e' */, 0xCA, 0x00  /* (to 0x02E3 state 378) */,
+                       0x72 /* 'r' */, 0xE3, 0x00  /* (to 0x02FF state 403) */,
+                       0x08, /* fail */
+/* pos 0220: 253 */    0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x022A state 254) */,
+                       0x61 /* 'a' */, 0xCA, 0x00  /* (to 0x02ED state 387) */,
+                       0x6F /* 'o' */, 0xD0, 0x00  /* (to 0x02F6 state 395) */,
+                       0x08, /* fail */
+/* pos 022a: 254 */    0xEE /* 'n' -> */,
+/* pos 022b: 255 */    0xE7 /* 'g' -> */,
+/* pos 022c: 256 */    0xF4 /* 't' -> */,
+/* pos 022d: 257 */    0xE8 /* 'h' -> */,
+/* pos 022e: 258 */    0xBA /* ':' -> */,
+/* pos 022f: 259 */    0x00, 0x1B                  /* - terminal marker 27 - */,
+/* pos 0231: 260 */    0xF9 /* 'y' -> */,
+/* pos 0232: 261 */    0xF0 /* 'p' -> */,
+/* pos 0233: 262 */    0xE5 /* 'e' -> */,
+/* pos 0234: 263 */    0xBA /* ':' -> */,
+/* pos 0235: 264 */    0x00, 0x1C                  /* - terminal marker 28 - */,
+/* pos 0237: 265 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x023E state 266) */,
+                       0x65 /* 'e' */, 0xF6, 0x01  /* (to 0x0430 state 637) */,
+                       0x08, /* fail */
+/* pos 023e: 266 */    0xF4 /* 't' -> */,
+/* pos 023f: 267 */    0xE5 /* 'e' -> */,
+/* pos 0240: 268 */    0xBA /* ':' -> */,
+/* pos 0241: 269 */    0x00, 0x1D                  /* - terminal marker 29 - */,
+/* pos 0243: 270 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x024A state 271) */,
+                       0x65 /* 'e' */, 0x0A, 0x00  /* (to 0x0250 state 276) */,
+                       0x08, /* fail */
+/* pos 024a: 271 */    0xEE /* 'n' -> */,
+/* pos 024b: 272 */    0xE7 /* 'g' -> */,
+/* pos 024c: 273 */    0xE5 /* 'e' -> */,
+/* pos 024d: 274 */    0xBA /* ':' -> */,
+/* pos 024e: 275 */    0x00, 0x1E                  /* - terminal marker 30 - */,
+/* pos 0250: 276 */    0x66 /* 'f' */, 0x07, 0x00  /* (to 0x0257 state 277) */,
+                       0x74 /* 't' */, 0x5A, 0x01  /* (to 0x03AD state 529) */,
+                       0x08, /* fail */
+/* pos 0257: 277 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x025E state 278) */,
+                       0x72 /* 'r' */, 0x4D, 0x01  /* (to 0x03A7 state 524) */,
+                       0x08, /* fail */
+/* pos 025e: 278 */    0xF2 /* 'r' -> */,
+/* pos 025f: 279 */    0xE5 /* 'e' -> */,
+/* pos 0260: 280 */    0xF2 /* 'r' -> */,
+/* pos 0261: 281 */    0xBA /* ':' -> */,
+/* pos 0262: 282 */    0x00, 0x1F                  /* - terminal marker 31 - */,
+/* pos 0264: 283 */    0x00, 0x20                  /* - terminal marker 32 - */,
+/* pos 0266: 284 */    0xE5 /* 'e' -> */,
+/* pos 0267: 285 */    0xF2 /* 'r' -> */,
+/* pos 0268: 286 */    0xF3 /* 's' -> */,
+/* pos 0269: 287 */    0xE9 /* 'i' -> */,
+/* pos 026a: 288 */    0xEF /* 'o' -> */,
+/* pos 026b: 289 */    0xEE /* 'n' -> */,
+/* pos 026c: 290 */    0xBA /* ':' -> */,
+/* pos 026d: 291 */    0x00, 0x21                  /* - terminal marker 33 - */,
+/* pos 026f: 292 */    0xF2 /* 'r' -> */,
+/* pos 0270: 293 */    0xE9 /* 'i' -> */,
+/* pos 0271: 294 */    0xE7 /* 'g' -> */,
+/* pos 0272: 295 */    0xE9 /* 'i' -> */,
+/* pos 0273: 296 */    0xEE /* 'n' -> */,
+/* pos 0274: 297 */    0xBA /* ':' -> */,
+/* pos 0275: 298 */    0x00, 0x22                  /* - terminal marker 34 - */,
+/* pos 0277: 299 */    0x61 /* 'a' */, 0x0D, 0x00  /* (to 0x0284 state 300) */,
+                       0x6D /* 'm' */, 0x14, 0x00  /* (to 0x028E state 309) */,
+                       0x70 /* 'p' */, 0x18, 0x00  /* (to 0x0295 state 315) */,
+                       0x73 /* 's' */, 0x1A, 0x00  /* (to 0x029A state 319) */,
+                       0x08, /* fail */
+/* pos 0284: 300 */    0xF5 /* 'u' -> */,
+/* pos 0285: 301 */    0xF4 /* 't' -> */,
+/* pos 0286: 302 */    0xE8 /* 'h' -> */,
+/* pos 0287: 303 */    0xEF /* 'o' -> */,
+/* pos 0288: 304 */    0xF2 /* 'r' -> */,
+/* pos 0289: 305 */    0xE9 /* 'i' -> */,
+/* pos 028a: 306 */    0xF4 /* 't' -> */,
+/* pos 028b: 307 */    0xF9 /* 'y' -> */,
+/* pos 028c: 308 */    0x00, 0x23                  /* - terminal marker 35 - */,
+/* pos 028e: 309 */    0xE5 /* 'e' -> */,
+/* pos 028f: 310 */    0xF4 /* 't' -> */,
+/* pos 0290: 311 */    0xE8 /* 'h' -> */,
+/* pos 0291: 312 */    0xEF /* 'o' -> */,
+/* pos 0292: 313 */    0xE4 /* 'd' -> */,
+/* pos 0293: 314 */    0x00, 0x24                  /* - terminal marker 36 - */,
+/* pos 0295: 315 */    0xE1 /* 'a' -> */,
+/* pos 0296: 316 */    0xF4 /* 't' -> */,
+/* pos 0297: 317 */    0xE8 /* 'h' -> */,
+/* pos 0298: 318 */    0x00, 0x25                  /* - terminal marker 37 - */,
+/* pos 029a: 319 */    0x63 /* 'c' */, 0x07, 0x00  /* (to 0x02A1 state 320) */,
+                       0x74 /* 't' */, 0x0A, 0x00  /* (to 0x02A7 state 325) */,
+                       0x08, /* fail */
+/* pos 02a1: 320 */    0xE8 /* 'h' -> */,
+/* pos 02a2: 321 */    0xE5 /* 'e' -> */,
+/* pos 02a3: 322 */    0xED /* 'm' -> */,
+/* pos 02a4: 323 */    0xE5 /* 'e' -> */,
+/* pos 02a5: 324 */    0x00, 0x26                  /* - terminal marker 38 - */,
+/* pos 02a7: 325 */    0xE1 /* 'a' -> */,
+/* pos 02a8: 326 */    0xF4 /* 't' -> */,
+/* pos 02a9: 327 */    0xF5 /* 'u' -> */,
+/* pos 02aa: 328 */    0xF3 /* 's' -> */,
+/* pos 02ab: 329 */    0x00, 0x27                  /* - terminal marker 39 - */,
+/* pos 02ad: 330 */    0xE8 /* 'h' -> */,
+/* pos 02ae: 331 */    0xE1 /* 'a' -> */,
+/* pos 02af: 332 */    0xF2 /* 'r' -> */,
+/* pos 02b0: 333 */    0xF3 /* 's' -> */,
+/* pos 02b1: 334 */    0xE5 /* 'e' -> */,
+/* pos 02b2: 335 */    0xF4 /* 't' -> */,
+/* pos 02b3: 336 */    0xBA /* ':' -> */,
+/* pos 02b4: 337 */    0x00, 0x28                  /* - terminal marker 40 - */,
+/* pos 02b6: 338 */    0xE1 /* 'a' -> */,
+/* pos 02b7: 339 */    0xEE /* 'n' -> */,
+/* pos 02b8: 340 */    0xE7 /* 'g' -> */,
+/* pos 02b9: 341 */    0xE5 /* 'e' -> */,
+/* pos 02ba: 342 */    0xF3 /* 's' -> */,
+/* pos 02bb: 343 */    0xBA /* ':' -> */,
+/* pos 02bc: 344 */    0x00, 0x29                  /* - terminal marker 41 - */,
+/* pos 02be: 345 */    0xEC /* 'l' -> */,
+/* pos 02bf: 346 */    0xEC /* 'l' -> */,
+/* pos 02c0: 347 */    0xEF /* 'o' -> */,
+/* pos 02c1: 348 */    0xF7 /* 'w' -> */,
+/* pos 02c2: 349 */    0xAD /* '-' -> */,
+/* pos 02c3: 350 */    0xEF /* 'o' -> */,
+/* pos 02c4: 351 */    0xF2 /* 'r' -> */,
+/* pos 02c5: 352 */    0xE9 /* 'i' -> */,
+/* pos 02c6: 353 */    0xE7 /* 'g' -> */,
+/* pos 02c7: 354 */    0xE9 /* 'i' -> */,
+/* pos 02c8: 355 */    0xEE /* 'n' -> */,
+/* pos 02c9: 356 */    0xBA /* ':' -> */,
+/* pos 02ca: 357 */    0x00, 0x2A                  /* - terminal marker 42 - */,
+/* pos 02cc: 358 */    0xE5 /* 'e' -> */,
+/* pos 02cd: 359 */    0xBA /* ':' -> */,
+/* pos 02ce: 360 */    0x00, 0x2B                  /* - terminal marker 43 - */,
+/* pos 02d0: 361 */    0xEC /* 'l' -> */,
+/* pos 02d1: 362 */    0xEF /* 'o' -> */,
+/* pos 02d2: 363 */    0xF7 /* 'w' -> */,
+/* pos 02d3: 364 */    0xBA /* ':' -> */,
+/* pos 02d4: 365 */    0x00, 0x2C                  /* - terminal marker 44 - */,
+/* pos 02d6: 366 */    0xE9 /* 'i' -> */,
+/* pos 02d7: 367 */    0xF3 /* 's' -> */,
+/* pos 02d8: 368 */    0xF0 /* 'p' -> */,
+/* pos 02d9: 369 */    0xEF /* 'o' -> */,
+/* pos 02da: 370 */    0xF3 /* 's' -> */,
+/* pos 02db: 371 */    0xE9 /* 'i' -> */,
+/* pos 02dc: 372 */    0xF4 /* 't' -> */,
+/* pos 02dd: 373 */    0xE9 /* 'i' -> */,
+/* pos 02de: 374 */    0xEF /* 'o' -> */,
+/* pos 02df: 375 */    0xEE /* 'n' -> */,
+/* pos 02e0: 376 */    0xBA /* ':' -> */,
+/* pos 02e1: 377 */    0x00, 0x2D                  /* - terminal marker 45 - */,
+/* pos 02e3: 378 */    0xEE /* 'n' -> */,
+/* pos 02e4: 379 */    0xE3 /* 'c' -> */,
+/* pos 02e5: 380 */    0xEF /* 'o' -> */,
+/* pos 02e6: 381 */    0xE4 /* 'd' -> */,
+/* pos 02e7: 382 */    0xE9 /* 'i' -> */,
+/* pos 02e8: 383 */    0xEE /* 'n' -> */,
+/* pos 02e9: 384 */    0xE7 /* 'g' -> */,
+/* pos 02ea: 385 */    0xBA /* ':' -> */,
+/* pos 02eb: 386 */    0x00, 0x2E                  /* - terminal marker 46 - */,
+/* pos 02ed: 387 */    0xEE /* 'n' -> */,
+/* pos 02ee: 388 */    0xE7 /* 'g' -> */,
+/* pos 02ef: 389 */    0xF5 /* 'u' -> */,
+/* pos 02f0: 390 */    0xE1 /* 'a' -> */,
+/* pos 02f1: 391 */    0xE7 /* 'g' -> */,
+/* pos 02f2: 392 */    0xE5 /* 'e' -> */,
+/* pos 02f3: 393 */    0xBA /* ':' -> */,
+/* pos 02f4: 394 */    0x00, 0x2F                  /* - terminal marker 47 - */,
+/* pos 02f6: 395 */    0xE3 /* 'c' -> */,
+/* pos 02f7: 396 */    0xE1 /* 'a' -> */,
+/* pos 02f8: 397 */    0xF4 /* 't' -> */,
+/* pos 02f9: 398 */    0xE9 /* 'i' -> */,
+/* pos 02fa: 399 */    0xEF /* 'o' -> */,
+/* pos 02fb: 400 */    0xEE /* 'n' -> */,
+/* pos 02fc: 401 */    0xBA /* ':' -> */,
+/* pos 02fd: 402 */    0x00, 0x30                  /* - terminal marker 48 - */,
+/* pos 02ff: 403 */    0xE1 /* 'a' -> */,
+/* pos 0300: 404 */    0xEE /* 'n' -> */,
+/* pos 0301: 405 */    0xE7 /* 'g' -> */,
+/* pos 0302: 406 */    0xE5 /* 'e' -> */,
+/* pos 0303: 407 */    0xBA /* ':' -> */,
+/* pos 0304: 408 */    0x00, 0x31                  /* - terminal marker 49 - */,
+/* pos 0306: 409 */    0x74 /* 't' */, 0x07, 0x00  /* (to 0x030D state 410) */,
+                       0x78 /* 'x' */, 0x09, 0x00  /* (to 0x0312 state 414) */,
+                       0x08, /* fail */
+/* pos 030d: 410 */    0xE1 /* 'a' -> */,
+/* pos 030e: 411 */    0xE7 /* 'g' -> */,
+/* pos 030f: 412 */    0xBA /* ':' -> */,
+/* pos 0310: 413 */    0x00, 0x32                  /* - terminal marker 50 - */,
+/* pos 0312: 414 */    0xF0 /* 'p' -> */,
+/* pos 0313: 415 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x031A state 416) */,
+                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x031F state 420) */,
+                       0x08, /* fail */
+/* pos 031a: 416 */    0xE3 /* 'c' -> */,
+/* pos 031b: 417 */    0xF4 /* 't' -> */,
+/* pos 031c: 418 */    0xBA /* ':' -> */,
+/* pos 031d: 419 */    0x00, 0x33                  /* - terminal marker 51 - */,
+/* pos 031f: 420 */    0xF2 /* 'r' -> */,
+/* pos 0320: 421 */    0xE5 /* 'e' -> */,
+/* pos 0321: 422 */    0xF3 /* 's' -> */,
+/* pos 0322: 423 */    0xBA /* ':' -> */,
+/* pos 0323: 424 */    0x00, 0x34                  /* - terminal marker 52 - */,
+/* pos 0325: 425 */    0xF2 /* 'r' -> */,
+/* pos 0326: 426 */    0xEF /* 'o' -> */,
+/* pos 0327: 427 */    0xED /* 'm' -> */,
+/* pos 0328: 428 */    0xBA /* ':' -> */,
+/* pos 0329: 429 */    0x00, 0x35                  /* - terminal marker 53 - */,
+/* pos 032b: 430 */    0xF4 /* 't' -> */,
+/* pos 032c: 431 */    0xE3 /* 'c' -> */,
+/* pos 032d: 432 */    0xE8 /* 'h' -> */,
+/* pos 032e: 433 */    0xBA /* ':' -> */,
+/* pos 032f: 434 */    0x00, 0x36                  /* - terminal marker 54 - */,
+/* pos 0331: 435 */    0xE1 /* 'a' -> */,
+/* pos 0332: 436 */    0xEE /* 'n' -> */,
+/* pos 0333: 437 */    0xE7 /* 'g' -> */,
+/* pos 0334: 438 */    0xE5 /* 'e' -> */,
+/* pos 0335: 439 */    0xBA /* ':' -> */,
+/* pos 0336: 440 */    0x00, 0x37                  /* - terminal marker 55 - */,
+/* pos 0338: 441 */    0xEE /* 'n' -> */,
+/* pos 0339: 442 */    0xED /* 'm' -> */,
+/* pos 033a: 443 */    0xEF /* 'o' -> */,
+/* pos 033b: 444 */    0xE4 /* 'd' -> */,
+/* pos 033c: 445 */    0xE9 /* 'i' -> */,
+/* pos 033d: 446 */    0xE6 /* 'f' -> */,
+/* pos 033e: 447 */    0xE9 /* 'i' -> */,
+/* pos 033f: 448 */    0xE5 /* 'e' -> */,
+/* pos 0340: 449 */    0xE4 /* 'd' -> */,
+/* pos 0341: 450 */    0xAD /* '-' -> */,
+/* pos 0342: 451 */    0xF3 /* 's' -> */,
+/* pos 0343: 452 */    0xE9 /* 'i' -> */,
+/* pos 0344: 453 */    0xEE /* 'n' -> */,
+/* pos 0345: 454 */    0xE3 /* 'c' -> */,
+/* pos 0346: 455 */    0xE5 /* 'e' -> */,
+/* pos 0347: 456 */    0xBA /* ':' -> */,
+/* pos 0348: 457 */    0x00, 0x38                  /* - terminal marker 56 - */,
+/* pos 034a: 458 */    0x61 /* 'a' */, 0x0A, 0x00  /* (to 0x0354 state 459) */,
+                       0x69 /* 'i' */, 0x15, 0x00  /* (to 0x0362 state 472) */,
+                       0x6F /* 'o' */, 0x17, 0x00  /* (to 0x0367 state 476) */,
+                       0x08, /* fail */
+/* pos 0354: 459 */    0xF3 /* 's' -> */,
+/* pos 0355: 460 */    0xF4 /* 't' -> */,
+/* pos 0356: 461 */    0xAD /* '-' -> */,
+/* pos 0357: 462 */    0xED /* 'm' -> */,
+/* pos 0358: 463 */    0xEF /* 'o' -> */,
+/* pos 0359: 464 */    0xE4 /* 'd' -> */,
+/* pos 035a: 465 */    0xE9 /* 'i' -> */,
+/* pos 035b: 466 */    0xE6 /* 'f' -> */,
+/* pos 035c: 467 */    0xE9 /* 'i' -> */,
+/* pos 035d: 468 */    0xE5 /* 'e' -> */,
+/* pos 035e: 469 */    0xE4 /* 'd' -> */,
+/* pos 035f: 470 */    0xBA /* ':' -> */,
+/* pos 0360: 471 */    0x00, 0x39                  /* - terminal marker 57 - */,
+/* pos 0362: 472 */    0xEE /* 'n' -> */,
+/* pos 0363: 473 */    0xEB /* 'k' -> */,
+/* pos 0364: 474 */    0xBA /* ':' -> */,
+/* pos 0365: 475 */    0x00, 0x3A                  /* - terminal marker 58 - */,
+/* pos 0367: 476 */    0xE3 /* 'c' -> */,
+/* pos 0368: 477 */    0xE1 /* 'a' -> */,
+/* pos 0369: 478 */    0xF4 /* 't' -> */,
+/* pos 036a: 479 */    0xE9 /* 'i' -> */,
+/* pos 036b: 480 */    0xEF /* 'o' -> */,
+/* pos 036c: 481 */    0xEE /* 'n' -> */,
+/* pos 036d: 482 */    0xBA /* ':' -> */,
+/* pos 036e: 483 */    0x00, 0x3B                  /* - terminal marker 59 - */,
+/* pos 0370: 484 */    0xE1 /* 'a' -> */,
+/* pos 0371: 485 */    0xF8 /* 'x' -> */,
+/* pos 0372: 486 */    0xAD /* '-' -> */,
+/* pos 0373: 487 */    0xE6 /* 'f' -> */,
+/* pos 0374: 488 */    0xEF /* 'o' -> */,
+/* pos 0375: 489 */    0xF2 /* 'r' -> */,
+/* pos 0376: 490 */    0xF7 /* 'w' -> */,
+/* pos 0377: 491 */    0xE1 /* 'a' -> */,
+/* pos 0378: 492 */    0xF2 /* 'r' -> */,
+/* pos 0379: 493 */    0xE4 /* 'd' -> */,
+/* pos 037a: 494 */    0xF3 /* 's' -> */,
+/* pos 037b: 495 */    0xBA /* ':' -> */,
+/* pos 037c: 496 */    0x00, 0x3C                  /* - terminal marker 60 - */,
+/* pos 037e: 497 */    0xF8 /* 'x' -> */,
+/* pos 037f: 498 */    0xF9 /* 'y' -> */,
+/* pos 0380: 499 */    0x2D /* '-' */, 0x07, 0x00  /* (to 0x0387 state 500) */,
+                       0x20 /* ' ' */, 0xBB, 0x00  /* (to 0x043E state 649) */,
+                       0x08, /* fail */
+/* pos 0387: 500 */    0xE1 /* 'a' -> */,
+/* pos 0388: 501 */    0xF5 /* 'u' -> */,
+/* pos 0389: 502 */    0xF4 /* 't' -> */,
+/* pos 038a: 503 */    0xE8 /* 'h' -> */,
+/* pos 038b: 504 */    0x65 /* 'e' */, 0x07, 0x00  /* (to 0x0392 state 505) */,
+                       0x6F /* 'o' */, 0x0E, 0x00  /* (to 0x039C state 514) */,
+                       0x08, /* fail */
+/* pos 0392: 505 */    0xEE /* 'n' -> */,
+/* pos 0393: 506 */    0xF4 /* 't' -> */,
+/* pos 0394: 507 */    0xE9 /* 'i' -> */,
+/* pos 0395: 508 */    0xE3 /* 'c' -> */,
+/* pos 0396: 509 */    0xE1 /* 'a' -> */,
+/* pos 0397: 510 */    0xF4 /* 't' -> */,
+/* pos 0398: 511 */    0xE5 /* 'e' -> */,
+/* pos 0399: 512 */    0xBA /* ':' -> */,
+/* pos 039a: 513 */    0x00, 0x3D                  /* - terminal marker 61 - */,
+/* pos 039c: 514 */    0xF2 /* 'r' -> */,
+/* pos 039d: 515 */    0xE9 /* 'i' -> */,
+/* pos 039e: 516 */    0xFA /* 'z' -> */,
+/* pos 039f: 517 */    0xE1 /* 'a' -> */,
+/* pos 03a0: 518 */    0xF4 /* 't' -> */,
+/* pos 03a1: 519 */    0xE9 /* 'i' -> */,
+/* pos 03a2: 520 */    0xEF /* 'o' -> */,
+/* pos 03a3: 521 */    0xEE /* 'n' -> */,
+/* pos 03a4: 522 */    0xBA /* ':' -> */,
+/* pos 03a5: 523 */    0x00, 0x3E                  /* - terminal marker 62 - */,
+/* pos 03a7: 524 */    0xE5 /* 'e' -> */,
+/* pos 03a8: 525 */    0xF3 /* 's' -> */,
+/* pos 03a9: 526 */    0xE8 /* 'h' -> */,
+/* pos 03aa: 527 */    0xBA /* ':' -> */,
+/* pos 03ab: 528 */    0x00, 0x3F                  /* - terminal marker 63 - */,
+/* pos 03ad: 529 */    0xF2 /* 'r' -> */,
+/* pos 03ae: 530 */    0xF9 /* 'y' -> */,
+/* pos 03af: 531 */    0xAD /* '-' -> */,
+/* pos 03b0: 532 */    0xE1 /* 'a' -> */,
+/* pos 03b1: 533 */    0xE6 /* 'f' -> */,
+/* pos 03b2: 534 */    0xF4 /* 't' -> */,
+/* pos 03b3: 535 */    0xE5 /* 'e' -> */,
+/* pos 03b4: 536 */    0xF2 /* 'r' -> */,
+/* pos 03b5: 537 */    0xBA /* ':' -> */,
+/* pos 03b6: 538 */    0x00, 0x40                  /* - terminal marker 64 - */,
+/* pos 03b8: 539 */    0xF6 /* 'v' -> */,
+/* pos 03b9: 540 */    0xE5 /* 'e' -> */,
+/* pos 03ba: 541 */    0xF2 /* 'r' -> */,
+/* pos 03bb: 542 */    0xBA /* ':' -> */,
+/* pos 03bc: 543 */    0x00, 0x41                  /* - terminal marker 65 - */,
+/* pos 03be: 544 */    0xAD /* '-' -> */,
+/* pos 03bf: 545 */    0xE3 /* 'c' -> */,
+/* pos 03c0: 546 */    0xEF /* 'o' -> */,
+/* pos 03c1: 547 */    0xEF /* 'o' -> */,
+/* pos 03c2: 548 */    0xEB /* 'k' -> */,
+/* pos 03c3: 549 */    0xE9 /* 'i' -> */,
+/* pos 03c4: 550 */    0xE5 /* 'e' -> */,
+/* pos 03c5: 551 */    0xBA /* ':' -> */,
+/* pos 03c6: 552 */    0x00, 0x42                  /* - terminal marker 66 - */,
+/* pos 03c8: 553 */    0xF2 /* 'r' -> */,
+/* pos 03c9: 554 */    0xE9 /* 'i' -> */,
+/* pos 03ca: 555 */    0xE3 /* 'c' -> */,
+/* pos 03cb: 556 */    0xF4 /* 't' -> */,
+/* pos 03cc: 557 */    0xAD /* '-' -> */,
+/* pos 03cd: 558 */    0xF4 /* 't' -> */,
+/* pos 03ce: 559 */    0xF2 /* 'r' -> */,
+/* pos 03cf: 560 */    0xE1 /* 'a' -> */,
+/* pos 03d0: 561 */    0xEE /* 'n' -> */,
+/* pos 03d1: 562 */    0xF3 /* 's' -> */,
+/* pos 03d2: 563 */    0xF0 /* 'p' -> */,
+/* pos 03d3: 564 */    0xEF /* 'o' -> */,
+/* pos 03d4: 565 */    0xF2 /* 'r' -> */,
+/* pos 03d5: 566 */    0xF4 /* 't' -> */,
+/* pos 03d6: 567 */    0xAD /* '-' -> */,
+/* pos 03d7: 568 */    0xF3 /* 's' -> */,
+/* pos 03d8: 569 */    0xE5 /* 'e' -> */,
+/* pos 03d9: 570 */    0xE3 /* 'c' -> */,
+/* pos 03da: 571 */    0xF5 /* 'u' -> */,
+/* pos 03db: 572 */    0xF2 /* 'r' -> */,
+/* pos 03dc: 573 */    0xE9 /* 'i' -> */,
+/* pos 03dd: 574 */    0xF4 /* 't' -> */,
+/* pos 03de: 575 */    0xF9 /* 'y' -> */,
+/* pos 03df: 576 */    0xBA /* ':' -> */,
+/* pos 03e0: 577 */    0x00, 0x43                  /* - terminal marker 67 - */,
+/* pos 03e2: 578 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x03E9 state 579) */,
+                       0x65 /* 'e' */, 0x84, 0x00  /* (to 0x0469 state 680) */,
+                       0x08, /* fail */
+/* pos 03e9: 579 */    0xE1 /* 'a' -> */,
+/* pos 03ea: 580 */    0xEE /* 'n' -> */,
+/* pos 03eb: 581 */    0xF3 /* 's' -> */,
+/* pos 03ec: 582 */    0xE6 /* 'f' -> */,
+/* pos 03ed: 583 */    0xE5 /* 'e' -> */,
+/* pos 03ee: 584 */    0xF2 /* 'r' -> */,
+/* pos 03ef: 585 */    0xAD /* '-' -> */,
+/* pos 03f0: 586 */    0xE5 /* 'e' -> */,
+/* pos 03f1: 587 */    0xEE /* 'n' -> */,
+/* pos 03f2: 588 */    0xE3 /* 'c' -> */,
+/* pos 03f3: 589 */    0xEF /* 'o' -> */,
+/* pos 03f4: 590 */    0xE4 /* 'd' -> */,
+/* pos 03f5: 591 */    0xE9 /* 'i' -> */,
+/* pos 03f6: 592 */    0xEE /* 'n' -> */,
+/* pos 03f7: 593 */    0xE7 /* 'g' -> */,
+/* pos 03f8: 594 */    0xBA /* ':' -> */,
+/* pos 03f9: 595 */    0x00, 0x44                  /* - terminal marker 68 - */,
+/* pos 03fb: 596 */    0xE5 /* 'e' -> */,
+/* pos 03fc: 597 */    0xF2 /* 'r' -> */,
+/* pos 03fd: 598 */    0xAD /* '-' -> */,
+/* pos 03fe: 599 */    0xE1 /* 'a' -> */,
+/* pos 03ff: 600 */    0xE7 /* 'g' -> */,
+/* pos 0400: 601 */    0xE5 /* 'e' -> */,
+/* pos 0401: 602 */    0xEE /* 'n' -> */,
+/* pos 0402: 603 */    0xF4 /* 't' -> */,
+/* pos 0403: 604 */    0xBA /* ':' -> */,
+/* pos 0404: 605 */    0x00, 0x45                  /* - terminal marker 69 - */,
+/* pos 0406: 606 */    0x61 /* 'a' */, 0x07, 0x00  /* (to 0x040D state 607) */,
+                       0x69 /* 'i' */, 0x09, 0x00  /* (to 0x0412 state 611) */,
+                       0x08, /* fail */
+/* pos 040d: 607 */    0xF2 /* 'r' -> */,
+/* pos 040e: 608 */    0xF9 /* 'y' -> */,
+/* pos 040f: 609 */    0xBA /* ':' -> */,
+/* pos 0410: 610 */    0x00, 0x46                  /* - terminal marker 70 - */,
+/* pos 0412: 611 */    0xE1 /* 'a' -> */,
+/* pos 0413: 612 */    0xBA /* ':' -> */,
+/* pos 0414: 613 */    0x00, 0x47                  /* - terminal marker 71 - */,
+/* pos 0416: 614 */    0xF7 /* 'w' -> */,
+/* pos 0417: 615 */    0xF7 /* 'w' -> */,
+/* pos 0418: 616 */    0xAD /* '-' -> */,
+/* pos 0419: 617 */    0xE1 /* 'a' -> */,
+/* pos 041a: 618 */    0xF5 /* 'u' -> */,
+/* pos 041b: 619 */    0xF4 /* 't' -> */,
+/* pos 041c: 620 */    0xE8 /* 'h' -> */,
+/* pos 041d: 621 */    0xE5 /* 'e' -> */,
+/* pos 041e: 622 */    0xEE /* 'n' -> */,
+/* pos 041f: 623 */    0xF4 /* 't' -> */,
+/* pos 0420: 624 */    0xE9 /* 'i' -> */,
+/* pos 0421: 625 */    0xE3 /* 'c' -> */,
+/* pos 0422: 626 */    0xE1 /* 'a' -> */,
+/* pos 0423: 627 */    0xF4 /* 't' -> */,
+/* pos 0424: 628 */    0xE5 /* 'e' -> */,
+/* pos 0425: 629 */    0xBA /* ':' -> */,
+/* pos 0426: 630 */    0x00, 0x48                  /* - terminal marker 72 - */,
+/* pos 0428: 631 */    0xF4 /* 't' -> */,
+/* pos 0429: 632 */    0xE3 /* 'c' -> */,
+/* pos 042a: 633 */    0xE8 /* 'h' -> */,
+/* pos 042b: 634 */    0x00, 0x49                  /* - terminal marker 73 - */,
+/* pos 042d: 635 */    0xF4 /* 't' -> */,
+/* pos 042e: 636 */    0x00, 0x4A                  /* - terminal marker 74 - */,
+/* pos 0430: 637 */    0xEC /* 'l' -> */,
+/* pos 0431: 638 */    0xE5 /* 'e' -> */,
+/* pos 0432: 639 */    0xF4 /* 't' -> */,
+/* pos 0433: 640 */    0xE5 /* 'e' -> */,
+/* pos 0434: 641 */    0x00, 0x4B                  /* - terminal marker 75 - */,
+/* pos 0436: 642 */    0xE9 /* 'i' -> */,
+/* pos 0437: 643 */    0xAD /* '-' -> */,
+/* pos 0438: 644 */    0xE1 /* 'a' -> */,
+/* pos 0439: 645 */    0xF2 /* 'r' -> */,
+/* pos 043a: 646 */    0xE7 /* 'g' -> */,
+/* pos 043b: 647 */    0xF3 /* 's' -> */,
+/* pos 043c: 648 */    0x00, 0x4C                  /* - terminal marker 76 - */,
+/* pos 043e: 649 */    0x00, 0x4D                  /* - terminal marker 77 - */,
+/* pos 0440: 650 */    0xAD /* '-' -> */,
+/* pos 0441: 651 */    0x72 /* 'r' */, 0x07, 0x00  /* (to 0x0448 state 652) */,
+                       0x66 /* 'f' */, 0x10, 0x00  /* (to 0x0454 state 662) */,
+                       0x08, /* fail */
+/* pos 0448: 652 */    0xE5 /* 'e' -> */,
+/* pos 0449: 653 */    0xE1 /* 'a' -> */,
+/* pos 044a: 654 */    0xEC /* 'l' -> */,
+/* pos 044b: 655 */    0xAD /* '-' -> */,
+/* pos 044c: 656 */    0xE9 /* 'i' -> */,
+/* pos 044d: 657 */    0xF0 /* 'p' -> */,
+/* pos 044e: 658 */    0xBA /* ':' -> */,
+/* pos 044f: 659 */    0x00, 0x4E                  /* - terminal marker 78 - */,
+/* pos 0451: 660 */    0xA0 /* ' ' -> */,
+/* pos 0452: 661 */    0x00, 0x4F                  /* - terminal marker 79 - */,
+/* pos 0454: 662 */    0xEF /* 'o' -> */,
+/* pos 0455: 663 */    0xF2 /* 'r' -> */,
+/* pos 0456: 664 */    0xF7 /* 'w' -> */,
+/* pos 0457: 665 */    0xE1 /* 'a' -> */,
+/* pos 0458: 666 */    0xF2 /* 'r' -> */,
+/* pos 0459: 667 */    0xE4 /* 'd' -> */,
+/* pos 045a: 668 */    0xE5 /* 'e' -> */,
+/* pos 045b: 669 */    0xE4 /* 'd' -> */,
+/* pos 045c: 670 */    0xAD /* '-' -> */,
+/* pos 045d: 671 */    0xE6 /* 'f' -> */,
+/* pos 045e: 672 */    0xEF /* 'o' -> */,
+/* pos 045f: 673 */    0xF2 /* 'r' -> */,
+/* pos 0460: 674 */    0x00, 0x50                  /* - terminal marker 80 - */,
+/* pos 0462: 675 */    0x00, 0x51                  /* - terminal marker 81 - */,
+/* pos 0464: 676 */    0xE1 /* 'a' -> */,
+/* pos 0465: 677 */    0xE4 /* 'd' -> */,
+/* pos 0466: 678 */    0xA0 /* ' ' -> */,
+/* pos 0467: 679 */    0x00, 0x52                  /* - terminal marker 82 - */,
+/* pos 0469: 680 */    0xBA /* ':' -> */,
+/* pos 046a: 681 */    0x00, 0x53                  /* - terminal marker 83 - */,
+/* total size 1132 bytes */
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 692414f009a96e49902353b74320f96c8710e77d..4fe4b2efd5201af0e87460752f8fbd89ac00ba06 100755
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -111,6 +111,15 @@ lws_free_wsi(struct lws *wsi)
 	wsi->peer = NULL;
 #endif
 
+#if defined(LWS_WITH_HTTP2)
+	if (wsi->upgraded_to_http2 || wsi->http2_substream) {
+		lws_hpack_destroy_dynamic_header(wsi);
+
+		if (wsi->u.h2.h2n)
+			lws_free_set_NULL(wsi->u.h2.h2n);
+	}
+#endif
+
 	lws_pt_unlock(pt);
 
 	/* since we will destroy the wsi, make absolutely sure now */
@@ -342,13 +351,79 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 		wsi->child_list = NULL;
 	}
 
+#if defined(LWS_WITH_HTTP2)
+
+	if (wsi->u.h2.parent_wsi) {
+		lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi, wsi->u.h2.parent_wsi);
+		lws_start_foreach_llp(struct lws **, w, wsi->u.h2.parent_wsi->u.h2.child_list) {
+			lwsl_info("   \\---- child %p\n", *w);
+		} lws_end_foreach_llp(w, u.h2.sibling_list);
+	}
+
+	if (wsi->upgraded_to_http2 || wsi->http2_substream) {
+		lwsl_info("closing %p: parent %p\n", wsi, wsi->u.h2.parent_wsi);
+
+		if (wsi->u.h2.child_list) {
+			lwsl_info(" parent %p: closing children: list:\n", wsi);
+			lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) {
+				lwsl_info("   \\---- child %p\n", *w);
+			} lws_end_foreach_llp(w, u.h2.sibling_list);
+			/* trigger closing of all of our http2 children first */
+			lws_start_foreach_llp(struct lws **, w, wsi->u.h2.child_list) {
+				lwsl_info("   closing child %p\n", *w);
+				/* disconnect from siblings */
+				wsi2 = (*w)->u.h2.sibling_list;
+				(*w)->u.h2.sibling_list = NULL;
+				(*w)->socket_is_permanently_unusable = 1;
+				lws_close_free_wsi(*w, reason);
+				*w = wsi2;
+				continue;
+			} lws_end_foreach_llp(w, u.h2.sibling_list);
+		}
+	}
+
+	if (wsi->upgraded_to_http2) {
+		/* remove pps */
+		struct lws_h2_protocol_send *w = wsi->u.h2.h2n->pps, *w1;
+		while (w) {
+			w1 = wsi->u.h2.h2n->pps->next;
+			free(w);
+			w = w1;
+		}
+		wsi->u.h2.h2n->pps = NULL;
+	}
+
+	if (wsi->http2_substream && wsi->u.h2.parent_wsi) {
+		lwsl_info("  %p: disentangling from siblings\n", wsi);
+		lws_start_foreach_llp(struct lws **, w,
+				wsi->u.h2.parent_wsi->u.h2.child_list) {
+			/* disconnect from siblings */
+			if (*w == wsi) {
+				wsi2 = (*w)->u.h2.sibling_list;
+				(*w)->u.h2.sibling_list = NULL;
+				*w = wsi2;
+				lwsl_info("  %p disentangled from sibling %p\n", wsi, wsi2);
+				break;
+			}
+		} lws_end_foreach_llp(w, u.h2.sibling_list);
+		wsi->u.h2.parent_wsi->u.h2.child_count--;
+		wsi->u.h2.parent_wsi = NULL;
+		if (wsi->u.h2.pending_status_body)
+			lws_free_set_NULL(wsi->u.h2.pending_status_body);
+	}
+
+	if (wsi->upgraded_to_http2 && wsi->u.h2.h2n &&
+	    wsi->u.h2.h2n->rx_scratch)
+		lws_free_set_NULL(wsi->u.h2.h2n->rx_scratch);
+#endif
+
 	if (wsi->mode == LWSCM_RAW_FILEDESC) {
-			lws_remove_child_from_any_parent(wsi);
-			remove_wsi_socket_from_fds(wsi);
-			wsi->protocol->callback(wsi,
-						LWS_CALLBACK_RAW_CLOSE_FILE,
-						wsi->user_space, NULL, 0);
-			goto async_close;
+		lws_remove_child_from_any_parent(wsi);
+		remove_wsi_socket_from_fds(wsi);
+		wsi->protocol->callback(wsi,
+					LWS_CALLBACK_RAW_CLOSE_FILE,
+					wsi->user_space, NULL, 0);
+		goto async_close;
 	}
 
 #ifdef LWS_WITH_CGI
@@ -394,7 +469,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 		goto just_kill_connection;
 	}
 
-	if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
+	if ((wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED ||
+	     wsi->mode == LWSCM_HTTP2_SERVING) &&
 	    wsi->u.http.fop_fd != NULL) {
 		lws_vfs_file_close(&wsi->u.http.fop_fd);
 		wsi->vhost->protocols->callback(wsi,
@@ -438,7 +514,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	    wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
 		goto just_kill_connection;
 
-	if (wsi->mode == LWSCM_HTTP_SERVING) {
+	if (wsi->mode == LWSCM_HTTP_SERVING ||
+	    wsi->mode == LWSCM_HTTP2_SERVING) {
 		if (wsi->user_space)
 			wsi->vhost->protocols->callback(wsi,
 						LWS_CALLBACK_HTTP_DROP_PROTOCOL,
@@ -536,6 +613,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 just_kill_connection:
 
 	lws_remove_child_from_any_parent(wsi);
+	n = 0;
 
 	if (wsi->user_space) {
 	    lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
@@ -576,8 +654,6 @@ just_kill_connection:
 	    reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
 	    !wsi->socket_is_permanently_unusable) {
 #ifdef LWS_OPENSSL_SUPPORT
-		int shutdown_error;
-
 		if (lws_is_ssl(wsi) && wsi->ssl) {
 			n = SSL_shutdown(wsi->ssl);
 			/*
@@ -592,30 +668,28 @@ just_kill_connection:
 				n = shutdown(wsi->desc.sockfd, SHUT_WR);
 				break;
 			default:
-				shutdown_error = SSL_get_error(wsi->ssl, n);
-				lwsl_debug("SSL_shutdown %d, get_error: %d\n",
-					   n, shutdown_error);
-				switch (shutdown_error) {
-				case SSL_ERROR_WANT_READ:
-					lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
+				if (SSL_want_read(wsi->ssl)) {
+					lws_change_pollfd(wsi, 0, LWS_POLLIN);
 					n = 0;
 					break;
-				case SSL_ERROR_WANT_WRITE:
-					lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLOUT);
+				}
+				if (SSL_want_write(wsi->ssl)) {
+					lws_change_pollfd(wsi, 0, LWS_POLLOUT);
 					n = 0;
 					break;
-				default: /* real error, close */
-					n = shutdown(wsi->desc.sockfd, SHUT_WR);
-					break;
 				}
+				n = shutdown(wsi->desc.sockfd, SHUT_WR);
+				break;
 			}
 		} else
 #endif
 		{
-			lwsl_info("%s: shuttdown conn: %p (sock %d, state %d)\n",
+			lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n",
 				  __func__, wsi, (int)(long)wsi->desc.sockfd,
 				  wsi->state);
-			n = shutdown(wsi->desc.sockfd, SHUT_WR);
+			if (!wsi->socket_is_permanently_unusable &&
+			    lws_sockfd_valid(wsi->desc.sockfd))
+				n = shutdown(wsi->desc.sockfd, SHUT_WR);
 		}
 		if (n)
 			lwsl_debug("closing: shutdown (state %d) ret %d\n",
@@ -627,7 +701,9 @@ just_kill_connection:
 		 */
 #if !defined(_WIN32_WCE) && !defined(LWS_WITH_ESP32)
 		/* libuv: no event available to guarantee completion */
-		if (!LWS_LIBUV_ENABLED(context)) {
+		if (!wsi->socket_is_permanently_unusable &&
+		    lws_sockfd_valid(wsi->desc.sockfd) &&
+		    !LWS_LIBUV_ENABLED(context)) {
 			lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
 			wsi->state = LWSS_SHUTDOWN;
 			lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
@@ -716,11 +792,14 @@ just_kill_connection:
 	if (!wsi->told_user_closed &&
 	    wsi->mode != LWSCM_RAW && wsi->protocol &&
 	    wsi->protocol->callback &&
-	    ((wsi->state_pre_close == LWSS_ESTABLISHED) ||
-	    (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
-	    (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
-	    (wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) ||
-	    (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
+	    (wsi->state_pre_close == LWSS_ESTABLISHED ||
+	     wsi->state_pre_close == LWSS_HTTP2_ESTABLISHED ||
+	     wsi->state_pre_close == LWSS_HTTP_BODY ||
+	     wsi->state_pre_close == LWSS_HTTP ||
+	     wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY ||
+	     wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK ||
+	     wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
+	     wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE ||
 	    (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
 	    (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
 		lwsl_debug("calling back CLOSED %d %d\n", wsi->mode, wsi->state);
@@ -750,7 +829,8 @@ async_close:
 	wsi->socket_is_permanently_unusable = 1;
 
 #ifdef LWS_WITH_LIBUV
-	if (!wsi->parent_carries_io)
+	if (!wsi->parent_carries_io &&
+	    lws_sockfd_valid(wsi->desc.sockfd))
 		if (LWS_LIBUV_ENABLED(context)) {
 			if (wsi->listener) {
 				lwsl_debug("%s: stop listener poll\n", __func__);
@@ -776,7 +856,7 @@ lws_close_free_wsi_final(struct lws *wsi)
 {
 	int n;
 
-	if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->desc.sockfd)) {
+	if (lws_socket_is_valid(wsi->desc.sockfd) && !lws_ssl_close(wsi)) {
 #if LWS_POSIX
 		n = compatible_close(wsi->desc.sockfd);
 		if (n)
@@ -941,6 +1021,11 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen)
 	int af = AF_INET;
 	void *p, *q;
 
+#if defined(LWS_WITH_HTTP2)
+	if (wsi->http2_substream)
+		wsi = wsi->u.h2.parent_wsi;
+#endif
+
 	if (wsi->parent_carries_io)
 		wsi = wsi->parent;
 
@@ -1056,6 +1141,23 @@ lws_protocol_get(struct lws *wsi)
 	return wsi->protocol;
 }
 
+LWS_VISIBLE struct lws *
+lws_get_network_wsi(struct lws *wsi)
+{
+	if (!wsi)
+		return NULL;
+
+#if defined(LWS_WITH_HTTP2)
+	if (!wsi->http2_substream)
+		return wsi;
+
+	while (wsi->u.h2.parent_wsi)
+		wsi = wsi->u.h2.parent_wsi;
+#endif
+
+	return wsi;
+}
+
 LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
 lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name)
 {
@@ -1309,13 +1411,40 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
 #endif
 
 LWS_VISIBLE int
-lws_rx_flow_control(struct lws *wsi, int enable)
+lws_rx_flow_control(struct lws *wsi, int _enable)
 {
-	if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
+	int en = _enable;
+
+	lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable);
+
+	if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) {
+		/*
+		 * convert user bool style to bitmap style... in user simple
+		 * bool style _enable = 0 = flow control it, = 1 = allow rx
+		 */
+		en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL;
+		if (_enable & 1)
+			en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT;
+	}
+
+	/* any bit set in rxflow_bitmap DISABLEs rxflow control */
+	if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT)
+		wsi->rxflow_bitmap &= ~(en & 0xff);
+	else
+		wsi->rxflow_bitmap |= en & 0xff;
+
+	if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) ==
+	    wsi->rxflow_change_to)
 		return 0;
 
-	lwsl_info("%s: (0x%p, %d)\n", __func__, wsi, enable);
-	wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
+	wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !wsi->rxflow_bitmap;
+
+	lwsl_info("%s: 0x%p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi,
+		  wsi->rxflow_bitmap, en, wsi->rxflow_change_to);
+
+	if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW ||
+	    !wsi->rxflow_will_be_applied)
+		return _lws_rx_flow_control(wsi);
 
 	return 0;
 }
@@ -1353,7 +1482,9 @@ int user_callback_handle_rxflow(lws_callback_function callback_function,
 {
 	int n;
 
+	wsi->rxflow_will_be_applied = 1;
 	n = callback_function(wsi, reason, user, in, len);
+	wsi->rxflow_will_be_applied = 0;
 	if (!n)
 		n = _lws_rx_flow_control(wsi);
 
@@ -1532,13 +1663,13 @@ int
 lws_ensure_user_space(struct lws *wsi)
 {
 	if (!wsi->protocol)
-		return 1;
+		return 0;
 
 	/* allocate the per-connection user memory (if any) */
 
 	if (wsi->protocol->per_session_data_size && !wsi->user_space) {
 		wsi->user_space = lws_zalloc(wsi->protocol->per_session_data_size, "user space");
-		if (wsi->user_space  == NULL) {
+		if (wsi->user_space == NULL) {
 			lwsl_err("%s: OOM\n", __func__);
 			return 1;
 		}
@@ -1606,14 +1737,44 @@ lwsl_timestamp(int level, char *p, int len)
 	return 0;
 }
 
+static const char * const colours[] = {
+	"[31;1m", /* LLL_ERR */
+	"[36;1m", /* LLL_WARN */
+	"[35;1m", /* LLL_NOTICE */
+	"[32;1m", /* LLL_INFO */
+	"[34;1m", /* LLL_DEBUG */
+	"[33;1m", /* LLL_PARSER */
+	"[33;1m", /* LLL_HEADER */
+	"[33;1m", /* LLL_EXT */
+	"[33;1m", /* LLL_CLIENT */
+	"[33;1m", /* LLL_LATENCY */
+	"[30;1m", /* LLL_USER */
+};
+
 #ifndef LWS_PLAT_OPTEE
 LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
 {
 #if !defined(LWS_WITH_ESP8266)
 	char buf[50];
+	static char tty;
+	int n, m = ARRAY_SIZE(colours) - 1;
+
+	if (!tty)
+		tty = isatty(2) | 2;
 
 	lwsl_timestamp(level, buf, sizeof(buf));
-	fprintf(stderr, "%s%s", buf, line);
+
+	if (tty == 3) {
+		n = 1 << (ARRAY_SIZE(colours) - 1);
+		while (n) {
+			if (level & n)
+				break;
+			m--;
+			n >>= 1;
+		}
+		fprintf(stderr, "%c%s%s%s%c[0m", 27, colours[m], buf, line, 27);
+	} else
+		fprintf(stderr, "%s%s", buf, line);
 #endif
 }
 #endif
@@ -1692,18 +1853,6 @@ lws_partial_buffered(struct lws *wsi)
 	return !!wsi->trunc_len;
 }
 
-void lws_set_protocol_write_pending(struct lws *wsi,
-				    enum lws_pending_protocol_send pend)
-{
-	lwsl_info("setting pps %d\n", pend);
-
-	if (wsi->pps)
-		lwsl_err("pps overwrite\n");
-	wsi->pps = pend;
-	lws_rx_flow_control(wsi, 0);
-	lws_callback_on_writable(wsi);
-}
-
 LWS_VISIBLE size_t
 lws_get_peer_write_allowance(struct lws *wsi)
 {
@@ -1711,11 +1860,8 @@ lws_get_peer_write_allowance(struct lws *wsi)
 	/* only if we are using HTTP2 on this connection */
 	if (wsi->mode != LWSCM_HTTP2_SERVING)
 		return -1;
-	/* user is only interested in how much he can send, or that he can't  */
-	if (wsi->u.http2.tx_credit <= 0)
-		return 0;
 
-	return wsi->u.http2.tx_credit;
+	return lws_h2_tx_cr_get(wsi);
 #else
 	(void)wsi;
 	return -1;
@@ -2591,9 +2737,15 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 			WSI_TOKEN_PUT_URI,
 			WSI_TOKEN_PATCH_URI,
 			WSI_TOKEN_DELETE_URI,
+			WSI_TOKEN_CONNECT,
+			WSI_TOKEN_HEAD_URI,
+		#ifdef LWS_WITH_HTTP2
+			WSI_TOKEN_HTTP_COLON_PATH,
+		#endif
 		};
 		static const char * const meth_names[] = {
 			"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
+			"CONNECT", ":path"
 		};
 
 		if (script_uri_path_len >= 0)
@@ -2609,15 +2761,23 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len
 //		if (script_uri_path_len < 0)
 //			uritok = 0;
 
-		lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
-			 lws_hdr_simple_ptr(wsi, uritok));
-		cgi_path[sizeof(cgi_path) - 1] = '\0';
-		env_array[n++] = cgi_path;
+		if (uritok >= 0) {
+			lws_snprintf(cgi_path, sizeof(cgi_path) - 1, "REQUEST_URI=%s",
+				 lws_hdr_simple_ptr(wsi, uritok));
+			cgi_path[sizeof(cgi_path) - 1] = '\0';
+			env_array[n++] = cgi_path;
+		}
 
-		env_array[n++] = p;
-		p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s",
-			      meth_names[m]);
-		p++;
+		if (m >= 0) {
+			env_array[n++] = p;
+			if (m < 8)
+				p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s",
+						meth_names[m]);
+			else
+				p += lws_snprintf(p, end - p, "REQUEST_METHOD=%s",
+						lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD));
+			p++;
+		}
 
 		env_array[n++] = p;
 		p += lws_snprintf(p, end - p, "QUERY_STRING=");
@@ -2829,13 +2989,21 @@ static const char * const significant_hdr[SIGNIFICANT_HDR_COUNT] = {
 	"transfer-encoding: chunked",
 };
 
+enum header_recode {
+	HR_NAME,
+	HR_WHITESPACE,
+	HR_ARG,
+	HR_CRLF,
+};
+
 LWS_VISIBLE LWS_EXTERN int
 lws_cgi_write_split_stdout_headers(struct lws *wsi)
 {
-	int n, m;
+	int n, m, cmd;
 	unsigned char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
-	     *end = &buf[sizeof(buf) - 1 - LWS_PRE];
-	char c;
+			*end = &buf[sizeof(buf) - 1 - LWS_PRE], *name,
+			*value = NULL;
+	char c, hrs;
 
 	if (!wsi->cgi)
 		return -1;
@@ -2846,10 +3014,9 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 		 * since they need to be handled separately
 		 */
 		switch (wsi->hdr_state) {
-
 		case LHCS_RESPONSE:
-			lwsl_info("LHCS_RESPONSE: issuing response %d\n",
-					wsi->cgi->response_code);
+			lwsl_debug("LHCS_RESPONSE: issuing response %d\n",
+				   wsi->cgi->response_code);
 			if (lws_add_http_header_status(wsi, wsi->cgi->response_code,
 						       &p, end))
 				return 1;
@@ -2859,12 +3026,93 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 					WSI_TOKEN_HTTP_TRANSFER_ENCODING,
 					(unsigned char *)"chunked", 7, &p, end))
 				return 1;
-			if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
-					(unsigned char *)"close", 5, &p, end))
-				return 1;
+			if (!(wsi->http2_substream))
+				if (lws_add_http_header_by_token(wsi, WSI_TOKEN_CONNECTION,
+						(unsigned char *)"close", 5, &p, end))
+					return 1;
 			n = lws_write(wsi, start, p - start,
-				      LWS_WRITE_HTTP_HEADERS);
+				      LWS_WRITE_HTTP_HEADERS | LWS_WRITE_NO_FIN);
 
+			/*
+			 * so we have a bunch of http/1 style ascii headers
+			 * starting from wsi->cgi->headers_buf through
+			 * wsi->cgi->headers_pos.  These are OK for http/1
+			 * connections, but they're no good for http/2 conns.
+			 *
+			 * Let's redo them at headers_pos forward using the
+			 * correct coding for http/1 or http/2
+			 */
+			if (!wsi->http2_substream)
+				goto post_hpack_recode;
+
+			p = wsi->cgi->headers_start;
+			wsi->cgi->headers_start = wsi->cgi->headers_pos;
+			wsi->cgi->headers_dumped = wsi->cgi->headers_start;
+			hrs = HR_NAME;
+			name = buf;
+
+			while (p < wsi->cgi->headers_start) {
+				//lwsl_notice("%c (%d)\n", *p, (int)(wsi->cgi->headers_start - p));
+				switch (hrs) {
+				case HR_NAME:
+					/*
+					 * in http/2 upper-case header names
+					 * are illegal.  So convert to lower-
+					 * case.
+					 */
+					if (name - buf > 64)
+						return -1;
+					if (*p != ':') {
+						if (*p >= 'A' && *p <= 'Z')
+							*name++ = (*p++) + ('a' - 'A');
+						else
+							*name++ = *p++;
+					} else {
+						p++;
+						*name++ = '\0';
+						value = name;
+						hrs = HR_WHITESPACE;
+					}
+					break;
+				case HR_WHITESPACE:
+					if (*p == ' ') {
+						p++;
+						break;
+					}
+					hrs = HR_ARG;
+					/* fallthru */
+				case HR_ARG:
+					if (name > end - 64)
+						return -1;
+
+					if (*p != '\x0a' && *p != '\x0d') {
+						*name++ = *p++;
+						break;
+					}
+					hrs = HR_CRLF;
+					/* fallthru */
+				case HR_CRLF:
+					if ((*p != '\x0a' && *p != '\x0d') ||
+					    p + 1 == wsi->cgi->headers_start) {
+						*name = '\0';
+						if ((strcmp((const char *)buf, "transfer-encoding")
+						)) {
+							lwsl_debug("adding header %s: %s\n", buf, value);
+							if (lws_add_http_header_by_name(wsi, buf,
+								(unsigned char *)value, name - value,
+								(unsigned char **)&wsi->cgi->headers_pos,
+								(unsigned char *)wsi->cgi->headers_end))
+								return 1;
+							hrs = HR_NAME;
+							name = buf;
+							break;
+						}
+					}
+					p++;
+					break;
+				}
+			}
+post_hpack_recode:
 			/* finalize cached headers before dumping them */
 			if (lws_finalize_http_header(wsi,
 					(unsigned char **)&wsi->cgi->headers_pos,
@@ -2888,8 +3136,14 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 
 			lwsl_debug("LHCS_DUMP_HEADERS: %d\n", n);
 
+			cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION;
+			if (wsi->cgi->headers_dumped + n != wsi->cgi->headers_pos) {
+				lwsl_notice("adding no fin flag\n");
+				cmd |= LWS_WRITE_NO_FIN;
+			}
+
 			m = lws_write(wsi, (unsigned char *)wsi->cgi->headers_dumped,
-				      n, LWS_WRITE_HTTP_HEADERS);
+				      n, cmd);
 			if (m < 0) {
 				lwsl_debug("%s: write says %d\n", __func__, m);
 				return -1;
@@ -2913,14 +3167,18 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 		if (!wsi->cgi->headers_buf) {
 			/* if we don't already have a headers buf, cook one up */
 			n = 2048;
-			wsi->cgi->headers_buf = lws_malloc(n, "cgi hdr buf");
+			if (wsi->http2_substream)
+				n = 4096;
+			wsi->cgi->headers_buf = lws_malloc(n + LWS_PRE,
+							   "cgi hdr buf");
 			if (!wsi->cgi->headers_buf) {
 				lwsl_err("OOM\n");
 				return -1;
 			}
 
 			lwsl_debug("allocated cgi hdrs\n");
-			wsi->cgi->headers_pos = wsi->cgi->headers_buf;
+			wsi->cgi->headers_start = wsi->cgi->headers_buf + LWS_PRE;
+			wsi->cgi->headers_pos = wsi->cgi->headers_start;
 			wsi->cgi->headers_dumped = wsi->cgi->headers_pos;
 			wsi->cgi->headers_end = wsi->cgi->headers_buf + n - 1;
 
@@ -3071,7 +3329,7 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 		return -1;
 	}
 	if (n > 0) {
-		if (m) {
+		if (!wsi->http2_substream && m) {
 			char chdr[LWS_HTTP_CHUNK_HDR_SIZE];
 			m = lws_snprintf(chdr, LWS_HTTP_CHUNK_HDR_SIZE - 3,
 					 "%X\x0d\x0a", n);
@@ -3080,16 +3338,22 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi)
 			memcpy(start + m + n, "\x0d\x0a", 2);
 			n += m + 2;
 		}
-		m = lws_write(wsi, (unsigned char *)start, n, LWS_WRITE_HTTP);
+		cmd = LWS_WRITE_HTTP;
+		if (wsi->cgi->content_length_seen + n == wsi->cgi->content_length)
+			cmd = LWS_WRITE_HTTP_FINAL;
+		m = lws_write(wsi, (unsigned char *)start, n, cmd);
 		//lwsl_notice("write %d\n", m);
 		if (m < 0) {
 			lwsl_debug("%s: stdout write says %d\n", __func__, m);
 			return -1;
 		}
-		wsi->cgi->content_length_seen += m;
+		wsi->cgi->content_length_seen += n;
 	} else {
 		if (wsi->cgi_stdout_zero_length) {
 			lwsl_debug("%s: stdout is POLLHUP'd\n", __func__);
+			if (wsi->http2_substream)
+				m = lws_write(wsi, (unsigned char *)start, 0,
+					      LWS_WRITE_HTTP_FINAL);
 			return 1;
 		}
 		wsi->cgi_stdout_zero_length = 1;
@@ -3385,10 +3649,13 @@ lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs)
 
 		cs->rx += vh->conn_stats.rx;
 		cs->tx += vh->conn_stats.tx;
-		cs->conn += vh->conn_stats.conn;
-		cs->trans += vh->conn_stats.trans;
+		cs->h1_conn += vh->conn_stats.h1_conn;
+		cs->h1_trans += vh->conn_stats.h1_trans;
+		cs->h2_trans += vh->conn_stats.h2_trans;
 		cs->ws_upg += vh->conn_stats.ws_upg;
-		cs->http2_upg += vh->conn_stats.http2_upg;
+		cs->h2_upg += vh->conn_stats.h2_upg;
+		cs->h2_alpn += vh->conn_stats.h2_alpn;
+		cs->h2_subs += vh->conn_stats.h2_subs;
 		cs->rejected += vh->conn_stats.rejected;
 
 		vh = vh->vhost_next;
@@ -3422,11 +3689,14 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 			" \"sts\":\"%d\",\n"
 			" \"rx\":\"%llu\",\n"
 			" \"tx\":\"%llu\",\n"
-			" \"conn\":\"%lu\",\n"
-			" \"trans\":\"%lu\",\n"
+			" \"h1_conn\":\"%lu\",\n"
+			" \"h1_trans\":\"%lu\",\n"
+			" \"h2_trans\":\"%lu\",\n"
 			" \"ws_upg\":\"%lu\",\n"
 			" \"rejected\":\"%lu\",\n"
-			" \"http2_upg\":\"%lu\""
+			" \"h2_upg\":\"%lu\",\n"
+			" \"h2_alpn\":\"%lu\",\n"
+			" \"h2_subs\":\"%lu\""
 			,
 			vh->name, vh->listen_port,
 #ifdef LWS_OPENSSL_SUPPORT
@@ -3436,10 +3706,14 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 #endif
 			!!(vh->options & LWS_SERVER_OPTION_STS),
 			vh->conn_stats.rx, vh->conn_stats.tx,
-			vh->conn_stats.conn, vh->conn_stats.trans,
+			vh->conn_stats.h1_conn,
+			vh->conn_stats.h1_trans,
+			vh->conn_stats.h2_trans,
 			vh->conn_stats.ws_upg,
 			vh->conn_stats.rejected,
-			vh->conn_stats.http2_upg
+			vh->conn_stats.h2_upg,
+			vh->conn_stats.h2_alpn,
+			vh->conn_stats.h2_subs
 	);
 
 	if (vh->mount_list) {
@@ -3598,14 +3872,23 @@ lws_json_dump_context(const struct lws_context *context, char *buf, int len,
 			"],\n\"listen_wsi\":\"%d\",\n"
 			" \"rx\":\"%llu\",\n"
 			" \"tx\":\"%llu\",\n"
-			" \"conn\":\"%lu\",\n"
-			" \"trans\":\"%lu\",\n"
+			" \"h1_conn\":\"%lu\",\n"
+			" \"h1_trans\":\"%lu\",\n"
+			" \"h2_trans\":\"%lu\",\n"
 			" \"ws_upg\":\"%lu\",\n"
 			" \"rejected\":\"%lu\",\n"
-			" \"http2_upg\":\"%lu\"",
-			listening,
-			cs.rx, cs.tx, cs.conn, cs.trans,
-			cs.ws_upg, cs.rejected, cs.http2_upg);
+			" \"h2_alpn\":\"%lu\",\n"
+			" \"h2_subs\":\"%lu\",\n"
+			" \"h2_upg\":\"%lu\"",
+			listening, cs.rx, cs.tx,
+			cs.h1_conn,
+			cs.h1_trans,
+			cs.h2_trans,
+			cs.ws_upg,
+			cs.rejected,
+			cs.h2_alpn,
+			cs.h2_subs,
+			cs.h2_upg);
 
 #ifdef LWS_WITH_CGI
 	for (n = 0; n < context->count_threads; n++) {
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 1d1daf0e3183e175661bbcd147e2ab370ea469cc..5d358fd00a1587ee3882a7e3ee307a8ccdbe8792 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -2346,9 +2346,17 @@ struct lws_context_creation_info {
 	/**< CONTEXT: max number of wsi a single IP may use simultaneously.
 	 *	      0 is no limit.  This is a hard limit, connections from
 	 *	      the same IP will simply be dropped once it acquires the
-	 *	      amount of simultanoues wsi / accepted connections
+	 *	      amount of simultaneous wsi / accepted connections
 	 *	      given here.
 	 */
+	uint32_t	http2_settings[7];
+	/**< CONTEXT: after context creation http2_settings[1] thru [6] have
+	 *	      been set to the lws platform default values.
+	 *   VHOST:   if http2_settings[0] is nonzero, the values given in
+	 *	      http2_settings[1]..[6] are used instead of the lws
+	 *	      platform default values.
+	 *	      Just leave all at 0 if you don't care.
+	 */
 
 	/* Add new things just above here ---^
 	 * This is part of the ABI, don't needlessly break compatibility
@@ -3367,6 +3375,8 @@ enum lws_token_indexes {
 	WSI_TOKEN_HTTP1_0					= 79,
 	WSI_TOKEN_X_FORWARDED_FOR				= 80,
 	WSI_TOKEN_CONNECT					= 81,
+	WSI_TOKEN_HEAD_URI					= 82,
+	WSI_TOKEN_TE						= 83,
 	/****** add new things just above ---^ ******/
 
 	/* use token storage to stash these internally, not for
@@ -3403,7 +3413,6 @@ struct lws_token_limits {
 LWS_VISIBLE LWS_EXTERN const unsigned char *
 lws_token_to_string(enum lws_token_indexes token);
 
-
 /**
  * lws_hdr_total_length: report length of all fragments of a header totalled up
  *		The returned length does not include the space for a
@@ -3735,6 +3744,9 @@ lws_urlencode(char *escaped, const char *string, int len);
  *
  * Since urldecoding only shrinks the output string, it is possible to
  * do it in-place, ie, string == escaped
+ *
+ * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars
+ * where hex required, etc)
  */
 LWS_VISIBLE LWS_EXTERN int
 lws_urldecode(char *string, const char *escaped, int len);
@@ -4029,6 +4041,9 @@ enum lws_write_protocol {
 	 * to be compatible with both in the future,header response part should
 	 * be sent using this regardless of http version expected)
 	 */
+	LWS_WRITE_HTTP_HEADERS_CONTINUATION			= 9,
+	/**< Continuation of http/2 headers
+	 */
 
 	/****** add new things just above ---^ ******/
 
@@ -4037,6 +4052,11 @@ enum lws_write_protocol {
 	LWS_WRITE_NO_FIN = 0x40,
 	/**< This part of the message is not the end of the message */
 
+	LWS_WRITE_H2_STREAM_END = 0x80,
+	/**< Flag indicates this packet should go out with STREAM_END if h2
+	 * STREAM_END is allowed on DATA or HEADERS.
+	 */
+
 	LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80
 	/**< client packet payload goes out on wire unmunged
 	 * only useful for security tests since normal servers cannot
@@ -4328,6 +4348,24 @@ LWS_VISIBLE LWS_EXTERN size_t
 lws_get_peer_write_allowance(struct lws *wsi);
 ///@}
 
+enum {
+	/*
+	 * Flags for enable and disable rxflow with reason bitmap and with
+	 * backwards-compatible single bool
+	 */
+	LWS_RXFLOW_REASON_USER_BOOL		= (1 << 0),
+	LWS_RXFLOW_REASON_HTTP_RXBUFFER		= (1 << 6),
+	LWS_RXFLOW_REASON_H2_PPS_PENDING	= (1 << 7),
+
+	LWS_RXFLOW_REASON_APPLIES		= (1 << 14),
+	LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT	= (1 << 13),
+	LWS_RXFLOW_REASON_APPLIES_ENABLE	= LWS_RXFLOW_REASON_APPLIES |
+						  LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT,
+	LWS_RXFLOW_REASON_APPLIES_DISABLE	= LWS_RXFLOW_REASON_APPLIES,
+	LWS_RXFLOW_REASON_FLAG_PROCESS_NOW	= (1 << 12),
+
+};
+
 /**
  * lws_rx_flow_control() - Enable and disable socket servicing for
  *				received packets.
@@ -4337,6 +4375,15 @@ lws_get_peer_write_allowance(struct lws *wsi);
  *
  * \param wsi:	Websocket connection instance to get callback for
  * \param enable:	0 = disable read servicing for this connection, 1 = enable
+ *
+ * If you need more than one additive reason for rxflow control, you can give
+ * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of
+ * b5..b0 set to idicate which bits to enable or disable.  If any bits are
+ * enabled, rx on the connection is suppressed.
+ *
+ * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW  flag may also be given to force any change
+ * in rxflowbstatus to benapplied immediately, this should be used when you are
+ * changing a wsi flow control state from outside a callback on that wsi.
  */
 LWS_VISIBLE LWS_EXTERN int
 lws_rx_flow_control(struct lws *wsi, int enable);
@@ -4651,222 +4698,6 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
 	} \
 }
 
-/**
- * lws_ring: generic ringbuffer struct
- *
- * all of the members are opaque and manipulated by lws_ring_...() apis.
- *
- * The lws_ring and its buffer is allocated at runtime on the heap, using
- *
- *  - lws_ring_create()
- *  - lws_ring_destroy()
- *
- * It may contain any type, the size of the "element" stored in the ring
- * buffer and the number of elements is given at creation time.
- *
- * Whole elements may be inserted into the ringbuffer and removed from it, using
- *
- *  - lws_ring_insert()
- *  - lws_ring_consume()
- *
- * You can find out how many whole elements are free or waiting using
- *
- *  - lws_ring_get_count_free_elements()
- *  - lws_ring_get_count_waiting_elements()
- *
- * In addition there are special purpose optional byte-centric apis
- *
- *  - lws_ring_next_linear_insert_range()
- *  - lws_ring_bump_head()
- *
- *  which let you, eg, read() directly into the ringbuffer without needing
- *  an intermediate bounce buffer.
- *
- *  The accessors understand that the ring wraps, and optimizes insertion and
- *  consumption into one or two memcpy()s depending on if the head or tail
- *  wraps.
- *
- *  lws_ring only supports a single head, but optionally multiple tails with
- *  an API to inform it when the "oldest" tail has moved on.  You can give
- *  NULL where-ever an api asks for a tail pointer, and it will use an internal
- *  single tail pointer for convenience.
- *
- *  The "oldest tail", which is the only tail if you give it NULL instead of
- *  some other tail, is used to track which elements in the ringbuffer are
- *  still unread by anyone.
- *
- *   - lws_ring_update_oldest_tail()
- */
-struct lws_ring;
-
-/**
- * lws_ring_create(): create a new ringbuffer
- *
- * \param element_len: the size in bytes of one element in the ringbuffer
- * \param count: the number of elements the ringbuffer can contain
- * \param destroy_element: NULL, or callback to be called for each element
- *			   that is removed from the ringbuffer due to the
- *			   oldest tail moving beyond it
- *
- * Creates the ringbuffer and allocates the storage.  Returns the new
- * lws_ring *, or NULL if the allocation failed.
- *
- * If non-NULL, destroy_element will get called back for every element that is
- * retired from the ringbuffer after the oldest tail has gone past it, and for
- * any element still left in the ringbuffer when it is destroyed.  It replaces
- * all other element destruction code in your user code.
- */
-LWS_VISIBLE LWS_EXTERN struct lws_ring *
-lws_ring_create(size_t element_len, size_t count,
-		void (*destroy_element)(void *element));
-
-/**
- * lws_ring_destroy():  destroy a previously created ringbuffer
- *
- * \param ring: the struct lws_ring to destroy
- *
- * Destroys the ringbuffer allocation and the struct lws_ring itself.
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_ring_destroy(struct lws_ring *ring);
-
-/**
- * lws_ring_get_count_free_elements():  return how many elements can fit
- *				      in the free space
- *
- * \param ring: the struct lws_ring to report on
- *
- * Returns how much room is left in the ringbuffer for whole element insertion.
- */
-LWS_VISIBLE LWS_EXTERN size_t
-lws_ring_get_count_free_elements(struct lws_ring *ring);
-
-/**
- * lws_ring_get_count_waiting_elements():  return how many elements can be consumed
- *
- * \param ring: the struct lws_ring to report on
- * \param tail: a pointer to the tail struct to use, or NULL for single tail
- *
- * Returns how many elements are waiting to be consumed from the perspective
- * of the tail pointer given.
- */
-LWS_VISIBLE LWS_EXTERN size_t
-lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail);
-
-/**
- * lws_ring_insert():  attempt to insert up to max_count elements from src
- *
- * \param ring: the struct lws_ring to report on
- * \param src: the array of elements to be inserted
- * \param max_count: the number of available elements at src
- *
- * Attempts to insert as many of the elements at src as possible, up to the
- * maximum max_count.  Returns the number of elements actually inserted.
- */
-LWS_VISIBLE LWS_EXTERN size_t
-lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count);
-
-/**
- * lws_ring_consume():  attempt to copy out and remove up to max_count elements
- *		        to src
- *
- * \param ring: the struct lws_ring to report on
- * \param tail: a pointer to the tail struct to use, or NULL for single tail
- * \param dest: the array of elements to be inserted. or NULL for no copy
- * \param max_count: the number of available elements at src
- *
- * Attempts to copy out as many waiting elements as possible into dest, from
- * the perspective of the given tail, up to max_count.  If dest is NULL, the
- * copying out is not done but the elements are logically consumed as usual.
- * NULL dest is useful in combination with lws_ring_get_element(), where you
- * can use the element direct from the ringbuffer and then call this with NULL
- * dest to logically consume it.
- *
- * Increments the tail position according to how many elements could be
- * consumed.
- *
- * Returns the number of elements consumed.
- */
-LWS_VISIBLE LWS_EXTERN size_t
-lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
-		 size_t max_count);
-
-/**
- * lws_ring_get_element():  get a pointer to the next waiting element for tail
- *
- * \param ring: the struct lws_ring to report on
- * \param tail: a pointer to the tail struct to use, or NULL for single tail
- *
- * Points to the next element that tail would consume, directly in the
- * ringbuffer.  This lets you write() or otherwise use the element without
- * having to copy it out somewhere first.
- *
- * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1)
- * which will logically consume the element you used up and increment your
- * tail (tail may also be NULL there if you use a single tail).
- *
- * Returns NULL if no waiting element, or a const void * pointing to it.
- */
-LWS_VISIBLE LWS_EXTERN const void *
-lws_ring_get_element(struct lws_ring *ring, uint32_t *tail);
-
-/**
- * lws_ring_update_oldest_tail():  free up elements older than tail for reuse
- *
- * \param ring: the struct lws_ring to report on
- * \param tail: a pointer to the tail struct to use, or NULL for single tail
- *
- * If you are using multiple tails, you must use this API to inform the
- * lws_ring when none of the tails still need elements in the fifo any more,
- * by updating it when the "oldest" tail has moved on.
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail);
-
-/**
- * lws_ring_get_oldest_tail():  get current oldest available data index
- *
- * \param ring: the struct lws_ring to report on
- *
- * If you are initializing a new ringbuffer consumer, you can set its tail to
- * this to start it from the oldest ringbuffer entry still available.
- */
-LWS_VISIBLE LWS_EXTERN uint32_t
-lws_ring_get_oldest_tail(struct lws_ring *ring);
-
-/**
- * lws_ring_next_linear_insert_range():  used to write directly into the ring
- *
- * \param ring: the struct lws_ring to report on
- * \param start: pointer to a void * set to the start of the next ringbuffer area
- * \param bytes: pointer to a size_t set to the max length you may use from *start
- *
- * This provides a low-level, bytewise access directly into the ringbuffer
- * allowing direct insertion of data without having to use a bounce buffer.
- *
- * The api reports the position and length of the next linear range that can
- * be written in the ringbuffer, ie, up to the point it would wrap, and sets
- * *start and *bytes accordingly.  You can then, eg, directly read() into
- * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring
- * with what you have done.
- *
- * Returns nonzero if no insertion is currently possible.
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
-				  size_t *bytes);
-
-
-/**
- * lws_ring_bump_head():  used to write directly into the ring
- *
- * \param ring: the struct lws_ring to operate on
- * \param bytes: the number of bytes you inserted at the current head
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_ring_bump_head(struct lws_ring *ring, size_t bytes);
-
-
 /**
  * lws_snprintf(): snprintf that truncates the returned length too
  *
@@ -5024,6 +4855,19 @@ lws_get_close_length(struct lws *wsi);
 LWS_VISIBLE LWS_EXTERN unsigned char *
 lws_get_close_payload(struct lws *wsi);
 
+/**
+ * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi
+ *
+ * \param wsi: wsi you have
+ *
+ * Returns wsi that has the tcp connection (which may be the incoming wsi)
+ *
+ * HTTP/1 connections will always return the incoming wsi
+ * HTTP/2 connections may return a different wsi that has the tcp connection
+ */
+LWS_VISIBLE LWS_EXTERN
+struct lws *lws_get_network_wsi(struct lws *wsi);
+
 /*
  * \deprecated DEPRECATED Note: this is not normally needed as a user api.
  * It's provided in case it is
@@ -5141,6 +4985,229 @@ lws_get_ssl(struct lws *wsi);
 #endif
 ///@}
 
+/** \defgroup lws_ring LWS Ringbuffer APIs
+ * ##lws_ring: generic ringbuffer struct
+ *
+ * Provides an abstract ringbuffer api supporting one head and one or an
+ * unlimited number of tails.
+ *
+ * All of the members are opaque and manipulated by lws_ring_...() apis.
+ *
+ * The lws_ring and its buffer is allocated at runtime on the heap, using
+ *
+ *  - lws_ring_create()
+ *  - lws_ring_destroy()
+ *
+ * It may contain any type, the size of the "element" stored in the ring
+ * buffer and the number of elements is given at creation time.
+ *
+ * When you create the ringbuffer, you can optionally provide an element
+ * destroy callback that frees any allocations inside the element.  This is then
+ * automatically called for elements with no tail behind them, ie, elements
+ * which don't have any pending consumer are auto-freed.
+ *
+ * Whole elements may be inserted into the ringbuffer and removed from it, using
+ *
+ *  - lws_ring_insert()
+ *  - lws_ring_consume()
+ *
+ * You can find out how many whole elements are free or waiting using
+ *
+ *  - lws_ring_get_count_free_elements()
+ *  - lws_ring_get_count_waiting_elements()
+ *
+ * In addition there are special purpose optional byte-centric apis
+ *
+ *  - lws_ring_next_linear_insert_range()
+ *  - lws_ring_bump_head()
+ *
+ *  which let you, eg, read() directly into the ringbuffer without needing
+ *  an intermediate bounce buffer.
+ *
+ *  The accessors understand that the ring wraps, and optimizes insertion and
+ *  consumption into one or two memcpy()s depending on if the head or tail
+ *  wraps.
+ *
+ *  lws_ring only supports a single head, but optionally multiple tails with
+ *  an API to inform it when the "oldest" tail has moved on.  You can give
+ *  NULL where-ever an api asks for a tail pointer, and it will use an internal
+ *  single tail pointer for convenience.
+ *
+ *  The "oldest tail", which is the only tail if you give it NULL instead of
+ *  some other tail, is used to track which elements in the ringbuffer are
+ *  still unread by anyone.
+ *
+ *   - lws_ring_update_oldest_tail()
+ */
+///@{
+struct lws_ring;
+
+/**
+ * lws_ring_create(): create a new ringbuffer
+ *
+ * \param element_len: the size in bytes of one element in the ringbuffer
+ * \param count: the number of elements the ringbuffer can contain
+ * \param destroy_element: NULL, or callback to be called for each element
+ *			   that is removed from the ringbuffer due to the
+ *			   oldest tail moving beyond it
+ *
+ * Creates the ringbuffer and allocates the storage.  Returns the new
+ * lws_ring *, or NULL if the allocation failed.
+ *
+ * If non-NULL, destroy_element will get called back for every element that is
+ * retired from the ringbuffer after the oldest tail has gone past it, and for
+ * any element still left in the ringbuffer when it is destroyed.  It replaces
+ * all other element destruction code in your user code.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_ring *
+lws_ring_create(size_t element_len, size_t count,
+		void (*destroy_element)(void *element));
+
+/**
+ * lws_ring_destroy():  destroy a previously created ringbuffer
+ *
+ * \param ring: the struct lws_ring to destroy
+ *
+ * Destroys the ringbuffer allocation and the struct lws_ring itself.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_destroy(struct lws_ring *ring);
+
+/**
+ * lws_ring_get_count_free_elements():  return how many elements can fit
+ *				      in the free space
+ *
+ * \param ring: the struct lws_ring to report on
+ *
+ * Returns how much room is left in the ringbuffer for whole element insertion.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_get_count_free_elements(struct lws_ring *ring);
+
+/**
+ * lws_ring_get_count_waiting_elements():  return how many elements can be consumed
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * Returns how many elements are waiting to be consumed from the perspective
+ * of the tail pointer given.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail);
+
+/**
+ * lws_ring_insert():  attempt to insert up to max_count elements from src
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param src: the array of elements to be inserted
+ * \param max_count: the number of available elements at src
+ *
+ * Attempts to insert as many of the elements at src as possible, up to the
+ * maximum max_count.  Returns the number of elements actually inserted.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count);
+
+/**
+ * lws_ring_consume():  attempt to copy out and remove up to max_count elements
+ *		        to src
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ * \param dest: the array of elements to be inserted. or NULL for no copy
+ * \param max_count: the number of available elements at src
+ *
+ * Attempts to copy out as many waiting elements as possible into dest, from
+ * the perspective of the given tail, up to max_count.  If dest is NULL, the
+ * copying out is not done but the elements are logically consumed as usual.
+ * NULL dest is useful in combination with lws_ring_get_element(), where you
+ * can use the element direct from the ringbuffer and then call this with NULL
+ * dest to logically consume it.
+ *
+ * Increments the tail position according to how many elements could be
+ * consumed.
+ *
+ * Returns the number of elements consumed.
+ */
+LWS_VISIBLE LWS_EXTERN size_t
+lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
+		 size_t max_count);
+
+/**
+ * lws_ring_get_element():  get a pointer to the next waiting element for tail
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * Points to the next element that tail would consume, directly in the
+ * ringbuffer.  This lets you write() or otherwise use the element without
+ * having to copy it out somewhere first.
+ *
+ * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1)
+ * which will logically consume the element you used up and increment your
+ * tail (tail may also be NULL there if you use a single tail).
+ *
+ * Returns NULL if no waiting element, or a const void * pointing to it.
+ */
+LWS_VISIBLE LWS_EXTERN const void *
+lws_ring_get_element(struct lws_ring *ring, uint32_t *tail);
+
+/**
+ * lws_ring_update_oldest_tail():  free up elements older than tail for reuse
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param tail: a pointer to the tail struct to use, or NULL for single tail
+ *
+ * If you are using multiple tails, you must use this API to inform the
+ * lws_ring when none of the tails still need elements in the fifo any more,
+ * by updating it when the "oldest" tail has moved on.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail);
+
+/**
+ * lws_ring_get_oldest_tail():  get current oldest available data index
+ *
+ * \param ring: the struct lws_ring to report on
+ *
+ * If you are initializing a new ringbuffer consumer, you can set its tail to
+ * this to start it from the oldest ringbuffer entry still available.
+ */
+LWS_VISIBLE LWS_EXTERN uint32_t
+lws_ring_get_oldest_tail(struct lws_ring *ring);
+
+/**
+ * lws_ring_next_linear_insert_range():  used to write directly into the ring
+ *
+ * \param ring: the struct lws_ring to report on
+ * \param start: pointer to a void * set to the start of the next ringbuffer area
+ * \param bytes: pointer to a size_t set to the max length you may use from *start
+ *
+ * This provides a low-level, bytewise access directly into the ringbuffer
+ * allowing direct insertion of data without having to use a bounce buffer.
+ *
+ * The api reports the position and length of the next linear range that can
+ * be written in the ringbuffer, ie, up to the point it would wrap, and sets
+ * *start and *bytes accordingly.  You can then, eg, directly read() into
+ * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring
+ * with what you have done.
+ *
+ * Returns nonzero if no insertion is currently possible.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
+				  size_t *bytes);
+
+/**
+ * lws_ring_bump_head():  used to write directly into the ring
+ *
+ * \param ring: the struct lws_ring to operate on
+ * \param bytes: the number of bytes you inserted at the current head
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_ring_bump_head(struct lws_ring *ring, size_t bytes);
+///@}
 
 /** \defgroup sha SHA and B64 helpers
  * ##SHA and B64 helpers
diff --git a/lib/lws-plat-esp32.c b/lib/lws-plat-esp32.c
index 517f344d5faedc439f79e19bbf3ef94076f181ca..b5adabe1f4a2c38eff71d53f865a3cd867327f81 100644
--- a/lib/lws-plat-esp32.c
+++ b/lib/lws-plat-esp32.c
@@ -60,17 +60,21 @@ lws_get_random(struct lws_context *context, void *buf, int len)
 LWS_VISIBLE int
 lws_send_pipe_choked(struct lws *wsi)
 {
+	struct lws *wsi_eff = wsi;
 	fd_set writefds;
 	struct timeval tv = { 0, 0 };
+#if defined(LWS_WITH_HTTP2)
+	wsi_eff = lws_get_network_wsi(wsi);
+#endif
 
 	/* treat the fact we got a truncated send pending as if we're choked */
-	if (wsi->trunc_len)
+	if (wsi_eff->trunc_len)
 		return 1;
 
 	FD_ZERO(&writefds);
-	FD_SET(wsi->desc.sockfd, &writefds);
+	FD_SET(wsi_eff->desc.sockfd, &writefds);
 
-	if (select(wsi->desc.sockfd + 1, NULL, &writefds, NULL, &tv) < 1)
+	if (select(wsi_eff->desc.sockfd + 1, NULL, &writefds, NULL, &tv) < 1)
 		return 1;
 
 	return 0;
@@ -535,6 +539,21 @@ _lws_plat_file_write(lws_fop_fd_t fops_fd, lws_filepos_t *amount,
 	return 0;
 }
 
+#if defined(LWS_WITH_HTTP2)
+/*
+ * These are the default SETTINGS used on this platform.  The user
+ * can selectively modify them for a vhost during vhost creation.
+ */
+const struct http2_settings const lws_h2_defaults_esp32 = { {
+	1,
+	/* H2SET_HEADER_TABLE_SIZE */			 512,
+	/* H2SET_ENABLE_PUSH */				   0,
+	/* H2SET_MAX_CONCURRENT_STREAMS */		   8,
+	/* H2SET_INITIAL_WINDOW_SIZE */		       65535,
+	/* H2SET_MAX_FRAME_SIZE */		       16384,
+	/* H2SET_MAX_HEADER_LIST_SIZE */	 	 512,
+}};
+#endif
 
 LWS_VISIBLE int
 lws_plat_init(struct lws_context *context,
@@ -556,11 +575,14 @@ lws_plat_init(struct lws_context *context,
 	if (info->plugin_dirs)
 		lws_plat_plugins_init(context, info->plugin_dirs);
 #endif
+#if defined(LWS_WITH_HTTP2)
+	/* override settings */
+	context->set = lws_h2_defaults_esp32;
+#endif
 
 	return 0;
 }
 
-
 LWS_VISIBLE void esp32_uvtimer_cb(TimerHandle_t t)
 {
 	struct timer_mapping *p = pvTimerGetTimerID(t);
@@ -568,24 +590,6 @@ LWS_VISIBLE void esp32_uvtimer_cb(TimerHandle_t t)
 	p->cb(p->t);
 }
 
-void ERR_error_string_n(unsigned long e, char *buf, size_t len)
-{
-	strncpy(buf, "unknown", len);
-}
-
-void ERR_free_strings(void)
-{
-}
-
-char *ERR_error_string(unsigned long e, char *buf)
-{
-	if (buf)
-		strcpy(buf, "unknown");
-
-	return "unknown";
-}
-
-
 /* helper functionality */
 
 #include "romfs.h"
@@ -1499,7 +1503,7 @@ lws_esp32_wlan_start_station(void)
 
 	tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, (const char *)&config.ap.ssid[7]);
 	esp_wifi_set_auto_connect(1);
-	ESP_ERROR_CHECK( esp_wifi_connect());
+	//ESP_ERROR_CHECK( esp_wifi_connect());
 
 	lws_esp32_scan_timer_cb(NULL);
 }
@@ -1591,13 +1595,13 @@ lws_esp32_set_creation_defaults(struct lws_context_creation_info *info)
 	(void)part;
 
 	info->port = 443;
-	info->fd_limit_per_thread = 30;
-	info->max_http_header_pool = 3;
-	info->max_http_header_data = 1024;
-	info->pt_serv_buf_size = 4096;
+	info->fd_limit_per_thread = 10;
+	info->max_http_header_pool = 16;
+	info->max_http_header_data = 512;
+	info->pt_serv_buf_size = 2048;
 	info->keepalive_timeout = 30;
 	info->timeout_secs = 30;
-	info->simultaneous_ssl_restriction = 3;
+	info->simultaneous_ssl_restriction = 4;
 	info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
 		       LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
 
diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c
index cb5e47c3f5cda4b64d82d0409dd8d9f8444069fd..f5536912916d3d5f53fcbac349169dbb076e9c35 100644
--- a/lib/lws-plat-unix.c
+++ b/lib/lws-plat-unix.c
@@ -47,12 +47,16 @@ LWS_VISIBLE int
 lws_send_pipe_choked(struct lws *wsi)
 {
 	struct lws_pollfd fds;
+	struct lws *wsi_eff = wsi;
 
+#if defined(LWS_WITH_HTTP2)
+	wsi_eff = lws_get_network_wsi(wsi);
+#endif
 	/* treat the fact we got a truncated send pending as if we're choked */
-	if (wsi->trunc_len)
+	if (wsi_eff->trunc_len)
 		return 1;
 
-	fds.fd = wsi->desc.sockfd;
+	fds.fd = wsi_eff->desc.sockfd;
 	fds.events = POLLOUT;
 	fds.revents = 0;
 
diff --git a/lib/mbedtls_wrapper/include/internal/ssl_types.h b/lib/mbedtls_wrapper/include/internal/ssl_types.h
index 5d7d9387cda82087e419f0a6bb1ff3b8a2a16f16..45198bc978679acdbb0e0d1501e393483a40453f 100644
--- a/lib/mbedtls_wrapper/include/internal/ssl_types.h
+++ b/lib/mbedtls_wrapper/include/internal/ssl_types.h
@@ -19,6 +19,12 @@
  extern "C" {
 #endif
 
+#include <lws_config.h>
+#if defined(LWS_WITH_ESP32)
+#undef MBEDTLS_CONFIG_FILE
+#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
+#endif
+
 #include "ssl_code.h"
 
 typedef void SSL_CIPHER;
diff --git a/lib/output.c b/lib/output.c
index 4d6897e011a8afc6004ffdbca38b5e90442d9724..0c5f41c8791a7c174e3ccf7ec60f2a3b9c422d72 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -53,7 +53,7 @@ LWS_VISIBLE void lwsl_hexdump(const void *vbuf, size_t len)
 	char line[80];
 	char *p;
 
-	lwsl_parser("\n");
+	lwsl_debug("\n");
 
 	for (n = 0; n < len;) {
 		start = n;
@@ -117,7 +117,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
 #else
 		lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
 			 "       It's illegal to do an lws_write outside of\n"
-			 "       the writable callback: fix your code",
+			 "       the writable callback: fix your code\n",
 			 wsi, (unsigned long)len, dump);
 #endif
 		assert(0);
@@ -133,7 +133,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
 		goto handle_truncated_send;
 	}
 
-	if (!lws_socket_is_valid(wsi->desc.sockfd))
+	if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
 		lwsl_warn("** error invalid sock but expected to send\n");
 
 	/* limit sending */
@@ -300,9 +300,10 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 
 	lws_restart_ws_ping_pong_timer(wsi);
 
-	if (wp == LWS_WRITE_HTTP ||
-	    wp == LWS_WRITE_HTTP_FINAL ||
-	    wp == LWS_WRITE_HTTP_HEADERS)
+	if ((wp & 0x1f) == LWS_WRITE_HTTP ||
+	    (wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
+	    (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
+	    (wp & 0x1f) == LWS_WRITE_HTTP_HEADERS)
 		goto send_raw;
 
 	/* if not in a state to send stuff, then just send nothing */
@@ -508,45 +509,61 @@ do_more_inside_frame:
 	}
 
 send_raw:
-	switch ((int)wp) {
+	switch ((int)(wp & 0x1f)) {
 	case LWS_WRITE_CLOSE:
 /*		lwsl_hexdump(&buf[-pre], len); */
 	case LWS_WRITE_HTTP:
 	case LWS_WRITE_HTTP_FINAL:
 	case LWS_WRITE_HTTP_HEADERS:
+	case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
 	case LWS_WRITE_PONG:
 	case LWS_WRITE_PING:
 #ifdef LWS_WITH_HTTP2
 		if (wsi->mode == LWSCM_HTTP2_SERVING) {
 			unsigned char flags = 0;
 
-			n = LWS_HTTP2_FRAME_TYPE_DATA;
-			if (wp == LWS_WRITE_HTTP_HEADERS) {
-				n = LWS_HTTP2_FRAME_TYPE_HEADERS;
-				flags = LWS_HTTP2_FLAG_END_HEADERS;
-				if (wsi->u.http2.send_END_STREAM)
-					flags |= LWS_HTTP2_FLAG_END_STREAM;
+			n = LWS_H2_FRAME_TYPE_DATA;
+			if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
+				n = LWS_H2_FRAME_TYPE_HEADERS;
+				if (!(wp & LWS_WRITE_NO_FIN))
+					flags = LWS_H2_FLAG_END_HEADERS;
+				if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
+					flags |= LWS_H2_FLAG_END_STREAM;
+					wsi->u.h2.send_END_STREAM = 1;
+				}
 			}
 
-			if ((wp == LWS_WRITE_HTTP ||
-			     wp == LWS_WRITE_HTTP_FINAL) &&
-			    wsi->u.http.content_length) {
-				wsi->u.http.content_remain -= len;
+			if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
+				n = LWS_H2_FRAME_TYPE_CONTINUATION;
+				if (!(wp & LWS_WRITE_NO_FIN))
+					flags = LWS_H2_FLAG_END_HEADERS;
+				if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
+					flags |= LWS_H2_FLAG_END_STREAM;
+					wsi->u.h2.send_END_STREAM = 1;
+				}
+			}
+
+			if (((wp & 0x1f) == LWS_WRITE_HTTP ||
+			     (wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
+			    wsi->u.http.tx_content_length) {
+				wsi->u.http.tx_content_remain -= len;
 				lwsl_info("%s: content_remain = %llu\n", __func__,
-					  (unsigned long long)wsi->u.http.content_remain);
-				if (!wsi->u.http.content_remain) {
+					  (unsigned long long)wsi->u.http.tx_content_remain);
+				if (!wsi->u.http.tx_content_remain) {
 					lwsl_info("%s: selecting final write mode\n", __func__);
 					wp = LWS_WRITE_HTTP_FINAL;
 				}
 			}
 
-			if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
+			if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) {
+			    //lws_get_network_wsi(wsi)->u.h2.END_STREAM) {
 				lwsl_info("%s: setting END_STREAM\n", __func__);
-				flags |= LWS_HTTP2_FLAG_END_STREAM;
+				flags |= LWS_H2_FLAG_END_STREAM;
+				wsi->u.h2.send_END_STREAM = 1;
 			}
 
-			return lws_http2_frame_write(wsi, n, flags,
-					wsi->u.http2.my_stream_id, len, buf);
+			return lws_h2_frame_write(wsi, n, flags,
+					wsi->u.h2.my_sid, len, buf);
 		}
 #endif
 		return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
@@ -600,13 +617,15 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	struct lws_process_html_args args;
 	lws_filepos_t amount, poss;
-	unsigned char *p;
+	unsigned char *p, *pstart;
 #if defined(LWS_WITH_RANGES)
 	unsigned char finished = 0;
 #endif
 	int n, m;
 
-	while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) {
+	lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
+
+	while (!lws_send_pipe_choked(wsi)) {
 
 		if (wsi->trunc_len) {
 			if (lws_issue_raw(wsi, wsi->trunc_alloc +
@@ -623,7 +642,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 
 		n = 0;
 
-		p = pt->serv_buf;
+		pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
+
+		p = pstart;
 
 #if defined(LWS_WITH_RANGES)
 		if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
@@ -638,7 +659,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 			wsi->u.http.filepos = wsi->u.http.range.start;
 
 			if (wsi->u.http.range.count_ranges > 1) {
-				n =  lws_snprintf((char *)p, context->pt_serv_buf_size,
+				n =  lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH,
 					"_lws\x0d\x0a"
 					"Content-Type: %s\x0d\x0a"
 					"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
@@ -656,15 +677,30 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 		}
 #endif
 
-		poss = context->pt_serv_buf_size - n;
+		poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
 
 		/*
 		 * if there is a hint about how much we will do well to send at one time,
 		 * restrict ourselves to only trying to send that.
 		 */
-		if (wsi->protocol->tx_packet_size && poss > wsi->protocol->tx_packet_size)
+		if (wsi->protocol->tx_packet_size &&
+		    poss > wsi->protocol->tx_packet_size)
 			poss = wsi->protocol->tx_packet_size;
 
+#if defined(LWS_WITH_HTTP2)
+		m = lws_h2_tx_cr_get(wsi);
+		if (!m) {
+			lwsl_info("%s: came here with no tx credit", __func__);
+			return 0;
+		}
+		if (m < poss)
+			poss = m;
+		/*
+		 * consumption of the actual payload amount sent will be handled
+		 * when the http2 data frame is sent
+		 */
+#endif
+
 #if defined(LWS_WITH_RANGES)
 		if (wsi->u.http.range.count_ranges) {
 			if (wsi->u.http.range.count_ranges > 1)
@@ -686,7 +722,10 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 		if (wsi->sending_chunked)
 			n = (int)amount;
 		else
-			n = (p - pt->serv_buf) + (int)amount;
+			n = (p - pstart) + (int)amount;
+
+		lwsl_debug("%s: sending %d\n", __func__, n);
+
 		if (n) {
 			lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
 					context->timeout_secs);
@@ -705,14 +744,14 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 				n = args.len;
 				p = (unsigned char *)args.p;
 			} else
-				p = pt->serv_buf;
+				p = pstart;
 
 #if defined(LWS_WITH_RANGES)
 			if (wsi->u.http.range.send_ctr + 1 ==
 				wsi->u.http.range.count_ranges && // last range
 			    wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
 			    wsi->u.http.range.budget - amount == 0) {// final part
-				n += lws_snprintf((char *)pt->serv_buf + n, 6,
+				n += lws_snprintf((char *)pstart + n, 6,
 					"_lws\x0d\x0a"); // append trailing boundary
 				lwsl_debug("added trailing boundary\n");
 			}
@@ -751,8 +790,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
 					goto file_had_it;
 			}
 		}
+
 all_sent:
-		if ((!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen)
+		if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen)
 #if defined(LWS_WITH_RANGES)
 		    || finished)
 #else
@@ -765,13 +805,30 @@ all_sent:
 			
 			lwsl_debug("file completed\n");
 
-			if (wsi->protocol->callback)
-				/* ignore callback returned value */
-				if (user_callback_handle_rxflow(
-				     wsi->protocol->callback, wsi,
-				     LWS_CALLBACK_HTTP_FILE_COMPLETION,
-				     wsi->user_space, NULL, 0) < 0)
-					return -1;
+			if (wsi->protocol->callback &&
+			    user_callback_handle_rxflow(wsi->protocol->callback,
+					    	    	wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
+					    	    	wsi->user_space, NULL,
+					    	    	0) < 0) {
+					/*
+					 * For http/1.x, the choices from
+					 * transaction_completed are either
+					 * 0 to use the connection for pipelined
+					 * or nonzero to hang it up.
+					 *
+					 * However for http/2. while we are
+					 * still interested in hanging up the
+					 * nwsi if there was a network-level
+					 * fatal error, simply completing the
+					 * transaction is a matter of the stream
+					 * state, not the root connection at the
+					 * network level
+					 */
+					if (wsi->http2_substream)
+						return 1;
+					else
+						return -1;
+				}
 
 			return 1;  /* >0 indicates completed */
 		}
diff --git a/lib/parsers.c b/lib/parsers.c
index 99119e132b62c3748f3ec0da91ffedb3da6590cd..f4c412d82de739dcd4355f5daea121966e64048b 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -110,6 +110,7 @@ _lws_header_table_reset(struct allocated_headers *ah)
 {
 	/* init the ah to reflect no headers or data have appeared yet */
 	memset(ah->frag_index, 0, sizeof(ah->frag_index));
+	memset(ah->frags, 0, sizeof(ah->frags));
 	ah->nfrag = 0;
 	ah->pos = 0;
 	ah->http_response = 0;
@@ -440,6 +441,7 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
 
 		/* he has been stuck waiting for an ah, but now his wait is
 		 * over, let him progress */
+
 		_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
 	}
 
@@ -676,6 +678,7 @@ issue_char(struct lws *wsi, unsigned char c)
 	if (frag_len == wsi->u.hdr.current_token_limit) {
 		if (lws_pos_in_bounds(wsi))
 			return -1;
+
 		wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
 		lwsl_warn("header %i exceeds limit %d\n",
 			  wsi->u.hdr.parser_state,
@@ -685,21 +688,213 @@ issue_char(struct lws *wsi, unsigned char c)
 	return 1;
 }
 
+int
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
+{
+	struct allocated_headers *ah = wsi->u.hdr.ah;
+	unsigned int enc = 0;
+	uint8_t c = *_c;
+
+	/*
+	 * PRIORITY 1
+	 * special URI processing... convert %xx
+	 */
+	switch (wsi->u.hdr.ues) {
+	case URIES_IDLE:
+		if (c == '%') {
+			wsi->u.hdr.ues = URIES_SEEN_PERCENT;
+			goto swallow;
+		}
+		break;
+	case URIES_SEEN_PERCENT:
+		if (char_to_hex(c) < 0)
+			/* illegal post-% char */
+			goto forbid;
+
+		wsi->u.hdr.esc_stash = c;
+		wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
+		goto swallow;
+
+	case URIES_SEEN_PERCENT_H1:
+		if (char_to_hex(c) < 0)
+			/* illegal post-% char */
+			goto forbid;
+
+		*_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
+				char_to_hex(c);
+		c = *_c;
+		enc = 1;
+		wsi->u.hdr.ues = URIES_IDLE;
+		break;
+	}
+
+	/*
+	 * PRIORITY 2
+	 * special URI processing...
+	 *  convert /.. or /... or /../ etc to /
+	 *  convert /./ to /
+	 *  convert // or /// etc to /
+	 *  leave /.dir or whatever alone
+	 */
+
+	switch (wsi->u.hdr.ups) {
+	case URIPS_IDLE:
+		if (!c)
+			return -1;
+		/* genuine delimiter */
+		if ((c == '&' || c == ';') && !enc) {
+			if (issue_char(wsi, c) < 0)
+				return -1;
+			/* swallow the terminator */
+			ah->frags[ah->nfrag].len--;
+			/* link to next fragment */
+			ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
+			ah->nfrag++;
+			if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+				goto excessive;
+			/* start next fragment after the & */
+			wsi->u.hdr.post_literal_equal = 0;
+			ah->frags[ah->nfrag].offset = ah->pos;
+			ah->frags[ah->nfrag].len = 0;
+			ah->frags[ah->nfrag].nfrag = 0;
+			goto swallow;
+		}
+		/* uriencoded = in the name part, disallow */
+		if (c == '=' && enc &&
+		    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
+		    !wsi->u.hdr.post_literal_equal) {
+			c = '_';
+			*_c =c;
+		}
+
+		/* after the real =, we don't care how many = */
+		if (c == '=' && !enc)
+			wsi->u.hdr.post_literal_equal = 1;
+
+		/* + to space */
+		if (c == '+' && !enc) {
+			c = ' ';
+			*_c = c;
+		}
+		/* issue the first / always */
+		if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
+			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
+		break;
+	case URIPS_SEEN_SLASH:
+		/* swallow subsequent slashes */
+		if (c == '/')
+			goto swallow;
+		/* track and swallow the first . after / */
+		if (c == '.') {
+			wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
+			goto swallow;
+		}
+		wsi->u.hdr.ups = URIPS_IDLE;
+		break;
+	case URIPS_SEEN_SLASH_DOT:
+		/* swallow second . */
+		if (c == '.') {
+			wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
+			goto swallow;
+		}
+		/* change /./ to / */
+		if (c == '/') {
+			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
+			goto swallow;
+		}
+		/* it was like /.dir ... regurgitate the . */
+		wsi->u.hdr.ups = URIPS_IDLE;
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		break;
+
+	case URIPS_SEEN_SLASH_DOT_DOT:
+
+		/* /../ or /..[End of URI] --> backup to last / */
+		if (c == '/' || c == '?') {
+			/*
+			 * back up one dir level if possible
+			 * safe against header fragmentation because
+			 * the method URI can only be in 1 fragment
+			 */
+			if (ah->frags[ah->nfrag].len > 2) {
+				ah->pos--;
+				ah->frags[ah->nfrag].len--;
+				do {
+					ah->pos--;
+					ah->frags[ah->nfrag].len--;
+				} while (ah->frags[ah->nfrag].len > 1 &&
+					 ah->data[ah->pos] != '/');
+			}
+			wsi->u.hdr.ups = URIPS_SEEN_SLASH;
+			if (ah->frags[ah->nfrag].len > 1)
+				break;
+			goto swallow;
+		}
+
+		/*  /..[^/] ... regurgitate and allow */
+
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		if (issue_char(wsi, '.') < 0)
+			return -1;
+		wsi->u.hdr.ups = URIPS_IDLE;
+		break;
+	}
+
+	if (c == '?' && !enc &&
+	    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
+		if (wsi->u.hdr.ues != URIES_IDLE)
+			goto forbid;
+
+		/* seal off uri header */
+		if (issue_char(wsi, '\0') < 0)
+			return -1;
+
+		/* move to using WSI_TOKEN_HTTP_URI_ARGS */
+		ah->nfrag++;
+		if (ah->nfrag >= ARRAY_SIZE(ah->frags))
+			goto excessive;
+		ah->frags[ah->nfrag].offset = ah->pos;
+		ah->frags[ah->nfrag].len = 0;
+		ah->frags[ah->nfrag].nfrag = 0;
+
+		wsi->u.hdr.post_literal_equal = 0;
+		ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
+		wsi->u.hdr.ups = URIPS_IDLE;
+		goto swallow;
+	}
+
+	return LPUR_CONTINUE;
+
+swallow:
+	return LPUR_SWALLOW;
+
+forbid:
+	return LPUR_FORBID;
+
+excessive:
+	return LPUR_EXCESSIVE;
+}
+
+static const unsigned char methods[] = {
+	WSI_TOKEN_GET_URI,
+	WSI_TOKEN_POST_URI,
+	WSI_TOKEN_OPTIONS_URI,
+	WSI_TOKEN_PUT_URI,
+	WSI_TOKEN_PATCH_URI,
+	WSI_TOKEN_DELETE_URI,
+	WSI_TOKEN_CONNECT,
+	WSI_TOKEN_HEAD_URI,
+};
+
 int LWS_WARN_UNUSED_RESULT
 lws_parse(struct lws *wsi, unsigned char c)
 {
-	static const unsigned char methods[] = {
-		WSI_TOKEN_GET_URI,
-		WSI_TOKEN_POST_URI,
-		WSI_TOKEN_OPTIONS_URI,
-		WSI_TOKEN_PUT_URI,
-		WSI_TOKEN_PATCH_URI,
-		WSI_TOKEN_DELETE_URI,
-		WSI_TOKEN_CONNECT,
-	};
 	struct allocated_headers *ah = wsi->u.hdr.ah;
 	struct lws_context *context = wsi->context;
-	unsigned int n, m, enc = 0;
+	unsigned int n, m;
+	int r;
 
 	assert(wsi->u.hdr.ah);
 
@@ -753,172 +948,19 @@ lws_parse(struct lws *wsi, unsigned char c)
 			goto start_fragment;
 		}
 
-		/*
-		 * PRIORITY 1
-		 * special URI processing... convert %xx
-		 */
-
-		switch (wsi->u.hdr.ues) {
-		case URIES_IDLE:
-			if (c == '%') {
-				wsi->u.hdr.ues = URIES_SEEN_PERCENT;
-				goto swallow;
-			}
+		r = lws_parse_urldecode(wsi, &c);
+		switch (r) {
+		case LPUR_CONTINUE:
 			break;
-		case URIES_SEEN_PERCENT:
-			if (char_to_hex(c) < 0)
-				/* illegal post-% char */
-				goto forbid;
-
-			wsi->u.hdr.esc_stash = c;
-			wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
-			goto swallow;
-
-		case URIES_SEEN_PERCENT_H1:
-			if (char_to_hex(c) < 0)
-				/* illegal post-% char */
-				goto forbid;
-
-			c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
-					char_to_hex(c);
-			enc = 1;
-			wsi->u.hdr.ues = URIES_IDLE;
-			break;
-		}
-
-		/*
-		 * PRIORITY 2
-		 * special URI processing...
-		 *  convert /.. or /... or /../ etc to /
-		 *  convert /./ to /
-		 *  convert // or /// etc to /
-		 *  leave /.dir or whatever alone
-		 */
-
-		switch (wsi->u.hdr.ups) {
-		case URIPS_IDLE:
-			if (!c)
-				return -1;
-			/* genuine delimiter */
-			if ((c == '&' || c == ';') && !enc) {
-				if (issue_char(wsi, c) < 0)
-					return -1;
-				/* swallow the terminator */
-				ah->frags[ah->nfrag].len--;
-				/* link to next fragment */
-				ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
-				ah->nfrag++;
-				if (ah->nfrag >= ARRAY_SIZE(ah->frags))
-					goto excessive;
-				/* start next fragment after the & */
-				wsi->u.hdr.post_literal_equal = 0;
-				ah->frags[ah->nfrag].offset = ah->pos;
-				ah->frags[ah->nfrag].len = 0;
-				ah->frags[ah->nfrag].nfrag = 0;
-				goto swallow;
-			}
-			/* uriencoded = in the name part, disallow */
-			if (c == '=' && enc &&
-			    ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
-			    !wsi->u.hdr.post_literal_equal)
-				c = '_';
-
-			/* after the real =, we don't care how many = */
-			if (c == '=' && !enc)
-				wsi->u.hdr.post_literal_equal = 1;
-
-			/* + to space */
-			if (c == '+' && !enc)
-				c = ' ';
-			/* issue the first / always */
-			if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
-				wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-			break;
-		case URIPS_SEEN_SLASH:
-			/* swallow subsequent slashes */
-			if (c == '/')
-				goto swallow;
-			/* track and swallow the first . after / */
-			if (c == '.') {
-				wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
-				goto swallow;
-			}
-			wsi->u.hdr.ups = URIPS_IDLE;
-			break;
-		case URIPS_SEEN_SLASH_DOT:
-			/* swallow second . */
-			if (c == '.') {
-				wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
-				goto swallow;
-			}
-			/* change /./ to / */
-			if (c == '/') {
-				wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-				goto swallow;
-			}
-			/* it was like /.dir ... regurgitate the . */
-			wsi->u.hdr.ups = URIPS_IDLE;
-			if (issue_char(wsi, '.') < 0)
-				return -1;
-			break;
-
-		case URIPS_SEEN_SLASH_DOT_DOT:
-
-			/* /../ or /..[End of URI] --> backup to last / */
-			if (c == '/' || c == '?') {
-				/*
-				 * back up one dir level if possible
-				 * safe against header fragmentation because
-				 * the method URI can only be in 1 fragment
-				 */
-				if (ah->frags[ah->nfrag].len > 2) {
-					ah->pos--;
-					ah->frags[ah->nfrag].len--;
-					do {
-						ah->pos--;
-						ah->frags[ah->nfrag].len--;
-					} while (ah->frags[ah->nfrag].len > 1 &&
-						 ah->data[ah->pos] != '/');
-				}
-				wsi->u.hdr.ups = URIPS_SEEN_SLASH;
-				if (ah->frags[ah->nfrag].len > 1)
-					break;
-				goto swallow;
-			}
-
-			/*  /..[^/] ... regurgitate and allow */
-
-			if (issue_char(wsi, '.') < 0)
-				return -1;
-			if (issue_char(wsi, '.') < 0)
-				return -1;
-			wsi->u.hdr.ups = URIPS_IDLE;
-			break;
-		}
-
-		if (c == '?' && !enc &&
-		    !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
-			if (wsi->u.hdr.ues != URIES_IDLE)
-				goto forbid;
-
-			/* seal off uri header */
-			if (issue_char(wsi, '\0') < 0)
-				return -1;
-
-			/* move to using WSI_TOKEN_HTTP_URI_ARGS */
-			ah->nfrag++;
-			if (ah->nfrag >= ARRAY_SIZE(ah->frags))
-				goto excessive;
-			ah->frags[ah->nfrag].offset = ah->pos;
-			ah->frags[ah->nfrag].len = 0;
-			ah->frags[ah->nfrag].nfrag = 0;
-
-			wsi->u.hdr.post_literal_equal = 0;
-			ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
-			wsi->u.hdr.ups = URIPS_IDLE;
+		case LPUR_SWALLOW:
 			goto swallow;
+		case LPUR_FORBID:
+			goto forbid;
+		case LPUR_EXCESSIVE:
+			goto excessive;
+		default:
+			return -1;
 		}
-
 check_eol:
 		/* bail at EOL */
 		if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
@@ -946,7 +988,7 @@ swallow:
 
 		/* collecting and checking a name part */
 	case WSI_TOKEN_NAME_PART:
-		lwsl_parser("WSI_TOKEN_NAME_PART '%c' (mode=%d)\n", c, wsi->mode);
+		lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos);
 
 		wsi->u.hdr.lextable_pos =
 				lextable_decode(wsi->u.hdr.lextable_pos, c);
@@ -954,7 +996,7 @@ swallow:
 		 * Server needs to look out for unknown methods...
 		 */
 		if (wsi->u.hdr.lextable_pos < 0 &&
-		    wsi->mode == LWSCM_HTTP_SERVING) {
+		    (wsi->mode == LWSCM_HTTP_SERVING)) {
 			/* this is not a header we know about */
 			for (m = 0; m < ARRAY_SIZE(methods); m++)
 				if (ah->frag_index[methods[m]]) {
@@ -1041,10 +1083,12 @@ excessive:
 		ah->frags[ah->nfrag].offset = ah->pos;
 		ah->frags[ah->nfrag].len = 0;
 		ah->frags[ah->nfrag].nfrag = 0;
+		ah->frags[ah->nfrag].flags = 2;
 
 		n = ah->frag_index[wsi->u.hdr.parser_state];
 		if (!n) { /* first fragment */
 			ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag;
+			ah->hdr_token_idx = wsi->u.hdr.parser_state;
 			break;
 		}
 		/* continuation */
@@ -1101,6 +1145,7 @@ set_parsing_complete:
 forbid:
 	lwsl_notice(" forbidding on uri sanitation\n");
 	lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
+
 	return -1;
 }
 
diff --git a/lib/pollfd.c b/lib/pollfd.c
index 5f9d94d55589ce729b3d74bc3a546b44c2c3e031..4d6704d41ccfd17a14f0cc110e71692c7a13ddd0 100644
--- a/lib/pollfd.c
+++ b/lib/pollfd.c
@@ -385,35 +385,38 @@ lws_callback_on_writable(struct lws *wsi)
 	if (wsi->mode != LWSCM_HTTP2_SERVING)
 		goto network_sock;
 
-	if (wsi->u.http2.requested_POLLOUT) {
+	if (wsi->u.h2.requested_POLLOUT) {
 		lwsl_info("already pending writable\n");
 		return 1;
 	}
 
-	if (wsi->u.http2.tx_credit <= 0) {
+	/* is this for DATA or for control messages? */
+	if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps &&
+	    !lws_h2_tx_cr_get(wsi)) {
 		/*
-		 * other side is not able to cope with us sending
-		 * anything so no matter if we have POLLOUT on our side.
+		 * other side is not able to cope with us sending DATA
+		 * anything so no matter if we have POLLOUT on our side if it's
+		 * DATA we want to send.
 		 *
 		 * Delay waiting for our POLLOUT until peer indicates he has
 		 * space for more using tx window command in http2 layer
 		 */
-		lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi,
-			  wsi->u.http2.tx_credit);
-		wsi->u.http2.waiting_tx_credit = 1;
+		lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
+		wsi->u.h2.skint = 1;
 		return 0;
 	}
 
-	network_wsi = lws_http2_get_network_wsi(wsi);
-	already = network_wsi->u.http2.requested_POLLOUT;
+	wsi->u.h2.skint = 0;
+	network_wsi = lws_get_network_wsi(wsi);
+	already = network_wsi->u.h2.requested_POLLOUT;
 
 	/* mark everybody above him as requesting pollout */
 
 	wsi2 = wsi;
 	while (wsi2) {
-		wsi2->u.http2.requested_POLLOUT = 1;
+		wsi2->u.h2.requested_POLLOUT = 1;
 		lwsl_info("mark %p pending writable\n", wsi2);
-		wsi2 = wsi2->u.http2.parent_wsi;
+		wsi2 = wsi2->u.h2.parent_wsi;
 	}
 
 	/* for network action, act only on the network wsi */
@@ -428,7 +431,7 @@ network_sock:
 		return 1;
 
 	if (wsi->position_in_fds_table < 0) {
-		lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
+		lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
 		return -1;
 	}
 
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 7d30078f49756e57a62be081a2fd6d16113eecdc..16ddcba1907720a93e1cc0ad969beb67247f22b4 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -525,13 +525,6 @@ enum http_connection_type {
 	HTTP_CONNECTION_KEEP_ALIVE
 };
 
-enum lws_pending_protocol_send {
-	LWS_PPS_NONE,
-	LWS_PPS_HTTP2_MY_SETTINGS,
-	LWS_PPS_HTTP2_ACK_SETTINGS,
-	LWS_PPS_HTTP2_PONG,
-};
-
 enum lws_rx_parse_state {
 	LWS_RXPS_NEW,
 
@@ -776,6 +769,7 @@ struct allocated_headers {
 	int16_t rxlen;
 	uint32_t pos;
 	uint32_t http_response;
+	int hdr_token_idx;
 
 #ifndef LWS_NO_CLIENT
 	char initial_handshake_hash_base64[30];
@@ -856,12 +850,29 @@ struct lws_context_per_thread {
 
 struct lws_conn_stats {
 	unsigned long long rx, tx;
-	unsigned long conn, trans, ws_upg, http2_upg, rejected;
+	unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs,
+		      h2_upg, rejected;
 };
 
 void
 lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs);
 
+
+enum lws_h2_settings {
+	H2SET_HEADER_TABLE_SIZE = 1,
+	H2SET_ENABLE_PUSH,
+	H2SET_MAX_CONCURRENT_STREAMS,
+	H2SET_INITIAL_WINDOW_SIZE,
+	H2SET_MAX_FRAME_SIZE,
+	H2SET_MAX_HEADER_LIST_SIZE,
+
+	H2SET_COUNT /* always last */
+};
+
+struct http2_settings {
+	uint32_t s[H2SET_COUNT];
+};
+
 /*
  * virtual host -related context information
  *   vhostwide SSL context
@@ -884,6 +895,9 @@ struct lws_vhost {
 #if !defined(LWS_WITH_ESP8266)
 	char http_proxy_address[128];
 	char proxy_basic_auth_token[128];
+#if defined(LWS_WITH_HTTP2)
+	struct http2_settings set;
+#endif
 #if defined(LWS_WITH_SOCKS5)
 	char socks_proxy_address[128];
 	char socks_user[96];
@@ -999,6 +1013,9 @@ struct lws_context {
 	time_t time_up;
 	const struct lws_plat_file_ops *fops;
 	struct lws_plat_file_ops fops_platform;
+#if defined(LWS_WITH_HTTP2)
+	struct http2_settings set;
+#endif
 #if defined(LWS_WITH_ZIP_FOPS)
 	struct lws_plat_file_ops fops_zip;
 #endif
@@ -1373,63 +1390,124 @@ struct _lws_http_mode_related {
 
 	enum http_version request_version;
 	enum http_connection_type connection_type;
-	lws_filepos_t content_length;
-	lws_filepos_t content_remain;
+	lws_filepos_t tx_content_length;
+	lws_filepos_t tx_content_remain;
+	lws_filepos_t rx_content_length;
+	lws_filepos_t rx_content_remain;
 };
 
-#ifdef LWS_WITH_HTTP2
-
-enum lws_http2_settings {
-	LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE = 1,
-	LWS_HTTP2_SETTINGS__ENABLE_PUSH,
-	LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS,
-	LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE,
-	LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE,
-	LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE,
+#define LWS_H2_FRAME_HEADER_LENGTH 9
 
-	LWS_HTTP2_SETTINGS__COUNT /* always last */
-};
+#ifdef LWS_WITH_HTTP2
 
-enum lws_http2_wellknown_frame_types {
-	LWS_HTTP2_FRAME_TYPE_DATA,
-	LWS_HTTP2_FRAME_TYPE_HEADERS,
-	LWS_HTTP2_FRAME_TYPE_PRIORITY,
-	LWS_HTTP2_FRAME_TYPE_RST_STREAM,
-	LWS_HTTP2_FRAME_TYPE_SETTINGS,
-	LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE,
-	LWS_HTTP2_FRAME_TYPE_PING,
-	LWS_HTTP2_FRAME_TYPE_GOAWAY,
-	LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE,
-	LWS_HTTP2_FRAME_TYPE_CONTINUATION,
-
-	LWS_HTTP2_FRAME_TYPE_COUNT /* always last */
+enum lws_h2_wellknown_frame_types {
+	LWS_H2_FRAME_TYPE_DATA,
+	LWS_H2_FRAME_TYPE_HEADERS,
+	LWS_H2_FRAME_TYPE_PRIORITY,
+	LWS_H2_FRAME_TYPE_RST_STREAM,
+	LWS_H2_FRAME_TYPE_SETTINGS,
+	LWS_H2_FRAME_TYPE_PUSH_PROMISE,
+	LWS_H2_FRAME_TYPE_PING,
+	LWS_H2_FRAME_TYPE_GOAWAY,
+	LWS_H2_FRAME_TYPE_WINDOW_UPDATE,
+	LWS_H2_FRAME_TYPE_CONTINUATION,
+
+	LWS_H2_FRAME_TYPE_COUNT /* always last */
 };
 
-enum lws_http2_flags {
-	LWS_HTTP2_FLAG_END_STREAM = 1,
-	LWS_HTTP2_FLAG_END_HEADERS = 4,
-	LWS_HTTP2_FLAG_PADDED = 8,
-	LWS_HTTP2_FLAG_PRIORITY = 0x20,
+enum lws_h2_flags {
+	LWS_H2_FLAG_END_STREAM = 1,
+	LWS_H2_FLAG_END_HEADERS = 4,
+	LWS_H2_FLAG_PADDED = 8,
+	LWS_H2_FLAG_PRIORITY = 0x20,
 
-	LWS_HTTP2_FLAG_SETTINGS_ACK = 1,
+	LWS_H2_FLAG_SETTINGS_ACK = 1,
 };
 
-#define LWS_HTTP2_STREAM_ID_MASTER 0
-#define LWS_HTTP2_FRAME_HEADER_LENGTH 9
-#define LWS_HTTP2_SETTINGS_LENGTH 6
-
-struct http2_settings {
-	unsigned int setting[LWS_HTTP2_SETTINGS__COUNT];
+enum lws_h2_errors {
+	H2_ERR_NO_ERROR,		   /* Graceful shutdown */
+	H2_ERR_PROTOCOL_ERROR,	   /* Protocol error detected */
+	H2_ERR_INTERNAL_ERROR,	   /* Implementation fault */
+	H2_ERR_FLOW_CONTROL_ERROR,  /* Flow-control limits exceeded */
+	H2_ERR_SETTINGS_TIMEOUT,	   /* Settings not acknowledged */
+	H2_ERR_STREAM_CLOSED,	   /* Frame received for closed stream */
+	H2_ERR_FRAME_SIZE_ERROR,	   /* Frame size incorrect */
+	H2_ERR_REFUSED_STREAM,	   /* Stream not processed */
+	H2_ERR_CANCEL,		   /* Stream cancelled */
+	H2_ERR_COMPRESSION_ERROR,   /* Compression state not updated */
+	H2_ERR_CONNECT_ERROR,	   /* TCP connection error for CONNECT method */
+	H2_ERR_ENHANCE_YOUR_CALM,   /* Processing capacity exceeded */
+	H2_ERR_INADEQUATE_SECURITY, /* Negotiated TLS parameters not acceptable */
+	H2_ERR_HTTP_1_1_REQUIRED,   /* Use HTTP/1.1 for the request */
 };
 
-enum http2_hpack_state {
+enum lws_h2_states {
+	LWS_H2_STATE_IDLE,
+	/*
+	 * Send PUSH_PROMISE    -> LWS_H2_STATE_RESERVED_LOCAL
+	 * Recv PUSH_PROMISE    -> LWS_H2_STATE_RESERVED_REMOTE
+	 * Send HEADERS         -> LWS_H2_STATE_OPEN
+	 * Recv HEADERS         -> LWS_H2_STATE_OPEN
+	 *
+	 *  - Only PUSH_PROMISE + HEADERS valid to send
+	 *  - Only HEADERS or PRIORITY valid to receive
+	 */
+	LWS_H2_STATE_RESERVED_LOCAL,
+	/*
+	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Send HEADERS         -> LWS_H2_STATE_HALF_CLOSED_REMOTE
+	 *
+	 * - Only HEADERS, RST_STREAM, or PRIORITY valid to send
+	 * - Only RST_STREAM, PRIORITY, or WINDOW_UPDATE valid to receive
+	 */
+	LWS_H2_STATE_RESERVED_REMOTE,
+	/*
+	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv HEADERS         -> LWS_H2_STATE_HALF_CLOSED_LOCAL
+	 *
+	 *  - Only RST_STREAM, WINDOW_UPDATE, or PRIORITY valid to send
+	 *  - Only HEADERS, RST_STREAM, or PRIORITY valid to receive
+	 */
+	LWS_H2_STATE_OPEN,
+	/*
+	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Send END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_LOCAL
+	 * Recv END_STREAM flag -> LWS_H2_STATE_HALF_CLOSED_REMOTE
+	 */
+	LWS_H2_STATE_HALF_CLOSED_REMOTE,
+	/*
+	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Send END_STREAM flag -> LWS_H2_STATE_CLOSED
+	 *
+	 *  - Any frame valid to send
+	 *  - Only WINDOW_UPDATE, PRIORITY, or RST_STREAM valid to receive
+	 */
+	LWS_H2_STATE_HALF_CLOSED_LOCAL,
+	/*
+	 * Send RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv RST_STREAM      -> LWS_H2_STATE_CLOSED
+	 * Recv END_STREAM flag -> LWS_H2_STATE_CLOSED
+	 *
+	 *  - Only WINDOW_UPDATE, PRIORITY, and RST_STREAM valid to send
+	 *  - Any frame valid to receive
+	 */
+	LWS_H2_STATE_CLOSED,
+	/*
+	 *  - Only PRIORITY, WINDOW_UPDATE (IGNORE) and RST_STREAM (IGNORE)
+	 *     may be received
+	 *
+	 *  - Only PRIORITY valid to send
+	 */
+};
 
-	/* optional before first header block */
-	HPKS_OPT_PADDING,
-	HKPS_OPT_E_DEPENDENCY,
-	HKPS_OPT_WEIGHT,
+#define LWS_H2_STREAM_ID_MASTER 0
+#define LWS_H2_SETTINGS_LEN 6
 
-	/* header block */
+enum http2_hpack_state {
 	HPKS_TYPE,
 
 	HPKS_IDX_EXT,
@@ -1438,36 +1516,160 @@ enum http2_hpack_state {
 	HPKS_HLEN_EXT,
 
 	HPKS_DATA,
-
-	/* optional after last header block */
-	HKPS_OPT_DISCARD_PADDING,
 };
 
+/*
+ * lws general parsimonious header strategy is only store values from known
+ * headers, and refer to them by index.
+ *
+ * That means if we can't map the peer header name to one that lws knows, we
+ * will drop the content but track the indexing with associated_lws_hdr_idx =
+ * LWS_HPACK_IGNORE_ENTRY.
+ */
+
 enum http2_hpack_type {
-	HPKT_INDEXED_HDR_7,
-	HPKT_INDEXED_HDR_6_VALUE_INCR,
-	HPKT_LITERAL_HDR_VALUE_INCR,
-	HPKT_INDEXED_HDR_4_VALUE,
-	HPKT_LITERAL_HDR_VALUE,
+	HPKT_INDEXED_HDR_7,		/* 1xxxxxxx: just "header field" */
+	HPKT_INDEXED_HDR_6_VALUE_INCR,  /* 01xxxxxx: NEW indexed hdr with value */
+	HPKT_LITERAL_HDR_VALUE_INCR,	/* 01000000: NEW literal hdr with value */
+	HPKT_INDEXED_HDR_4_VALUE,	/* 0000xxxx: indexed hdr with value */
+	HPKT_INDEXED_HDR_4_VALUE_NEVER,	/* 0001xxxx: indexed hdr with value NEVER NEW */
+	HPKT_LITERAL_HDR_VALUE,		/* 00000000: literal hdr with value */
+	HPKT_LITERAL_HDR_VALUE_NEVER,	/* 00010000: literal hdr with value NEVER NEW */
 	HPKT_SIZE_5
 };
 
+#define LWS_HPACK_IGNORE_ENTRY 0xffff
+
+
 struct hpack_dt_entry {
-	int token; /* additions that don't map to a token are ignored */
-	int arg_offset;
-	int arg_len;
+	char *value; /* malloc'd */
+	uint16_t value_len;
+	uint16_t hdr_len; /* virtual, for accounting */
+	uint16_t lws_hdr_idx; /* LWS_HPACK_IGNORE_ENTRY = IGNORE */
 };
 
 struct hpack_dynamic_table {
-	struct hpack_dt_entry *entries;
-	char *args;
-	int pos;
-	int next;
-	int num_entries;
-	int args_length;
+	struct hpack_dt_entry *entries; /* malloc'd */
+	uint32_t virtual_payload_usage;
+	uint32_t virtual_payload_max;
+	uint16_t pos;
+	uint16_t used_entries;
+	uint16_t num_entries;
+};
+
+enum lws_h2_protocol_send_type {
+	LWS_PPS_NONE,
+	LWS_H2_PPS_MY_SETTINGS,
+	LWS_H2_PPS_ACK_SETTINGS,
+	LWS_H2_PPS_PONG,
+	LWS_H2_PPS_GOAWAY,
+	LWS_H2_PPS_RST_STREAM,
+	LWS_H2_PPS_UPDATE_WINDOW,
 };
 
-struct _lws_http2_related {
+struct lws_h2_protocol_send {
+	struct lws_h2_protocol_send *next; /* linked list */
+	enum lws_h2_protocol_send_type type;
+
+	union uu {
+		struct {
+			char		str[32];
+			uint32_t	highest_sid;
+			uint32_t	err;
+		} ga;
+		struct {
+			uint32_t	sid;
+			uint32_t	err;
+		} rs;
+		struct {
+			uint8_t		ping_payload[8];
+		} ping;
+		struct {
+			uint32_t	sid;
+			uint32_t	credit;
+		} update_window;
+	} u;
+};
+
+struct lws_h2_ghost_sid {
+	struct lws_h2_ghost_sid *next;
+	uint32_t sid;
+};
+
+#define LWS_H2_RX_SCRATCH_SIZE 512
+
+/*
+ * http/2 connection info that is only used by the root connection that has
+ * the network connection.
+ *
+ * h2 tends to spawn many child connections from one network connection, so
+ * it's necessary to make members only needed by the network connection
+ * distinct and only malloc'd on network connections.
+ *
+ * There's only one HPACK parser per network connection.
+ *
+ * But there is an ah per logical child connection... the network connection
+ * fills it but it belongs to the logical child.
+ */
+struct lws_h2_netconn {
+	struct http2_settings set;
+	struct hpack_dynamic_table hpack_dyn_table;
+	uint8_t	ping_payload[8];
+	uint8_t one_setting[LWS_H2_SETTINGS_LEN];
+	char goaway_str[32]; /* for rx */
+	struct lws *swsi;
+	struct lws_h2_protocol_send *pps; /* linked list */
+	char *rx_scratch;
+
+	enum http2_hpack_state hpack;
+	enum http2_hpack_type hpack_type;
+
+	unsigned int huff:1;
+	unsigned int value:1;
+	unsigned int unknown_header:1;
+	unsigned int cont_exp:1;
+	unsigned int cont_exp_headers:1;
+	unsigned int we_told_goaway:1;
+	unsigned int pad_length:1;
+	unsigned int collected_priority:1;
+	unsigned int is_first_header_char:1;
+	unsigned int seen_nonpseudoheader:1;
+	unsigned int zero_huff_padding:1;
+	unsigned int last_action_dyntable_resize:1;
+
+	uint32_t hdr_idx;
+	uint32_t hpack_len;
+	uint32_t hpack_e_dep;
+	uint32_t count;
+	uint32_t preamble;
+	uint32_t length;
+	uint32_t sid;
+	uint32_t inside;
+	uint32_t highest_sid;
+	uint32_t highest_sid_opened;
+	uint32_t cont_exp_sid;
+	uint32_t dep;
+	uint32_t goaway_last_sid;
+	uint32_t goaway_err;
+	uint32_t hpack_hdr_len;
+
+	uint32_t rx_scratch_pos;
+	uint32_t rx_scratch_len;
+
+	uint16_t hpack_pos;
+
+	uint8_t frame_state;
+	uint8_t type;
+	uint8_t flags;
+	uint8_t padding;
+	uint8_t weight_temp;
+	uint8_t huff_pad;
+	char first_hdr_char;
+	uint8_t hpack_m;
+	uint8_t ext_count;
+};
+
+struct _lws_h2_related {
 	/*
 	 * having this first lets us also re-use all HTTP union code
 	 * and in turn, http_mode_related has allocated headers in right
@@ -1475,52 +1677,36 @@ struct _lws_http2_related {
 	 */
 	struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */
 
-	struct http2_settings my_settings;
-	struct http2_settings peer_settings;
-
+	struct lws_h2_netconn *h2n; /* malloc'd for root net conn */
 	struct lws *parent_wsi;
-	struct lws *next_child_wsi;
+	struct lws *child_list;
+	struct lws *sibling_list;
 
-	struct hpack_dynamic_table *hpack_dyn_table;
-	struct lws *stream_wsi;
-	unsigned char ping_payload[8];
-	unsigned char one_setting[LWS_HTTP2_SETTINGS_LENGTH];
+	char *pending_status_body;
 
-	unsigned int count;
-	unsigned int length;
-	unsigned int stream_id;
-	enum http2_hpack_state hpack;
-	enum http2_hpack_type hpack_type;
-	unsigned int header_index;
-	unsigned int hpack_len;
-	unsigned int hpack_e_dep;
-	int tx_credit;
-	unsigned int my_stream_id;
+	int tx_cr;
+	int peer_tx_cr_est;
+	unsigned int my_sid;
 	unsigned int child_count;
 	int my_priority;
+	uint32_t dependent_on;
 
 	unsigned int END_STREAM:1;
 	unsigned int END_HEADERS:1;
 	unsigned int send_END_STREAM:1;
 	unsigned int GOING_AWAY;
 	unsigned int requested_POLLOUT:1;
-	unsigned int waiting_tx_credit:1;
-	unsigned int huff:1;
-	unsigned int value:1;
+	unsigned int skint:1;
 
-	unsigned short round_robin_POLLOUT;
-	unsigned short count_POLLOUT_children;
-	unsigned short hpack_pos;
+	uint16_t round_robin_POLLOUT;
+	uint16_t count_POLLOUT_children;
+	uint8_t h2_state; /* the RFC7540 state of the connection */
+	uint8_t weight;
 
-	unsigned char type;
-	unsigned char flags;
-	unsigned char frame_state;
-	unsigned char padding;
-	unsigned char hpack_m;
-	unsigned char initialized;
+	uint8_t initialized;
 };
 
-#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.http2.parent_wsi)
+#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->u.h2.parent_wsi)
 
 #endif
 
@@ -1589,6 +1775,7 @@ struct lws_cgi {
 	struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
 	struct lws *wsi; /* owner */
 	unsigned char *headers_buf;
+	unsigned char *headers_start;
 	unsigned char *headers_pos;
 	unsigned char *headers_dumped;
 	unsigned char *headers_end;
@@ -1620,6 +1807,13 @@ enum lws_chunk_parser {
 };
 #endif
 
+enum lws_parse_urldecode_results {
+	LPUR_CONTINUE,
+	LPUR_SWALLOW,
+	LPUR_FORBID,
+	LPUR_EXCESSIVE,
+};
+
 struct lws_rewrite;
 
 #ifdef LWS_WITH_ACCESS_LOG
@@ -1640,7 +1834,7 @@ struct lws {
 	union u {
 		struct _lws_http_mode_related http;
 #ifdef LWS_WITH_HTTP2
-		struct _lws_http2_related http2;
+		struct _lws_h2_related h2;
 #endif
 		struct _lws_header_related hdr;
 		struct _lws_websocket_related ws;
@@ -1715,8 +1909,8 @@ struct lws {
 #endif
 	/* ints */
 	int position_in_fds_table;
-	int rxflow_len;
-	int rxflow_pos;
+	uint32_t rxflow_len;
+	uint32_t rxflow_pos;
 	unsigned int trunc_alloc_len; /* size of malloc */
 	unsigned int trunc_offset; /* where we are in terms of spilling */
 	unsigned int trunc_len; /* how much is buffered */
@@ -1727,6 +1921,7 @@ struct lws {
 
 	unsigned int hdr_parsing_completed:1;
 	unsigned int http2_substream:1;
+	unsigned int upgraded_to_http2:1;
 	unsigned int listener:1;
 	unsigned int user_space_externally_allocated:1;
 	unsigned int socket_is_permanently_unusable:1;
@@ -1745,6 +1940,8 @@ struct lws {
 	unsigned int parent_carries_io:1;
 	unsigned int parent_pending_cb_on_writable:1;
 	unsigned int cgi_stdout_zero_length:1;
+	unsigned int seen_zero_length_recv:1;
+	unsigned int rxflow_will_be_applied:1;
 
 #if defined(LWS_WITH_ESP8266)
 	unsigned int pending_send_completion:3;
@@ -1787,17 +1984,17 @@ struct lws {
 #ifndef LWS_NO_EXTENSIONS
 	unsigned char count_act_ext;
 #endif
-	unsigned char ietf_spec_revision;
+	uint8_t ietf_spec_revision;
 	char mode; /* enum connection_mode */
 	char state; /* enum lws_connection_states */
 	char state_pre_close;
 	char lws_rx_parse_state; /* enum lws_rx_parse_state */
 	char rx_frame_type; /* enum lws_write_protocol */
 	char pending_timeout; /* enum pending_timeout */
-	char pps; /* enum lws_pending_protocol_send */
 	char tsi; /* thread service index we belong to */
 	char protocol_interpret_idx;
 	char redirects;
+	uint8_t rxflow_bitmap;
 #ifdef LWS_WITH_CGI
 	char cgi_channel; /* which of stdin/out/err */
 	char hdr_state;
@@ -1810,6 +2007,8 @@ struct lws {
 #endif
 };
 
+#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap))
+
 LWS_EXTERN int log_level;
 
 LWS_EXTERN int
@@ -1849,15 +2048,15 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
 	    int ret, int completion);
 #endif
 
-LWS_EXTERN void
-lws_set_protocol_write_pending(struct lws *wsi,
-			       enum lws_pending_protocol_send pend);
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_client_rx_sm(struct lws *wsi, unsigned char c);
 
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_parse(struct lws *wsi, unsigned char c);
 
+LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse_urldecode(struct lws *wsi, uint8_t *_c);
+
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_http_action(struct lws *wsi);
 
@@ -1963,21 +2162,19 @@ user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
 			    enum lws_callback_reasons reason, void *user,
 			    void *in, size_t len);
 #ifdef LWS_WITH_HTTP2
-LWS_EXTERN struct lws *lws_http2_get_network_wsi(struct lws *wsi);
-struct lws * lws_http2_get_nth_child(struct lws *wsi, int n);
+struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
+LWS_EXTERN void lws_h2_init(struct lws *wsi);
 LWS_EXTERN int
-lws_http2_interpret_settings_payload(struct http2_settings *settings,
+lws_h2_settings(struct lws *nwsi, struct http2_settings *settings,
 				     unsigned char *buf, int len);
-LWS_EXTERN void lws_http2_init(struct http2_settings *settings);
 LWS_EXTERN int
-lws_http2_parser(struct lws *wsi, unsigned char c);
-LWS_EXTERN int lws_http2_do_pps_send(struct lws_context *context,
-				     struct lws *wsi);
-LWS_EXTERN int lws_http2_frame_write(struct lws *wsi, int type, int flags,
+lws_h2_parser(struct lws *wsi, unsigned char c);
+LWS_EXTERN int lws_h2_do_pps_send(struct lws *wsi);
+LWS_EXTERN int lws_h2_frame_write(struct lws *wsi, int type, int flags,
 				     unsigned int sid, unsigned int len,
 				     unsigned char *buf);
 LWS_EXTERN struct lws *
-lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid);
+lws_h2_wsi_from_id(struct lws *wsi, unsigned int sid);
 LWS_EXTERN int lws_hpack_interpret(struct lws *wsi,
 				   unsigned char c);
 LWS_EXTERN int
@@ -1994,10 +2191,26 @@ LWS_EXTERN int
 lws_add_http2_header_status(struct lws *wsi,
 			    unsigned int code, unsigned char **p,
 			    unsigned char *end);
-LWS_EXTERN
-void lws_http2_configure_if_upgraded(struct lws *wsi);
+LWS_EXTERN int
+lws_h2_configure_if_upgraded(struct lws *wsi);
+LWS_EXTERN void
+lws_hpack_destroy_dynamic_header(struct lws *wsi);
+LWS_EXTERN int
+lws_hpack_dynamic_size(struct lws *wsi, int size);
+LWS_EXTERN int
+lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason);
+LWS_EXTERN int
+lws_h2_tx_cr_get(struct lws *wsi);
+LWS_EXTERN void
+lws_h2_tx_cr_consume(struct lws *wsi, int consumed);
+LWS_EXTERN int
+lws_hdr_extant(struct lws *wsi, enum lws_token_indexes h);
+LWS_EXTERN void
+lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
+
+LWS_EXTERN const struct http2_settings lws_h2_defaults;
 #else
-#define lws_http2_configure_if_upgraded(x)
+#define lws_h2_configure_if_upgraded(x)
 #endif
 
 LWS_EXTERN int
diff --git a/lib/server.c b/lib/server.c
index b915759c4bfc677a29ae86e9d18ce8edb3556e32..3ddbb67f89016ded111eb515436de59898fa7bef 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -22,14 +22,12 @@
 
 #include "private-libwebsockets.h"
 
-#if defined(_DEBUG) || defined(LWS_WITH_ACCESS_LOG)
-	static const char * const method_names[] = {
-		"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT",
+static const char * const method_names[] = {
+	"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
 #ifdef LWS_WITH_HTTP2
-		":path",
+	":path",
 #endif
 	};
-#endif
 
 #if defined (LWS_WITH_ESP8266)
 #undef memcpy
@@ -51,10 +49,12 @@ lws_context_init_server(struct lws_context_creation_info *info,
 	struct lws *wsi;
 	int m = 0;
 
+	(void)method_names;
 	(void)opt;
 	/* set up our external listening socket we serve on */
 
-	if (info->port == CONTEXT_PORT_NO_LISTEN || info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
+	if (info->port == CONTEXT_PORT_NO_LISTEN ||
+	    info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
 		return 0;
 
 	vh = vhost->context->vhost_list;
@@ -513,7 +513,8 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 				return -1;
 
 			n = lws_write(wsi, start, p - start,
-				      LWS_WRITE_HTTP_HEADERS);
+				      LWS_WRITE_HTTP_HEADERS |
+				      LWS_WRITE_H2_STREAM_END);
 			if (n != (p - start)) {
 				lwsl_err("_write returned %d from %ld\n", n,
 					 (long)(p - start));
@@ -603,6 +604,8 @@ lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
 			if (hm->origin_protocol == LWSMPRO_CALLBACK ||
 			    ((hm->origin_protocol == LWSMPRO_CGI ||
 			     lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
+			     (wsi->http2_substream &&
+				lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)) ||
 			     hm->protocol) &&
 			    hm->mountpoint_len > best)) {
 				best = hm->mountpoint_len;
@@ -684,7 +687,7 @@ lws_unauthorised_basic_auth(struct lws *wsi)
 	if (lws_finalize_http_header(wsi, &p, end))
 		return -1;
 
-	n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
+	n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
 	if (n < 0)
 		return -1;
 
@@ -732,12 +735,12 @@ int lws_clean_url(char *p)
  *
  */
 #ifdef LWS_WITH_ACCESS_LOG
+static const char * const hver[] = {
+	"http/1.0", "http/1.1", "http/2"
+};
 static void
 lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
 {
-	static const char * const hver[] = {
-		"http/1.0", "http/1.1", "http/2"
-	};
 #ifdef LWS_WITH_IPV6
 	char ads[INET6_ADDRSTRLEN];
 #else
@@ -765,7 +768,12 @@ lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
 		if (!pa)
 			pa = "(unknown)";
 
-		me = method_names[meth];
+		if (wsi->http2_substream)
+			me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
+		else
+			me = method_names[meth];
+		if (!me)
+			me = "(null)";
 
 		lws_snprintf(wsi->access_log.header_log, l,
 			 "%s - - [%s] \"%s %s %s\"",
@@ -817,6 +825,7 @@ static const unsigned char methods[] = {
 	WSI_TOKEN_PATCH_URI,
 	WSI_TOKEN_DELETE_URI,
 	WSI_TOKEN_CONNECT,
+	WSI_TOKEN_HEAD_URI,
 #ifdef LWS_WITH_HTTP2
 	WSI_TOKEN_HTTP_COLON_PATH,
 #endif
@@ -835,7 +844,9 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
 		return -1;
 	}
 
-	if (count != 1) {
+	if (count != 1 &&
+	    !(wsi->http2_substream &&
+	      lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
 		lwsl_warn("multiple methods?\n");
 		return -1;
 	}
@@ -870,7 +881,7 @@ lws_http_action(struct lws *wsi)
 	};
 
 	meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
-	if (meth < 0)
+	if (meth < 0 || meth >= ARRAY_SIZE(method_names))
 		goto bail_nuke_ah;
 
 	/* we insist on absolute paths */
@@ -881,24 +892,24 @@ lws_http_action(struct lws *wsi)
 		goto bail_nuke_ah;
 	}
 
-	lwsl_info("Method: %s request for '%s'\n", method_names[meth], uri_ptr);
+	lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth], meth, uri_ptr);
 
 	if (lws_ensure_user_space(wsi))
 		goto bail_nuke_ah;
 
 	/* HTTP header had a content length? */
 
-	wsi->u.http.content_length = 0;
+	wsi->u.http.rx_content_length = 0;
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
 		lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
 		lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
-		wsi->u.http.content_length = 100 * 1024 * 1024;
+		wsi->u.http.rx_content_length = 100 * 1024 * 1024;
 
 	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
 		lws_hdr_copy(wsi, content_length_str,
 			     sizeof(content_length_str) - 1,
 			     WSI_TOKEN_HTTP_CONTENT_LENGTH);
-		wsi->u.http.content_length = atoll(content_length_str);
+		wsi->u.http.rx_content_length = atoll(content_length_str);
 	}
 
 	if (wsi->http2_substream) {
@@ -1026,20 +1037,26 @@ lws_http_action(struct lws *wsi)
 
 		lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
 
-		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
-			goto bail_nuke_ah;
-
 		/* > at start indicates deal with by redirect */
 		if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
 		    hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
 			n = lws_snprintf((char *)end, 256, "%s%s",
 				    oprot[hit->origin_protocol & 1],
 				    hit->origin);
-		else
-			n = lws_snprintf((char *)end, 256,
-			    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
-			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
-			    uri_ptr);
+		else {
+			if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
+				if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY))
+					goto bail_nuke_ah;
+				n = lws_snprintf((char *)end, 256,
+				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
+				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY),
+				    uri_ptr);
+			} else
+				n = lws_snprintf((char *)end, 256,
+				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
+				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
+				    uri_ptr);
+		}
 
 		lws_clean_url((char *)end);
 		n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
@@ -1300,10 +1317,15 @@ deal_body:
 	 * In any case, return 0 and let lws_read decide how to
 	 * proceed based on state
 	 */
-	if (wsi->state != LWSS_HTTP_ISSUING_FILE)
+	if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
 		/* Prepare to read body if we have a content length: */
-		if (wsi->u.http.content_length > 0)
+		lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n", (long long)wsi->u.http.rx_content_length, wsi->upgraded_to_http2, wsi->http2_substream);
+		if (wsi->u.http.rx_content_length > 0) {
+			lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n", __func__, wsi);
 			wsi->state = LWSS_HTTP_BODY;
+			wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
+		}
+	}
 
 	return 0;
 
@@ -1397,10 +1419,13 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 		assert(0);
 	}
 
+	lwsl_hexdump(*buf, len);
+
 	while (len--) {
 		wsi->more_rx_waiting = !!len;
 
 		if (wsi->mode != LWSCM_HTTP_SERVING &&
+		    wsi->mode != LWSCM_HTTP2_SERVING &&
 		    wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
 			lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
 			goto bail_nuke_ah;
@@ -1461,10 +1486,12 @@ raw_transition:
 		} else
 			lwsl_info("no host\n");
 
-		wsi->vhost->conn_stats.trans++;
-		if (!wsi->conn_stat_done) {
-			wsi->vhost->conn_stats.conn++;
-			wsi->conn_stat_done = 1;
+		if (wsi->mode != LWSCM_HTTP2_SERVING) {
+			wsi->vhost->conn_stats.h1_trans++;
+			if (!wsi->conn_stat_done) {
+				wsi->vhost->conn_stats.h1_conn++;
+				wsi->conn_stat_done = 1;
+			}
 		}
 
 		/* check for unwelcome guests */
@@ -1533,7 +1560,7 @@ raw_transition:
 #ifdef LWS_WITH_HTTP2
 			if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
 					"h2c")) {
-				wsi->vhost->conn_stats.http2_upg++;
+				wsi->vhost->conn_stats.h2_upg++;
 				lwsl_info("Upgrade to h2c\n");
 				goto upgrade_h2c;
 			}
@@ -1588,18 +1615,24 @@ upgrade_h2c:
 		/* http2 union member has http union struct at start */
 		wsi->u.http.ah = ah;
 
-		lws_http2_init(&wsi->u.http2.peer_settings);
-		lws_http2_init(&wsi->u.http2.my_settings);
+		if (!wsi->u.h2.h2n) {
+			wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
+			if (!wsi->u.h2.h2n)
+				return 1;
+		}
+
+		lws_h2_init(wsi);
 
 		/* HTTP2 union */
 
-		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
+		lws_h2_settings(wsi, &wsi->u.h2.h2n->set,
 				(unsigned char *)protocol_list, n);
 
-		strcpy(protocol_list,
-		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
-		      "Connection: Upgrade\x0d\x0a"
-		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
+		lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
+
+		strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
+				      "Connection: Upgrade\x0d\x0a"
+				      "Upgrade: h2c\x0d\x0a\x0d\x0a");
 		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
 					strlen(protocol_list));
 		if (n != strlen(protocol_list)) {
@@ -2053,8 +2086,8 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
 	}
 
 	new_wsi->tsi = n;
-	lwsl_debug("Accepted wsi %p to context %p, tsi %d\n", new_wsi,
-		    vhost->context, new_wsi->tsi);
+	lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
+		   vhost->name, new_wsi->tsi);
 
 	new_wsi->vhost = vhost;
 	new_wsi->context = vhost->context;
@@ -2073,7 +2106,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
 
 	/*
 	 * these can only be set once the protocol is known
-	 * we set an unestablished connection's protocol pointer
+	 * we set an un-established connection's protocol pointer
 	 * to the start of the supported list, so it can look
 	 * for matching ones during the handshake
 	 */
@@ -2100,6 +2133,8 @@ lws_http_transaction_completed(struct lws *wsi)
 {
 	int n = NO_PENDING_TIMEOUT;
 
+	lwsl_info("%s: wsi %p\n", __func__, wsi);
+
 	lws_access_log(wsi);
 
 	if (!wsi->hdr_parsing_completed) {
@@ -2109,6 +2144,12 @@ lws_http_transaction_completed(struct lws *wsi)
 
 	lwsl_debug("%s: wsi %p\n", __func__, wsi);
 	/* if we can't go back to accept new headers, drop the connection */
+	if (wsi->http2_substream)
+		return 0;
+
+	if (wsi->seen_zero_length_recv)
+		return 1;
+
 	if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
 		lwsl_info("%s: %p: close connection\n", __func__, wsi);
 		return 1;
@@ -2120,8 +2161,8 @@ lws_http_transaction_completed(struct lws *wsi)
 	/* otherwise set ourselves up ready to go again */
 	wsi->state = LWSS_HTTP;
 	wsi->mode = LWSCM_HTTP_SERVING;
-	wsi->u.http.content_length = 0;
-	wsi->u.http.content_remain = 0;
+	wsi->u.http.tx_content_length = 0;
+	wsi->u.http.tx_content_remain = 0;
 	wsi->hdr_parsing_completed = 0;
 #ifdef LWS_WITH_ACCESS_LOG
 	wsi->access_log.sent = 0;
@@ -2144,7 +2185,7 @@ lws_http_transaction_completed(struct lws *wsi)
 	 * reset the existing header table and keep it.
 	 */
 	if (wsi->u.hdr.ah) {
-		lwsl_info("%s: wsi->more_rx_waiting=%d\n", __func__,
+		lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
 				wsi->more_rx_waiting);
 
 		if (!wsi->more_rx_waiting) {
@@ -2426,7 +2467,7 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
 		ah = wsi->u.hdr.ah;
 		memcpy(ah->rx, readbuf, len);
 		ah->rxpos = 0;
-		ah->rxlen = len;
+		ah->rxlen = (int16_t)len;
 
 		lwsl_notice("%s: calling service on readbuf ah\n", __func__);
 		pt = &wsi->context->pt[(int)wsi->tsi];
@@ -2539,6 +2580,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 #if !defined(LWS_WITH_ESP8266)
 		if (wsi->favoured_pollin &&
 		    (pollfd->revents & pollfd->events & LWS_POLLOUT)) {
+			lwsl_notice("favouring pollout\n");
 			wsi->favoured_pollin = 0;
 			goto try_pollout;
 		}
@@ -2565,7 +2607,10 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 				ah->rxpos = 0;
 				switch (ah->rxlen) {
 				case 0:
-					lwsl_info("%s: read 0 len\n", __func__);
+					lwsl_info("%s: read 0 len a\n", __func__);
+					wsi->seen_zero_length_recv = 1;
+					lws_change_pollfd(wsi, LWS_POLLIN, 0);
+					goto try_pollout;
 					/* fallthru */
 				case LWS_SSL_CAPABLE_ERROR:
 					goto fail;
@@ -2592,25 +2637,31 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 			/* just ignore incoming if waiting for close */
 			if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
 			    wsi->state != LWSS_HTTP_ISSUING_FILE) {
+				/*
+				 * otherwise give it to whoever wants it
+				 * according to the connection state
+				 */
+
 				n = lws_read(wsi, ah->rx + ah->rxpos,
 					     ah->rxlen - ah->rxpos);
 				if (n < 0) /* we closed wsi */
 					return 1;
-				if (wsi->u.hdr.ah) {
-					if ( wsi->u.hdr.ah->rxlen)
-						 wsi->u.hdr.ah->rxpos += n;
-
-					lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
-						   __func__, wsi,
-						   wsi->u.hdr.ah->rxpos,
-						   wsi->u.hdr.ah->rxlen);
-
-					if (lws_header_table_is_in_detachable_state(wsi) &&
-					    (wsi->mode != LWSCM_HTTP_SERVING &&
-					     wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
-					     wsi->mode != LWSCM_HTTP2_SERVING))
-						lws_header_table_detach(wsi, 1);
-				}
+
+				if (!wsi->u.hdr.ah)
+					break;
+				if ( wsi->u.hdr.ah->rxlen)
+					 wsi->u.hdr.ah->rxpos += n;
+
+				lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
+					   __func__, wsi, wsi->u.hdr.ah->rxpos,
+					   wsi->u.hdr.ah->rxlen);
+
+				if (lws_header_table_is_in_detachable_state(wsi) &&
+				    (wsi->mode != LWSCM_HTTP_SERVING &&
+				     wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
+				     wsi->mode != LWSCM_HTTP2_SERVING))
+					lws_header_table_detach(wsi, 1);
+
 				break;
 			}
 
@@ -2622,7 +2673,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 		lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
 		switch (len) {
 		case 0:
-			lwsl_info("%s: read 0 len\n", __func__);
+			lwsl_info("%s: read 0 len b\n", __func__);
 
 			/* fallthru */
 		case LWS_SSL_CAPABLE_ERROR:
@@ -2661,6 +2712,13 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 				wsi->favoured_pollin = 1;
 			break;
 		}
+		/*
+		 *  he may have used up the
+		 * writability above, if we will defer POLLOUT
+		 * processing in favour of POLLIN, note it
+		 */
+		if (pollfd->revents & LWS_POLLOUT)
+			wsi->favoured_pollin = 1;
 
 try_pollout:
 		
@@ -3050,7 +3108,9 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 	wsi->u.http.filepos = 0;
 	wsi->state = LWSS_HTTP_ISSUING_FILE;
 
-	return lws_serve_http_file_fragment(wsi);
+	lws_callback_on_writable(wsi);
+
+	return 0;
 }
 
 int
@@ -3069,7 +3129,7 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
 		/*
 		 * we were accepting input but now we stopped doing so
 		 */
-		if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+		if (wsi->rxflow_bitmap) {
 			lws_rxflow_cache(wsi, *buf, 0, len);
 			lwsl_parser("%s: cached %ld\n", __func__, (long)len);
 			return 1;
@@ -3083,8 +3143,10 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
 		}
 
 		/* account for what we're using in rxflow buffer */
-		if (wsi->rxflow_buffer)
+		if (wsi->rxflow_buffer) {
 			wsi->rxflow_pos++;
+			assert(wsi->rxflow_pos <= wsi->rxflow_len);
+		}
 
 		/* consume payload bytes efficiently */
 		if (
diff --git a/lib/service.c b/lib/service.c
index 23d51bbf72b2c3415340d76e73a834be5a4546f4..49760f828c9d7246edf37b8d9061d1572b51a87a 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -73,7 +73,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	int write_type = LWS_WRITE_PONG;
 	struct lws_tokens eff_buf;
 #ifdef LWS_WITH_HTTP2
-	struct lws *wsi2;
+	struct lws **wsi2, *wsi2a;
 #endif
 	int ret, m, n;
 
@@ -95,6 +95,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	 *	       corrupted.
 	 */
 	if (wsi->trunc_len) {
+		//lwsl_notice("%s: completing partial\n", __func__);
 		if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
 				  wsi->trunc_len) < 0) {
 			lwsl_info("%s signalling to close\n", __func__);
@@ -115,18 +116,18 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	/*
 	 * Priority 2: protocol packets
 	 */
-	if (wsi->pps) {
-		lwsl_info("servicing pps %d\n", wsi->pps);
-		switch (wsi->pps) {
-		case LWS_PPS_HTTP2_MY_SETTINGS:
-		case LWS_PPS_HTTP2_ACK_SETTINGS:
-			lws_http2_do_pps_send(lws_get_context(wsi), wsi);
-			break;
-		default:
-			break;
+	if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) {
+		lwsl_info("servicing pps\n");
+		if (lws_h2_do_pps_send(wsi)) {
+			wsi->socket_is_permanently_unusable = 1;
+			goto bail_die;
 		}
-		wsi->pps = LWS_PPS_NONE;
-		lws_rx_flow_control(wsi, 1);
+		if (wsi->u.h2.h2n->pps)
+			goto bail_ok;
+
+		/* we can resume whatever we were doing */
+		lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE |
+					 LWS_RXFLOW_REASON_H2_PPS_PENDING);
 
 		goto bail_ok; /* leave POLLOUT active */
 	}
@@ -385,33 +386,146 @@ user_service_go_again:
 		goto notify;
 	}
 
-	wsi->u.http2.requested_POLLOUT = 0;
-	if (!wsi->u.http2.initialized) {
+	wsi->u.h2.requested_POLLOUT = 0;
+	if (!wsi->u.h2.initialized) {
 		lwsl_info("pollout on uninitialized http2 conn\n");
 		goto bail_ok;
 	}
 
-	lwsl_info("%s: doing children\n", __func__);
+//	if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) {
+//		lws_callback_on_writable(wsi);
+//		goto bail_ok;
+//	}
+
+	lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi);
+	wsi2a = wsi->u.h2.child_list;
+	while (wsi2a) {
+		if (wsi2a->u.h2.requested_POLLOUT)
+			lwsl_debug("  * %p\n", wsi2a);
+		else
+			lwsl_debug("    %p\n", wsi2a);
+
+		wsi2a = wsi2a->u.h2.sibling_list;
+	}
+
+	wsi2 = &wsi->u.h2.child_list;
+	if (!*wsi2)
+		goto bail_ok;
 
-	wsi2 = wsi;
 	do {
-		wsi2 = wsi2->u.http2.next_child_wsi;
-		lwsl_info("%s: child %p\n", __func__, wsi2);
-		if (!wsi2)
-			continue;
-		if (!wsi2->u.http2.requested_POLLOUT)
-			continue;
-		wsi2->u.http2.requested_POLLOUT = 0;
-		if (lws_calllback_as_writeable(wsi2)) {
+		struct lws *w, **wa;
+	
+		wa = &(*wsi2)->u.h2.sibling_list;
+		if (!(*wsi2)->u.h2.requested_POLLOUT) {
+			lwsl_debug("  child %p doesn't want POLLOUT\n", *wsi2);
+			goto next_child;
+		}
+
+		/*
+		 * we're going to do writable callback for this child.
+		 * move him to be the last child
+		 */
+
+		lwsl_debug("servicing child %p\n", *wsi2);
+
+		w = *wsi2;
+		while (w) {
+			if (!w->u.h2.sibling_list) { /* w is the current last */
+				lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2);
+				if (w == *wsi2) /* we are already last */
+					break;
+				w->u.h2.sibling_list = *wsi2; /* last points to us as new last */
+				*wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */
+				w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */
+				w = w->u.h2.sibling_list; /* w becomes us */
+				break;
+			}
+			w = w->u.h2.sibling_list;
+		}
+
+		w->u.h2.requested_POLLOUT = 0;
+		lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state);
+
+		if (w->u.h2.pending_status_body) {
+			w->u.h2.send_END_STREAM = 1;
+			n = lws_write(w,
+				      (uint8_t *)w->u.h2.pending_status_body + LWS_PRE,
+				      strlen(w->u.h2.pending_status_body + LWS_PRE),
+				      LWS_WRITE_HTTP_FINAL);
+			lws_free_set_NULL(w->u.h2.pending_status_body);
+			lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
+			wa = &wsi->u.h2.child_list;
+			goto next_child;
+		}
+
+		if (w->state == LWSS_HTTP_ISSUING_FILE) {
+
+			w->leave_pollout_active = 0;
+
+			/* >0 == completion, <0 == error
+			 *
+			 * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+			 * it's done.  That's the case even if we just completed the
+			 * send, so wait for that.
+			 */
+			n = lws_serve_http_file_fragment(w);
+			lwsl_debug("lws_serve_http_file_fragment says %d\n", n);
+
+			/*
+			 * We will often hear about out having sent the final
+			 * DATA here... if so close the actual wsi
+			 */
+			if (n < 0 || w->u.h2.send_END_STREAM) {
+				lwsl_debug("Closing POLLOUT child %p\n", w);
+				lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
+				wa = &wsi->u.h2.child_list;
+				goto next_child;
+			}
+			if (n > 0)
+				if (lws_http_transaction_completed(w))
+					goto bail_die;
+			if (!n) {
+				lws_callback_on_writable(w);
+				(w)->u.h2.requested_POLLOUT = 1;
+			}
+
+			goto next_child;
+		}
+
+		if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) {
 			lwsl_debug("Closing POLLOUT child\n");
-			lws_close_free_wsi(wsi2, LWS_CLOSE_STATUS_NOSTATUS);
+			lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
+			wa = &wsi->u.h2.child_list;
 		}
-		wsi2 = wsi;
-	} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
 
-	lwsl_info("%s: completed\n", __func__);
+next_child:
+		wsi2 = wa;
+	} while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
+
+	lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list);
+	wsi2a = wsi->u.h2.child_list;
+	while (wsi2a) {
+		if (wsi2a->u.h2.requested_POLLOUT)
+			lwsl_debug("  * %p\n", wsi2a);
+		else
+			lwsl_debug("    %p\n", wsi2a);
+
+		wsi2a = wsi2a->u.h2.sibling_list;
+	}
+
+
+	wsi2a = wsi->u.h2.child_list;
+	while (wsi2a) {
+		if (wsi2a->u.h2.requested_POLLOUT) {
+			lws_change_pollfd(wsi, 0, LWS_POLLOUT);
+			break;
+		}
+		wsi2a = wsi2a->u.h2.sibling_list;
+	}
 
 	goto bail_ok;
+
+
 notify:
 #endif
 	wsi->handling_pollout = 0;
@@ -502,15 +616,42 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
 
 int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
 {
+#if defined(LWS_WITH_HTTP2)
+	if (wsi->upgraded_to_http2) {
+		struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+
+		assert(h2n->rx_scratch);
+		buf += n;
+		len -= n;
+		assert ((char *)buf >= (char *)h2n->rx_scratch &&
+			(char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]);
+
+		h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch);
+		h2n->rx_scratch_len = len;
+
+		lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi);
+
+		return 0;
+	}
+#endif
 	/* his RX is flowcontrolled, don't send remaining now */
 	if (wsi->rxflow_buffer) {
-		/* rxflow while we were spilling prev rxflow */
-		lwsl_info("stalling in existing rxflow buf\n");
-		return 1;
+		if (buf >= wsi->rxflow_buffer &&
+		    &buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) {
+			/* rxflow while we were spilling prev rxflow */
+			lwsl_info("%s: staying in rxflow buf\n", __func__);
+			return 1;
+		} else {
+			lwsl_err("%s: conflicting rxflow buf, "
+				 "current %p len %d, new %p len %d\n", __func__,
+				 wsi->rxflow_buffer, wsi->rxflow_len, buf, len);
+			assert(0);
+			return 1;
+		}
 	}
 
 	/* a new rxflow, buffer it and warn caller */
-	lwsl_info("new rxflow input buffer len %d\n", len - n);
+	lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n);
 	wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf");
 	if (!wsi->rxflow_buffer)
 		return -1;
@@ -735,9 +876,9 @@ spin_chunks:
 	if (wsi->chunked && !wsi->chunk_remaining)
 		return 0;
 
-	if (wsi->u.http.content_remain &&
-	    wsi->u.http.content_remain < *len)
-		n = (int)wsi->u.http.content_remain;
+	if (wsi->u.http.rx_content_remain &&
+	    wsi->u.http.rx_content_remain < *len)
+		n = (int)wsi->u.http.rx_content_remain;
 	else
 		n = *len;
 
@@ -775,10 +916,10 @@ spin_chunks:
 		return 0;
 
 	/* if we know the content length, decrement the content remaining */
-	if (wsi->u.http.content_length > 0)
-		wsi->u.http.content_remain -= n;
+	if (wsi->u.http.rx_content_length > 0)
+		wsi->u.http.rx_content_remain -= n;
 
-	if (wsi->u.http.content_remain || !wsi->u.http.content_length)
+	if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length)
 		return 0;
 
 completed:
@@ -1071,7 +1212,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 
 		default:
 			n = SSL_get_error(wsi->ssl, n);
-			if (n != SSL_ERROR_SYSCALL) {
+			if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
 				if (SSL_want_read(wsi->ssl)) {
 					lwsl_debug("(wants read)\n");
 					lws_change_pollfd(wsi, 0, LWS_POLLIN);
@@ -1194,12 +1335,23 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 			 */
 			break;
 
-		if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
+		if (lws_is_flowcontrolled(wsi))
 			/* We cannot deal with any kind of new RX
 			 * because we are RX-flowcontrolled.
 			 */
 			break;
 
+#if defined(LWS_WITH_HTTP2)
+		wsi1 = lws_get_network_wsi(wsi);
+		if (wsi1 && wsi1->trunc_len)
+			/* We cannot deal with any kind of new RX
+			 * because we are dealing with a partial send
+			 * (new RX may trigger new http_action() that expect
+			 * to be able to send)
+			 */
+			break;
+#endif
+
 		/* 2: RX Extension needs to be drained
 		 */
 
@@ -1228,13 +1380,14 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 			 */
 			break;
 
-		/* 3: RX Flowcontrol buffer needs to be drained
+		/* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained
 		 */
 
 		if (wsi->rxflow_buffer) {
 			lwsl_info("draining rxflow (len %d)\n",
 				wsi->rxflow_len - wsi->rxflow_pos
 			);
+			assert(wsi->rxflow_pos < wsi->rxflow_len);
 			/* well, drain it */
 			eff_buf.token = (char *)wsi->rxflow_buffer +
 						wsi->rxflow_pos;
@@ -1243,14 +1396,36 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 			goto drain;
 		}
 
+#if defined(LWS_WITH_HTTP2)
+		if (wsi->upgraded_to_http2) {
+			struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+
+			if (h2n->rx_scratch_len) {
+				lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n",
+					  __func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len);
+				eff_buf.token = (char *)h2n->rx_scratch +
+						h2n->rx_scratch_pos;
+				eff_buf.token_len = h2n->rx_scratch_len;
+
+				h2n->rx_scratch_len = 0;
+				goto drain;
+			}
+		}
+#endif
+
 		/* 4: any incoming (or ah-stashed incoming rx) data ready?
 		 * notice if rx flow going off raced poll(), rx flow wins
 		 */
 
 		if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
 			break;
-
 read:
+		if (lws_is_flowcontrolled(wsi)) {
+			lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
+				    __func__, wsi, wsi->rxflow_bitmap);
+			break;
+		}
+
 		/* all the union members start with hdr, so even in ws mode
 		 * we can deal with the ah via u.hdr
 		 */
@@ -1267,15 +1442,32 @@ read:
 				 * as to what it can output...) has to go in per-wsi rx buf area.
 				 * Otherwise in large temp serv_buf area.
 				 */
-				eff_buf.token = (char *)pt->serv_buf;
-				if (lws_is_ws_with_ext(wsi)) {
-					eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
-				} else {
-					eff_buf.token_len = context->pt_serv_buf_size;
+
+#if defined(LWS_WITH_HTTP2)
+				if (wsi->upgraded_to_http2) {
+					if (!wsi->u.h2.h2n->rx_scratch) {
+						wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch");
+						if (!wsi->u.h2.h2n->rx_scratch)
+							goto close_and_handled;
+					}
+					eff_buf.token = wsi->u.h2.h2n->rx_scratch;
+					eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE;
+				} else
+#endif
+				{
+					eff_buf.token = (char *)pt->serv_buf;
+					if (lws_is_ws_with_ext(wsi)) {
+						eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
+					} else {
+						eff_buf.token_len = context->pt_serv_buf_size;
+					}
+
+					if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
+						eff_buf.token_len = context->pt_serv_buf_size;
 				}
 
-				if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
-					eff_buf.token_len = context->pt_serv_buf_size;
+				if ((int)pending > eff_buf.token_len)
+					pending = eff_buf.token_len;
 
 				eff_buf.token_len = lws_ssl_capable_read(wsi,
 					(unsigned char *)eff_buf.token, pending ? pending :
@@ -1361,7 +1553,6 @@ drain:
 				 * around again it will pick up from where it
 				 * left off.
 				 */
-
 				n = lws_read(wsi, (unsigned char *)eff_buf.token,
 					     eff_buf.token_len);
 				if (n < 0) {
@@ -1376,8 +1567,7 @@ drain:
 		} while (more);
 
 		if (wsi->u.hdr.ah) {
-			lwsl_notice("%s: %p: detaching\n",
-				 __func__, wsi);
+			lwsl_debug("%s: %p: detaching\n", __func__, wsi);
 			lws_header_table_force_to_detachable_state(wsi);
 			/* we can run the normal ah detach flow despite
 			 * being in ws union mode, since all union members
@@ -1398,7 +1588,7 @@ drain:
 
 		if (draining_flow && wsi->rxflow_buffer &&
 		    wsi->rxflow_pos == wsi->rxflow_len) {
-			lwsl_info("flow buffer: drained\n");
+			lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
 			lws_free_set_NULL(wsi->rxflow_buffer);
 			/* having drained the rxflow buffer, can rearm POLLIN */
 #ifdef LWS_NO_SERVER
diff --git a/lib/ssl-http2.c b/lib/ssl-http2.c
index 9c25646f12517a41b0b5546583275450b272d3a6..4931d90a49846a2229987d2d51b56cf5591f4bd1 100644
--- a/lib/ssl-http2.c
+++ b/lib/ssl-http2.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2017 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
@@ -49,8 +49,8 @@
 
 #include "private-libwebsockets.h"
 
-#ifndef LWS_NO_SERVER
-#ifdef LWS_OPENSSL_SUPPORT
+#if !defined(LWS_NO_SERVER)
+#if defined(LWS_OPENSSL_SUPPORT)
 
 #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
 
@@ -59,29 +59,19 @@ struct alpn_ctx {
 	unsigned short len;
 };
 
-static int
-npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
-{
-	struct alpn_ctx *alpn_ctx = arg;
-
-	lwsl_info("%s\n", __func__);
-	*data = alpn_ctx->data;
-	*len = alpn_ctx->len;
-
-	return SSL_TLSEXT_ERR_OK;
-}
 
 static int
 alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
 	const unsigned char *in, unsigned int inlen, void *arg)
 {
+#if !defined(LWS_WITH_MBEDTLS)
 	struct alpn_ctx *alpn_ctx = arg;
 
 	if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
 				  alpn_ctx->len, in, inlen) !=
 	    OPENSSL_NPN_NEGOTIATED)
 		return SSL_TLSEXT_ERR_NOACK;
-
+#endif
 	return SSL_TLSEXT_ERR_OK;
 }
 #endif
@@ -93,9 +83,6 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost)
 	static struct alpn_ctx protos = { (unsigned char *)"\x02h2"
 					  "\x08http/1.1", 6 + 9 };
 
-	SSL_CTX_set_next_protos_advertised_cb(vhost->ssl_ctx, npn_cb, &protos);
-
-	// ALPN selection callback
 	SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &protos);
 	lwsl_notice(" HTTP2 / ALPN enabled\n");
 #else
@@ -105,34 +92,36 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost)
 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
 }
 
-void lws_http2_configure_if_upgraded(struct lws *wsi)
+int lws_h2_configure_if_upgraded(struct lws *wsi)
 {
 #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
 	struct allocated_headers *ah;
-	const char *method = "alpn";
-	const unsigned char *name;
+	const unsigned char *name = NULL;
+	char cstr[10];
 	unsigned len;
 
 	SSL_get0_alpn_selected(wsi->ssl, &name, &len);
-
 	if (!len) {
-		SSL_get0_next_proto_negotiated(wsi->ssl, &name, &len);
-		method = "npn";
+		lwsl_info("no ALPN upgrade\n");
+		return 0;
 	}
 
-	if (!len) {
-		lwsl_info("no npn/alpn upgrade\n");
-		return;
-	}
+	if (len > sizeof(cstr) - 1)
+		len = sizeof(cstr) - 1;
 
-	(void)method;
-	lwsl_info("negotiated %s using %s\n", name, method);
+	memcpy(cstr, name, len);
+	cstr[len] = '\0';
+
+	lwsl_info("negotiated '%s' using ALPN\n", cstr);
 	wsi->use_ssl = 1;
 	if (strncmp((char *)name, "http/1.1", 8) == 0)
-		return;
+		return 0;
 
 	/* http2 */
 
+	wsi->upgraded_to_http2 = 1;
+	wsi->vhost->conn_stats.h2_alpn++;
+
 	/* adopt the header info */
 
 	ah = wsi->u.hdr.ah;
@@ -143,13 +132,21 @@ void lws_http2_configure_if_upgraded(struct lws *wsi)
 	/* http2 union member has http union struct at start */
 	wsi->u.http.ah = ah;
 
+	wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
+	if (!wsi->u.h2.h2n)
+		return 1;
 
-	lws_http2_init(&wsi->u.http2.peer_settings);
-	lws_http2_init(&wsi->u.http2.my_settings);
+	lws_h2_init(wsi);
 
 	/* HTTP2 union */
+
+	lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
+	wsi->u.h2.tx_cr = 65535;
+
+	lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi);
+
+	return 0;
 #endif
 }
-
 #endif
 #endif
diff --git a/lib/ssl-server.c b/lib/ssl-server.c
index a7590bb003aee2eb98827c227683251b689a87bb..a9516f2239da7d1bfff26c6ef5127a478ebc669c 100644
--- a/lib/ssl-server.c
+++ b/lib/ssl-server.c
@@ -141,18 +141,13 @@ lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
 static int
 lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
 {
-	struct lws_context *context;
+	struct lws_context *context = (struct lws_context *)arg;
 	struct lws_vhost *vhost, *vh;
 	const char *servername;
-	int port;
 
 	if (!ssl)
 		return SSL_TLSEXT_ERR_NOACK;
 
-	context = (struct lws_context *)SSL_CTX_get_ex_data(
-					SSL_get_SSL_CTX(ssl),
-					openssl_SSL_CTX_private_data_index);
-
 	/*
 	 * We can only get ssl accepted connections by using a vhost's ssl_ctx
 	 * find out which listening one took us and only match vhosts on the
@@ -165,22 +160,31 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
 		vh = vh->vhost_next;
 	}
 
-	assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */
-	port = vh->listen_port;
+	if (!vh) {
+		assert(vh); /* can't match the incoming vh? */
+		return SSL_TLSEXT_ERR_OK;
+	}
 
 	servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+	if (!servername) {
+		/* the client doesn't know what hostname it wants */
+		lwsl_info("SNI: Unknown ServerName: %s\n", servername);
 
-	if (servername) {
-		vhost = lws_select_vhost(context, port, servername);
-		if (vhost) {
-			lwsl_debug("SNI: Found: %s (port %d)\n",
-				   servername, port);
-			SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
-			return SSL_TLSEXT_ERR_OK;
-		}
-		lwsl_err("SNI: Unknown ServerName: %s\n", servername);
+		return SSL_TLSEXT_ERR_OK;
 	}
 
+	vhost = lws_select_vhost(context, vh->listen_port, servername);
+	if (!vhost) {
+		lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port);
+
+		return SSL_TLSEXT_ERR_OK;
+	}
+
+	lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port);
+
+	/* select the ssl ctx from the selected vhost for this conn */
+	SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
+
 	return SSL_TLSEXT_ERR_OK;
 }
 #endif
@@ -319,6 +323,7 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 #if !defined(LWS_WITH_MBEDTLS) && !defined(OPENSSL_NO_TLSEXT)
 	SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
 					       lws_ssl_server_name_cb);
+	SSL_CTX_set_tlsext_servername_arg(vhost->ssl_ctx, context);
 #endif
 
 	/*
@@ -405,18 +410,20 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
 		p = NULL;
 #endif
 
-		if (alloc_pem_to_der_file(vhost->context,
-			       info->ssl_private_key_filepath, &p, &flen)) {
-			lwsl_err("couldn't find cert file %s\n",
-				 info->ssl_cert_filepath);
+		if (info->ssl_private_key_filepath) {
+			if (alloc_pem_to_der_file(vhost->context,
+				       info->ssl_private_key_filepath, &p, &flen)) {
+				lwsl_err("couldn't find cert file %s\n",
+					 info->ssl_cert_filepath);
 
-			return 1;
-		}
-		err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
-		if (!err) {
-			lwsl_err("Problem loading key\n");
+				return 1;
+			}
+			err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
+			if (!err) {
+				lwsl_err("Problem loading key\n");
 
-			return 1;
+				return 1;
+			}
 		}
 
 #if !defined(LWS_WITH_ESP32)
diff --git a/lib/ssl.c b/lib/ssl.c
index 450fbc382bd507eb4e965df28ff7bc660580d6f9..15bc810b99a6eddd2dfa81f222a6264ee34cb892 100644
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -94,7 +94,7 @@ int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
 	size_t s;
 	int n = 0;
 
-	f =fopen(filename, "rb");
+	f = fopen(filename, "rb");
 	if (f == NULL) {
 		n = 1;
 		goto bail;
@@ -131,7 +131,9 @@ int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
 	*amount = s;
 
 bail:
-	fclose(f);
+	if (f)
+		fclose(f);
+
 	return n;
 
 }
@@ -414,10 +416,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
 {
 	struct lws_context *context = wsi->context;
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
-	int n = 0;
-#if !defined(LWS_WITH_MBEDTLS)
-	int ssl_read_errno = 0;
-#endif
+	int n = 0, m;
 
 	if (!wsi->ssl)
 		return lws_ssl_capable_read_no_ssl(wsi, buf, len);
@@ -445,29 +444,18 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
 	lwsl_debug("%p: SSL_read says %d\n", wsi, n);
 	/* manpage: returning 0 means connection shut down */
 	if (!n) {
-		n = lws_ssl_get_error(wsi, n);
-		lwsl_debug("%p: ssl err %d errno %d\n", wsi, n, errno);
-		if (n == SSL_ERROR_ZERO_RETURN)
-			return LWS_SSL_CAPABLE_ERROR;
-
-		if (n == SSL_ERROR_SYSCALL) {
-#if !defined(LWS_WITH_MBEDTLS)
-			int err = ERR_get_error();
-			if (err == 0 && (ssl_read_errno == EPIPE ||
-					 ssl_read_errno == ECONNABORTED ||
-					 ssl_read_errno == 0))
-				return LWS_SSL_CAPABLE_ERROR;
-#endif
-		}
-
-		lwsl_info("%s failed: %s\n",__func__,
-			 ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
-		lws_ssl_elaborate_error();
+		wsi->socket_is_permanently_unusable = 1;
 
 		return LWS_SSL_CAPABLE_ERROR;
 	}
 
 	if (n < 0) {
+		m = lws_ssl_get_error(wsi, n);
+		lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
+		if (m == SSL_ERROR_ZERO_RETURN ||
+		    m == SSL_ERROR_SYSCALL)
+			return LWS_SSL_CAPABLE_ERROR;
+
 		if (SSL_want_read(wsi->ssl)) {
 			lwsl_debug("%s: WANT_READ\n", __func__);
 			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
@@ -478,10 +466,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
 			lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
 			return LWS_SSL_CAPABLE_MORE_SERVICE;
 		}
-
-		lwsl_info("%s failed2: %s\n",__func__,
-				 ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
-			lws_ssl_elaborate_error();
+		wsi->socket_is_permanently_unusable = 1;
 
 		return LWS_SSL_CAPABLE_ERROR;
 	}
@@ -542,10 +527,7 @@ lws_ssl_pending(struct lws *wsi)
 LWS_VISIBLE int
 lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
 {
-	int n;
-#if !defined(LWS_WITH_MBEDTLS)
-       	int ssl_read_errno = 0;
-#endif
+	int n, m;
 
 	if (!wsi->ssl)
 		return lws_ssl_capable_write_no_ssl(wsi, buf, len);
@@ -554,33 +536,29 @@ lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
 	if (n > 0)
 		return n;
 
-	n = lws_ssl_get_error(wsi, n);
-	if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) {
-		if (n == SSL_ERROR_WANT_WRITE) {
-			lwsl_debug("%s: WANT_WRITE\n", __func__);
-			lws_set_blocking_send(wsi);
+	m = lws_ssl_get_error(wsi, n);
+	if (m != SSL_ERROR_SYSCALL) {
+
+		if (SSL_want_read(wsi->ssl)) {
+			lwsl_notice("%s: want read\n", __func__);
+
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
 		}
-		return LWS_SSL_CAPABLE_MORE_SERVICE;
-	}
 
-	if (n == SSL_ERROR_ZERO_RETURN)
-		return LWS_SSL_CAPABLE_ERROR;
+		if (SSL_want_write(wsi->ssl)) {
+			lws_set_blocking_send(wsi);
 
-#if !defined(LWS_WITH_MBEDTLS)
-	if (n == SSL_ERROR_SYSCALL) {
-		int err = ERR_get_error();
+			lwsl_notice("%s: want write\n", __func__);
 
-		if (err == 0 && (ssl_read_errno == EPIPE ||
-				 ssl_read_errno == ECONNABORTED ||
-				 ssl_read_errno == 0))
-			return LWS_SSL_CAPABLE_ERROR;
+			return LWS_SSL_CAPABLE_MORE_SERVICE;
+		}
 	}
-#endif
 
-	lwsl_info("%s failed: %s\n",__func__,
-			ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
+	lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
 	lws_ssl_elaborate_error();
 
+	wsi->socket_is_permanently_unusable = 1;
+
 	return LWS_SSL_CAPABLE_ERROR;
 }
 
@@ -653,7 +631,8 @@ lws_ssl_close(struct lws *wsi)
 #endif
 
 	n = SSL_get_fd(wsi->ssl);
-	SSL_shutdown(wsi->ssl);
+	if (!wsi->socket_is_permanently_unusable)
+		SSL_shutdown(wsi->ssl);
 	compatible_close(n);
 	SSL_free(wsi->ssl);
 	wsi->ssl = NULL;
@@ -676,6 +655,7 @@ LWS_VISIBLE int
 lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 {
 	struct lws_context *context = wsi->context;
+	struct lws_vhost *vh;
 	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
 	int n, m;
 #if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
@@ -856,9 +836,11 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 		m = lws_ssl_get_error(wsi, n);
 
 #if defined(LWS_WITH_MBEDTLS)
-		if (m == 5 && errno == 11)
+		if (m == SSL_ERROR_SYSCALL && errno == 11)
 			m = SSL_ERROR_WANT_READ;
 #endif
+		if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
+			goto failed;
 
 go_again:
 		if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
@@ -880,6 +862,7 @@ go_again:
 
 			break;
 		}
+failed:
 		lws_stats_atomic_bump(wsi->context, pt,
 				      LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
                 lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
@@ -897,6 +880,18 @@ accepted:
 		wsi->accept_start_us = time_in_microseconds();
 #endif
 
+		/* adapt our vhost to match the SNI SSL_CTX that was chosen */
+		vh = context->vhost_list;
+		while (vh) {
+			if (!vh->being_destroyed &&
+			    vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
+				lwsl_info("setting wsi to vh %s\n", vh->name);
+				wsi->vhost = vh;
+				break;
+			}
+			vh = vh->vhost_next;
+		}
+
 		/* OK, we are accepted... give him some time to negotiate */
 		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
 				context->timeout_secs);
@@ -905,9 +900,10 @@ accepted:
 			wsi->mode = LWSCM_RAW;
 		else
 			wsi->mode = LWSCM_HTTP_SERVING;
-
-		lws_http2_configure_if_upgraded(wsi);
-
+#if defined(LWS_WITH_HTTP2)
+		if (lws_h2_configure_if_upgraded(wsi))
+			goto fail;
+#endif
 		lwsl_debug("accepted new SSL conn\n");
 		break;
 	}
diff --git a/lwsws/main.c b/lwsws/main.c
index eae913ef1b02e5b0da604cc73e5b56281c021227..16307f5273f607d4b1c3965963eb728e0c72b5f3 100644
--- a/lwsws/main.c
+++ b/lwsws/main.c
@@ -54,6 +54,7 @@ static int opts = 0, do_reload = 1;
 static uv_loop_t loop;
 static uv_signal_t signal_outer;
 static int pids[32];
+void lwsl_emit_stderr(int level, const char *line);
 
 #define LWSWS_CONFIG_STRING_SIZE (32 * 1024)
 
@@ -119,7 +120,7 @@ context_creation(void)
 	memset(&info, 0, sizeof(info));
 
 	info.external_baggage_free_on_destroy = config_strings;
-	info.max_http_header_pool = 256;
+	info.max_http_header_pool = 1024;
 	info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 |
 			      LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
 			      LWS_SERVER_OPTION_LIBUV;
diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c
index 8d14260865a35c9587702700647bae31086ee6d5..950c7b723eb59f1df28d180ae7c8d1675aad48a8 100644
--- a/plugins/protocol_dumb_increment.c
+++ b/plugins/protocol_dumb_increment.c
@@ -132,6 +132,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 		callback_dumb_increment, \
 		sizeof(struct per_session_data__dumb_increment), \
 		10, /* rx buf size must be >= permessage-deflate rx size */ \
+		0, NULL, 0 \
 	}
 
 #if !defined (LWS_PLUGIN_STATIC)
diff --git a/plugins/protocol_esp32_lws_ota.c b/plugins/protocol_esp32_lws_ota.c
index a1dc14beef97d850e1392d6d401d401570b6cc3a..d3e8808d4075306935cef18d2aba2ff748f74f0c 100644
--- a/plugins/protocol_esp32_lws_ota.c
+++ b/plugins/protocol_esp32_lws_ota.c
@@ -126,7 +126,7 @@ ota_file_upload_cb(void *data, const char *name, const char *filename,
 			return 1;
 		}
 
-		lwsl_debug("writing 0x%lx... 0x%lx\n",
+		lwsl_notice("writing 0x%lx... 0x%lx\n",
 			   pss->part->address + pss->file_length,
 			   pss->part->address + pss->file_length + len);
 		if (esp_ota_write(pss->otahandle, buf, len) != ESP_OK) {
@@ -193,6 +193,7 @@ callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason,
 	case LWS_CALLBACK_HTTP_BODY:
 		/* create the POST argument parser if not already existing */
 		//lwsl_notice("LWS_CALLBACK_HTTP_BODY (ota) %d %d %p\n", (int)pss->file_length, (int)len, pss->spa);
+		lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5);
 		if (!pss->spa) {
 			pss->spa = lws_spa_create(wsi, ota_param_names,
 					ARRAY_SIZE(ota_param_names), 4096,
@@ -228,7 +229,7 @@ callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason,
 		if (lws_finalize_http_header(wsi, &p, end))
 			goto bail;
 
-		n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
+		n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
 		if (n < 0)
 			goto bail;
 
@@ -236,6 +237,8 @@ callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason,
 		break;
 
 	case LWS_CALLBACK_HTTP_WRITEABLE:
+		if (!pss->result_len)
+			break;
 		lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
 			   pss->result_len);
 		n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
diff --git a/plugins/protocol_esp32_lws_scan.c b/plugins/protocol_esp32_lws_scan.c
index 686599c25ca1a39d8fb74a00d8c996f089ad5cb7..da82dc6b0f9d3d2fa1a6ac0a448c7bdca59e2ef5 100644
--- a/plugins/protocol_esp32_lws_scan.c
+++ b/plugins/protocol_esp32_lws_scan.c
@@ -378,7 +378,7 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
 			struct timeval t;
 			uint8_t mac[6];
 			struct lws_esp32_image i;
-			char img_factory[512], img_ota[512], group[16], role[16];
+			char img_factory[384], img_ota[384], group[16], role[16];
 			int grt;
 
 		case SCAN_STATE_INITIAL:
@@ -418,12 +418,21 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
 			strcpy(img_factory, " { \"date\": \"Empty\" }");
 			strcpy(img_ota, " { \"date\": \"Empty\" }");
 
-			lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
-				ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL), &i,
-				img_factory, sizeof(img_factory));
-			lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
-				ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL), &i,
-				img_ota, sizeof(img_ota));
+			if (grt != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) {
+				lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
+					ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL), &i,
+					img_factory, sizeof(img_factory) - 1);
+				img_factory[sizeof(img_factory) - 1] = '\0';
+				if (img_factory[0] == 0xff || strlen(img_factory) < 8)
+					strcpy(img_factory, " { \"date\": \"Empty\" }");
+
+				lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP,
+					ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL), &i,
+					img_ota, sizeof(img_ota) - 1);
+				img_ota[sizeof(img_ota) - 1] = '\0';
+				if (img_ota[0] == 0xff || strlen(img_ota) < 8)
+					strcpy(img_ota, " { \"date\": \"Empty\" }");
+			}
 
 			p += snprintf((char *)p, end - p,
 				      "{ \"model\":\"%s\",\n"
@@ -440,9 +449,7 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
 				      " \"conn_mask\":\"%s\",\n"
 				      " \"conn_gw\":\"%s\",\n"
 				      " \"group\":\"%s\",\n"
-				      " \"role\":\"%s\",\n"
-				      " \"img_factory\": %s,\n"
-				      " \"img_ota\": %s,\n",
+				      " \"role\":\"%s\",\n",
 				      lws_esp32.model,
 				      grt == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON, 
 				      lws_esp32.serial,
@@ -455,11 +462,15 @@ callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason,
 				      lws_esp32.sta_ip,
 				      lws_esp32.sta_mask,
 				      lws_esp32.sta_gw,
-				      group, role,
+				      group, role);
+			p += snprintf((char *)p, end - p,
+				      " \"img_factory\": %s,\n"
+				      " \"img_ota\": %s,\n",
 					img_factory,
 					img_ota
 				      );
 
+
 			n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
 			pss->scan_state = SCAN_STATE_INITIAL_MANIFEST;
 			pss->ap_record = 0;
@@ -563,6 +574,7 @@ scan_state_final:
 		}
 issue:
 //		lwsl_notice("issue: %d (%d)\n", p - start, n);
+		lwsl_hexdump(start, p - start);
 		m = lws_write(wsi, (unsigned char *)start, p - start, n);
 		if (m < 0) {
 			lwsl_err("ERROR %d writing to di socket\n", m);
diff --git a/plugins/protocol_lws_meta.c b/plugins/protocol_lws_meta.c
index db25badaa197b48b7fa8f3f80688de98a7cd4593..4944cff1ad8efa183dc7620929cccd173d6a1777 100644
--- a/plugins/protocol_lws_meta.c
+++ b/plugins/protocol_lws_meta.c
@@ -501,7 +501,7 @@ callback_lws_meta(struct lws *wsi, enum lws_callback_reasons reason,
 			return -1;
 		}
 
-		lwsl_debug("%s: RX len %d\n", __func__, (int)len);
+		// lwsl_debug("%s: RX len %d\n", __func__, (int)len);
 
 		if (lws_get_protocol(cwsi)->callback(cwsi,
 					LWS_CALLBACK_RECEIVE,
diff --git a/plugins/protocol_lws_mirror.c b/plugins/protocol_lws_mirror.c
index a9fb0657bb88c021fc06bba8fb2618dabf4a5607..3683a12b1b8a04d1d2a8378224807d62328b5f09 100644
--- a/plugins/protocol_lws_mirror.c
+++ b/plugins/protocol_lws_mirror.c
@@ -27,13 +27,13 @@
 #include <string.h>
 #include <stdlib.h>
 
-#define QUEUELEN 64
+#define QUEUELEN 32
 /* queue free space below this, rx flow is disabled */
 #define RXFLOW_MIN (4)
 /* queue free space above this, rx flow is enabled */
 #define RXFLOW_MAX (QUEUELEN / 3)
 
-#define MAX_MIRROR_INSTANCES 10
+#define MAX_MIRROR_INSTANCES 3
 
 struct mirror_instance;
 
@@ -44,6 +44,7 @@ struct per_session_data__lws_mirror {
 	uint32_t tail;
 };
 
+/* this is the element in the ring */
 struct a_message {
 	void *payload;
 	size_t len;
@@ -53,6 +54,7 @@ struct mirror_instance {
 	struct mirror_instance *next;
 	struct per_session_data__lws_mirror *same_mi_pss_list;
 	struct lws_ring *ring;
+	int messages_allocated;
 	char name[30];
 	char rx_enabled;
 };
@@ -99,7 +101,7 @@ mirror_rxflow_instance(struct mirror_instance *mi, int enable)
 static int
 mirror_update_worst_tail(struct mirror_instance *mi)
 {
-	uint32_t wai, worst = 0, worst_tail, oldest;
+	uint32_t wai, worst = 0, worst_tail = 0, oldest;
 	struct per_session_data__lws_mirror *worst_pss = NULL;
 
 	oldest = lws_ring_get_oldest_tail(mi->ring);
@@ -317,10 +319,6 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
 		update_worst = oldest_tail == pss->tail;
 		sent_something = 0;
 
-		lwsl_debug(" original oldest %d, free elements %ld\n",
-			   oldest_tail,
-			   lws_ring_get_count_free_elements(pss->mi->ring));
-
 		do {
 			msg = lws_ring_get_element(pss->mi->ring, &pss->tail);
 			if (!msg)
@@ -385,6 +383,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
 			lwsl_notice("OOM: dropping\n");
 			break;
 		}
+
 		memcpy((char *)amsg.payload + LWS_PRE, in, len);
 		if (!lws_ring_insert(pss->mi->ring, &amsg, 1)) {
 			mirror_destroy_message(&amsg);
@@ -414,6 +413,7 @@ req_writable:
 		callback_lws_mirror, \
 		sizeof(struct per_session_data__lws_mirror), \
 		128, /* rx buf size must be >= permessage-deflate rx size */ \
+		0, NULL, 0 \
 	}
 
 #if !defined (LWS_PLUGIN_STATIC)
diff --git a/plugins/protocol_lws_server_status.c b/plugins/protocol_lws_server_status.c
index 96b2ef2985df916470bbf4bf1b4efa2d8e2b0871..9619eb66bba942eab75d5d16dd3ded108e898a99 100644
--- a/plugins/protocol_lws_server_status.c
+++ b/plugins/protocol_lws_server_status.c
@@ -23,6 +23,8 @@
 #include "../lib/libwebsockets.h"
 #include <string.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <fcntl.h>
 
 struct lws_ss_load_sample {
@@ -102,8 +104,9 @@ uv_timeout_cb_server_status(uv_timer_t *w
 				contents[n] = '\0';
 				lws_json_purify(pure, contents, sizeof(pure));
 
-				n = lws_snprintf(p, l, "{\"path\":\"%s\",\"val\":\"%s\"}",
-						 fp->filepath, pure);
+				n = lws_snprintf(p, l,
+					"{\"path\":\"%s\",\"val\":\"%s\"}",
+						fp->filepath, pure);
 				p += n;
 				l -= n;
 				first = 0;
@@ -162,20 +165,22 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason,
 			if (!strcmp(pvo->name, "filepath")) {
 				fp = malloc(sizeof(*fp));
 				fp->next = NULL;
-				lws_snprintf(&fp->filepath[0], sizeof(fp->filepath), "%s", pvo->value);
+				lws_snprintf(&fp->filepath[0],
+					     sizeof(fp->filepath), "%s",
+					     pvo->value);
 				*fp_old = fp;
 				fp_old = &fp->next;
 			}
 			pvo = pvo->next;
 		}
 		v->context = lws_get_context(wsi);
-		uv_timer_init(lws_uv_getloop(v->context, 0), &v->timeout_watcher);
+		uv_timer_init(lws_uv_getloop(v->context, 0),
+			      &v->timeout_watcher);
 		uv_timer_start(&v->timeout_watcher,
-				uv_timeout_cb_server_status, 2000, period);
+			       uv_timeout_cb_server_status, 2000, period);
 		break;
 
 	case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */
-	//	lwsl_notice("ss: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", v, v->context);
 		if (!v)
 			break;
 		uv_timer_stop(&v->timeout_watcher);
@@ -213,7 +218,7 @@ static const struct lws_protocols protocols[] = {
 
 LWS_EXTERN LWS_VISIBLE int
 init_protocol_lws_server_status(struct lws_context *context,
-			     struct lws_plugin_capability *c)
+				struct lws_plugin_capability *c)
 {
 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
 		lwsl_err("Plugin API %d, library API %d",
@@ -234,4 +239,3 @@ destroy_protocol_lws_server_status(struct lws_context *context)
 {
 	return 0;
 }
-
diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c
index 5b32139b7d7c2244169d163de86576674e7ca21d..d9240caeddbf55f5571fe3353bdd2190135ab921 100644
--- a/plugins/protocol_lws_status.c
+++ b/plugins/protocol_lws_status.c
@@ -165,6 +165,7 @@ callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
 				break;
 			}
 
+			strcpy(ip, "unknown");
 			lws_get_peer_simple(pss->walk_next->wsi, ip, sizeof(ip));
 			p += lws_snprintf(p, end - p,
 					"{\"peer\":\"%s\",\"time\":\"%ld\","
@@ -238,6 +239,7 @@ walk_final:
 		callback_lws_status, \
 		sizeof(struct per_session_data__lws_status), \
 		512, /* rx buf size must be >= permessage-deflate rx size */ \
+		0, NULL, 0 \
 	}
 
 #if !defined (LWS_PLUGIN_STATIC)
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index 7560bf5a7f8bfd9444819813b9fe28ba985319b5..8880fe2e4bc00137a93f6f33c65cd9ac362a71f1 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -40,7 +40,7 @@ struct per_session_data__post_demo {
 
 	char filename[64];
 	long file_length;
-#if !defined(LWS_WITH_ESP8266)
+#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
 	lws_filefd_type fd;
 #endif
 };
@@ -65,7 +65,9 @@ file_upload_cb(void *data, const char *name, const char *filename,
 {
 	struct per_session_data__post_demo *pss =
 			(struct per_session_data__post_demo *)data;
+#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
 	int n;
+#endif
 
 	switch (state) {
 	case LWS_UFS_OPEN:
@@ -73,7 +75,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 		/* we get the original filename in @filename arg, but for
 		 * simple demo use a fixed name so we don't have to deal with
 		 * attacks  */
-#if !defined(LWS_WITH_ESP8266)
+#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
 		pss->fd = (lws_filefd_type)open("/tmp/post-file",
 			       O_CREAT | O_TRUNC | O_RDWR, 0600);
 #endif
@@ -87,7 +89,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 			if (pss->file_length > 100000)
 				return 1;
 
-#if !defined(LWS_WITH_ESP8266)
+#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
 			n = write((int)pss->fd, buf, len);
 			lwsl_notice("%s: write %d says %d\n", __func__, len, n);
 #else
@@ -96,7 +98,7 @@ file_upload_cb(void *data, const char *name, const char *filename,
 		}
 		if (state == LWS_UFS_CONTENT)
 			break;
-#if !defined(LWS_WITH_ESP8266)
+#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32)
 		close((int)pss->fd);
 		pss->fd = LWS_INVALID_FILE;
 #endif
@@ -185,6 +187,8 @@ callback_post_demo(struct lws *wsi, enum lws_callback_reasons reason,
 		break;
 
 	case LWS_CALLBACK_HTTP_WRITEABLE:
+		if (!pss->result_len)
+			break;
 		lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n",
 			   pss->result_len);
 		n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE,
@@ -225,6 +229,7 @@ try_to_reuse:
 		callback_post_demo, \
 		sizeof(struct per_session_data__post_demo), \
 		1024, \
+		0, NULL, 0 \
 	}
 
 #if !defined (LWS_PLUGIN_STATIC)
diff --git a/plugins/server-status.html b/plugins/server-status.html
index a49513252a926e1259bcd743a70ba23fd1fb8e9f..e59888b29a46c7255abb02e719b62eb280a216d9 100644
--- a/plugins/server-status.html
+++ b/plugins/server-status.html
@@ -5,40 +5,37 @@
  <script src="/lws-common.js"></script>
  <title>LWS Server Status</title>
 <style type="text/css">
-	span.title { font-size:18pt; font: Arial; font-weight:normal;
+	span.title { font-size:18pt; font-family: Arial; font-weight:normal;
 			text-align:center; color:#000000; }
-	span.mount { font-size:10pt; font: Arial; font-weight:normal;
+	span.mount { font-size:10pt; font-family: Arial; font-weight:normal;
 			text-align:center; color:#000000; }
-	span.mountname { font-size:14pt; font: Arial; font-weight:bold;
+	span.mountname { font-size:14pt; font-family: Arial; font-weight:bold;
 			text-align:center; color:#404010; }
-	span.n { font-size:12pt; font: Arial; font-weight:normal;
+	span.n { font-size:12pt; font-family: Arial; font-weight:normal;
 			text-align:center; color:#808020; }
-	span.v { font-size:12pt; font: Arial; font-weight:bold;
+	span.v { font-size:12pt; font-family: Arial; font-weight:bold;
 			text-align:center; color:#202020; }
-	span.m1 { font-size:12pt; font: Arial; font-weight:bold;
+	span.m1 { font-size:12pt; font-family: Arial; font-weight:bold;
 			text-align:center; color:#202020; }
-	span.m2 { font-size:12pt; font: Arial; font-weight:normal;
+	span.m2 { font-size:12pt; font-family: Arial; font-weight:normal;
 			text-align:center; color:#202020; }
-	.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+	.browser { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;}
 	.group2 { vertical-align:middle;
 		text-align:center;
 		background:#f0f0e0; 
 		padding:12px; 
 		-webkit-border-radius:10px; 
-		-moz-border-radius:10px;
 		border-radius:10px; }
 	.explain { vertical-align:middle;
 		text-align:center;
 		background:#f0f0c0; padding:12px;
 		-webkit-border-radius:10px;
-		-moz-border-radius:10px;
 		border-radius:10px;
 		color:#404000; }
 	td.wsstatus { vertical-align:middle; width:200px; height:50px;
 		text-align:center;
 		background:#f0f0c0; padding:6px;
 		-webkit-border-radius:8px;
-		-moz-border-radius:8px;
 		border-radius:8px;
 		color:#404000; }
 	td.l { vertical-align:middle;
@@ -46,52 +43,45 @@
 		background:#d0d0b0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.dl { vertical-align:middle;
 		text-align:center;
 		background:#c0c0c0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.c { vertical-align:middle;
 		text-align:center;
 		background:#c0c0a0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.c0 { vertical-align:middle;
 		text-align:center;
 		background:#b0b090; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.dc0 { vertical-align:middle;
 		text-align:center;
 		background:#a0a0a0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.c1 { vertical-align:middle;
 		text-align:center;
 		background:#c0c0c0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
 	td.t { vertical-align:middle;
 		text-align:center;
 		background:#e0e0c0; 
 		padding:3px; 
 		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
 		border-radius:3px; }
-	.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
-	.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+	.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
+	.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
 .tabs {
   position: relative;   
   min-height: 750px; /* This part sucks */
@@ -279,16 +269,22 @@ function get_appropriate_ws_url()
 				  "<br>" +
 				  "<span class=n>Listening wsi:</span> <span class=v>" + san(jso.i.contexts[ci].listen_wsi) + "</span>, " +
 				  "<span class=n>Current wsi alive:</span> <span class=v>" + (parseInt(san(jso.i.contexts[ci].wsi_alive)) -
-				  	parseInt(san(jso.i.contexts[ci].listen_wsi))) + "</span><br>" +
+				  							      parseInt(san(jso.i.contexts[ci].listen_wsi))) + "</span><br>" +
 			  	  "<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].rx)) +"</span>, " +
 			  	  "<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].tx)) +"</span><br>" +
-			  	  "<span class=n>Total connections:</span> <span class=v>" + san(jso.i.contexts[ci].conn) +"</span>, " +
-			  	  "<span class=n>Total HTTP Transactions:</span> <span class=v>" + san(jso.i.contexts[ci].trans) +"</span><br>" +
-			  	  "<span class=n>Total ws upgrades:</span> <span class=v>" + san(jso.i.contexts[ci].ws_upg) +"</span>, " +
-			  	  "<span class=n>Total h2 upgrades:</span> <span class=v>" + san(jso.i.contexts[ci].http2_upg) +"</span>, " +
-			  	  "<span class=n>Total Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].rejected) +"</span><br>" +
-				  "<span class=n>Current cgi alive:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_alive) + "</span>, " +
-				  "<span class=n>Total CGI spawned:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_spawned) +
+			  	  
+			  	  "<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_conn) +"</span>, " +
+			  	  "<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].ws_upg) +"</span>, " +
+			  	  "<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].h2_upg) +"</span>, " +
+			  	  "<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].h2_alpn) +"</span>, " +
+			  	  "<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].rejected) +"</span><br>" +
+
+			  	  "<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_trans) + "</span>, " +
+			  	  "<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].h2_trans) +"</span>, " +
+			  	   "<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].h2_subs) +"</span><br>" +
+
+				  "<span class=n>CGI: alive:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_alive) + "</span>, " +
+				  "<span class=n>spawned:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_spawned) +
 				  "</span><table>";
 				
 				for (n = 0; n < jso.i.contexts[ci].pt.length; n++) {
@@ -322,13 +318,20 @@ function get_appropriate_ws_url()
 					if (jso.i.contexts[ci].vhosts[n].sts == '1')
 						s = s + " (STS)";
 					s = s +"<br>" +
-					"<span class=n>rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].rx)) + "B</span>, " +
-					"<span class=n>tx</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].tx)) + "B</span><br>" +
-					"<span class=n>vh connections</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].conn) + "</span>, " +
-					"<span class=n>vh http transactions</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].trans) + "</span><br>" +
-					"<span class=n>vh upgrades to ws:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].ws_upg) + "</span>, " +
-					"<span class=n>to http/2:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].http2_upg) + "</span>, " +
-					"<span class=n>rejected:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].rejected) + "</span><br>" +
+					
+					  "<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].rx)) +"</span>, " +
+					  "<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].tx)) +"</span><br>" +
+					  
+					  "<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_conn) +"</span>, " +
+					  "<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].ws_upg) +"</span>, " +
+					  "<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_upg) +"</span>, " +
+					  "<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_alpn) +"</span>, " +
+					  "<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].rejected) +"</span><br>" +
+					
+					  "<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_trans) + "</span>, " +
+					  "<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_trans) +"</span>, " +
+					  "<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_subs) +"</span><br>" +
+					
 					"<table style=\"margin-left:16px\"><tr><td class=t>Mountpoint</td><td class=t>Origin</td><td class=t>Cache Policy</td></tr>";
 
 					var m;
diff --git a/scripts/travis_install.sh b/scripts/travis_install.sh
index f51f087e77ac3bbcf97c9ab0f52ffbeb8d460483..f7695bb096a55b5cb431f5ff249cae62402f850f 100755
--- a/scripts/travis_install.sh
+++ b/scripts/travis_install.sh
@@ -22,11 +22,13 @@ if [ "$TRAVIS_OS_NAME" == "osx" ];
 then
 	if [ "$LWS_METHOD" == "libev" ];
 	then
+		brew update;
 		brew install libev;
 	fi
 
 	if [ "$LWS_METHOD" == "libuv" -o "$LWS_METHOD" == "lwsws" ];
 	then
+		brew update;
 		brew install libuv;
 	fi
 
diff --git a/test-apps/attack.sh b/test-apps/attack.sh
index cb63db4677e2384b7b595febe92dde962d14abc4..4314579abe62bb7e075064864cc2112a4d0d2ba8 100755
--- a/test-apps/attack.sh
+++ b/test-apps/attack.sh
@@ -29,11 +29,18 @@ function check {
 	fi
 	if [ "$1" = "defaultplusforbidden" ] ; then
 	cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
-	echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
+	echo -e -n "HTTP/1.0 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
 		diff /tmp/lwscap /tmp/plusforb > /dev/null
 		if [ $? -ne 0 ] ; then
-			echo "FAIL: got something other than test.html + forbidden back"
-			exit 1
+			cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
+
+			echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
+			diff /tmp/lwscap /tmp/plusforb > /dev/null
+			if [ $? -ne 0 ] ; then
+
+				echo "FAIL: got something other than test.html + forbidden back"
+				exit 1
+			fi
 		fi
 	fi
 
@@ -98,7 +105,7 @@ function check {
 
 rm -rf $LOG
 killall libwebsockets-test-server 2>/dev/null
-libwebsockets-test-server -d15 2>> $LOG &
+libwebsockets-test-server -d127 2>> $LOG &
 CPID=$!
 
 echo "Started server on PID $CPID"
@@ -111,7 +118,7 @@ check
 echo
 echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F"
 rm -f /tmp/lwscap
-echo -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "UPDATE_SETTINGS=1"
 check 2 "Root_Channels_1_Channel_name_http_post=?"
 check 3 "Root_Channels_1_Channel_location_http_post=?"
@@ -120,14 +127,14 @@ check
 echo
 echo "---- ? processing (/cgi-bin/settings.js?key1=value1)"
 rm -f /tmp/lwscap
-echo -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "key1=value1"
 check
 
 echo
 echo "---- ? processing (/t%3dest?key1%3d2=value1)"
 rm -f /tmp/lwscap
-echo -e "GET /t%3dest?key1%3d2=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET /t%3dest?key1%3d2=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 0 "/t=est"
 check 1 "key1_2=value1"
 check
@@ -135,14 +142,14 @@ check
 echo
 echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)"
 rm -f /tmp/lwscap
-echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo  -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "arg=1"
 check
 
 echo
 echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)"
 rm -f /tmp/lwscap
-echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check 1 "arg=/../."
 check
 
@@ -169,17 +176,17 @@ check
 
 echo
 echo "---- missing URI"
-echo -e "GET HTTP/1.1\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap
+echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap
 check
 
 echo
 echo "---- repeated method"
-echo -e "GET blah HTTP/1.1\x0d\x0aGET blah HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT >/tmp/lwscap 
+echo -n -e "GET blah HTTP/1.0\x0d\x0aGET blah HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT >/tmp/lwscap 
 check
 
 echo
 echo "---- crazy header name part"
-echo -e "GET blah HTTP/1.1\x0d\x0a................................................................................................................" \
+echo -n -e "GET blah HTTP/1.0\x0d\x0a................................................................................................................" \
 	"......................................................................................................................." \
  	"......................................................................................................................." \
  	"......................................................................................................................." \
@@ -201,7 +208,7 @@ check
 
 echo
 echo "---- excessive uri content"
-echo -e "GET ................................................................................................................" \
+echo -n -e "GET ................................................................................................................" \
 	"......................................................................................................................." \
  	"......................................................................................................................." \
  	"......................................................................................................................." \
@@ -223,7 +230,7 @@ check
 
 echo
 echo "---- good request but http payload coming too (test.html served then forbidden)"
-echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
+echo -n -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
 	"......................................................................................................................." \
  	"......................................................................................................................." \
  	"......................................................................................................................." \
@@ -246,70 +253,70 @@ check
 echo
 echo "---- nonexistent file"
 rm -f /tmp/lwscap
-echo -e "GET /nope HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check media
 check
 
 echo
 echo "---- relative uri path"
 rm -f /tmp/lwscap
-echo -e "GET nope HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check forbidden
 check
 
 echo
 echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
-echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check rejected
 check
 
 echo
 echo "---- directory attack 2 (/../ should be /)"
 rm -f /tmp/lwscap
-echo -e -n "GET /../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check default
 check
 
 echo
 echo "---- directory attack 3 (/./ should be /)"
 rm -f /tmp/lwscap
-echo -e -n "GET /./ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /./ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check default
 check
 
 echo
 echo "---- directory attack 4 (/blah/.. should be /)"
 rm -f /tmp/lwscap
-echo -e -n "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /blah/.. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check default
 check
 
 echo
 echo "---- directory attack 5 (/blah/../ should be /)"
 rm -f /tmp/lwscap
-echo -e -n "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /blah/../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check default
 check
 
 echo
 echo "---- directory attack 6 (/blah/../. should be /)"
 rm -f /tmp/lwscap
-echo -e -n "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /blah/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check default
 check
 
 echo
 echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
-echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check rejected
 check
 
 echo
 echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
-echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check rejected
 check
 
diff --git a/test-apps/http2.png b/test-apps/http2.png
new file mode 100644
index 0000000000000000000000000000000000000000..439bfa482fa00e69af2d562f17a6e89453eb98cc
Binary files /dev/null and b/test-apps/http2.png differ
diff --git a/test-apps/lws-cgi-test.sh b/test-apps/lws-cgi-test.sh
index cc14af29e70c7e056d5d977cd1a049645de7dbc3..a40e2b88c051da8c4eaf942465febedf39f66749 100755
--- a/test-apps/lws-cgi-test.sh
+++ b/test-apps/lws-cgi-test.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
-echo -e -n "Content-type: text/html\x0d\x0a"
+echo -e -n "content-type: text/html\x0d\x0a"
+echo -e -n "transfer-encoding: chunked\x0d\x0a"
 echo -e -n "\x0d\x0a"
 
 echo "<html><body>"
diff --git a/test-apps/lws-common.js b/test-apps/lws-common.js
index 1e94eaaec28417948fe34852dd3716c4c0a66020..f0c5b92f27c0d38cc23607d0ed1c47ede93e3375 100644
--- a/test-apps/lws-common.js
+++ b/test-apps/lws-common.js
@@ -14,6 +14,7 @@
  */
 
 function lws_gray_out(vis, options) {
+
 	var options = options || {};
 	var zindex = options.zindex || 50;
 	var opacity = options.opacity || 70;
@@ -36,7 +37,7 @@ function lws_gray_out(vis, options) {
 	if (vis) {
 		dark.style.opacity = opaque;
 		dark.style.MozOpacity = opaque;
-		dark.style.filter ='alpha(opacity='+opacity+')';
+		// dark.style.filter ='alpha(opacity='+opacity+')';
 		dark.style.zIndex = zindex;
 		dark.style.backgroundColor = bgcolor;
 		dark.style.width = gsize(1);
diff --git a/test-apps/test-client.c b/test-apps/test-client.c
index 0c1d8b6d287f34bb2de3b662056aa26c379daf50..ea7d0826f6cd179477ef61f16a4dc72af5729d5a 100644
--- a/test-apps/test-client.c
+++ b/test-apps/test-client.c
@@ -223,7 +223,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 		force_exit = 1;
 		break;
 
-#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
+#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) && !defined(LWS_WITH_MBEDTLS)
 	case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
 		if (crl_path[0]) {
 			/* Enable CRL checking of the server certificate */
diff --git a/test-apps/test-server-http.c b/test-apps/test-server-http.c
index 843807b33247629c3a30a7d250211665cfed1502..155e493e24d925d4b0856580b71643584b52d138 100644
--- a/test-apps/test-server-http.c
+++ b/test-apps/test-server-http.c
@@ -221,10 +221,8 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 			}
 		}
 
-		{
-			lws_get_peer_simple(wsi, buf, sizeof(buf));
+		if (lws_get_peer_simple(wsi, buf, sizeof(buf)))
 			lwsl_info("HTTP connect from %s\n", buf);
-		}
 
 		if (len < 1) {
 			lws_return_http_status(wsi,
@@ -276,8 +274,16 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 #endif
 
 		/* if a legal POST URL, let it continue and accept data */
-		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
+			n = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_POST_URI);
+			if (n < 0)
+				return -1;
+			if (strcmp(buf, "/formtest")) {
+				lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL);
+				return -1;
+			}
 			return 0;
+		}
 
 		/* check for the "send a big file by hand" example case */
 
@@ -400,6 +406,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 				(unsigned char *)leaf_path + sizeof(leaf_path)))
 				return 1;
 		}
+#if !defined(LWS_WITH_HTTP2)
 		if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi,
 						(unsigned char *)
 						"Strict-Transport-Security:",
@@ -409,6 +416,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
 						(unsigned char *)leaf_path +
 							sizeof(leaf_path)))
 			return 1;
+#endif
 		n = (char *)p - leaf_path;
 
 		n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n);
@@ -513,15 +521,13 @@ 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");
+		lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE: %p\n", wsi);
 
 		if (pss->client_finished)
 			return -1;
 
-		if (!lws_get_child(wsi) && !pss->fop_fd) {
-			lwsl_notice("fop_fd NULL\n");
+		if (!lws_get_child(wsi) && !pss->fop_fd)
 			goto try_to_reuse;
-		}
 
 #ifndef LWS_NO_CLIENT
 		if (pss->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
@@ -769,7 +775,7 @@ bail:
 			return 1;
 		}
 		break;
-#if defined(LWS_HAVE_SSL_CTX_set1_param)
+#if defined(LWS_HAVE_SSL_CTX_set1_param) && !defined(LWS_WITH_MBEDTLS)
 	case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
 		if (crl_path[0]) {
 			/* Enable CRL checking */
diff --git a/test-apps/test-server.c b/test-apps/test-server.c
index fe903b26bfdabf3cfb5063571ed2e7368b8b911f..1d36f9e03e6e674b2eb4552e6bf135ebe0c32795 100644
--- a/test-apps/test-server.c
+++ b/test-apps/test-server.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets-test-server - libwebsockets test implementation
  *
- * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
  *
  * This file is made available under the Creative Commons CC0 1.0
  * Universal Public Domain Dedication.
@@ -383,7 +383,7 @@ int main(int argc, char **argv)
 	lws_set_log_level(debug_level, lwsl_emit_syslog);
 
 	lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n");
-	lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
+	lwsl_notice("(C) Copyright 2010-2017 Andy Green <andy@warmcat.com>\n");
 
 	printf("Using resource path \"%s\"\n", resource_path);
 #ifdef EXTERNAL_POLL
@@ -425,7 +425,7 @@ int main(int argc, char **argv)
 	}
 	info.gid = gid;
 	info.uid = uid;
-	info.max_http_header_pool = 16;
+	info.max_http_header_pool = 256;
 	info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
 	info.extensions = exts;
 	info.timeout_secs = 5;
@@ -442,7 +442,7 @@ int main(int argc, char **argv)
 			       "!DHE-RSA-AES256-SHA256:"
 			       "!AES256-GCM-SHA384:"
 			       "!AES256-SHA256";
-	info.ip_limit_ah = 4; /* for testing */
+	info.ip_limit_ah = 24; /* for testing */
 	info.ip_limit_wsi = 105; /* for testing */
 
 	if (use_ssl)
diff --git a/test-apps/test.html b/test-apps/test.html
index 92b78c3dc79926381bc00043071da59ae77159fe..a3fb3fcd8b6cdd69b2c4cc6b09e4a9cce7022cd8 100644
--- a/test-apps/test.html
+++ b/test-apps/test.html
@@ -5,48 +5,44 @@
  <script src="/lws-common.js"></script>
  <title>Minimal Websocket test app</title>
 <style type="text/css">
-	span.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
-	.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+	span.title { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000; }
+	.browser { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;}
 	.group2 { vertical-align:middle;
 		text-align:center;
 		background:#f0f0e0; 
 		padding:12px; 
 		-webkit-border-radius:10px; 
-		-moz-border-radius:10px;
 		border-radius:10px; }
 	.explain { vertical-align:middle;
 		text-align:center;
 		background:#f0f0c0; padding:12px;
 		-webkit-border-radius:10px;
-		-moz-border-radius:10px;
 		border-radius:10px;
 		color:#404000; }
 	td.wsstatus { vertical-align:middle; width:200px; height:50px;
 		text-align:center;
 		background:#f0f0c0; padding:6px;
 		-webkit-border-radius:8px;
-		-moz-border-radius:8px;
 		border-radius:8px;
 		color:#404000; }
 	.tdform { vertical-align:middle; width:200px; height:50px;
 		text-align:center;
 		background:#f0f0d0; padding:6px;
 		-webkit-border-radius:8px;
-		-moz-border-radius:8px; margin:10px;
+		margin:10px;
 		border-radius:8px;
 		border: 1px solid black;
-		border-collapse: collapse;font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; 
+		border-collapse: collapse;font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000; 
 		color:#404000; }
 		
 	td.l { vertical-align:middle;
 		text-align:center;
 		background:#d0d0b0; 
 		padding:3px; 
-		-webkit-border-radius:3px; 
-		-moz-border-radius:3px;
+		-webkit-border-radius:3px;
 		border-radius:3px; }
-	.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
-	.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+	.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
+	.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
 .tabs {
   position: relative;   
   min-height: 750px; /* This part sucks */
@@ -102,6 +98,8 @@
 	<section class="browser">Detected Browser: 
 	<div id=brow>...</div></section>
   </td>
+  <td width="64" id="transport">
+  </td>
  </tr>
 
 </table>
@@ -213,7 +211,7 @@ run.
 		<td colspan=2><span class="title">Open and close testing</span></td>
 		</tr>
 		<tr>	
-<td class="explain" colspan=3 style="padding:3">
+<td class="explain" colspan=3 style="padding:3px">
 To help with open and close testing, you can open and close a connection by hand using
  the buttons.<br>
  "<b>Close</b>" closes the connection from the browser with code 3000
@@ -257,7 +255,7 @@ whenever the information changes server-side.
 	<td align=center colspan=2><div id=servinfo></div></td>
 	</tr>
 	<tr>
-	<td align=center colspan=2><div id=conninfo style="border : solid 2px #e0d040; padding : 4px; width : 500px; height : 350px; overflow : auto; "></</div></td>
+	<td align=center colspan=2><div id=conninfo style="border: solid 2px #e0d040; padding: 4px; width: 500px; height:350px; overflow: auto;"></div></td>
 	</tr>
 </table>
 </div>
@@ -528,6 +526,27 @@ if (location.search) {
     }
 }
 
+var transport_protocol = "";
+
+if ( performance && performance.timing.nextHopProtocol ) {
+    transport_protocol = performance.timing.nextHopProtocol;
+} else if ( window.chrome && window.chrome.loadTimes ) {
+    transport_protocol = window.chrome.loadTimes().connectionInfo;
+} else {
+
+  var p = performance.getEntriesByType("resource");
+  for (var i=0; i < p.length; i++) {
+var value = "nextHopProtocol" in p[i];
+  if (value)
+    transport_protocol = p[i].nextHopProtocol;
+    }
+   }
+   
+   console.log("transport protocol " + transport_protocol);
+   
+   if (transport_protocol == "h2")
+   	document.getElementById("transport").innerHTML = "<img src=\"/http2.png\">";
+
 var mirror_name = "";
 if (params.mirror)
 	mirror_name = params.mirror;