diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index fca470e8..e7a88651 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -4644,6 +4644,8 @@ lws_timed_callback_vh_protocol(struct lws_vhost *vh, #define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE #define LWS_SEND_BUFFER_POST_PADDING 0 +#define LWS_WRITE_RAW LWS_WRITE_HTTP + /* * NOTE: These public enums are part of the abi. If you want to add one, * add it at where specified so existing users are unaffected. diff --git a/minimal-examples/raw/README.md b/minimal-examples/raw/README.md index c076566e..1c70f56a 100644 --- a/minimal-examples/raw/README.md +++ b/minimal-examples/raw/README.md @@ -1,3 +1,5 @@ |name|demonstrates| ---|--- minimal-raw-file|Shows how to adopt a file descriptor (device node, fifo, file, etc) into the lws event loop and handle events +minimal-raw-vhost|Shows how to set up a vhost that listens and accepts RAW socket connections + diff --git a/minimal-examples/raw/minimal-raw-file/README.md b/minimal-examples/raw/minimal-raw-file/README.md index 3d18c928..47fba247 100644 --- a/minimal-examples/raw/minimal-raw-file/README.md +++ b/minimal-examples/raw/minimal-raw-file/README.md @@ -1,13 +1,13 @@ # lws minimal ws server This demonstrates adopting a file descriptor into the lws event -loop. The filepath is given as an argument to the example app, eg +loop. The filepath to open and adopt is given as an argument to the example app, eg ``` $ ./lws-minimal-raw-file ``` -On a Linux system, some example files might be +On a Linux system, some example files for testing might be - /proc/self/fd/0 (stdin) - /dev/ttyUSB0 (a USB <-> serial converter) @@ -21,7 +21,7 @@ This isn't very useful standalone as shown here for clarity, but you can freely combine raw file descriptor adoption with other lws server and client features. -Becuase raw file events have their own callback, the handlers can +Becuase raw file events have their own callback reasons, the handlers can be integrated in a single protocol that also handles http and ws server and client callbacks without conflict. diff --git a/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt b/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt new file mode 100644 index 00000000..db4810b9 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-vhost/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-vhost) +set(SRCS minimal-raw-vhost.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/raw/minimal-raw-vhost/README.md b/minimal-examples/raw/minimal-raw-vhost/README.md new file mode 100644 index 00000000..d53b59e2 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-vhost/README.md @@ -0,0 +1,40 @@ +# lws minimal ws server raw vhost + +This demonstrates setting up a vhost to listen and accept raw sockets. +Raw sockets are just sockets... lws does not send anything on them or +interpret by itself what it receives on them. So you can implement +arbitrary tcp protocols using them. + +This isn't very useful standalone as shown here for clarity, but you can +freely combine a raw socket vhost with other lws server +and client features and other vhosts handling http or ws. + +Becuase raw socket events have their own callback reasons, the handlers can +be integrated in a single protocol that also handles http and ws +server and client callbacks without conflict. + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-raw-vhost +[2018/03/22 14:49:47:9516] USER: LWS minimal raw vhost +[2018/03/22 14:49:47:9673] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +[2018/03/22 14:49:52:3789] USER: LWS_CALLBACK_RAW_ADOPT +[2018/03/22 14:49:57:4271] USER: LWS_CALLBACK_RAW_CLOSE +``` + +``` + $ nc localhost 7681 +hello +hello +``` + +Connect one or more sessions to the server using netcat... lines you type +into netcat are sent to the server, which echos them to all connected clients. + diff --git a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c new file mode 100644 index 00000000..8c1b2ff7 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c @@ -0,0 +1,148 @@ +/* + * lws-minimal-raw-vhost + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates integrating a raw tcp listener into the lws event loop. + * + * This demo doesn't have any http or ws support. You can connect to it + * using netcat. If you make multiple connections to it, things typed in one + * netcat session are broadcast to all netcat connections. + * + * $ nc localhost 7681 + * + * You can add more vhosts with things like http or ws support, it's as it is + * for clarity. + * + * The main point is the apis and ways of managing raw sockets are almost + * identical to http or ws mode sockets in lws. The callback names for raw + * wsi are changed to be specific to RAW mode is all. + */ + +#include +#include +#include +#include +#include +#include + +struct raw_pss { + struct raw_pss *pss_list; + struct lws *wsi; +}; + +/* one of these is created for each vhost our protocol is used with */ + +struct raw_vhd { + struct raw_pss *pss_list; /* linked-list of live pss*/ + + int len; + uint8_t buf[4096]; +}; + +static int +callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct raw_pss *pss = (struct raw_pss *)user; + struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( + lws_get_vhost(wsi), lws_get_protocol(wsi)); + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), sizeof(struct raw_vhd)); + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + break; + + /* callbacks related to raw socket descriptor */ + + case LWS_CALLBACK_RAW_ADOPT: + lwsl_user("LWS_CALLBACK_RAW_ADOPT\n"); + pss->wsi = wsi; + lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); + break; + + case LWS_CALLBACK_RAW_CLOSE: + lwsl_user("LWS_CALLBACK_RAW_CLOSE\n"); + lws_ll_fwd_remove(struct raw_pss, pss_list, pss, vhd->pss_list); + break; + + case LWS_CALLBACK_RAW_RX: + vhd->len = len; + if (vhd->len > (int)sizeof(vhd->buf)) + vhd->len = sizeof(vhd->buf); + memcpy(vhd->buf, in, vhd->len); + lws_start_foreach_llp(struct raw_pss **, ppss, vhd->pss_list) { + lws_callback_on_writable((*ppss)->wsi); + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + if (lws_write(wsi, vhd->buf, vhd->len, LWS_WRITE_RAW) != + vhd->len) { + lwsl_notice("%s: raw write failed\n", __func__); + return 1; + } + break; + + default: + break; + } + + return 0; +} + +static struct lws_protocols protocols[] = { + { "raw-test", callback_raw_test, sizeof(struct raw_pss), 0 }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + signal(SIGINT, sigint_handler); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.protocols = protocols; + info.options = LWS_SERVER_OPTION_ONLY_RAW; /* vhost accepts RAW */ + + lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */, NULL); + + lwsl_user("LWS minimal raw vhost\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c index bc161a87..26f20c2f 100644 --- a/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c @@ -83,21 +83,15 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_ESTABLISHED: /* add ourselves to the list of live pss held in the vhd */ - pss->pss_list = vhd->pss_list; - vhd->pss_list = pss; + lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); pss->wsi = wsi; pss->last = vhd->current; break; case LWS_CALLBACK_CLOSED: /* remove our closing pss from the list of live pss */ - lws_start_foreach_llp(struct per_session_data__minimal **, - ppss, vhd->pss_list) { - if (*ppss == pss) { - *ppss = pss->pss_list; - break; - } - } lws_end_foreach_llp(ppss, pss_list); + lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, + pss, vhd->pss_list); break; case LWS_CALLBACK_SERVER_WRITEABLE: