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

cmake: allow setting LWS_ROLE_WS

You can build lws without support for ws, with -DLWS_ROLE_WS=0.

This is thanks to the role ops isolating all the ws-specific business
in the ws role.

Also retire more test apps replaced by minmal-examples.
This commit is contained in:
Andy Green 2018-04-25 08:42:18 +08:00
parent 8e9751e26e
commit 27e86e2641
36 changed files with 546 additions and 1678 deletions

View file

@ -16,6 +16,7 @@ env:
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
- LWS_METHOD=nologs CMAKE_ARGS="-DLWS_WITH_NO_LOGS=ON"
- LWS_METHOD=smp CMAKE_ARGS="-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=nows CMAKE_ARGS="-DLWS_ROLE_WS=0"
os:
- linux

View file

@ -9,6 +9,8 @@ set(LWS_WITH_BUNDLED_ZLIB_DEFAULT OFF)
if(WIN32)
set(LWS_WITH_BUNDLED_ZLIB_DEFAULT ON)
endif()
set(LWS_ROLE_H1 1)
set(LWS_ROLE_RAW 1)
#
# Select features recommended for PC distro packaging
@ -18,6 +20,7 @@ option(LWS_WITH_DISTRO_RECOMMENDED "Enable features recommended for distro packa
#
# Major individual features
#
option(LWS_ROLE_WS "Compile with support for websockets" ON)
option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" OFF)
option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
@ -70,7 +73,6 @@ option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF)
option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF)
option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF)
option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF)
option(LWS_WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF)
#
# Extensions (permessage-deflate)
#
@ -197,9 +199,6 @@ endif()
# translate old functionality enables to set up ROLE enables so nothing changes
set(LWS_ROLE_H1 1)
set(LWS_ROLE_WS 1)
set(LWS_ROLE_RAW 1)
if (LWS_WITH_HTTP2)
set(LWS_ROLE_H2 1)
endif()
@ -207,6 +206,10 @@ if (LWS_WITH_CGI)
set(LWS_ROLE_CGI 1)
endif()
if (NOT LWS_ROLE_WS)
set(LWS_WITHOUT_EXTENSIONS 1)
endif()
if (LWS_WITH_HTTP2 AND LWS_WITHOUT_SERVER)
message(FATAL_ERROR "HTTP2 can only be used with server at the moment")
endif()
@ -709,7 +712,7 @@ if (LWS_ROLE_WS)
if (NOT LWS_WITHOUT_CLIENT)
list(APPEND SOURCES
lib/roles/ws/client-ws.c
lib/roles/ws/client-parser.c)
lib/roles/ws/client-parser-ws.c)
endif()
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
@ -1592,15 +1595,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)
""
"")
endif()
if (UNIX AND NOT LWS_WITHOUT_SERVER AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) AND LWS_HAVE_PTHREAD_H)
create_test_app(test-server-pthreads
"test-apps/test-server-pthreads.c"
"test-apps/test-server-http.c"
"test-apps/test-server-dumb-increment.c"
""
""
"")
endif()
if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
AND LWS_WITH_LIBEV)
create_test_app(test-server-libev
@ -1720,13 +1714,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)
create_test_app(test-client "test-apps/test-client.c" "" "" "" "" "")
endif()
#
# test-fraggle
#
if (NOT LWS_WITHOUT_TEST_FRAGGLE AND NOT LWS_WITHOUT_SERVER)
create_test_app(test-fraggle "test-apps/test-fraggle.c" "" "" "" "" "")
endif()
#
# test-ping
#
@ -1786,27 +1773,15 @@ if (NOT LWS_WITHOUT_TESTAPPS)
endmacro()
create_plugin(protocol_lws_meta ""
"plugins/protocol_lws_meta.c" "" "")
if (LWS_ROLE_WS)
create_plugin(protocol_dumb_increment ""
"plugins/protocol_dumb_increment.c" "" "")
create_plugin(protocol_lws_mirror ""
"plugins/protocol_lws_mirror.c" "" "")
create_plugin(protocol_lws_status ""
"plugins/protocol_lws_status.c" "" "")
create_plugin(protocol_post_demo ""
"plugins/protocol_post_demo.c" "" "")
create_plugin(protocol_lws_table_dirlisting ""
"plugins/generic-table/protocol_table_dirlisting.c" "" "")
if (LWS_WITH_SSL)
create_plugin(protocol_lws_ssh_base "plugins/ssh-base/include"
"plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c" "plugins/ssh-base/crypto/chacha.c;plugins/ssh-base/crypto/ed25519.c;plugins/ssh-base/crypto/fe25519.c;plugins/ssh-base/crypto/ge25519.c;plugins/ssh-base/crypto/poly1305.c;plugins/ssh-base/crypto/sc25519.c;plugins/ssh-base/crypto/smult_curve25519_ref.c" "")
create_plugin(protocol_lws_sshd_demo "plugins/ssh-base/include" "plugins/protocol_lws_sshd_demo.c" "" "")
include_directories("${PROJECT_SOURCE_DIR}/plugins/ssh-base/include")
endif()
if (NOT WIN32)
create_plugin(protocol_lws_raw_test ""
"plugins/protocol_lws_raw_test.c" "" "")
@ -1820,14 +1795,29 @@ endif()
if (NOT LWS_WITHOUT_CLIENT)
create_plugin(protocol_client_loopback_test ""
"plugins/protocol_client_loopback_test.c" "" "")
endif(NOT LWS_WITHOUT_CLIENT)
endif()
endif()
create_plugin(protocol_post_demo ""
"plugins/protocol_post_demo.c" "" "")
if (LWS_WITH_SSL)
create_plugin(protocol_lws_ssh_base "plugins/ssh-base/include"
"plugins/ssh-base/sshd.c;plugins/ssh-base/telnet.c;plugins/ssh-base/kex-25519.c" "plugins/ssh-base/crypto/chacha.c;plugins/ssh-base/crypto/ed25519.c;plugins/ssh-base/crypto/fe25519.c;plugins/ssh-base/crypto/ge25519.c;plugins/ssh-base/crypto/poly1305.c;plugins/ssh-base/crypto/sc25519.c;plugins/ssh-base/crypto/smult_curve25519_ref.c" "")
create_plugin(protocol_lws_sshd_demo "plugins/ssh-base/include" "plugins/protocol_lws_sshd_demo.c" "" "")
include_directories("${PROJECT_SOURCE_DIR}/plugins/ssh-base/include")
endif()
if (LWS_WITH_ACME)
create_plugin(protocol_lws_acme_client ""
"plugins/acme-client/protocol_lws_acme_client.c" "" "")
endif()
if (LWS_WITH_GENERIC_SESSIONS)
if (LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS)
create_plugin(protocol_generic_sessions ""
"plugins/generic-sessions/protocol_generic_sessions.c"
"plugins/generic-sessions/utils.c"
@ -2157,7 +2147,6 @@ message(" LWS_WITHOUT_TEST_SERVER = ${LWS_WITHOUT_TEST_SERVER}")
message(" LWS_WITHOUT_TEST_SERVER_EXTPOLL = ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}")
message(" LWS_WITHOUT_TEST_PING = ${LWS_WITHOUT_TEST_PING}")
message(" LWS_WITHOUT_TEST_CLIENT = ${LWS_WITHOUT_TEST_CLIENT}")
message(" LWS_WITHOUT_TEST_FRAGGLE = ${LWS_WITHOUT_TEST_FRAGGLE}")
message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}")
message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}")
message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}")

View file

@ -13,8 +13,8 @@ The Travis build of lws done on every commit now runs
Tests|Count|Explanation
---|---|---
Build / Linux / gcc|12|-Wall -Werror
Build / Mac / Clang|12|-Wall -Werror
Build / Linux / gcc|13|-Wall -Werror
Build / Mac / Clang|13|-Wall -Werror
Build / Windows / MSVC|7|default
Selftests|29|minimal examples built and run against each other and remote server
attack.sh|225|Correctness, robustness and security tests for http parser

View file

@ -25,8 +25,7 @@
#define LWS_BUILD_HASH "unknown-build-hash"
#endif
#if defined(LWS_WITH_TLS)
static const struct lws_role_ops * available_roles[] = {
const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
#endif
@ -36,8 +35,10 @@ static const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_WS)
&role_ops_ws,
#endif
NULL
};
#if defined(LWS_WITH_TLS)
static char alpn_discovered[32];
#endif
@ -60,17 +61,15 @@ int
lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
{
#if defined(LWS_WITH_TLS)
int n;
if (!alpn)
return 0;
lwsl_info("%s: '%s'\n", __func__, alpn);
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++)
if (!strcmp(available_roles[n]->alpn, alpn) &&
available_roles[n]->alpn_negotiated)
return available_roles[n]->alpn_negotiated(wsi, alpn);
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
if (!strcmp(ar->alpn, alpn) && ar->alpn_negotiated)
return ar->alpn_negotiated(wsi, alpn);
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
#endif
return 0;
}
@ -565,13 +564,6 @@ lws_create_vhost(struct lws_context *context,
if (info->options & LWS_SERVER_OPTION_ONLY_RAW)
lwsl_info("%s set to only support RAW\n", vh->name);
#if defined(LWS_WITH_HTTP2)
vh->h2.set = context->set;
if (info->http2_settings[0])
for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
vh->h2.set.s[n] = info->http2_settings[n];
#endif
vh->iface = info->iface;
#if !defined(LWS_WITH_ESP32) && \
!defined(OPTEE_TA) && !defined(WIN32)
@ -589,9 +581,11 @@ lws_create_vhost(struct lws_context *context,
vh->user = info->user;
vh->alpn = info->alpn;
#if defined(LWS_ROLE_H2)
role_ops_h2.init_vhost(vh, info);
#endif
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
if (ar->init_vhost)
if (ar->init_vhost(vh, info))
return NULL;
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
vh->ssl_info_event_mask = info->ssl_info_event_mask;
if (info->keepalive_timeout)
@ -740,40 +734,6 @@ lws_create_vhost(struct lws_context *context,
mounts = mounts->mount_next;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
#ifdef LWS_WITH_PLUGINS
if (context->plugin_extension_count) {
m = 0;
while (info->extensions && info->extensions[m].callback)
m++;
/*
* give the vhost a unified list of extensions including the
* ones that came from plugins
*/
vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
(m + context->plugin_extension_count + 1),
"extensions");
if (!vh->ws.extensions)
return NULL;
memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
sizeof(struct lws_extension) * m);
plugin = context->plugin_list;
while (plugin) {
memcpy((struct lws_extension *)&vh->ws.extensions[m],
plugin->caps.extensions,
sizeof(struct lws_extension) *
plugin->caps.count_extensions);
m += plugin->caps.count_extensions;
plugin = plugin->list;
}
} else
#endif
vh->ws.extensions = info->extensions;
#endif
vh->listen_port = info->port;
vh->http_proxy_port = 0;
vh->http_proxy_address[0] = '\0';
@ -1122,16 +1082,17 @@ lws_create_context(struct lws_context_creation_info *info)
else {
char *p = alpn_discovered, first = 1;
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_roles); n++) {
if (available_roles[n]->alpn) {
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
if (ar->alpn) {
if (!first)
*p++ = ',';
p += lws_snprintf(p, alpn_discovered +
sizeof(alpn_discovered) - 2 - p,
"%s", available_roles[n]->alpn);
"%s", ar->alpn);
first = 0;
}
}
} LWS_FOR_EVERY_AVAILABLE_ROLE_END;
context->alpn_default = alpn_discovered;
}
@ -1596,12 +1557,11 @@ lws_vhost_destroy2(struct lws_vhost *vh)
lws_free((void *)vh->protocols);
}
#ifdef LWS_WITH_PLUGINS
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (context->plugin_extension_count)
lws_free((void *)vh->ws.extensions);
#endif
#endif
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar)
if (ar->destroy_vhost)
ar->destroy_vhost(vh);
LWS_FOR_EVERY_AVAILABLE_ROLE_END;
#ifdef LWS_WITH_ACCESS_LOG
if (vh->log_fd != (int)LWS_INVALID_FILE)
close(vh->log_fd);

View file

@ -95,7 +95,6 @@ __lws_free_wsi(struct lws *wsi)
lws_buflist_destroy_all_segments(&wsi->buflist);
lws_free_set_NULL(wsi->trunc_alloc);
lws_free_set_NULL(wsi->ws);
lws_free_set_NULL(wsi->udp);
/* we may not have an ah, but may be on the waiting list... */
@ -834,11 +833,6 @@ just_kill_connection:
wsi->told_user_closed = 1;
}
/* deallocate any active extension contexts */
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
lwsl_warn("extension destruction failed\n");
async_close:
wsi->socket_is_permanently_unusable = 1;

View file

@ -249,7 +249,6 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
lws_restart_ws_ping_pong_timer(wsi);
return n;
}

View file

@ -218,10 +218,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
}
#if defined(LWS_WITH_TLS)
if (!pt->rx_draining_ext_list &&
if (!pt->ws.rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
if (!pt->ws.rx_draining_ext_list && !n) /* poll timeout */ {
#endif
lws_service_fd_tsi(context, NULL, tsi);
return 0;

View file

@ -141,10 +141,10 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
n = poll(pt->fds, pt->fds_count, timeout_ms);
#if defined(LWS_WITH_TLS)
if (!pt->rx_draining_ext_list &&
if (!pt->ws.rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
if (!pt->ws.rx_draining_ext_list && !n) /* poll timeout */ {
#endif
lws_service_fd_tsi(context, NULL, tsi);
return 0;

View file

@ -259,12 +259,16 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
lws_pt_unlock(pt);
m = 0;
#if defined(LWS_WITH_TLS)
if (!n && !pt->rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
m |= !n && !lws_ssl_anybody_has_buffered_read_tsi(context, tsi);
#endif
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
m |= !n && !pt->ws.rx_draining_ext_list;
#endif
if (m) {
lws_service_fd_tsi(context, NULL, tsi);
lws_service_do_ripe_rxflow(pt);

View file

@ -527,12 +527,14 @@ struct lws_role_ops {
* ws-over-h2 is upgraded from h2 like this.
*/
int (*check_upgrades)(struct lws *wsi);
/* role-specific context init during vhost creation */
/* role-specific context init during context creation */
int (*init_context)(struct lws_context *context,
struct lws_context_creation_info *info);
/* role-specific per-vhost init during vhost creation */
int (*init_vhost)(struct lws_vhost *vh,
struct lws_context_creation_info *info);
/* role-specific per-vhost destructor during vhost destroy */
int (*destroy_vhost)(struct lws_vhost *vh);
/* generic 1Hz callback for the role itself */
int (*periodic_checks)(struct lws_context *context, int tsi,
time_t now);
@ -582,9 +584,22 @@ struct lws_role_ops {
uint16_t close_cb[2];
};
/* null-terminated array of pointers to roles lws built with */
extern const struct lws_role_ops *available_roles[];
#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \
const struct lws_role_ops **ppxx = available_roles; \
while (*ppxx) { \
const struct lws_role_ops *xx = *ppxx++;
#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }}
/* core roles */
extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
role_ops_pipe;
/* bring in role private declarations */
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
#include "roles/http/private.h"
#else
@ -767,9 +782,8 @@ struct lws_context_per_thread {
#endif
struct lws_pollfd *fds;
volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list;
#if defined(LWS_ROLE_WS)
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_pt_role_ws ws;
#endif
struct lws_dll_lws dll_head_timeout;
struct lws_dll_lws dll_head_hrtimer;
@ -887,7 +901,7 @@ struct lws_vhost {
#if defined(LWS_ROLE_H2)
struct lws_vhost_role_h2 h2;
#endif
#if defined(LWS_ROLE_WS)
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_vhost_role_ws ws;
#endif
@ -1368,6 +1382,9 @@ struct lws {
#if defined(LWS_ROLE_H2)
struct _lws_h2_related h2;
#endif
#if defined(LWS_ROLE_WS)
struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
#endif
/* lifetime members */
@ -1388,7 +1405,7 @@ struct lws {
struct lws *parent; /* points to parent, if any */
struct lws *child_list; /* points to first child */
struct lws *sibling_list; /* subsequent children at same level */
struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */
#ifdef LWS_WITH_CGI
struct lws_cgi *cgi; /* wsi being cgi master have one of these */
#endif

161
lib/roles/README.md Normal file
View file

@ -0,0 +1,161 @@
## Information for new role implementers
### Introduction
In lws the "role" is the job the wsi is doing in the system, eg,
being an http1 or h2, or ws connection, or being a listen socket, etc.
This is different than, eg, a new ws protocol or a different callback
for an existing role. A new role is needed when you want to add support for
something completely new, like a completely new wire protocol that
doesn't use http or ws.
So... what's the point of implementing the protocol inside the lws role framework?
You inherit all the well-maintained lws core functionality around:
- connection lifecycle sequencing in a valgrind-clean way
- proxy support, HTTP and Socks5
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
- apis for cert lifecycle management and parsing
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
- clean connection tracking and closing even on advanced event loops
- user code follows the same simple callbacks on wsi
- multi-vhost support
- core multithreaded service support with usually no locking requirement on the role code
- direct compatibility with all other lws roles + protocols in the same event loop
- compatibility with higher-level stuff like lwsws as the server application
### Code placement
The code specific to that role should live in `./lib/roles/**role name**`
If a role is asymmetic between a client and server side, like http is, it
should generally be implemented as a single role.
### Allowing control over enabling roles
All roles should add a cmake define `LWS_ROLE_**role name**` and make its build
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
as well so user builds can understand if the role is available in the lws build it is
trying to bind to.
If the role is disabled in cmake, nothing in its directory is built.
### Role ops struct
The role is defined by `struct lws_role_ops` in `lib/private/libwebsockets.h`,
each role instantiates one of these and fills in the appropriate ops
callbacks to perform its job. By convention that lives in
`./lib/roles/**role name**/ops-**role_name**.c`.
### Private role declarations
Truly private declarations for the role can go in the role directory as you like.
However when the declarations must be accessible to other things in lws build, eg,
the role adds members to `struct lws` when enabled, they should be in the role
directory in a file `private.h`.
Search for "bring in role private declarations" in `./lib/private-libwebsockets.h
and add your private role file there following the style used for the other roles,
eg,
```
#if defined(LWS_ROLE_WS)
#include "roles/ws/private.h"
#else
#define lwsi_role_ws(wsi) (0)
#endif
```
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
### Integrating role assets to lws
If your role needs special storage in lws objects, that's no problem. But to keep
things sane, there are some rules.
- declare a "container struct" in your private.h for everything, eg, the ws role wants
to add storage in lws_vhost for enabled extensions, it declares in its private.h
```
struct lws_vhost_role_ws {
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *extensions;
#endif
};
```
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
eg, again for LWS_ROLE_WS
```
struct lws_vhost {
...
#if defined(LWS_ROLE_WS)
struct lws_vhost_role_ws ws;
#endif
...
```
### Adding to lws available roles list
Edit the NULL-terminated array `available_roles` at the top of `./lib/context.c` to include
a pointer to your new role's ops struct, following the style already there.
```
const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
#endif
...
```
This makes lws aware that your role exists, and it can auto-generate some things like
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
### Enabling role adoption
The primary way wsi get bound to a specific role is via the lws adoption api
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./lib/libwebsockets.h`
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
to bind a wsi with suitable flags to your role ops.
### Implementation of the role
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
bound to them via the adoption api.
The core support for wsis in lws has some generic concepts
- the wsi holds a pointer member `role_ops` that indicates which role ops the
wsi is bound to
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
and `lwsi_role_server(wsi)`.
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
various proxy or tunnel states, to 'established', and then various states shutting
down until 'dead socket'. The states have testable flags and helpers to discover if
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
you can adjust the state using `lwsi_set_state()`.

View file

@ -81,6 +81,7 @@ struct lws_role_ops role_ops_cgi = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ rops_periodic_checks_cgi,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_cgi,

View file

@ -25,54 +25,6 @@
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#if !defined(LWS_NO_CLIENT)
static int
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
{
int m;
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
!lwsi_role_client(wsi))
return 0;
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
while (len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
lws_rxflow_cache(wsi, *buf, 0, (int)len);
*buf += len;
return 0;
}
if (wsi->ws->rx_draining_ext) {
//lwsl_notice("%s: draining ext\n", __func__);
if (lwsi_role_client(wsi))
m = lws_ws_client_rx_sm(wsi, 0);
else
m = lws_ws_rx_sm(wsi, 0, 0);
if (m < 0)
return -1;
continue;
}
/* caller will account for buflist usage */
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
__func__, (int)len);
return -1;
}
len--;
}
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
return 0;
}
#endif
/*
* We have to take care about parsing because the headers may be split
@ -115,8 +67,8 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
assert(0);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
#if !defined(LWS_NO_CLIENT)
if (lws_handshake_client(wsi, &buf, (size_t)len))
#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
last_char = buf;
@ -248,9 +200,9 @@ postbody_completion:
case LRS_SHUTDOWN:
ws_mode:
#if !defined(LWS_NO_CLIENT)
#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
// lwsl_notice("%s: ws_mode\n", __func__);
if (lws_handshake_client(wsi, &buf, (size_t)len))
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
#if defined(LWS_ROLE_WS)
@ -263,7 +215,8 @@ ws_mode:
goto bail;
}
#endif
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__, lws_ptr_diff(buf, oldbuf));
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
// lws_ptr_diff(buf, oldbuf));
break;
case LRS_DEFERRING_ACTION:
@ -293,14 +246,20 @@ read_ok:
bail:
/*
* h2 / h2-ws calls us recursively in lws_read()->lws_h2_parser()->
* lws_read() pattern, having stripped the h2 framing in the middle.
* h2 / h2-ws calls us recursively in
*
* lws_read_h1()->
* lws_h2_parser()->
* lws_read_h1()
*
* pattern, having stripped the h2 framing in the middle.
*
* When taking down the whole connection, make sure that only the
* outer lws_read() does the wsi close.
*/
if (!wsi->outer_will_close)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "lws_read bail");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"lws_read_h1 bail");
return -1;
}
@ -526,19 +485,6 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
}
#endif
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/*
* we stopped caring about anything except control
* packets. Force flow control off, defeat tx
* draining.
*/
lws_rx_flow_control(wsi, 1);
if (wsi->ws)
wsi->ws->tx_draining_ext = 0;
}
if (lws_is_flowcontrolled(wsi))
/* We cannot deal with any kind of new RX because we are
* RX-flowcontrolled.
@ -669,6 +615,7 @@ struct lws_role_ops role_ops_h1 = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h1,

View file

@ -1715,7 +1715,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
case LWS_H2_FRAME_TYPE_DATA:
lwsl_notice("%s: LWS_H2_FRAME_TYPE_DATA\n", __func__);
lwsl_info("%s: LWS_H2_FRAME_TYPE_DATA\n", __func__);
/* let the network wsi live a bit longer if subs are active...
* our frame may take a long time to chew through */
@ -1805,7 +1805,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
*/
n = lws_read_h1(h2n->swsi, in - 1, n);
lwsl_notice("%s: lws_read_h1 %d\n", __func__, n);
// lwsl_notice("%s: lws_read_h1 %d\n", __func__, n);
h2n->swsi->outer_will_close = 0;
/*
* can return 0 in POST body with
@ -1819,8 +1819,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
h2n->inside = h2n->length;
h2n->count = h2n->length - 1;
if (n < 0)
goto already_closed_swsi;
//if (n < 0)
// goto already_closed_swsi;
goto close_swsi_and_return;
}
@ -1983,7 +1983,7 @@ close_swsi_and_return:
h2n->frame_state = 0;
h2n->count = 0;
already_closed_swsi:
// already_closed_swsi:
*inused = in - oldin;
return 2;
@ -2144,7 +2144,7 @@ lws_h2_ws_handshake(struct lws *wsi)
*/
lwsi_set_state(wsi, LRS_ESTABLISHED);
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
wsi->lws_rx_parse_state = 0; // ==LWS_RXPS_NEW;
uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH);
n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH);

View file

@ -155,8 +155,10 @@ rops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi,
* draining.
*/
lws_rx_flow_control(wsi, 1);
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws)
wsi->ws->tx_draining_ext = 0;
#endif
}
if (wsi->http2_substream || wsi->upgraded_to_http2) {
@ -254,15 +256,12 @@ drain:
if (ebuf.len) {
n = 0;
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY) {
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
ebuf.len);
// lwsl_notice("h2 n = %d\n", n);
} else {
else
n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
ebuf.len);
// lwsl_notice("h1 n = %d\n", n);
}
if (n < 0) {
/* we closed wsi */
@ -352,20 +351,23 @@ static int
rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol *wp)
{
unsigned char flags = 0;
unsigned char flags = 0, base = (*wp) & 0x1f;
int n;
/* if not in a state to send stuff, then just send nothing */
if (!lwsi_role_ws(wsi) &&
((*wp) & 0x1f) != LWS_WRITE_HTTP &&
((*wp) & 0x1f) != LWS_WRITE_HTTP_FINAL &&
((*wp) & 0x1f) != LWS_WRITE_HTTP_HEADERS_CONTINUATION &&
((*wp) & 0x1f) != LWS_WRITE_HTTP_HEADERS &&
base != LWS_WRITE_HTTP &&
base != LWS_WRITE_HTTP_FINAL &&
base != LWS_WRITE_HTTP_HEADERS_CONTINUATION &&
base != LWS_WRITE_HTTP_HEADERS &&
((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK) ||
((*wp) & 0x1f) != LWS_WRITE_CLOSE)) {
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)
#if defined(LWS_ROLE_WS)
|| base != LWS_WRITE_CLOSE
#endif
)) {
//assert(0);
lwsl_notice("binning wsistate 0x%x %d\n", wsi->wsistate, *wp);
return 0;
@ -376,7 +378,7 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
*/
n = LWS_H2_FRAME_TYPE_DATA;
if ((*wp & 0x1f) == LWS_WRITE_HTTP_HEADERS) {
if (base == LWS_WRITE_HTTP_HEADERS) {
n = LWS_H2_FRAME_TYPE_HEADERS;
if (!((*wp) & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
@ -387,41 +389,35 @@ rops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,
}
}
if ((*wp & 0x1f) == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
if (base == LWS_WRITE_HTTP_HEADERS_CONTINUATION) {
n = LWS_H2_FRAME_TYPE_CONTINUATION;
if (!((*wp) & LWS_WRITE_NO_FIN))
flags = LWS_H2_FLAG_END_HEADERS;
if (wsi->h2.send_END_STREAM ||
((*wp) & LWS_WRITE_H2_STREAM_END)) {
if (wsi->h2.send_END_STREAM || ((*wp) & LWS_WRITE_H2_STREAM_END)) {
flags |= LWS_H2_FLAG_END_STREAM;
wsi->h2.send_END_STREAM = 1;
}
}
if (((*wp & 0x1f) == LWS_WRITE_HTTP ||
(*wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
wsi->http.tx_content_length) {
if ((base == LWS_WRITE_HTTP ||
base == LWS_WRITE_HTTP_FINAL) &&
wsi->http.tx_content_length) {
wsi->http.tx_content_remain -= len;
lwsl_info("%s: wsi %p: tx_content_remain = %llu\n",
__func__, wsi,
lwsl_info("%s: wsi %p: tx_content_rem = %llu\n", __func__, wsi,
(unsigned long long)wsi->http.tx_content_remain);
if (!wsi->http.tx_content_remain) {
lwsl_info("%s: selecting final write mode\n",
__func__);
*wp = LWS_WRITE_HTTP_FINAL;
lwsl_info("%s: selecting final write mode\n", __func__);
base = *wp = LWS_WRITE_HTTP_FINAL;
}
}
if ((*wp & 0x1f) == LWS_WRITE_HTTP_FINAL ||
((*wp) & LWS_WRITE_H2_STREAM_END)) {
//lws_get_network_wsi(wsi)->h2.END_STREAM) {
if (base == LWS_WRITE_HTTP_FINAL || ((*wp) & LWS_WRITE_H2_STREAM_END)) {
lwsl_info("%s: setting END_STREAM\n", __func__);
flags |= LWS_H2_FLAG_END_STREAM;
wsi->h2.send_END_STREAM = 1;
}
return lws_h2_frame_write(wsi, n, flags, wsi->h2.my_sid,
(int)len, buf);
return lws_h2_frame_write(wsi, n, flags, wsi->h2.my_sid, (int)len, buf);
}
static int
@ -471,6 +467,13 @@ static int
rops_init_vhost_h2(struct lws_vhost *vh,
struct lws_context_creation_info *info)
{
int n;
vh->h2.set = vh->context->set;
if (info->http2_settings[0])
for (n = 1; n < LWS_H2_SETTINGS_LEN; n++)
vh->h2.set.s[n] = info->http2_settings[n];
return 0;
}
@ -510,7 +513,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
lws_h2_rst_stream(wsi, 0, "none");
if (wsi->h2.parent_wsi) {
if (wsi->h2.parent_wsi && lwsl_visible(LLL_INFO)) {
lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi,
wsi->h2.parent_wsi);
lws_start_foreach_llp(struct lws **, w,
@ -523,7 +526,7 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
if (wsi->upgraded_to_http2 || wsi->http2_substream || wsi->client_h2_substream) {
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
if (wsi->h2.child_list) {
if (wsi->h2.child_list && lwsl_visible(LLL_INFO)) {
lwsl_info(" parent %p: closing children: list:\n", wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
@ -531,6 +534,8 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)
(*w)->role_ops ? (*w)->role_ops->name : "?",
*w);
} lws_end_foreach_llp(w, h2.sibling_list);
}
if (wsi->h2.child_list) {
/* trigger closing of all of our http2 children first */
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
@ -732,7 +737,10 @@ static int
rops_perform_user_POLLOUT_h2(struct lws *wsi)
{
struct lws **wsi2, *wsi2a;
int write_type = LWS_WRITE_PONG, n;
#if defined(LWS_ROLE_WS)
int write_type = LWS_WRITE_PONG;
#endif
int n;
wsi = lws_get_network_wsi(wsi);
@ -880,9 +888,11 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)
goto next_child;
}
#if defined(LWS_ROLE_WS)
/* Notify peer that we decided to close */
if (lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) {
if (lwsi_role_ws(w) && lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) {
lwsl_debug("sending close packet\n");
w->waiting_to_send_close_frame = 0;
n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
@ -934,7 +944,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi)
/* otherwise for PING, leave POLLOUT active either way */
goto next_child;
}
#endif
if (lws_callback_as_writeable(w)) {
lwsl_info("Closing POLLOUT child (end stream %d)\n",
w->h2.send_END_STREAM);
@ -1023,6 +1033,7 @@ struct lws_role_ops role_ops_h2 = {
/* check_upgrades */ rops_check_upgrades_h2,
/* init_context */ rops_init_context_h2,
/* init_vhost */ rops_init_vhost_h2,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h2,

View file

@ -1027,9 +1027,10 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_ORIGIN));
}
#if defined(LWS_ROLE_WS)
if (wsi->do_ws)
p = lws_generate_client_ws_handshake(wsi, p);
#endif
/* give userland a chance to append, eg, cookies */

View file

@ -1519,9 +1519,11 @@ raw_transition:
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
WSI_TOKEN_UPGRADE),
"websocket")) {
#if defined(LWS_ROLE_WS)
wsi->vhost->conn_stats.ws_upg++;
lwsl_info("Upgrade to ws\n");
goto upgrade_ws;
#endif
}
#if defined(LWS_WITH_HTTP2)
if (!strcasecmp(lws_hdr_simple_ptr(wsi,
@ -1601,13 +1603,13 @@ upgrade_h2c:
return 0;
#endif
#if defined(LWS_ROLE_WS)
upgrade_ws:
if (lws_process_ws_upgrade(wsi))
goto bail_nuke_ah;
return 0;
#endif
} /* while all chars are handled */
return 0;
@ -1871,6 +1873,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
lwsl_notice("OOM trying to get user_space\n");
goto bail;
}
#if defined(LWS_ROLE_WS)
if (type & LWS_ADOPT_WS_PARENTIO) {
new_wsi->desc.sockfd = LWS_SOCK_INVALID;
lwsl_debug("binding to %s\n", new_wsi->protocol->name);
@ -1887,6 +1890,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
return new_wsi;
}
#endif
} else
if (type & LWS_ADOPT_HTTP) {/* he will transition later */
new_wsi->protocol =

View file

@ -155,6 +155,7 @@ struct lws_role_ops role_ops_listen = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_listen,

View file

@ -60,6 +60,7 @@ struct lws_role_ops role_ops_pipe = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_pipe,

View file

@ -169,6 +169,7 @@ struct lws_role_ops role_ops_raw_skt = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_skt,
@ -195,6 +196,7 @@ struct lws_role_ops role_ops_raw_file = {
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_raw_file,

View file

@ -37,6 +37,7 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
ebuf.token = NULL;
ebuf.len = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
assert(!c);
@ -46,6 +47,7 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
goto drain_extension;
}
#endif
if (wsi->socket_is_permanently_unusable)
return -1;
@ -310,10 +312,10 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
case LWS_RXPS_WS_FRAME_PAYLOAD:
assert(wsi->ws->rx_ubuf);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext)
goto drain_extension;
#endif
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
@ -486,8 +488,8 @@ ping_drop:
if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
goto already_done;
drain_extension:
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
@ -548,9 +550,9 @@ utf8_fail:
if (
/* coverity says dead code otherwise */
#if !defined(LWS_WITHOUT_EXTENSIONS)
//#if !defined(LWS_WITHOUT_EXTENSIONS)
n &&
#endif
//#endif
ebuf.len)
/* extension had more... main loop will come back
* we want callback to be done with this set, if so,

View file

@ -61,6 +61,57 @@ lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi)
return 0;
}
#if !defined(LWS_NO_CLIENT)
int
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
{
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
!lwsi_role_client(wsi))
return 0;
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
while (len) {
/*
* we were accepting input but now we stopped doing so
*/
if (lws_is_flowcontrolled(wsi)) {
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
lws_rxflow_cache(wsi, *buf, 0, (int)len);
*buf += len;
return 0;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
int m;
//lwsl_notice("%s: draining ext\n", __func__);
if (lwsi_role_client(wsi))
m = lws_ws_client_rx_sm(wsi, 0);
else
m = lws_ws_rx_sm(wsi, 0, 0);
if (m < 0)
return -1;
continue;
}
#endif
/* caller will account for buflist usage */
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
__func__, (int)len);
return -1;
}
len--;
}
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
return 0;
}
#endif
char *
lws_generate_client_ws_handshake(struct lws *wsi, char *p)
{

View file

@ -48,6 +48,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
ebuf.token = NULL;
ebuf.len = 0;
@ -57,6 +58,7 @@ lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
goto drain_extension;
}
#endif
switch (wsi->ws->ietf_spec_revision) {
case 13:
/*
@ -385,12 +387,12 @@ handle_first:
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
goto drain_extension;
}
#endif
/*
* if there's no protocol max frame size given, we are
* supposed to default to context->pt_serv_buf_size
@ -554,8 +556,9 @@ ping_drop:
if (wsi->ws->opcode == LWSWSOPC_PONG && !ebuf.len)
goto already_done;
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
#endif
// lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
@ -676,6 +679,7 @@ LWS_VISIBLE int lws_frame_is_binary(struct lws *wsi)
void
lws_add_wsi_to_draining_ext_list(struct lws *wsi)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->ws->rx_draining_ext)
@ -684,15 +688,17 @@ lws_add_wsi_to_draining_ext_list(struct lws *wsi)
lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
wsi->ws->rx_draining_ext = 1;
wsi->ws->rx_draining_ext_list = pt->rx_draining_ext_list;
pt->rx_draining_ext_list = wsi;
wsi->ws->rx_draining_ext_list = pt->ws.rx_draining_ext_list;
pt->ws.rx_draining_ext_list = wsi;
#endif
}
void
lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct lws **w = &pt->rx_draining_ext_list;
struct lws **w = &pt->ws.rx_draining_ext_list;
if (!wsi->ws->rx_draining_ext)
return;
@ -711,6 +717,7 @@ lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
w = &((*w)->ws->rx_draining_ext_list);
}
wsi->ws->rx_draining_ext_list = NULL;
#endif
}
LWS_EXTERN void
@ -801,11 +808,15 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
LWS_VISIBLE int
lws_is_final_fragment(struct lws *wsi)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
wsi->ws->final, (long)wsi->ws->rx_packet_length,
(long)wsi->ws->rx_draining_ext);
return wsi->ws->final && !wsi->ws->rx_packet_length &&
!wsi->ws->rx_draining_ext;
#else
return wsi->ws->final && !wsi->ws->rx_packet_length;
#endif
}
LWS_VISIBLE int
@ -933,10 +944,12 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
* draining.
*/
lws_rx_flow_control(wsi, 1);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws)
wsi->ws->tx_draining_ext = 0;
#endif
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->tx_draining_ext)
/*
* We cannot deal with new RX until the TX ext path has
@ -947,7 +960,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
* blocking.
*/
return LWS_HPI_RET_HANDLED;
#endif
if (lws_is_flowcontrolled(wsi)) {
/* We cannot deal with any kind of new RX because we are
* RX-flowcontrolled.
@ -969,6 +982,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
}
#endif
#if !defined(LWS_WITHOUT_EXTENSIONS)
/* 2: RX Extension needs to be drained
*/
@ -995,6 +1009,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
* priority either.
*/
return LWS_HPI_RET_HANDLED;
#endif
/* 3: buflist needs to be drained
*/
@ -1062,6 +1077,8 @@ read:
}
// lwsl_notice("Actual RX %d\n", ebuf.len);
lws_restart_ws_ping_pong_timer(wsi);
/*
* coverity thinks ssl_capable_read() may read over
* 2GB. Dissuade it...
@ -1157,8 +1174,10 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
#endif
int n;
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
wsi->protocol->name, wsi->ws->tx_draining_ext);
#endif
/* Priority 3: pending control packets (pong or close)
*
@ -1250,6 +1269,7 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
return LWS_HP_RET_USER_SERVICE;
#if !defined(LWS_WITHOUT_EXTENSIONS)
/* Priority 5: Tx path extension with more to send
*
* These are handled as new fragments each time around
@ -1267,7 +1287,6 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
/* Priority 6: extensions
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (!wsi->ws->extension_data_pending)
return LWS_HP_RET_USER_SERVICE;
@ -1412,6 +1431,7 @@ rops_periodic_checks_ws(struct lws_context *context, int tsi, time_t now)
static int
rops_service_flag_pending_ws(struct lws_context *context, int tsi)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi;
int forced = 0;
@ -1422,7 +1442,7 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)
* 1) For all guys with already-available ext data to drain, if they are
* not flowcontrolled, fake their POLLIN status
*/
wsi = pt->rx_draining_ext_list;
wsi = pt->ws.rx_draining_ext_list;
while (wsi) {
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
@ -1433,6 +1453,9 @@ rops_service_flag_pending_ws(struct lws_context *context, int tsi)
}
return forced;
#else
return 0;
#endif
}
static int
@ -1466,8 +1489,9 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
static int
rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
struct lws **w = &pt->rx_draining_ext_list;
struct lws **w = &pt->ws.rx_draining_ext_list;
wsi->ws->rx_draining_ext = 0;
/* remove us from context draining ext list */
@ -1482,7 +1506,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
}
if (wsi->ws->tx_draining_ext) {
struct lws **w = &pt->tx_draining_ext_list;
struct lws **w = &pt->ws.tx_draining_ext_list;
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
/* remove us from context draining ext list */
@ -1495,6 +1519,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
}
wsi->ws->tx_draining_ext_list = NULL;
}
#endif
lws_free_set_NULL(wsi->ws->rx_ubuf);
if (wsi->trunc_alloc)
@ -1504,6 +1529,11 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
wsi->ws->ping_payload_len = 0;
wsi->ws->ping_pending_flag = 0;
/* deallocate any active extension contexts */
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
lwsl_warn("extension destruction failed\n");
return 0;
}
@ -1511,20 +1541,22 @@ static int
rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol *wp)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
enum lws_write_protocol wpt;
#endif
int masked7 = lwsi_role_client(wsi);
unsigned char is_masked_bit = 0;
unsigned char *dropmask = NULL;
enum lws_write_protocol wpt;
struct lws_tokens ebuf;
size_t orig_len = len;
int pre = 0, n = 0;
// lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->tx_draining_ext) {
/* remove us from the list */
struct lws **w = &pt->tx_draining_ext_list;
struct lws **w = &pt->ws.tx_draining_ext_list;
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
@ -1555,7 +1587,7 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
wsi->ws->tx_draining_stashed_wp, wpt);
// assert(0);
}
#endif
lws_restart_ws_ping_pong_timer(wsi);
if (((*wp) & 0x1f) == LWS_WRITE_HTTP ||
@ -1610,8 +1642,8 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
/* extension requires further draining */
wsi->ws->tx_draining_ext = 1;
wsi->ws->tx_draining_ext_list = pt->tx_draining_ext_list;
pt->tx_draining_ext_list = wsi;
wsi->ws->tx_draining_ext_list = pt->ws.tx_draining_ext_list;
pt->ws.tx_draining_ext_list = wsi;
/* we must come back to do more */
lws_callback_on_writable(wsi);
/*
@ -1861,12 +1893,77 @@ rops_callback_on_writable_ws(struct lws *wsi)
return 0;
}
static int
rops_init_vhost_ws(struct lws_vhost *vh, struct lws_context_creation_info *info)
{
#if !defined(LWS_WITHOUT_EXTENSIONS)
#ifdef LWS_WITH_PLUGINS
struct lws_plugin *plugin = vh->context->plugin_list;
int m;
if (vh->context->plugin_extension_count) {
m = 0;
while (info->extensions && info->extensions[m].callback)
m++;
/*
* give the vhost a unified list of extensions including the
* ones that came from plugins
*/
vh->ws.extensions = lws_zalloc(sizeof(struct lws_extension) *
(m + vh->context->plugin_extension_count + 1),
"extensions");
if (!vh->ws.extensions)
return 1;
memcpy((struct lws_extension *)vh->ws.extensions, info->extensions,
sizeof(struct lws_extension) * m);
plugin = vh->context->plugin_list;
while (plugin) {
memcpy((struct lws_extension *)&vh->ws.extensions[m],
plugin->caps.extensions,
sizeof(struct lws_extension) *
plugin->caps.count_extensions);
m += plugin->caps.count_extensions;
plugin = plugin->list;
}
} else
#endif
vh->ws.extensions = info->extensions;
#endif
return 0;
}
static int
rops_destroy_vhost_ws(struct lws_vhost *vh)
{
#ifdef LWS_WITH_PLUGINS
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (vh->context->plugin_extension_count)
lws_free((void *)vh->ws.extensions);
#endif
#endif
return 0;
}
static int
rops_destroy_role_ws(struct lws *wsi)
{
lws_free_set_NULL(wsi->ws);
return 0;
}
struct lws_role_ops role_ops_ws = {
/* role name */ "ws",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* init_vhost */ rops_init_vhost_ws,
/* destroy_vhost */ rops_destroy_vhost_ws,
/* periodic_checks */ rops_periodic_checks_ws,
/* service_flag_pending */ rops_service_flag_pending_ws,
/* handle_POLLIN */ rops_handle_POLLIN_ws,
@ -1880,7 +1977,7 @@ struct lws_role_ops role_ops_ws = {
/* close_via_role_protocol */ rops_close_via_role_protocol_ws,
/* close_role */ rops_close_role_ws,
/* close_kill_connection */ rops_close_kill_connection_ws,
/* destroy_role */ NULL,
/* destroy_role */ rops_destroy_role_ws,
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE,
LWS_CALLBACK_SERVER_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,

View file

@ -73,20 +73,25 @@ enum lws_websocket_opcodes_07 {
#define ALREADY_PROCESSED_IGNORE_CHAR 1
#define ALREADY_PROCESSED_NO_CB 2
struct lws_vhost_role_ws {
#if !defined(LWS_WITHOUT_EXTENSIONS)
struct lws_vhost_role_ws {
const struct lws_extension *extensions;
#endif
};
struct lws_pt_role_ws {
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
};
#endif
struct _lws_websocket_related {
char *rx_ubuf;
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
void *act_ext_user[LWS_MAX_EXTENSIONS_ACTIVE];
#endif
struct lws *rx_draining_ext_list;
struct lws *tx_draining_ext_list;
#endif
/* Also used for close content... control opcode == < 128 */
uint8_t ping_payload_buf[128 - 3 + LWS_PRE];
uint8_t mask[4];
@ -120,20 +125,22 @@ 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;
unsigned int send_check_ping:1;
unsigned int first_fragment:1;
unsigned int peer_has_sent_close:1;
#if !defined(LWS_WITHOUT_EXTENSIONS)
unsigned int extension_data_pending:1;
unsigned int rx_draining_ext:1;
unsigned int tx_draining_ext:1;
uint8_t count_act_ext;
#endif
};
int
lws_ws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,

View file

@ -736,8 +736,10 @@ utf8_fail:
wsi->ws->first_fragment = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
__func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
#endif
return avail; /* how much we used from the input */
}
@ -765,7 +767,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
*buf += len; /* stashing it is taking care of it */
return 1;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
lwsl_debug("%s: draining rx ext\n", __func__);
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
@ -773,6 +775,7 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
return -1;
continue;
}
#endif
/* consume payload bytes efficiently */
while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
@ -807,10 +810,12 @@ lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
* We already handled this byte in bulk, just deal
* with the ramifications
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_debug("%s: coming out of bulk with len %d, "
"wsi->ws->rx_draining_ext %d\n",
__func__, (int)len,
wsi->ws->rx_draining_ext);
#endif
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
ALREADY_PROCESSED_NO_CB, 0);
}

View file

@ -41,8 +41,6 @@ lws_callback_as_writeable(struct lws *wsi)
}
#endif
assert(!(lwsi_role_ws(wsi) && wsi->ws->tx_draining_ext));
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
m = user_callback_handle_rxflow(wsi->protocol->callback,
@ -319,9 +317,9 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
* We only need to wait if really nothing already to do and we have
* to wait for something from network
*/
#if defined(LWS_ROLE_WS)
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
/* 1) if we know we are draining rx ext, do not wait in poll */
if (pt->rx_draining_ext_list)
if (pt->ws.rx_draining_ext_list)
return 0;
#endif

View file

@ -255,8 +255,6 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_restart_ws_ping_pong_timer(wsi);
// lwsl_hexdump_err(buf, n);
/*

View file

@ -63,8 +63,8 @@ MACRO(require_lws_config reqconfig _val result)
ENDMACRO()
set(requirements 1)
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})

View file

@ -64,7 +64,7 @@ ENDMACRO()
set(requirements 1)
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
#require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})
@ -75,4 +75,4 @@ if (requirements)
else()
target_link_libraries(${SAMP} websockets)
endif()
endif()
endif()

View file

@ -109,9 +109,9 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
case LWS_CALLBACK_RECEIVE:
if (len < 6)
break;
if (strcmp((const char *)in, "reset\n") == 0)
if (strncmp((const char *)in, "reset\n", 6) == 0)
pss->number = 0;
if (strcmp((const char *)in, "closeme\n") == 0) {
if (strncmp((const char *)in, "closeme\n", 8) == 0) {
lwsl_notice("dumb_inc: closing as requested\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);

View file

@ -1,616 +0,0 @@
/*
* lws meta protocol handler
*
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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
*
*/
#if !defined (LWS_PLUGIN_STATIC)
#define LWS_DLL
#define LWS_INTERNAL
#include "../lib/libwebsockets.h"
#endif
#include <string.h>
#include <stdlib.h>
#define MAX_SUBCHANNELS 8
enum lws_meta_parser_state {
MP_IDLE, /* in body of message */
MP_CMD, /* await cmd */
MP_OPEN_SUBCHANNEL_PROTOCOL,
MP_OPEN_SUBCHANNEL_URL,
MP_OPEN_SUBCHANNEL_COOKIE,
MP_CLOSE_CHID,
MP_CLOSE_LEN,
MP_CLOSE_CODEM,
MP_CLOSE_CODEL,
MP_CLOSE_PAYLOAD,
MP_WRITE_CHID,
};
enum {
PENDING_TYPE_OPEN_RESULT = 0,
PENDING_TYPE_CHILD_CLOSE
};
/*
* while we haven't reported the result yet, we keep a linked-list of
* connection opens and their result.
*/
struct pending_conn {
struct pending_conn *next;
char protocol[123];
char cookie[8];
int ch;
int len;
unsigned char type;
};
/*
* the parent, lws-meta connection
*/
struct per_session_data__lws_meta {
struct lws *wsi[MAX_SUBCHANNELS + 1];
char told_closing[MAX_SUBCHANNELS + 1];
struct pending_conn *first;
struct pending_conn *pend;
char suburl[64];
unsigned char close[126];
int active_subchannel_tx, active_subchannel_rx;
enum lws_meta_parser_state state;
int pos;
int count_pending;
int round_robin;
int close_status_16;
int close_len;
int which_close;
int ch;
};
static int
lws_find_free_channel(struct per_session_data__lws_meta *pss)
{
int n;
for (n = 1; n <= MAX_SUBCHANNELS; n++)
if (pss->wsi[n] == NULL)
return n;
return 0; /* none free */
}
static struct lws *
lws_get_channel_wsi(struct per_session_data__lws_meta *pss, int ch)
{
if (!ch)
return 0;
return pss->wsi[ch];
}
static int
lws_get_channel_id(struct lws *wsi)
{
return (int)(lws_intptr_t)lws_get_opaque_parent_data(wsi);
}
static void
lws_set_channel_id(struct lws *wsi, int id)
{
lws_set_opaque_parent_data(wsi, (void *)(lws_intptr_t)id);
}
static struct pending_conn *
new_pending(struct per_session_data__lws_meta *pss)
{
struct pending_conn *pend;
if (pss->count_pending >= MAX_SUBCHANNELS * 2) {
lwsl_notice("too many pending open subchannel\n");
return NULL;
}
pss->count_pending++;
pend = malloc(sizeof(*pend));
if (!pend) {
lwsl_notice("OOM\n");
return NULL;
}
memset(pend, 0, sizeof(*pend));
return pend;
}
static int
callback_lws_meta(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__lws_meta *pss =
(struct per_session_data__lws_meta *)user;
struct lws_write_passthru *pas;
struct pending_conn *pend, *pend1;
struct lws *cwsi;
lws_sock_file_fd_type fd;
unsigned char *bin, buf[LWS_PRE + 512], *start = &buf[LWS_PRE],
*end = &buf[sizeof(buf) - 1], *p = start;
int n, m;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
pss->state = MP_CMD;
pss->pos = 0;
break;
case LWS_CALLBACK_CLOSED:
break;
case LWS_CALLBACK_CHILD_CLOSING:
cwsi = (struct lws *)in;
/* remove it from our tracking */
pss->wsi[lws_get_channel_id(cwsi)] = NULL;
if (pss->told_closing[lws_get_channel_id(cwsi)]) {
pss->told_closing[lws_get_channel_id(cwsi)] = 0;
break;
}
pend = new_pending(pss);
if (!pend)
return -1;
/* note which channel id */
pend->ch = lws_get_channel_id(cwsi);
if (lws_get_close_length(cwsi)) {
pend->len = lws_get_close_length(cwsi);
memcpy(pend->protocol, lws_get_close_payload(cwsi),
pend->len);
}
pend->type = PENDING_TYPE_CHILD_CLOSE;
pend->next = pss->first;
pss->first = pend;
/*
* nothing else will complete from this wsi, so abandon
* tracking in-process messages from this wsi.
*/
if (pss->active_subchannel_tx == pend->ch)
pss->active_subchannel_tx = 0;
if (pss->active_subchannel_rx == pend->ch)
pss->active_subchannel_rx = 0;
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
if (!pss->active_subchannel_tx) {
/* not in the middle of a message...
*
* PRIORITY 1: pending open and close notifications
*/
pend = pss->first;
while (pend && p < end - 128) {
switch (pend->type) {
case PENDING_TYPE_OPEN_RESULT:
lwsl_debug("open result %s %s\n",
pend->cookie, pend->protocol);
*p++ = LWS_META_CMD_OPEN_RESULT;
memcpy(p, pend->cookie,
strlen(pend->cookie) + 1);
p += strlen(pend->cookie) + 1;
*p++ = LWS_META_TRANSPORT_OFFSET +
pend->ch;
memcpy(p, pend->protocol,
strlen(pend->protocol) + 1);
p += strlen(pend->protocol) + 1;
break;
case PENDING_TYPE_CHILD_CLOSE:
*p++ = LWS_META_CMD_CLOSE_NOTIFY;
*p++ = LWS_META_TRANSPORT_OFFSET +
pend->ch;
for (n = 0; n < pend->len; n++)
*p++ = pend->protocol[n];
break;
}
pss->count_pending--;
pend1 = pend;
pend = pend->next;
free(pend1);
pss->first = pend;
}
if (p != start) {
if (lws_write(wsi, start, p - start,
LWS_WRITE_BINARY) < 0)
return 1;
if (pend) /* still more */
lws_callback_on_writable(wsi);
break;
}
/* PRIORITY 2: pick a child for the writable callback */
cwsi = NULL;
for (n = 0; n < MAX_SUBCHANNELS; n++) {
m = ((pss->round_robin + n) % MAX_SUBCHANNELS) + 1;
if (pss->wsi[m] &&
lws_get_child_pending_on_writable(pss->wsi[m])) {
pss->round_robin = m;
cwsi = pss->wsi[m];
break;
}
}
} else
/* one child is in middle of message, stay with it */
cwsi = pss->wsi[pss->active_subchannel_tx];
if (!cwsi)
break;
lws_clear_child_pending_on_writable(cwsi);
if (lws_handle_POLLOUT_event(cwsi, NULL))
return -1;
break;
case LWS_CALLBACK_RECEIVE:
bin = (unsigned char *)in;
/*
* at the start of a message, we may have one or more
* lws_meta command blocks.
*/
while (pss->state != MP_IDLE &&
(unsigned int)(bin - (unsigned char *)in) < len) {
switch (pss->state) {
case MP_IDLE: /* in body of message */
if (!lws_is_first_fragment(wsi))
break;
pss->state = MP_CMD;
/* fallthru */
case MP_CMD: /* await cmd */
pss->pos = 0;
switch (*bin++) {
case LWS_META_CMD_OPEN_SUBCHANNEL:
pss->pend = new_pending(pss);
if (!pss->pend)
return -1;
pss->state = MP_OPEN_SUBCHANNEL_PROTOCOL;
break;
case LWS_META_CMD_CLOSE_NOTIFY:
case LWS_META_CMD_CLOSE_RQ:
pss->which_close = bin[-1];
pss->state = MP_CLOSE_CHID;
break;
case LWS_META_CMD_WRITE:
pss->state = MP_WRITE_CHID;
break;
// open result is also illegal to receive
default:
lwsl_notice("bad lws_meta cmd 0x%x\n",
bin[-1]);
return -1;
}
break;
case MP_OPEN_SUBCHANNEL_PROTOCOL:
pss->pend->protocol[pss->pos++] = *bin++;
if (pss->pos == sizeof(pss->pend->protocol) - 1) {
lwsl_notice("protocol name too long\n");
return -1;
}
if (bin[-1] != '\0')
break;
pss->state = MP_OPEN_SUBCHANNEL_URL;
pss->pos = 0;
break;
case MP_OPEN_SUBCHANNEL_URL:
pss->suburl[pss->pos++] = *bin++;
if (pss->pos == sizeof(pss->suburl) - 1) {
lwsl_notice("suburl too long\n");
return -1;
}
if (bin[-1] != '\0')
break;
pss->state = MP_OPEN_SUBCHANNEL_COOKIE;
pss->pos = 0;
break;
case MP_OPEN_SUBCHANNEL_COOKIE:
pss->pend->cookie[pss->pos++] = *bin++;
if (pss->pos == sizeof(pss->pend->cookie) - 1) {
lwsl_notice("cookie too long\n");
return -1;
}
if (bin[-1] != '\0')
break;
lwsl_debug("%s: %s / %s / %s\n", __func__,
pss->pend->protocol,
pss->suburl,
pss->pend->cookie);
pss->pend->ch = lws_find_free_channel(pss);
if (pss->pend->ch) {
fd.sockfd = 0; // not going to be used
cwsi = lws_adopt_descriptor_vhost(
lws_get_vhost(wsi),
LWS_ADOPT_WS_PARENTIO,
fd, pss->pend->protocol,
wsi);
if (!cwsi) {
lwsl_notice("open failed\n");
pss->pend->ch = 0;
} else {
pss->wsi[pss->pend->ch] = cwsi;
lws_set_channel_id(cwsi,
pss->pend->ch);
lwsl_debug("cwsi %p on parent %p open OK %s\n",
cwsi, wsi, pss->pend->protocol);
}
} else
lwsl_notice("no free subchannels\n");
pss->pend->type = PENDING_TYPE_OPEN_RESULT;
pss->pend->next = pss->first;
pss->first = pss->pend;
lws_callback_on_writable(wsi);
pss->state = MP_CMD;
pss->pos = 0;
break;
case MP_CLOSE_CHID:
pss->ch = (*bin++) - LWS_META_TRANSPORT_OFFSET;
pss->state = MP_CLOSE_LEN;
pss->pos = 0;
break;
case MP_CLOSE_LEN:
pss->close_len = (*bin++) -
LWS_META_TRANSPORT_OFFSET;
lwsl_debug("close len %d\n", pss->close_len);
pss->state = MP_CLOSE_CODEM;
pss->pos = 0;
break;
case MP_CLOSE_CODEM:
pss->close[pss->pos++] = *bin;
pss->close_status_16 = (*bin++) * 256;
pss->state = MP_CLOSE_CODEL;
break;
case MP_CLOSE_CODEL:
pss->close[pss->pos++] = *bin;
pss->close_status_16 |= *bin++;
pss->state = MP_CLOSE_PAYLOAD;
break;
case MP_CLOSE_PAYLOAD:
pss->close[pss->pos++] = *bin++;
if (pss->pos == sizeof(pss->close) - 1) {
lwsl_notice("close payload too long\n");
return -1;
}
if (--pss->close_len)
break;
pss->state = MP_CMD;
cwsi = lws_get_channel_wsi(pss, pss->ch);
if (!cwsi) {
lwsl_notice("close (%d) bad ch %d\n",
pss->which_close, pss->ch);
break;
}
if (pss->which_close == LWS_META_CMD_CLOSE_RQ) {
if (lws_get_protocol(cwsi)->callback(
cwsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
lws_wsi_user(cwsi), &pss->close,
pss->pos))
return -1;
/*
* we need to echo back the close payload
* when we send the close notification
*/
lws_close_reason(cwsi,
pss->close_status_16,
&pss->close[2],
pss->pos - 2);
}
/* so force him closed */
lws_set_timeout(cwsi,
PENDING_TIMEOUT_KILLED_BY_PARENT,
LWS_TO_KILL_SYNC);
break;
case MP_WRITE_CHID:
pss->active_subchannel_rx = (*bin++) -
LWS_META_TRANSPORT_OFFSET;
pss->state = MP_IDLE;
break;
}
}
len -= bin - (unsigned char *)in;
if (!len)
break;
cwsi = lws_get_channel_wsi(pss, pss->active_subchannel_rx);
if (!cwsi) {
lwsl_notice("bad ch %d\n", pss->active_subchannel_rx);
return -1;
}
// lwsl_debug("%s: RX len %d\n", __func__, (int)len);
if (lws_get_protocol(cwsi)->callback(cwsi,
LWS_CALLBACK_RECEIVE,
lws_wsi_user(cwsi), bin, len))
lws_set_timeout(cwsi,
PENDING_TIMEOUT_KILLED_BY_PARENT,
LWS_TO_KILL_SYNC);
if (lws_is_final_fragment(wsi)) {
pss->active_subchannel_rx = 0;
pss->state = MP_CMD;
}
break;
/*
* child wrote something via lws_write.... which passed it up to us to
* deal with, because we are the parent. Prepend two bytes for
* lws-meta command and channel index, and send it out on parent
*/
case LWS_CALLBACK_CHILD_WRITE_VIA_PARENT:
pas = in;
bin = ((unsigned char *)pas->buf);
if ((pas->wp & 7) == 4 /*LWS_WRITE_CLOSE */) {
*p++ = LWS_META_CMD_CLOSE_NOTIFY;
*p++ = LWS_META_TRANSPORT_OFFSET +
lws_get_channel_id(pas->wsi);
*p++ = (unsigned char)pas->len +
LWS_META_TRANSPORT_OFFSET - 2;
*p++ = *bin++;
*p++ = *bin++;
for (n = 0; n < (int)pas->len - 2; n++)
*p++ = bin[n];
if (lws_write(wsi, start, p - start,
LWS_WRITE_BINARY) < 0)
return 1;
pss->told_closing[lws_get_channel_id(pas->wsi)] = 1;
break;
}
if ((pas->wp & 7) == LWS_WRITE_TEXT ||
(pas->wp & 7) == LWS_WRITE_BINARY) {
if (pas->wp & LWS_WRITE_NO_FIN)
pss->active_subchannel_tx =
lws_get_channel_id(pas->wsi);
/* start of message, prepend the subchannel id */
bin -= 2;
bin[0] = LWS_META_CMD_WRITE;
bin[1] = lws_get_channel_id(pas->wsi) +
LWS_META_TRANSPORT_OFFSET;
if (lws_write(wsi, bin, pas->len + 2, pas->wp) < 0)
return 1;
} else
if (lws_write(wsi, bin, pas->len, pas->wp) < 0)
return 1;
/* track EOM */
if (!(pas->wp & LWS_WRITE_NO_FIN))
pss->active_subchannel_tx = 0;
break;
default:
break;
}
return 0;
}
#define LWS_PLUGIN_PROTOCOL_LWS_META { \
"lws-meta", \
callback_lws_meta, \
sizeof(struct per_session_data__lws_meta), \
1024, /* rx buf size must be >= permessage-deflate rx size */ \
0, NULL, 0 \
}
#if !defined (LWS_PLUGIN_STATIC)
static const struct lws_protocols protocols[] = {
LWS_PLUGIN_PROTOCOL_LWS_META
};
LWS_EXTERN LWS_VISIBLE int
init_protocol_lws_meta(struct lws_context *context,
struct lws_plugin_capability *c)
{
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
c->api_magic);
return 1;
}
c->protocols = protocols;
c->count_protocols = ARRAY_SIZE(protocols);
c->extensions = NULL;
c->count_extensions = 0;
return 0;
}
LWS_EXTERN LWS_VISIBLE int
destroy_protocol_lws_meta(struct lws_context *context)
{
return 0;
}
#endif

