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:
parent
028551271e
commit
904a9c0920
48 changed files with 5511 additions and 2585 deletions
|
@ -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")
|
||||
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 ;\
|
||||
|
|
|
@ -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
|
||||
|
|
32
lib/client.c
32
lib/client.c
|
@ -36,7 +36,7 @@ lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_debug("%s: caching %ld\n", __func__, (long)len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, len);
|
||||
return 0;
|
||||
|
@ -308,7 +308,6 @@ start_ws_handshake:
|
|||
}
|
||||
|
||||
/* send our request to the server */
|
||||
|
||||
lws_latency_pre(context, wsi);
|
||||
|
||||
n = lws_ssl_capable_write(wsi, (unsigned char *)sb, p - sb);
|
||||
|
@ -350,10 +349,12 @@ client_http_body_sent:
|
|||
break;
|
||||
|
||||
case LWSCM_WSCL_WAITING_SERVER_REPLY:
|
||||
|
||||
/* handle server hung up on us */
|
||||
|
||||
if (pollfd->revents & LWS_POLLHUP) {
|
||||
/*
|
||||
* handle server hanging up on us...
|
||||
* but if there is POLLIN waiting, handle that first
|
||||
*/
|
||||
if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) ==
|
||||
LWS_POLLHUP) {
|
||||
|
||||
lwsl_debug("Server connection %p (fd=%d) dead\n",
|
||||
(void *)wsi, pollfd->fd);
|
||||
|
@ -364,18 +365,15 @@ client_http_body_sent:
|
|||
if (!(pollfd->revents & LWS_POLLIN))
|
||||
break;
|
||||
|
||||
/* interpret the server response */
|
||||
|
||||
/*
|
||||
/* interpret the server response
|
||||
*
|
||||
* HTTP/1.1 101 Switching Protocols
|
||||
* Upgrade: websocket
|
||||
* Connection: Upgrade
|
||||
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
|
||||
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
|
||||
* Sec-WebSocket-Protocol: chat
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* we have to take some care here to only take from the
|
||||
* socket bytewise. The browser may (and has been seen to
|
||||
* in the case that onopen() performs websocket traffic)
|
||||
|
@ -409,7 +407,6 @@ client_http_body_sent:
|
|||
* libwebsocket timeout still active here too, so if parsing did
|
||||
* not complete just wait for next packet coming in this state
|
||||
*/
|
||||
|
||||
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
|
||||
break;
|
||||
|
||||
|
@ -418,7 +415,6 @@ client_http_body_sent:
|
|||
* packet traffic already arrived we'll trigger poll() again
|
||||
* right away and deal with it that way
|
||||
*/
|
||||
|
||||
return lws_client_interpret_server_handshake(wsi);
|
||||
|
||||
bail3:
|
||||
|
@ -480,7 +476,7 @@ lws_http_transaction_completed_client(struct lws *wsi)
|
|||
/* otherwise set ourselves up ready to go again */
|
||||
wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
|
||||
wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED;
|
||||
wsi->u.http.content_length = 0;
|
||||
wsi->u.http.rx_content_length = 0;
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* He asked for it to stay alive indefinitely */
|
||||
|
@ -694,12 +690,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
}
|
||||
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
||||
wsi->u.http.content_length =
|
||||
wsi->u.http.rx_content_length =
|
||||
atoll(lws_hdr_simple_ptr(wsi,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH));
|
||||
lwsl_notice("%s: incoming content length %llu\n", __func__,
|
||||
(unsigned long long)wsi->u.http.content_length);
|
||||
wsi->u.http.content_remain = wsi->u.http.content_length;
|
||||
(unsigned long long)wsi->u.http.rx_content_length);
|
||||
wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
|
||||
} else /* can't do 1.1 without a content length or chunked */
|
||||
if (!wsi->chunked)
|
||||
wsi->u.http.connection_type = HTTP_CONNECTION_CLOSE;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
72
lib/header.c
72
lib/header.c
|
@ -107,8 +107,8 @@ int lws_add_http_header_content_length(struct lws *wsi,
|
|||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
(unsigned char *)b, n, p, end))
|
||||
return 1;
|
||||
wsi->u.http.content_length = content_length;
|
||||
wsi->u.http.content_remain = content_length;
|
||||
wsi->u.http.tx_content_length = content_length;
|
||||
wsi->u.http.tx_content_remain = content_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
|
|||
unsigned char *p = pt->serv_buf + LWS_PRE;
|
||||
unsigned char *start = p;
|
||||
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
|
||||
int n = 0, m, len;
|
||||
int n = 0, m = 0, len;
|
||||
char slen[20];
|
||||
|
||||
if (!html_body)
|
||||
|
@ -254,29 +254,65 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
|
|||
return 1;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
{
|
||||
if (wsi->http2_substream) {
|
||||
unsigned char *body = p + 512;
|
||||
|
||||
/*
|
||||
* for HTTP/2, the headers must be sent separately, since they
|
||||
* go out in their own frame. That puts us in a bind that
|
||||
* we won't always be able to get away with two lws_write()s in
|
||||
* sequence, since the first may use up the writability due to
|
||||
* the pipe being choked or SSL_WANT_.
|
||||
*
|
||||
* However we do need to send the human-readable body, and the
|
||||
* END_STREAM.
|
||||
*
|
||||
* Solve it by writing the headers now...
|
||||
*/
|
||||
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
|
||||
if (m != (int)(p - start))
|
||||
return 1;
|
||||
|
||||
len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
/*
|
||||
* ... but stash the body and send it as a priority next
|
||||
* handle_POLLOUT
|
||||
*/
|
||||
|
||||
n = len;
|
||||
m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
|
||||
}
|
||||
#else
|
||||
p += lws_snprintf((char *)p, end - p - 1,
|
||||
"<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
len = sprintf((char *)body,
|
||||
"<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
wsi->u.http.tx_content_length = len;
|
||||
wsi->u.http.tx_content_remain = len;
|
||||
|
||||
n = (int)(p - start);
|
||||
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
|
||||
if (m != n)
|
||||
return 1;
|
||||
wsi->u.h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
|
||||
"pending status body");
|
||||
if (!wsi->u.h2.pending_status_body)
|
||||
return -1;
|
||||
|
||||
strcpy(wsi->u.h2.pending_status_body + LWS_PRE,
|
||||
(const char *)body);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/*
|
||||
* for http/1, we can just append the body after the finalized
|
||||
* headers and send it all in one go.
|
||||
*/
|
||||
p += lws_snprintf((char *)p, end - p - 1,
|
||||
"<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
|
||||
n = (int)(p - start);
|
||||
|
||||
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
|
||||
if (m != n)
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice("%s: return\n", __func__);
|
||||
|
||||
return m != n;
|
||||
}
|
||||
|
@ -313,7 +349,7 @@ lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
|
|||
if (lws_finalize_http_header(wsi, p, end))
|
||||
return -1;
|
||||
|
||||
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS);
|
||||
n = lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
|
1161
lib/hpack.c
1161
lib/hpack.c
File diff suppressed because it is too large
Load diff
1829
lib/http2.c
1829
lib/http2.c
File diff suppressed because it is too large
Load diff
|
@ -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]);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
1483
lib/lextable.h
1483
lib/lextable.h
File diff suppressed because it is too large
Load diff
|
@ -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++) {
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
137
lib/output.c
137
lib/output.c
|
@ -53,7 +53,7 @@ LWS_VISIBLE void lwsl_hexdump(const void *vbuf, size_t len)
|
|||
char line[80];
|
||||
char *p;
|
||||
|
||||
lwsl_parser("\n");
|
||||
lwsl_debug("\n");
|
||||
|
||||
for (n = 0; n < len;) {
|
||||
start = n;
|
||||
|
@ -117,7 +117,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
#else
|
||||
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
|
||||
" It's illegal to do an lws_write outside of\n"
|
||||
" the writable callback: fix your code",
|
||||
" the writable callback: fix your code\n",
|
||||
wsi, (unsigned long)len, dump);
|
||||
#endif
|
||||
assert(0);
|
||||
|
@ -133,7 +133,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
goto handle_truncated_send;
|
||||
}
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd))
|
||||
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
|
||||
lwsl_warn("** error invalid sock but expected to send\n");
|
||||
|
||||
/* limit sending */
|
||||
|
@ -300,9 +300,10 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
if (wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL ||
|
||||
wp == LWS_WRITE_HTTP_HEADERS)
|
||||
if ((wp & 0x1f) == LWS_WRITE_HTTP ||
|
||||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
|
||||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION ||
|
||||
(wp & 0x1f) == LWS_WRITE_HTTP_HEADERS)
|
||||
goto send_raw;
|
||||
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
@ -508,45 +509,61 @@ do_more_inside_frame:
|
|||
}
|
||||
|
||||
send_raw:
|
||||
switch ((int)wp) {
|
||||
switch ((int)(wp & 0x1f)) {
|
||||
case LWS_WRITE_CLOSE:
|
||||
/* lwsl_hexdump(&buf[-pre], len); */
|
||||
case LWS_WRITE_HTTP:
|
||||
case LWS_WRITE_HTTP_FINAL:
|
||||
case LWS_WRITE_HTTP_HEADERS:
|
||||
case LWS_WRITE_HTTP_HEADERS_CONTINUATION:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_PING:
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING) {
|
||||
unsigned char flags = 0;
|
||||
|
||||
n = LWS_HTTP2_FRAME_TYPE_DATA;
|
||||
if (wp == LWS_WRITE_HTTP_HEADERS) {
|
||||
n = LWS_HTTP2_FRAME_TYPE_HEADERS;
|
||||
flags = LWS_HTTP2_FLAG_END_HEADERS;
|
||||
if (wsi->u.http2.send_END_STREAM)
|
||||
flags |= LWS_HTTP2_FLAG_END_STREAM;
|
||||
n = LWS_H2_FRAME_TYPE_DATA;
|
||||
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
|
||||
n = LWS_H2_FRAME_TYPE_HEADERS;
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->u.h2.send_END_STREAM = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->u.http.content_length) {
|
||||
wsi->u.http.content_remain -= len;
|
||||
if ((wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
|
||||
n = LWS_H2_FRAME_TYPE_CONTINUATION;
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
flags = LWS_H2_FLAG_END_HEADERS;
|
||||
if (wsi->u.h2.send_END_STREAM || (wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->u.h2.send_END_STREAM = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (((wp & 0x1f) == LWS_WRITE_HTTP ||
|
||||
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->u.http.tx_content_length) {
|
||||
wsi->u.http.tx_content_remain -= len;
|
||||
lwsl_info("%s: content_remain = %llu\n", __func__,
|
||||
(unsigned long long)wsi->u.http.content_remain);
|
||||
if (!wsi->u.http.content_remain) {
|
||||
(unsigned long long)wsi->u.http.tx_content_remain);
|
||||
if (!wsi->u.http.tx_content_remain) {
|
||||
lwsl_info("%s: selecting final write mode\n", __func__);
|
||||
wp = LWS_WRITE_HTTP_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
|
||||
if ((wp & 0x1f) == LWS_WRITE_HTTP_FINAL || (wp & LWS_WRITE_H2_STREAM_END)) {
|
||||
//lws_get_network_wsi(wsi)->u.h2.END_STREAM) {
|
||||
lwsl_info("%s: setting END_STREAM\n", __func__);
|
||||
flags |= LWS_HTTP2_FLAG_END_STREAM;
|
||||
flags |= LWS_H2_FLAG_END_STREAM;
|
||||
wsi->u.h2.send_END_STREAM = 1;
|
||||
}
|
||||
|
||||
return lws_http2_frame_write(wsi, n, flags,
|
||||
wsi->u.http2.my_stream_id, len, buf);
|
||||
return lws_h2_frame_write(wsi, n, flags,
|
||||
wsi->u.h2.my_sid, len, buf);
|
||||
}
|
||||
#endif
|
||||
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
|
||||
|
@ -600,13 +617,15 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
struct lws_process_html_args args;
|
||||
lws_filepos_t amount, poss;
|
||||
unsigned char *p;
|
||||
unsigned char *p, *pstart;
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
unsigned char finished = 0;
|
||||
#endif
|
||||
int n, m;
|
||||
|
||||
while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) {
|
||||
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
|
||||
|
||||
while (!lws_send_pipe_choked(wsi)) {
|
||||
|
||||
if (wsi->trunc_len) {
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
||||
|
@ -623,7 +642,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
|
||||
n = 0;
|
||||
|
||||
p = pt->serv_buf;
|
||||
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
p = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
|
||||
|
@ -638,7 +659,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
wsi->u.http.filepos = wsi->u.http.range.start;
|
||||
|
||||
if (wsi->u.http.range.count_ranges > 1) {
|
||||
n = lws_snprintf((char *)p, context->pt_serv_buf_size,
|
||||
n = lws_snprintf((char *)p, context->pt_serv_buf_size - LWS_H2_FRAME_HEADER_LENGTH,
|
||||
"_lws\x0d\x0a"
|
||||
"Content-Type: %s\x0d\x0a"
|
||||
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
|
||||
|
@ -656,15 +677,30 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
poss = context->pt_serv_buf_size - n;
|
||||
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
/*
|
||||
* if there is a hint about how much we will do well to send at one time,
|
||||
* restrict ourselves to only trying to send that.
|
||||
*/
|
||||
if (wsi->protocol->tx_packet_size && poss > wsi->protocol->tx_packet_size)
|
||||
if (wsi->protocol->tx_packet_size &&
|
||||
poss > wsi->protocol->tx_packet_size)
|
||||
poss = wsi->protocol->tx_packet_size;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
m = lws_h2_tx_cr_get(wsi);
|
||||
if (!m) {
|
||||
lwsl_info("%s: came here with no tx credit", __func__);
|
||||
return 0;
|
||||
}
|
||||
if (m < poss)
|
||||
poss = m;
|
||||
/*
|
||||
* consumption of the actual payload amount sent will be handled
|
||||
* when the http2 data frame is sent
|
||||
*/
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.count_ranges) {
|
||||
if (wsi->u.http.range.count_ranges > 1)
|
||||
|
@ -686,7 +722,10 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
if (wsi->sending_chunked)
|
||||
n = (int)amount;
|
||||
else
|
||||
n = (p - pt->serv_buf) + (int)amount;
|
||||
n = (p - pstart) + (int)amount;
|
||||
|
||||
lwsl_debug("%s: sending %d\n", __func__, n);
|
||||
|
||||
if (n) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
context->timeout_secs);
|
||||
|
@ -705,14 +744,14 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
n = args.len;
|
||||
p = (unsigned char *)args.p;
|
||||
} else
|
||||
p = pt->serv_buf;
|
||||
p = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.send_ctr + 1 ==
|
||||
wsi->u.http.range.count_ranges && // last range
|
||||
wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
|
||||
wsi->u.http.range.budget - amount == 0) {// final part
|
||||
n += lws_snprintf((char *)pt->serv_buf + n, 6,
|
||||
n += lws_snprintf((char *)pstart + n, 6,
|
||||
"_lws\x0d\x0a"); // append trailing boundary
|
||||
lwsl_debug("added trailing boundary\n");
|
||||
}
|
||||
|
@ -751,8 +790,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
goto file_had_it;
|
||||
}
|
||||
}
|
||||
|
||||
all_sent:
|
||||
if ((!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen)
|
||||
if ((!wsi->trunc_len && wsi->u.http.filepos >= wsi->u.http.filelen)
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
|| finished)
|
||||
#else
|
||||
|
@ -765,13 +805,30 @@ all_sent:
|
|||
|
||||
lwsl_debug("file completed\n");
|
||||
|
||||
if (wsi->protocol->callback)
|
||||
/* ignore callback returned value */
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL, 0) < 0)
|
||||
return -1;
|
||||
if (wsi->protocol->callback &&
|
||||
user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL,
|
||||
0) < 0) {
|
||||
/*
|
||||
* For http/1.x, the choices from
|
||||
* transaction_completed are either
|
||||
* 0 to use the connection for pipelined
|
||||
* or nonzero to hang it up.
|
||||
*
|
||||
* However for http/2. while we are
|
||||
* still interested in hanging up the
|
||||
* nwsi if there was a network-level
|
||||
* fatal error, simply completing the
|
||||
* transaction is a matter of the stream
|
||||
* state, not the root connection at the
|
||||
* network level
|
||||
*/
|
||||
if (wsi->http2_substream)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
|
|
395
lib/parsers.c
395
lib/parsers.c
|
@ -110,6 +110,7 @@ _lws_header_table_reset(struct allocated_headers *ah)
|
|||
{
|
||||
/* init the ah to reflect no headers or data have appeared yet */
|
||||
memset(ah->frag_index, 0, sizeof(ah->frag_index));
|
||||
memset(ah->frags, 0, sizeof(ah->frags));
|
||||
ah->nfrag = 0;
|
||||
ah->pos = 0;
|
||||
ah->http_response = 0;
|
||||
|
@ -440,6 +441,7 @@ int lws_header_table_detach(struct lws *wsi, int autoservice)
|
|||
|
||||
/* he has been stuck waiting for an ah, but now his wait is
|
||||
* over, let him progress */
|
||||
|
||||
_lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
|
||||
}
|
||||
|
||||
|
@ -676,6 +678,7 @@ issue_char(struct lws *wsi, unsigned char c)
|
|||
if (frag_len == wsi->u.hdr.current_token_limit) {
|
||||
if (lws_pos_in_bounds(wsi))
|
||||
return -1;
|
||||
|
||||
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
|
||||
lwsl_warn("header %i exceeds limit %d\n",
|
||||
wsi->u.hdr.parser_state,
|
||||
|
@ -685,21 +688,213 @@ issue_char(struct lws *wsi, unsigned char c)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
|
||||
{
|
||||
struct allocated_headers *ah = wsi->u.hdr.ah;
|
||||
unsigned int enc = 0;
|
||||
uint8_t c = *_c;
|
||||
|
||||
/*
|
||||
* PRIORITY 1
|
||||
* special URI processing... convert %xx
|
||||
*/
|
||||
switch (wsi->u.hdr.ues) {
|
||||
case URIES_IDLE:
|
||||
if (c == '%') {
|
||||
wsi->u.hdr.ues = URIES_SEEN_PERCENT;
|
||||
goto swallow;
|
||||
}
|
||||
break;
|
||||
case URIES_SEEN_PERCENT:
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
wsi->u.hdr.esc_stash = c;
|
||||
wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
|
||||
goto swallow;
|
||||
|
||||
case URIES_SEEN_PERCENT_H1:
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
*_c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
|
||||
char_to_hex(c);
|
||||
c = *_c;
|
||||
enc = 1;
|
||||
wsi->u.hdr.ues = URIES_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRIORITY 2
|
||||
* special URI processing...
|
||||
* convert /.. or /... or /../ etc to /
|
||||
* convert /./ to /
|
||||
* convert // or /// etc to /
|
||||
* leave /.dir or whatever alone
|
||||
*/
|
||||
|
||||
switch (wsi->u.hdr.ups) {
|
||||
case URIPS_IDLE:
|
||||
if (!c)
|
||||
return -1;
|
||||
/* genuine delimiter */
|
||||
if ((c == '&' || c == ';') && !enc) {
|
||||
if (issue_char(wsi, c) < 0)
|
||||
return -1;
|
||||
/* swallow the terminator */
|
||||
ah->frags[ah->nfrag].len--;
|
||||
/* link to next fragment */
|
||||
ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
|
||||
ah->nfrag++;
|
||||
if (ah->nfrag >= ARRAY_SIZE(ah->frags))
|
||||
goto excessive;
|
||||
/* start next fragment after the & */
|
||||
wsi->u.hdr.post_literal_equal = 0;
|
||||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
goto swallow;
|
||||
}
|
||||
/* uriencoded = in the name part, disallow */
|
||||
if (c == '=' && enc &&
|
||||
ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
|
||||
!wsi->u.hdr.post_literal_equal) {
|
||||
c = '_';
|
||||
*_c =c;
|
||||
}
|
||||
|
||||
/* after the real =, we don't care how many = */
|
||||
if (c == '=' && !enc)
|
||||
wsi->u.hdr.post_literal_equal = 1;
|
||||
|
||||
/* + to space */
|
||||
if (c == '+' && !enc) {
|
||||
c = ' ';
|
||||
*_c = c;
|
||||
}
|
||||
/* issue the first / always */
|
||||
if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
break;
|
||||
case URIPS_SEEN_SLASH:
|
||||
/* swallow subsequent slashes */
|
||||
if (c == '/')
|
||||
goto swallow;
|
||||
/* track and swallow the first . after / */
|
||||
if (c == '.') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
|
||||
goto swallow;
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
break;
|
||||
case URIPS_SEEN_SLASH_DOT:
|
||||
/* swallow second . */
|
||||
if (c == '.') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
|
||||
goto swallow;
|
||||
}
|
||||
/* change /./ to / */
|
||||
if (c == '/') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
goto swallow;
|
||||
}
|
||||
/* it was like /.dir ... regurgitate the . */
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case URIPS_SEEN_SLASH_DOT_DOT:
|
||||
|
||||
/* /../ or /..[End of URI] --> backup to last / */
|
||||
if (c == '/' || c == '?') {
|
||||
/*
|
||||
* back up one dir level if possible
|
||||
* safe against header fragmentation because
|
||||
* the method URI can only be in 1 fragment
|
||||
*/
|
||||
if (ah->frags[ah->nfrag].len > 2) {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
do {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
} while (ah->frags[ah->nfrag].len > 1 &&
|
||||
ah->data[ah->pos] != '/');
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
if (ah->frags[ah->nfrag].len > 1)
|
||||
break;
|
||||
goto swallow;
|
||||
}
|
||||
|
||||
/* /..[^/] ... regurgitate and allow */
|
||||
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '?' && !enc &&
|
||||
!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
|
||||
/* seal off uri header */
|
||||
if (issue_char(wsi, '\0') < 0)
|
||||
return -1;
|
||||
|
||||
/* move to using WSI_TOKEN_HTTP_URI_ARGS */
|
||||
ah->nfrag++;
|
||||
if (ah->nfrag >= ARRAY_SIZE(ah->frags))
|
||||
goto excessive;
|
||||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
|
||||
wsi->u.hdr.post_literal_equal = 0;
|
||||
ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
goto swallow;
|
||||
}
|
||||
|
||||
return LPUR_CONTINUE;
|
||||
|
||||
swallow:
|
||||
return LPUR_SWALLOW;
|
||||
|
||||
forbid:
|
||||
return LPUR_FORBID;
|
||||
|
||||
excessive:
|
||||
return LPUR_EXCESSIVE;
|
||||
}
|
||||
|
||||
static const unsigned char methods[] = {
|
||||
WSI_TOKEN_GET_URI,
|
||||
WSI_TOKEN_POST_URI,
|
||||
WSI_TOKEN_OPTIONS_URI,
|
||||
WSI_TOKEN_PUT_URI,
|
||||
WSI_TOKEN_PATCH_URI,
|
||||
WSI_TOKEN_DELETE_URI,
|
||||
WSI_TOKEN_CONNECT,
|
||||
WSI_TOKEN_HEAD_URI,
|
||||
};
|
||||
|
||||
int LWS_WARN_UNUSED_RESULT
|
||||
lws_parse(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
static const unsigned char methods[] = {
|
||||
WSI_TOKEN_GET_URI,
|
||||
WSI_TOKEN_POST_URI,
|
||||
WSI_TOKEN_OPTIONS_URI,
|
||||
WSI_TOKEN_PUT_URI,
|
||||
WSI_TOKEN_PATCH_URI,
|
||||
WSI_TOKEN_DELETE_URI,
|
||||
WSI_TOKEN_CONNECT,
|
||||
};
|
||||
struct allocated_headers *ah = wsi->u.hdr.ah;
|
||||
struct lws_context *context = wsi->context;
|
||||
unsigned int n, m, enc = 0;
|
||||
unsigned int n, m;
|
||||
int r;
|
||||
|
||||
assert(wsi->u.hdr.ah);
|
||||
|
||||
|
@ -753,172 +948,19 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
goto start_fragment;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRIORITY 1
|
||||
* special URI processing... convert %xx
|
||||
*/
|
||||
|
||||
switch (wsi->u.hdr.ues) {
|
||||
case URIES_IDLE:
|
||||
if (c == '%') {
|
||||
wsi->u.hdr.ues = URIES_SEEN_PERCENT;
|
||||
goto swallow;
|
||||
}
|
||||
r = lws_parse_urldecode(wsi, &c);
|
||||
switch (r) {
|
||||
case LPUR_CONTINUE:
|
||||
break;
|
||||
case URIES_SEEN_PERCENT:
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
wsi->u.hdr.esc_stash = c;
|
||||
wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
|
||||
case LPUR_SWALLOW:
|
||||
goto swallow;
|
||||
|
||||
case URIES_SEEN_PERCENT_H1:
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
|
||||
char_to_hex(c);
|
||||
enc = 1;
|
||||
wsi->u.hdr.ues = URIES_IDLE;
|
||||
break;
|
||||
case LPUR_FORBID:
|
||||
goto forbid;
|
||||
case LPUR_EXCESSIVE:
|
||||
goto excessive;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRIORITY 2
|
||||
* special URI processing...
|
||||
* convert /.. or /... or /../ etc to /
|
||||
* convert /./ to /
|
||||
* convert // or /// etc to /
|
||||
* leave /.dir or whatever alone
|
||||
*/
|
||||
|
||||
switch (wsi->u.hdr.ups) {
|
||||
case URIPS_IDLE:
|
||||
if (!c)
|
||||
return -1;
|
||||
/* genuine delimiter */
|
||||
if ((c == '&' || c == ';') && !enc) {
|
||||
if (issue_char(wsi, c) < 0)
|
||||
return -1;
|
||||
/* swallow the terminator */
|
||||
ah->frags[ah->nfrag].len--;
|
||||
/* link to next fragment */
|
||||
ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
|
||||
ah->nfrag++;
|
||||
if (ah->nfrag >= ARRAY_SIZE(ah->frags))
|
||||
goto excessive;
|
||||
/* start next fragment after the & */
|
||||
wsi->u.hdr.post_literal_equal = 0;
|
||||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
goto swallow;
|
||||
}
|
||||
/* uriencoded = in the name part, disallow */
|
||||
if (c == '=' && enc &&
|
||||
ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
|
||||
!wsi->u.hdr.post_literal_equal)
|
||||
c = '_';
|
||||
|
||||
/* after the real =, we don't care how many = */
|
||||
if (c == '=' && !enc)
|
||||
wsi->u.hdr.post_literal_equal = 1;
|
||||
|
||||
/* + to space */
|
||||
if (c == '+' && !enc)
|
||||
c = ' ';
|
||||
/* issue the first / always */
|
||||
if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
break;
|
||||
case URIPS_SEEN_SLASH:
|
||||
/* swallow subsequent slashes */
|
||||
if (c == '/')
|
||||
goto swallow;
|
||||
/* track and swallow the first . after / */
|
||||
if (c == '.') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
|
||||
goto swallow;
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
break;
|
||||
case URIPS_SEEN_SLASH_DOT:
|
||||
/* swallow second . */
|
||||
if (c == '.') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
|
||||
goto swallow;
|
||||
}
|
||||
/* change /./ to / */
|
||||
if (c == '/') {
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
goto swallow;
|
||||
}
|
||||
/* it was like /.dir ... regurgitate the . */
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case URIPS_SEEN_SLASH_DOT_DOT:
|
||||
|
||||
/* /../ or /..[End of URI] --> backup to last / */
|
||||
if (c == '/' || c == '?') {
|
||||
/*
|
||||
* back up one dir level if possible
|
||||
* safe against header fragmentation because
|
||||
* the method URI can only be in 1 fragment
|
||||
*/
|
||||
if (ah->frags[ah->nfrag].len > 2) {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
do {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
} while (ah->frags[ah->nfrag].len > 1 &&
|
||||
ah->data[ah->pos] != '/');
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
if (ah->frags[ah->nfrag].len > 1)
|
||||
break;
|
||||
goto swallow;
|
||||
}
|
||||
|
||||
/* /..[^/] ... regurgitate and allow */
|
||||
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '?' && !enc &&
|
||||
!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
|
||||
/* seal off uri header */
|
||||
if (issue_char(wsi, '\0') < 0)
|
||||
return -1;
|
||||
|
||||
/* move to using WSI_TOKEN_HTTP_URI_ARGS */
|
||||
ah->nfrag++;
|
||||
if (ah->nfrag >= ARRAY_SIZE(ah->frags))
|
||||
goto excessive;
|
||||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
|
||||
wsi->u.hdr.post_literal_equal = 0;
|
||||
ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
goto swallow;
|
||||
}
|
||||
|
||||
check_eol:
|
||||
/* bail at EOL */
|
||||
if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
|
||||
|
@ -946,7 +988,7 @@ swallow:
|
|||
|
||||
/* collecting and checking a name part */
|
||||
case WSI_TOKEN_NAME_PART:
|
||||
lwsl_parser("WSI_TOKEN_NAME_PART '%c' (mode=%d)\n", c, wsi->mode);
|
||||
lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) wsi->u.hdr.lextable_pos=%d\n", c, c, wsi->mode, wsi->u.hdr.lextable_pos);
|
||||
|
||||
wsi->u.hdr.lextable_pos =
|
||||
lextable_decode(wsi->u.hdr.lextable_pos, c);
|
||||
|
@ -954,7 +996,7 @@ swallow:
|
|||
* Server needs to look out for unknown methods...
|
||||
*/
|
||||
if (wsi->u.hdr.lextable_pos < 0 &&
|
||||
wsi->mode == LWSCM_HTTP_SERVING) {
|
||||
(wsi->mode == LWSCM_HTTP_SERVING)) {
|
||||
/* this is not a header we know about */
|
||||
for (m = 0; m < ARRAY_SIZE(methods); m++)
|
||||
if (ah->frag_index[methods[m]]) {
|
||||
|
@ -1041,10 +1083,12 @@ excessive:
|
|||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
ah->frags[ah->nfrag].flags = 2;
|
||||
|
||||
n = ah->frag_index[wsi->u.hdr.parser_state];
|
||||
if (!n) { /* first fragment */
|
||||
ah->frag_index[wsi->u.hdr.parser_state] = ah->nfrag;
|
||||
ah->hdr_token_idx = wsi->u.hdr.parser_state;
|
||||
break;
|
||||
}
|
||||
/* continuation */
|
||||
|
@ -1101,6 +1145,7 @@ set_parsing_complete:
|
|||
forbid:
|
||||
lwsl_notice(" forbidding on uri sanitation\n");
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
27
lib/pollfd.c
27
lib/pollfd.c
|
@ -385,35 +385,38 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
if (wsi->mode != LWSCM_HTTP2_SERVING)
|
||||
goto network_sock;
|
||||
|
||||
if (wsi->u.http2.requested_POLLOUT) {
|
||||
if (wsi->u.h2.requested_POLLOUT) {
|
||||
lwsl_info("already pending writable\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wsi->u.http2.tx_credit <= 0) {
|
||||
/* is this for DATA or for control messages? */
|
||||
if (wsi->upgraded_to_http2 && !wsi->u.h2.h2n->pps &&
|
||||
!lws_h2_tx_cr_get(wsi)) {
|
||||
/*
|
||||
* other side is not able to cope with us sending
|
||||
* anything so no matter if we have POLLOUT on our side.
|
||||
* other side is not able to cope with us sending DATA
|
||||
* anything so no matter if we have POLLOUT on our side if it's
|
||||
* DATA we want to send.
|
||||
*
|
||||
* Delay waiting for our POLLOUT until peer indicates he has
|
||||
* space for more using tx window command in http2 layer
|
||||
*/
|
||||
lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi,
|
||||
wsi->u.http2.tx_credit);
|
||||
wsi->u.http2.waiting_tx_credit = 1;
|
||||
lwsl_notice("%s: %p: skint (%d)\n", __func__, wsi, wsi->u.h2.tx_cr);
|
||||
wsi->u.h2.skint = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
network_wsi = lws_http2_get_network_wsi(wsi);
|
||||
already = network_wsi->u.http2.requested_POLLOUT;
|
||||
wsi->u.h2.skint = 0;
|
||||
network_wsi = lws_get_network_wsi(wsi);
|
||||
already = network_wsi->u.h2.requested_POLLOUT;
|
||||
|
||||
/* mark everybody above him as requesting pollout */
|
||||
|
||||
wsi2 = wsi;
|
||||
while (wsi2) {
|
||||
wsi2->u.http2.requested_POLLOUT = 1;
|
||||
wsi2->u.h2.requested_POLLOUT = 1;
|
||||
lwsl_info("mark %p pending writable\n", wsi2);
|
||||
wsi2 = wsi2->u.http2.parent_wsi;
|
||||
wsi2 = wsi2->u.h2.parent_wsi;
|
||||
}
|
||||
|
||||
/* for network action, act only on the network wsi */
|
||||
|
@ -428,7 +431,7 @@ network_sock:
|
|||
return 1;
|
||||
|
||||
if (wsi->position_in_fds_table < 0) {
|
||||
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
|
||||
lwsl_debug("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
192
lib/server.c
192
lib/server.c
|
@ -22,14 +22,12 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
#if defined(_DEBUG) || defined(LWS_WITH_ACCESS_LOG)
|
||||
static const char * const method_names[] = {
|
||||
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT",
|
||||
static const char * const method_names[] = {
|
||||
"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
":path",
|
||||
":path",
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined (LWS_WITH_ESP8266)
|
||||
#undef memcpy
|
||||
|
@ -51,10 +49,12 @@ lws_context_init_server(struct lws_context_creation_info *info,
|
|||
struct lws *wsi;
|
||||
int m = 0;
|
||||
|
||||
(void)method_names;
|
||||
(void)opt;
|
||||
/* set up our external listening socket we serve on */
|
||||
|
||||
if (info->port == CONTEXT_PORT_NO_LISTEN || info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
|
||||
if (info->port == CONTEXT_PORT_NO_LISTEN ||
|
||||
info->port == CONTEXT_PORT_NO_LISTEN_SERVER)
|
||||
return 0;
|
||||
|
||||
vh = vhost->context->vhost_list;
|
||||
|
@ -513,7 +513,8 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
|||
return -1;
|
||||
|
||||
n = lws_write(wsi, start, p - start,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
LWS_WRITE_HTTP_HEADERS |
|
||||
LWS_WRITE_H2_STREAM_END);
|
||||
if (n != (p - start)) {
|
||||
lwsl_err("_write returned %d from %ld\n", n,
|
||||
(long)(p - start));
|
||||
|
@ -603,6 +604,8 @@ lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
|
|||
if (hm->origin_protocol == LWSMPRO_CALLBACK ||
|
||||
((hm->origin_protocol == LWSMPRO_CGI ||
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
|
||||
(wsi->http2_substream &&
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)) ||
|
||||
hm->protocol) &&
|
||||
hm->mountpoint_len > best)) {
|
||||
best = hm->mountpoint_len;
|
||||
|
@ -684,7 +687,7 @@ lws_unauthorised_basic_auth(struct lws *wsi)
|
|||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return -1;
|
||||
|
||||
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
|
||||
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -732,12 +735,12 @@ int lws_clean_url(char *p)
|
|||
*
|
||||
*/
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
static const char * const hver[] = {
|
||||
"http/1.0", "http/1.1", "http/2"
|
||||
};
|
||||
static void
|
||||
lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
|
||||
{
|
||||
static const char * const hver[] = {
|
||||
"http/1.0", "http/1.1", "http/2"
|
||||
};
|
||||
#ifdef LWS_WITH_IPV6
|
||||
char ads[INET6_ADDRSTRLEN];
|
||||
#else
|
||||
|
@ -765,7 +768,12 @@ lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int meth)
|
|||
if (!pa)
|
||||
pa = "(unknown)";
|
||||
|
||||
me = method_names[meth];
|
||||
if (wsi->http2_substream)
|
||||
me = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
|
||||
else
|
||||
me = method_names[meth];
|
||||
if (!me)
|
||||
me = "(null)";
|
||||
|
||||
lws_snprintf(wsi->access_log.header_log, l,
|
||||
"%s - - [%s] \"%s %s %s\"",
|
||||
|
@ -817,6 +825,7 @@ static const unsigned char methods[] = {
|
|||
WSI_TOKEN_PATCH_URI,
|
||||
WSI_TOKEN_DELETE_URI,
|
||||
WSI_TOKEN_CONNECT,
|
||||
WSI_TOKEN_HEAD_URI,
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
WSI_TOKEN_HTTP_COLON_PATH,
|
||||
#endif
|
||||
|
@ -835,7 +844,9 @@ lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (count != 1) {
|
||||
if (count != 1 &&
|
||||
!(wsi->http2_substream &&
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
|
||||
lwsl_warn("multiple methods?\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -870,7 +881,7 @@ lws_http_action(struct lws *wsi)
|
|||
};
|
||||
|
||||
meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
|
||||
if (meth < 0)
|
||||
if (meth < 0 || meth >= ARRAY_SIZE(method_names))
|
||||
goto bail_nuke_ah;
|
||||
|
||||
/* we insist on absolute paths */
|
||||
|
@ -881,24 +892,24 @@ lws_http_action(struct lws *wsi)
|
|||
goto bail_nuke_ah;
|
||||
}
|
||||
|
||||
lwsl_info("Method: %s request for '%s'\n", method_names[meth], uri_ptr);
|
||||
lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth], meth, uri_ptr);
|
||||
|
||||
if (lws_ensure_user_space(wsi))
|
||||
goto bail_nuke_ah;
|
||||
|
||||
/* HTTP header had a content length? */
|
||||
|
||||
wsi->u.http.content_length = 0;
|
||||
wsi->u.http.rx_content_length = 0;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
|
||||
wsi->u.http.content_length = 100 * 1024 * 1024;
|
||||
wsi->u.http.rx_content_length = 100 * 1024 * 1024;
|
||||
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
||||
lws_hdr_copy(wsi, content_length_str,
|
||||
sizeof(content_length_str) - 1,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH);
|
||||
wsi->u.http.content_length = atoll(content_length_str);
|
||||
wsi->u.http.rx_content_length = atoll(content_length_str);
|
||||
}
|
||||
|
||||
if (wsi->http2_substream) {
|
||||
|
@ -1026,20 +1037,26 @@ lws_http_action(struct lws *wsi)
|
|||
|
||||
lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
|
||||
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
|
||||
goto bail_nuke_ah;
|
||||
|
||||
/* > at start indicates deal with by redirect */
|
||||
if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
||||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
|
||||
n = lws_snprintf((char *)end, 256, "%s%s",
|
||||
oprot[hit->origin_protocol & 1],
|
||||
hit->origin);
|
||||
else
|
||||
n = lws_snprintf((char *)end, 256,
|
||||
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
|
||||
uri_ptr);
|
||||
else {
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY))
|
||||
goto bail_nuke_ah;
|
||||
n = lws_snprintf((char *)end, 256,
|
||||
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY),
|
||||
uri_ptr);
|
||||
} else
|
||||
n = lws_snprintf((char *)end, 256,
|
||||
"%s%s%s/", oprot[!!lws_is_ssl(wsi)],
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
|
||||
uri_ptr);
|
||||
}
|
||||
|
||||
lws_clean_url((char *)end);
|
||||
n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
|
||||
|
@ -1300,10 +1317,15 @@ deal_body:
|
|||
* In any case, return 0 and let lws_read decide how to
|
||||
* proceed based on state
|
||||
*/
|
||||
if (wsi->state != LWSS_HTTP_ISSUING_FILE)
|
||||
if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
|
||||
/* Prepare to read body if we have a content length: */
|
||||
if (wsi->u.http.content_length > 0)
|
||||
lwsl_debug("wsi->u.http.rx_content_length %lld %d %d\n", (long long)wsi->u.http.rx_content_length, wsi->upgraded_to_http2, wsi->http2_substream);
|
||||
if (wsi->u.http.rx_content_length > 0) {
|
||||
lwsl_notice("%s: %p: LWSS_HTTP_BODY state set\n", __func__, wsi);
|
||||
wsi->state = LWSS_HTTP_BODY;
|
||||
wsi->u.http.rx_content_remain = wsi->u.http.rx_content_length;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -1397,10 +1419,13 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
assert(0);
|
||||
}
|
||||
|
||||
lwsl_hexdump(*buf, len);
|
||||
|
||||
while (len--) {
|
||||
wsi->more_rx_waiting = !!len;
|
||||
|
||||
if (wsi->mode != LWSCM_HTTP_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP2_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
|
||||
lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
|
||||
goto bail_nuke_ah;
|
||||
|
@ -1461,10 +1486,12 @@ raw_transition:
|
|||
} else
|
||||
lwsl_info("no host\n");
|
||||
|
||||
wsi->vhost->conn_stats.trans++;
|
||||
if (!wsi->conn_stat_done) {
|
||||
wsi->vhost->conn_stats.conn++;
|
||||
wsi->conn_stat_done = 1;
|
||||
if (wsi->mode != LWSCM_HTTP2_SERVING) {
|
||||
wsi->vhost->conn_stats.h1_trans++;
|
||||
if (!wsi->conn_stat_done) {
|
||||
wsi->vhost->conn_stats.h1_conn++;
|
||||
wsi->conn_stat_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for unwelcome guests */
|
||||
|
@ -1533,7 +1560,7 @@ raw_transition:
|
|||
#ifdef LWS_WITH_HTTP2
|
||||
if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
|
||||
"h2c")) {
|
||||
wsi->vhost->conn_stats.http2_upg++;
|
||||
wsi->vhost->conn_stats.h2_upg++;
|
||||
lwsl_info("Upgrade to h2c\n");
|
||||
goto upgrade_h2c;
|
||||
}
|
||||
|
@ -1588,18 +1615,24 @@ upgrade_h2c:
|
|||
/* http2 union member has http union struct at start */
|
||||
wsi->u.http.ah = ah;
|
||||
|
||||
lws_http2_init(&wsi->u.http2.peer_settings);
|
||||
lws_http2_init(&wsi->u.http2.my_settings);
|
||||
if (!wsi->u.h2.h2n) {
|
||||
wsi->u.h2.h2n = lws_zalloc(sizeof(*wsi->u.h2.h2n), "h2n");
|
||||
if (!wsi->u.h2.h2n)
|
||||
return 1;
|
||||
}
|
||||
|
||||
lws_h2_init(wsi);
|
||||
|
||||
/* HTTP2 union */
|
||||
|
||||
lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
|
||||
lws_h2_settings(wsi, &wsi->u.h2.h2n->set,
|
||||
(unsigned char *)protocol_list, n);
|
||||
|
||||
strcpy(protocol_list,
|
||||
"HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
||||
"Connection: Upgrade\x0d\x0a"
|
||||
"Upgrade: h2c\x0d\x0a\x0d\x0a");
|
||||
lws_hpack_dynamic_size(wsi, wsi->u.h2.h2n->set.s[H2SET_HEADER_TABLE_SIZE]);
|
||||
|
||||
strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
||||
"Connection: Upgrade\x0d\x0a"
|
||||
"Upgrade: h2c\x0d\x0a\x0d\x0a");
|
||||
n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
|
||||
strlen(protocol_list));
|
||||
if (n != strlen(protocol_list)) {
|
||||
|
@ -2053,8 +2086,8 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
|
|||
}
|
||||
|
||||
new_wsi->tsi = n;
|
||||
lwsl_debug("Accepted wsi %p to context %p, tsi %d\n", new_wsi,
|
||||
vhost->context, new_wsi->tsi);
|
||||
lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi,
|
||||
vhost->name, new_wsi->tsi);
|
||||
|
||||
new_wsi->vhost = vhost;
|
||||
new_wsi->context = vhost->context;
|
||||
|
@ -2073,7 +2106,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
|
|||
|
||||
/*
|
||||
* these can only be set once the protocol is known
|
||||
* we set an unestablished connection's protocol pointer
|
||||
* we set an un-established connection's protocol pointer
|
||||
* to the start of the supported list, so it can look
|
||||
* for matching ones during the handshake
|
||||
*/
|
||||
|
@ -2100,6 +2133,8 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
{
|
||||
int n = NO_PENDING_TIMEOUT;
|
||||
|
||||
lwsl_info("%s: wsi %p\n", __func__, wsi);
|
||||
|
||||
lws_access_log(wsi);
|
||||
|
||||
if (!wsi->hdr_parsing_completed) {
|
||||
|
@ -2109,6 +2144,12 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
|
||||
lwsl_debug("%s: wsi %p\n", __func__, wsi);
|
||||
/* if we can't go back to accept new headers, drop the connection */
|
||||
if (wsi->http2_substream)
|
||||
return 0;
|
||||
|
||||
if (wsi->seen_zero_length_recv)
|
||||
return 1;
|
||||
|
||||
if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
|
||||
lwsl_info("%s: %p: close connection\n", __func__, wsi);
|
||||
return 1;
|
||||
|
@ -2120,8 +2161,8 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
/* otherwise set ourselves up ready to go again */
|
||||
wsi->state = LWSS_HTTP;
|
||||
wsi->mode = LWSCM_HTTP_SERVING;
|
||||
wsi->u.http.content_length = 0;
|
||||
wsi->u.http.content_remain = 0;
|
||||
wsi->u.http.tx_content_length = 0;
|
||||
wsi->u.http.tx_content_remain = 0;
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
wsi->access_log.sent = 0;
|
||||
|
@ -2144,7 +2185,7 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
* reset the existing header table and keep it.
|
||||
*/
|
||||
if (wsi->u.hdr.ah) {
|
||||
lwsl_info("%s: wsi->more_rx_waiting=%d\n", __func__,
|
||||
lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
|
||||
wsi->more_rx_waiting);
|
||||
|
||||
if (!wsi->more_rx_waiting) {
|
||||
|
@ -2426,7 +2467,7 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len)
|
|||
ah = wsi->u.hdr.ah;
|
||||
memcpy(ah->rx, readbuf, len);
|
||||
ah->rxpos = 0;
|
||||
ah->rxlen = len;
|
||||
ah->rxlen = (int16_t)len;
|
||||
|
||||
lwsl_notice("%s: calling service on readbuf ah\n", __func__);
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
@ -2539,6 +2580,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
#if !defined(LWS_WITH_ESP8266)
|
||||
if (wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
|
||||
lwsl_notice("favouring pollout\n");
|
||||
wsi->favoured_pollin = 0;
|
||||
goto try_pollout;
|
||||
}
|
||||
|
@ -2565,7 +2607,10 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
ah->rxpos = 0;
|
||||
switch (ah->rxlen) {
|
||||
case 0:
|
||||
lwsl_info("%s: read 0 len\n", __func__);
|
||||
lwsl_info("%s: read 0 len a\n", __func__);
|
||||
wsi->seen_zero_length_recv = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
goto try_pollout;
|
||||
/* fallthru */
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
goto fail;
|
||||
|
@ -2592,25 +2637,31 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
/* just ignore incoming if waiting for close */
|
||||
if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
|
||||
wsi->state != LWSS_HTTP_ISSUING_FILE) {
|
||||
/*
|
||||
* otherwise give it to whoever wants it
|
||||
* according to the connection state
|
||||
*/
|
||||
|
||||
n = lws_read(wsi, ah->rx + ah->rxpos,
|
||||
ah->rxlen - ah->rxpos);
|
||||
if (n < 0) /* we closed wsi */
|
||||
return 1;
|
||||
if (wsi->u.hdr.ah) {
|
||||
if ( wsi->u.hdr.ah->rxlen)
|
||||
wsi->u.hdr.ah->rxpos += n;
|
||||
|
||||
lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
|
||||
__func__, wsi,
|
||||
wsi->u.hdr.ah->rxpos,
|
||||
wsi->u.hdr.ah->rxlen);
|
||||
if (!wsi->u.hdr.ah)
|
||||
break;
|
||||
if ( wsi->u.hdr.ah->rxlen)
|
||||
wsi->u.hdr.ah->rxpos += n;
|
||||
|
||||
lwsl_debug("%s: wsi %p: ah read rxpos %d, rxlen %d\n",
|
||||
__func__, wsi, wsi->u.hdr.ah->rxpos,
|
||||
wsi->u.hdr.ah->rxlen);
|
||||
|
||||
if (lws_header_table_is_in_detachable_state(wsi) &&
|
||||
(wsi->mode != LWSCM_HTTP_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
|
||||
wsi->mode != LWSCM_HTTP2_SERVING))
|
||||
lws_header_table_detach(wsi, 1);
|
||||
|
||||
if (lws_header_table_is_in_detachable_state(wsi) &&
|
||||
(wsi->mode != LWSCM_HTTP_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
|
||||
wsi->mode != LWSCM_HTTP2_SERVING))
|
||||
lws_header_table_detach(wsi, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2622,7 +2673,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
|
||||
switch (len) {
|
||||
case 0:
|
||||
lwsl_info("%s: read 0 len\n", __func__);
|
||||
lwsl_info("%s: read 0 len b\n", __func__);
|
||||
|
||||
/* fallthru */
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
|
@ -2661,6 +2712,13 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
|
|||
wsi->favoured_pollin = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* he may have used up the
|
||||
* writability above, if we will defer POLLOUT
|
||||
* processing in favour of POLLIN, note it
|
||||
*/
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->favoured_pollin = 1;
|
||||
|
||||
try_pollout:
|
||||
|
||||
|
@ -3050,7 +3108,9 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|||
wsi->u.http.filepos = 0;
|
||||
wsi->state = LWSS_HTTP_ISSUING_FILE;
|
||||
|
||||
return lws_serve_http_file_fragment(wsi);
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -3069,7 +3129,7 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
if (wsi->rxflow_bitmap) {
|
||||
lws_rxflow_cache(wsi, *buf, 0, len);
|
||||
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
|
||||
return 1;
|
||||
|
@ -3083,8 +3143,10 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
if (wsi->rxflow_buffer) {
|
||||
wsi->rxflow_pos++;
|
||||
assert(wsi->rxflow_pos <= wsi->rxflow_len);
|
||||
}
|
||||
|
||||
/* consume payload bytes efficiently */
|
||||
if (
|
||||
|
|
298
lib/service.c
298
lib/service.c
|
@ -73,7 +73,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
int write_type = LWS_WRITE_PONG;
|
||||
struct lws_tokens eff_buf;
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
struct lws *wsi2;
|
||||
struct lws **wsi2, *wsi2a;
|
||||
#endif
|
||||
int ret, m, n;
|
||||
|
||||
|
@ -95,6 +95,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
* corrupted.
|
||||
*/
|
||||
if (wsi->trunc_len) {
|
||||
//lwsl_notice("%s: completing partial\n", __func__);
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0) {
|
||||
lwsl_info("%s signalling to close\n", __func__);
|
||||
|
@ -115,18 +116,18 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
/*
|
||||
* Priority 2: protocol packets
|
||||
*/
|
||||
if (wsi->pps) {
|
||||
lwsl_info("servicing pps %d\n", wsi->pps);
|
||||
switch (wsi->pps) {
|
||||
case LWS_PPS_HTTP2_MY_SETTINGS:
|
||||
case LWS_PPS_HTTP2_ACK_SETTINGS:
|
||||
lws_http2_do_pps_send(lws_get_context(wsi), wsi);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (wsi->upgraded_to_http2 && wsi->u.h2.h2n->pps) {
|
||||
lwsl_info("servicing pps\n");
|
||||
if (lws_h2_do_pps_send(wsi)) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
goto bail_die;
|
||||
}
|
||||
wsi->pps = LWS_PPS_NONE;
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
if (wsi->u.h2.h2n->pps)
|
||||
goto bail_ok;
|
||||
|
||||
/* we can resume whatever we were doing */
|
||||
lws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE |
|
||||
LWS_RXFLOW_REASON_H2_PPS_PENDING);
|
||||
|
||||
goto bail_ok; /* leave POLLOUT active */
|
||||
}
|
||||
|
@ -385,33 +386,146 @@ user_service_go_again:
|
|||
goto notify;
|
||||
}
|
||||
|
||||
wsi->u.http2.requested_POLLOUT = 0;
|
||||
if (!wsi->u.http2.initialized) {
|
||||
wsi->u.h2.requested_POLLOUT = 0;
|
||||
if (!wsi->u.h2.initialized) {
|
||||
lwsl_info("pollout on uninitialized http2 conn\n");
|
||||
goto bail_ok;
|
||||
}
|
||||
|
||||
lwsl_info("%s: doing children\n", __func__);
|
||||
// if (SSL_want_read(wsi->ssl) || SSL_want_write(wsi->ssl)) {
|
||||
// lws_callback_on_writable(wsi);
|
||||
// goto bail_ok;
|
||||
// }
|
||||
|
||||
lwsl_info("%s: %p: children waiting for POLLOUT service:\n", __func__, wsi);
|
||||
wsi2a = wsi->u.h2.child_list;
|
||||
while (wsi2a) {
|
||||
if (wsi2a->u.h2.requested_POLLOUT)
|
||||
lwsl_debug(" * %p\n", wsi2a);
|
||||
else
|
||||
lwsl_debug(" %p\n", wsi2a);
|
||||
|
||||
wsi2a = wsi2a->u.h2.sibling_list;
|
||||
}
|
||||
|
||||
wsi2 = &wsi->u.h2.child_list;
|
||||
if (!*wsi2)
|
||||
goto bail_ok;
|
||||
|
||||
wsi2 = wsi;
|
||||
do {
|
||||
wsi2 = wsi2->u.http2.next_child_wsi;
|
||||
lwsl_info("%s: child %p\n", __func__, wsi2);
|
||||
if (!wsi2)
|
||||
continue;
|
||||
if (!wsi2->u.http2.requested_POLLOUT)
|
||||
continue;
|
||||
wsi2->u.http2.requested_POLLOUT = 0;
|
||||
if (lws_calllback_as_writeable(wsi2)) {
|
||||
lwsl_debug("Closing POLLOUT child\n");
|
||||
lws_close_free_wsi(wsi2, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
struct lws *w, **wa;
|
||||
|
||||
wa = &(*wsi2)->u.h2.sibling_list;
|
||||
if (!(*wsi2)->u.h2.requested_POLLOUT) {
|
||||
lwsl_debug(" child %p doesn't want POLLOUT\n", *wsi2);
|
||||
goto next_child;
|
||||
}
|
||||
wsi2 = wsi;
|
||||
} while (wsi2 != NULL && !lws_send_pipe_choked(wsi));
|
||||
|
||||
lwsl_info("%s: completed\n", __func__);
|
||||
/*
|
||||
* we're going to do writable callback for this child.
|
||||
* move him to be the last child
|
||||
*/
|
||||
|
||||
lwsl_debug("servicing child %p\n", *wsi2);
|
||||
|
||||
w = *wsi2;
|
||||
while (w) {
|
||||
if (!w->u.h2.sibling_list) { /* w is the current last */
|
||||
lwsl_debug("w=%p, *wsi2 = %p\n", w, *wsi2);
|
||||
if (w == *wsi2) /* we are already last */
|
||||
break;
|
||||
w->u.h2.sibling_list = *wsi2; /* last points to us as new last */
|
||||
*wsi2 = (*wsi2)->u.h2.sibling_list; /* guy pointing to us until now points to our old next */
|
||||
w->u.h2.sibling_list->u.h2.sibling_list = NULL; /* we point to nothing because we are last */
|
||||
w = w->u.h2.sibling_list; /* w becomes us */
|
||||
break;
|
||||
}
|
||||
w = w->u.h2.sibling_list;
|
||||
}
|
||||
|
||||
w->u.h2.requested_POLLOUT = 0;
|
||||
lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2), (*wsi2)->state);
|
||||
|
||||
if (w->u.h2.pending_status_body) {
|
||||
w->u.h2.send_END_STREAM = 1;
|
||||
n = lws_write(w,
|
||||
(uint8_t *)w->u.h2.pending_status_body + LWS_PRE,
|
||||
strlen(w->u.h2.pending_status_body + LWS_PRE),
|
||||
LWS_WRITE_HTTP_FINAL);
|
||||
lws_free_set_NULL(w->u.h2.pending_status_body);
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
wa = &wsi->u.h2.child_list;
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (w->state == LWSS_HTTP_ISSUING_FILE) {
|
||||
|
||||
w->leave_pollout_active = 0;
|
||||
|
||||
/* >0 == completion, <0 == error
|
||||
*
|
||||
* We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
|
||||
* it's done. That's the case even if we just completed the
|
||||
* send, so wait for that.
|
||||
*/
|
||||
n = lws_serve_http_file_fragment(w);
|
||||
lwsl_debug("lws_serve_http_file_fragment says %d\n", n);
|
||||
|
||||
/*
|
||||
* We will often hear about out having sent the final
|
||||
* DATA here... if so close the actual wsi
|
||||
*/
|
||||
if (n < 0 || w->u.h2.send_END_STREAM) {
|
||||
lwsl_debug("Closing POLLOUT child %p\n", w);
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
wa = &wsi->u.h2.child_list;
|
||||
goto next_child;
|
||||
}
|
||||
if (n > 0)
|
||||
if (lws_http_transaction_completed(w))
|
||||
goto bail_die;
|
||||
if (!n) {
|
||||
lws_callback_on_writable(w);
|
||||
(w)->u.h2.requested_POLLOUT = 1;
|
||||
}
|
||||
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (lws_calllback_as_writeable(w) || w->u.h2.send_END_STREAM) {
|
||||
lwsl_debug("Closing POLLOUT child\n");
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
wa = &wsi->u.h2.child_list;
|
||||
}
|
||||
|
||||
next_child:
|
||||
wsi2 = wa;
|
||||
} while (wsi2 && *wsi2 && !lws_send_pipe_choked(wsi));
|
||||
|
||||
lwsl_info("%s: %p: children waiting for POLLOUT service: %p\n", __func__, wsi, wsi->u.h2.child_list);
|
||||
wsi2a = wsi->u.h2.child_list;
|
||||
while (wsi2a) {
|
||||
if (wsi2a->u.h2.requested_POLLOUT)
|
||||
lwsl_debug(" * %p\n", wsi2a);
|
||||
else
|
||||
lwsl_debug(" %p\n", wsi2a);
|
||||
|
||||
wsi2a = wsi2a->u.h2.sibling_list;
|
||||
}
|
||||
|
||||
|
||||
wsi2a = wsi->u.h2.child_list;
|
||||
while (wsi2a) {
|
||||
if (wsi2a->u.h2.requested_POLLOUT) {
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
break;
|
||||
}
|
||||
wsi2a = wsi2a->u.h2.sibling_list;
|
||||
}
|
||||
|
||||
goto bail_ok;
|
||||
|
||||
|
||||
notify:
|
||||
#endif
|
||||
wsi->handling_pollout = 0;
|
||||
|
@ -502,15 +616,42 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
|
|||
|
||||
int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
|
||||
{
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->upgraded_to_http2) {
|
||||
struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
|
||||
|
||||
assert(h2n->rx_scratch);
|
||||
buf += n;
|
||||
len -= n;
|
||||
assert ((char *)buf >= (char *)h2n->rx_scratch &&
|
||||
(char *)&buf[len] <= (char *)&h2n->rx_scratch[LWS_H2_RX_SCRATCH_SIZE]);
|
||||
|
||||
h2n->rx_scratch_pos = ((char *)buf - (char *)h2n->rx_scratch);
|
||||
h2n->rx_scratch_len = len;
|
||||
|
||||
lwsl_info("%s: %p: pausing h2 rx_scratch\n", __func__, wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* his RX is flowcontrolled, don't send remaining now */
|
||||
if (wsi->rxflow_buffer) {
|
||||
/* rxflow while we were spilling prev rxflow */
|
||||
lwsl_info("stalling in existing rxflow buf\n");
|
||||
return 1;
|
||||
if (buf >= wsi->rxflow_buffer &&
|
||||
&buf[len - 1] < &wsi->rxflow_buffer[wsi->rxflow_len]) {
|
||||
/* rxflow while we were spilling prev rxflow */
|
||||
lwsl_info("%s: staying in rxflow buf\n", __func__);
|
||||
return 1;
|
||||
} else {
|
||||
lwsl_err("%s: conflicting rxflow buf, "
|
||||
"current %p len %d, new %p len %d\n", __func__,
|
||||
wsi->rxflow_buffer, wsi->rxflow_len, buf, len);
|
||||
assert(0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* a new rxflow, buffer it and warn caller */
|
||||
lwsl_info("new rxflow input buffer len %d\n", len - n);
|
||||
lwsl_info("%s: new rxflow input buffer len %d\n", __func__, len - n);
|
||||
wsi->rxflow_buffer = lws_malloc(len - n, "rxflow buf");
|
||||
if (!wsi->rxflow_buffer)
|
||||
return -1;
|
||||
|
@ -735,9 +876,9 @@ spin_chunks:
|
|||
if (wsi->chunked && !wsi->chunk_remaining)
|
||||
return 0;
|
||||
|
||||
if (wsi->u.http.content_remain &&
|
||||
wsi->u.http.content_remain < *len)
|
||||
n = (int)wsi->u.http.content_remain;
|
||||
if (wsi->u.http.rx_content_remain &&
|
||||
wsi->u.http.rx_content_remain < *len)
|
||||
n = (int)wsi->u.http.rx_content_remain;
|
||||
else
|
||||
n = *len;
|
||||
|
||||
|
@ -775,10 +916,10 @@ spin_chunks:
|
|||
return 0;
|
||||
|
||||
/* if we know the content length, decrement the content remaining */
|
||||
if (wsi->u.http.content_length > 0)
|
||||
wsi->u.http.content_remain -= n;
|
||||
if (wsi->u.http.rx_content_length > 0)
|
||||
wsi->u.http.rx_content_remain -= n;
|
||||
|
||||
if (wsi->u.http.content_remain || !wsi->u.http.content_length)
|
||||
if (wsi->u.http.rx_content_remain || !wsi->u.http.rx_content_length)
|
||||
return 0;
|
||||
|
||||
completed:
|
||||
|
@ -1071,7 +1212,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
|
||||
default:
|
||||
n = SSL_get_error(wsi->ssl, n);
|
||||
if (n != SSL_ERROR_SYSCALL) {
|
||||
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("(wants read)\n");
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLIN);
|
||||
|
@ -1194,12 +1335,23 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
*/
|
||||
break;
|
||||
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
|
||||
if (lws_is_flowcontrolled(wsi))
|
||||
/* We cannot deal with any kind of new RX
|
||||
* because we are RX-flowcontrolled.
|
||||
*/
|
||||
break;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
wsi1 = lws_get_network_wsi(wsi);
|
||||
if (wsi1 && wsi1->trunc_len)
|
||||
/* We cannot deal with any kind of new RX
|
||||
* because we are dealing with a partial send
|
||||
* (new RX may trigger new http_action() that expect
|
||||
* to be able to send)
|
||||
*/
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* 2: RX Extension needs to be drained
|
||||
*/
|
||||
|
||||
|
@ -1228,13 +1380,14 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
*/
|
||||
break;
|
||||
|
||||
/* 3: RX Flowcontrol buffer needs to be drained
|
||||
/* 3: RX Flowcontrol buffer / h2 rx scratch needs to be drained
|
||||
*/
|
||||
|
||||
if (wsi->rxflow_buffer) {
|
||||
lwsl_info("draining rxflow (len %d)\n",
|
||||
wsi->rxflow_len - wsi->rxflow_pos
|
||||
);
|
||||
assert(wsi->rxflow_pos < wsi->rxflow_len);
|
||||
/* well, drain it */
|
||||
eff_buf.token = (char *)wsi->rxflow_buffer +
|
||||
wsi->rxflow_pos;
|
||||
|
@ -1243,14 +1396,36 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
goto drain;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->upgraded_to_http2) {
|
||||
struct lws_h2_netconn *h2n = wsi->u.h2.h2n;
|
||||
|
||||
if (h2n->rx_scratch_len) {
|
||||
lwsl_info("%s: %p: resuming h2 rx_scratch pos = %d len = %d\n",
|
||||
__func__, wsi, h2n->rx_scratch_pos, h2n->rx_scratch_len);
|
||||
eff_buf.token = (char *)h2n->rx_scratch +
|
||||
h2n->rx_scratch_pos;
|
||||
eff_buf.token_len = h2n->rx_scratch_len;
|
||||
|
||||
h2n->rx_scratch_len = 0;
|
||||
goto drain;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 4: any incoming (or ah-stashed incoming rx) data ready?
|
||||
* notice if rx flow going off raced poll(), rx flow wins
|
||||
*/
|
||||
|
||||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
break;
|
||||
|
||||
read:
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
|
||||
__func__, wsi, wsi->rxflow_bitmap);
|
||||
break;
|
||||
}
|
||||
|
||||
/* all the union members start with hdr, so even in ws mode
|
||||
* we can deal with the ah via u.hdr
|
||||
*/
|
||||
|
@ -1267,15 +1442,32 @@ read:
|
|||
* as to what it can output...) has to go in per-wsi rx buf area.
|
||||
* Otherwise in large temp serv_buf area.
|
||||
*/
|
||||
eff_buf.token = (char *)pt->serv_buf;
|
||||
if (lws_is_ws_with_ext(wsi)) {
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
|
||||
} else {
|
||||
eff_buf.token_len = context->pt_serv_buf_size;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->upgraded_to_http2) {
|
||||
if (!wsi->u.h2.h2n->rx_scratch) {
|
||||
wsi->u.h2.h2n->rx_scratch = lws_malloc(LWS_H2_RX_SCRATCH_SIZE, "h2 rx scratch");
|
||||
if (!wsi->u.h2.h2n->rx_scratch)
|
||||
goto close_and_handled;
|
||||
}
|
||||
eff_buf.token = wsi->u.h2.h2n->rx_scratch;
|
||||
eff_buf.token_len = LWS_H2_RX_SCRATCH_SIZE;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
eff_buf.token = (char *)pt->serv_buf;
|
||||
if (lws_is_ws_with_ext(wsi)) {
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_alloc;
|
||||
} else {
|
||||
eff_buf.token_len = context->pt_serv_buf_size;
|
||||
}
|
||||
|
||||
if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
|
||||
eff_buf.token_len = context->pt_serv_buf_size;
|
||||
}
|
||||
|
||||
if ((unsigned int)eff_buf.token_len > context->pt_serv_buf_size)
|
||||
eff_buf.token_len = context->pt_serv_buf_size;
|
||||
if ((int)pending > eff_buf.token_len)
|
||||
pending = eff_buf.token_len;
|
||||
|
||||
eff_buf.token_len = lws_ssl_capable_read(wsi,
|
||||
(unsigned char *)eff_buf.token, pending ? pending :
|
||||
|
@ -1361,7 +1553,6 @@ drain:
|
|||
* around again it will pick up from where it
|
||||
* left off.
|
||||
*/
|
||||
|
||||
n = lws_read(wsi, (unsigned char *)eff_buf.token,
|
||||
eff_buf.token_len);
|
||||
if (n < 0) {
|
||||
|
@ -1376,8 +1567,7 @@ drain:
|
|||
} while (more);
|
||||
|
||||
if (wsi->u.hdr.ah) {
|
||||
lwsl_notice("%s: %p: detaching\n",
|
||||
__func__, wsi);
|
||||
lwsl_debug("%s: %p: detaching\n", __func__, wsi);
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
/* we can run the normal ah detach flow despite
|
||||
* being in ws union mode, since all union members
|
||||
|
@ -1398,7 +1588,7 @@ drain:
|
|||
|
||||
if (draining_flow && wsi->rxflow_buffer &&
|
||||
wsi->rxflow_pos == wsi->rxflow_len) {
|
||||
lwsl_info("flow buffer: drained\n");
|
||||
lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
|
||||
lws_free_set_NULL(wsi->rxflow_buffer);
|
||||
/* having drained the rxflow buffer, can rearm POLLIN */
|
||||
#ifdef LWS_NO_SERVER
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
114
lib/ssl.c
|
@ -94,7 +94,7 @@ int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
|||
size_t s;
|
||||
int n = 0;
|
||||
|
||||
f =fopen(filename, "rb");
|
||||
f = fopen(filename, "rb");
|
||||
if (f == NULL) {
|
||||
n = 1;
|
||||
goto bail;
|
||||
|
@ -131,7 +131,9 @@ int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
|
|||
*amount = s;
|
||||
|
||||
bail:
|
||||
fclose(f);
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
|
@ -414,10 +416,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
|||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0;
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
int ssl_read_errno = 0;
|
||||
#endif
|
||||
int n = 0, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
|
||||
|
@ -445,29 +444,18 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
|||
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
|
||||
/* manpage: returning 0 means connection shut down */
|
||||
if (!n) {
|
||||
n = lws_ssl_get_error(wsi, n);
|
||||
lwsl_debug("%p: ssl err %d errno %d\n", wsi, n, errno);
|
||||
if (n == SSL_ERROR_ZERO_RETURN)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (n == SSL_ERROR_SYSCALL) {
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
int err = ERR_get_error();
|
||||
if (err == 0 && (ssl_read_errno == EPIPE ||
|
||||
ssl_read_errno == ECONNABORTED ||
|
||||
ssl_read_errno == 0))
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
lwsl_info("%s failed: %s\n",__func__,
|
||||
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
lwsl_debug("%p: ssl err %d errno %d\n", wsi, m, errno);
|
||||
if (m == SSL_ERROR_ZERO_RETURN ||
|
||||
m == SSL_ERROR_SYSCALL)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_debug("%s: WANT_READ\n", __func__);
|
||||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
|
@ -478,10 +466,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
|||
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
lwsl_info("%s failed2: %s\n",__func__,
|
||||
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
@ -542,10 +527,7 @@ lws_ssl_pending(struct lws *wsi)
|
|||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n;
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
int ssl_read_errno = 0;
|
||||
#endif
|
||||
int n, m;
|
||||
|
||||
if (!wsi->ssl)
|
||||
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
|
||||
|
@ -554,33 +536,29 @@ lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, int len)
|
|||
if (n > 0)
|
||||
return n;
|
||||
|
||||
n = lws_ssl_get_error(wsi, n);
|
||||
if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) {
|
||||
if (n == SSL_ERROR_WANT_WRITE) {
|
||||
lwsl_debug("%s: WANT_WRITE\n", __func__);
|
||||
lws_set_blocking_send(wsi);
|
||||
m = lws_ssl_get_error(wsi, n);
|
||||
if (m != SSL_ERROR_SYSCALL) {
|
||||
|
||||
if (SSL_want_read(wsi->ssl)) {
|
||||
lwsl_notice("%s: want read\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
if (SSL_want_write(wsi->ssl)) {
|
||||
lws_set_blocking_send(wsi);
|
||||
|
||||
lwsl_notice("%s: want write\n", __func__);
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
if (n == SSL_ERROR_ZERO_RETURN)
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
|
||||
#if !defined(LWS_WITH_MBEDTLS)
|
||||
if (n == SSL_ERROR_SYSCALL) {
|
||||
int err = ERR_get_error();
|
||||
|
||||
if (err == 0 && (ssl_read_errno == EPIPE ||
|
||||
ssl_read_errno == ECONNABORTED ||
|
||||
ssl_read_errno == 0))
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsl_info("%s failed: %s\n",__func__,
|
||||
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
|
||||
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string(m, NULL));
|
||||
lws_ssl_elaborate_error();
|
||||
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
|
@ -653,7 +631,8 @@ lws_ssl_close(struct lws *wsi)
|
|||
#endif
|
||||
|
||||
n = SSL_get_fd(wsi->ssl);
|
||||
SSL_shutdown(wsi->ssl);
|
||||
if (!wsi->socket_is_permanently_unusable)
|
||||
SSL_shutdown(wsi->ssl);
|
||||
compatible_close(n);
|
||||
SSL_free(wsi->ssl);
|
||||
wsi->ssl = NULL;
|
||||
|
@ -676,6 +655,7 @@ LWS_VISIBLE int
|
|||
lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_vhost *vh;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n, m;
|
||||
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_MBEDTLS)
|
||||
|
@ -856,9 +836,11 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
|
|||
m = lws_ssl_get_error(wsi, n);
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
if (m == 5 && errno == 11)
|
||||
if (m == SSL_ERROR_SYSCALL && errno == 11)
|
||||
m = SSL_ERROR_WANT_READ;
|
||||
#endif
|
||||
if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL)
|
||||
goto failed;
|
||||
|
||||
go_again:
|
||||
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
|
||||
|
@ -880,6 +862,7 @@ go_again:
|
|||
|
||||
break;
|
||||
}
|
||||
failed:
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
|
||||
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
|
||||
|
@ -897,6 +880,18 @@ accepted:
|
|||
wsi->accept_start_us = time_in_microseconds();
|
||||
#endif
|
||||
|
||||
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
|
||||
vh = context->vhost_list;
|
||||
while (vh) {
|
||||
if (!vh->being_destroyed &&
|
||||
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
|
||||
lwsl_info("setting wsi to vh %s\n", vh->name);
|
||||
wsi->vhost = vh;
|
||||
break;
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* OK, we are accepted... give him some time to negotiate */
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
|
||||
context->timeout_secs);
|
||||
|
@ -905,9 +900,10 @@ accepted:
|
|||
wsi->mode = LWSCM_RAW;
|
||||
else
|
||||
wsi->mode = LWSCM_HTTP_SERVING;
|
||||
|
||||
lws_http2_configure_if_upgraded(wsi);
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (lws_h2_configure_if_upgraded(wsi))
|
||||
goto fail;
|
||||
#endif
|
||||
lwsl_debug("accepted new SSL conn\n");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
BIN
test-apps/http2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -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>"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue