extension permessage deflate
Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
58ad3d6e09
commit
6711266a50
40 changed files with 1922 additions and 1141 deletions
|
@ -447,12 +447,10 @@ endif()
|
|||
|
||||
if (NOT LWS_WITHOUT_EXTENSIONS)
|
||||
list(APPEND HDR_PRIVATE
|
||||
lib/extension-deflate-frame.h
|
||||
lib/extension-deflate-stream.h)
|
||||
lib/extension-permessage-deflate.h)
|
||||
list(APPEND SOURCES
|
||||
lib/extension.c
|
||||
lib/extension-deflate-frame.c
|
||||
lib/extension-deflate-stream.c)
|
||||
lib/extension-permessage-deflate.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_LIBEV)
|
||||
|
@ -699,7 +697,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
#
|
||||
# Helper function for adding a test app.
|
||||
#
|
||||
macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4)
|
||||
macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4 S5)
|
||||
|
||||
set(TEST_SRCS ${MAIN_SRC})
|
||||
set(TEST_HDR)
|
||||
|
@ -715,6 +713,10 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
else()
|
||||
list(APPEND TEST_SRCS ${S4})
|
||||
endif()
|
||||
if ("${S5}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND TEST_SRCS ${S5})
|
||||
endif()
|
||||
if (WIN32)
|
||||
list(APPEND TEST_SRCS
|
||||
${WIN32_HELPERS_PATH}/getopt.c
|
||||
|
@ -772,13 +774,27 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
# test-server
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_SERVER)
|
||||
create_test_app(test-server "test-server/test-server.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
|
||||
create_test_app(test-server "test-server/test-server.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")))
|
||||
create_test_app(test-server-pthreads "test-server/test-server-pthreads.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
|
||||
create_test_app(test-server-pthreads
|
||||
"test-server/test-server-pthreads.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
endif()
|
||||
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
AND LWS_WITH_LIBEV)
|
||||
create_test_app(test-server-libev "test-server/test-server-libev.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
|
||||
create_test_app(test-server-libev
|
||||
"test-server/test-server-libev.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -786,7 +802,11 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
# test-server-extpoll
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL)
|
||||
create_test_app(test-server-extpoll "test-server/test-server.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c")
|
||||
create_test_app(test-server-extpoll "test-server/test-server.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
# Set defines for this executable only.
|
||||
set_property(
|
||||
TARGET test-server-extpoll
|
||||
|
@ -891,27 +911,27 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
# test-client
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_CLIENT)
|
||||
create_test_app(test-client "test-server/test-client.c" "" "" "")
|
||||
create_test_app(test-client "test-server/test-client.c" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-fraggle
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_FRAGGLE)
|
||||
create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "")
|
||||
create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-ping
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_PING)
|
||||
create_test_app(test-ping "test-server/test-ping.c" "" "" "")
|
||||
create_test_app(test-ping "test-server/test-ping.c" "" "" "" "")
|
||||
endif()
|
||||
#
|
||||
# test-echo
|
||||
#
|
||||
if (NOT WITHOUT_TEST_ECHO)
|
||||
create_test_app(test-echo "test-server/test-echo.c" "" "" "")
|
||||
create_test_app(test-echo "test-server/test-echo.c" "" "" "" "")
|
||||
endif()
|
||||
|
||||
endif(NOT LWS_WITHOUT_CLIENT)
|
||||
|
|
|
@ -261,8 +261,8 @@ if left `NULL`, then the "DEFAULT" set of ciphers are all possible to select.
|
|||
Async nature of client connections
|
||||
----------------------------------
|
||||
|
||||
When you call `lws_client_connect(..)` and get a `wsi` back, it does not
|
||||
mean your connection is active. It just mean it started trying to connect.
|
||||
When you call `lws_client_connect_info(..)` and get a `wsi` back, it does not
|
||||
mean your connection is active. It just means it started trying to connect.
|
||||
|
||||
Your client connection is actually active only when you receive
|
||||
`LWS_CALLBACK_CLIENT_ESTABLISHED` for it.
|
||||
|
|
|
@ -335,7 +335,5 @@ The autobahn test itself admits this is not part of the standard, just someone's
|
|||
random opinion about how they think a ws server should act. So we will fail
|
||||
this by design and it is no problem about RFC6455 compliance.
|
||||
|
||||
- Test 6.x: detect we were sent invalid UTF-8. Validity of encoding is left
|
||||
to the user code in libwebsockets until now.
|
||||
|
||||
|
||||
|
|
56
changelog
56
changelog
|
@ -1,6 +1,50 @@
|
|||
Changelog
|
||||
---------
|
||||
|
||||
Extension Changes
|
||||
-----------------
|
||||
|
||||
1) There is now a "permessage-deflate" / RFC7692 implementation. It's very
|
||||
similar to "deflate-frame" we have offered for a long while; deflate-frame is
|
||||
now provided as an alias of permessage-deflate.
|
||||
|
||||
The main differences are that the new permessage-deflate implementation:
|
||||
|
||||
- properly performs streaming respecting input and output buffer limits. The
|
||||
old deflate-frame implementation could only work on complete deflate input
|
||||
and produce complete inflate output for each frame. The new implementation
|
||||
only mallocs buffers at initialization.
|
||||
|
||||
- goes around the event loop after each input package is processed allowing
|
||||
interleaved output processing. The RX flow control api can be used to
|
||||
force compressed input processing to match the rate of compressed output
|
||||
processing (test--echo shows an example of how to do this).
|
||||
|
||||
- when being "deflate-frame" for compatibility he uses the same default zlib
|
||||
settings as the old "deflate-frame", but instead of exponentially increasing
|
||||
malloc allocations until the whole output will fit, he observes the default
|
||||
input and output chunking buffer sizes of "permessage-deflate", that's
|
||||
1024 in and 1024 out at a time.
|
||||
|
||||
2) deflate-stream has been disabled for many versions (for over a year) and is
|
||||
now removed. Browsers are now standardizing on "permessage-deflate" / RFC7692
|
||||
|
||||
3) struct lws_extension is simplified, and lws extensions now have a public
|
||||
api (their callback) for use in user code to compose extensions and options
|
||||
the user code wants. lws_get_internal_exts() is deprecated but kept around
|
||||
as a NOP. The changes allow one extension implementation to go by different
|
||||
names and allows the user client code to control option offers per-ext.
|
||||
|
||||
The test client and server are updated to use the new way. If you use
|
||||
the old way it should still work, but extensions will be disabled until you
|
||||
update your code.
|
||||
|
||||
Extensions are now responsible for allocating and per-instance private struct
|
||||
at instance construction time and freeing it when the instance is destroyed.
|
||||
Not needing to know the size means the extension's struct can be opaque
|
||||
to user code.
|
||||
|
||||
|
||||
User api additions
|
||||
------------------
|
||||
|
||||
|
@ -47,7 +91,8 @@ LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
|
|||
defined in the standard, and may be a string or non-human-
|
||||
readble data.
|
||||
If you return 0 lws will echo the close and then close the
|
||||
connection. If you return nonzero lws will just close the connection.
|
||||
connection. If you return nonzero lws will just close the
|
||||
connection.
|
||||
|
||||
As usual not handling it does the right thing, if you're not interested in it
|
||||
just ignore it.
|
||||
|
@ -134,6 +179,15 @@ anyway.
|
|||
5) Pings and close used to be limited to 124 bytes, the correct limit is 125
|
||||
so that is now also allowed.
|
||||
|
||||
6) LWS_PRE is provided as a synonym for LWS_SEND_BUFFER_POST_PADDING, either is
|
||||
valid to use now.
|
||||
|
||||
7) There's generic support for RFC7462 style extension options built into the
|
||||
library now. As a consequence, a field "options" is added to lws_extension.
|
||||
It can be NULL if there are no options on the extension. Extension internal
|
||||
info is part of the public abi because extensions may be implemented outside
|
||||
the library.
|
||||
|
||||
|
||||
v1.6.0-chrome48-firefox42
|
||||
=======================
|
||||
|
|
|
@ -335,35 +335,33 @@ failed:
|
|||
*/
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_client_connect(struct lws_context *context, const char *address,
|
||||
int port, int ssl_connection, const char *path,
|
||||
const char *host, const char *origin,
|
||||
const char *protocol, int ietf_version_or_minus_one)
|
||||
lws_client_connect_info(struct lws_client_connect_info *i)
|
||||
{
|
||||
struct lws *wsi;
|
||||
int v = SPEC_LATEST_SUPPORTED;
|
||||
|
||||
wsi = lws_zalloc(sizeof(struct lws));
|
||||
if (wsi == NULL)
|
||||
goto bail;
|
||||
|
||||
wsi->context = context;
|
||||
wsi->context = i->context;
|
||||
wsi->sock = LWS_SOCK_INVALID;
|
||||
|
||||
/* -1 means just use latest supported */
|
||||
|
||||
if (ietf_version_or_minus_one == -1)
|
||||
ietf_version_or_minus_one = SPEC_LATEST_SUPPORTED;
|
||||
if (i->ietf_version_or_minus_one != -1 && i->ietf_version_or_minus_one)
|
||||
v = i->ietf_version_or_minus_one;
|
||||
|
||||
wsi->ietf_spec_revision = ietf_version_or_minus_one;
|
||||
wsi->ietf_spec_revision = v;
|
||||
wsi->user_space = NULL;
|
||||
wsi->state = LWSS_CLIENT_UNCONNECTED;
|
||||
wsi->protocol = NULL;
|
||||
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
wsi->use_ssl = ssl_connection;
|
||||
wsi->use_ssl = i->ssl_connection;
|
||||
#else
|
||||
if (ssl_connection) {
|
||||
if (i->ssl_connection) {
|
||||
lwsl_err("libwebsockets not configured for ssl\n");
|
||||
goto bail;
|
||||
}
|
||||
|
@ -376,31 +374,35 @@ lws_client_connect(struct lws_context *context, const char *address,
|
|||
* we're not necessarily in a position to action these right away,
|
||||
* stash them... we only need during connect phase so u.hdr is fine
|
||||
*/
|
||||
wsi->u.hdr.ah->c_port = port;
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
|
||||
wsi->u.hdr.ah->c_port = i->port;
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, i->address))
|
||||
goto bail1;
|
||||
|
||||
/* these only need u.hdr lifetime as well */
|
||||
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, path))
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, i->path))
|
||||
goto bail1;
|
||||
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host))
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, i->host))
|
||||
goto bail1;
|
||||
|
||||
if (origin)
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, origin))
|
||||
if (i->origin)
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_ORIGIN, i->origin))
|
||||
goto bail1;
|
||||
/*
|
||||
* this is a list of protocols we tell the server we're okay with
|
||||
* stash it for later when we compare server response with it
|
||||
*/
|
||||
if (protocol)
|
||||
if (i->protocol)
|
||||
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS,
|
||||
protocol))
|
||||
i->protocol))
|
||||
goto bail1;
|
||||
|
||||
wsi->protocol = &context->protocols[0];
|
||||
wsi->protocol = &i->context->protocols[0];
|
||||
if (wsi && !wsi->user_space && i->userdata) {
|
||||
wsi->user_space_externally_allocated = 1;
|
||||
wsi->user_space = i->userdata;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check with each extension if it is able to route and proxy this
|
||||
|
@ -409,9 +411,9 @@ lws_client_connect(struct lws_context *context, const char *address,
|
|||
* connection.
|
||||
*/
|
||||
|
||||
if (lws_ext_cb_all_exts(context, wsi,
|
||||
LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION,
|
||||
(void *)address, port) > 0) {
|
||||
if (lws_ext_cb_all_exts(i->context, wsi,
|
||||
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
|
||||
(void *)i->address, i->port) > 0) {
|
||||
lwsl_client("lws_client_connect: ext handling conn\n");
|
||||
|
||||
lws_set_timeout(wsi,
|
||||
|
@ -436,6 +438,7 @@ bail:
|
|||
|
||||
/**
|
||||
* lws_client_connect_extended() - Connect to another websocket server
|
||||
* DEPRECAATED use lws_client_connect_info
|
||||
* @context: Websocket context
|
||||
* @address: Remote server address, eg, "myserver.com"
|
||||
* @port: Port to connect to on the remote server, eg, 80
|
||||
|
@ -461,16 +464,33 @@ lws_client_connect_extended(struct lws_context *context, const char *address,
|
|||
const char *protocol, int ietf_version_or_minus_one,
|
||||
void *userdata)
|
||||
{
|
||||
struct lws *wsi;
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
wsi = lws_client_connect(context, address, port, ssl_connection, path,
|
||||
host, origin, protocol,
|
||||
ietf_version_or_minus_one);
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
if (wsi && !wsi->user_space && userdata) {
|
||||
wsi->user_space_externally_allocated = 1;
|
||||
wsi->user_space = userdata ;
|
||||
}
|
||||
i.context = context;
|
||||
i.address = address;
|
||||
i.port = port;
|
||||
i.ssl_connection = ssl_connection;
|
||||
i.path = path;
|
||||
i.host = host;
|
||||
i.origin = origin;
|
||||
i.protocol = protocol;
|
||||
i.ietf_version_or_minus_one = ietf_version_or_minus_one;
|
||||
i.userdata = userdata;
|
||||
|
||||
return wsi;
|
||||
return lws_client_connect_info(&i);
|
||||
}
|
||||
|
||||
/* deprecated use lws_client_connect_info */
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_client_connect(struct lws_context *context, const char *address,
|
||||
int port, int ssl_connection, const char *path,
|
||||
const char *host, const char *origin,
|
||||
const char *protocol, int ietf_version_or_minus_one)
|
||||
{
|
||||
return lws_client_connect_extended(context, address, port, ssl_connection, path,
|
||||
host, origin, protocol,
|
||||
ietf_version_or_minus_one, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,29 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
unsigned short close_code;
|
||||
unsigned char *pp;
|
||||
struct lws_tokens eff_buf;
|
||||
int handled, m;
|
||||
int handled, n, m, rx_draining_ext = 0;
|
||||
|
||||
|
||||
if (wsi->u.ws.rx_draining_ext) {
|
||||
struct lws **w = &wsi->context->rx_draining_ext_list;
|
||||
lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__, c);
|
||||
assert(!c);
|
||||
eff_buf.token = NULL;
|
||||
eff_buf.token_len = 0;
|
||||
wsi->u.ws.rx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.rx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.rx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.rx_draining_ext_list = NULL;
|
||||
rx_draining_ext = 1;
|
||||
|
||||
goto drain_extension;
|
||||
}
|
||||
|
||||
switch (wsi->lws_rx_parse_state) {
|
||||
case LWS_RXPS_NEW:
|
||||
|
@ -40,6 +62,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
/* revisit if an extension wants them... */
|
||||
switch (wsi->u.ws.opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
wsi->u.ws.rsv_first_msg = (c & 0x70);
|
||||
wsi->u.ws.continuation_possible = 1;
|
||||
wsi->u.ws.check_utf8 =
|
||||
!!(wsi->context->options &
|
||||
|
@ -47,6 +70,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
wsi->u.ws.utf8 = 0;
|
||||
break;
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->u.ws.rsv_first_msg = (c & 0x70);
|
||||
wsi->u.ws.check_utf8 = 0;
|
||||
wsi->u.ws.continuation_possible = 1;
|
||||
break;
|
||||
|
@ -80,13 +104,14 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
/* revisit if an extension wants them... */
|
||||
if (
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
!wsi->count_active_extensions &&
|
||||
!wsi->count_act_ext &&
|
||||
#endif
|
||||
wsi->u.ws.rsv) {
|
||||
lwsl_info("illegal rsv bits set\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->u.ws.final = !!((c >> 7) & 1);
|
||||
lwsl_ext("%s: This RX frame Final %d\n", __func__, wsi->u.ws.final);
|
||||
|
||||
if (wsi->u.ws.owed_a_fin &&
|
||||
(wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME ||
|
||||
|
@ -165,6 +190,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
|
||||
wsi->u.ws.rx_packet_length |= c;
|
||||
//lwsl_err("&&&&& packet length %d\n", wsi->u.ws.rx_packet_length);
|
||||
if (wsi->u.ws.this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
|
@ -246,28 +272,28 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
|
||||
wsi->u.ws.mask_nonce[0] = c;
|
||||
wsi->u.ws.mask[0] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
|
||||
wsi->u.ws.mask_nonce[1] = c;
|
||||
wsi->u.ws.mask[1] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
|
||||
wsi->u.ws.mask_nonce[2] = c;
|
||||
wsi->u.ws.mask[2] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
|
||||
wsi->u.ws.mask_nonce[3] = c;
|
||||
wsi->u.ws.mask[3] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
|
||||
|
@ -282,17 +308,12 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
|
||||
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
|
||||
|
||||
if (!wsi->u.ws.rx_user_buffer) {
|
||||
lwsl_err("NULL client rx_user_buffer\n");
|
||||
return 1;
|
||||
}
|
||||
assert(wsi->u.ws.rx_ubuf);
|
||||
|
||||
if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce)
|
||||
c ^= wsi->u.ws.mask_nonce[
|
||||
(wsi->u.ws.frame_mask_index++) & 3];
|
||||
c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
|
||||
|
||||
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
|
||||
(wsi->u.ws.rx_user_buffer_head++)] = c;
|
||||
wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c;
|
||||
|
||||
if (--wsi->u.ws.rx_packet_length == 0) {
|
||||
/* spill because we have the whole frame */
|
||||
|
@ -304,13 +325,12 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to LWS_MAX_SOCKET_IO_BUF
|
||||
*/
|
||||
|
||||
if (!wsi->protocol->rx_buffer_size &&
|
||||
wsi->u.ws.rx_user_buffer_head != LWS_MAX_SOCKET_IO_BUF)
|
||||
wsi->u.ws.rx_ubuf_head != LWS_MAX_SOCKET_IO_BUF)
|
||||
break;
|
||||
|
||||
if (wsi->protocol->rx_buffer_size &&
|
||||
wsi->u.ws.rx_user_buffer_head != wsi->protocol->rx_buffer_size)
|
||||
wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
|
||||
break;
|
||||
|
||||
/* spill because we filled our rx buffer */
|
||||
|
@ -325,12 +345,12 @@ spill:
|
|||
|
||||
switch (wsi->u.ws.opcode) {
|
||||
case LWSWSOPC_CLOSE:
|
||||
pp = (unsigned char *)&wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING];
|
||||
pp = (unsigned char *)&wsi->u.ws.rx_ubuf[
|
||||
LWS_PRE];
|
||||
if (wsi->context->options & LWS_SERVER_OPTION_VALIDATE_UTF8 &&
|
||||
wsi->u.ws.rx_user_buffer_head > 2 &&
|
||||
wsi->u.ws.rx_ubuf_head > 2 &&
|
||||
lws_check_utf8(&wsi->u.ws.utf8, pp + 2,
|
||||
wsi->u.ws.rx_user_buffer_head - 2))
|
||||
wsi->u.ws.rx_ubuf_head - 2))
|
||||
goto utf8_fail;
|
||||
|
||||
/* is this an acknowledgement of our close? */
|
||||
|
@ -344,8 +364,8 @@ spill:
|
|||
}
|
||||
|
||||
lwsl_parser("client sees server close len = %d\n",
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
if (wsi->u.ws.rx_user_buffer_head >= 2) {
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
if (wsi->u.ws.rx_ubuf_head >= 2) {
|
||||
close_code = (pp[0] << 8) | pp[1];
|
||||
if (close_code < 1000 || close_code == 1004 ||
|
||||
close_code == 1005 || close_code == 1006 ||
|
||||
|
@ -361,7 +381,7 @@ spill:
|
|||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
wsi->user_space, pp,
|
||||
wsi->u.ws.rx_user_buffer_head))
|
||||
wsi->u.ws.rx_ubuf_head))
|
||||
return -1;
|
||||
/*
|
||||
* parrot the close packet payload back
|
||||
|
@ -369,16 +389,16 @@ spill:
|
|||
* immediately afterwards
|
||||
*/
|
||||
lws_write(wsi, (unsigned char *)
|
||||
&wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
|
||||
&wsi->u.ws.rx_ubuf[
|
||||
LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head, LWS_WRITE_CLOSE);
|
||||
wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
|
||||
/* close the connection */
|
||||
return -1;
|
||||
|
||||
case LWSWSOPC_PING:
|
||||
lwsl_info("received %d byte ping, sending pong\n",
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
/* he set a close reason on this guy, ignore PING */
|
||||
if (wsi->u.ws.close_in_ping_buffer_len)
|
||||
|
@ -394,30 +414,30 @@ spill:
|
|||
}
|
||||
|
||||
/* control packets can only be < 128 bytes long */
|
||||
if (wsi->u.ws.rx_user_buffer_head > 128 - 3) {
|
||||
if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
|
||||
lwsl_parser("DROP PING payload too large\n");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* stash the pong payload */
|
||||
memcpy(wsi->u.ws.ping_payload_buf + LWS_SEND_BUFFER_PRE_PADDING,
|
||||
&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
|
||||
&wsi->u.ws.rx_ubuf[LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_user_buffer_head;
|
||||
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
|
||||
wsi->u.ws.ping_pending_flag = 1;
|
||||
|
||||
/* get it sent as soon as possible */
|
||||
lws_callback_on_writable(wsi);
|
||||
ping_drop:
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
handled = 1;
|
||||
break;
|
||||
|
||||
case LWSWSOPC_PONG:
|
||||
lwsl_info("client receied pong\n");
|
||||
lwsl_hexdump(&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
/* issue it */
|
||||
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
|
||||
|
@ -438,17 +458,17 @@ ping_drop:
|
|||
* state machine.
|
||||
*/
|
||||
|
||||
eff_buf.token = &wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING];
|
||||
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
|
||||
eff_buf.token = &wsi->u.ws.rx_ubuf[
|
||||
LWS_PRE];
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
|
||||
&eff_buf, 0) <= 0) { /* not handle or fail */
|
||||
|
||||
lwsl_ext("Unhandled ext opc 0x%x\n",
|
||||
wsi->u.ws.opcode);
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -464,14 +484,22 @@ ping_drop:
|
|||
if (handled)
|
||||
goto already_done;
|
||||
|
||||
eff_buf.token = &wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING];
|
||||
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
|
||||
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_PAYLOAD_RX,
|
||||
&eff_buf, 0) < 0) /* fail */
|
||||
drain_extension:
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
|
||||
lwsl_ext("Ext RX returned %d\n", n);
|
||||
if (n < 0) /* fail */
|
||||
return -1;
|
||||
|
||||
lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len);
|
||||
|
||||
if (rx_draining_ext && !eff_buf.token_len) {
|
||||
lwsl_err(" --- ignoring zero drain result, ending drain\n");
|
||||
goto already_done;
|
||||
}
|
||||
|
||||
if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) {
|
||||
if (lws_check_utf8(&wsi->u.ws.utf8,
|
||||
(unsigned char *)eff_buf.token,
|
||||
|
@ -479,7 +507,8 @@ ping_drop:
|
|||
goto utf8_fail;
|
||||
|
||||
/* we are ending partway through utf-8 character? */
|
||||
if (wsi->u.ws.final && wsi->u.ws.utf8) {
|
||||
if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final && wsi->u.ws.utf8 && !n) {
|
||||
lwsl_info("FINAL utf8 error\n");
|
||||
utf8_fail: lwsl_info("utf8 error\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -489,8 +518,10 @@ utf8_fail: lwsl_info("utf8 error\n");
|
|||
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
||||
goto already_done;
|
||||
|
||||
if (eff_buf.token)
|
||||
eff_buf.token[eff_buf.token_len] = '\0';
|
||||
if (!eff_buf.token)
|
||||
goto already_done;
|
||||
|
||||
eff_buf.token[eff_buf.token_len] = '\0';
|
||||
|
||||
if (!wsi->protocol->callback)
|
||||
goto already_done;
|
||||
|
@ -498,6 +529,21 @@ utf8_fail: lwsl_info("utf8 error\n");
|
|||
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
|
||||
lwsl_info("Client doing pong callback\n");
|
||||
|
||||
if (n && eff_buf.token_len) {
|
||||
/* extension had more... main loop will come back
|
||||
* we want callback to be done with this set, if so,
|
||||
* because lws_is_final() hides it was final until the
|
||||
* last chunk
|
||||
*/
|
||||
wsi->u.ws.rx_draining_ext = 1;
|
||||
wsi->u.ws.rx_draining_ext_list = wsi->context->rx_draining_ext_list;
|
||||
wsi->context->rx_draining_ext_list = wsi;
|
||||
lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
|
||||
}
|
||||
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
|
||||
wsi->state == LWSS_AWAITING_CLOSE_ACK)
|
||||
goto already_done;
|
||||
|
||||
m = wsi->protocol->callback(wsi,
|
||||
(enum lws_callback_reasons)callback_action,
|
||||
wsi->user_space, eff_buf.token, eff_buf.token_len);
|
||||
|
@ -507,7 +553,7 @@ utf8_fail: lwsl_info("utf8 error\n");
|
|||
return 1;
|
||||
|
||||
already_done:
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("client rx illegal state\n");
|
||||
|
|
121
lib/client.c
121
lib/client.c
|
@ -23,9 +23,7 @@
|
|||
|
||||
int lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
unsigned int n;
|
||||
|
||||
lwsl_debug("%s: len %u\n", __func__, len);
|
||||
int m;
|
||||
|
||||
switch (wsi->mode) {
|
||||
case LWSCM_WSCL_WAITING_PROXY_REPLY:
|
||||
|
@ -33,24 +31,30 @@ int lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
case LWSCM_WSCL_WAITING_SERVER_REPLY:
|
||||
case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
|
||||
case LWSCM_WS_CLIENT:
|
||||
for (n = 0; n < len; n++) {
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
lwsl_debug("%s: caching %d\n", __func__, len - n);
|
||||
lws_rxflow_cache(wsi, *buf, 0, len - n);
|
||||
lwsl_debug("%s: caching %d\n", __func__, len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wsi->u.ws.rx_draining_ext) {
|
||||
m = lws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
|
||||
if (lws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_debug("client_rx_sm failed\n");
|
||||
return 1;
|
||||
lwsl_debug("client_rx_sm exited\n");
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
lwsl_debug("%s: finished with %d\n", __func__, len);
|
||||
return 0;
|
||||
|
@ -58,8 +62,6 @@ int lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
break;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: did nothing\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -79,7 +81,7 @@ int lws_client_socket_service(struct lws_context *context,
|
|||
* timeout protection set in client-handshake.c
|
||||
*/
|
||||
|
||||
if (lws_client_connect_2(wsi) == NULL) {
|
||||
if (!lws_client_connect_2(wsi)) {
|
||||
/* closed */
|
||||
lwsl_client("closed\n");
|
||||
return -1;
|
||||
|
@ -512,8 +514,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
char *p;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
const struct lws_extension *ext;
|
||||
const struct lws_ext_options *opts;
|
||||
char ext_name[128];
|
||||
const char *c;
|
||||
const char *c, *a;
|
||||
char ignore;
|
||||
int more = 1;
|
||||
void *v;
|
||||
|
@ -650,15 +653,20 @@ check_extensions:
|
|||
c = (char *)context->serv_buf;
|
||||
n = 0;
|
||||
ignore = 0;
|
||||
a = NULL;
|
||||
while (more) {
|
||||
|
||||
if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
|
||||
if (*c == ';')
|
||||
if (*c && (*c != ',' && *c != '\t')) {
|
||||
if (*c == ';') {
|
||||
ignore = 1;
|
||||
if (ignore) {
|
||||
if (!a)
|
||||
a = c + 1;
|
||||
}
|
||||
if (ignore || *c == ' ') {
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ext_name[n] = *c++;
|
||||
if (n < sizeof(ext_name) - 1)
|
||||
n++;
|
||||
|
@ -676,7 +684,7 @@ check_extensions:
|
|||
|
||||
/* check we actually support it */
|
||||
|
||||
lwsl_ext("checking client ext %s\n", ext_name);
|
||||
lwsl_notice("checking client ext %s\n", ext_name);
|
||||
|
||||
n = 0;
|
||||
ext = lws_get_context(wsi)->extensions;
|
||||
|
@ -687,30 +695,56 @@ check_extensions:
|
|||
}
|
||||
|
||||
n = 1;
|
||||
lwsl_ext("instantiating client ext %s\n", ext_name);
|
||||
lwsl_notice("instantiating client ext %s\n", ext_name);
|
||||
|
||||
/* instantiate the extension on this conn */
|
||||
|
||||
wsi->active_extensions_user[
|
||||
wsi->count_active_extensions] =
|
||||
lws_zalloc(ext->per_session_data_size);
|
||||
if (wsi->active_extensions_user[
|
||||
wsi->count_active_extensions] == NULL) {
|
||||
lwsl_err("Out of mem\n");
|
||||
goto bail2;
|
||||
}
|
||||
wsi->active_extensions[
|
||||
wsi->count_active_extensions] = ext;
|
||||
wsi->active_extensions[wsi->count_act_ext] = ext;
|
||||
|
||||
/* allow him to construct his context */
|
||||
/* allow him to construct his ext instance */
|
||||
|
||||
ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
|
||||
wsi->active_extensions_user[
|
||||
wsi->count_active_extensions],
|
||||
NULL, 0);
|
||||
LWS_EXT_CB_CLIENT_CONSTRUCT,
|
||||
(void *)&wsi->act_ext_user[wsi->count_act_ext],
|
||||
(void *)&opts, 0);
|
||||
|
||||
wsi->count_active_extensions++;
|
||||
/*
|
||||
* allow the user code to override ext defaults if it
|
||||
* wants to
|
||||
*/
|
||||
ext_name[0] = '\0';
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_WS_EXT_DEFAULTS,
|
||||
(char *)ext->name, ext_name,
|
||||
sizeof(ext_name)))
|
||||
goto bail2;
|
||||
|
||||
if (ext_name[0] &&
|
||||
lws_ext_parse_options(ext, wsi, wsi->act_ext_user[
|
||||
wsi->count_act_ext], opts, ext_name,
|
||||
strlen(ext_name))) {
|
||||
lwsl_err("%s: unable to parse user defaults '%s'",
|
||||
__func__, ext_name);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/*
|
||||
* give the extension the server options
|
||||
*/
|
||||
if (a && lws_ext_parse_options(ext, wsi, wsi->act_ext_user[wsi->count_act_ext], opts, a, c - a)) {
|
||||
lwsl_err("%s: unable to parse remote defaults '%s'", __func__, a);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
if (ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CB_OPTION_CONFIRM,
|
||||
wsi->act_ext_user[wsi->count_act_ext],
|
||||
NULL, 0)) {
|
||||
lwsl_err("%s: ext %s rejects server options %s", ext->name, a);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
wsi->count_act_ext++;
|
||||
|
||||
ext++;
|
||||
}
|
||||
|
@ -720,6 +754,7 @@ check_extensions:
|
|||
goto bail2;
|
||||
}
|
||||
|
||||
a = NULL;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
|
@ -772,9 +807,9 @@ check_accept:
|
|||
n = wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = LWS_MAX_SOCKET_IO_BUF;
|
||||
n += LWS_SEND_BUFFER_PRE_PADDING;
|
||||
wsi->u.ws.rx_user_buffer = lws_malloc(n);
|
||||
if (!wsi->u.ws.rx_user_buffer) {
|
||||
n += LWS_PRE;
|
||||
wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
|
||||
if (!wsi->u.ws.rx_ubuf) {
|
||||
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
||||
goto bail2;
|
||||
}
|
||||
|
@ -803,12 +838,12 @@ check_accept:
|
|||
|
||||
while (ext && ext->callback) {
|
||||
v = NULL;
|
||||
for (n = 0; n < wsi->count_active_extensions; n++)
|
||||
for (n = 0; n < wsi->count_act_ext; n++)
|
||||
if (wsi->active_extensions[n] == ext)
|
||||
v = wsi->active_extensions_user[n];
|
||||
v = wsi->act_ext_user[n];
|
||||
|
||||
ext->callback(context, ext, wsi,
|
||||
LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
|
||||
ext++;
|
||||
}
|
||||
#endif
|
||||
|
@ -816,7 +851,7 @@ check_accept:
|
|||
return 0;
|
||||
|
||||
bail3:
|
||||
lws_free_set_NULL(wsi->u.ws.rx_user_buffer);
|
||||
lws_free_set_NULL(wsi->u.ws.rx_ubuf);
|
||||
close_reason = LWS_CLOSE_STATUS_NOSTATUS;
|
||||
|
||||
bail2:
|
||||
|
@ -923,7 +958,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
while (ext && ext->callback) {
|
||||
|
||||
n = lws_ext_cb_all_exts(context, wsi,
|
||||
LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
|
||||
(char *)ext->name, 0);
|
||||
if (n) { /* an extension vetos us */
|
||||
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
|
||||
|
@ -951,7 +986,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
|
||||
if (ext_count)
|
||||
*p++ = ',';
|
||||
p += sprintf(p, "%s", ext->name);
|
||||
p += sprintf(p, "%s", ext->client_offer);
|
||||
ext_count++;
|
||||
|
||||
ext++;
|
||||
|
|
|
@ -262,11 +262,11 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
*/
|
||||
if (info->port != CONTEXT_PORT_NO_LISTEN) {
|
||||
if (lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
goto bail;
|
||||
} else
|
||||
if (lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
goto bail;
|
||||
|
||||
return context;
|
||||
|
@ -319,10 +319,10 @@ lws_context_destroy(struct lws_context *context)
|
|||
*/
|
||||
|
||||
n = lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0);
|
||||
LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
|
||||
|
||||
n = lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
|
||||
|
||||
/*
|
||||
* inform all the protocols that they are done and will have no more
|
||||
|
|
|
@ -1,284 +0,0 @@
|
|||
#include "private-libwebsockets.h"
|
||||
#include "extension-deflate-frame.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LWS_ZLIB_WINDOW_BITS 15
|
||||
#define LWS_ZLIB_MEMLEVEL 8
|
||||
|
||||
int lws_extension_callback_deflate_frame(
|
||||
struct lws_context *context,
|
||||
const struct lws_extension *ext,
|
||||
struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct lws_ext_deflate_frame_conn *conn =
|
||||
(struct lws_ext_deflate_frame_conn *)user;
|
||||
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
|
||||
size_t current_payload, remaining_payload, total_payload;
|
||||
int n;
|
||||
size_t len_so_far;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/*
|
||||
* for deflate-frame, both client and server sides act the same
|
||||
*/
|
||||
|
||||
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
|
||||
case LWS_EXT_CALLBACK_CONSTRUCT:
|
||||
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
|
||||
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
|
||||
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
|
||||
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
|
||||
if (n != Z_OK) {
|
||||
lwsl_ext("deflateInit returned %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
n = deflateInit2(&conn->zs_out,
|
||||
(context->listen_port ?
|
||||
DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER :
|
||||
DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT),
|
||||
Z_DEFLATED,
|
||||
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (n != Z_OK) {
|
||||
lwsl_ext("deflateInit2 returned %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
conn->buf_pre_used = 0;
|
||||
conn->buf_pre_length = 0;
|
||||
conn->buf_in_length = sizeof(conn->buf_in);
|
||||
conn->buf_out_length = sizeof(conn->buf_out);
|
||||
conn->compressed_out = 0;
|
||||
conn->buf_pre = NULL;
|
||||
conn->buf_in = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
|
||||
conn->buf_in_length);
|
||||
if (!conn->buf_in)
|
||||
goto bail;
|
||||
conn->buf_out = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
|
||||
conn->buf_out_length);
|
||||
if (!conn->buf_out)
|
||||
goto bail;
|
||||
lwsl_ext("zlibs constructed\n");
|
||||
break;
|
||||
bail:
|
||||
lwsl_err("Out of mem\n");
|
||||
(void)inflateEnd(&conn->zs_in);
|
||||
(void)deflateEnd(&conn->zs_out);
|
||||
return -1;
|
||||
|
||||
case LWS_EXT_CALLBACK_DESTROY:
|
||||
lws_free(conn->buf_pre);
|
||||
lws_free(conn->buf_in);
|
||||
lws_free(conn->buf_out);
|
||||
conn->buf_pre_used = 0;
|
||||
conn->buf_pre_length = 0;
|
||||
conn->buf_in_length = 0;
|
||||
conn->buf_out_length = 0;
|
||||
conn->compressed_out = 0;
|
||||
(void)inflateEnd(&conn->zs_in);
|
||||
(void)deflateEnd(&conn->zs_out);
|
||||
lwsl_ext("zlibs destructed\n");
|
||||
break;
|
||||
|
||||
case LWS_EXT_CALLBACK_PAYLOAD_RX:
|
||||
if (!(wsi->u.ws.rsv & 0x40))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* inflate the incoming payload
|
||||
*/
|
||||
current_payload = eff_buf->token_len;
|
||||
|
||||
remaining_payload = wsi->u.ws.rx_packet_length;
|
||||
if (remaining_payload) {
|
||||
total_payload = conn->buf_pre_used +
|
||||
current_payload +
|
||||
remaining_payload;
|
||||
|
||||
if (conn->buf_pre_length < total_payload) {
|
||||
conn->buf_pre_length = total_payload;
|
||||
lws_free(conn->buf_pre);
|
||||
conn->buf_pre = lws_malloc(total_payload + 4);
|
||||
if (!conn->buf_pre) {
|
||||
lwsl_err("Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(conn->buf_pre + conn->buf_pre_used,
|
||||
eff_buf->token, current_payload);
|
||||
conn->buf_pre_used += current_payload;
|
||||
|
||||
eff_buf->token = NULL;
|
||||
eff_buf->token_len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (conn->buf_pre_used) {
|
||||
total_payload = conn->buf_pre_used +
|
||||
current_payload;
|
||||
|
||||
memcpy(conn->buf_pre + conn->buf_pre_used,
|
||||
eff_buf->token, current_payload);
|
||||
conn->buf_pre_used = 0;
|
||||
|
||||
conn->zs_in.next_in = conn->buf_pre;
|
||||
} else {
|
||||
total_payload = current_payload;
|
||||
|
||||
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
|
||||
}
|
||||
|
||||
conn->zs_in.next_in[total_payload + 0] = 0;
|
||||
conn->zs_in.next_in[total_payload + 1] = 0;
|
||||
conn->zs_in.next_in[total_payload + 2] = 0xff;
|
||||
conn->zs_in.next_in[total_payload + 3] = 0xff;
|
||||
|
||||
conn->zs_in.avail_in = total_payload + 4;
|
||||
|
||||
conn->zs_in.next_out =
|
||||
conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
|
||||
conn->zs_in.avail_out = conn->buf_in_length;
|
||||
|
||||
while (1) {
|
||||
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
/*
|
||||
* screwed.. close the connection...
|
||||
* we will get a destroy callback to take care
|
||||
* of closing nicely
|
||||
*/
|
||||
lwsl_info("zlib error inflate %d: %s\n",
|
||||
n, conn->zs_in.msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->zs_in.avail_out)
|
||||
break;
|
||||
|
||||
len_so_far = conn->zs_in.next_out -
|
||||
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
|
||||
|
||||
conn->buf_in_length *= 2;
|
||||
if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) {
|
||||
lwsl_ext("zlib in buffer hit limit %u\n",
|
||||
LWS_MAX_ZLIB_CONN_BUFFER);
|
||||
return -1;
|
||||
}
|
||||
conn->buf_in = lws_realloc(conn->buf_in,
|
||||
LWS_SEND_BUFFER_PRE_PADDING +
|
||||
conn->buf_in_length);
|
||||
if (!conn->buf_in) {
|
||||
lwsl_err("Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
lwsl_debug(
|
||||
"deflate-frame ext RX did realloc to %ld\n",
|
||||
conn->buf_in_length);
|
||||
conn->zs_in.next_out = conn->buf_in +
|
||||
LWS_SEND_BUFFER_PRE_PADDING + len_so_far;
|
||||
conn->zs_in.avail_out =
|
||||
conn->buf_in_length - len_so_far;
|
||||
}
|
||||
|
||||
/* rewrite the buffer pointers and length */
|
||||
eff_buf->token =
|
||||
(char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
|
||||
eff_buf->token_len = (int)(conn->zs_in.next_out -
|
||||
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
|
||||
|
||||
return 0;
|
||||
|
||||
case LWS_EXT_CALLBACK_PAYLOAD_TX:
|
||||
/*
|
||||
* deflate the outgoing payload
|
||||
*/
|
||||
current_payload = eff_buf->token_len;
|
||||
|
||||
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
|
||||
conn->zs_out.avail_in = current_payload;
|
||||
|
||||
conn->zs_out.next_out =
|
||||
conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
|
||||
conn->zs_out.avail_out = conn->buf_out_length;
|
||||
|
||||
while (1) {
|
||||
n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
/*
|
||||
* screwed.. close the connection... we will
|
||||
* get a destroy callback to take care of
|
||||
* closing nicely
|
||||
*/
|
||||
lwsl_ext("zlib error deflate\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn->zs_out.avail_out)
|
||||
break;
|
||||
|
||||
len_so_far = (conn->zs_out.next_out -
|
||||
(conn->buf_out +
|
||||
LWS_SEND_BUFFER_PRE_PADDING));
|
||||
conn->buf_out_length *= 2;
|
||||
if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) {
|
||||
lwsl_ext("zlib out hit limit %u\n",
|
||||
LWS_MAX_ZLIB_CONN_BUFFER);
|
||||
return -1;
|
||||
}
|
||||
conn->buf_out = lws_realloc(conn->buf_out,
|
||||
LWS_SEND_BUFFER_PRE_PADDING +
|
||||
conn->buf_out_length);
|
||||
if (!conn->buf_out) {
|
||||
lwsl_err("Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
lwsl_debug(
|
||||
"deflate-frame ext TX did realloc to %ld\n",
|
||||
conn->buf_out_length);
|
||||
|
||||
conn->zs_out.next_out = (conn->buf_out +
|
||||
LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
|
||||
conn->zs_out.avail_out =
|
||||
(conn->buf_out_length - len_so_far);
|
||||
}
|
||||
|
||||
conn->compressed_out = 1;
|
||||
|
||||
/* rewrite the buffer pointers and length */
|
||||
eff_buf->token = (char *)(conn->buf_out +
|
||||
LWS_SEND_BUFFER_PRE_PADDING);
|
||||
eff_buf->token_len = (int)(conn->zs_out.next_out -
|
||||
(conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
|
||||
|
||||
return 0;
|
||||
|
||||
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
|
||||
if (conn->compressed_out) {
|
||||
conn->compressed_out = 0;
|
||||
*((unsigned char *)eff_buf->token) |= 0x40;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
|
||||
/* Avoid x-webkit-deflate-frame extension on client */
|
||||
if (!strcmp((char *)in, "x-webkit-deflate-frame"))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
#include <zlib.h>
|
||||
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
|
||||
|
||||
struct lws_ext_deflate_frame_conn {
|
||||
z_stream zs_in;
|
||||
z_stream zs_out;
|
||||
size_t buf_pre_used;
|
||||
size_t buf_pre_length;
|
||||
size_t buf_in_length;
|
||||
size_t buf_out_length;
|
||||
int compressed_out;
|
||||
unsigned char *buf_pre;
|
||||
unsigned char *buf_in;
|
||||
unsigned char *buf_out;
|
||||
};
|
||||
|
||||
extern int lws_extension_callback_deflate_frame(
|
||||
struct lws_context *context, const struct lws_extension *ext,
|
||||
struct lws *wsi, enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
|
@ -1,166 +0,0 @@
|
|||
#include "private-libwebsockets.h"
|
||||
#include "extension-deflate-stream.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LWS_ZLIB_WINDOW_BITS 15
|
||||
#define LWS_ZLIB_MEMLEVEL 8
|
||||
|
||||
int
|
||||
lws_extension_callback_deflate_stream(struct lws_context *context,
|
||||
const struct lws_extension *ext,
|
||||
struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct lws_ext_deflate_stream_conn *conn =
|
||||
(struct lws_ext_deflate_stream_conn *)user;
|
||||
int n;
|
||||
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
/*
|
||||
* for deflate-stream, both client and server sides act the same
|
||||
*/
|
||||
|
||||
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
|
||||
case LWS_EXT_CALLBACK_CONSTRUCT:
|
||||
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
|
||||
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
|
||||
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
|
||||
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
|
||||
if (n != Z_OK) {
|
||||
lwsl_err("deflateInit returned %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
n = deflateInit2(&conn->zs_out,
|
||||
DEFLATE_STREAM_COMPRESSION_LEVEL, Z_DEFLATED,
|
||||
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if (n != Z_OK) {
|
||||
lwsl_err("deflateInit returned %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
lwsl_ext("zlibs constructed\n");
|
||||
conn->remaining_in = 0;
|
||||
break;
|
||||
|
||||
case LWS_EXT_CALLBACK_DESTROY:
|
||||
(void)inflateEnd(&conn->zs_in);
|
||||
(void)deflateEnd(&conn->zs_out);
|
||||
lwsl_ext("zlibs destructed\n");
|
||||
break;
|
||||
|
||||
case LWS_EXT_CALLBACK_PACKET_RX_PREPARSE:
|
||||
|
||||
/*
|
||||
* inflate the incoming compressed data
|
||||
* Notice, length may be 0 and pointer NULL
|
||||
* in the case we are flushing with nothing new coming in
|
||||
*/
|
||||
if (conn->remaining_in) {
|
||||
conn->zs_in.next_in = conn->buf_in;
|
||||
conn->zs_in.avail_in = conn->remaining_in;
|
||||
conn->remaining_in = 0;
|
||||
} else {
|
||||
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
|
||||
conn->zs_in.avail_in = eff_buf->token_len;
|
||||
}
|
||||
|
||||
conn->zs_in.next_out = conn->buf_out;
|
||||
conn->zs_in.avail_out = sizeof(conn->buf_out);
|
||||
|
||||
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
/*
|
||||
* screwed.. close the connection... we will get a
|
||||
* destroy callback to take care of closing nicely
|
||||
*/
|
||||
lwsl_err("zlib error inflate %d\n", n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* rewrite the buffer pointers and length */
|
||||
|
||||
eff_buf->token = (char *)conn->buf_out;
|
||||
eff_buf->token_len =
|
||||
sizeof(conn->buf_out) - conn->zs_in.avail_out;
|
||||
|
||||
/* copy avail data if not consumed */
|
||||
if (conn->zs_in.avail_in > 0) {
|
||||
conn->remaining_in = conn->zs_in.avail_in;
|
||||
memcpy(conn->buf_in, conn->zs_in.next_in,
|
||||
conn->zs_in.avail_in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we filled the output buffer, signal that we likely have
|
||||
* more and need to be called again
|
||||
*/
|
||||
|
||||
if (eff_buf->token_len == sizeof(conn->buf_out))
|
||||
return 1;
|
||||
|
||||
/* we don't need calling again until new input data comes */
|
||||
|
||||
return 0;
|
||||
|
||||
case LWS_EXT_CALLBACK_FLUSH_PENDING_TX:
|
||||
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
|
||||
|
||||
/*
|
||||
* deflate the outgoing compressed data
|
||||
*/
|
||||
|
||||
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
|
||||
conn->zs_out.avail_in = eff_buf->token_len;
|
||||
|
||||
conn->zs_out.next_out = conn->buf_out;
|
||||
conn->zs_out.avail_out = sizeof(conn->buf_out);
|
||||
|
||||
n = Z_PARTIAL_FLUSH;
|
||||
if (reason == LWS_EXT_CALLBACK_FLUSH_PENDING_TX)
|
||||
n = Z_FULL_FLUSH;
|
||||
|
||||
n = deflate(&conn->zs_out, n);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
/*
|
||||
* screwed.. close the connection... we will get a
|
||||
* destroy callback to take care of closing nicely
|
||||
*/
|
||||
lwsl_ext("zlib error deflate\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* rewrite the buffer pointers and length */
|
||||
|
||||
eff_buf->token = (char *)conn->buf_out;
|
||||
eff_buf->token_len =
|
||||
sizeof(conn->buf_out) - conn->zs_out.avail_out;
|
||||
|
||||
/*
|
||||
* if we filled the output buffer, signal that we likely have
|
||||
* more and need to be called again... even in deflate case
|
||||
* we might sometimes need to spill more than came in
|
||||
*/
|
||||
|
||||
if (eff_buf->token_len == sizeof(conn->buf_out))
|
||||
return 1;
|
||||
|
||||
/* we don't need calling again until new input data comes */
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
#include <zlib.h>
|
||||
|
||||
#define DEFLATE_STREAM_CHUNK 128
|
||||
#define DEFLATE_STREAM_COMPRESSION_LEVEL 1
|
||||
|
||||
struct lws_ext_deflate_stream_conn {
|
||||
z_stream zs_in;
|
||||
z_stream zs_out;
|
||||
int remaining_in;
|
||||
unsigned char buf_in[LWS_MAX_SOCKET_IO_BUF];
|
||||
unsigned char buf_out[LWS_MAX_SOCKET_IO_BUF];
|
||||
};
|
||||
|
||||
extern int lws_extension_callback_deflate_stream(
|
||||
struct lws_context *context, const struct lws_extension *ext,
|
||||
struct lws *wsi, enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
411
lib/extension-permessage-deflate.c
Normal file
411
lib/extension-permessage-deflate.c
Normal file
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* ./lib/extension-permessage-deflate.c
|
||||
*
|
||||
* Copyright (C) 2016 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
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include "extension-permessage-deflate.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LWS_ZLIB_MEMLEVEL 8
|
||||
|
||||
const struct lws_ext_options lws_ext_pm_deflate_options[] = {
|
||||
/* public RFC7692 settings */
|
||||
{ "server_no_context_takeover", EXTARG_NONE },
|
||||
{ "client_no_context_takeover", EXTARG_NONE },
|
||||
{ "server_max_window_bits", EXTARG_OPT_DEC },
|
||||
{ "client_max_window_bits", EXTARG_OPT_DEC },
|
||||
/* ones only user code can set */
|
||||
{ "rx_buf_size", EXTARG_DEC },
|
||||
{ "tx_buf_size", EXTARG_DEC },
|
||||
{ "compression_level", EXTARG_DEC },
|
||||
{ "mem_level", EXTARG_DEC },
|
||||
{ NULL, 0 }, /* sentinel */
|
||||
};
|
||||
|
||||
int
|
||||
lws_extension_callback_pm_deflate(struct lws_context *context,
|
||||
const struct lws_extension *ext,
|
||||
struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct lws_ext_pm_deflate_priv *priv =
|
||||
(struct lws_ext_pm_deflate_priv *)user;
|
||||
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
|
||||
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
|
||||
int n, ret = 0, was_fin = 0, extra;
|
||||
struct lws_ext_option_arg *oa;
|
||||
|
||||
switch (reason) {
|
||||
case LWS_EXT_CB_OPTION_SET:
|
||||
oa = in;
|
||||
lwsl_info("%s: option set: idx %d, %s, len %d\n", __func__, oa->option_index, oa->start, oa->len);
|
||||
if (oa->start)
|
||||
priv->args[oa->option_index] = atoi(oa->start);
|
||||
else
|
||||
priv->args[oa->option_index] = 1;
|
||||
break;
|
||||
case LWS_EXT_CB_OPTION_CONFIRM:
|
||||
if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
|
||||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_CLIENT_CONSTRUCT:
|
||||
case LWS_EXT_CB_CONSTRUCT:
|
||||
/* fill in **user */
|
||||
priv = lws_zalloc(sizeof(*priv));
|
||||
*((void **)user) = priv;
|
||||
lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
|
||||
/* fill in pointer to options list */
|
||||
if (in)
|
||||
*((const struct lws_ext_options **)in) = lws_ext_pm_deflate_options;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LWS_EXT_CB_OPTION_DEFAULT:
|
||||
|
||||
/* set the public, RFC7692 defaults... */
|
||||
|
||||
priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
|
||||
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
|
||||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
|
||||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
|
||||
|
||||
/* ...and the ones the user code can override */
|
||||
|
||||
priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
|
||||
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
|
||||
priv->args[PMD_COMP_LEVEL] = 1;
|
||||
priv->args[PMD_MEM_LEVEL] = 8;
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_DESTROY:
|
||||
lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
|
||||
lws_free(priv->buf_rx_inflated);
|
||||
lws_free(priv->buf_tx_deflated);
|
||||
if (priv->rx_init)
|
||||
(void)inflateEnd(&priv->rx);
|
||||
if (priv->tx_init)
|
||||
(void)deflateEnd(&priv->tx);
|
||||
lws_free(priv);
|
||||
return ret;
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_RX:
|
||||
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
|
||||
__func__, eff_buf->token_len, priv->rx.avail_in);
|
||||
if (!(wsi->u.ws.rsv_first_msg & 0x40))
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
for (n = 0; n < eff_buf->token_len; n++) {
|
||||
printf("%02X ", (unsigned char)eff_buf->token[n]);
|
||||
if ((n & 15) == 15)
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
if (!priv->rx_init)
|
||||
if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
|
||||
lwsl_err("%s: iniflateInit failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
priv->rx_init = 1;
|
||||
if (!priv->buf_rx_inflated)
|
||||
priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_RX_BUF_PWR2]));
|
||||
if (!priv->buf_rx_inflated) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to leave the input stream alone if we didn't
|
||||
* finish with it yet. The input stream is held in the wsi
|
||||
* rx buffer by the caller, so this assumption is safe while
|
||||
* we block new rx while draining the existing rx
|
||||
*/
|
||||
if (eff_buf->token && eff_buf->token_len) {
|
||||
priv->rx.next_in = (unsigned char *)eff_buf->token;
|
||||
priv->rx.avail_in = eff_buf->token_len;
|
||||
}
|
||||
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
|
||||
eff_buf->token = (char *)priv->rx.next_out;
|
||||
priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
|
||||
|
||||
if (priv->rx_held_valid) {
|
||||
lwsl_ext("-- RX piling on held byte --\n");
|
||||
*(priv->rx.next_out++) = priv->rx_held;
|
||||
priv->rx.avail_out--;
|
||||
priv->rx_held_valid = 0;
|
||||
}
|
||||
|
||||
/* if...
|
||||
*
|
||||
* - he has no remaining input content for this message, and
|
||||
* - and this is the final fragment, and
|
||||
* - we used everything that could be drained on the input side
|
||||
*
|
||||
* ...then put back the 00 00 FF FF the sender stripped as our
|
||||
* input to zlib
|
||||
*/
|
||||
if (!priv->rx.avail_in && wsi->u.ws.final &&
|
||||
!wsi->u.ws.rx_packet_length) {
|
||||
lwsl_ext("RX APPEND_TRAILER-DO\n");
|
||||
was_fin = 1;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
}
|
||||
|
||||
n = inflate(&priv->rx, Z_NO_FLUSH);
|
||||
lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
|
||||
priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_info("zlib error inflate %d: %s\n",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* If we did not already send in the 00 00 FF FF, and he's
|
||||
* out of input, he did not EXACTLY fill the output buffer
|
||||
* (which is ambiguous and we will force it to go around
|
||||
* again by witholding a byte), and he's otherwise working on
|
||||
* being a FIN fragment, then do the FIN message processing
|
||||
* of faking up the 00 00 FF FF that the sender stripped.
|
||||
*/
|
||||
if (!priv->rx.avail_in && wsi->u.ws.final &&
|
||||
!wsi->u.ws.rx_packet_length && !was_fin &&
|
||||
priv->rx.avail_out /* ambiguous as to if it is the end */
|
||||
) {
|
||||
lwsl_ext("RX APPEND_TRAILER-DO\n");
|
||||
was_fin = 1;
|
||||
priv->rx.next_in = trail;
|
||||
priv->rx.avail_in = sizeof(trail);
|
||||
n = inflate(&priv->rx, Z_SYNC_FLUSH);
|
||||
lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
|
||||
priv->rx.avail_in, priv->rx.avail_out);
|
||||
switch (n) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_info("zlib error inflate %d: %s\n",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* we must announce in our returncode now if there is more
|
||||
* output to be expected from inflate, so we can decide to
|
||||
* set the FIN bit on this bufferload or not. However zlib
|
||||
* is ambiguous when we exactly filled the inflate buffer. It
|
||||
* does not give us a clue as to whether we should understand
|
||||
* that to mean he ended on a buffer boundary, or if there is
|
||||
* more in the pipeline.
|
||||
*
|
||||
* So to work around that safely, if it used all output space
|
||||
* exactly, we ALWAYS say there is more coming and we withold
|
||||
* the last byte of the buffer to guarantee that is true.
|
||||
*
|
||||
* That still leaves us at least one byte to finish with a FIN
|
||||
* on, even if actually nothing more is coming from the next
|
||||
* inflate action itself.
|
||||
*/
|
||||
if (!priv->rx.avail_out) { /* he used all available out buf */
|
||||
lwsl_ext("-- rx grabbing held --\n");
|
||||
/* snip the last byte and hold it for next time */
|
||||
priv->rx_held = *(--priv->rx.next_out);
|
||||
priv->rx_held_valid = 1;
|
||||
}
|
||||
|
||||
eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
|
||||
priv->count_rx_between_fin += eff_buf->token_len;
|
||||
|
||||
lwsl_ext(" %s: RX leaving with new effbuff len %d, "
|
||||
"ret %d, rx.avail_in=%d, TOTAL RX since FIN %d\n",
|
||||
__func__, eff_buf->token_len, priv->rx_held_valid,
|
||||
priv->rx.avail_in, priv->count_rx_between_fin);
|
||||
|
||||
if (was_fin) {
|
||||
priv->count_rx_between_fin = 0;
|
||||
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
||||
(void)inflateEnd(&priv->rx);
|
||||
priv->rx_init = 0;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
for (n = 0; n < eff_buf->token_len; n++)
|
||||
putchar(eff_buf->token[n]);
|
||||
puts("\n");
|
||||
#endif
|
||||
|
||||
return priv->rx_held_valid;
|
||||
|
||||
case LWS_EXT_CB_PAYLOAD_TX:
|
||||
|
||||
if (!priv->tx_init)
|
||||
if (deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
|
||||
Z_DEFLATED,
|
||||
-priv->args[PMD_CLIENT_MAX_WINDOW_BITS +
|
||||
!wsi->context->listen_port],
|
||||
priv->args[PMD_MEM_LEVEL],
|
||||
Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
lwsl_ext("inflateInit2 failed\n");
|
||||
return 1;
|
||||
}
|
||||
priv->tx_init = 1;
|
||||
if (!priv->buf_tx_deflated)
|
||||
priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
|
||||
(1 << priv->args[PMD_TX_BUF_PWR2]));
|
||||
if (!priv->buf_tx_deflated) {
|
||||
lwsl_err("%s: OOM\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (eff_buf->token) {
|
||||
lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
|
||||
eff_buf->token_len);
|
||||
priv->tx.next_in = (unsigned char *)eff_buf->token;
|
||||
priv->tx.avail_in = eff_buf->token_len;
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (n = 0; n < eff_buf->token_len; n++) {
|
||||
printf("%02X ", (unsigned char)eff_buf->token[n]);
|
||||
if ((n & 15) == 15)
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
|
||||
eff_buf->token = (char *)priv->tx.next_out;
|
||||
priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
|
||||
|
||||
n = deflate(&priv->tx, Z_SYNC_FLUSH);
|
||||
if (n == Z_STREAM_ERROR) {
|
||||
lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (priv->tx_held_valid) {
|
||||
priv->tx_held_valid = 0;
|
||||
if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
|
||||
/*
|
||||
* we can get a situation he took something in
|
||||
* but did not generate anything out, at the end
|
||||
* of a message (eg, next thing he sends is 80
|
||||
* 00, a zero length FIN, like Authobahn can
|
||||
* send).
|
||||
* If we have come back as a FIN, we must not
|
||||
* place the pending trailer 00 00 FF FF, just
|
||||
* the 1 byte of live data
|
||||
*/
|
||||
*(--eff_buf->token) = priv->tx_held[0];
|
||||
else {
|
||||
/* he generated data, prepend whole pending */
|
||||
eff_buf->token -= 5;
|
||||
for (n = 0; n < 5; n++)
|
||||
eff_buf->token[n] = priv->tx_held[n];
|
||||
|
||||
}
|
||||
}
|
||||
priv->compressed_out = 1;
|
||||
eff_buf->token_len = (int)(priv->tx.next_out -
|
||||
(unsigned char *)eff_buf->token);
|
||||
|
||||
/*
|
||||
* we must announce in our returncode now if there is more
|
||||
* output to be expected from inflate, so we can decide to
|
||||
* set the FIN bit on this bufferload or not. However zlib
|
||||
* is ambiguous when we exactly filled the inflate buffer. It
|
||||
* does not give us a clue as to whether we should understand
|
||||
* that to mean he ended on a buffer boundary, or if there is
|
||||
* more in the pipeline.
|
||||
*
|
||||
* Worse, the guy providing the stuff we are sending may not
|
||||
* know until after that this was, actually, the last chunk,
|
||||
* that can happen even if we did not fill the output buf, ie
|
||||
* he may send after this a zero-length FIN fragment.
|
||||
*
|
||||
* This is super difficult because we must snip the last 4
|
||||
* bytes in the case this is the last compressed output of the
|
||||
* message. The only way to deal with it is defer sending the
|
||||
* last 5 bytes of each frame until the next one, when we will
|
||||
* be in a position to understand if that has a FIN or not.
|
||||
*/
|
||||
|
||||
extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
|
||||
|
||||
if (eff_buf->token_len >= 4 + extra) {
|
||||
lwsl_ext("tx held %d\n", 4 + extra);
|
||||
priv->tx_held_valid = extra;
|
||||
for (n = 3 + extra; n >= 0; n--)
|
||||
priv->tx_held[n] = *(--priv->tx.next_out);
|
||||
eff_buf->token_len -= 4 + extra;
|
||||
}
|
||||
lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
|
||||
eff_buf->token_len, !priv->tx.avail_out);
|
||||
|
||||
return !priv->tx.avail_out; /* 1 == have more tx pending */
|
||||
|
||||
case LWS_EXT_CB_PACKET_TX_PRESEND:
|
||||
if (!priv->compressed_out)
|
||||
break;
|
||||
priv->compressed_out = 0;
|
||||
|
||||
if ((*(eff_buf->token) & 0x80) && priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
|
||||
(void)deflateEnd(&priv->tx);
|
||||
priv->tx_init = 0;
|
||||
}
|
||||
|
||||
n = *(eff_buf->token) & 15;
|
||||
/* set RSV1, but not on CONTINUATION */
|
||||
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
|
||||
*eff_buf->token |= 0x40;
|
||||
#if 0
|
||||
for (n = 0; n < eff_buf->token_len; n++) {
|
||||
printf("%02X ", (unsigned char)eff_buf->token[n]);
|
||||
if ((n & 15) == 15)
|
||||
puts("\n");
|
||||
}
|
||||
puts("\n");
|
||||
#endif
|
||||
lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
|
||||
(unsigned char)*eff_buf->token);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
41
lib/extension-permessage-deflate.h
Normal file
41
lib/extension-permessage-deflate.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include <zlib.h>
|
||||
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
|
||||
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
|
||||
|
||||
enum arg_indexes {
|
||||
PMD_SERVER_NO_CONTEXT_TAKEOVER,
|
||||
PMD_CLIENT_NO_CONTEXT_TAKEOVER,
|
||||
PMD_SERVER_MAX_WINDOW_BITS,
|
||||
PMD_CLIENT_MAX_WINDOW_BITS,
|
||||
PMD_RX_BUF_PWR2,
|
||||
PMD_TX_BUF_PWR2,
|
||||
PMD_COMP_LEVEL,
|
||||
PMD_MEM_LEVEL,
|
||||
|
||||
PMD_ARG_COUNT
|
||||
};
|
||||
|
||||
struct lws_ext_pm_deflate_priv {
|
||||
z_stream rx;
|
||||
z_stream tx;
|
||||
|
||||
unsigned char *buf_rx_inflated; /* RX inflated output buffer */
|
||||
unsigned char *buf_tx_deflated; /* TX deflated output buffer */
|
||||
|
||||
size_t count_rx_between_fin;
|
||||
|
||||
unsigned char args[PMD_ARG_COUNT];
|
||||
unsigned char tx_held[5];
|
||||
unsigned char rx_held;
|
||||
|
||||
unsigned char tx_init:1;
|
||||
unsigned char rx_init:1;
|
||||
unsigned char compressed_out:1;
|
||||
unsigned char rx_held_valid:1;
|
||||
unsigned char tx_held_valid:1;
|
||||
unsigned char rx_append_trailer:1;
|
||||
unsigned char pending_tx_trailer:1;
|
||||
};
|
||||
|
200
lib/extension.c
200
lib/extension.c
|
@ -1,31 +1,6 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
#include "extension-deflate-frame.h"
|
||||
#include "extension-deflate-stream.h"
|
||||
|
||||
struct lws_extension lws_internal_extensions[] = {
|
||||
#ifdef LWS_EXT_DEFLATE_STREAM
|
||||
{
|
||||
"deflate-stream",
|
||||
lws_extension_callback_deflate_stream,
|
||||
sizeof(struct lws_ext_deflate_stream_conn)
|
||||
},
|
||||
#else
|
||||
{
|
||||
"x-webkit-deflate-frame",
|
||||
lws_extension_callback_deflate_frame,
|
||||
sizeof(struct lws_ext_deflate_frame_conn)
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_deflate_frame,
|
||||
sizeof(struct lws_ext_deflate_frame_conn)
|
||||
},
|
||||
#endif
|
||||
{ /* terminator */
|
||||
NULL, NULL, 0
|
||||
}
|
||||
};
|
||||
#include "extension-permessage-deflate.h"
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_context_init_extensions(struct lws_context_creation_info *info,
|
||||
|
@ -35,28 +10,164 @@ lws_context_init_extensions(struct lws_context_creation_info *info,
|
|||
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws_extension *lws_get_internal_extensions()
|
||||
|
||||
enum lws_ext_option_parser_states {
|
||||
LEAPS_SEEK_NAME,
|
||||
LEAPS_EAT_NAME,
|
||||
LEAPS_SEEK_VAL,
|
||||
LEAPS_EAT_DEC,
|
||||
LEAPS_SEEK_ARG_TERM
|
||||
};
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
|
||||
void *ext_user, const struct lws_ext_options *opts, const char *in, int len)
|
||||
{
|
||||
return lws_internal_extensions;
|
||||
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
|
||||
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
|
||||
pending_close_quote = 0;
|
||||
struct lws_ext_option_arg oa;
|
||||
|
||||
while (opts[count_options].name)
|
||||
count_options++;
|
||||
while (len) {
|
||||
lwsl_ext("'%c'", *in);
|
||||
switch (leap) {
|
||||
case LEAPS_SEEK_NAME:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
match_map = (1 << count_options) - 1;
|
||||
leap = LEAPS_EAT_NAME;
|
||||
w = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LEAPS_EAT_NAME:
|
||||
oa.start = NULL;
|
||||
oa.len = 0;
|
||||
m = match_map;
|
||||
n = 0;
|
||||
w = 0;
|
||||
pending_close_quote = 0;
|
||||
while (m) {
|
||||
if (m & 1) {
|
||||
// lwsl_ext(" m=%d, n=%d\n", m, n);
|
||||
|
||||
if (*in == opts[n].name[w]) {
|
||||
if (!opts[n].name[w + 1]) {
|
||||
oa.option_index = n;
|
||||
lwsl_ext("hit %d\n", oa.option_index);
|
||||
leap = LEAPS_SEEK_VAL;
|
||||
if (len ==1)
|
||||
goto set_arg;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
match_map &= ~(1 << n);
|
||||
if (!match_map)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
m >>= 1;
|
||||
n++;
|
||||
}
|
||||
w++;
|
||||
break;
|
||||
case LEAPS_SEEK_VAL:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
if (*in == ';' || len == 1) { /* ie,nonoptional */
|
||||
if (opts[oa.option_index].type == EXTARG_DEC)
|
||||
return -1;
|
||||
leap = LEAPS_SEEK_NAME;
|
||||
goto set_arg;
|
||||
}
|
||||
if (*in == '=') {
|
||||
w = 0;
|
||||
pending_close_quote = 0;
|
||||
if (opts[oa.option_index].type == EXTARG_NONE)
|
||||
return -1;
|
||||
|
||||
leap = LEAPS_EAT_DEC;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
|
||||
case LEAPS_EAT_DEC:
|
||||
if (*in >= '0' && *in <= '9') {
|
||||
if (!w)
|
||||
oa.start = in;
|
||||
w++;
|
||||
if (len != 1)
|
||||
break;
|
||||
}
|
||||
if (!w && *in =='"') {
|
||||
pending_close_quote = 1;
|
||||
break;
|
||||
}
|
||||
if (!w)
|
||||
return -1;
|
||||
if (pending_close_quote && *in != '"' && len != 1)
|
||||
return -1;
|
||||
leap = LEAPS_SEEK_ARG_TERM;
|
||||
if (oa.start)
|
||||
oa.len = in - oa.start;
|
||||
if (len == 1)
|
||||
oa.len++;
|
||||
|
||||
set_arg:
|
||||
m = ext->callback(lws_get_context(wsi),
|
||||
ext, wsi, LWS_EXT_CB_OPTION_SET,
|
||||
ext_user, (char *)&oa, 0);
|
||||
if (len == 1)
|
||||
break;
|
||||
if (pending_close_quote && *in == '"')
|
||||
break;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LEAPS_SEEK_ARG_TERM:
|
||||
if (*in == ' ')
|
||||
break;
|
||||
if (*in == ';') {
|
||||
leap = LEAPS_SEEK_NAME;
|
||||
break;
|
||||
}
|
||||
if (*in == ',') {
|
||||
len = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
len--;
|
||||
in++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
|
||||
|
||||
int lws_ext_cb_wsi_active_exts(struct lws *wsi, int reason, void *arg, int len)
|
||||
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
|
||||
{
|
||||
int n, m, handled = 0;
|
||||
|
||||
for (n = 0; n < wsi->count_active_extensions; n++) {
|
||||
m = wsi->active_extensions[n]->callback(
|
||||
lws_get_context(wsi),
|
||||
wsi->active_extensions[n], wsi,
|
||||
reason,
|
||||
wsi->active_extensions_user[n],
|
||||
arg, len);
|
||||
for (n = 0; n < wsi->count_act_ext; n++) {
|
||||
m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
|
||||
wsi->active_extensions[n], wsi, reason,
|
||||
wsi->act_ext_user[n], arg, len);
|
||||
if (m < 0) {
|
||||
lwsl_ext(
|
||||
"Extension '%s' failed to handle callback %d!\n",
|
||||
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
|
||||
wsi->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
|
@ -77,9 +188,8 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
|
|||
m = ext->callback(context, ext, wsi, reason,
|
||||
(void *)(long)n, arg, len);
|
||||
if (m < 0) {
|
||||
lwsl_ext(
|
||||
"Extension '%s' failed to handle callback %d!\n",
|
||||
wsi->active_extensions[n]->name, reason);
|
||||
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
|
||||
wsi->active_extensions[n]->name, reason);
|
||||
return -1;
|
||||
}
|
||||
if (m)
|
||||
|
@ -116,8 +226,8 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
ret = 0;
|
||||
|
||||
/* show every extension the new incoming data */
|
||||
m = lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_PACKET_TX_PRESEND, &eff_buf, 0);
|
||||
m = lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */
|
||||
|
@ -191,13 +301,13 @@ lws_any_extension_handled(struct lws *wsi,
|
|||
|
||||
/* maybe an extension will take care of it for us */
|
||||
|
||||
for (n = 0; n < wsi->count_active_extensions && !handled; n++) {
|
||||
for (n = 0; n < wsi->count_act_ext && !handled; n++) {
|
||||
if (!wsi->active_extensions[n]->callback)
|
||||
continue;
|
||||
|
||||
handled |= wsi->active_extensions[n]->callback(context,
|
||||
wsi->active_extensions[n], wsi,
|
||||
r, wsi->active_extensions_user[n], v, len);
|
||||
r, wsi->act_ext_user[n], v, len);
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
|
|
@ -64,6 +64,8 @@ lws_read(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
int body_chunk_len;
|
||||
size_t n;
|
||||
|
||||
lwsl_debug("%s: incoming len %d\n", __func__, (int)len);
|
||||
|
||||
switch (wsi->state) {
|
||||
#ifdef LWS_USE_HTTP2
|
||||
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
|
||||
|
@ -185,7 +187,7 @@ postbody_completion:
|
|||
switch (wsi->mode) {
|
||||
case LWSCM_WS_SERVING:
|
||||
|
||||
if (lws_interpret_incoming_packet(wsi, buf, len) < 0) {
|
||||
if (lws_interpret_incoming_packet(wsi, &buf, len) < 0) {
|
||||
lwsl_info("interpret_incoming_packet has bailed\n");
|
||||
goto bail;
|
||||
}
|
||||
|
|
|
@ -178,10 +178,10 @@ lws_return_http_status(struct lws *wsi, unsigned int code, const char *html_body
|
|||
int n, m;
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
unsigned char *p = context->serv_buf +
|
||||
LWS_SEND_BUFFER_PRE_PADDING;
|
||||
LWS_PRE;
|
||||
unsigned char *start = p;
|
||||
unsigned char *end = p + sizeof(context->serv_buf) -
|
||||
LWS_SEND_BUFFER_PRE_PADDING;
|
||||
LWS_PRE;
|
||||
|
||||
if (!html_body)
|
||||
html_body = "";
|
||||
|
|
12
lib/http2.c
12
lib/http2.c
|
@ -425,7 +425,7 @@ update_end_headers:
|
|||
|
||||
int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
unsigned char settings[LWS_SEND_BUFFER_PRE_PADDING + 6 * LWS_HTTP2_SETTINGS__COUNT];
|
||||
unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
|
||||
struct lws *swsi;
|
||||
int n, m = 0;
|
||||
|
||||
|
@ -436,12 +436,12 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
|||
for (n = 1; n < LWS_HTTP2_SETTINGS__COUNT; n++)
|
||||
if (wsi->u.http2.my_settings.setting[n] != lws_http2_default_settings.setting[n]) {
|
||||
lws_http2_settings_write(wsi, n,
|
||||
&settings[LWS_SEND_BUFFER_PRE_PADDING + m]);
|
||||
&settings[LWS_PRE + m]);
|
||||
m += sizeof(wsi->u.http2.one_setting);
|
||||
}
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
|
||||
0, LWS_HTTP2_STREAM_ID_MASTER, m,
|
||||
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
|
||||
&settings[LWS_PRE]);
|
||||
if (n != m) {
|
||||
lwsl_info("send %d %d\n", n, m);
|
||||
return 1;
|
||||
|
@ -451,7 +451,7 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
|||
/* send ack ... always empty */
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
|
||||
1, LWS_HTTP2_STREAM_ID_MASTER, 0,
|
||||
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
|
||||
&settings[LWS_PRE]);
|
||||
if (n) {
|
||||
lwsl_err("ack tells %d\n", n);
|
||||
return 1;
|
||||
|
@ -490,11 +490,11 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
|||
}
|
||||
break;
|
||||
case LWS_PPS_HTTP2_PONG:
|
||||
memcpy(&settings[LWS_SEND_BUFFER_PRE_PADDING], wsi->u.http2.ping_payload, 8);
|
||||
memcpy(&settings[LWS_PRE], wsi->u.http2.ping_payload, 8);
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_PING,
|
||||
LWS_HTTP2_FLAG_SETTINGS_ACK,
|
||||
LWS_HTTP2_STREAM_ID_MASTER, 8,
|
||||
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
|
||||
&settings[LWS_PRE]);
|
||||
if (n != 8) {
|
||||
lwsl_info("send %d %d\n", n, m);
|
||||
return 1;
|
||||
|
|
|
@ -130,8 +130,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
* parent and just his ch1 aspect is closing?
|
||||
*/
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
|
||||
lwsl_ext("extension vetoed close\n");
|
||||
return;
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
|
||||
/* show every extension the new incoming data */
|
||||
|
||||
m = lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0);
|
||||
m = lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
|
||||
if (m < 0) {
|
||||
lwsl_ext("Extension reports fatal error\n");
|
||||
goto just_kill_connection;
|
||||
|
@ -193,14 +193,14 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
/* if no prepared close reason, use 1000 and no aux data */
|
||||
if (!wsi->u.ws.close_in_ping_buffer_len) {
|
||||
wsi->u.ws.close_in_ping_buffer_len = 2;
|
||||
wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING] =
|
||||
wsi->u.ws.ping_payload_buf[LWS_PRE] =
|
||||
(reason >> 16) & 0xff;
|
||||
wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING + 1] =
|
||||
wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
|
||||
reason & 0xff;
|
||||
}
|
||||
|
||||
n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
|
||||
LWS_SEND_BUFFER_PRE_PADDING],
|
||||
LWS_PRE],
|
||||
wsi->u.ws.close_in_ping_buffer_len,
|
||||
LWS_WRITE_CLOSE);
|
||||
if (n >= 0) {
|
||||
|
@ -227,7 +227,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
|
||||
just_kill_connection:
|
||||
|
||||
lwsl_debug("close: just_kill_connection\n");
|
||||
lwsl_debug("close: just_kill_connection: %p\n", wsi);
|
||||
|
||||
/*
|
||||
* we won't be servicing or receiving anything further from this guy
|
||||
|
@ -246,7 +246,36 @@ just_kill_connection:
|
|||
wsi->mode == LWSCM_WS_SERVING ||
|
||||
wsi->mode == LWSCM_WS_CLIENT) {
|
||||
|
||||
lws_free_set_NULL(wsi->u.ws.rx_user_buffer);
|
||||
if (wsi->u.ws.rx_draining_ext) {
|
||||
struct lws **w = &wsi->context->rx_draining_ext_list;
|
||||
|
||||
wsi->u.ws.rx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.rx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.rx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.rx_draining_ext_list = NULL;
|
||||
}
|
||||
|
||||
if (wsi->u.ws.tx_draining_ext) {
|
||||
struct lws **w = &wsi->context->tx_draining_ext_list;
|
||||
|
||||
wsi->u.ws.tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.tx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.tx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.tx_draining_ext_list = NULL;
|
||||
}
|
||||
lws_free_set_NULL(wsi->u.ws.rx_ubuf);
|
||||
|
||||
if (wsi->trunc_alloc)
|
||||
/* not going to be completed... nuke it */
|
||||
|
@ -282,18 +311,14 @@ just_kill_connection:
|
|||
|
||||
/* deallocate any active extension contexts */
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_DESTROY, NULL, 0) < 0)
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
|
||||
lwsl_warn("extension destruction failed\n");
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
for (n = 0; n < wsi->count_active_extensions; n++)
|
||||
lws_free(wsi->active_extensions_user[n]);
|
||||
#endif
|
||||
/*
|
||||
* inform all extensions in case they tracked this guy out of band
|
||||
* even though not active on him specifically
|
||||
*/
|
||||
if (lws_ext_cb_all_exts(context, wsi,
|
||||
LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
|
||||
LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
|
||||
lwsl_warn("ext destroy wsi failed\n");
|
||||
|
||||
if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
|
||||
|
@ -763,7 +788,9 @@ lws_get_protocol(struct lws *wsi)
|
|||
LWS_VISIBLE int
|
||||
lws_is_final_fragment(struct lws *wsi)
|
||||
{
|
||||
return wsi->u.ws.final && !wsi->u.ws.rx_packet_length;
|
||||
lwsl_info("%s: final %d, rx pk length %d, draining %d", __func__,
|
||||
wsi->u.ws.final, wsi->u.ws.rx_packet_length, wsi->u.ws.rx_draining_ext);
|
||||
return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext;
|
||||
}
|
||||
|
||||
LWS_VISIBLE unsigned char
|
||||
|
@ -959,11 +986,11 @@ lws_close_reason(struct lws *wsi, enum lws_close_status status,
|
|||
{
|
||||
unsigned char *p, *start;
|
||||
int budget = sizeof(wsi->u.ws.ping_payload_buf) -
|
||||
LWS_SEND_BUFFER_PRE_PADDING;
|
||||
LWS_PRE;
|
||||
|
||||
assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
|
||||
|
||||
start = p = &wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING];
|
||||
start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
|
||||
|
||||
*p++ = (((int)status) >> 8) & 0xff;
|
||||
*p++ = ((int)status) & 0xff;
|
||||
|
@ -979,8 +1006,10 @@ LWS_EXTERN int
|
|||
_lws_rx_flow_control(struct lws *wsi)
|
||||
{
|
||||
/* there is no pending change */
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) {
|
||||
lwsl_info("%s: no pending change\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stuff is still buffered, not ready to really accept new input */
|
||||
if (wsi->rxflow_buffer) {
|
||||
|
@ -1066,3 +1095,22 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LWS_NO_EXTENSIONS
|
||||
|
||||
/* we need to provide dummy callbacks for internal exts
|
||||
* so user code runs when faced with a lib compiled with
|
||||
* extensions disabled.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_extension_callback_pm_deflate(struct lws_context *context,
|
||||
const struct lws_extension *ext,
|
||||
struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2016 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
|
||||
|
@ -323,6 +323,8 @@ enum lws_callback_reasons {
|
|||
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38,
|
||||
|
||||
LWS_CALLBACK_WS_EXT_DEFAULTS = 39,
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
|
||||
LWS_CALLBACK_USER = 1000, /* user code can use any including / above */
|
||||
|
@ -415,29 +417,32 @@ struct lws_plat_file_ops {
|
|||
* add it at where specified so existing users are unaffected.
|
||||
*/
|
||||
enum lws_extension_callback_reasons {
|
||||
LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT = 0,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT = 1,
|
||||
LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT = 2,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT = 3,
|
||||
LWS_EXT_CALLBACK_CONSTRUCT = 4,
|
||||
LWS_EXT_CALLBACK_CLIENT_CONSTRUCT = 5,
|
||||
LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE = 6,
|
||||
LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION = 7,
|
||||
LWS_EXT_CALLBACK_DESTROY = 8,
|
||||
LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING = 9,
|
||||
LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED = 10,
|
||||
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE = 11,
|
||||
LWS_EXT_CALLBACK_PACKET_TX_PRESEND = 12,
|
||||
LWS_EXT_CALLBACK_PACKET_TX_DO_SEND = 13,
|
||||
LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX = 14,
|
||||
LWS_EXT_CALLBACK_FLUSH_PENDING_TX = 15,
|
||||
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX = 16,
|
||||
LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION = 17,
|
||||
LWS_EXT_CALLBACK_1HZ = 18,
|
||||
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE = 19,
|
||||
LWS_EXT_CALLBACK_IS_WRITEABLE = 20,
|
||||
LWS_EXT_CALLBACK_PAYLOAD_TX = 21,
|
||||
LWS_EXT_CALLBACK_PAYLOAD_RX = 22,
|
||||
LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1,
|
||||
LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3,
|
||||
LWS_EXT_CB_CONSTRUCT = 4,
|
||||
LWS_EXT_CB_CLIENT_CONSTRUCT = 5,
|
||||
LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7,
|
||||
LWS_EXT_CB_DESTROY = 8,
|
||||
LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9,
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10,
|
||||
LWS_EXT_CB_PACKET_RX_PREPARSE = 11,
|
||||
LWS_EXT_CB_PACKET_TX_PRESEND = 12,
|
||||
LWS_EXT_CB_PACKET_TX_DO_SEND = 13,
|
||||
LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14,
|
||||
LWS_EXT_CB_FLUSH_PENDING_TX = 15,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16,
|
||||
LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17,
|
||||
LWS_EXT_CB_1HZ = 18,
|
||||
LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19,
|
||||
LWS_EXT_CB_IS_WRITEABLE = 20,
|
||||
LWS_EXT_CB_PAYLOAD_TX = 21,
|
||||
LWS_EXT_CB_PAYLOAD_RX = 22,
|
||||
LWS_EXT_CB_OPTION_DEFAULT = 23,
|
||||
LWS_EXT_CB_OPTION_SET = 24,
|
||||
LWS_EXT_CB_OPTION_CONFIRM = 25,
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
};
|
||||
|
@ -1056,14 +1061,13 @@ typedef int
|
|||
lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
/**
|
||||
* typedef lws_extension_callback_function() - Hooks to allow extensions to operate
|
||||
* @context: Websockets context
|
||||
* @ext: This extension
|
||||
* @wsi: Opaque websocket instance pointer
|
||||
* @reason: The reason for the call
|
||||
* @user: Pointer to per-session user data allocated by library
|
||||
* @user: Pointer to ptr to per-session user data allocated by library
|
||||
* @in: Pointer used for some callback reasons
|
||||
* @len: Length set for some callback reasons
|
||||
*
|
||||
|
@ -1075,26 +1079,26 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
* each active extension on each connection. That is what is pointed to
|
||||
* by the @user parameter.
|
||||
*
|
||||
* LWS_EXT_CALLBACK_CONSTRUCT: called when the server has decided to
|
||||
* LWS_EXT_CB_CONSTRUCT: called when the server has decided to
|
||||
* select this extension from the list provided by the client,
|
||||
* just before the server will send back the handshake accepting
|
||||
* the connection with this extension active. This gives the
|
||||
* extension a chance to initialize its connection context found
|
||||
* in @user.
|
||||
*
|
||||
* LWS_EXT_CALLBACK_CLIENT_CONSTRUCT: same as LWS_EXT_CALLBACK_CONSTRUCT
|
||||
* LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT
|
||||
* but called when client is instantiating this extension. Some
|
||||
* extensions will work the same on client and server side and then
|
||||
* you can just merge handlers for both CONSTRUCTS.
|
||||
*
|
||||
* LWS_EXT_CALLBACK_DESTROY: called when the connection the extension was
|
||||
* LWS_EXT_CB_DESTROY: called when the connection the extension was
|
||||
* being used on is about to be closed and deallocated. It's the
|
||||
* last chance for the extension to deallocate anything it has
|
||||
* allocated in the user data (pointed to by @user) before the
|
||||
* user data is deleted. This same callback is used whether you
|
||||
* are in client or server instantiation context.
|
||||
*
|
||||
* LWS_EXT_CALLBACK_PACKET_RX_PREPARSE: when this extension was active on
|
||||
* LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on
|
||||
* a connection, and a packet of data arrived at the connection,
|
||||
* it is passed to this callback to give the extension a chance to
|
||||
* change the data, eg, decompress it. @user is pointing to the
|
||||
|
@ -1106,21 +1110,22 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
* a new buffer allocated in its private user context data and
|
||||
* set the pointed-to lws_tokens members to point to its buffer.
|
||||
*
|
||||
* LWS_EXT_CALLBACK_PACKET_TX_PRESEND: this works the same way as
|
||||
* LWS_EXT_CALLBACK_PACKET_RX_PREPARSE above, except it gives the
|
||||
* LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as
|
||||
* LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the
|
||||
* extension a chance to change websocket data just before it will
|
||||
* be sent out. Using the same lws_token pointer scheme in @in,
|
||||
* the extension can change the buffer and the length to be
|
||||
* transmitted how it likes. Again if it wants to grow the
|
||||
* buffer safely, it should copy the data into its own buffer and
|
||||
* set the lws_tokens token pointer to it.
|
||||
*
|
||||
* LWS_EXT_CB_ARGS_VALIDATE:
|
||||
*/
|
||||
typedef int
|
||||
lws_extension_callback_function(struct lws_context *context,
|
||||
const struct lws_extension *ext, struct lws *wsi,
|
||||
enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct lws_protocols - List of protocols and handlers server
|
||||
|
@ -1140,7 +1145,7 @@ lws_extension_callback_function(struct lws_context *context,
|
|||
* error, but the buffer will spill to the user callback when
|
||||
* full, which you can detect by using
|
||||
* lws_remaining_packet_payload(). Notice that you
|
||||
* just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING
|
||||
* just talk about frame size here, the LWS_PRE
|
||||
* and post-padding are automatically also allocated on top.
|
||||
* @id: ignored by lws, but useful to contain user information bound
|
||||
* to the selected protocol. For example if this protocol was
|
||||
|
@ -1174,30 +1179,65 @@ struct lws_protocols {
|
|||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
};
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
enum lws_ext_options_types {
|
||||
EXTARG_NONE,
|
||||
EXTARG_DEC,
|
||||
EXTARG_OPT_DEC
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lws_ext_options - Option arguments to the extension. These are
|
||||
* used in the negotiation at ws upgrade time.
|
||||
* The helper function lws_ext_parse_options()
|
||||
* uses these to generate callbacks
|
||||
*
|
||||
* @name: Option name, eg, "server_no_context_takeover"
|
||||
* @type: What kind of args the option can take
|
||||
*/
|
||||
struct lws_ext_options {
|
||||
const char *name;
|
||||
enum lws_ext_options_types type;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
};
|
||||
|
||||
struct lws_ext_option_arg {
|
||||
int option_index;
|
||||
const char *start;
|
||||
int len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lws_extension - An extension we know how to cope with
|
||||
*
|
||||
* @name: Formal extension name, eg, "deflate-stream"
|
||||
* @name: Formal extension name, eg, "permessage-deflate"
|
||||
* @callback: Service callback
|
||||
* @per_session_data_size: Libwebsockets will auto-malloc this much
|
||||
* memory for the use of the extension, a pointer
|
||||
* to it comes in the @user callback parameter
|
||||
* @per_context_private_data: Optional storage for this extension that
|
||||
* is per-context, so it can track stuff across
|
||||
* all sessions, etc, if it wants
|
||||
* @client_offer: String containing exts and options client offers
|
||||
*/
|
||||
|
||||
struct lws_extension {
|
||||
const char *name;
|
||||
lws_extension_callback_function *callback;
|
||||
size_t per_session_data_size;
|
||||
void *per_context_private_data;
|
||||
const char *client_offer;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The internal exts are part of the public abi
|
||||
* If we add more extensions, publish the callback here
|
||||
*/
|
||||
|
||||
extern int lws_extension_callback_pm_deflate(
|
||||
struct lws_context *context, const struct lws_extension *ext,
|
||||
struct lws *wsi, enum lws_extension_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
|
||||
|
||||
/**
|
||||
* struct lws_context_creation_info - parameters to create context with
|
||||
|
@ -1216,14 +1256,15 @@ struct lws_extension {
|
|||
* extensions this context supports. If you configured with
|
||||
* --without-extensions, you should give NULL here.
|
||||
* @token_limits: NULL or struct lws_token_limits pointer which is initialized
|
||||
* with a token length limit for each possible WSI_TOKEN_***
|
||||
* with a token length limit for each possible WSI_TOKEN_***
|
||||
* @ssl_cert_filepath: If libwebsockets was compiled to use ssl, and you want
|
||||
* to listen using SSL, set to the filepath to fetch the
|
||||
* server cert from, otherwise NULL for unencrypted
|
||||
* @ssl_private_key_filepath: filepath to private key if wanting SSL mode;
|
||||
* if this is set to NULL but sll_cert_filepath is set, the
|
||||
* OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called to allow
|
||||
* setting of the private key directly via openSSL library calls
|
||||
* OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called
|
||||
* to allow setting of the private key directly via openSSL
|
||||
* library calls
|
||||
* @ssl_ca_filepath: CA certificate filepath or NULL
|
||||
* @ssl_cipher_list: List of valid ciphers to use (eg,
|
||||
* "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"
|
||||
|
@ -1231,7 +1272,8 @@ struct lws_extension {
|
|||
* @http_proxy_address: If non-NULL, attempts to proxy via the given address.
|
||||
* If proxy auth is required, use format
|
||||
* "username:password@server:port"
|
||||
* @http_proxy_port: If http_proxy_address was non-NULL, uses this port at the address
|
||||
* @http_proxy_port: If http_proxy_address was non-NULL, uses this port at
|
||||
* the address
|
||||
* @gid: group id to change to after setting listen socket, or -1.
|
||||
* @uid: user id to change to after setting listen socket, or -1.
|
||||
* @options: 0, or LWS_SERVER_OPTION_... bitfields
|
||||
|
@ -1297,6 +1339,22 @@ struct lws_context_creation_info {
|
|||
void *_unused[8];
|
||||
};
|
||||
|
||||
struct lws_client_connect_info {
|
||||
struct lws_context *context;
|
||||
const char *address;
|
||||
int port;
|
||||
int ssl_connection;
|
||||
const char *path;
|
||||
const char *host;
|
||||
const char *origin;
|
||||
const char *protocol;
|
||||
int ietf_version_or_minus_one;
|
||||
void *userdata;
|
||||
const struct lws_extension *client_exts;
|
||||
void *_unused[4];
|
||||
};
|
||||
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_set_log_level(int level,
|
||||
void (*log_emit_function)(int level, const char *line));
|
||||
|
@ -1397,14 +1455,14 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
|||
* IMPORTANT NOTICE!
|
||||
*
|
||||
* When sending with websocket protocol
|
||||
*
|
||||
*
|
||||
* LWS_WRITE_TEXT,
|
||||
* LWS_WRITE_BINARY,
|
||||
* LWS_WRITE_CONTINUATION,
|
||||
* LWS_WRITE_PING,
|
||||
* LWS_WRITE_PONG
|
||||
*
|
||||
* the send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE
|
||||
*
|
||||
* the send buffer has to have LWS_PRE bytes valid BEFORE
|
||||
* the buffer pointer you pass to lws_write().
|
||||
*
|
||||
* This allows us to add protocol info before and after the data, and send as
|
||||
|
@ -1413,27 +1471,27 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
|||
* So for example you need this kind of code to use lws_write with a
|
||||
* 128-byte payload
|
||||
*
|
||||
* char buf[LWS_SEND_BUFFER_PRE_PADDING + 128];
|
||||
* char buf[LWS_PRE + 128];
|
||||
*
|
||||
* // fill your part of the buffer... for example here it's all zeros
|
||||
* memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, 128);
|
||||
* memset(&buf[LWS_PRE], 0, 128);
|
||||
*
|
||||
* lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT);
|
||||
*
|
||||
* lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128, LWS_WRITE_TEXT);
|
||||
*
|
||||
* When sending HTTP, with
|
||||
*
|
||||
*
|
||||
* LWS_WRITE_HTTP,
|
||||
* LWS_WRITE_HTTP_HEADERS
|
||||
* LWS_WRITE_HTTP_FINAL
|
||||
*
|
||||
* there is no protocol data prepended, and don't need to take care about the
|
||||
* LWS_SEND_BUFFER_PRE_PADDING bytes valid before the buffer pointer.
|
||||
*
|
||||
* LWS_SEND_BUFFER_PRE_PADDING is at least the frame nonce + 2 header + 8 length
|
||||
* there is no protocol data prepended, and don't need to take care about the
|
||||
* LWS_PRE bytes valid before the buffer pointer.
|
||||
*
|
||||
* LWS_PRE is at least the frame nonce + 2 header + 8 length
|
||||
* LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off.
|
||||
* The example apps no longer use it.
|
||||
*
|
||||
* Pad LWS_SEND_BUFFER_PRE_PADDING to the CPU word size, so that word references
|
||||
* Pad LWS_PRE to the CPU word size, so that word references
|
||||
* to the address immediately after the padding won't cause an unaligned access
|
||||
* error. Sometimes for performance reasons the recommended padding is even
|
||||
* larger than sizeof(void *).
|
||||
|
@ -1453,7 +1511,9 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
|||
#endif
|
||||
#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \
|
||||
((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n))
|
||||
#define LWS_SEND_BUFFER_PRE_PADDING _LWS_PAD(4 + 10)
|
||||
#define LWS_PRE _LWS_PAD(4 + 10)
|
||||
/* used prior to 1.7 and retained for backward compatibility */
|
||||
#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE
|
||||
#define LWS_SEND_BUFFER_POST_PADDING 0
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
|
@ -1542,12 +1602,13 @@ lws_remaining_packet_payload(struct lws *wsi);
|
|||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_get_peer_write_allowance(struct lws *wsi);
|
||||
|
||||
/* deprecated, use lws_client_connect_info() */
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_client_connect(struct lws_context *clients, const char *address,
|
||||
int port, int ssl_connection, const char *path,
|
||||
const char *host, const char *origin, const char *protocol,
|
||||
int ietf_version_or_minus_one);
|
||||
|
||||
/* deprecated, use lws_client_connect_info() */
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_client_connect_extended(struct lws_context *clients, const char *address,
|
||||
int port, int ssl_connection, const char *path,
|
||||
|
@ -1555,6 +1616,9 @@ lws_client_connect_extended(struct lws_context *clients, const char *address,
|
|||
const char *protocol, int ietf_version_or_minus_one,
|
||||
void *userdata);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_client_connect_info(struct lws_client_connect_info * ccinfo);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const char *
|
||||
lws_canonical_hostname(struct lws_context *context);
|
||||
|
||||
|
@ -1704,7 +1768,11 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_read(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_extension *lws_get_internal_extensions();
|
||||
/* deprecated */
|
||||
#define lws_get_internal_extensions() NULL
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
|
||||
void *ext_user, const struct lws_ext_options *opts, const char *o, int len);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -101,8 +101,9 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
|
|||
int n;
|
||||
int m;
|
||||
char buf;
|
||||
struct lws *wsi;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
struct lws *wsi, *wsi_next;
|
||||
struct lws *wsi_next;
|
||||
#endif
|
||||
|
||||
/* stay dead once we are dead */
|
||||
|
@ -123,18 +124,25 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
|
|||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
/* if we know we have non-network pending data, do not wait in poll */
|
||||
if (lws_ssl_anybody_has_buffered_read(context))
|
||||
/* if we know we are draining rx ext, do not wait in poll */
|
||||
if (context->rx_draining_ext_list)
|
||||
timeout_ms = 0;
|
||||
#endif
|
||||
n = poll(context->fds, context->fds_count, timeout_ms);
|
||||
context->service_tid = 0;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (!lws_ssl_anybody_has_buffered_read(context) && n == 0) {
|
||||
/* if we know we have non-network pending data, do not wait in poll */
|
||||
if (lws_ssl_anybody_has_buffered_read(context)) {
|
||||
timeout_ms = 0;
|
||||
lwsl_err("ssl buffered read\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
n = poll(context->fds, context->fds_count, timeout_ms);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (!context->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read(context) && n == 0) {
|
||||
#else
|
||||
if (n == 0) /* poll timeout */ {
|
||||
if (!context->rx_draining_ext_list && n == 0) /* poll timeout */ {
|
||||
#endif
|
||||
lws_service_fd(context, NULL);
|
||||
return 0;
|
||||
|
@ -146,6 +154,17 @@ lws_plat_service(struct lws_context *context, int timeout_ms)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For all guys with already-available ext data to drain, if they are
|
||||
* not flowcontrolled, fake their POLLIN status
|
||||
*/
|
||||
wsi = context->rx_draining_ext_list;
|
||||
while (wsi) {
|
||||
context->fds[wsi->position_in_fds_table].revents |=
|
||||
context->fds[wsi->position_in_fds_table].events & POLLIN;
|
||||
wsi = wsi->u.ws.rx_draining_ext_list;
|
||||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
/*
|
||||
* For all guys with buffered SSL read data already saved up, if they
|
||||
|
|
148
lib/output.c
148
lib/output.c
|
@ -25,15 +25,15 @@ static int
|
|||
lws_0405_frame_mask_generate(struct lws *wsi)
|
||||
{
|
||||
#if 0
|
||||
wsi->u.ws.mask_nonce[0] = 0;
|
||||
wsi->u.ws.mask_nonce[1] = 0;
|
||||
wsi->u.ws.mask_nonce[2] = 0;
|
||||
wsi->u.ws.mask_nonce[3] = 0;
|
||||
wsi->u.ws.mask[0] = 0;
|
||||
wsi->u.ws.mask[1] = 0;
|
||||
wsi->u.ws.mask[2] = 0;
|
||||
wsi->u.ws.mask[3] = 0;
|
||||
#else
|
||||
int n;
|
||||
/* fetch the per-frame nonce */
|
||||
|
||||
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask_nonce, 4);
|
||||
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
|
||||
if (n != 4) {
|
||||
lwsl_parser("Unable to read from random device %s %d\n",
|
||||
SYSTEM_RANDOM_FILEPATH, n);
|
||||
|
@ -41,7 +41,7 @@ lws_0405_frame_mask_generate(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
/* start masking from first byte of masking key buffer */
|
||||
wsi->u.ws.frame_mask_index = 0;
|
||||
wsi->u.ws.mask_idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -112,8 +112,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
assert(0);
|
||||
}
|
||||
|
||||
m = lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len);
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */ {
|
||||
|
@ -208,7 +207,7 @@ handle_truncated_send:
|
|||
* @wsi: Websocket instance (available from user callback)
|
||||
* @buf: The data to send. For data being sent on a websocket
|
||||
* connection (ie, not default http), this buffer MUST have
|
||||
* LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer.
|
||||
* LWS_PRE bytes valid BEFORE the pointer.
|
||||
* This is so the protocol header data can be added in-situ.
|
||||
* @len: Count of the data bytes in the payload starting from buf
|
||||
* @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
|
||||
|
@ -231,27 +230,48 @@ handle_truncated_send:
|
|||
* pressure at any given time.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
|
||||
size_t len, enum lws_write_protocol protocol)
|
||||
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol wp)
|
||||
{
|
||||
int masked7 = wsi->mode == LWSCM_WS_CLIENT;
|
||||
int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
|
||||
unsigned char is_masked_bit = 0;
|
||||
unsigned char *dropmask = NULL;
|
||||
struct lws_tokens eff_buf;
|
||||
int pre = 0, n;
|
||||
size_t orig_len = len;
|
||||
|
||||
if (protocol == LWS_WRITE_HTTP ||
|
||||
protocol == LWS_WRITE_HTTP_FINAL ||
|
||||
protocol == LWS_WRITE_HTTP_HEADERS)
|
||||
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
|
||||
/* remove us from the list */
|
||||
struct lws **w = &wsi->context->tx_draining_ext_list;
|
||||
lwsl_debug("%s: TX EXT DRAINING: Remove from list\n", __func__);
|
||||
wsi->u.ws.tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.tx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.tx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.tx_draining_ext_list = NULL;
|
||||
wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
|
||||
LWS_WRITE_CONTINUATION;
|
||||
|
||||
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
|
||||
}
|
||||
|
||||
if (wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL ||
|
||||
wp == LWS_WRITE_HTTP_HEADERS)
|
||||
goto send_raw;
|
||||
|
||||
/* websocket protocol, either binary or text */
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
||||
if (wsi->state != LWSS_ESTABLISHED &&
|
||||
!(wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
|
||||
protocol == LWS_WRITE_CLOSE))
|
||||
return -1;
|
||||
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
|
||||
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
|
||||
wp != LWS_WRITE_CLOSE))
|
||||
return 0;
|
||||
|
||||
/* if we are continuing a frame that already had its header done */
|
||||
|
||||
|
@ -264,20 +284,56 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
|
|||
|
||||
/*
|
||||
* give a chance to the extensions to modify payload
|
||||
* pre-TX mangling is not allowed to truncate
|
||||
* the extension may decide to produce unlimited payload erratically
|
||||
* (eg, compression extension), so we require only that if he produces
|
||||
* something, it will be a complete fragment of the length known at
|
||||
* the time (just the fragment length known), and if he has
|
||||
* more we will come back next time he is writeable and allow him to
|
||||
* produce more fragments until he's drained.
|
||||
*
|
||||
* This allows what is sent each time it is writeable to be limited to
|
||||
* a size that can be sent without partial sends or blocking, allows
|
||||
* interleaving of control frames and other connection service.
|
||||
*/
|
||||
eff_buf.token = (char *)buf;
|
||||
eff_buf.token_len = len;
|
||||
|
||||
switch ((int)protocol) {
|
||||
switch ((int)wp) {
|
||||
case LWS_WRITE_PING:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_CLOSE:
|
||||
break;
|
||||
default:
|
||||
if (lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_PAYLOAD_TX,
|
||||
&eff_buf, 0) < 0)
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
if (n && eff_buf.token_len) {
|
||||
/* extension requires further draining */
|
||||
wsi->u.ws.tx_draining_ext = 1;
|
||||
wsi->u.ws.tx_draining_ext_list =
|
||||
wsi->context->tx_draining_ext_list;
|
||||
wsi->context->tx_draining_ext_list = wsi;
|
||||
/* we must come back to do more */
|
||||
lws_callback_on_writable(wsi);
|
||||
/*
|
||||
* keep a copy of the write type for the overall
|
||||
* action that has provoked generation of these
|
||||
* fragments, so the last guy can use its FIN state.
|
||||
*/
|
||||
wsi->u.ws.tx_draining_stashed_wp = wp;
|
||||
/* this is definitely not actually the last fragment
|
||||
* because the extension asserted he has more coming
|
||||
* So make sure this intermediate one doesn't go out
|
||||
* with a FIN.
|
||||
*/
|
||||
wp |= LWS_WRITE_NO_FIN;
|
||||
}
|
||||
|
||||
if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
|
||||
wsi->u.ws.stashed_write_pending = 0;
|
||||
wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -285,12 +341,24 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
|
|||
* compression extension, it has already updated its state according
|
||||
* to this being issued
|
||||
*/
|
||||
if ((char *)buf != eff_buf.token)
|
||||
if ((char *)buf != eff_buf.token) {
|
||||
/*
|
||||
* ext might eat it, but no have anything to issue yet
|
||||
* in that case we have to follow his lead, but stash and
|
||||
* replace the write type that was lost here the first time.
|
||||
*/
|
||||
if (len && !eff_buf.token_len) {
|
||||
if (!wsi->u.ws.stashed_write_pending)
|
||||
wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
|
||||
wsi->u.ws.stashed_write_pending = 1;
|
||||
return len;
|
||||
}
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->u.ws.clean_buffer = 0;
|
||||
}
|
||||
|
||||
buf = (unsigned char *)eff_buf.token;
|
||||
len = eff_buf.token_len;
|
||||
|
@ -303,7 +371,7 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
|
|||
is_masked_bit = 0x80;
|
||||
}
|
||||
|
||||
switch (protocol & 0xf) {
|
||||
switch (wp & 0xf) {
|
||||
case LWS_WRITE_TEXT:
|
||||
n = LWSWSOPC_TEXT_FRAME;
|
||||
break;
|
||||
|
@ -324,11 +392,11 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
|
|||
n = LWSWSOPC_PONG;
|
||||
break;
|
||||
default:
|
||||
lwsl_warn("lws_write: unknown write opc / protocol\n");
|
||||
lwsl_warn("lws_write: unknown write opc / wp\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(protocol & LWS_WRITE_NO_FIN))
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
n |= 1 << 7;
|
||||
|
||||
if (len < 126) {
|
||||
|
@ -370,10 +438,10 @@ do_more_inside_frame:
|
|||
|
||||
/*
|
||||
* Deal with masking if we are in client -> server direction and
|
||||
* the protocol demands it
|
||||
* the wp demands it
|
||||
*/
|
||||
|
||||
if (wsi->mode == LWSCM_WS_CLIENT) {
|
||||
if (masked7) {
|
||||
if (!wsi->u.ws.inside_frame)
|
||||
if (lws_0405_frame_mask_generate(wsi)) {
|
||||
lwsl_err("frame mask generation failed\n");
|
||||
|
@ -385,17 +453,16 @@ do_more_inside_frame:
|
|||
*/
|
||||
if (dropmask) { /* never set if already inside frame */
|
||||
for (n = 4; n < (int)len + 4; n++)
|
||||
dropmask[n] = dropmask[n] ^
|
||||
wsi->u.ws.mask_nonce[
|
||||
(wsi->u.ws.frame_mask_index++) & 3];
|
||||
dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
|
||||
(wsi->u.ws.mask_idx++) & 3];
|
||||
|
||||
/* copy the frame nonce into place */
|
||||
memcpy(dropmask, wsi->u.ws.mask_nonce, 4);
|
||||
memcpy(dropmask, wsi->u.ws.mask, 4);
|
||||
}
|
||||
}
|
||||
|
||||
send_raw:
|
||||
switch ((int)protocol) {
|
||||
switch ((int)wp) {
|
||||
case LWS_WRITE_CLOSE:
|
||||
/* lwsl_hexdump(&buf[-pre], len); */
|
||||
case LWS_WRITE_HTTP:
|
||||
|
@ -408,26 +475,26 @@ send_raw:
|
|||
unsigned char flags = 0;
|
||||
|
||||
n = LWS_HTTP2_FRAME_TYPE_DATA;
|
||||
if (protocol == LWS_WRITE_HTTP_HEADERS) {
|
||||
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;
|
||||
}
|
||||
|
||||
if ((protocol == LWS_WRITE_HTTP ||
|
||||
protocol == LWS_WRITE_HTTP_FINAL) &&
|
||||
if ((wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->u.http.content_length) {
|
||||
wsi->u.http.content_remain -= len;
|
||||
lwsl_info("%s: content_remain = %lu\n", __func__,
|
||||
wsi->u.http.content_remain);
|
||||
if (!wsi->u.http.content_remain) {
|
||||
lwsl_info("%s: selecting final write mode\n", __func__);
|
||||
protocol = LWS_WRITE_HTTP_FINAL;
|
||||
wp = LWS_WRITE_HTTP_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (protocol == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
|
||||
if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
|
||||
lwsl_info("%s: setting END_STREAM\n", __func__);
|
||||
flags |= LWS_HTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
|
@ -441,8 +508,6 @@ send_raw:
|
|||
break;
|
||||
}
|
||||
|
||||
wsi->u.ws.inside_frame = 1;
|
||||
|
||||
/*
|
||||
* give any active extensions a chance to munge the buffer
|
||||
* before send. We pass in a pointer to an lws_tokens struct
|
||||
|
@ -463,6 +528,7 @@ send_raw:
|
|||
*/
|
||||
|
||||
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
|
||||
wsi->u.ws.inside_frame = 1;
|
||||
if (n <= 0)
|
||||
return n;
|
||||
|
||||
|
|
155
lib/parsers.c
155
lib/parsers.c
|
@ -738,12 +738,33 @@ int
|
|||
lws_rx_sm(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
struct lws_tokens eff_buf;
|
||||
int ret = 0;
|
||||
int ret = 0, n, rx_draining_ext = 0;
|
||||
int callback_action = LWS_CALLBACK_RECEIVE;
|
||||
if (wsi->socket_is_permanently_unusable)
|
||||
return -1;
|
||||
|
||||
switch (wsi->lws_rx_parse_state) {
|
||||
case LWS_RXPS_NEW:
|
||||
if (wsi->u.ws.rx_draining_ext) {
|
||||
struct lws **w = &wsi->context->rx_draining_ext_list;
|
||||
|
||||
eff_buf.token = NULL;
|
||||
eff_buf.token_len = 0;
|
||||
wsi->u.ws.rx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.rx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.rx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.rx_draining_ext_list = NULL;
|
||||
rx_draining_ext = 1;
|
||||
lwsl_err("%s: doing draining flow\n", __func__);
|
||||
|
||||
goto drain_extension;
|
||||
}
|
||||
switch (wsi->ietf_spec_revision) {
|
||||
case 13:
|
||||
/*
|
||||
|
@ -758,20 +779,20 @@ lws_rx_sm(struct lws *wsi, unsigned char c)
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case LWS_RXPS_04_MASK_NONCE_1:
|
||||
wsi->u.ws.mask_nonce[1] = c;
|
||||
case LWS_RXPS_04_mask_1:
|
||||
wsi->u.ws.mask[1] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_2;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_mask_2;
|
||||
break;
|
||||
case LWS_RXPS_04_MASK_NONCE_2:
|
||||
wsi->u.ws.mask_nonce[2] = c;
|
||||
case LWS_RXPS_04_mask_2:
|
||||
wsi->u.ws.mask[2] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_3;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_mask_3;
|
||||
break;
|
||||
case LWS_RXPS_04_MASK_NONCE_3:
|
||||
wsi->u.ws.mask_nonce[3] = c;
|
||||
case LWS_RXPS_04_mask_3:
|
||||
wsi->u.ws.mask[3] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
|
||||
|
@ -780,7 +801,7 @@ lws_rx_sm(struct lws *wsi, unsigned char c)
|
|||
* this is the start of a frame with a new key
|
||||
*/
|
||||
|
||||
wsi->u.ws.frame_mask_index = 0;
|
||||
wsi->u.ws.mask_idx = 0;
|
||||
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
|
||||
break;
|
||||
|
@ -825,9 +846,22 @@ handle_first:
|
|||
switch (wsi->u.ws.opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->u.ws.rsv_first_msg = (c & 0x70);
|
||||
wsi->u.ws.frame_is_binary =
|
||||
wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 0xb:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
lwsl_info("illegal opcode\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
|
||||
break;
|
||||
|
@ -944,33 +978,33 @@ handle_first:
|
|||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
|
||||
wsi->u.ws.mask_nonce[0] = c;
|
||||
wsi->u.ws.mask[0] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
|
||||
wsi->u.ws.mask_nonce[1] = c;
|
||||
wsi->u.ws.mask[1] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
|
||||
wsi->u.ws.mask_nonce[2] = c;
|
||||
wsi->u.ws.mask[2] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
|
||||
wsi->u.ws.mask_nonce[3] = c;
|
||||
wsi->u.ws.mask[3] = c;
|
||||
if (c)
|
||||
wsi->u.ws.all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
wsi->u.ws.frame_mask_index = 0;
|
||||
wsi->u.ws.mask_idx = 0;
|
||||
if (wsi->u.ws.rx_packet_length == 0) {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
|
@ -980,19 +1014,16 @@ handle_first:
|
|||
|
||||
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
|
||||
|
||||
if (!wsi->u.ws.rx_user_buffer) {
|
||||
lwsl_err("NULL user buffer...\n");
|
||||
return 1;
|
||||
}
|
||||
assert(wsi->u.ws.rx_ubuf);
|
||||
|
||||
if (wsi->u.ws.all_zero_nonce)
|
||||
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
|
||||
(wsi->u.ws.rx_user_buffer_head++)] = c;
|
||||
wsi->u.ws.rx_ubuf[LWS_PRE +
|
||||
(wsi->u.ws.rx_ubuf_head++)] = c;
|
||||
else
|
||||
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
|
||||
(wsi->u.ws.rx_user_buffer_head++)] =
|
||||
c ^ wsi->u.ws.mask_nonce[
|
||||
(wsi->u.ws.frame_mask_index++) & 3];
|
||||
wsi->u.ws.rx_ubuf[LWS_PRE +
|
||||
(wsi->u.ws.rx_ubuf_head++)] =
|
||||
c ^ wsi->u.ws.mask[
|
||||
(wsi->u.ws.mask_idx++) & 3];
|
||||
|
||||
if (--wsi->u.ws.rx_packet_length == 0) {
|
||||
/* spill because we have the whole frame */
|
||||
|
@ -1006,12 +1037,12 @@ handle_first:
|
|||
*/
|
||||
|
||||
if (!wsi->protocol->rx_buffer_size &&
|
||||
wsi->u.ws.rx_user_buffer_head !=
|
||||
wsi->u.ws.rx_ubuf_head !=
|
||||
LWS_MAX_SOCKET_IO_BUF)
|
||||
break;
|
||||
else
|
||||
if (wsi->protocol->rx_buffer_size &&
|
||||
wsi->u.ws.rx_user_buffer_head !=
|
||||
wsi->u.ws.rx_ubuf_head !=
|
||||
wsi->protocol->rx_buffer_size)
|
||||
break;
|
||||
|
||||
|
@ -1044,9 +1075,9 @@ spill:
|
|||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
wsi->user_space,
|
||||
&wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head))
|
||||
&wsi->u.ws.rx_ubuf[
|
||||
LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head))
|
||||
return -1;
|
||||
|
||||
lwsl_parser("server sees client close packet\n");
|
||||
|
@ -1057,7 +1088,7 @@ spill:
|
|||
|
||||
case LWSWSOPC_PING:
|
||||
lwsl_info("received %d byte ping, sending pong\n",
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
if (wsi->u.ws.ping_pending_flag) {
|
||||
/*
|
||||
|
@ -1069,29 +1100,29 @@ spill:
|
|||
}
|
||||
process_as_ping:
|
||||
/* control packets can only be < 128 bytes long */
|
||||
if (wsi->u.ws.rx_user_buffer_head > 128 - 3) {
|
||||
if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
|
||||
lwsl_parser("DROP PING payload too large\n");
|
||||
goto ping_drop;
|
||||
}
|
||||
|
||||
/* stash the pong payload */
|
||||
memcpy(wsi->u.ws.ping_payload_buf + LWS_SEND_BUFFER_PRE_PADDING,
|
||||
&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
|
||||
&wsi->u.ws.rx_ubuf[LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_user_buffer_head;
|
||||
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
|
||||
wsi->u.ws.ping_pending_flag = 1;
|
||||
|
||||
/* get it sent as soon as possible */
|
||||
lws_callback_on_writable(wsi);
|
||||
ping_drop:
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
return 0;
|
||||
|
||||
case LWSWSOPC_PONG:
|
||||
lwsl_info("received pong\n");
|
||||
lwsl_hexdump(&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
wsi->u.ws.rx_user_buffer_head);
|
||||
lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
|
||||
wsi->u.ws.rx_ubuf_head);
|
||||
|
||||
/* issue it */
|
||||
callback_action = LWS_CALLBACK_RECEIVE_PONG;
|
||||
|
@ -1111,17 +1142,15 @@ ping_drop:
|
|||
* state machine.
|
||||
*/
|
||||
|
||||
eff_buf.token = &wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING];
|
||||
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
|
||||
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
|
||||
&eff_buf, 0) <= 0) /* not handle or fail */
|
||||
lwsl_ext("ext opc opcode 0x%x unknown\n",
|
||||
wsi->u.ws.opcode);
|
||||
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1131,13 +1160,34 @@ ping_drop:
|
|||
* so it can be sent straight out again using lws_write
|
||||
*/
|
||||
|
||||
eff_buf.token = &wsi->u.ws.rx_user_buffer[
|
||||
LWS_SEND_BUFFER_PRE_PADDING];
|
||||
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
|
||||
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
|
||||
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_PAYLOAD_RX, &eff_buf, 0) < 0)
|
||||
drain_extension:
|
||||
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
|
||||
|
||||
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
|
||||
wsi->state == LWSS_AWAITING_CLOSE_ACK)
|
||||
goto already_done;
|
||||
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
|
||||
if (n < 0) {
|
||||
/*
|
||||
* we may rely on this to get RX, just drop connection
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rx_draining_ext && eff_buf.token_len == 0)
|
||||
goto already_done;
|
||||
|
||||
if (n && eff_buf.token_len) {
|
||||
/* extension had more... main loop will come back */
|
||||
wsi->u.ws.rx_draining_ext = 1;
|
||||
wsi->u.ws.rx_draining_ext_list = wsi->context->rx_draining_ext_list;
|
||||
wsi->context->rx_draining_ext_list = wsi;
|
||||
}
|
||||
|
||||
if (eff_buf.token_len > 0 ||
|
||||
callback_action == LWS_CALLBACK_RECEIVE_PONG) {
|
||||
|
@ -1146,7 +1196,7 @@ ping_drop:
|
|||
if (wsi->protocol->callback) {
|
||||
|
||||
if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
|
||||
lwsl_info("Doing pong callback\n");
|
||||
lwsl_info("Doing pong callback\n");
|
||||
|
||||
ret = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
|
@ -1160,7 +1210,8 @@ ping_drop:
|
|||
lwsl_err("No callback on payload spill!\n");
|
||||
}
|
||||
|
||||
wsi->u.ws.rx_user_buffer_head = 0;
|
||||
already_done:
|
||||
wsi->u.ws.rx_ubuf_head = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -250,8 +250,8 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
network_sock:
|
||||
#endif
|
||||
|
||||
if (lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
return 1;
|
||||
|
||||
if (wsi->position_in_fds_table < 0) {
|
||||
|
|
|
@ -292,6 +292,9 @@ extern "C" {
|
|||
#ifndef LWS_MAX_EXTENSIONS_ACTIVE
|
||||
#define LWS_MAX_EXTENSIONS_ACTIVE 3
|
||||
#endif
|
||||
#ifndef LWS_MAX_EXT_OFFERS
|
||||
#define LWS_MAX_EXT_OFFERS 8
|
||||
#endif
|
||||
#ifndef SPEC_LATEST_SUPPORTED
|
||||
#define SPEC_LATEST_SUPPORTED 13
|
||||
#endif
|
||||
|
@ -373,9 +376,9 @@ enum lws_pending_protocol_send {
|
|||
enum lws_rx_parse_state {
|
||||
LWS_RXPS_NEW,
|
||||
|
||||
LWS_RXPS_04_MASK_NONCE_1,
|
||||
LWS_RXPS_04_MASK_NONCE_2,
|
||||
LWS_RXPS_04_MASK_NONCE_3,
|
||||
LWS_RXPS_04_mask_1,
|
||||
LWS_RXPS_04_mask_2,
|
||||
LWS_RXPS_04_mask_3,
|
||||
|
||||
LWS_RXPS_04_FRAME_HDR_1,
|
||||
LWS_RXPS_04_FRAME_HDR_LEN,
|
||||
|
@ -525,6 +528,8 @@ struct lws_context {
|
|||
const struct lws_protocols *protocols;
|
||||
void *http_header_data;
|
||||
struct allocated_headers *ah_pool;
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_CTX *ssl_client_ctx;
|
||||
|
@ -795,7 +800,7 @@ struct _lws_http2_related {
|
|||
unsigned int my_stream_id;
|
||||
unsigned int child_count;
|
||||
int my_priority;
|
||||
|
||||
|
||||
unsigned int END_STREAM:1;
|
||||
unsigned int END_HEADERS:1;
|
||||
unsigned int send_END_STREAM:1;
|
||||
|
@ -834,20 +839,25 @@ struct _lws_header_related {
|
|||
};
|
||||
|
||||
struct _lws_websocket_related {
|
||||
char *rx_user_buffer;
|
||||
char *rx_ubuf;
|
||||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
size_t rx_packet_length;
|
||||
unsigned int rx_user_buffer_head;
|
||||
unsigned char mask_nonce[4];
|
||||
unsigned int rx_ubuf_head;
|
||||
unsigned char mask[4];
|
||||
/* Also used for close content... control opcode == < 128 */
|
||||
unsigned char ping_payload_buf[128 - 3 + LWS_SEND_BUFFER_PRE_PADDING];
|
||||
unsigned char ping_payload_buf[128 - 3 + LWS_PRE];
|
||||
|
||||
unsigned char ping_payload_len;
|
||||
unsigned char frame_mask_index;
|
||||
unsigned char mask_idx;
|
||||
unsigned char opcode;
|
||||
unsigned char rsv;
|
||||
unsigned char rsv_first_msg;
|
||||
/* zero if no info, or length including 2-byte close code */
|
||||
unsigned char close_in_ping_buffer_len;
|
||||
unsigned char utf8;
|
||||
unsigned char stashed_write_type;
|
||||
unsigned char tx_draining_stashed_wp;
|
||||
|
||||
unsigned int final:1;
|
||||
unsigned int frame_is_binary:1;
|
||||
|
@ -861,6 +871,10 @@ struct _lws_websocket_related {
|
|||
unsigned int owed_a_fin:1;
|
||||
unsigned int check_utf8:1;
|
||||
unsigned int defeat_check_utf8:1;
|
||||
unsigned int pmce_compressed_message:1;
|
||||
unsigned int stashed_write_pending:1;
|
||||
unsigned int rx_draining_ext:1;
|
||||
unsigned int tx_draining_ext:1;
|
||||
};
|
||||
|
||||
struct lws {
|
||||
|
@ -896,7 +910,7 @@ struct lws {
|
|||
unsigned char *trunc_alloc; /* non-NULL means buffering in progress */
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
void *active_extensions_user[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
SSL *ssl;
|
||||
|
@ -936,7 +950,7 @@ struct lws {
|
|||
|
||||
/* chars */
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
unsigned char count_active_extensions;
|
||||
unsigned char count_act_ext;
|
||||
#endif
|
||||
unsigned char ietf_spec_revision;
|
||||
char mode; /* enum connection_mode */
|
||||
|
@ -1042,15 +1056,16 @@ lws_any_extension_handled(struct lws *wsi,
|
|||
void *v, size_t len);
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_ext_cb_wsi_active_exts(struct lws *wsi, int reason,
|
||||
lws_ext_cb_active(struct lws *wsi, int reason,
|
||||
void *buf, int len);
|
||||
LWS_EXTERN int
|
||||
lws_ext_cb_all_exts(struct lws_context *context,
|
||||
struct lws *wsi, int reason,
|
||||
void *arg, int len);
|
||||
|
||||
#else
|
||||
#define lws_any_extension_handled(_a, _b, _c, _d) (0)
|
||||
#define lws_ext_cb_wsi_active_exts(_a, _b, _c, _d) (0)
|
||||
#define lws_ext_cb_active(_a, _b, _c, _d) (0)
|
||||
#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0)
|
||||
#define lws_issue_raw_ext_access lws_issue_raw
|
||||
#define lws_context_init_extensions(_a, _b)
|
||||
|
@ -1137,7 +1152,7 @@ int lws_context_init_server(struct lws_context_creation_info *info,
|
|||
LWS_EXTERN int
|
||||
handshake_0405(struct lws_context *context, struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
LWS_EXTERN void
|
||||
lws_server_get_canonical_hostname(struct lws_context *context,
|
||||
struct lws_context_creation_info *info);
|
||||
|
|
|
@ -26,20 +26,19 @@
|
|||
LWS_VISIBLE int
|
||||
lws_extension_server_handshake(struct lws *wsi, char **p)
|
||||
{
|
||||
int n;
|
||||
char *c;
|
||||
char ext_name[128];
|
||||
const struct lws_extension *ext;
|
||||
struct lws_context *context = wsi->context;
|
||||
const struct lws_extension *ext;
|
||||
char ext_name[128];
|
||||
int ext_count = 0;
|
||||
char *c;//, *start;
|
||||
int more = 1;
|
||||
char ignore;
|
||||
int n, m;
|
||||
|
||||
/*
|
||||
* Figure out which extensions the client has that we want to
|
||||
* enable on this connection, and give him back the list
|
||||
*/
|
||||
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
|
||||
return 0;
|
||||
|
||||
|
@ -49,21 +48,21 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
*/
|
||||
|
||||
if (lws_hdr_copy(wsi, (char *)context->serv_buf,
|
||||
sizeof(context->serv_buf),
|
||||
WSI_TOKEN_EXTENSIONS) < 0)
|
||||
sizeof(context->serv_buf), WSI_TOKEN_EXTENSIONS) < 0)
|
||||
return 1;
|
||||
|
||||
c = (char *)context->serv_buf;
|
||||
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
|
||||
wsi->count_active_extensions = 0;
|
||||
wsi->count_act_ext = 0;
|
||||
n = 0;
|
||||
ignore = 0;
|
||||
// start = c;
|
||||
while (more) {
|
||||
|
||||
if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
|
||||
if (ext_name[n] == ';')
|
||||
if (*c && (*c != ',' && *c != '\t')) {
|
||||
if (*c == ';')
|
||||
ignore = 1;
|
||||
if (ignore) {
|
||||
if (ignore || *c == ' ') {
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
|
@ -93,14 +92,21 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0
|
||||
m = ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CB_ARGS_VALIDATE,
|
||||
NULL, start + n, 0);
|
||||
if (m) {
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* oh, we do support this one he asked for... but let's
|
||||
* ask user code if it's OK to apply it on this
|
||||
* particular connection + protocol
|
||||
*/
|
||||
|
||||
n = lws_get_context(wsi)->protocols[0].callback(wsi,
|
||||
m = lws_get_context(wsi)->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
|
||||
wsi->user_space, ext_name, 0);
|
||||
|
||||
|
@ -110,7 +116,7 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
* unhandled
|
||||
*/
|
||||
|
||||
if (n) {
|
||||
if (m) {
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
@ -120,40 +126,28 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
if (ext_count)
|
||||
*(*p)++ = ',';
|
||||
else
|
||||
LWS_CPYAPP(*p,
|
||||
"\x0d\x0aSec-WebSocket-Extensions: ");
|
||||
LWS_CPYAPP(*p, "\x0d\x0aSec-WebSocket-Extensions: ");
|
||||
*p += sprintf(*p, "%s", ext_name);
|
||||
ext_count++;
|
||||
|
||||
/* instantiate the extension on this conn */
|
||||
|
||||
wsi->active_extensions_user[
|
||||
wsi->count_active_extensions] =
|
||||
lws_zalloc(ext->per_session_data_size);
|
||||
if (wsi->active_extensions_user[
|
||||
wsi->count_active_extensions] == NULL) {
|
||||
lwsl_err("Out of mem\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
wsi->active_extensions[
|
||||
wsi->count_active_extensions] = ext;
|
||||
wsi->active_extensions[wsi->count_act_ext] = ext;
|
||||
|
||||
/* allow him to construct his context */
|
||||
|
||||
ext->callback(lws_get_context(wsi),
|
||||
ext, wsi,
|
||||
LWS_EXT_CALLBACK_CONSTRUCT,
|
||||
wsi->active_extensions_user[
|
||||
wsi->count_active_extensions], NULL, 0);
|
||||
ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CB_CONSTRUCT,
|
||||
(void *)&wsi->act_ext_user[wsi->count_act_ext],
|
||||
NULL, 0);
|
||||
|
||||
wsi->count_active_extensions++;
|
||||
lwsl_parser("count_active_extensions <- %d\n",
|
||||
wsi->count_active_extensions);
|
||||
wsi->count_act_ext++;
|
||||
lwsl_parser("count_act_ext <- %d\n", wsi->count_act_ext);
|
||||
|
||||
ext++;
|
||||
}
|
||||
|
||||
//start = c;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
|
@ -208,7 +202,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
/* make a buffer big enough for everything */
|
||||
|
||||
response = (char *)context->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_SEND_BUFFER_PRE_PADDING;
|
||||
response = (char *)context->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
|
||||
p = response;
|
||||
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
||||
"Upgrade: WebSocket\x0d\x0a"
|
||||
|
@ -240,7 +234,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
|
||||
|
||||
if (!lws_any_extension_handled(wsi, LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
|
||||
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
|
||||
response, p - response)) {
|
||||
|
||||
/* okay send the handshake response accepting the connection */
|
||||
|
|
37
lib/server.c
37
lib/server.c
|
@ -539,9 +539,9 @@ upgrade_ws:
|
|||
n = wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = LWS_MAX_SOCKET_IO_BUF;
|
||||
n += LWS_SEND_BUFFER_PRE_PADDING;
|
||||
wsi->u.ws.rx_user_buffer = lws_malloc(n);
|
||||
if (!wsi->u.ws.rx_user_buffer) {
|
||||
n += LWS_PRE;
|
||||
wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
|
||||
if (!wsi->u.ws.rx_ubuf) {
|
||||
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
|
@ -876,11 +876,9 @@ LWS_VISIBLE int lws_serve_http_file(struct lws *wsi, const char *file,
|
|||
int other_headers_len)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
unsigned char *response = context->serv_buf +
|
||||
LWS_SEND_BUFFER_PRE_PADDING;
|
||||
unsigned char *response = context->serv_buf + LWS_PRE;
|
||||
unsigned char *p = response;
|
||||
unsigned char *end = p + sizeof(context->serv_buf) -
|
||||
LWS_SEND_BUFFER_PRE_PADDING;
|
||||
unsigned char *end = p + sizeof(context->serv_buf) - LWS_PRE;
|
||||
int ret = 0;
|
||||
|
||||
wsi->u.http.fd = lws_plat_file_open(wsi, file, &wsi->u.http.filelen,
|
||||
|
@ -929,38 +927,47 @@ LWS_VISIBLE int lws_serve_http_file(struct lws *wsi, const char *file,
|
|||
}
|
||||
|
||||
int
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char *buf, size_t len)
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
size_t n = 0;
|
||||
int m;
|
||||
|
||||
lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
|
||||
#if 0
|
||||
lwsl_parser("received %d byte packet\n", (int)len);
|
||||
lwsl_hexdump(buf, len);
|
||||
lwsl_hexdump(*buf, len);
|
||||
#endif
|
||||
|
||||
/* let the rx protocol state machine have as much as it needs */
|
||||
|
||||
while (n < len) {
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
lws_rxflow_cache(wsi, buf, n, len);
|
||||
|
||||
lws_rxflow_cache(wsi, *buf, 0, len);
|
||||
lwsl_parser("%s: cached %d\n", __func__, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wsi->u.ws.rx_draining_ext) {
|
||||
m = lws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
|
||||
/* process the byte */
|
||||
m = lws_rx_sm(wsi, buf[n++]);
|
||||
m = lws_rx_sm(wsi, *(*buf)++);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
len--;
|
||||
}
|
||||
|
||||
lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
146
lib/service.c
146
lib/service.c
|
@ -53,12 +53,18 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
#endif
|
||||
int ret, m, n;
|
||||
|
||||
/* pending truncated sends have uber priority */
|
||||
/*
|
||||
* user callback is lowest priority to get these notifications
|
||||
* actually, since other pending things cannot be disordered
|
||||
*/
|
||||
|
||||
/* Priority 1: pending truncated sends are incomplete ws fragments
|
||||
* If anything else sent first the protocol would be
|
||||
* corrupted.
|
||||
*/
|
||||
if (wsi->trunc_len) {
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
||||
wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0) {
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0) {
|
||||
lwsl_info("%s signalling to close\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
@ -68,8 +74,10 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)
|
||||
return -1; /* retry closing now */
|
||||
|
||||
|
||||
#ifdef LWS_USE_HTTP2
|
||||
/* protocol packets are next */
|
||||
/* Priority 2: protocol packets
|
||||
*/
|
||||
if (wsi->pps) {
|
||||
lwsl_info("servicing pps %d\n", wsi->pps);
|
||||
switch (wsi->pps) {
|
||||
|
@ -86,8 +94,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
return 0; /* leave POLLOUT active */
|
||||
}
|
||||
#endif
|
||||
/* pending control packets have next priority */
|
||||
|
||||
/* Priority 3: pending control packets (pong or close)
|
||||
*/
|
||||
if ((wsi->state == LWSS_ESTABLISHED &&
|
||||
wsi->u.ws.ping_pending_flag) ||
|
||||
(wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
|
||||
|
@ -96,8 +104,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
if (wsi->u.ws.payload_is_close)
|
||||
write_type = LWS_WRITE_CLOSE;
|
||||
|
||||
n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
|
||||
LWS_SEND_BUFFER_PRE_PADDING],
|
||||
n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
|
||||
wsi->u.ws.ping_payload_len, write_type);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
@ -112,15 +119,30 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* if we are closing, don't confuse the user with writeable cb */
|
||||
|
||||
/* Priority 4: if we are closing, not allowed to send more data frags
|
||||
* which means user callback or tx ext flush banned now
|
||||
*/
|
||||
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
|
||||
goto user_service;
|
||||
|
||||
/* if nothing critical, user can get the callback */
|
||||
/* Priority 5: Tx path extension with more to send
|
||||
*
|
||||
* These are handled as new fragments each time around
|
||||
* So while we must block new writeable callback to enforce
|
||||
* payload ordering, but since they are always complete
|
||||
* fragments control packets can interleave OK.
|
||||
*/
|
||||
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
|
||||
lwsl_ext("SERVICING TX EXT DRAINING\n");
|
||||
if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
|
||||
return -1;
|
||||
/* leave POLLOUT active */
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
|
||||
NULL, 0);
|
||||
/* Priority 6: user can get the callback
|
||||
*/
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
if (!wsi->extension_data_pending)
|
||||
goto user_service;
|
||||
|
@ -144,9 +166,9 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
|
||||
/* give every extension a chance to spill */
|
||||
|
||||
m = lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
|
||||
&eff_buf, 0);
|
||||
m = lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_PACKET_TX_PRESEND,
|
||||
&eff_buf, 0);
|
||||
if (m < 0) {
|
||||
lwsl_err("ext reports fatal error\n");
|
||||
return -1;
|
||||
|
@ -162,7 +184,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
|
||||
if (eff_buf.token_len) {
|
||||
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
|
||||
eff_buf.token_len);
|
||||
eff_buf.token_len);
|
||||
if (n < 0) {
|
||||
lwsl_info("closing from POLLOUT spill\n");
|
||||
return -1;
|
||||
|
@ -276,7 +298,7 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
|
|||
* if extensions want in on it (eg, we are a mux parent)
|
||||
* give them a chance to service child timeouts
|
||||
*/
|
||||
if (lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_1HZ,
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ,
|
||||
NULL, sec) < 0)
|
||||
return 0;
|
||||
|
||||
|
@ -495,7 +517,7 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
|
|||
case LWSCM_WS_CLIENT:
|
||||
case LWSCM_HTTP2_SERVING:
|
||||
|
||||
/* the guy requested a callback when it was OK to write */
|
||||
/* 1: something requested a callback when it was OK to write */
|
||||
|
||||
if ((pollfd->revents & LWS_POLLOUT) &&
|
||||
(wsi->state == LWSS_ESTABLISHED ||
|
||||
|
@ -503,16 +525,77 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
|
|||
wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS ||
|
||||
wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
|
||||
wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) &&
|
||||
lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
|
||||
wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
|
||||
lwsl_info("lws_service_fd: closing\n");
|
||||
goto close_and_handled;
|
||||
}
|
||||
#if 1
|
||||
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
|
||||
wsi->state == LWSS_AWAITING_CLOSE_ACK) {
|
||||
/*
|
||||
* we stopped caring about anything except control
|
||||
* packets. Force flow control off, defeat tx
|
||||
* draining.
|
||||
*/
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
wsi->u.ws.tx_draining_ext = 0;
|
||||
}
|
||||
#endif
|
||||
if (wsi->u.ws.tx_draining_ext) {
|
||||
/* we cannot deal with new RX until the TX ext
|
||||
* path has been drained. It's because new
|
||||
* rx will, eg, crap on the wsi rx buf that
|
||||
* may be needed to retain state.
|
||||
*
|
||||
* TX ext drain path MUST go through event loop
|
||||
* to avoid blocking.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
if (wsi->rxflow_buffer &&
|
||||
(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
lwsl_info("draining rxflow\n");
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
|
||||
/* We cannot deal with any kind of new RX
|
||||
* because we are RX-flowcontrolled.
|
||||
*/
|
||||
break;
|
||||
|
||||
/* 2: RX Extension needs to be drained
|
||||
*/
|
||||
|
||||
if (wsi->state == LWSS_ESTABLISHED &&
|
||||
wsi->u.ws.rx_draining_ext) {
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (wsi->mode == LWSCM_WS_CLIENT) {
|
||||
n = lws_client_rx_sm(wsi, 0);
|
||||
if (n < 0)
|
||||
/* we closed wsi */
|
||||
n = 0;
|
||||
} else
|
||||
#endif
|
||||
n = lws_rx_sm(wsi, 0);
|
||||
|
||||
goto handled;
|
||||
}
|
||||
|
||||
if (wsi->u.ws.rx_draining_ext)
|
||||
/*
|
||||
* We have RX EXT content to drain, but can't do it
|
||||
* right now. That means we cannot do anything lower
|
||||
* priority either.
|
||||
*/
|
||||
break;
|
||||
|
||||
/* 3: RX Flowcontrol buffer needs to be drained
|
||||
*/
|
||||
|
||||
if (wsi->rxflow_buffer) {
|
||||
lwsl_info("draining rxflow (len %d)\n",
|
||||
wsi->rxflow_len - wsi->rxflow_pos
|
||||
);
|
||||
/* well, drain it */
|
||||
eff_buf.token = (char *)wsi->rxflow_buffer +
|
||||
wsi->rxflow_pos;
|
||||
|
@ -521,14 +604,13 @@ lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
|
|||
goto drain;
|
||||
}
|
||||
|
||||
/* any incoming data ready? */
|
||||
/* notice if rx flow going off raced poll(), rx flow wins */
|
||||
if (wsi->rxflow_buffer ||
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
/* 4: any incoming data ready?
|
||||
* notice if rx flow going off raced poll(), rx flow wins
|
||||
*/
|
||||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
break;
|
||||
read:
|
||||
eff_buf.token_len = lws_ssl_capable_read(wsi,
|
||||
context->serv_buf,
|
||||
eff_buf.token_len = lws_ssl_capable_read(wsi, context->serv_buf,
|
||||
pending ? pending :
|
||||
sizeof(context->serv_buf));
|
||||
switch (eff_buf.token_len) {
|
||||
|
@ -562,8 +644,8 @@ drain:
|
|||
do {
|
||||
more = 0;
|
||||
|
||||
m = lws_ext_cb_wsi_active_exts(wsi,
|
||||
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, &eff_buf, 0);
|
||||
m = lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_PACKET_RX_PREPARSE, &eff_buf, 0);
|
||||
if (m < 0)
|
||||
goto close_and_handled;
|
||||
if (m)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
(<i>struct lws *</i> <b>wsi</b>,
|
||||
<i>unsigned char *</i> <b>buf</b>,
|
||||
<i>size_t</i> <b>len</b>,
|
||||
<i>enum lws_write_protocol</i> <b>protocol</b>)
|
||||
<i>enum lws_write_protocol</i> <b>wp</b>)
|
||||
<h3>Arguments</h3>
|
||||
<dl>
|
||||
<dt><b>wsi</b>
|
||||
|
@ -12,16 +12,10 @@
|
|||
<dt><b>buf</b>
|
||||
<dd>The data to send. For data being sent on a websocket
|
||||
connection (ie, not default http), this buffer MUST have
|
||||
LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer.
|
||||
LWS_PRE bytes valid BEFORE the pointer.
|
||||
This is so the protocol header data can be added in-situ.
|
||||
<dt><b>len</b>
|
||||
<dd>Count of the data bytes in the payload starting from buf
|
||||
<dt><b>protocol</b>
|
||||
<dd>Use LWS_WRITE_HTTP to reply to an http connection, and one
|
||||
of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
|
||||
data on a websockets connection. Remember to allow the extra
|
||||
bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
|
||||
are used.
|
||||
</dl>
|
||||
<h3>Description</h3>
|
||||
<blockquote>
|
||||
|
@ -110,50 +104,19 @@ Helper to report HTTP errors back to the client cleanly and
|
|||
consistently
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2>lws_client_connect - Connect to another websocket server</h2>
|
||||
<h2>lws_client_connect_info - Connect to another websocket server</h2>
|
||||
<i>struct lws *</i>
|
||||
<b>lws_client_connect</b>
|
||||
(<i>struct lws_context *</i> <b>context</b>,
|
||||
<i>const char *</i> <b>address</b>,
|
||||
<i>int</i> <b>port</b>,
|
||||
<i>int</i> <b>ssl_connection</b>,
|
||||
<i>const char *</i> <b>path</b>,
|
||||
<i>const char *</i> <b>host</b>,
|
||||
<i>const char *</i> <b>origin</b>,
|
||||
<i>const char *</i> <b>protocol</b>,
|
||||
<i>int</i> <b>ietf_version_or_minus_one</b>)
|
||||
<b>lws_client_connect_info</b>
|
||||
(<i>struct lws_client_connect_info *</i> <b>i</b>)
|
||||
<h3>Arguments</h3>
|
||||
<dl>
|
||||
<dt><b>context</b>
|
||||
<dd>Websocket context
|
||||
<dt><b>address</b>
|
||||
<dd>Remote server address, eg, "myserver.com"
|
||||
<dt><b>port</b>
|
||||
<dd>Port to connect to on the remote server, eg, 80
|
||||
<dt><b>ssl_connection</b>
|
||||
<dd>0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
|
||||
signed certs
|
||||
<dt><b>path</b>
|
||||
<dd>Websocket path on server
|
||||
<dt><b>host</b>
|
||||
<dd>Hostname on server
|
||||
<dt><b>origin</b>
|
||||
<dd>Socket origin name
|
||||
<dt><b>protocol</b>
|
||||
<dd>Comma-separated list of protocols being asked for from
|
||||
the server, or just one. The server will pick the one it
|
||||
likes best. If you don't want to specify a protocol, which is
|
||||
legal, use NULL here.
|
||||
<dt><b>ietf_version_or_minus_one</b>
|
||||
<dd>-1 to ask to connect using the default, latest
|
||||
protocol supported, or the specific protocol ordinal
|
||||
</dl>
|
||||
<h3>Description</h3>
|
||||
<blockquote>
|
||||
This function creates a connection to a remote server
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2>lws_client_connect_extended - Connect to another websocket server</h2>
|
||||
<h2>lws_client_connect_extended - Connect to another websocket server DEPRECAATED use lws_client_connect_info</h2>
|
||||
<i>struct lws *</i>
|
||||
<b>lws_client_connect_extended</b>
|
||||
(<i>struct lws_context *</i> <b>context</b>,
|
||||
|
@ -1228,7 +1191,7 @@ connection.
|
|||
<dt><b>reason</b>
|
||||
<dd>The reason for the call
|
||||
<dt><b>user</b>
|
||||
<dd>Pointer to per-session user data allocated by library
|
||||
<dd>Pointer to ptr to per-session user data allocated by library
|
||||
<dt><b>in</b>
|
||||
<dd>Pointer used for some callback reasons
|
||||
<dt><b>len</b>
|
||||
|
@ -1244,7 +1207,7 @@ Libwebsockets takes care of allocating and freeing "user" memory for
|
|||
each active extension on each connection. That is what is pointed to
|
||||
by the <tt><b>user</b></tt> parameter.
|
||||
</blockquote>
|
||||
<h3>LWS_EXT_CALLBACK_CONSTRUCT</h3>
|
||||
<h3>LWS_EXT_CB_CONSTRUCT</h3>
|
||||
<blockquote>
|
||||
called when the server has decided to
|
||||
select this extension from the list provided by the client,
|
||||
|
@ -1253,14 +1216,14 @@ the connection with this extension active. This gives the
|
|||
extension a chance to initialize its connection context found
|
||||
in <tt><b>user</b></tt>.
|
||||
</blockquote>
|
||||
<h3>LWS_EXT_CALLBACK_CLIENT_CONSTRUCT</h3>
|
||||
<h3>LWS_EXT_CB_CLIENT_CONSTRUCT</h3>
|
||||
<blockquote>
|
||||
same as LWS_EXT_CALLBACK_CONSTRUCT
|
||||
same as LWS_EXT_CB_CONSTRUCT
|
||||
but called when client is instantiating this extension. Some
|
||||
extensions will work the same on client and server side and then
|
||||
you can just merge handlers for both CONSTRUCTS.
|
||||
</blockquote>
|
||||
<h3>LWS_EXT_CALLBACK_DESTROY</h3>
|
||||
<h3>LWS_EXT_CB_DESTROY</h3>
|
||||
<blockquote>
|
||||
called when the connection the extension was
|
||||
being used on is about to be closed and deallocated. It's the
|
||||
|
@ -1269,7 +1232,7 @@ allocated in the user data (pointed to by <tt><b>user</b></tt>) before the
|
|||
user data is deleted. This same callback is used whether you
|
||||
are in client or server instantiation context.
|
||||
</blockquote>
|
||||
<h3>LWS_EXT_CALLBACK_PACKET_RX_PREPARSE</h3>
|
||||
<h3>LWS_EXT_CB_PACKET_RX_PREPARSE</h3>
|
||||
<blockquote>
|
||||
when this extension was active on
|
||||
a connection, and a packet of data arrived at the connection,
|
||||
|
@ -1283,10 +1246,10 @@ length. If the extension will grow the content, it should use
|
|||
a new buffer allocated in its private user context data and
|
||||
set the pointed-to lws_tokens members to point to its buffer.
|
||||
</blockquote>
|
||||
<h3>LWS_EXT_CALLBACK_PACKET_TX_PRESEND</h3>
|
||||
<h3>LWS_EXT_CB_PACKET_TX_PRESEND</h3>
|
||||
<blockquote>
|
||||
this works the same way as
|
||||
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE above, except it gives the
|
||||
LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the
|
||||
extension a chance to change websocket data just before it will
|
||||
be sent out. Using the same lws_token pointer scheme in <tt><b>in</b></tt>,
|
||||
the extension can change the buffer and the length to be
|
||||
|
@ -1325,7 +1288,7 @@ you support. If the frame size is exceeded, there is no
|
|||
error, but the buffer will spill to the user callback when
|
||||
full, which you can detect by using
|
||||
<b>lws_remaining_packet_payload</b>. Notice that you
|
||||
just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING
|
||||
just talk about frame size here, the LWS_PRE
|
||||
and post-padding are automatically also allocated on top.
|
||||
<dt><b>id</b>
|
||||
<dd>ignored by lws, but useful to contain user information bound
|
||||
|
@ -1354,27 +1317,33 @@ there is no agreed protocol name, that's true during HTTP part of the
|
|||
header.
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2>struct lws_extension - An extension we know how to cope with</h2>
|
||||
<b>struct lws_extension</b> {<br>
|
||||
<h2>struct lws_ext_options - Option arguments to the extension. These are used in the negotiation at ws upgrade time. The helper function lws_ext_parse_options() uses these to generate callbacks</h2>
|
||||
<b>struct lws_ext_options</b> {<br>
|
||||
<i>const char *</i> <b>name</b>;<br>
|
||||
<i>lws_extension_callback_function *</i> <b>callback</b>;<br>
|
||||
<i>size_t</i> <b>per_session_data_size</b>;<br>
|
||||
<i>void *</i> <b>per_context_private_data</b>;<br>
|
||||
<i>enum lws_ext_options_types</i> <b>type</b>;<br>
|
||||
};<br>
|
||||
<h3>Members</h3>
|
||||
<dl>
|
||||
<dt><b>name</b>
|
||||
<dd>Formal extension name, eg, "deflate-stream"
|
||||
<dd>Option name, eg, "server_no_context_takeover"
|
||||
<dt><b>type</b>
|
||||
<dd>What kind of args the option can take
|
||||
</dl>
|
||||
<hr>
|
||||
<h2>struct lws_extension - An extension we know how to cope with</h2>
|
||||
<b>struct lws_extension</b> {<br>
|
||||
<i>const char *</i> <b>name</b>;<br>
|
||||
<i>lws_extension_callback_function *</i> <b>callback</b>;<br>
|
||||
<i>const char *</i> <b>client_offer</b>;<br>
|
||||
};<br>
|
||||
<h3>Members</h3>
|
||||
<dl>
|
||||
<dt><b>name</b>
|
||||
<dd>Formal extension name, eg, "permessage-deflate"
|
||||
<dt><b>callback</b>
|
||||
<dd>Service callback
|
||||
<dt><b>per_session_data_size</b>
|
||||
<dd>Libwebsockets will auto-malloc this much
|
||||
memory for the use of the extension, a pointer
|
||||
to it comes in the <tt><b>user</b></tt> callback parameter
|
||||
<dt><b>per_context_private_data</b>
|
||||
<dd>Optional storage for this extension that
|
||||
is per-context, so it can track stuff across
|
||||
all sessions, etc, if it wants
|
||||
<dt><b>client_offer</b>
|
||||
<dd>String containing exts and options client offers
|
||||
</dl>
|
||||
<hr>
|
||||
<h2>struct lws_context_creation_info - parameters to create context with</h2>
|
||||
|
@ -1434,8 +1403,9 @@ server cert from, otherwise NULL for unencrypted
|
|||
<dt><b>ssl_private_key_filepath</b>
|
||||
<dd>filepath to private key if wanting SSL mode;
|
||||
if this is set to NULL but sll_cert_filepath is set, the
|
||||
OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called to allow
|
||||
setting of the private key directly via openSSL library calls
|
||||
OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called
|
||||
to allow setting of the private key directly via openSSL
|
||||
library calls
|
||||
<dt><b>ssl_ca_filepath</b>
|
||||
<dd>CA certificate filepath or NULL
|
||||
<dt><b>ssl_cipher_list</b>
|
||||
|
@ -1447,7 +1417,8 @@ or you can leave it as NULL to get "DEFAULT"
|
|||
If proxy auth is required, use format
|
||||
"username:password<tt><b>server</b></tt>:port"
|
||||
<dt><b>http_proxy_port</b>
|
||||
<dd>If http_proxy_address was non-NULL, uses this port at the address
|
||||
<dd>If http_proxy_address was non-NULL, uses this port at
|
||||
the address
|
||||
<dt><b>gid</b>
|
||||
<dd>group id to change to after setting listen socket, or -1.
|
||||
<dt><b>uid</b>
|
||||
|
|
|
@ -109,14 +109,10 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
lwsl_notice("denied deflate-stream extension\n");
|
||||
return 1;
|
||||
}
|
||||
if ((strcmp(in, "deflate-frame") == 0) && deny_deflate) {
|
||||
lwsl_notice("denied deflate-frame extension\n");
|
||||
if ((strcmp(in, "x-webkit-deflate-frame") == 0))
|
||||
return 1;
|
||||
}
|
||||
if ((strcmp(in, "x-google-mux") == 0) && deny_mux) {
|
||||
lwsl_notice("denied x-google-mux extension\n");
|
||||
if ((strcmp(in, "deflate-frame") == 0))
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -134,7 +130,7 @@ static int
|
|||
callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096];
|
||||
unsigned char buf[LWS_PRE + 4096];
|
||||
unsigned int rands[4];
|
||||
int l = 0;
|
||||
int n;
|
||||
|
@ -173,15 +169,15 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
for (n = 0; n < 1; n++) {
|
||||
lws_get_random(lws_get_context(wsi), rands, sizeof(rands));
|
||||
l += sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING + l],
|
||||
l += sprintf((char *)&buf[LWS_PRE + l],
|
||||
"c #%06X %d %d %d;",
|
||||
(int)rands[0] & 0xffffff,
|
||||
(int)rands[1] % 500,
|
||||
(int)rands[2] % 250,
|
||||
(int)rands[3] % 24);
|
||||
(int)abs(rands[1] % 500),
|
||||
(int)abs(rands[2] % 250),
|
||||
(int)abs(rands[3] % 24));
|
||||
}
|
||||
|
||||
n = lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], l,
|
||||
n = lws_write(wsi, &buf[LWS_PRE], l,
|
||||
opts | LWS_WRITE_TEXT);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
@ -225,6 +221,22 @@ static struct lws_protocols protocols[] = {
|
|||
{ NULL, NULL, 0, 0 } /* end */
|
||||
};
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate; client_max_window_bits"
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"deflate_frame"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
void sighandler(int sig)
|
||||
{
|
||||
force_exit = 1;
|
||||
|
@ -257,11 +269,11 @@ static int ratelimit_connects(unsigned int *last, unsigned int secs)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n = 0, ret = 0, port = 7681, use_ssl = 0;
|
||||
int n = 0, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
|
||||
unsigned int rl_dumb = 0, rl_mirror = 0;
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_client_connect_info i;
|
||||
struct lws_context *context;
|
||||
int ietf_version = -1; /* latest */
|
||||
const char *address;
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
|
@ -321,9 +333,6 @@ int main(int argc, char **argv)
|
|||
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.protocols = protocols;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
info.extensions = lws_get_internal_extensions();
|
||||
#endif
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
|
||||
|
@ -333,6 +342,17 @@ int main(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
i.context = context;
|
||||
i.address = address;
|
||||
i.port = port;
|
||||
i.ssl_connection = use_ssl;
|
||||
i.path = "/";
|
||||
i.host = argv[optind];
|
||||
i.origin = argv[optind];
|
||||
i.ietf_version_or_minus_one = ietf_version;
|
||||
i.client_exts = exts;
|
||||
/*
|
||||
* sit there servicing the websocket context to handle incoming
|
||||
* packets, and drawing random circles on the mirror protocol websocket
|
||||
|
@ -347,19 +367,14 @@ int main(int argc, char **argv)
|
|||
|
||||
if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
|
||||
lwsl_notice("dumb: connecting\n");
|
||||
wsi_dumb = lws_client_connect(context, address, port,
|
||||
use_ssl, "/", argv[optind], argv[optind],
|
||||
protocols[PROTOCOL_DUMB_INCREMENT].name,
|
||||
ietf_version);
|
||||
i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
|
||||
wsi_dumb = lws_client_connect_info(&i);
|
||||
}
|
||||
|
||||
if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
|
||||
lwsl_notice("mirror: connecting\n");
|
||||
wsi_mirror = lws_client_connect(context,
|
||||
address, port, use_ssl, "/",
|
||||
argv[optind], argv[optind],
|
||||
protocols[PROTOCOL_LWS_MIRROR].name,
|
||||
ietf_version);
|
||||
i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
|
||||
wsi_mirror = lws_client_connect_info(&i);
|
||||
}
|
||||
|
||||
lws_service(context, 500);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* This implements both the client and server sides. It defaults to
|
||||
* serving, use --client <remote address> to connect as client.
|
||||
*
|
||||
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2016 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
|
||||
|
@ -44,11 +44,13 @@ static volatile int force_exit = 0;
|
|||
static int versa, state;
|
||||
static int times = -1;
|
||||
|
||||
#define MAX_ECHO_PAYLOAD (128 * 1024)
|
||||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
|
||||
#define MAX_ECHO_PAYLOAD 1024
|
||||
|
||||
struct per_session_data__echo {
|
||||
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_ECHO_PAYLOAD];
|
||||
size_t rx, tx;
|
||||
unsigned char buf[LWS_PRE + MAX_ECHO_PAYLOAD];
|
||||
unsigned int len;
|
||||
unsigned int index;
|
||||
int final;
|
||||
|
@ -60,16 +62,17 @@ static int
|
|||
callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__echo *pss = (struct per_session_data__echo *)user;
|
||||
struct per_session_data__echo *pss =
|
||||
(struct per_session_data__echo *)user;
|
||||
int n;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
/* when the callback is used for server operations --> */
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
do_tx:
|
||||
|
||||
n = LWS_WRITE_CONTINUATION;
|
||||
if (!pss->continuation) {
|
||||
if (pss->binary)
|
||||
|
@ -80,8 +83,11 @@ do_tx:
|
|||
}
|
||||
if (!pss->final)
|
||||
n |= LWS_WRITE_NO_FIN;
|
||||
lwsl_info("+++ test-echo: writing %d, with final %d\n",
|
||||
pss->len, pss->final);
|
||||
|
||||
n = lws_write(wsi, &pss->buf[LWS_SEND_BUFFER_PRE_PADDING], pss->len, n);
|
||||
pss->tx += pss->len;
|
||||
n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, n);
|
||||
if (n < 0) {
|
||||
lwsl_err("ERROR %d writing to socket, hanging up\n", n);
|
||||
return 1;
|
||||
|
@ -98,15 +104,16 @@ do_tx:
|
|||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
do_rx:
|
||||
if (len > MAX_ECHO_PAYLOAD) {
|
||||
lwsl_err("Server received packet bigger than %u, hanging up\n", MAX_ECHO_PAYLOAD);
|
||||
return 1;
|
||||
}
|
||||
memcpy(&pss->buf[LWS_SEND_BUFFER_PRE_PADDING], in, len);
|
||||
pss->len = (unsigned int)len;
|
||||
pss->final = lws_is_final_fragment(wsi);
|
||||
pss->binary = lws_frame_is_binary(wsi);
|
||||
lwsl_info("len %d final %d\n", len, pss->final);
|
||||
lwsl_info("+++ test-echo: RX len %d final %d, pss->len=%d\n",
|
||||
len, pss->final, (int)pss->len);
|
||||
|
||||
memcpy(&pss->buf[LWS_PRE], in, len);
|
||||
assert((int)pss->len == -1);
|
||||
pss->len = (unsigned int)len;
|
||||
pss->rx += len;
|
||||
|
||||
lws_rx_flow_control(wsi, 0);
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
@ -124,6 +131,7 @@ do_rx:
|
|||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
lwsl_debug("Client has connected\n");
|
||||
pss->index = 0;
|
||||
pss->len = -1;
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
|
@ -140,16 +148,15 @@ do_rx:
|
|||
if (versa) {
|
||||
if (pss->len != (unsigned int)-1)
|
||||
goto do_tx;
|
||||
else {
|
||||
lwsl_debug("****** writable with nothing new\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* we will send our packet... */
|
||||
pss->len = sprintf((char *)&pss->buf[LWS_SEND_BUFFER_PRE_PADDING], "hello from libwebsockets-test-echo client pid %d index %d\n", getpid(), pss->index++);
|
||||
lwsl_notice("Client TX: %s", &pss->buf[LWS_SEND_BUFFER_PRE_PADDING]);
|
||||
n = lws_write(wsi, &pss->buf[LWS_SEND_BUFFER_PRE_PADDING], pss->len, LWS_WRITE_TEXT);
|
||||
pss->len = sprintf((char *)&pss->buf[LWS_PRE],
|
||||
"hello from libwebsockets-test-echo client pid %d index %d\n",
|
||||
getpid(), pss->index++);
|
||||
lwsl_notice("Client TX: %s", &pss->buf[LWS_PRE]);
|
||||
n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, LWS_WRITE_TEXT);
|
||||
if (n < 0) {
|
||||
lwsl_err("ERROR %d writing to socket, hanging up\n", n);
|
||||
return -1;
|
||||
|
@ -158,11 +165,14 @@ do_rx:
|
|||
lwsl_err("Partial write\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
|
||||
|
||||
break;
|
||||
#endif
|
||||
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
|
||||
/* reject everything else except permessage-deflate */
|
||||
if (strcmp(in, "permessage-deflate"))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -176,15 +186,31 @@ static struct lws_protocols protocols[] = {
|
|||
/* first protocol must always be HTTP handler */
|
||||
|
||||
{
|
||||
"", /* name */
|
||||
callback_echo, /* callback */
|
||||
sizeof(struct per_session_data__echo) /* per_session_data_size */
|
||||
"", /* name - can be overriden with -e */
|
||||
callback_echo,
|
||||
sizeof(struct per_session_data__echo), /* per_session_data_size */
|
||||
MAX_ECHO_PAYLOAD,
|
||||
},
|
||||
{
|
||||
NULL, NULL, 0 /* End of list */
|
||||
}
|
||||
};
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate; client_no_context_takeover; client_max_window_bits"
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"deflate_frame"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
|
||||
void sighandler(int sig)
|
||||
{
|
||||
force_exit = 1;
|
||||
|
@ -206,6 +232,7 @@ static struct option options[] = {
|
|||
{ "passphrase", required_argument, NULL, 'P' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "times", required_argument, NULL, 'n' },
|
||||
{ "echogen", no_argument, NULL, 'e' },
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
{ "daemonize", no_argument, NULL, 'D' },
|
||||
#endif
|
||||
|
@ -238,6 +265,8 @@ int main(int argc, char **argv)
|
|||
struct lws *wsi;
|
||||
int disallow_selfsigned = 0;
|
||||
struct timeval tv;
|
||||
const char *connect_protocol = NULL;
|
||||
struct lws_client_connect_info i;
|
||||
#endif
|
||||
|
||||
int debug_level = 7;
|
||||
|
@ -255,7 +284,7 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
|
||||
while (n >= 0) {
|
||||
n = getopt_long(argc, argv, "i:hsp:d:DC:k:P:vu:n:"
|
||||
n = getopt_long(argc, argv, "i:hsp:d:DC:k:P:vu:n:e"
|
||||
#ifndef LWS_NO_CLIENT
|
||||
"c:r:"
|
||||
#endif
|
||||
|
@ -313,6 +342,11 @@ int main(int argc, char **argv)
|
|||
case 'v':
|
||||
versa = 1;
|
||||
break;
|
||||
case 'e':
|
||||
protocols[0].name = "lws-echogen";
|
||||
connect_protocol = protocols[0].name;
|
||||
lwsl_err("using lws-echogen\n");
|
||||
break;
|
||||
case 'i':
|
||||
strncpy(interface_name, optarg, sizeof interface_name);
|
||||
interface_name[(sizeof interface_name) - 1] = '\0';
|
||||
|
@ -370,7 +404,7 @@ int main(int argc, char **argv)
|
|||
lws_set_log_level(debug_level, lwsl_emit_syslog);
|
||||
|
||||
lwsl_notice("libwebsockets echo test - "
|
||||
"(C) Copyright 2010-2015 Andy Green <andy@warmcat.com> - "
|
||||
"(C) Copyright 2010-2016 Andy Green <andy@warmcat.com> - "
|
||||
"licensed under LGPL2.1\n");
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (client) {
|
||||
|
@ -380,7 +414,8 @@ int main(int argc, char **argv)
|
|||
lwsl_info("allowing selfsigned\n");
|
||||
use_ssl = 2;
|
||||
} else {
|
||||
lwsl_info("requiring server cert validation againts %s\n", ssl_cert);
|
||||
lwsl_info("requiring server cert validation against %s\n",
|
||||
ssl_cert);
|
||||
info.ssl_ca_filepath = ssl_cert;
|
||||
}
|
||||
} else {
|
||||
|
@ -396,9 +431,6 @@ int main(int argc, char **argv)
|
|||
info.port = listen_port;
|
||||
info.iface = _interface;
|
||||
info.protocols = protocols;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
info.extensions = lws_get_internal_extensions();
|
||||
#endif
|
||||
if (use_ssl && !client) {
|
||||
info.ssl_cert_filepath = ssl_cert;
|
||||
info.ssl_private_key_filepath = ssl_key;
|
||||
|
@ -410,6 +442,9 @@ int main(int argc, char **argv)
|
|||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
info.extensions = exts;
|
||||
#endif
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
|
@ -430,19 +465,31 @@ int main(int argc, char **argv)
|
|||
#ifndef LWS_NO_CLIENT
|
||||
if (client && !state && times) {
|
||||
state = 1;
|
||||
lwsl_notice("Client connecting to %s:%u....\n", address, port);
|
||||
lwsl_notice("Client connecting to %s:%u....\n",
|
||||
address, port);
|
||||
/* we are in client mode */
|
||||
|
||||
address[sizeof(address) - 1] = '\0';
|
||||
sprintf(ads_port, "%s:%u", address, port & 65535);
|
||||
if (times > 0)
|
||||
times--;
|
||||
|
||||
wsi = lws_client_connect(context, address,
|
||||
port, use_ssl, uri, ads_port,
|
||||
ads_port, NULL, -1);
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
i.context = context;
|
||||
i.address = address;
|
||||
i.port = port;
|
||||
i.ssl_connection = use_ssl;
|
||||
i.path = uri;
|
||||
i.host = ads_port;
|
||||
i.origin = ads_port;
|
||||
i.protocol = connect_protocol;
|
||||
i.client_exts = exts;
|
||||
|
||||
wsi = lws_client_connect_info(&i);
|
||||
if (!wsi) {
|
||||
lwsl_err("Client failed to connect to %s:%u\n", address, port);
|
||||
lwsl_err("Client failed to connect to %s:%u\n",
|
||||
address, port);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +505,7 @@ int main(int argc, char **argv)
|
|||
times--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (client && !state && !times)
|
||||
break;
|
||||
#endif
|
||||
|
|
|
@ -57,13 +57,13 @@ callback_fraggle(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
void *user, void *in, size_t len)
|
||||
{
|
||||
int n;
|
||||
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 8000];
|
||||
unsigned char buf[LWS_PRE + 8000];
|
||||
struct per_session_data__fraggle *psf = user;
|
||||
int chunk;
|
||||
int write_mode = LWS_WRITE_CONTINUATION;
|
||||
unsigned long sum;
|
||||
unsigned char *p = (unsigned char *)in;
|
||||
unsigned char *bp = &buf[LWS_SEND_BUFFER_PRE_PADDING];
|
||||
unsigned char *bp = &buf[LWS_PRE];
|
||||
int ran;
|
||||
|
||||
switch (reason) {
|
||||
|
|
|
@ -57,7 +57,7 @@ static unsigned int interval_us = 1000000;
|
|||
static unsigned int size = 64;
|
||||
static int flood;
|
||||
static const char *address;
|
||||
static unsigned char pingbuf[LWS_SEND_BUFFER_PRE_PADDING + MAX_MIRROR_PAYLOAD];
|
||||
static unsigned char pingbuf[LWS_PRE + MAX_MIRROR_PAYLOAD];
|
||||
static char peer_name[128];
|
||||
static unsigned long started;
|
||||
static int screen_width = 80;
|
||||
|
@ -220,7 +220,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
|
||||
shift = 56;
|
||||
p = &pingbuf[LWS_SEND_BUFFER_PRE_PADDING];
|
||||
p = &pingbuf[LWS_PRE];
|
||||
|
||||
/* 64-bit ping index in network byte order */
|
||||
|
||||
|
@ -229,7 +229,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
shift -= 8;
|
||||
}
|
||||
|
||||
while ((unsigned int)(p - &pingbuf[LWS_SEND_BUFFER_PRE_PADDING]) < size)
|
||||
while ((unsigned int)(p - &pingbuf[LWS_PRE]) < size)
|
||||
*p++ = 0;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
@ -257,11 +257,11 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
if (use_mirror)
|
||||
n = lws_write(wsi,
|
||||
&pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
&pingbuf[LWS_PRE],
|
||||
size, write_options | LWS_WRITE_BINARY);
|
||||
else
|
||||
n = lws_write(wsi,
|
||||
&pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
|
||||
&pingbuf[LWS_PRE],
|
||||
size, write_options | LWS_WRITE_PING);
|
||||
|
||||
if (n < 0)
|
||||
|
|
|
@ -26,10 +26,10 @@ int
|
|||
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512];
|
||||
unsigned char buf[LWS_PRE + 512];
|
||||
struct per_session_data__dumb_increment *pss =
|
||||
(struct per_session_data__dumb_increment *)user;
|
||||
unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
|
||||
unsigned char *p = &buf[LWS_PRE];
|
||||
int n, m;
|
||||
|
||||
switch (reason) {
|
||||
|
|
123
test-server/test-server-echogen.c
Normal file
123
test-server/test-server-echogen.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* libwebsockets-test-server - libwebsockets test implementation
|
||||
*
|
||||
* Copyright (C) 2016 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
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
#include "test-server.h"
|
||||
|
||||
/* echogen protocol
|
||||
*
|
||||
* if you connect to him using his protocol, he'll send you a file chopped
|
||||
* up in various frame sizes repeated until he reaches a limit.
|
||||
*/
|
||||
|
||||
#define TOTAL 993840
|
||||
|
||||
int
|
||||
callback_lws_echogen(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
unsigned char buf[LWS_PRE + 8192];
|
||||
struct per_session_data__echogen *pss =
|
||||
(struct per_session_data__echogen *)user;
|
||||
unsigned char *p = &buf[LWS_PRE];
|
||||
int n, m;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->total = TOTAL;
|
||||
pss->fragsize = 2048;
|
||||
pss->total_rx = 0;
|
||||
sprintf((char *)buf, "%s/test.html", resource_path);
|
||||
pss->fd = open((char *)buf, LWS_O_RDONLY);
|
||||
if (pss->fd < 0) {
|
||||
lwsl_err("Failed to open %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
pss->wr = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
if (pss->fd >= 0)
|
||||
close(pss->fd);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
||||
// pss->fragsize += 16;
|
||||
// if (pss->fragsize >= 4096)
|
||||
// pss->fragsize = 32;
|
||||
|
||||
lwsl_err("%s: cb writeable, total left %ld\n", __func__, (long)pss->total);
|
||||
m = pss->fragsize;
|
||||
if ((size_t)m >= pss->total) {
|
||||
m = (int)pss->total;
|
||||
pss->wr = LWS_WRITE_CONTINUATION; /* ie, FIN */
|
||||
}
|
||||
n = read(pss->fd, p, m);
|
||||
if (n < 0) {
|
||||
lwsl_err("failed read\n");
|
||||
return -1;
|
||||
}
|
||||
if (n < m) {
|
||||
lseek(pss->fd, 0, SEEK_SET);
|
||||
m = read(pss->fd, p + n, m - n);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
} else
|
||||
m = 0;
|
||||
pss->total -= n + m;
|
||||
m = lws_write(wsi, p, n + m, pss->wr);
|
||||
if (m < n) {
|
||||
lwsl_err("ERROR %d writing to di socket\n", n);
|
||||
return -1;
|
||||
}
|
||||
if (!pss->total) {
|
||||
lwsl_err("Completed OK\n");
|
||||
break;
|
||||
}
|
||||
pss->wr = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
pss->total_rx += len;
|
||||
lwsl_err("rx %ld\n", (long)pss->total_rx);
|
||||
if (pss->total_rx == TOTAL) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL,
|
||||
(unsigned char *)"done", 4);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
|
||||
lwsl_notice("LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len %d\n",
|
||||
len);
|
||||
for (n = 0; n < (int)len; n++)
|
||||
lwsl_notice(" %d: 0x%02X\n", n,
|
||||
((unsigned char *)in)[n]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -169,8 +169,8 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
/* well, let's demonstrate how to send the hard way */
|
||||
|
||||
p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
|
||||
end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
|
||||
p = buffer + LWS_PRE;
|
||||
end = p + sizeof(buffer) - LWS_PRE;
|
||||
|
||||
pss->fd = lws_plat_file_open(wsi, leaf_path, &file_len,
|
||||
LWS_O_RDONLY);
|
||||
|
@ -217,8 +217,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
* this is mandated by changes in HTTP2
|
||||
*/
|
||||
|
||||
n = lws_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
||||
p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
|
||||
n = lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE),
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
|
||||
if (n < 0) {
|
||||
|
@ -312,7 +311,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
*/
|
||||
do {
|
||||
/* we'd like the send this much */
|
||||
n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
|
||||
n = sizeof(buffer) - LWS_PRE;
|
||||
|
||||
/* but if the peer told us he wants less, we can adapt */
|
||||
m = lws_get_peer_write_allowance(wsi);
|
||||
|
@ -328,7 +327,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
n = lws_plat_file_read(wsi, pss->fd,
|
||||
&amount, buffer +
|
||||
LWS_SEND_BUFFER_PRE_PADDING, n);
|
||||
LWS_PRE, n);
|
||||
/* problem reading, close conn */
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
@ -343,7 +342,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
* is handled by the library itself if you sent a
|
||||
* content-length header
|
||||
*/
|
||||
m = lws_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING,
|
||||
m = lws_write(wsi, buffer + LWS_PRE,
|
||||
n, LWS_WRITE_HTTP);
|
||||
if (m < 0)
|
||||
/* write failed, close conn */
|
||||
|
|
|
@ -62,7 +62,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
n = lws_write(wsi, (unsigned char *)
|
||||
ringbuffer[pss->ringbuffer_tail].payload +
|
||||
LWS_SEND_BUFFER_PRE_PADDING,
|
||||
LWS_PRE,
|
||||
ringbuffer[pss->ringbuffer_tail].len,
|
||||
LWS_WRITE_TEXT);
|
||||
if (n < 0) {
|
||||
|
@ -102,10 +102,10 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
free(ringbuffer[ringbuffer_head].payload);
|
||||
|
||||
ringbuffer[ringbuffer_head].payload =
|
||||
malloc(LWS_SEND_BUFFER_PRE_PADDING + len);
|
||||
malloc(LWS_PRE + len);
|
||||
ringbuffer[ringbuffer_head].len = len;
|
||||
memcpy((char *)ringbuffer[ringbuffer_head].payload +
|
||||
LWS_SEND_BUFFER_PRE_PADDING, in, len);
|
||||
LWS_PRE, in, len);
|
||||
if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
|
||||
ringbuffer_head = 0;
|
||||
else
|
||||
|
|
|
@ -67,6 +67,7 @@ enum demo_protocols {
|
|||
|
||||
PROTOCOL_DUMB_INCREMENT,
|
||||
PROTOCOL_LWS_MIRROR,
|
||||
PROTOCOL_LWS_ECHOGEN,
|
||||
|
||||
/* always last */
|
||||
DEMO_PROTOCOL_COUNT
|
||||
|
@ -95,6 +96,12 @@ static struct lws_protocols protocols[] = {
|
|||
sizeof(struct per_session_data__lws_mirror),
|
||||
128,
|
||||
},
|
||||
{
|
||||
"lws-echogen",
|
||||
callback_lws_echogen,
|
||||
sizeof(struct per_session_data__echogen),
|
||||
128,
|
||||
},
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
|
@ -124,6 +131,22 @@ void sighandler(int sig)
|
|||
lws_cancel_service(context);
|
||||
}
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate"
|
||||
},
|
||||
{
|
||||
"deflate-frame",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"deflate_frame"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "debug", required_argument, NULL, 'd' },
|
||||
|
@ -258,10 +281,6 @@ int main(int argc, char **argv)
|
|||
|
||||
info.iface = iface;
|
||||
info.protocols = protocols;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
info.extensions = lws_get_internal_extensions();
|
||||
#endif
|
||||
|
||||
info.ssl_cert_filepath = NULL;
|
||||
info.ssl_private_key_filepath = NULL;
|
||||
|
||||
|
@ -285,8 +304,8 @@ int main(int argc, char **argv)
|
|||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.max_http_header_pool = 1;
|
||||
info.options = opts;
|
||||
|
||||
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
info.extensions = exts;
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
lwsl_err("libwebsocket init failed\n");
|
||||
|
|
|
@ -66,6 +66,14 @@ struct per_session_data__lws_mirror {
|
|||
int ringbuffer_tail;
|
||||
};
|
||||
|
||||
struct per_session_data__echogen {
|
||||
size_t total;
|
||||
size_t total_rx;
|
||||
int fd;
|
||||
int fragsize;
|
||||
int wr;
|
||||
};
|
||||
|
||||
extern int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len);
|
||||
|
@ -75,6 +83,9 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
extern int
|
||||
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
extern int
|
||||
callback_lws_echogen(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
|
||||
extern void
|
||||
dump_handshake_info(struct lws *wsi);
|
||||
|
|
|
@ -277,7 +277,7 @@ document.getElementById("number").textContent = get_appropriate_ws_url();
|
|||
try {
|
||||
socket_di.onopen = function() {
|
||||
document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("wsdi_status").textContent = " websocket connection opened ";
|
||||
document.getElementById("wsdi_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
}
|
||||
|
||||
socket_di.onmessage =function got_packet(msg) {
|
||||
|
@ -309,7 +309,7 @@ function ot_open() {
|
|||
try {
|
||||
socket_ot.onopen = function() {
|
||||
document.getElementById("ot_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("ot_status").textContent = " websocket connection opened ";
|
||||
document.getElementById("ot_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
document.getElementById("ot_open_btn").disabled = true;
|
||||
document.getElementById("ot_close_btn").disabled = false;
|
||||
document.getElementById("ot_req_close_btn").disabled = false;
|
||||
|
@ -359,7 +359,7 @@ function ot_req_close() {
|
|||
try {
|
||||
socket_lm.onopen = function() {
|
||||
document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("wslm_status").textContent = " websocket connection opened ";
|
||||
document.getElementById("wslm_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
}
|
||||
|
||||
socket_lm.onmessage =function got_packet(msg) {
|
||||
|
|
Loading…
Add table
Reference in a new issue