http2: make usable

HTTP/2 support is now able to serve the test server, complete with
websockets, from a single vhost.

 - This works the same with both OpenSSL and mbedTLS.

 - POST is now wired up and works (also for file upload).

 - CGI is wired up and works.

 - Redirect is adapted and works

 - lwsws works.

 - URI urldecode, sanitation and argument parsing wired up for :path

valgrind clean (aside from openssl-style false uninit data usage in mbedtls send occasionally)

h2spec reports:

$ h2spec  -h 127.0.0.1 -p 7681 -t -k -o 1
...
145 tests, 145 passed, 0 skipped, 0 failed"

Incorporates:

 - "https://github.com/warmcat/libwebsockets/pull/1039
	Fixes issue with -Werror=unused-variable flag

 - 2c843a1395
	ssl: fix infinite loop on client cert verification failure

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>"

Caused and fixes Coverity 184887 - 184892
This commit is contained in:
Andy Green 2017-10-13 10:33:02 +08:00
parent 028551271e
commit 904a9c0920
48 changed files with 5511 additions and 2585 deletions

View file

@ -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")

View file

@ -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`

View file

@ -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

View file

@ -2,13 +2,17 @@
Libwebsockets covers a lot of interesting features for people making embedded servers or clients
- http(s) serving and client operation
- ws(s) serving and client operation
- http(s) apis for file transfer and upload
- http POST form handling (including multipart)
- HTTP(S) serving and client operation
- HTTP/2 support for serving
- WS(S) serving and client operation
- HTTP(S) apis for file transfer and upload
- HTTP 1 + 2 POST form handling (including multipart / file upload)
- cookie-based sessions
- account management (including registration, email verification, lost pw etc)
- strong ssl PFS support (A+ on SSLlabs test)
- strong SSL / TLS PFS support (A+ on SSLlabs test)
- ssh server integration
- serving gzipped files directly from inside zip files, without conversion
- support for linux, bsd, windows etc... and very small nonlinux targets like ESP32
You can browse by api category <a href="modules.html">here</a>

View file

@ -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 ;\

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -107,8 +107,8 @@ int lws_add_http_header_content_length(struct lws *wsi,
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
return 1;
wsi->u.http.content_length = content_length;
wsi->u.http.content_remain = content_length;
wsi->u.http.tx_content_length = content_length;
wsi->u.http.tx_content_remain = content_length;
return 0;
}
@ -228,7 +228,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
unsigned char *p = pt->serv_buf + LWS_PRE;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
int n = 0, m, len;
int n = 0, m = 0, len;
char slen[20];
if (!html_body)
@ -254,29 +254,65 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
return 1;
#if defined(LWS_WITH_HTTP2)
{
if (wsi->http2_substream) {
unsigned char *body = p + 512;
/*
* for HTTP/2, the headers must be sent separately, since they
* go out in their own frame. That puts us in a bind that
* we won't always be able to get away with two lws_write()s in
* sequence, since the first may use up the writability due to
* the pipe being choked or SSL_WANT_.
*
* However we do need to send the human-readable body, and the
* END_STREAM.
*
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != (int)(p - start))
return 1;
len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
/*
* ... but stash the body and send it as a priority next
* handle_POLLOUT
*/
n = len;
m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
}
#else
p += lws_snprintf((char *)p, end - p - 1,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
wsi->u.http.tx_content_length = len;
wsi->u.http.tx_content_remain = len;
n = (int)(p - start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
"pending status body");
if (!wsi->u.h2.pending_status_body)
return -1;
strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
(const char *)body);
lws_callback_on_writable(wsi);
return 0;
} else
#endif
{
/*
* for http/1, we can just append the body after the finalized
* headers and send it all in one go.
*/
p += lws_snprintf((char *)p, end - p - 1,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
n = (int)(p - start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
}
lwsl_notice("%s: return\n", __func__);
return m != n;
}
@ -313,7 +349,7 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
if (lws_finalize_http_header(wsi, p, end))
return -1;
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS);
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
return n;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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]);

View file

@ -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 */

File diff suppressed because it is too large Load diff

View file

@ -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++) {

View file

@ -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
*

View file

@ -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;

View file

@ -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;

View file

@ -19,6 +19,12 @@
extern "C" {
#endif
#include <lws_config.h>
#if defined(LWS_WITH_ESP32)
#undef MBEDTLS_CONFIG_FILE
#define MBEDTLS_CONFIG_FILE <mbedtls/esp_config.h>
#endif
#include "ssl_code.h"
typedef void SSL_CIPHER;

View file

@ -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 */
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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 (

View file

@ -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

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -49,8 +49,8 @@
#include "private-libwebsockets.h"
#ifndef LWS_NO_SERVER
#ifdef LWS_OPENSSL_SUPPORT
#if !defined(LWS_NO_SERVER)
#if defined(LWS_OPENSSL_SUPPORT)
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
@ -59,29 +59,19 @@ struct alpn_ctx {
unsigned short len;
};
static int
npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
{
struct alpn_ctx *alpn_ctx = arg;
lwsl_info("%s\n", __func__);
*data = alpn_ctx->data;
*len = alpn_ctx->len;
return SSL_TLSEXT_ERR_OK;
}
static int
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
#if !defined(LWS_WITH_MBEDTLS)
struct alpn_ctx *alpn_ctx = arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_NOACK;
#endif
return SSL_TLSEXT_ERR_OK;
}
#endif
@ -93,9 +83,6 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost)
static struct alpn_ctx protos = { (unsigned char *)"\x02h2"
"\x08http/1.1", 6 + 9 };
SSL_CTX_set_next_protos_advertised_cb(vhost->ssl_ctx, npn_cb, &protos);
// ALPN selection callback
SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &protos);
lwsl_notice(" HTTP2 / ALPN enabled\n");
#else
@ -105,34 +92,36 @@ lws_context_init_http2_ssl(struct lws_vhost *vhost)
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
void lws_http2_configure_if_upgraded(struct lws *wsi)
int lws_h2_configure_if_upgraded(struct lws *wsi)
{
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L)
struct allocated_headers *ah;
const char *method = "alpn";
const unsigned char *name;
const unsigned char *name = NULL;
char cstr[10];
unsigned len;
SSL_get0_alpn_selected(wsi->ssl, &name, &len);
if (!len) {
SSL_get0_next_proto_negotiated(wsi->ssl, &name, &len);
method = "npn";
lwsl_info("no ALPN upgrade\n");
return 0;
}
if (!len) {
lwsl_info("no npn/alpn upgrade\n");
return;
}
if (len > sizeof(cstr) - 1)
len = sizeof(cstr) - 1;
(void)method;
lwsl_info("negotiated %s using %s\n", name, method);
memcpy(cstr, name, len);
cstr[len] = '\0';
lwsl_info("negotiated '%s' using ALPN\n", cstr);
wsi->use_ssl = 1;
if (strncmp((char *)name, "http/1.1", 8) == 0)
return;
return 0;
/* http2 */
wsi->upgraded_to_http2 = 1;
wsi->vhost->conn_stats.h2_alpn++;
/* adopt the header info */
ah = wsi->u.hdr.ah;
@ -143,13 +132,21 @@ void lws_http2_configure_if_upgraded(struct lws *wsi)
/* http2 union member has http union struct at start */
wsi->u.http.ah = ah;
wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
if (!wsi->u.h2.h2n)
return 1;
lws_http2_init(&wsi->u.http2.peer_settings);
lws_http2_init(&wsi->u.http2.my_settings);
lws_h2_init(wsi);
/* HTTP2 union */
lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
wsi->u.h2.tx_cr = 65535;
lwsl_info("%s: wsi %p: configured for h2\n", __func__, wsi);
return 0;
#endif
}
#endif
#endif

View file

@ -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)

114
lib/ssl.c
View file

@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -27,13 +27,13 @@
#include <string.h>
#include <stdlib.h>
#define QUEUELEN 64
#define QUEUELEN 32
/* queue free space below this, rx flow is disabled */
#define RXFLOW_MIN (4)
/* queue free space above this, rx flow is enabled */
#define RXFLOW_MAX (QUEUELEN / 3)
#define MAX_MIRROR_INSTANCES 10
#define MAX_MIRROR_INSTANCES 3
struct mirror_instance;
@ -44,6 +44,7 @@ struct per_session_data__lws_mirror {
uint32_t tail;
};
/* this is the element in the ring */
struct a_message {
void *payload;
size_t len;
@ -53,6 +54,7 @@ struct mirror_instance {
struct mirror_instance *next;
struct per_session_data__lws_mirror *same_mi_pss_list;
struct lws_ring *ring;
int messages_allocated;
char name[30];
char rx_enabled;
};
@ -99,7 +101,7 @@ mirror_rxflow_instance(struct mirror_instance *mi, int enable)
static int
mirror_update_worst_tail(struct mirror_instance *mi)
{
uint32_t wai, worst = 0, worst_tail, oldest;
uint32_t wai, worst = 0, worst_tail = 0, oldest;
struct per_session_data__lws_mirror *worst_pss = NULL;
oldest = lws_ring_get_oldest_tail(mi->ring);
@ -317,10 +319,6 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
update_worst = oldest_tail == pss->tail;
sent_something = 0;
lwsl_debug(" original oldest %d, free elements %ld\n",
oldest_tail,
lws_ring_get_count_free_elements(pss->mi->ring));
do {
msg = lws_ring_get_element(pss->mi->ring, &pss->tail);
if (!msg)
@ -385,6 +383,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
lwsl_notice("OOM: dropping\n");
break;
}
memcpy((char *)amsg.payload + LWS_PRE, in, len);
if (!lws_ring_insert(pss->mi->ring, &amsg, 1)) {
mirror_destroy_message(&amsg);
@ -414,6 +413,7 @@ req_writable:
callback_lws_mirror, \
sizeof(struct per_session_data__lws_mirror), \
128, /* rx buf size must be >= permessage-deflate rx size */ \
0, NULL, 0 \
}
#if !defined (LWS_PLUGIN_STATIC)

View file

@ -23,6 +23,8 @@
#include "../lib/libwebsockets.h"
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct lws_ss_load_sample {
@ -102,8 +104,9 @@ uv_timeout_cb_server_status(uv_timer_t *w
contents[n] = '\0';
lws_json_purify(pure, contents, sizeof(pure));
n = lws_snprintf(p, l, "{\"path\":\"%s\",\"val\":\"%s\"}",
fp->filepath, pure);
n = lws_snprintf(p, l,
"{\"path\":\"%s\",\"val\":\"%s\"}",
fp->filepath, pure);
p += n;
l -= n;
first = 0;
@ -162,20 +165,22 @@ callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason,
if (!strcmp(pvo->name, "filepath")) {
fp = malloc(sizeof(*fp));
fp->next = NULL;
lws_snprintf(&fp->filepath[0], sizeof(fp->filepath), "%s", pvo->value);
lws_snprintf(&fp->filepath[0],
sizeof(fp->filepath), "%s",
pvo->value);
*fp_old = fp;
fp_old = &fp->next;
}
pvo = pvo->next;
}
v->context = lws_get_context(wsi);
uv_timer_init(lws_uv_getloop(v->context, 0), &v->timeout_watcher);
uv_timer_init(lws_uv_getloop(v->context, 0),
&v->timeout_watcher);
uv_timer_start(&v->timeout_watcher,
uv_timeout_cb_server_status, 2000, period);
uv_timeout_cb_server_status, 2000, period);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */
// lwsl_notice("ss: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", v, v->context);
if (!v)
break;
uv_timer_stop(&v->timeout_watcher);
@ -213,7 +218,7 @@ static const struct lws_protocols protocols[] = {
LWS_EXTERN LWS_VISIBLE int
init_protocol_lws_server_status(struct lws_context *context,
struct lws_plugin_capability *c)
struct lws_plugin_capability *c)
{
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
lwsl_err("Plugin API %d, library API %d",
@ -234,4 +239,3 @@ destroy_protocol_lws_server_status(struct lws_context *context)
{
return 0;
}

View file

@ -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)

View file

@ -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)

View file

@ -5,40 +5,37 @@
<script src="/lws-common.js"></script>
<title>LWS Server Status</title>
<style type="text/css">
span.title { font-size:18pt; font: Arial; font-weight:normal;
span.title { font-size:18pt; font-family: Arial; font-weight:normal;
text-align:center; color:#000000; }
span.mount { font-size:10pt; font: Arial; font-weight:normal;
span.mount { font-size:10pt; font-family: Arial; font-weight:normal;
text-align:center; color:#000000; }
span.mountname { font-size:14pt; font: Arial; font-weight:bold;
span.mountname { font-size:14pt; font-family: Arial; font-weight:bold;
text-align:center; color:#404010; }
span.n { font-size:12pt; font: Arial; font-weight:normal;
span.n { font-size:12pt; font-family: Arial; font-weight:normal;
text-align:center; color:#808020; }
span.v { font-size:12pt; font: Arial; font-weight:bold;
span.v { font-size:12pt; font-family: Arial; font-weight:bold;
text-align:center; color:#202020; }
span.m1 { font-size:12pt; font: Arial; font-weight:bold;
span.m1 { font-size:12pt; font-family: Arial; font-weight:bold;
text-align:center; color:#202020; }
span.m2 { font-size:12pt; font: Arial; font-weight:normal;
span.m2 { font-size:12pt; font-family: Arial; font-weight:normal;
text-align:center; color:#202020; }
.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
.browser { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;}
.group2 { vertical-align:middle;
text-align:center;
background:#f0f0e0;
padding:12px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px; }
.explain { vertical-align:middle;
text-align:center;
background:#f0f0c0; padding:12px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
color:#404000; }
td.wsstatus { vertical-align:middle; width:200px; height:50px;
text-align:center;
background:#f0f0c0; padding:6px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
border-radius:8px;
color:#404000; }
td.l { vertical-align:middle;
@ -46,52 +43,45 @@
background:#d0d0b0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.dl { vertical-align:middle;
text-align:center;
background:#c0c0c0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.c { vertical-align:middle;
text-align:center;
background:#c0c0a0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.c0 { vertical-align:middle;
text-align:center;
background:#b0b090;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.dc0 { vertical-align:middle;
text-align:center;
background:#a0a0a0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.c1 { vertical-align:middle;
text-align:center;
background:#c0c0c0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
td.t { vertical-align:middle;
text-align:center;
background:#e0e0c0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
border-radius:3px; }
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
.tabs {
position: relative;
min-height: 750px; /* This part sucks */
@ -279,16 +269,22 @@ function get_appropriate_ws_url()
"<br>" +
"<span class=n>Listening wsi:</span> <span class=v>" + san(jso.i.contexts[ci].listen_wsi) + "</span>, " +
"<span class=n>Current wsi alive:</span> <span class=v>" + (parseInt(san(jso.i.contexts[ci].wsi_alive)) -
parseInt(san(jso.i.contexts[ci].listen_wsi))) + "</span><br>" +
parseInt(san(jso.i.contexts[ci].listen_wsi))) + "</span><br>" +
"<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].rx)) +"</span>, " +
"<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].tx)) +"</span><br>" +
"<span class=n>Total connections:</span> <span class=v>" + san(jso.i.contexts[ci].conn) +"</span>, " +
"<span class=n>Total HTTP Transactions:</span> <span class=v>" + san(jso.i.contexts[ci].trans) +"</span><br>" +
"<span class=n>Total ws upgrades:</span> <span class=v>" + san(jso.i.contexts[ci].ws_upg) +"</span>, " +
"<span class=n>Total h2 upgrades:</span> <span class=v>" + san(jso.i.contexts[ci].http2_upg) +"</span>, " +
"<span class=n>Total Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].rejected) +"</span><br>" +
"<span class=n>Current cgi alive:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_alive) + "</span>, " +
"<span class=n>Total CGI spawned:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_spawned) +
"<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_conn) +"</span>, " +
"<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].ws_upg) +"</span>, " +
"<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].h2_upg) +"</span>, " +
"<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].h2_alpn) +"</span>, " +
"<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].rejected) +"</span><br>" +
"<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].h1_trans) + "</span>, " +
"<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].h2_trans) +"</span>, " +
"<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].h2_subs) +"</span><br>" +
"<span class=n>CGI: alive:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_alive) + "</span>, " +
"<span class=n>spawned:</span> <span class=v>" + san(jso.i.contexts[ci].cgi_spawned) +
"</span><table>";
for (n = 0; n < jso.i.contexts[ci].pt.length; n++) {
@ -322,13 +318,20 @@ function get_appropriate_ws_url()
if (jso.i.contexts[ci].vhosts[n].sts == '1')
s = s + " (STS)";
s = s +"<br>" +
"<span class=n>rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].rx)) + "B</span>, " +
"<span class=n>tx</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].tx)) + "B</span><br>" +
"<span class=n>vh connections</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].conn) + "</span>, " +
"<span class=n>vh http transactions</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].trans) + "</span><br>" +
"<span class=n>vh upgrades to ws:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].ws_upg) + "</span>, " +
"<span class=n>to http/2:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].http2_upg) + "</span>, " +
"<span class=n>rejected:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].rejected) + "</span><br>" +
"<span class=n>Total Rx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].rx)) +"</span>, " +
"<span class=n>Total Tx:</span> <span class=v>" + humanize(san(jso.i.contexts[ci].vhosts[n].tx)) +"</span><br>" +
"<span class=n>CONNECTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_conn) +"</span>, " +
"<span class=n>Websocket:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].ws_upg) +"</span>, " +
"<span class=n>H2 upgrade:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_upg) +"</span>, " +
"<span class=n>H2 ALPN:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_alpn) +"</span>, " +
"<span class=n>Rejected:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].rejected) +"</span><br>" +
"<span class=n>TRANSACTIONS: HTTP/1.x:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h1_trans) + "</span>, " +
"<span class=n>H2:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_trans) +"</span>, " +
"<span class=n>Total H2 substreams:</span> <span class=v>" + san(jso.i.contexts[ci].vhosts[n].h2_subs) +"</span><br>" +
"<table style=\"margin-left:16px\"><tr><td class=t>Mountpoint</td><td class=t>Origin</td><td class=t>Cache Policy</td></tr>";
var m;

View file

@ -22,11 +22,13 @@ if [ "$TRAVIS_OS_NAME" == "osx" ];
then
if [ "$LWS_METHOD" == "libev" ];
then
brew update;
brew install libev;
fi
if [ "$LWS_METHOD" == "libuv" -o "$LWS_METHOD" == "lwsws" ];
then
brew update;
brew install libuv;
fi

View file

@ -29,11 +29,18 @@ function check {
fi
if [ "$1" = "defaultplusforbidden" ] ; then
cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
echo -e -n "HTTP/1.0 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
diff /tmp/lwscap /tmp/plusforb > /dev/null
if [ $? -ne 0 ] ; then
echo "FAIL: got something other than test.html + forbidden back"
exit 1
cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb
echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a<html><body><h1>403</h1></body></html>" >> /tmp/plusforb
diff /tmp/lwscap /tmp/plusforb > /dev/null
if [ $? -ne 0 ] ; then
echo "FAIL: got something other than test.html + forbidden back"
exit 1
fi
fi
fi
@ -98,7 +105,7 @@ function check {
rm -rf $LOG
killall libwebsockets-test-server 2>/dev/null
libwebsockets-test-server -d15 2>> $LOG &
libwebsockets-test-server -d127 2>> $LOG &
CPID=$!
echo "Started server on PID $CPID"
@ -111,7 +118,7 @@ check
echo
echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F"
rm -f /tmp/lwscap
echo -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check 1 "UPDATE_SETTINGS=1"
check 2 "Root_Channels_1_Channel_name_http_post=?"
check 3 "Root_Channels_1_Channel_location_http_post=?"
@ -120,14 +127,14 @@ check
echo
echo "---- ? processing (/cgi-bin/settings.js?key1=value1)"
rm -f /tmp/lwscap
echo -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET /cgi-bin/settings.js?key1=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check 1 "key1=value1"
check
echo
echo "---- ? processing (/t%3dest?key1%3d2=value1)"
rm -f /tmp/lwscap
echo -e "GET /t%3dest?key1%3d2=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET /t%3dest?key1%3d2=value1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check 0 "/t=est"
check 1 "key1_2=value1"
check
@ -135,14 +142,14 @@ check
echo
echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)"
rm -f /tmp/lwscap
echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check 1 "arg=1"
check
echo
echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)"
rm -f /tmp/lwscap
echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check 1 "arg=/../."
check
@ -169,17 +176,17 @@ check
echo
echo "---- missing URI"
echo -e "GET HTTP/1.1\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap
echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap
check
echo
echo "---- repeated method"
echo -e "GET blah HTTP/1.1\x0d\x0aGET blah HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT >/tmp/lwscap
echo -n -e "GET blah HTTP/1.0\x0d\x0aGET blah HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT >/tmp/lwscap
check
echo
echo "---- crazy header name part"
echo -e "GET blah HTTP/1.1\x0d\x0a................................................................................................................" \
echo -n -e "GET blah HTTP/1.0\x0d\x0a................................................................................................................" \
"......................................................................................................................." \
"......................................................................................................................." \
"......................................................................................................................." \
@ -201,7 +208,7 @@ check
echo
echo "---- excessive uri content"
echo -e "GET ................................................................................................................" \
echo -n -e "GET ................................................................................................................" \
"......................................................................................................................." \
"......................................................................................................................." \
"......................................................................................................................." \
@ -223,7 +230,7 @@ check
echo
echo "---- good request but http payload coming too (test.html served then forbidden)"
echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
echo -n -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
"......................................................................................................................." \
"......................................................................................................................." \
"......................................................................................................................." \
@ -246,70 +253,70 @@ check
echo
echo "---- nonexistent file"
rm -f /tmp/lwscap
echo -e "GET /nope HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check media
check
echo
echo "---- relative uri path"
rm -f /tmp/lwscap
echo -e "GET nope HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check forbidden
check
echo
echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check rejected
check
echo
echo "---- directory attack 2 (/../ should be /)"
rm -f /tmp/lwscap
echo -e -n "GET /../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check default
check
echo
echo "---- directory attack 3 (/./ should be /)"
rm -f /tmp/lwscap
echo -e -n "GET /./ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /./ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check default
check
echo
echo "---- directory attack 4 (/blah/.. should be /)"
rm -f /tmp/lwscap
echo -e -n "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /blah/.. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check default
check
echo
echo "---- directory attack 5 (/blah/../ should be /)"
rm -f /tmp/lwscap
echo -e -n "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /blah/../ HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check default
check
echo
echo "---- directory attack 6 (/blah/../. should be /)"
rm -f /tmp/lwscap
echo -e -n "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /blah/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check default
check
echo
echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check rejected
check
echo
echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check rejected
check

BIN
test-apps/http2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -1,6 +1,7 @@
#!/bin/sh
echo -e -n "Content-type: text/html\x0d\x0a"
echo -e -n "content-type: text/html\x0d\x0a"
echo -e -n "transfer-encoding: chunked\x0d\x0a"
echo -e -n "\x0d\x0a"
echo "<html><body>"

View file

@ -14,6 +14,7 @@
*/
function lws_gray_out(vis, options) {
var options = options || {};
var zindex = options.zindex || 50;
var opacity = options.opacity || 70;
@ -36,7 +37,7 @@ function lws_gray_out(vis, options) {
if (vis) {
dark.style.opacity = opaque;
dark.style.MozOpacity = opaque;
dark.style.filter ='alpha(opacity='+opacity+')';
// dark.style.filter ='alpha(opacity='+opacity+')';
dark.style.zIndex = zindex;
dark.style.backgroundColor = bgcolor;
dark.style.width = gsize(1);

View file

@ -223,7 +223,7 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
force_exit = 1;
break;
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param) && !defined(LWS_WITH_MBEDTLS)
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
if (crl_path[0]) {
/* Enable CRL checking of the server certificate */

View file

@ -221,10 +221,8 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
}
}
{
lws_get_peer_simple(wsi, buf, sizeof(buf));
if (lws_get_peer_simple(wsi, buf, sizeof(buf)))
lwsl_info("HTTP connect from %s\n", buf);
}
if (len < 1) {
lws_return_http_status(wsi,
@ -276,8 +274,16 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
#endif
/* if a legal POST URL, let it continue and accept data */
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
n = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_POST_URI);
if (n < 0)
return -1;
if (strcmp(buf, "/formtest")) {
lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL);
return -1;
}
return 0;
}
/* check for the "send a big file by hand" example case */
@ -400,6 +406,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
(unsigned char *)leaf_path + sizeof(leaf_path)))
return 1;
}
#if !defined(LWS_WITH_HTTP2)
if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi,
(unsigned char *)
"Strict-Transport-Security:",
@ -409,6 +416,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
(unsigned char *)leaf_path +
sizeof(leaf_path)))
return 1;
#endif
n = (char *)p - leaf_path;
n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n);
@ -513,15 +521,13 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
goto try_to_reuse;
case LWS_CALLBACK_HTTP_WRITEABLE:
lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n");
lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE: %p\n", wsi);
if (pss->client_finished)
return -1;
if (!lws_get_child(wsi) && !pss->fop_fd) {
lwsl_notice("fop_fd NULL\n");
if (!lws_get_child(wsi) && !pss->fop_fd)
goto try_to_reuse;
}
#ifndef LWS_NO_CLIENT
if (pss->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
@ -769,7 +775,7 @@ bail:
return 1;
}
break;
#if defined(LWS_HAVE_SSL_CTX_set1_param)
#if defined(LWS_HAVE_SSL_CTX_set1_param) && !defined(LWS_WITH_MBEDTLS)
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
if (crl_path[0]) {
/* Enable CRL checking */

View file

@ -1,7 +1,7 @@
/*
* libwebsockets-test-server - libwebsockets test implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
@ -383,7 +383,7 @@ int main(int argc, char **argv)
lws_set_log_level(debug_level, lwsl_emit_syslog);
lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n");
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
lwsl_notice("(C) Copyright 2010-2017 Andy Green <andy@warmcat.com>\n");
printf("Using resource path \"%s\"\n", resource_path);
#ifdef EXTERNAL_POLL
@ -425,7 +425,7 @@ int main(int argc, char **argv)
}
info.gid = gid;
info.uid = uid;
info.max_http_header_pool = 16;
info.max_http_header_pool = 256;
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
info.extensions = exts;
info.timeout_secs = 5;
@ -442,7 +442,7 @@ int main(int argc, char **argv)
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
info.ip_limit_ah = 4; /* for testing */
info.ip_limit_ah = 24; /* for testing */
info.ip_limit_wsi = 105; /* for testing */
if (use_ssl)

View file

@ -5,48 +5,44 @@
<script src="/lws-common.js"></script>
<title>Minimal Websocket test app</title>
<style type="text/css">
span.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
span.title { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000; }
.browser { font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; border-radius:10px;}
.group2 { vertical-align:middle;
text-align:center;
background:#f0f0e0;
padding:12px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px; }
.explain { vertical-align:middle;
text-align:center;
background:#f0f0c0; padding:12px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
color:#404000; }
td.wsstatus { vertical-align:middle; width:200px; height:50px;
text-align:center;
background:#f0f0c0; padding:6px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
border-radius:8px;
color:#404000; }
.tdform { vertical-align:middle; width:200px; height:50px;
text-align:center;
background:#f0f0d0; padding:6px;
-webkit-border-radius:8px;
-moz-border-radius:8px; margin:10px;
margin:10px;
border-radius:8px;
border: 1px solid black;
border-collapse: collapse;font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000;
border-collapse: collapse;font-size:18pt; font-family: Arial; font-weight:normal; text-align:center; color:#000000;
color:#404000; }
td.l { vertical-align:middle;
text-align:center;
background:#d0d0b0;
padding:3px;
-webkit-border-radius:3px;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px; }
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; border-radius:10px; }
.tabs {
position: relative;
min-height: 750px; /* This part sucks */
@ -102,6 +98,8 @@
<section class="browser">Detected Browser:
<div id=brow>...</div></section>
</td>
<td width="64" id="transport">
</td>
</tr>
</table>
@ -213,7 +211,7 @@ run.
<td colspan=2><span class="title">Open and close testing</span></td>
</tr>
<tr>
<td class="explain" colspan=3 style="padding:3">
<td class="explain" colspan=3 style="padding:3px">
To help with open and close testing, you can open and close a connection by hand using
the buttons.<br>
"<b>Close</b>" closes the connection from the browser with code 3000
@ -257,7 +255,7 @@ whenever the information changes server-side.
<td align=center colspan=2><div id=servinfo></div></td>
</tr>
<tr>
<td align=center colspan=2><div id=conninfo style="border : solid 2px #e0d040; padding : 4px; width : 500px; height : 350px; overflow : auto; "></</div></td>
<td align=center colspan=2><div id=conninfo style="border: solid 2px #e0d040; padding: 4px; width: 500px; height:350px; overflow: auto;"></div></td>
</tr>
</table>
</div>
@ -528,6 +526,27 @@ if (location.search) {
}
}
var transport_protocol = "";
if ( performance && performance.timing.nextHopProtocol ) {
transport_protocol = performance.timing.nextHopProtocol;
} else if ( window.chrome && window.chrome.loadTimes ) {
transport_protocol = window.chrome.loadTimes().connectionInfo;
} else {
var p = performance.getEntriesByType("resource");
for (var i=0; i < p.length; i++) {
var value = "nextHopProtocol" in p[i];
if (value)
transport_protocol = p[i].nextHopProtocol;
}
}
console.log("transport protocol " + transport_protocol);
if (transport_protocol == "h2")
document.getElementById("transport").innerHTML = "<img src=\"/http2.png\">";
var mirror_name = "";
if (params.mirror)
mirror_name = params.mirror;