diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7149959b7..89372d9a5 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 811083d9a..d065eb61b 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,15 +416,30 @@ seeing
```
at lws startup.
-For non-SSL HTTP2.0 upgrade
-```
- $ nghttp -nvasu http://localhost:7681/test.htm
-```
-For SSL / ALPN 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 -nvas https://localhost:7681/test.html
```
+Testing with h2spec (https://github.com/summerwind/h2spec)
+
+```
+ $ 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
To enable cross-compiling **libwebsockets** using CMake you need to create
@@ -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 e6afe3fa4..7e934ba52 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 9a427b39a..33b1348cf 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 here
diff --git a/component.mk b/component.mk
index 236395a4d..8baf3d6da 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 e53c356ca..898db1246 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 54ffb59ba..b2a4c12f5 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 8b5512b2a..ae96ddfea 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 e837a18ae..bc7609d92 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 872da2061..e2562cd6e 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, "
%u
%s",
- 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,
- "%u
%s",
- code, html_body);
+ len = sprintf((char *)body,
+ "%u
%s",
+ 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,
+ "%u
%s",
+ 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 a07d13e47..f7d916f93 100644
--- a/lib/hpack.c
+++ b/lib/hpack.c
@@ -1,7 +1,7 @@
/*
* lib/hpack.c
*
- * Copyright (C) 2014 Andy Green
+ * Copyright (C) 2014-2017 Andy Green
*
* 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;
-
- index -= ARRAY_SIZE(static_token);
- if (index >= dyn->num_entries)
- return 0;
-
- if (arg && len) {
- *arg = dyn->args + dyn->entries[index].arg_offset;
- *len = dyn->entries[index].arg_len;
}
- return dyn->entries[index].token;
+ 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);
+ 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->entries[index].value;
+ *len = dyn->entries[index].value_len;
+ }
+
+ 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 n, m;
+ const char *p;
+
+ 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 ret = 1;
+ int new_index, n;
- wsi = lws_http2_get_network_wsi(wsi);
- dyn = wsi->u.http2.hpack_dyn_table;
+ /* 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) {
- dyn = lws_zalloc(sizeof(*dyn), "hpack dyn");
- if (!dyn)
- return 1;
- wsi->u.http2.hpack_dyn_table = dyn;
+ if (!dyn->entries) {
+ lwsl_err("%s: unsized dyn table\n", __func__);
- 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;
+ 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);
}
- if (dyn->next == dyn->num_entries)
- return 1;
+ /*
+ * evict guys to make room, allowing for some overage. We have to
+ * take care about getting a single huge header, and evicting
+ * everything
+ */
- if (dyn->args_length - dyn->pos < len)
- return 1;
+ 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);
+ }
- 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;
+ if (dyn->used_entries < dyn->num_entries)
+ dyn->used_entries++;
- lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
- __func__, dyn->next, token, lws_token_to_string(token), len);
+ dyn->entries[new_index].value_len = 0;
- dyn->pos += len;
- dyn->next++;
+ 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];
+ }
+
+ lws_free(dyn->entries);
+ }
+
+ dyn->entries = dte;
+ dyn->num_entries = size;
+ dyn->used_entries = min;
+ dyn->pos = min % size;
+
+ 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;
+
+ 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 (arg)
+ p = arg;
+
+ if (idx < ARRAY_SIZE(http2_canned))
+ p = http2_canned[idx];
if (lws_frag_start(wsi, tok))
return 1;
- if (idx < ARRAY_SIZE(http2_canned)) {
- p = http2_canned[idx];
- while (*p)
+ 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;
+ int n, m;
- lwsl_debug(" state %d\n", wsi->u.http2.hpack);
+ if (!h2n)
+ return -1;
- 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;
+ /*
+ * 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:
+ 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 (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 */
+ 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;
}
- //lwsl_debug("indexed\n");
+ 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_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) {
-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;
- }
- } else
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.http2.hpack = HPKS_DATA;
+ 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:
+ 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
+ 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;
+ }
+ c1 = h2n->hpack_pos & 0x7fff;
+ h2n->hpack_pos = 0;
+ h2n->huff_pad = 0;
+ h2n->zero_huff_padding = 0;
- if (!c1 && prev == HUFTABLE_0x100_PREV)
- ; /* EOT */
- } else {
+ /* 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;
}
}
- 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))
- return 1;
- break;
- default:
- break;
- }
+ n = 8;
- 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;
- }
+ /* 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;
}
- break;
- case HKPS_OPT_DISCARD_PADDING:
- lwsl_info("eating padding %x\n", c);
- if (! --wsi->u.http2.padding)
- wsi->u.http2.hpack = HPKS_TYPE;
+
+ /*
+ * 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;
+ }
+ 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;
+
+ 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;
+ }
+
+ 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,
+static int
+lws_h2_num_start(int starting_bits, unsigned long num)
+{
+ int mask = (1 << starting_bits) - 1;
+
+ if (num < mask)
+ return (int)num;
+
+ 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) {
- *((*p)++) |= num;
- return *p >= end;
- }
-
- *((*p)++) |= mask;
- if (*p >= end)
- return 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 0bde70040..560f414ba 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
+ * Copyright (C) 2010-2017 Andy Green
*
* 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),
+};
-void lws_http2_init(struct http2_settings *settings)
+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)
{
- memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings));
+ int n;
+
+ 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)
+{
+}
+#endif
+
+void lws_h2_init(struct lws *wsi)
+{
+ wsi->u.h2.h2n->set = wsi->vhost->set;
+}
+
+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_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
+lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
+ unsigned int sid)
{
- do {
- if (wsi->u.http2.my_stream_id == sid)
- return wsi;
+ struct lws *wsi;
+ struct lws *nwsi = lws_get_network_wsi(parent_wsi);
+ struct lws_h2_netconn *h2n = nwsi->u.h2.h2n;
- wsi = wsi->u.http2.next_child_wsi;
- } while (wsi);
+ /*
+ * 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.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.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.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 = &vh->protocols[0];
+ if (lws_ensure_user_space(wsi))
+ goto bail1;
+
+ wsi->vhost->conn_stats.h2_subs++;
+
+ 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;
+
+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_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
- unsigned int sid)
+lws_h2_wsi_from_id(struct lws *parent_wsi, unsigned int sid)
{
- struct lws *wsi = lws_create_new_server_wsi(vhost);
-
- if (!wsi)
- 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;
-
- 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.http2.my_priority = 16;
- wsi->u.http2.tx_credit = 65535;
-
- wsi->state = LWSS_HTTP2_ESTABLISHED;
- wsi->mode = parent_wsi->mode;
-
- wsi->protocol = &vhost->protocols[0];
- if (lws_ensure_user_space(wsi))
- goto bail;
-
- lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
- parent_wsi, wsi, sid, wsi->user_space);
-
- return wsi;
-
-bail:
- vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
- NULL, NULL, 0);
- lws_free(wsi);
+ 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;
}
-int
-lws_http2_interpret_settings_payload(struct http2_settings *settings,
- unsigned char *buf, int len)
+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_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)
-{
- while (wsi->u.http2.parent_wsi)
- wsi = wsi->u.http2.parent_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.
+ */
- return wsi;
+int
+lws_h2_tx_cr_get(struct lws *wsi)
+{
+ int c = wsi->u.h2.tx_cr;
+ struct lws *nwsi;
+
+ 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;
-
- switch (wsi->state) {
- case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
- if (https_client_preface[wsi->u.http2.count++] != c)
- 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;
-
- /*
- * 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...
- */
-
- lws_set_protocol_write_pending(wsi,
- LWS_PPS_HTTP2_MY_SETTINGS);
- }
- 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;
- }
- break;
- case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
- wsi->u.http2.hpack_e_dep <<= 8;
- wsi->u.http2.hpack_e_dep |= c;
- break;
- }
- if (wsi->u.http2.count != wsi->u.http2.length)
- break;
-
- /* end of frame */
-
- 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;
- }
- 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;
- }
- break;
- }
- switch (wsi->u.http2.frame_state++) {
- case 0:
- wsi->u.http2.length = c;
- break;
- case 1:
- case 2:
- wsi->u.http2.length <<= 8;
- wsi->u.http2.length |= c;
- break;
- case 3:
- wsi->u.http2.type = c;
- break;
- case 4:
- wsi->u.http2.flags = c;
- break;
- case 5:
- case 6:
- case 7:
- case 8:
- wsi->u.http2.stream_id <<= 8;
- wsi->u.http2.stream_id |= c;
- 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);
-
- 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;
-
- 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;
-
- 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;
- }
-
- /* 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);
-
- swsi = wsi->u.http2.stream_wsi;
- if (!swsi)
- return 1;
-
-
- /* prepare the hpack parser at the right start */
-
- 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 (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;
- break;
- }
- if (wsi->u.http2.length == 0)
- wsi->u.http2.frame_state = 0;
-
- }
- break;
- }
-
- return 0;
-}
-
-int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
-{
- unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
- struct lws *swsi;
+ 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;
- 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);
+ /* 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_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
- 0, LWS_HTTP2_STREAM_ID_MASTER, m,
- &settings[LWS_PRE]);
+ 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);
- return 1;
+ goto bail;
}
break;
- case LWS_PPS_HTTP2_ACK_SETTINGS:
+
+ case LWS_H2_PPS_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]);
+ 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);
- return 1;
+ 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_http2_get_network_wsi(wsi))) {
- lwsl_info("skipping nonexistent ssl upgrade headers\n");
+ if (lws_is_ssl(lws_get_network_wsi(wsi)))
break;
- }
-
/*
- * we need to treat the headers from this upgrade
- * as the first job. These need to get
- * shifted to stream ID 1
+ * we need to treat the headers from the upgrade as the
+ * first job. So these need to get shifted to sid 1.
*/
- lwsl_info("%s: setting up sid 1\n", __func__);
+ h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1);
+ if (!h2n->swsi)
+ goto bail;
- 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;
+ h2n->swsi->u.http.ah = wsi->u.http.ah;
wsi->u.http.ah = NULL;
- 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;
+ 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 */
- swsi->u.http2.END_STREAM = 1;
+ h2n->swsi->u.h2.END_STREAM = 1;
lwsl_info("servicing initial http request\n");
- return lws_http_action(swsi);
+
+ wsi->vhost->conn_stats.h2_trans++;
+
+ if (lws_http_action(h2n->swsi))
+ goto bail;
+
+ break;
}
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]);
+ 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);
- return 1;
+ goto bail;
}
break;
+
+ 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;
+ 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;
+ }
+ 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 */
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+
+ if (!h2n->swsi->u.h2.END_HEADERS) {
+ /* we are not finished yet */
+ lwsl_info("witholding http action for continuation\n");
+ break;
+ }
+
+ /* 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;
+ }
+
+ /* 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 LWS_H2_STATE_RESERVED_REMOTE:
+ lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_LOCAL);
+ break;
+ }
+
+ 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);
+ }
+
+ {
+ int n = 0, len;
+ char buf[256];
+ const unsigned char *c;
+
+ do {
+ c = lws_token_to_string(n);
+ if (!c) {
+ n++;
+ continue;
+ }
+
+ len = lws_hdr_total_length(h2n->swsi, n);
+ if (!len || len > sizeof(buf) - 1) {
+ n++;
+ continue;
+ }
+
+ lws_hdr_copy(h2n->swsi, buf, sizeof buf, n);
+ buf[sizeof(buf) - 1] = '\0';
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 (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;
+ }
+ }
+
+ 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;
}
-struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
+int
+lws_h2_parser(struct lws *wsi, unsigned char c)
{
- do {
- wsi = wsi->u.http2.next_child_wsi;
- if (!wsi)
- return NULL;
- } while (n--);
+ struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
+ struct lws_h2_protocol_send *pps;
+ int n;
- return wsi;
+ if (!h2n)
+ return 1;
+
+ switch (wsi->state) {
+ case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
+ if (preface[h2n->count++] != c)
+ return 1;
+
+ 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;
+ lws_pps_schedule(wsi, pps);
+ break;
+
+ case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
+ case LWSS_HTTP2_ESTABLISHED:
+ if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH)
+ goto try_frame_start;
+
+ /*
+ * post-header, preamble / payload / padding part
+ */
+ h2n->count++;
+
+ if (h2n->flags & LWS_H2_FLAG_PADDED && !h2n->pad_length) {
+ /*
+ * Get the padding count... actual padding is
+ * at the end of the frame.
+ */
+ 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 */
+ }
+
+ if (h2n->flags & LWS_H2_FLAG_PRIORITY &&
+ !h2n->collected_priority) {
+ /* going to be 5 preamble bytes */
+
+ 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 */
+ }
+ 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;
+
+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;
}
+
diff --git a/lib/lejp-conf.c b/lib/lejp-conf.c
index c2f4793d1..e84f4b24c 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 c9fe6ff3a..ab42c3e47 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 2f4f079ac..f940afd25 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) */,
+/* 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 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) */,
+/* 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 0079: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0080 state 25) */,
- 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0203 state 243) */,
+/* pos 007c: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0083 state 25) */,
+ 0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0206 state 243) */,
0x08, /* fail */
-/* pos 0080: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0087 state 26) */,
- 0x74 /* 't' */, 0x86, 0x01 /* (to 0x0209 state 248) */,
+/* pos 0083: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x008A state 26) */,
+ 0x74 /* 't' */, 0x86, 0x01 /* (to 0x020C 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 015c: 133 */ 0xF4 /* 't' -> */,
-/* pos 015d: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0164 state 135) */,
- 0x2D /* '-' */, 0x59, 0x00 /* (to 0x01B9 state 192) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 0197: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x019E state 167) */,
- 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x0328 state 430) */,
+/* pos 019a: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x01A1 state 167) */,
+ 0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x032B 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 0254: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025B state 278) */,
- 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A4 state 524) */,
+/* pos 0257: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025E state 278) */,
+ 0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A7 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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) */,
+/* 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 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 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 692414f00..4fe4b2efd 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);
- n = 0;
- break;
- default: /* real error, close */
- n = shutdown(wsi->desc.sockfd, SHUT_WR);
- break;
}
+ if (SSL_want_write(wsi->ssl)) {
+ lws_change_pollfd(wsi, 0, LWS_POLLOUT);
+ n = 0;
+ 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 1d1daf0e3..5d358fd00 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);
@@ -4652,9 +4699,299 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
}
/**
- * lws_ring: generic ringbuffer struct
+ * lws_snprintf(): snprintf that truncates the returned length too
*
- * all of the members are opaque and manipulated by lws_ring_...() apis.
+ * \param str: destination buffer
+ * \param size: bytes left in destination buffer
+ * \param format: format string
+ * \param ...: args for format
+ *
+ * This lets you correctly truncate buffers by concatenating lengths, if you
+ * reach the limit the reported length doesn't exceed the limit.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
+
+/**
+ * lws_get_random(): fill a buffer with platform random data
+ *
+ * \param context: the lws context
+ * \param buf: buffer to fill
+ * \param len: how much to fill
+ *
+ * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
+ * it's interested to see if the frame it's dealing with was sent in binary
+ * mode.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_get_random(struct lws_context *context, void *buf, int len);
+/**
+ * lws_daemonize(): make current process run in the background
+ *
+ * \param _lock_path: the filepath to write the lock file
+ *
+ * Spawn lws as a background process, taking care of various things
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_daemonize(const char *_lock_path);
+/**
+ * lws_get_library_version(): return string describing the version of lws
+ *
+ * On unix, also includes the git describe
+ */
+LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
+lws_get_library_version(void);
+
+/**
+ * lws_wsi_user() - get the user data associated with the connection
+ * \param wsi: lws connection
+ *
+ * Not normally needed since it's passed into the callback
+ */
+LWS_VISIBLE LWS_EXTERN void *
+lws_wsi_user(struct lws *wsi);
+
+/**
+ * lws_wsi_set_user() - set the user data associated with the client connection
+ * \param wsi: lws connection
+ * \param user: user data
+ *
+ * By default lws allocates this and it's not legal to externally set it
+ * yourself. However client connections may have it set externally when the
+ * connection is created... if so, this api can be used to modify it at
+ * runtime additionally.
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_wsi_user(struct lws *wsi, void *user);
+
+/**
+ * lws_parse_uri: cut up prot:/ads:port/path into pieces
+ * Notice it does so by dropping '\0' into input string
+ * and the leading / on the path is consequently lost
+ *
+ * \param p: incoming uri string.. will get written to
+ * \param prot: result pointer for protocol part (https://)
+ * \param ads: result pointer for address part
+ * \param port: result pointer for port part
+ * \param path: result pointer for path part
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
+ const char **path);
+
+/**
+ * lws_now_secs(): return seconds since 1970-1-1
+ */
+LWS_VISIBLE LWS_EXTERN unsigned long
+lws_now_secs(void);
+
+/**
+ * lws_get_context - Allow geting lws_context from a Websocket connection
+ * instance
+ *
+ * With this function, users can access context in the callback function.
+ * Otherwise users may have to declare context as a global variable.
+ *
+ * \param wsi: Websocket connection instance
+ */
+LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT
+lws_get_context(const struct lws *wsi);
+
+/**
+ * lws_get_count_threads(): how many service threads the context uses
+ *
+ * \param context: the lws context
+ *
+ * By default this is always 1, if you asked for more than lws can handle it
+ * will clip the number of threads. So you can use this to find out how many
+ * threads are actually in use.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_get_count_threads(struct lws_context *context);
+
+/**
+ * lws_get_parent() - get parent wsi or NULL
+ * \param wsi: lws connection
+ *
+ * Specialized wsi like cgi stdin/out/err are associated to a parent wsi,
+ * this allows you to get their parent.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_get_parent(const struct lws *wsi);
+
+/**
+ * lws_get_child() - get child wsi or NULL
+ * \param wsi: lws connection
+ *
+ * Allows you to find a related wsi from the parent wsi.
+ */
+LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
+lws_get_child(const struct lws *wsi);
+
+/**
+ * lws_parent_carries_io() - mark wsi as needing to send messages via parent
+ *
+ * \param wsi: child lws connection
+ */
+
+LWS_VISIBLE LWS_EXTERN void
+lws_set_parent_carries_io(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void *
+lws_get_opaque_parent_data(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_set_opaque_parent_data(struct lws *wsi, void *data);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_get_child_pending_on_writable(const struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_clear_child_pending_on_writable(struct lws *wsi);
+
+LWS_VISIBLE LWS_EXTERN int
+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
+ * useful when integrating with other app poll loop service code.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
+
+/**
+ * lws_set_allocator() - custom allocator support
+ *
+ * \param realloc
+ *
+ * Allows you to replace the allocator (and deallocator) used by lws
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason));
+///@}
+
+/** \defgroup wsstatus Websocket status APIs
+ * ##Websocket connection status APIs
+ *
+ * These provide information about ws connection or message status
+ */
+///@{
+/**
+ * lws_send_pipe_choked() - tests if socket is writable or not
+ * \param wsi: lws connection
+ *
+ * Allows you to check if you can write more on the socket
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_send_pipe_choked(struct lws *wsi);
+
+/**
+ * lws_is_final_fragment() - tests if last part of ws message
+ *
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_final_fragment(struct lws *wsi);
+
+/**
+ * lws_is_first_fragment() - tests if first part of ws message
+ *
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_first_fragment(struct lws *wsi);
+
+/**
+ * lws_get_reserved_bits() - access reserved bits of ws frame
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN unsigned char
+lws_get_reserved_bits(struct lws *wsi);
+
+/**
+ * lws_partial_buffered() - find out if lws buffered the last write
+ * \param wsi: websocket connection to check
+ *
+ * Returns 1 if you cannot use lws_write because the last
+ * write on this connection is still buffered, and can't be cleared without
+ * returning to the service loop and waiting for the connection to be
+ * writeable again.
+ *
+ * If you will try to do >1 lws_write call inside a single
+ * WRITEABLE callback, you must check this after every write and bail if
+ * set, ask for a new writeable callback and continue writing from there.
+ *
+ * This is never set at the start of a writeable callback, but any write
+ * may set it.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_partial_buffered(struct lws *wsi);
+
+/**
+ * lws_frame_is_binary(): true if the current frame was sent in binary mode
+ *
+ * \param wsi: the connection we are inquiring about
+ *
+ * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
+ * it's interested to see if the frame it's dealing with was sent in binary
+ * mode.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_frame_is_binary(struct lws *wsi);
+
+/**
+ * lws_is_ssl() - Find out if connection is using SSL
+ * \param wsi: websocket connection to check
+ *
+ * Returns 0 if the connection is not using SSL, 1 if using SSL and
+ * using verified cert, and 2 if using SSL but the cert was not
+ * checked (appears for client wsi told to skip check on connection)
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_ssl(struct lws *wsi);
+/**
+ * lws_is_cgi() - find out if this wsi is running a cgi process
+ * \param wsi: lws connection
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_is_cgi(struct lws *wsi);
+
+#ifdef LWS_OPENSSL_SUPPORT
+/**
+ * lws_get_ssl() - Return wsi's SSL context structure
+ * \param wsi: websocket connection
+ *
+ * Returns pointer to the SSL library's context structure
+ */
+LWS_VISIBLE LWS_EXTERN SSL*
+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
*
@@ -4664,6 +5001,11 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
* 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()
@@ -4697,6 +5039,7 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
*
* - lws_ring_update_oldest_tail()
*/
+///@{
struct lws_ring;
/**
@@ -4856,7 +5199,6 @@ 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
*
@@ -4865,283 +5207,8 @@ lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
*/
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
- *
- * \param str: destination buffer
- * \param size: bytes left in destination buffer
- * \param format: format string
- * \param ...: args for format
- *
- * This lets you correctly truncate buffers by concatenating lengths, if you
- * reach the limit the reported length doesn't exceed the limit.
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3);
-
-/**
- * lws_get_random(): fill a buffer with platform random data
- *
- * \param context: the lws context
- * \param buf: buffer to fill
- * \param len: how much to fill
- *
- * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
- * it's interested to see if the frame it's dealing with was sent in binary
- * mode.
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_get_random(struct lws_context *context, void *buf, int len);
-/**
- * lws_daemonize(): make current process run in the background
- *
- * \param _lock_path: the filepath to write the lock file
- *
- * Spawn lws as a background process, taking care of various things
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_daemonize(const char *_lock_path);
-/**
- * lws_get_library_version(): return string describing the version of lws
- *
- * On unix, also includes the git describe
- */
-LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT
-lws_get_library_version(void);
-
-/**
- * lws_wsi_user() - get the user data associated with the connection
- * \param wsi: lws connection
- *
- * Not normally needed since it's passed into the callback
- */
-LWS_VISIBLE LWS_EXTERN void *
-lws_wsi_user(struct lws *wsi);
-
-/**
- * lws_wsi_set_user() - set the user data associated with the client connection
- * \param wsi: lws connection
- * \param user: user data
- *
- * By default lws allocates this and it's not legal to externally set it
- * yourself. However client connections may have it set externally when the
- * connection is created... if so, this api can be used to modify it at
- * runtime additionally.
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_set_wsi_user(struct lws *wsi, void *user);
-
-/**
- * lws_parse_uri: cut up prot:/ads:port/path into pieces
- * Notice it does so by dropping '\0' into input string
- * and the leading / on the path is consequently lost
- *
- * \param p: incoming uri string.. will get written to
- * \param prot: result pointer for protocol part (https://)
- * \param ads: result pointer for address part
- * \param port: result pointer for port part
- * \param path: result pointer for path part
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
- const char **path);
-
-/**
- * lws_now_secs(): return seconds since 1970-1-1
- */
-LWS_VISIBLE LWS_EXTERN unsigned long
-lws_now_secs(void);
-
-/**
- * lws_get_context - Allow geting lws_context from a Websocket connection
- * instance
- *
- * With this function, users can access context in the callback function.
- * Otherwise users may have to declare context as a global variable.
- *
- * \param wsi: Websocket connection instance
- */
-LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT
-lws_get_context(const struct lws *wsi);
-
-/**
- * lws_get_count_threads(): how many service threads the context uses
- *
- * \param context: the lws context
- *
- * By default this is always 1, if you asked for more than lws can handle it
- * will clip the number of threads. So you can use this to find out how many
- * threads are actually in use.
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_get_count_threads(struct lws_context *context);
-
-/**
- * lws_get_parent() - get parent wsi or NULL
- * \param wsi: lws connection
- *
- * Specialized wsi like cgi stdin/out/err are associated to a parent wsi,
- * this allows you to get their parent.
- */
-LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_get_parent(const struct lws *wsi);
-
-/**
- * lws_get_child() - get child wsi or NULL
- * \param wsi: lws connection
- *
- * Allows you to find a related wsi from the parent wsi.
- */
-LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
-lws_get_child(const struct lws *wsi);
-
-/**
- * lws_parent_carries_io() - mark wsi as needing to send messages via parent
- *
- * \param wsi: child lws connection
- */
-
-LWS_VISIBLE LWS_EXTERN void
-lws_set_parent_carries_io(struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN void *
-lws_get_opaque_parent_data(const struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN void
-lws_set_opaque_parent_data(struct lws *wsi, void *data);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_get_child_pending_on_writable(const struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN void
-lws_clear_child_pending_on_writable(struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN int
-lws_get_close_length(struct lws *wsi);
-
-LWS_VISIBLE LWS_EXTERN unsigned char *
-lws_get_close_payload(struct lws *wsi);
-
-/*
- * \deprecated DEPRECATED Note: this is not normally needed as a user api.
- * It's provided in case it is
- * useful when integrating with other app poll loop service code.
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
-
-/**
- * lws_set_allocator() - custom allocator support
- *
- * \param realloc
- *
- * Allows you to replace the allocator (and deallocator) used by lws
- */
-LWS_VISIBLE LWS_EXTERN void
-lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason));
///@}
-/** \defgroup wsstatus Websocket status APIs
- * ##Websocket connection status APIs
- *
- * These provide information about ws connection or message status
- */
-///@{
-/**
- * lws_send_pipe_choked() - tests if socket is writable or not
- * \param wsi: lws connection
- *
- * Allows you to check if you can write more on the socket
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_send_pipe_choked(struct lws *wsi);
-
-/**
- * lws_is_final_fragment() - tests if last part of ws message
- *
- * \param wsi: lws connection
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_is_final_fragment(struct lws *wsi);
-
-/**
- * lws_is_first_fragment() - tests if first part of ws message
- *
- * \param wsi: lws connection
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_is_first_fragment(struct lws *wsi);
-
-/**
- * lws_get_reserved_bits() - access reserved bits of ws frame
- * \param wsi: lws connection
- */
-LWS_VISIBLE LWS_EXTERN unsigned char
-lws_get_reserved_bits(struct lws *wsi);
-
-/**
- * lws_partial_buffered() - find out if lws buffered the last write
- * \param wsi: websocket connection to check
- *
- * Returns 1 if you cannot use lws_write because the last
- * write on this connection is still buffered, and can't be cleared without
- * returning to the service loop and waiting for the connection to be
- * writeable again.
- *
- * If you will try to do >1 lws_write call inside a single
- * WRITEABLE callback, you must check this after every write and bail if
- * set, ask for a new writeable callback and continue writing from there.
- *
- * This is never set at the start of a writeable callback, but any write
- * may set it.
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_partial_buffered(struct lws *wsi);
-
-/**
- * lws_frame_is_binary(): true if the current frame was sent in binary mode
- *
- * \param wsi: the connection we are inquiring about
- *
- * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
- * it's interested to see if the frame it's dealing with was sent in binary
- * mode.
- */
-LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
-lws_frame_is_binary(struct lws *wsi);
-
-/**
- * lws_is_ssl() - Find out if connection is using SSL
- * \param wsi: websocket connection to check
- *
- * Returns 0 if the connection is not using SSL, 1 if using SSL and
- * using verified cert, and 2 if using SSL but the cert was not
- * checked (appears for client wsi told to skip check on connection)
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_is_ssl(struct lws *wsi);
-/**
- * lws_is_cgi() - find out if this wsi is running a cgi process
- * \param wsi: lws connection
- */
-LWS_VISIBLE LWS_EXTERN int
-lws_is_cgi(struct lws *wsi);
-
-#ifdef LWS_OPENSSL_SUPPORT
-/**
- * lws_get_ssl() - Return wsi's SSL context structure
- * \param wsi: websocket connection
- *
- * Returns pointer to the SSL library's context structure
- */
-LWS_VISIBLE LWS_EXTERN SSL*
-lws_get_ssl(struct lws *wsi);
-#endif
-///@}
-
-
/** \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 517f344d5..b5adabe1f 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 cb5e47c3f..f55369129 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 5d7d9387c..45198bc97 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
+#if defined(LWS_WITH_ESP32)
+#undef MBEDTLS_CONFIG_FILE
+#define MBEDTLS_CONFIG_FILE
+#endif
+
#include "ssl_code.h"
typedef void SSL_CIPHER;
diff --git a/lib/output.c b/lib/output.c
index 4d6897e01..0c5f41c87 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 99119e132..f4c412d82 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;
+ case LPUR_SWALLOW:
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;
+ case LPUR_FORBID:
+ goto forbid;
+ case LPUR_EXCESSIVE:
+ goto excessive;
+ default:
+ return -1;
}
-
- /*
- * 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;
- goto swallow;
- }
-
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 5f9d94d55..4d6704d41 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 7d30078f4..16ddcba19 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;
};
+#define LWS_H2_FRAME_HEADER_LENGTH 9
+
#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,
+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_HTTP2_SETTINGS__COUNT /* always last */
+ LWS_H2_FRAME_TYPE_COUNT /* always last */
};
-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,
+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_FRAME_TYPE_COUNT /* always last */
+ LWS_H2_FLAG_SETTINGS_ACK = 1,
};
-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,
-
- LWS_HTTP2_FLAG_SETTINGS_ACK = 1,
+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 */
};
-#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_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
+ */
};
+#define LWS_H2_STREAM_ID_MASTER 0
+#define LWS_H2_SETTINGS_LEN 6
+
enum http2_hpack_state {
-
- /* optional before first header block */
- HPKS_OPT_PADDING,
- HKPS_OPT_E_DEPENDENCY,
- HKPS_OPT_WEIGHT,
-
- /* header block */
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;
};
-struct _lws_http2_related {
+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_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 b915759c4..3ddbb67f8 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 (!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);
- 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 23d51bbf7..49760f828 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)) {
- lwsl_debug("Closing POLLOUT child\n");
- lws_close_free_wsi(wsi2, LWS_CLOSE_STATUS_NOSTATUS);
+ 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;
}
- wsi2 = wsi;
- } while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
- lwsl_info("%s: completed\n", __func__);
+ /*
+ * 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(w, LWS_CLOSE_STATUS_NOSTATUS);
+ wa = &wsi->u.h2.child_list;
+ }
+
+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 9c25646f1..4931d90a4 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
+ * Copyright (C) 2010-2017 Andy Green
*
* 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 a7590bb00..a9516f223 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 450fbc382..15bc810b9 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;
+ }
+
+ if (SSL_want_write(wsi->ssl)) {
+ lws_set_blocking_send(wsi);
+
+ lwsl_notice("%s: want write\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 !defined(LWS_WITH_MBEDTLS)
- if (n == SSL_ERROR_SYSCALL) {
- 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));
+ 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 eae913ef1..16307f527 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 8d1426086..950c7b723 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 a1dc14bee..d3e8808d4 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 686599c25..da82dc6b0 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 db25badaa..4944cff1a 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 a9fb0657b..3683a12b1 100644
--- a/plugins/protocol_lws_mirror.c
+++ b/plugins/protocol_lws_mirror.c
@@ -27,13 +27,13 @@
#include
#include
-#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 96b2ef298..9619eb66b 100644
--- a/plugins/protocol_lws_server_status.c
+++ b/plugins/protocol_lws_server_status.c
@@ -23,6 +23,8 @@
#include "../lib/libwebsockets.h"
#include
#include
+#include
+#include
#include
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 5b32139b7..d9240caed 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 7560bf5a7..8880fe2e4 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 a49513252..e59888b29 100644
--- a/plugins/server-status.html
+++ b/plugins/server-status.html
@@ -5,40 +5,37 @@
LWS Server Status