View file

@ -1,373 +0,0 @@
/*
* libwebsockets-test-fraggle - random fragmentation test
*
* Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include "../lib/libwebsockets.h"
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
static int client;
static int terminate;
enum demo_protocols {
PROTOCOL_FRAGGLE,
/* always last */
DEMO_PROTOCOL_COUNT
};
/* fraggle protocol */
enum fraggle_states {
FRAGSTATE_START_MESSAGE,
FRAGSTATE_RANDOM_PAYLOAD,
FRAGSTATE_POST_PAYLOAD_SUM,
};
struct per_session_data__fraggle {
int packets_left;
int total_message;
unsigned long sum;
enum fraggle_states state;
};
static int
callback_fraggle(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
unsigned char buf[LWS_PRE + 8000], *bp = &buf[LWS_PRE];
struct per_session_data__fraggle *psf = user;
unsigned char *p = (unsigned char *)in;
int n, chunk, flags, ran;
unsigned long sum;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
fprintf(stderr, "server sees client connect\n");
psf->state = FRAGSTATE_START_MESSAGE;
/* start the ball rolling */
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
fprintf(stderr, "client connects to server\n");
psf->state = FRAGSTATE_START_MESSAGE;
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
switch (psf->state) {
case FRAGSTATE_START_MESSAGE:
psf->state = FRAGSTATE_RANDOM_PAYLOAD;
psf->sum = 0;
psf->total_message = 0;
psf->packets_left = 0;
/* fallthru */
case FRAGSTATE_RANDOM_PAYLOAD:
for (n = 0; (unsigned int)n < len; n++)
psf->sum += p[n];
psf->total_message += (int)len;
psf->packets_left++;
if (lws_is_final_fragment(wsi))
psf->state = FRAGSTATE_POST_PAYLOAD_SUM;
break;
case FRAGSTATE_POST_PAYLOAD_SUM:
sum = ((unsigned int)p[0]) << 24;
sum |= p[1] << 16;
sum |= p[2] << 8;
sum |= p[3];
if (sum == psf->sum)
fprintf(stderr, "EOM received %d correctly "
"from %d fragments\n",
psf->total_message, psf->packets_left);
else
fprintf(stderr, "**** ERROR at EOM: "
"length %d, rx sum = 0x%lX, "
"server says it sent 0x%lX\n",
psf->total_message, psf->sum, sum);
psf->state = FRAGSTATE_START_MESSAGE;
break;
}
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
switch (psf->state) {
case FRAGSTATE_START_MESSAGE:
lws_get_random(lws_get_context(wsi), &ran, sizeof(ran));
psf->packets_left = (ran & 1023) + 1;
fprintf(stderr, "Spamming %d random fragments\n",
psf->packets_left);
psf->sum = 0;
psf->total_message = 0;
psf->state = FRAGSTATE_RANDOM_PAYLOAD;
/* fallthru */
case FRAGSTATE_RANDOM_PAYLOAD:
/*
* note how one chunk can be 8000, but we use the
* default rx buffer size of 4096, so we exercise the
* code for rx spill because the rx buffer is full
*/
lws_get_random(lws_get_context(wsi), &ran, sizeof(ran));
chunk = (ran & 511) + 1;
psf->total_message += chunk;
lws_get_random(lws_get_context(wsi), bp, chunk);
for (n = 0; n < chunk; n++)
psf->sum += bp[n];
psf->packets_left--;
flags = lws_write_ws_flags(LWS_WRITE_BINARY, !psf->sum,
!psf->packets_left);
if (!psf->packets_left)
psf->state = FRAGSTATE_POST_PAYLOAD_SUM;
n = lws_write(wsi, bp, chunk, flags);
if (n < 0)
return -1;
lws_callback_on_writable(wsi);
break;
case FRAGSTATE_POST_PAYLOAD_SUM:
fprintf(stderr, "Spamming session over, "
"len = %d. sum = 0x%lX\n",
psf->total_message, psf->sum);
bp[0] = psf->sum >> 24;
bp[1] = (unsigned char)(psf->sum >> 16);
bp[2] = (unsigned char)(psf->sum >> 8);
bp[3] = (unsigned char)psf->sum;
n = lws_write(wsi, (unsigned char *)bp,
4, LWS_WRITE_BINARY);
if (n < 0)
return -1;
if (n < 4) {
lwsl_err("Partial write\n");
return -1;
}
psf->state = FRAGSTATE_START_MESSAGE;
lws_callback_on_writable(wsi);
break;
}
break;
case LWS_CALLBACK_CLOSED:
terminate = 1;
break;
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
if (strcmp(in, "deflate-stream") == 0) {
fprintf(stderr, "denied deflate-stream extension\n");
return 1;
}
break;
default:
break;
}
return 0;
}
/* list of supported protocols and callbacks */
static struct lws_protocols protocols[] = {
{
"fraggle-protocol",
callback_fraggle,
sizeof(struct per_session_data__fraggle),
},
{
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 */ }
};
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", required_argument, NULL, 'd' },
{ "port", required_argument, NULL, 'p' },
{ "ssl", no_argument, NULL, 's' },
{ "interface", required_argument, NULL, 'i' },
{ "client", no_argument, NULL, 'c' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
int n = 0;
int port = 7681;
int use_ssl = 0;
struct lws_context *context;
int opts = 0;
char interface_name[128] = "", ads_port[300];
const char *iface = NULL;
struct lws *wsi;
const char *address = NULL;
int server_port = port;
struct lws_context_creation_info info;
memset(&info, 0, sizeof info);
lwsl_notice("libwebsockets test server fraggle - license LGPL2.1+SLE\n");
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
while (n >= 0) {
n = getopt_long(argc, argv, "ci:hsp:d:", options, NULL);
if (n < 0)
continue;
switch (n) {
case 'd':
lws_set_log_level(atoi(optarg), NULL);
break;
case 's':
use_ssl = 1;
break;
case 'p':
port = atoi(optarg);
server_port = port;
break;
case 'i':
lws_strncpy(interface_name, optarg, sizeof interface_name);
iface = interface_name;
break;
case 'c':
client = 1;
fprintf(stderr, " Client mode\n");
break;
case 'h':
fprintf(stderr, "Usage: libwebsockets-test-fraggle "
"[--port=<p>] [--ssl] "
"[-d <log bitfield>] "
"[--client]\n");
exit(1);
}
}
if (client) {
server_port = CONTEXT_PORT_NO_LISTEN;
if (optind >= argc) {
fprintf(stderr, "Must give address of server\n");
return 1;
}
}
info.port = server_port;
info.iface = iface;
info.protocols = protocols;
info.extensions = exts;
if (use_ssl) {
info.ssl_cert_filepath = LOCAL_RESOURCE_PATH
"/libwebsockets-test-server.pem";
info.ssl_private_key_filepath = LOCAL_RESOURCE_PATH
"/libwebsockets-test-server.key.pem";
}
info.gid = -1;
info.uid = -1;
info.options = opts;
info.extensions = exts;
if (use_ssl)
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
context = lws_create_context(&info);
if (context == NULL) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
if (client) {
struct lws_client_connect_info i;
address = argv[optind];
lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
address, port & 65535);
memset(&i, 0, sizeof(i));
i.context = context;
i.address = address;
i.port = port;
i.ssl_connection = use_ssl;
i.path = "/";
i.host = ads_port;
i.origin = ads_port;
i.protocol = protocols[PROTOCOL_FRAGGLE].name;
lwsl_notice("Connecting to %s:%u\n", address, port);
wsi = lws_client_connect_via_info(&i);
if (wsi == NULL) {
fprintf(stderr, "Client connect to server failed\n");
goto bail;
}
}
n = 0;
while (!n && !terminate)
n = lws_service(context, 50);
fprintf(stderr, "Terminating...\n");
bail:
lws_context_destroy(context);
return 0;
}

