1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

ss: support huge urls

This commit is contained in:
Andy Green 2021-03-02 16:34:33 +00:00
parent b2a2e0871c
commit ede7f8b0f3
12 changed files with 733 additions and 37 deletions

View file

@ -318,10 +318,7 @@ lws_hex_to_byte_array(const char *h, uint8_t *dest, int max);
* \param len: the number of bytes the buffer dest points to can hold
*
* This creates random ascii-hex strings up to a given length, with a
* terminating NUL. Hex characters are produced in pairs, if the length of
* the destination buffer is even, after accounting for the NUL there will be
* an unused byte at the end after the NUL. So lengths should be odd to get
* length - 1 characters exactly followed by the NUL.
* terminating NUL.
*
* There will not be any characters produced that are not 0-9, a-f, so it's
* safe to go straight into, eg, JSON.

View file

@ -158,18 +158,22 @@ static char *hexch = "0123456789abcdef";
int
lws_hex_random(struct lws_context *context, char *dest, size_t len)
{
size_t n = (len - 1) / 2;
size_t n = ((len - 1) / 2) + 1;
uint8_t b, *r = (uint8_t *)dest + len - n;
if (lws_get_random(context, r, n) != n)
return 1;
while (n--) {
while (len >= 3) {
b = *r++;
*dest++ = hexch[b >> 4];
*dest++ = hexch[b & 0xf];
len -= 2;
}
if (len == 2)
*dest++ = hexch[(*r) >> 4];
*dest = '\0';
return 0;

View file

@ -2123,9 +2123,14 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t _inlen,
WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
h2n->swsi->http.rx_content_length &&
h2n->swsi->http.rx_content_remain <
lws_ptr_diff_size_t(iend, in) + 1 && /* last */
lws_ptr_diff_size_t(iend, in) + 1 - 9 && /* last */
h2n->inside < h2n->length) {
lwsl_warn("%s: %lu %lu %lu %lu\n", __func__,
(unsigned long)h2n->swsi->http.rx_content_remain,
(unsigned long)(lws_ptr_diff_size_t(iend, in) + 1 - 9),
(unsigned long)h2n->inside, (unsigned long)h2n->length);
/* unread data in frame */
lws_h2_goaway(wsi,
H2_ERR_PROTOCOL_ERROR,

View file

@ -1070,7 +1070,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
{
const char *meth, *pp = lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
char *p = pkt, *p1;
char *p = pkt, *p1, *end = p + wsi->a.context->pt_serv_buf_size;
meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
if (!meth) {
@ -1122,24 +1122,29 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
* Sec-WebSocket-Version: 4
*/
p += lws_snprintf(p, 2048, "%s %s HTTP/1.1\x0d\x0a", meth,
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"%s %s HTTP/1.1\x0d\x0a", meth,
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
p += lws_snprintf(p, 64, "Pragma: no-cache\x0d\x0a"
"Cache-Control: no-cache\x0d\x0a");
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Pragma: no-cache\x0d\x0a"
"Cache-Control: no-cache\x0d\x0a");
p += lws_snprintf(p, 128, "Host: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Host: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) {
if (lws_check_opt(wsi->a.context->options,
LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN))
p += lws_snprintf(p, 128, "Origin: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi,
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Origin: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_ORIGIN));
else
p += lws_snprintf(p, 128, "Origin: http://%s\x0d\x0a",
lws_hdr_simple_ptr(wsi,
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Origin: http://%s\x0d\x0a",
lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_ORIGIN));
}
@ -1153,19 +1158,22 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->parent &&
lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
p += lws_snprintf(p, 128, "Content-Length: %s\x0d\x0a",
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Content-Length: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH));
if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)))
wsi->client_http_body_pending = 1;
}
if (wsi->parent &&
lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)) {
p += lws_snprintf(p, 128, "Authorization: %s\x0d\x0a",
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Authorization: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION));
}
if (wsi->parent &&
lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) {
p += lws_snprintf(p, 128, "Content-Type: %s\x0d\x0a",
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
"Content-Type: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE));
}
#endif
@ -1192,12 +1200,12 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
return NULL;
if (wsi->flags & LCCSCF_HTTP_X_WWW_FORM_URLENCODED) {
p += lws_snprintf(p, 128, "Content-Type: application/x-www-form-urlencoded\x0d\x0a");
p += lws_snprintf(p, 128, "Content-Length: %lu\x0d\x0a", wsi->http.writeable_len);
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "Content-Type: application/x-www-form-urlencoded\x0d\x0a");
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "Content-Length: %lu\x0d\x0a", wsi->http.writeable_len);
lws_client_http_body_pending(wsi, 1);
}
p += lws_snprintf(p, 4, "\x0d\x0a");
p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\x0d\x0a");
if (wsi->client_http_body_pending)
lws_callback_on_writable(wsi);