View file

@ -19,6 +19,8 @@
*/
#include "test-server.h"
#if defined(LWS_ROLE_WS)
/* dumb_increment protocol */
int
@ -53,11 +55,12 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
break;
case LWS_CALLBACK_RECEIVE:
if (len < 6)
break;
if (strcmp((const char *)in, "reset\n") == 0)
// if (len < 6)
// break;
lwsl_hexdump_notice(in, len);
if (strncmp((const char *)in, "reset\n", 6) == 0)
pss->number = 0;
if (strcmp((const char *)in, "closeme\n") == 0) {
if (strncmp((const char *)in, "closeme\n", 8) == 0) {
lwsl_notice("dumb_inc: closing as requested\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);
@ -100,3 +103,4 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
return 0;
}
#endif

View file

@ -47,10 +47,11 @@ void test_server_unlock(int care)
}
#define LWS_PLUGIN_STATIC
#if defined(LWS_ROLE_WS)
#include "../plugins/protocol_dumb_increment.c"
#include "../plugins/protocol_lws_mirror.c"
#include "../plugins/protocol_lws_status.c"
#include "../plugins/protocol_lws_meta.c"
#endif
/*
* This demo server shows how to use libwebsockets for one or more
@ -74,7 +75,6 @@ enum demo_protocols {
PROTOCOL_DUMB_INCREMENT,
PROTOCOL_LWS_MIRROR,
PROTOCOL_LWS_STATUS,
PROTOCOL_LWS_META,
/* always last */
DEMO_PROTOCOL_COUNT
@ -91,13 +91,15 @@ static struct lws_protocols protocols[] = {
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
#if defined(LWS_ROLE_WS)
LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,
LWS_PLUGIN_PROTOCOL_MIRROR,
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
LWS_PLUGIN_PROTOCOL_LWS_META,
#endif
{ NULL, NULL, 0, 0 } /* terminator */
};
static const struct lws_extension exts[] = {
{
"permessage-deflate",

View file

@ -1,401 +0,0 @@
/*
* libwebsockets-test-server - libwebsockets test implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
#include "test-server.h"
#include <pthread.h>
int close_testing;
int max_poll_elements;
int debug_level = 7;
#ifdef EXTERNAL_POLL
struct lws_pollfd *pollfds;
int *fd_lookup;
int count_pollfds;
#endif
volatile int force_exit = 0;
struct lws_context *context;
#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
char crl_path[1024] = "";
#endif
#define LWS_PLUGIN_STATIC
#include "../plugins/protocol_lws_mirror.c"
#include "../plugins/protocol_lws_status.c"
/*
* This mutex lock protects code that changes or relies on wsi list outside of
* the service thread. The service thread will acquire it when changing the
* wsi list and other threads should acquire it while dereferencing wsis or
* calling apis like lws_callback_on_writable_all_protocol() which
* use the wsi list and wsis from a different thread context.
*/
pthread_mutex_t lock_established_conns;
/* http server gets files from this path */
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
char *resource_path = LOCAL_RESOURCE_PATH;
/*
* multithreaded version - protect wsi lifecycle changes in the library
* these are called from protocol 0 callbacks
*/
void test_server_lock(int care)
{
if (care)
pthread_mutex_lock(&lock_established_conns);
}
void test_server_unlock(int care)
{
if (care)
pthread_mutex_unlock(&lock_established_conns);
}
/*
* This demo server shows how to use libwebsockets for one or more
* websocket protocols in the same server
*
* It defines the following websocket protocols:
*
* dumb-increment-protocol: once the socket is opened, an incrementing
* ascii string is sent down it every 50ms.
* If you send "reset\n" on the websocket, then
* the incrementing number is reset to 0.
*
* lws-mirror-protocol: copies any received packet to every connection also
* using this protocol, including the sender
*/
enum demo_protocols {
/* always first */
PROTOCOL_HTTP = 0,
PROTOCOL_DUMB_INCREMENT,
PROTOCOL_LWS_MIRROR,
PROTOCOL_LWS_STATUS,
/* always last */
DEMO_PROTOCOL_COUNT
};
/* list of supported protocols and callbacks */
static struct lws_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", /* name */
callback_http, /* callback */
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
{
"dumb-increment-protocol",
callback_dumb_increment,
sizeof(struct per_session_data__dumb_increment),
10, /* rx buf size must be >= permessage-deflate rx size
* dumb-increment only sends very small packets, so we set
* this accordingly. If your protocol will send bigger
* things, adjust this to match */
},
LWS_PLUGIN_PROTOCOL_MIRROR,
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
{ NULL, NULL, 0, 0 } /* terminator */
};
void *thread_dumb_increment(void *threadid)
{
while (!force_exit) {
/*
* this lock means wsi in the active list cannot
* disappear underneath us, because the code to add and remove
* them is protected by the same lock
*/
pthread_mutex_lock(&lock_established_conns);
lws_callback_on_writable_all_protocol(context,
&protocols[PROTOCOL_DUMB_INCREMENT]);
pthread_mutex_unlock(&lock_established_conns);
usleep(100000);
}
pthread_exit(NULL);
}
void *thread_service(void *threadid)
{
while (lws_service_tsi(context, 50, (int)(lws_intptr_t)threadid) >= 0 && !force_exit)
;
pthread_exit(NULL);
}
void sighandler(int sig)
{
force_exit = 1;
lws_cancel_service(context);
}
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 */ }
};
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", required_argument, NULL, 'd' },
{ "port", required_argument, NULL, 'p' },
{ "ssl", no_argument, NULL, 's' },
{ "allow-non-ssl", no_argument, NULL, 'a' },
{ "interface", required_argument, NULL, 'i' },
{ "closetest", no_argument, NULL, 'c' },
{ "libev", no_argument, NULL, 'e' },
{ "threads", required_argument, NULL, 'j' },
#ifndef LWS_NO_DAEMONIZE
{ "daemonize", no_argument, NULL, 'D' },
#endif
{ "resource_path", required_argument, NULL, 'r' },
{ NULL, 0, 0, 0 }
};
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
char interface_name[128] = "";
const char *iface = NULL;
pthread_t pthread_dumb, pthread_service[32];
char cert_path[1024];
char key_path[1024];
int threads = 1;
int use_ssl = 0;
void *retval;
int opts = 0;
int n = 0;
#ifndef _WIN32
/* LOG_PERROR is not POSIX standard, and may not be portable */
#ifdef __sun
int syslog_options = LOG_PID;
#else
int syslog_options = LOG_PID | LOG_PERROR;
#endif
#endif
#ifndef LWS_NO_DAEMONIZE
int daemonize = 0;
#endif
/*
* take care to zero down the info struct, he contains random garbaage
* from the stack otherwise
*/
memset(&info, 0, sizeof info);
info.port = 7681;
pthread_mutex_init(&lock_established_conns, NULL);
while (n >= 0) {
n = getopt_long(argc, argv, "eci:hsap:d:Dr:j:", options, NULL);
if (n < 0)
continue;
switch (n) {
case 'j':
threads = atoi(optarg);
if (threads > (int)ARRAY_SIZE(pthread_service)) {
lwsl_err("Max threads %lu\n",
(unsigned long)ARRAY_SIZE(pthread_service));
return 1;
}
break;
case 'e':
opts |= LWS_SERVER_OPTION_LIBEV;
break;
#ifndef LWS_NO_DAEMONIZE
case 'D':
daemonize = 1;
#if !defined(_WIN32) && !defined(__sun)
syslog_options &= ~LOG_PERROR;
#endif
break;
#endif
case 'd':
debug_level = atoi(optarg);
break;
case 's':
use_ssl = 1;
break;
case 'a':
opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
break;
case 'p':
info.port = atoi(optarg);
break;
case 'i':
lws_strncpy(interface_name, optarg, sizeof interface_name);
iface = interface_name;
break;
case 'c':
close_testing = 1;
fprintf(stderr, " Close testing mode -- closes on "
"client after 50 dumb increments"
"and suppresses lws_mirror spam\n");
break;
case 'r':
resource_path = optarg;
printf("Setting resource path to \"%s\"\n", resource_path);
break;
case 'h':
fprintf(stderr, "Usage: test-server "
"[--port=<p>] [--ssl] "
"[-d <log bitfield>] "
"[--resource_path <path>]\n");
exit(1);
}
}
#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
/*
* normally lock path would be /var/lock/lwsts or similar, to
* simplify getting started without having to take care about
* permissions or running as root, set to /tmp/.lwsts-lock
*/
if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
fprintf(stderr, "Failed to daemonize\n");
return 1;
}
#endif
signal(SIGINT, sighandler);
#ifndef _WIN32
/* we will only try to log things according to our debug_level */
setlogmask(LOG_UPTO (LOG_DEBUG));
openlog("lwsts", syslog_options, LOG_DAEMON);
#endif
/* tell the library what debug level to emit and to send it to syslog */
lws_set_log_level(debug_level, NULL);
lwsl_notice("libwebsockets test server pthreads - license LGPL2.1+SLE\n");
lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
printf("Using resource path \"%s\"\n", resource_path);
#ifdef EXTERNAL_POLL
max_poll_elements = getdtablesize();
pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd));
fd_lookup = malloc(max_poll_elements * sizeof (int));
if (pollfds == NULL || fd_lookup == NULL) {
lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
return -1;
}
#endif
info.iface = iface;
info.protocols = protocols;
info.extensions = exts;
info.ssl_cert_filepath = NULL;
info.ssl_private_key_filepath = NULL;
if (use_ssl) {
if (strlen(resource_path) > sizeof(cert_path) - 32) {
lwsl_err("resource path too long\n");
return -1;
}
sprintf(cert_path, "%s/libwebsockets-test-server.pem",
resource_path);
if (strlen(resource_path) > sizeof(key_path) - 32) {
lwsl_err("resource path too long\n");
return -1;
}
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
resource_path);
info.ssl_cert_filepath = cert_path;
info.ssl_private_key_filepath = key_path;
}
info.gid = -1;
info.uid = -1;
info.options = opts;
info.count_threads = threads;
info.extensions = exts;
info.max_http_header_pool = 4;
info.pt_serv_buf_size = 128 * 1024;
/* when doing slow benchmarks with thousands of concurrent
* connections, we need wait longer
*/
info.timeout_secs = 30;
info.keepalive_timeout = 30;
context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
return -1;
}
/* start the dumb increment thread */
n = pthread_create(&pthread_dumb, NULL, thread_dumb_increment, 0);
if (n) {
lwsl_err("Unable to create dumb thread\n");
goto done;
}
/*
* notice the actual number of threads may be capped by the library,
* so use lws_get_count_threads() to get the actual amount of threads
* initialized.
*/
lwsl_notice("Service thread count: %d\n", lws_get_count_threads(context));
for (n = 0; n < lws_get_count_threads(context); n++)
if (pthread_create(&pthread_service[n], NULL, thread_service,
(void *)(lws_intptr_t)n))
lwsl_err("Failed to start service thread\n");
/* wait for all the service threads to exit */
while ((--n) >= 0)
pthread_join(pthread_service[n], &retval);
/* wait for pthread_dumb to exit */
pthread_join(pthread_dumb, &retval);
done:
lws_context_destroy(context);
pthread_mutex_destroy(&lock_established_conns);
lwsl_notice("libwebsockets-test-server exited cleanly\n");
#ifndef _WIN32
closelog();
#endif
return 0;
}

View file

@ -66,9 +66,10 @@ char crl_path[1024] = "";
*/
#define LWS_PLUGIN_STATIC
#if defined(LWS_ROLE_WS)
#include "../plugins/protocol_lws_mirror.c"
#include "../plugins/protocol_lws_status.c"
#include "../plugins/protocol_lws_meta.c"
#endif
/* singlethreaded version --> no locks */
@ -122,6 +123,7 @@ static struct lws_protocols protocols[] = {
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
#if defined(LWS_ROLE_WS)
{
"dumb-increment-protocol",
callback_dumb_increment,
@ -133,8 +135,7 @@ static struct lws_protocols protocols[] = {
},
LWS_PLUGIN_PROTOCOL_MIRROR,
LWS_PLUGIN_PROTOCOL_LWS_STATUS,
LWS_PLUGIN_PROTOCOL_LWS_META,
#endif
{ NULL, NULL, 0, 0 } /* terminator */
};