View file

@ -130,10 +130,11 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
lws_sspc_handle_t *h = (lws_sspc_handle_t *)lws_get_opaque_user_data(wsi);
uint8_t s[32], pkt[LWS_PRE + 2048], *p = pkt + LWS_PRE,
*end = p + sizeof(pkt) - LWS_PRE;
size_t pktsize = wsi->a.context->max_http_header_data;
void *m = (void *)((uint8_t *)&h[1]);
uint8_t *pkt = NULL, *p = NULL, *end = NULL;
const uint8_t *cp;
uint8_t s[32];
lws_usec_t us;
int flags, n;
@ -313,7 +314,12 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
lws_dll2_get_tail(&h->metadata_owner),
lws_sspc_metadata_t, list);
cp = p;
pkt = lws_malloc(pktsize + LWS_PRE, __func__);
if (!pkt)
goto hangup;
cp = p = pkt + LWS_PRE;
end = p + pktsize;
n = lws_sspc_serialize_metadata(md, p, end);
if (n < 0)
goto metadata_hangup;
@ -361,12 +367,17 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
* length?
*/
pkt = lws_malloc(pktsize + LWS_PRE, __func__);
if (!pkt)
goto hangup;
cp = p = pkt + LWS_PRE;
end = p + pktsize;
if (h->metadata_owner.count) {
lws_sspc_metadata_t *md = lws_container_of(
lws_dll2_get_tail(&h->metadata_owner),
lws_sspc_metadata_t, list);
cp = p;
n = lws_sspc_serialize_metadata(md, p, end);
if (n < 0)
goto metadata_hangup;
@ -393,7 +404,7 @@ callback_sspc_client(struct lws *wsi, enum lws_callback_reasons reason,
// break;
}
len = sizeof(pkt) - LWS_PRE - 19;
len = pktsize - LWS_PRE - 19;
flags = 0;
if (!h->ssi.tx) {
n = 0;
@ -457,12 +468,15 @@ do_write:
break;
}
lws_free(pkt);
return lws_callback_http_dummy(wsi, reason, user, in, len);
metadata_hangup:
lwsl_err("%s: metadata too large\n", __func__);
hangup:
lws_free(pkt);
lwsl_warn("hangup\n");
/* hang up on him */
return -1;

View file

@ -385,7 +385,10 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
/*
* The current wsi is decoupled from the pss / conn and
* the conn no longer has a pointer on it
* the conn no longer has a pointer on it.
*
* If there's an outgoing, proxied SS conn on our behalf, we
* have to destroy those
*/
if (conn->ss) {
@ -397,8 +400,9 @@ callback_ss_proxy(struct lws *wsi, enum lws_callback_reasons reason,
lwsl_info("%s: destroying %s, wsi %s\n",
__func__, lws_ss_tag(conn->ss),
lws_wsi_tag(conn->ss->wsi));
/* sever relationship with ss about to be deleted */
lws_set_opaque_user_data(wsi, NULL);
/* sever conn relationship with ss about to be deleted */
conn->ss->wsi = NULL;
if (cw && wsi != cw) {

View file

@ -482,10 +482,11 @@ _lws_ss_client_connect(lws_ss_handle_t *h, int is_retry, void *conn_if_sspc_onw)
const struct ss_pcols *ssp;
size_t used_in, used_out;
union lws_ss_contemp ct;
char path[1024], ep[96];
lws_ss_state_return_t r;
int port, _port, tls;
char *path, ep[96];
lws_strexp_t exp;
struct lws *wsi;
if (!h->policy) {
lwsl_err("%s: ss with no policy\n", __func__);
@ -651,14 +652,20 @@ _lws_ss_client_connect(lws_ss_handle_t *h, int is_retry, void *conn_if_sspc_onw)
i.protocol = ssp->protocol->name; /* lws protocol name */
i.local_protocol_name = i.protocol;
path = lws_malloc(h->context->max_http_header_data, __func__);
if (!path) {
lwsl_warn("%s: OOM on path prealloc\n", __func__);
return LWSSSSRET_TX_DONT_SEND;
}
if (ssp->munge) /* eg, raw doesn't use; endpoint strexp already done */
ssp->munge(h, path, sizeof(path), &i, &ct);
ssp->munge(h, path, h->context->max_http_header_data, &i, &ct);
i.pwsi = &h->wsi;
#if defined(LWS_WITH_SSPLUGINS)
if (h->policy->plugins[0] && h->policy->plugins[0]->munge)
h->policy->plugins[0]->munge(h, path, sizeof(path));
h->policy->plugins[0]->munge(h, path, h->context->max_http_header_data);
#endif
lwsl_info("%s: connecting %s, '%s' '%s' %s\n", __func__, i.method,
@ -666,10 +673,14 @@ _lws_ss_client_connect(lws_ss_handle_t *h, int is_retry, void *conn_if_sspc_onw)
h->txn_ok = 0;
r = lws_ss_event_helper(h, LWSSSCS_CONNECTING);
if (r)
if (r) {
lws_free(path);
return r;
}
if (!lws_client_connect_via_info(&i)) {
wsi = lws_client_connect_via_info(&i);
lws_free(path);
if (!wsi) {
/*
* We already found that we could not connect, without even
* having to go around the event loop

View file

@ -0,0 +1,133 @@
project(lws-minimal-secure-streams-hugeurl C)
cmake_minimum_required(VERSION 2.8.12)
find_package(libwebsockets CONFIG REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
include(CheckCSourceCompiles)
include(LwsCheckRequirements)
set(SAMP lws-minimal-secure-streams-hugeurl)
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements)
require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements)
require_lws_config(LWS_WITH_SYS_STATE 1 requirements)
if (requirements)
add_executable(${SAMP} minimal-secure-streams.c)
find_program(VALGRIND "valgrind")
if (LWS_CTEST_INTERNET_AVAILABLE AND NOT WIN32)
#
# When running in CI, wait for a lease on the resources
# before starting this test, so the server does not get
# thousands of simultaneous tls connection attempts
#
# sai-resource holds the lease on the resources until
# the time given in seconds or the sai-resource instance
# exits, whichever happens first
#
# If running under Sai, creates a lock test called "res_sspcmin_hurl"
#
sai_resource(warmcat_conns 1 40 sspcmin_hurl)
#
# simple test not via proxy
#
if (VALGRIND)
message("testing via valgrind")
add_test(NAME ss-warmcat-hurl COMMAND
${VALGRIND} --tool=memcheck --leak-check=yes --num-callers=20
$<TARGET_FILE:lws-minimal-secure-streams>)
else()
add_test(NAME ss-warmcat-hurl COMMAND lws-minimal-secure-streams)
endif()
set_tests_properties(ss-warmcat-hurl
PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams
TIMEOUT 20)
if (DEFINED ENV{SAI_OVN})
set_tests_properties(ss-warmcat-hurl PROPERTIES FIXTURES_REQUIRED "res_sspcmin_hurl")
endif()
if (HAS_LWS_WITH_SECURE_STREAMS_PROXY_API OR LWS_WITH_SECURE_STREAMS_PROXY_API)
#
# Define test dep to bring up and take down the test
# proxy
#
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# uds abstract namespace for linux
set(CTEST_SOCKET_PATH "@ctest-ssp-hurl-$ENV{SAI_PROJECT}-$ENV{SAI_OVN}")
else()
# filesystem socket for others
set(CTEST_SOCKET_PATH "/tmp/ctest-ssp-hurl-$ENV{SAI_PROJECT}-$ENV{SAI_OVN}")
endif()
add_test(NAME st_ssproxy-hurl COMMAND
${CMAKE_SOURCE_DIR}/scripts/ctest-background.sh
ssproxy-hurl $<TARGET_FILE:lws-minimal-secure-streams-proxy>
-i ${CTEST_SOCKET_PATH} )
set_tests_properties(st_ssproxy-hurl PROPERTIES WORKING_DIRECTORY . FIXTURES_SETUP ssproxy-hurl TIMEOUT 800)
add_test(NAME ki_ssproxy-hurl COMMAND
${CMAKE_SOURCE_DIR}/scripts/ctest-background-kill.sh
ssproxy-hurl $<TARGET_FILE:lws-minimal-secure-streams-proxy>
-i ${CTEST_SOCKET_PATH})
set_tests_properties(ki_ssproxy-hurl PROPERTIES FIXTURES_CLEANUP ssproxy-hurl)
#
# the client part that will connect to the proxy
#
if (VALGRIND)
message("testing via valgrind")
add_test(NAME sspc-minimal-hurl COMMAND
${VALGRIND} --tool=memcheck --leak-check=yes --num-callers=20
$<TARGET_FILE:lws-minimal-secure-streams-client> -i +${CTEST_SOCKET_PATH})
else()
add_test(NAME sspc-minimal-hurl COMMAND lws-minimal-secure-streams-client -i +${CTEST_SOCKET_PATH})
endif()
set(fixlist "ssproxy-hurl")
if (DEFINED ENV{SAI_OVN})
list(APPEND fixlist "res_ssproxy-hurl")
endif()
set_tests_properties(sspc-minimal-hurl PROPERTIES
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/minimal-examples/secure-streams/minimal-secure-streams
FIXTURES_REQUIRED "${fixlist}"
TIMEOUT 40)
endif()
endif()
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})
endif()
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\ni#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API)\n return 0;\n #else\n fail\n #endif\n return 0;\n}\n" HAS_LWS_WITH_SECURE_STREAMS_PROXY_API)
if (HAS_LWS_WITH_SECURE_STREAMS_PROXY_API OR LWS_WITH_SECURE_STREAMS_PROXY_API)
add_compile_options(-DLWS_SS_USE_SSPC)
add_executable(${SAMP}-client minimal-secure-streams.c)
if (websockets_shared)
target_link_libraries(${SAMP}-client websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
add_dependencies(${SAMP}-client websockets_shared)
else()
target_link_libraries(${SAMP}-client websockets ${LIBWEBSOCKETS_DEP_LIBS})
endif()
endif()
endif()

View file

@ -0,0 +1,72 @@
# lws minimal secure streams hugeurl
This application sends a huge url to httpbin.org, by default 4000 bytes in
a urlarg ?x=xxxxxx..., where the argument is a random string in hex.
Notice that httpbin.org has its own limit for urlsize, of 4094 bytes for
the entire URL.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-h <hugeurl size>|Default 4000
--h1|Force http/1.1 instead of default h2
```
[2021/03/02 16:38:00:2662] U: LWS secure streams hugeurl test client [-d<verb>][-h <urlarg len>]
[2021/03/02 16:38:00:2662] U: main: huge argument size: 4000 bytes
[2021/03/02 16:38:00:2662] N: LWS: 4.1.99-v4.1.0-294-g85c1fe07a7, loglevel 1031
[2021/03/02 16:38:00:2662] N: NET CLI SRV H1 H2 WS SS-JSON-POL SSPROX IPV6-on
[2021/03/02 16:38:00:2663] N: ++ [1903157|wsi|0|pipe] (1)
[2021/03/02 16:38:00:2663] N: ++ [1903157|vh|0|netlink] (1)
[2021/03/02 16:38:00:2677] N: ++ [1903157|vh|1|_ss_default||-1] (2)
[2021/03/02 16:38:00:2736] N: ++ [1903157|vh|2|arca1||-1] (3)
[2021/03/02 16:38:00:2798] N: ++ [1903157|wsiSScli|0|captive_portal_detect] (1)
[2021/03/02 16:38:00:2798] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect]: (unset) -> LWSSSCS_CREATING
[2021/03/02 16:38:00:2798] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect]: LWSSSCS_CREATING -> LWSSSCS_POLL
[2021/03/02 16:38:00:2800] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect]: LWSSSCS_POLL -> LWSSSCS_CONNECTING
[2021/03/02 16:38:00:2801] N: ++ [1903157|wsicli|0|GET/h1/connectivitycheck.android.com/([1903157|wsiSScli|0|captive_portal_det] (1)
[2021/03/02 16:38:00:3227] W: lws_metrics_hist_bump_priv_tagged: 'ss="captive_portal_detect",http_resp="204"'
[2021/03/02 16:38:00:3227] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect|204]: LWSSSCS_CONNECTING -> LWSSSCS_CONNECTED
[2021/03/02 16:38:00:3227] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect|204]: LWSSSCS_CONNECTED -> LWSSSCS_QOS_ACK_REMOTE
[2021/03/02 16:38:00:3227] N: lws_system_cpd_set: setting CPD result OK
[2021/03/02 16:38:00:3227] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect|204]: LWSSSCS_QOS_ACK_REMOTE -> LWSSSCS_DISCONNECTED
[2021/03/02 16:38:00:3228] N: lws_ss_check_next_state: [1903157|wsiSScli|0|captive_portal_detect|204]: LWSSSCS_DISCONNECTED -> LWSSSCS_DESTROYING
[2021/03/02 16:38:00:3228] N: -- [1903157|wsiSScli|0|captive_portal_detect|204] (0) 42.928ms
[2021/03/02 16:38:00:3231] N: -- [1903157|wsicli|0|GET/h1/connectivitycheck.android.com/([1903157|wsiSScli|0|captive_portal_det] (0) 42.994ms
[2021/03/02 16:38:00:3853] N: ++ [1903157|wsiSScli|1|httpbin_anything] (1)
[2021/03/02 16:38:00:3854] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything]: (unset) -> LWSSSCS_CREATING
[2021/03/02 16:38:00:3854] U: myss_state: LWSSSCS_CREATING (1), ord 0x0
[2021/03/02 16:38:00:3855] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything]: LWSSSCS_CREATING -> LWSSSCS_CONNECTING
[2021/03/02 16:38:00:3855] U: myss_state: LWSSSCS_CONNECTING (6), ord 0x0
[2021/03/02 16:38:00:3855] N: ++ [1903157|wsicli|1|GET/h1/httpbin.org/([1903157|wsiSScli|1|httpbin_anything])] (1)
[2021/03/02 16:38:00:6855] N: ++ [1903157|mux|0|h2_sid1_(1903157|wsicli|1)] (1)
[2021/03/02 16:38:00:6857] N: secstream_h1: [1903157|wsiSScli|1|httpbin_anything] no handle / tx
[2021/03/02 16:38:00:7904] W: lws_metrics_hist_bump_priv_tagged: 'ss="httpbin_anything",http_resp="200"'
[2021/03/02 16:38:00:7904] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything|200]: LWSSSCS_CONNECTING -> LWSSSCS_CONNECTED
[2021/03/02 16:38:00:7904] U: myss_state: LWSSSCS_CONNECTED (5), ord 0x0
[2021/03/02 16:38:00:7907] U: myss_rx: return hugeurl len 4000 matches OK
[2021/03/02 16:38:00:7907] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything|200]: LWSSSCS_CONNECTED -> LWSSSCS_QOS_ACK_REMOTE
[2021/03/02 16:38:00:7907] U: myss_state: LWSSSCS_QOS_ACK_REMOTE (10), ord 0x0
[2021/03/02 16:38:00:7908] N: myss_state: LWSSSCS_QOS_ACK_REMOTE
[2021/03/02 16:38:00:7908] N: -- [1903157|wsi|0|pipe] (0) 524.500ms
[2021/03/02 16:38:00:7908] N: -- [1903157|mux|0|h2_sid1_(1903157|wsicli|1)] (0) 105.284ms
[2021/03/02 16:38:00:7912] N: -- [1903157|vh|2|arca1||-1] (2) 517.621ms
[2021/03/02 16:38:00:7912] N: -- [1903157|wsicli|1|GET/h1/httpbin.org/([1903157|wsiSScli|1|httpbin_anything|arca1|h2|h2])] (0) 405.690ms
[2021/03/02 16:38:00:7912] N: -- [1903157|vh|0|netlink] (1) 524.918ms
[2021/03/02 16:38:00:7913] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything|200]: LWSSSCS_QOS_ACK_REMOTE -> LWSSSCS_DISCONNECTED
[2021/03/02 16:38:00:7913] U: myss_state: LWSSSCS_DISCONNECTED (2), ord 0x0
[2021/03/02 16:38:00:7913] N: lws_ss_check_next_state: [1903157|wsiSScli|1|httpbin_anything|200]: LWSSSCS_DISCONNECTED -> LWSSSCS_DESTROYING
[2021/03/02 16:38:00:7913] U: myss_state: LWSSSCS_DESTROYING (7), ord 0x0
[2021/03/02 16:38:00:7913] N: -- [1903157|wsiSScli|1|httpbin_anything|200] (0) 405.986ms
[2021/03/02 16:38:00:7925] N: -- [1903157|vh|1|_ss_default||-1] (0) 524.844ms
[2021/03/02 16:38:00:7926] U: Completed: OK
```

View file

@ -0,0 +1,444 @@
/*
* lws-minimal-secure-streams-hugeurl
*
* Written in 2010-2021 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
*
* This checks huge url operations via httpbin.org
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static unsigned int timeout_ms = 3000;
static int interrupted, bad = 1, h1;
static lws_state_notify_link_t nl;
static size_t hugeurl_size = 4000;
static char *hugeurl, *check;
#if !defined(LWS_SS_USE_SSPC)
static const char * const default_ss_policy =
"{"
"\"release\":" "\"01234567\","
"\"product\":" "\"myproduct\","
"\"schema-version\":" "1,"
#if defined(VIA_LOCALHOST_SOCKS)
"\"via-socks5\":" "\"127.0.0.1:1080\","
#endif
"\"retry\": [" /* named backoff / retry strategies */
"{\"default\": {"
"\"backoff\": [" "1000,"
"2000,"
"3000,"
"5000,"
"10000"
"],"
"\"conceal\":" "5,"
"\"jitterpc\":" "20,"
"\"svalidping\":" "30,"
"\"svalidhup\":" "35"
"}}"
"],"
"\"certs\": [" /* named individual certificates in BASE64 DER */
/*
* Let's Encrypt certs for warmcat.com / libwebsockets.org
*
* We fetch the real policy from there using SS and switch to
* using that.
*/
"{\"amazon_root_ca_1\": \""
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0"
"BAQsFADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQ"
"QDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExN"
"zAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcG"
"A1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggE"
"PADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrA"
"IthtOgQ3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdY"
"Z6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH"
"3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0"
"tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyz"
"iKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIq"
"g0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw"
"HQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwU"
"AA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9r"
"bxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/m"
"sv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96L"
"XFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bld"
"ZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8o"
"b2xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5"
"\"}"
"],"
"\"trust_stores\": [" /* named cert chains */
"{"
"\"name\": \"arca1\","
"\"stack\": ["
"\"amazon_root_ca_1\""
"]"
"}"
"],"
"\"s\": [{"
"\"httpbin_anything_h1\": {"
"\"endpoint\":" "\"httpbin.org\","
"\"port\":" "443,"
"\"protocol\":" "\"h1\","
"\"http_method\":" "\"GET\","
"\"http_url\":" "\"anything?x=${hugearg}\","
"\"nghttp2_quirk_end_stream\":" "true,"
"\"h2q_oflow_txcr\":" "true,"
"\"metadata\": [{"
"\"hugearg\":" "\"\""
"}],"
"\"tls\":" "true,"
"\"opportunistic\":" "true,"
"\"retry\":" "\"default\","
"\"tls_trust_store\":" "\"arca1\""
"}},{"
"\"httpbin_anything_h2\": {"
"\"endpoint\":" "\"httpbin.org\","
"\"port\":" "443,"
"\"protocol\":" "\"h2\","
"\"http_method\":" "\"GET\","
"\"http_url\":" "\"anything?x=${hugearg}\","
"\"nghttp2_quirk_end_stream\":" "true,"
"\"h2q_oflow_txcr\":" "true,"
"\"metadata\": [{"
"\"hugearg\":" "\"\""
"}],"
"\"tls\":" "true,"
"\"opportunistic\":" "true,"
"\"retry\":" "\"default\","
"\"tls_trust_store\":" "\"arca1\""
"}},{"
/*
* "captive_portal_detect" describes
* what to do in order to check if the path to
* the Internet is being interrupted by a
* captive portal. If there's a larger policy
* fetched from elsewhere, it should also include
* this since it needs to be done at least after
* every DHCP acquisition
*/
"\"captive_portal_detect\": {"
"\"endpoint\": \"connectivitycheck.android.com\","
"\"http_url\": \"generate_204\","
"\"port\": 80,"
"\"protocol\": \"h1\","
"\"http_method\": \"GET\","
"\"opportunistic\": true,"
"\"http_expect\": 204,"
"\"http_fail_redirect\": true"
"}}"
"]}"
;
#endif
typedef struct myss {
struct lws_ss_handle *ss;
void *opaque_data;
/* ... application specific state ... */
lws_sorted_usec_list_t sul;
struct lejp_ctx ctx;
size_t comp;
char started;
} myss_t;
static const char * const lejp_tokens[] = {
"url"
};
/*
* Parse the "url" member of the JSON, and collect the part after the first '='
* into the prepared buffer "check".
*/
static signed char
lws_httpbin_json_cb(struct lejp_ctx *ctx, char reason)
{
myss_t *m = (myss_t *)ctx->user;
const char *p = ctx->buf;
size_t l = ctx->npos;
if (!(reason & LEJP_FLAG_CB_IS_VALUE))
return 0;
if (ctx->path_match - 1)
return 0;
if (!m->started)
while (l--)
if (*p++ == '=') {
m->started = 1;
break;
}
if (!m->started)
return 0;
if (m->comp + l > hugeurl_size) {
lwsl_err("%s: returned url string too large %u, %u\n",
__func__, (unsigned int)m->comp, (unsigned int)l);
return -1;
}
memcpy(check + m->comp, p, l);
m->comp += l;
return 0;
}
/* secure streams payload interface */
static lws_ss_state_return_t
myss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
myss_t *m = (myss_t *)userobj;
if (flags & LWSSS_FLAG_SOM)
lejp_construct(&m->ctx, lws_httpbin_json_cb, m,
lejp_tokens, LWS_ARRAY_SIZE(lejp_tokens));
if (len) {
int pr = lejp_parse(&m->ctx, buf, (int)len);
if (pr != LEJP_CONTINUE && pr < 0) {
lwsl_err("%s: parse failed line %u: %d: %s\n", __func__,
(unsigned int)m->ctx.line, pr,
lejp_error_to_string(pr));
return LWSSSSRET_DESTROY_ME;
}
}
if (flags & LWSSS_FLAG_EOM) {
interrupted = 1;
/* confirm that what we collected is the expected size */
if (m->comp != hugeurl_size) {
lwsl_err("%s: wrong urlarg size recovered %d %d\n",
__func__, (int)m->comp, (int)hugeurl_size);
return LWSSSSRET_OK;
}
/* confirm what we sent is the same as what we collected */
if (memcmp(hugeurl, check, hugeurl_size)) {
lwsl_err("%s: huge url content mismatch\n", __func__);
return LWSSSSRET_OK;
}
lwsl_user("%s: return hugeurl len %u matches OK\n", __func__,
(unsigned int)hugeurl_size);
bad = 0;
}
return LWSSSSRET_OK;
}
static lws_ss_state_return_t
myss_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
myss_t *m = (myss_t *)userobj;
lwsl_user("%s: %s (%d), ord 0x%x\n", __func__,
lws_ss_state_name((int)state), state, (unsigned int)ack);
switch (state) {
case LWSSSCS_CREATING:
lws_ss_start_timeout(m->ss, timeout_ms);
/* let's make the hugeurl part */
hugeurl = malloc(hugeurl_size + 1);
if (!hugeurl) {
lwsl_err("OOM\n");
return LWSSSSRET_DESTROY_ME;
}
check = malloc(hugeurl_size + 1);
if (!check) {
lwsl_err("OOM\n");
free(hugeurl);
hugeurl = NULL;
return LWSSSSRET_DESTROY_ME;
}
/* Create the big, random, urlarg */
lws_hex_random(lws_ss_get_context(m->ss), hugeurl,
hugeurl_size + 1);
lws_ss_set_metadata(m->ss, "hugearg", hugeurl, hugeurl_size);
return lws_ss_client_connect(m->ss);
case LWSSSCS_ALL_RETRIES_FAILED:
/* if we're out of retries, we want to close the app and FAIL */
interrupted = 1;
break;
case LWSSSCS_QOS_ACK_REMOTE:
lwsl_notice("%s: LWSSSCS_QOS_ACK_REMOTE\n", __func__);
break;
case LWSSSCS_TIMEOUT:
lwsl_notice("%s: LWSSSCS_TIMEOUT\n", __func__);
break;
case LWSSSCS_USER_BASE:
lwsl_notice("%s: LWSSSCS_USER_BASE\n", __func__);
break;
default:
break;
}
return LWSSSSRET_OK;
}
static lws_ss_info_t ssi = {
.handle_offset = offsetof(myss_t, ss),
.opaque_user_data_offset = offsetof(myss_t, opaque_data),
.rx = myss_rx,
.state = myss_state,
.user_alloc = sizeof(myss_t),
.streamtype = "httpbin_anything_h2"
};
static int
app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
{
struct lws_context *context = lws_system_context_from_system_mgr(mgr);
/*
* For the things we care about, let's notice if we are trying to get
* past them when we haven't solved them yet, and make the system
* state wait while we trigger the dependent action.
*/
if (target != LWS_SYSTATE_OPERATIONAL)
return 0;
if (current != LWS_SYSTATE_OPERATIONAL)
return 0;
if (h1)
ssi.streamtype = "httpbin_anything_h1";
if (!lws_ss_create(context, 0, &ssi, NULL, NULL, NULL, NULL))
return 0;
lwsl_err("%s: failed to create secure stream\n", __func__);
return -1;
}
static lws_state_notify_link_t * const app_notifier_list[] = {
&nl, NULL
};
static void
sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
const char *p;
int n = 0;
signal(SIGINT, sigint_handler);
memset(&info, 0, sizeof info);
lws_cmdline_option_handle_builtin(argc, argv, &info);
lwsl_user("LWS secure streams hugeurl test client [-d<verb>][-h <urlarg len>]\n");
info.fd_limit_per_thread = 1 + 6 + 1;
info.port = CONTEXT_PORT_NO_LISTEN;
#if defined(LWS_SS_USE_SSPC)
info.protocols = lws_sspc_protocols;
/* connect to ssproxy via UDS by default, else via
* tcp connection to this port */
if ((p = lws_cmdline_option(argc, argv, "-p")))
info.ss_proxy_port = (uint16_t)atoi(p);
/* UDS "proxy.ss.lws" in abstract namespace, else this socket
* path; when -p given this can specify the network interface
* to bind to */
if ((p = lws_cmdline_option(argc, argv, "-i")))
info.ss_proxy_bind = p;
/* if -p given, -a specifies the proxy address to connect to */
if ((p = lws_cmdline_option(argc, argv, "-a")))
info.ss_proxy_address = p;
#else
info.pss_policies_json = default_ss_policy;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#endif
if (lws_cmdline_option(argc, argv, "--h1"))
h1 = 1;
if ((p = lws_cmdline_option(argc, argv, "-h")))
hugeurl_size = (size_t)atol(p);
if (hugeurl_size < 1 || hugeurl_size > 16384) {
lwsl_err("%s: -h should be between 1 and 16384\n", __func__);
return 1;
}
lwsl_user("%s: huge argument size: %u bytes\n", __func__,
(unsigned int)hugeurl_size);
info.pt_serv_buf_size = (unsigned int)((hugeurl_size * 2) + 2048);
info.max_http_header_data = (unsigned short)(hugeurl_size + 2048);
/* integrate us with lws system state management when context created */
nl.name = "app";
nl.notify_cb = app_system_state_nf;
info.register_notifier_list = app_notifier_list;
/* create the context */
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
/* the event loop */
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
if (hugeurl)
free(hugeurl);
if (check)
free(check);
lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
return bad;
}

View file

@ -275,6 +275,9 @@ int main(int argc, const char **argv)
nl.notify_cb = app_system_state_nf;
info.register_notifier_list = app_notifier_list;
info.pt_serv_buf_size = (unsigned int)((6144 * 2) + 2048);
info.max_http_header_data = (unsigned short)(6144 + 2048);
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");

View file

@ -422,6 +422,7 @@ int main(int argc, const char **argv)
#else
info.pss_policies_json = default_ss_policy;
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
#endif
#if defined(LWS_WITH_DETAILED_LATENCY)