diff --git a/CMakeLists.txt b/CMakeLists.txt
index e9e1a4478..e44fee848 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,7 @@ option(LWS_FOR_GITOHASHI "Enable features recommended for use with gitohashi" OF
option(LWS_ROLE_H1 "Compile with support for http/1 (needed for ws)" ON)
option(LWS_ROLE_WS "Compile with support for websockets" ON)
option(LWS_ROLE_DBUS "Compile with support for DBUS" OFF)
+option(LWS_ROLE_RAW_PROXY "Raw packet proxy" OFF)
option(LWS_WITH_HTTP2 "Compile with server support for HTTP/2" ON)
option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
@@ -154,6 +155,7 @@ if(LWS_WITH_DISTRO_RECOMMENDED)
set(LWS_WITH_LWSAC 1)
set(LWS_WITH_LEJP_CONF 1)
set(LWS_WITH_PLUGINS 1)
+ set(LWS_ROLE_RAW_PROXY 1)
endif()
# do you care about this? Then send me a patch where it disables it on travis
@@ -279,6 +281,12 @@ if (LWS_WITH_LWSWS)
set(LWS_WITH_LEJP 1)
set(LWS_WITH_LEJP_CONF 1)
set(LWS_WITH_PEER_LIMITS 1)
+ set(LWS_ROLE_RAW_PROXY 1)
+endif()
+
+if (LWS_ROLE_RAW_PROXY)
+ set (LWS_WITHOUT_CLIENT 0)
+ set (LWS_WITHOUT_SERVER 0)
endif()
if (LWS_WITH_ACME)
@@ -896,6 +904,11 @@ if (LWS_ROLE_RAW)
lib/roles/raw-file/ops-raw-file.c)
endif()
+if (LWS_ROLE_RAW_PROXY)
+ list(APPEND SOURCES
+ lib/roles/raw-proxy/ops-raw-proxy.c)
+endif()
+
if (LWS_ROLE_CGI)
list(APPEND SOURCES
lib/roles/cgi/cgi-server.c
@@ -2016,6 +2029,11 @@ endif()
create_plugin(protocol_post_demo ""
"plugins/protocol_post_demo.c" "" "")
+if (LWS_ROLE_RAW_PROXY)
+ create_plugin(protocol_lws_raw_proxy ""
+ "plugins/raw-proxy/protocol_lws_raw_proxy.c" "" "")
+endif()
+
if (LWS_WITH_FTS)
create_plugin(protocol_fulltext_demo ""
"plugins/protocol_fulltext_demo.c" "" "")
diff --git a/README.md b/README.md
index 85a0846a8..2ae78a8d7 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,30 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to
News
----
+## New features on master
+
+ - **http fallback support** - you can specify a role and protocol to apply if non-http or non-tls
+ packets arrive at an http(s) listen port. For example, you can specify that the new `raw proxy`
+ role + protocol should be used, to proxy your sshd port over :443 or :80. Without affecting
+ normal http(s) serving on those ports but allowing, eg, `ssh -p 443 invalid@libwebsockets.org`.
+ [http fallback docs](https://libwebsockets.org/git/libwebsockets/tree/READMEs/README.http-fallback.md)
+
+ - **raw tcp proxy role and protocol** - adding raw tcp proxying is now trivial using the built-in lws
+ implementation. You can control the onward connection using a pvo in the format "ipv4:server.com:port"
+ [raw proxy minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/raw/minimal-raw-proxy),
+ [raw proxy docs](https://libwebsockets.org/git/libwebsockets/tree/plugins/raw-proxy),
+ Cmake config: `-DLWS_ROLE_RAW_PROXY=1 -DLWS_WITH_PLUGINS=1`
+
+ - **deaddrop HTML file upload protocol** - protocol and minimal example for file upload and sharing using
+ drag and drop and a file picker. Integrated with basic auth, uploaded files marked with upload user,
+ and files owned by the authenticated user may be deleted via the UI. Supports multiple simultaneous
+ uploads both by drag-and-drop and from the file picker.
+ [deaddrop minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/http-server/minimal-http-server-deaddrop)
+
+ - **basic auth for ws(s)** - You can apply basic auth credential requirement to ws connections same
+ as on mounts now. Just add a pvo "basic-auth" with the value being the credentials file path when
+ enabling the ws protocol for the vhost.
+
## v3.1 released: new features in v3.1
- **lws threadpool** - lightweight pool of pthreads integrated to lws wsi, with all
diff --git a/READMEs/README.http-fallback.md b/READMEs/README.http-fallback.md
new file mode 100644
index 000000000..120b00f05
--- /dev/null
+++ b/READMEs/README.http-fallback.md
@@ -0,0 +1,172 @@
+# Http fallback and raw proxying
+
+Lws has several interesting options and features that can be applied to get
+some special behaviours... this article discusses them and how they work.
+
+## Overview of normal vhost selection
+
+Lws supports multiple http or https vhosts sharing a listening socket on the
+same port.
+
+For unencrypted http, the Host: header is used to select which vhost the
+connection should bind to, by comparing what is given there against the
+names the server was configured with for the various vhosts. If no match, it
+selects the first configured vhost.
+
+For TLS, it has an extension called SNI (Server Name Indication) which tells
+the server early in the TLS handshake the host name the connection is aimed at.
+That allows lws to select the vhost early, and use vhost-specific TLS certs
+so everything is happy. Again, if there is no match the connection proceeds
+using the first configured vhost and its certs.
+
+## Http(s) fallback options
+
+What happens if you try to connect, eg, an ssh client to the http server port
+(this is not an idle question...)? Obviously the http server part or the tls
+part of lws will fail the connection and close it. (We will look at that flow
+in a moment in detail for both unencrypted and tls listeners.)
+
+However if the first configured vhost for the port was created with the
+vhost creation info struct `.options` flag `LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`,
+then instead of the error, the connection transitions to whatever role was
+given in the vhost creation info struct `.listen_accept_role` and `.listen_accept_protocol`.
+
+With lejp-conf / lwsws, the options can be applied to the first vhost using:
+
+```
+ "listen-accept-role": "the-role-name",
+ "listen-accept-protocol": "the-protocol-name",
+ "fallback-listen-accept": "1"
+```
+
+See `./minimal-examples/raw/minimal-raw-fallback-http-server` for examples of
+all the options in use via commandline flags.
+
+So long as the first packet for the protocol doesn't look like GET, POST, or
+a valid tls packet if connection to an https vhost, this allows the one listen
+socket to handle both http(s) and a second protocol, as we will see, like ssh.
+
+Notice there is a restriction that no vhost selection processing is possible,
+neither for tls listeners nor plain http ones... the packet belonging to a
+different protocol will not send any Host: header nor tls SNI.
+
+Therefore although the flags and settings are applied to the first configured
+vhost, actually their effect is global for a given listen port. If enabled,
+all vhosts on the same listen port will do the fallback action.
+
+### Plain http flow
+
+
+
+Normally, if the first received packet does not contain a valid HTTP method,
+then the connection is dropped. Which is what you want from an http server.
+
+However if enabled, the connection can transition to the defined secondary
+role / protocol.
+
+|Flag|lejp-conf / lwsws|Function|
+|---|---|---|
+|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing|
+
+### TLS https flow
+
+
+
+If the port is listening with tls, the point that a packet from a different
+protocol will fail is earlier, when the tls tunnel is being set up.
+
+|Flag|lejp-conf / lwsws|Function|
+|---|---|---|
+|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing|
+|`LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`|`"redirect-http": "1"`|Treat invalid tls packet as http, issue http redirect to https://|
+|`LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`|`"allow-http-on-https": "1"`|Accept unencrypted http connections on this tls port (dangerous)|
+
+The latter two options are higher priority than, and defeat, the first one.
+
+### Non-http listener
+
+
+
+It's also possible to skip the fallback processing and just force the first
+vhost on the port to use the specified role and protocol in the first place.
+
+|Flag|lejp-conf / lwsws|Function|
+|---|---|---|
+|LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG|`"apply-listen-accept": "1"`|Force vhost to use listen-accept-role / listen-accept-protocol|
+
+## Using http(s) fallback with raw-proxy
+
+If enabled for build with `cmake .. -DLWS_ROLE_RAW_PROXY=1 -DLWS_WITH_PLUGINS=1`
+then lws includes ready-to-use support for raw tcp proxying.
+
+This can be used standalone on the first vhost on a port, but most intriguingly
+it can be specified as the fallback for http(s)...
+
+See `./minimal-examples/raw/minimal-raw-proxy-fallback.c` for a working example.
+
+### fallback with raw-proxy in code
+
+On the first vhost for the port, specify the required "onward" pvo to configure
+the raw-proxy protocol...you can adjust the "ipv4:127.0.0.1:22" to whatever you
+want...
+
+```
+ static struct lws_protocol_vhost_options pvo1 = {
+ NULL,
+ NULL,
+ "onward", /* pvo name */
+ "ipv4:127.0.0.1:22" /* pvo value */
+ };
+
+ static const struct lws_protocol_vhost_options pvo = {
+ NULL, /* "next" pvo linked-list */
+ &pvo1, /* "child" pvo linked-list */
+ "raw-proxy", /* protocol name we belong to on this vhost */
+ "" /* ignored */
+ };
+```
+
+... and set up the fallback enable and bindings...
+
+```
+ info.options |= LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG;
+ info.listen_accept_role = "raw_proxy";
+ info.listen_accept_proxy = "raw_proxy";
+ info.pvo = &pvo;
+```
+
+### fallback with raw-proxy in JSON conf
+
+On the first vhost for the port, enable the raw-proxy protocol on the vhost and
+set the pvo config
+
+```
+ "ws-protocols": [{
+ "raw-proxy": {
+ "status": "ok",
+ "onward": "ipv4:127.0.0.1:22"
+ }
+ }],
+```
+
+Enable the fallback behaviour on the vhost and the role / protocol binding
+
+```
+ "listen-accept-role": "raw-proxy",
+ "listen-accept-protocol": "raw-proxy",
+ "fallback-listen-accept": "1"
+```
+
+### Testing
+
+With this configured, the listen port will function normally for http or https
+depending on how it was set up.
+
+But if you try to connect to it with an ssh client, that will also work fine.
+
+The libwebsockets.org server is set up in this way, you can confirm it by
+visiting `https://libwebsockets.org` on port 443 as usual, but also trying
+`ssh -p 443 invalid@libwebsockets.org`... you will get permission denied from
+your ssh client. With valid credentials in fact that works perfectly for
+ssh, scp, git-over-ssh etc all on port 443...
+
diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in
index 045be0e65..41c06afcf 100644
--- a/cmake/lws_config.h.in
+++ b/cmake/lws_config.h.in
@@ -7,186 +7,101 @@
#endif
#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share"
-
-#cmakedefine LWS_ROLE_H1
-#cmakedefine LWS_ROLE_WS
-#cmakedefine LWS_ROLE_RAW
-#cmakedefine LWS_ROLE_H2
-#cmakedefine LWS_ROLE_CGI
-#cmakedefine LWS_ROLE_DBUS
-
-#cmakedefine LWS_WITH_LWSAC
-#cmakedefine LWS_WITH_FTS
-
-/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
- * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
-#cmakedefine USE_WOLFSSL
-
-/* Also define to 1 (in addition to USE_WOLFSSL) when using the
- (older) CyaSSL library */
-#cmakedefine USE_OLD_CYASSL
-#cmakedefine LWS_WITH_BORINGSSL
-
-#cmakedefine LWS_WITH_MBEDTLS
-#cmakedefine LWS_WITH_POLARSSL
-#cmakedefine LWS_WITH_ESP32
-
-#cmakedefine LWS_WITH_PLUGINS
-#cmakedefine LWS_WITH_NO_LOGS
-
-/* The Libwebsocket version */
-#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}"
-
#define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR}
#define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR}
#define LWS_LIBRARY_VERSION_PATCH ${LWS_LIBRARY_VERSION_PATCH}
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
-#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
-
-/* The current git commit hash that we're building from */
-#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
-
-/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
-#cmakedefine LWS_OPENSSL_SUPPORT
-#cmakedefine LWS_WITH_TLS
-
-/* The client should load and trust CA root certs it finds in the OS */
-#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS
-
-/* Sets the path where the client certs should be installed. */
-#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}"
-
-/* Turn off websocket extensions */
-#cmakedefine LWS_WITHOUT_EXTENSIONS
-
-/* notice if client or server gone */
-#cmakedefine LWS_WITHOUT_SERVER
-#cmakedefine LWS_WITHOUT_CLIENT
-
-#cmakedefine LWS_WITH_POLL
-
-/* Enable libev io loop */
-#cmakedefine LWS_WITH_LIBEV
-
-/* Enable libuv io loop */
-#cmakedefine LWS_WITH_LIBUV
-
-/* Enable libevent io loop */
-#cmakedefine LWS_WITH_LIBEVENT
-
-/* Build with support for ipv6 */
-#cmakedefine LWS_WITH_IPV6
-
-/* Build with support for UNIX domain socket */
-#cmakedefine LWS_WITH_UNIX_SOCK
-
-/* Build with support for HTTP2 */
-#cmakedefine LWS_WITH_HTTP2
-
-/* Turn on latency measuring code */
-#cmakedefine LWS_LATENCY
-
-/* Don't build the daemonizeation api */
-#cmakedefine LWS_NO_DAEMONIZE
-
-/* Build without server support */
-#cmakedefine LWS_NO_SERVER
-
-/* Build without client support */
-#cmakedefine LWS_NO_CLIENT
-
-/* If we should compile with MinGW support */
-#cmakedefine LWS_MINGW_SUPPORT
-
-/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */
-#cmakedefine LWS_BUILTIN_GETIFADDRS
-
-/* use SHA1() not internal libwebsockets_SHA1 */
-#cmakedefine LWS_SHA1_USE_OPENSSL_NAME
-
-/* SSL server using ECDH certificate */
-#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
-#cmakedefine LWS_HAVE_SSL_CTX_set1_param
-#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host
-#cmakedefine LWS_HAVE_RSA_SET0_KEY
-#cmakedefine LWS_HAVE_X509_get_key_usage
-#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate
-#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites
-
-#cmakedefine LWS_HAVE_UV_VERSION_H
-#cmakedefine LWS_HAVE_NEW_UV_VERSION_H
-#cmakedefine LWS_HAVE_PTHREAD_H
-
-/* CGI apis */
-#cmakedefine LWS_WITH_CGI
-
-/* whether the Openssl is recent enough, and / or built with, ecdh */
-#cmakedefine LWS_HAVE_OPENSSL_ECDH_H
-
-/* HTTP Proxy support */
-#cmakedefine LWS_WITH_HTTP_PROXY
-
-/* HTTP Ranges support */
-#cmakedefine LWS_WITH_RANGES
-
-/* Http access log support */
-#cmakedefine LWS_WITH_ACCESS_LOG
-#cmakedefine LWS_WITH_SERVER_STATUS
-
-#cmakedefine LWS_WITH_STATEFUL_URLDECODE
-#cmakedefine LWS_WITH_PEER_LIMITS
-
-/* Maximum supported service threads */
+#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR * 1000000) + \
+ (LWS_LIBRARY_VERSION_MINOR * 1000) + \
+ LWS_LIBRARY_VERSION_PATCH
#define LWS_MAX_SMP ${LWS_MAX_SMP}
-/* Lightweight JSON Parser */
-#cmakedefine LWS_WITH_LEJP
-
-/* SMTP */
-#cmakedefine LWS_WITH_SMTP
-
-/* OPTEE */
-#cmakedefine LWS_PLAT_OPTEE
-
-/* ZIP FOPS */
-#cmakedefine LWS_WITH_ZIP_FOPS
-#cmakedefine LWS_HAVE_STDINT_H
-
#cmakedefine LWS_AVOID_SIGPIPE_IGN
-
+#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
+#cmakedefine LWS_BUILTIN_GETIFADDRS
#cmakedefine LWS_FALLBACK_GETHOSTBYNAME
-
-#cmakedefine LWS_WITH_STATS
-#cmakedefine LWS_WITH_SOCKS5
-
-#cmakedefine LWS_HAVE_SYS_CAPABILITY_H
-#cmakedefine LWS_HAVE_LIBCAP
-
-#cmakedefine LWS_HAVE_ATOLL
+#cmakedefine LWS_HAS_INTPTR_T
#cmakedefine LWS_HAVE__ATOI64
-#cmakedefine LWS_HAVE__STAT32I64
-
-#cmakedefine LWS_WITH_JWS
-#cmakedefine LWS_WITH_ACME
-#cmakedefine LWS_WITH_SELFTESTS
-
+#cmakedefine LWS_HAVE_ATOLL
+#cmakedefine LWS_HAVE_LIBCAP
#cmakedefine LWS_HAVE_MALLOC_H
-
+#cmakedefine LWS_HAVE_NEW_UV_VERSION_H
+#cmakedefine LWS_HAVE_OPENSSL_ECDH_H
#cmakedefine LWS_HAVE_PIPE2
-
-/* OpenSSL various APIs */
-
-#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
-#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
-#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
+#cmakedefine LWS_HAVE_PTHREAD_H
+#cmakedefine LWS_HAVE_RSA_SET0_KEY
+#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate
+#cmakedefine LWS_HAVE_SSL_CTX_set1_param
+#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites
#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
#cmakedefine LWS_HAVE_SSL_get0_alpn_selected
#cmakedefine LWS_HAVE_SSL_set_alpn_protos
-
-#cmakedefine LWS_HAS_INTPTR_T
-
-#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION
+#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
+#cmakedefine LWS_HAVE__STAT32I64
+#cmakedefine LWS_HAVE_STDINT_H
+#cmakedefine LWS_HAVE_SYS_CAPABILITY_H
+#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
+#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
+#cmakedefine LWS_HAVE_UV_VERSION_H
+#cmakedefine LWS_HAVE_X509_get_key_usage
+#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host
+#cmakedefine LWS_LATENCY
+#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}"
+#cmakedefine LWS_MINGW_SUPPORT
+#cmakedefine LWS_NO_CLIENT
+#cmakedefine LWS_NO_DAEMONIZE
+#cmakedefine LWS_NO_SERVER
+#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}"
+#cmakedefine LWS_OPENSSL_SUPPORT
+#cmakedefine LWS_PLAT_OPTEE
+#cmakedefine LWS_ROLE_CGI
+#cmakedefine LWS_ROLE_DBUS
+#cmakedefine LWS_ROLE_H1
+#cmakedefine LWS_ROLE_H2
+#cmakedefine LWS_ROLE_RAW
+#cmakedefine LWS_ROLE_RAW_PROXY
+#cmakedefine LWS_ROLE_WS
+#cmakedefine LWS_SHA1_USE_OPENSSL_NAME
+#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS
+#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
+#cmakedefine LWS_WITH_ACCESS_LOG
+#cmakedefine LWS_WITH_ACME
+#cmakedefine LWS_WITH_BORINGSSL
+#cmakedefine LWS_WITH_CGI
+#cmakedefine LWS_WITH_ESP32
+#cmakedefine LWS_WITH_FTS
+#cmakedefine LWS_WITH_HTTP2
#cmakedefine LWS_WITH_HTTP_BROTLI
+#cmakedefine LWS_WITH_HTTP_PROXY
+#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION
+#cmakedefine LWS_WITH_IPV6
+#cmakedefine LWS_WITH_JWS
+#cmakedefine LWS_WITH_LEJP
+#cmakedefine LWS_WITH_LIBEV
+#cmakedefine LWS_WITH_LIBEVENT
+#cmakedefine LWS_WITH_LIBUV
+#cmakedefine LWS_WITH_LWSAC
+#cmakedefine LWS_WITH_MBEDTLS
+#cmakedefine LWS_WITH_NO_LOGS
+#cmakedefine LWS_WITHOUT_CLIENT
+#cmakedefine LWS_WITHOUT_EXTENSIONS
+#cmakedefine LWS_WITHOUT_SERVER
+#cmakedefine LWS_WITH_PEER_LIMITS
+#cmakedefine LWS_WITH_PLUGINS
+#cmakedefine LWS_WITH_POLARSSL
+#cmakedefine LWS_WITH_POLL
+#cmakedefine LWS_WITH_RANGES
+#cmakedefine LWS_WITH_SELFTESTS
+#cmakedefine LWS_WITH_SERVER_STATUS
+#cmakedefine LWS_WITH_SMTP
+#cmakedefine LWS_WITH_SOCKS5
+#cmakedefine LWS_WITH_STATEFUL_URLDECODE
+#cmakedefine LWS_WITH_STATS
#cmakedefine LWS_WITH_THREADPOOL
+#cmakedefine LWS_WITH_TLS
+#cmakedefine LWS_WITH_UNIX_SOCK
+#cmakedefine LWS_WITH_ZIP_FOPS
+#cmakedefine USE_OLD_CYASSL
+#cmakedefine USE_WOLFSSL
${LWS_SIZEOFPTR_CODE}
diff --git a/doc-assets/accept-flow-1.svg b/doc-assets/accept-flow-1.svg
new file mode 100644
index 000000000..1c70ca1d2
--- /dev/null
+++ b/doc-assets/accept-flow-1.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/doc-assets/accept-flow-2.svg b/doc-assets/accept-flow-2.svg
new file mode 100644
index 000000000..1a47cf94c
--- /dev/null
+++ b/doc-assets/accept-flow-2.svg
@@ -0,0 +1,71 @@
+
+
diff --git a/doc-assets/accept-flow-3.svg b/doc-assets/accept-flow-3.svg
new file mode 100644
index 000000000..ea51c3c9c
--- /dev/null
+++ b/doc-assets/accept-flow-3.svg
@@ -0,0 +1,23 @@
+
+
diff --git a/include/libwebsockets/lws-adopt.h b/include/libwebsockets/lws-adopt.h
index 256e3f9fd..4686098bb 100644
--- a/include/libwebsockets/lws-adopt.h
+++ b/include/libwebsockets/lws-adopt.h
@@ -68,6 +68,7 @@ typedef enum {
LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */
LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */
LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
+ LWS_ADOPT_FLAG_RAW_PROXY = 32, /* flag: raw proxy */
LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
} lws_adoption_type;
diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h
index 8b49218e9..4dc20a8a1 100644
--- a/include/libwebsockets/lws-callbacks.h
+++ b/include/libwebsockets/lws-callbacks.h
@@ -682,6 +682,42 @@ enum lws_callback_reasons {
* options.
*/
+ /* ---------------------------------------------------------------------
+ * ----- Callbacks related to RAW PROXY -----
+ */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_RX = 89,
+ /**< RAW mode client (outgoing) RX */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_RX = 90,
+ /**< RAW mode server (listening) RX */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_CLOSE = 91,
+ /**< RAW mode client (outgoing) is closing */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_CLOSE = 92,
+ /**< RAW mode server (listening) is closing */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE = 93,
+ /**< RAW mode client (outgoing) may be written */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE = 94,
+ /**< RAW mode server (listening) may be written */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_ADOPT = 95,
+ /**< RAW mode client (onward) accepted socket was adopted
+ * (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_PROXY_SRV_ADOPT = 96,
+ /**< RAW mode server (listening) accepted socket was adopted
+ * (equivalent to 'wsi created') */
+
+ LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL = 97,
+ LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL = 98,
+ LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL = 99,
+ LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL = 100,
+
+
/* ---------------------------------------------------------------------
* ----- Callbacks related to RAW sockets -----
*/
diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h
index 991eb9694..4c095ea15 100644
--- a/include/libwebsockets/lws-context-vhost.h
+++ b/include/libwebsockets/lws-context-vhost.h
@@ -583,7 +583,6 @@ struct lws_context_creation_info {
* "TLS_CHACHA20_POLY1305_SHA256") or you can leave it as NULL to get
* "DEFAULT".
*/
-
const char *listen_accept_role;
/**< VHOST: NULL for default, or force accepted incoming connections to
* bind to this role. Uses the role names from their ops struct, eg,
diff --git a/include/libwebsockets/lws-write.h b/include/libwebsockets/lws-write.h
index 6e0be6b34..f6e464da9 100644
--- a/include/libwebsockets/lws-write.h
+++ b/include/libwebsockets/lws-write.h
@@ -257,7 +257,7 @@ lws_write_ws_flags(int initial, int is_start, int is_end)
* `return lws_raw_transaction_completed(wsi)` should better be used than
* return -1.
*/
-LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_raw_transaction_completed(struct lws *wsi);
///@}
diff --git a/lib/core/adopt.c b/lib/core/adopt.c
index 49ac5af16..ac37063d9 100644
--- a/lib/core/adopt.c
+++ b/lib/core/adopt.c
@@ -185,12 +185,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
* itself by default protocols[0]
*/
n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
- if (!(type & LWS_ADOPT_HTTP)) {
- if (!(type & LWS_ADOPT_SOCKET))
- n = LWS_CALLBACK_RAW_ADOPT_FILE;
- else
- n = LWS_CALLBACK_RAW_ADOPT;
- }
+ if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)])
+ n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)];
lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
diff --git a/lib/core/connect.c b/lib/core/connect.c
index 50111f8ad..90137119f 100644
--- a/lib/core/connect.c
+++ b/lib/core/connect.c
@@ -271,6 +271,9 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
i->uri_replace_to);
#endif
+ if (i->method && !strcmp(i->method, "RAW"))
+ lws_http_client_connect_via_info2(wsi);
+
return wsi;
bail1:
diff --git a/lib/core/context.c b/lib/core/context.c
index 05db439fd..9749447a6 100644
--- a/lib/core/context.c
+++ b/lib/core/context.c
@@ -37,6 +37,9 @@ const struct lws_role_ops *available_roles[] = {
#endif
#if defined(LWS_ROLE_DBUS)
&role_ops_dbus,
+#endif
+#if defined(LWS_ROLE_RAW_PROXY)
+ &role_ops_raw_proxy,
#endif
NULL
};
@@ -110,6 +113,8 @@ lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn)
int
lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)
{
+ int n;
+
/*
* if the vhost is told to bind accepted sockets to a given role,
* then look it up by name and try to bind to the specific role.
@@ -120,11 +125,31 @@ lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot)
const struct lws_role_ops *role =
lws_role_by_name(wsi->vhost->listen_accept_role);
- if (role && role->adoption_bind(wsi, type, prot))
- return 0;
+ if (!prot)
+ prot = wsi->vhost->listen_accept_protocol;
- lwsl_warn("%s: adoption bind to role %s failed", __func__,
- wsi->vhost->listen_accept_role);
+ if (!role)
+ lwsl_err("%s: can't find role '%s'\n", __func__,
+ wsi->vhost->listen_accept_role);
+
+ if (role && role->adoption_bind) {
+ n = role->adoption_bind(wsi, type, prot);
+ if (n < 0)
+ return -1;
+ if (n) /* did the bind */
+ return 0;
+ }
+
+ if (type & _LWS_ADOPT_FINISH) {
+ lwsl_debug("%s: leaving bound to role %s\n", __func__,
+ wsi->role_ops->name);
+ return 0;
+ }
+
+
+ lwsl_warn("%s: adoption bind to role '%s', "
+ "protocol '%s', type 0x%x, failed\n", __func__,
+ wsi->vhost->listen_accept_role, prot, type);
}
/*
diff --git a/lib/core/service.c b/lib/core/service.c
index ef251409c..c8295de61 100644
--- a/lib/core/service.c
+++ b/lib/core/service.c
@@ -56,7 +56,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
volatile struct lws *vwsi = (volatile struct lws *)wsi;
int n;
- //lwsl_notice("%s: %p\n", __func__, wsi);
+ // lwsl_notice("%s: %p\n", __func__, wsi);
vwsi->leave_pollout_active = 0;
vwsi->handling_pollout = 1;
@@ -177,11 +177,9 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
vwsi->leave_pollout_active = 0;
}
- if (lwsi_role_client(wsi) &&
- !wsi->hdr_parsing_completed &&
+ if (lwsi_role_client(wsi) && !wsi->hdr_parsing_completed &&
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS &&
- lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY
- )
+ lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY)
goto bail_ok;
diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c
index b85a0895b..4880e2193 100644
--- a/lib/roles/cgi/ops-cgi.c
+++ b/lib/roles/cgi/ops-cgi.c
@@ -114,6 +114,8 @@ struct lws_role_ops role_ops_cgi = {
/* destroy_role */ rops_destroy_role_cgi,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { 0, 0 },
+ /* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
diff --git a/lib/roles/dbus/dbus.c b/lib/roles/dbus/dbus.c
index d509cfd34..c3fdd3ba4 100644
--- a/lib/roles/dbus/dbus.c
+++ b/lib/roles/dbus/dbus.c
@@ -519,6 +519,8 @@ struct lws_role_ops role_ops_dbus = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { 0, 0 },
+ /* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 2fa0fe16e..a90e89a10 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -651,7 +651,8 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
return LWS_HPI_RET_HANDLED;
}
-int rops_handle_POLLOUT_h1(struct lws *wsi)
+static int
+rops_handle_POLLOUT_h1(struct lws *wsi)
{
if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
return LWS_HP_RET_USER_SERVICE;
@@ -791,6 +792,8 @@ rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name)
if (!(type & LWS_ADOPT_HTTP))
return 0; /* no match */
+ if (type & _LWS_ADOPT_FINISH && !lwsi_role_http(wsi))
+ return 0;
if (type & _LWS_ADOPT_FINISH) {
if (!lws_header_table_attach(wsi, 0))
@@ -1028,6 +1031,10 @@ struct lws_role_ops role_ops_h1 = {
#else
NULL,
#endif
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
+ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
+ /* rx_cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
+ 0 /* may be POST, etc */ },
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
LWS_CALLBACK_HTTP_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index 9f23f9e35..573eb83fc 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -1197,6 +1197,10 @@ struct lws_role_ops role_ops_h2 = {
/* destroy_role */ rops_destroy_role_h2,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
+ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
+ /* rx cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
+ 0 /* may be POST, etc */ },
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
LWS_CALLBACK_HTTP_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index 88cbb7862..ee7bfd0e7 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -44,7 +44,7 @@ lws_client_connect_2(struct lws *wsi)
const char *ads;
sockaddr46 sa46;
const struct sockaddr *psa;
- int n, port = 0;
+ int n, m, port = 0, rawish = 0;
const char *cce = "", *iface;
const char *meth = NULL;
#ifdef LWS_WITH_IPV6
@@ -57,10 +57,8 @@ lws_client_connect_2(struct lws *wsi)
#endif
#endif
- lwsl_client("%s: %p\n", __func__, wsi);
-
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
- if (!wsi->http.ah) {
+ if (!wsi->http.ah && !wsi->stash) {
cce = "ah was NULL at cc2";
lwsl_err("%s\n", cce);
goto oom4;
@@ -68,7 +66,14 @@ lws_client_connect_2(struct lws *wsi)
/* we can only piggyback GET or POST */
- meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+ if (wsi->stash)
+ meth = wsi->stash->method;
+ else
+ meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD);
+
+ if (meth && !strcmp(meth, "RAW"))
+ rawish = 1;
+
if (meth && strcmp(meth, "GET") && strcmp(meth, "POST"))
goto create_new_conn;
@@ -165,10 +170,15 @@ create_new_conn:
* want to use it too
*/
- if (!wsi->client_hostname_copy)
- wsi->client_hostname_copy =
- lws_strdup(lws_hdr_simple_ptr(wsi,
+ if (!wsi->client_hostname_copy) {
+ if (wsi->stash)
+ wsi->client_hostname_copy = lws_strdup(
+ wsi->stash->host);
+ else
+ wsi->client_hostname_copy =
+ lws_strdup(lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS));
+ }
/*
* If we made our own connection, and we're doing a method that can take
@@ -192,7 +202,10 @@ create_new_conn:
* unix socket destination?
*/
- ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+ if (wsi->stash)
+ ads = wsi->stash->address;
+ else
+ ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#if defined(LWS_WITH_UNIX_SOCK)
if (*ads == '+') {
ads++;
@@ -226,8 +239,7 @@ create_new_conn:
plen = sprintf((char *)pt->serv_buf,
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a",
- lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
- wsi->c_port);
+ ads, wsi->c_port);
if (wsi->vhost->proxy_basic_auth_token[0])
plen += sprintf((char *)pt->serv_buf + plen,
@@ -255,7 +267,7 @@ create_new_conn:
/* Priority 3: Connect directly */
- ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
+ /* ads already set */
port = wsi->c_port;
}
@@ -264,7 +276,7 @@ create_new_conn:
* to whatever we decided to connect to
*/
- lwsl_info("%s: %p: address %s\n", __func__, wsi, ads);
+ lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port);
n = lws_getaddrinfo46(wsi, ads, &result);
@@ -448,7 +460,10 @@ ads_known:
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
AWAITING_TIMEOUT);
- iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
+ if (wsi->stash)
+ iface = wsi->stash->iface;
+ else
+ iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE);
if (iface) {
n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0,
@@ -532,7 +547,15 @@ ads_known:
* (will overwrite existing pointer,
* leaving old string/frag there but unreferenced)
*/
- if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
+ if (wsi->stash) {
+ lws_free(wsi->stash->address);
+ wsi->stash->address =
+ lws_strdup(wsi->vhost->http.http_proxy_address);
+ if (!wsi->stash->address)
+ goto failed;
+ } else
+ if (lws_hdr_simple_create(wsi,
+ _WSI_TOKEN_CLIENT_PEER_ADDRESS,
wsi->vhost->http.http_proxy_address))
goto failed;
wsi->c_port = wsi->vhost->http.http_proxy_port;
@@ -564,7 +587,7 @@ ads_known:
goto failed;
}
- lws_set_timeout(wsi,
+ lws_out(wsi,
PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
AWAITING_TIMEOUT);
@@ -603,7 +626,33 @@ send_hs:
__func__, wsi);
/* we are making our own connection */
- lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
+ if (!rawish)
+ lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
+ else {
+ /* for a method = "RAW" connection, this makes us
+ * established */
+
+ /* clear his established timeout */
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ m = wsi->role_ops->adoption_cb[0];
+ if (m) {
+ n = user_callback_handle_rxflow(
+ wsi->protocol->callback, wsi,
+ m, wsi->user_space, NULL, 0);
+ if (n < 0) {
+ lwsl_info("LWS_CALLBACK_RAW_PROXY_CLI_ADOPT failed\n");
+ goto failed;
+ }
+ }
+
+ /* service.c pollout processing wants this */
+ wsi->hdr_parsing_completed = 1;
+
+ lwsi_set_state(wsi, LRS_ESTABLISHED);
+
+ return wsi;
+ }
/*
* provoke service to issue the handshake directly.
@@ -925,11 +974,16 @@ lws_http_client_connect_via_info2(struct lws *wsi)
{
struct client_info_stash *stash = wsi->stash;
+ lwsl_debug("%s: %p (stash %p)\n", __func__, wsi, stash);
+
if (!stash)
return wsi;
wsi->opaque_user_data = wsi->stash->opaque_user_data;
+ if (stash->method && !strcmp(stash->method, "RAW"))
+ goto no_ah;
+
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so into a temp
@@ -975,6 +1029,7 @@ lws_http_client_connect_via_info2(struct lws *wsi)
lws_client_stash_destroy(wsi);
#endif
+no_ah:
wsi->context->count_wsi_allocated++;
return lws_client_connect_2(wsi);
diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c
index 1d6f13dcf..f43b69f51 100644
--- a/lib/roles/http/client/client.c
+++ b/lib/roles/http/client/client.c
@@ -1055,7 +1055,8 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
wsi->user_space, NULL, 0))
return NULL;
- lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt);
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED,
+ &role_ops_raw_skt);
lws_header_table_detach(wsi, 1);
return NULL;
diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c
index b3e2cfa6f..bdb4a2e2f 100644
--- a/lib/roles/http/server/lejp-conf.c
+++ b/lib/roles/http/server/lejp-conf.c
@@ -817,6 +817,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG);
return 0;
case LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT:
+ lwsl_notice("vh %s: LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: %s\n",
+ a->info->vhost_name, ctx->buf);
set_reset_flag(&a->info->options, ctx->buf,
LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG);
return 0;
diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c
index e70052293..4c7db995d 100644
--- a/lib/roles/http/server/parsers.c
+++ b/lib/roles/http/server/parsers.c
@@ -106,7 +106,7 @@ __lws_header_table_reset(struct lws *wsi, int autoservice)
/* while we hold the ah, keep a timeout on the wsi */
__lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
- wsi->vhost->timeout_secs_ah_idle);
+ wsi->vhost->timeout_secs_ah_idle);
time(&ah->assigned);
@@ -197,6 +197,12 @@ lws_header_table_attach(struct lws *wsi, int autoservice)
(void *)wsi, (void *)wsi->http.ah, wsi->tsi,
pt->http.ah_count_in_use);
+ if (!lwsi_role_http(wsi)) {
+ lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name);
+ assert(0);
+ return -1;
+ }
+
lws_pt_lock(pt, __func__);
/* if we are already bound to one, just clear it down */
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index 1e7c80fc6..063491bd3 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -1654,6 +1654,7 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
const struct lws_role_ops *role = &role_ops_raw_skt;
const struct lws_protocols *p1, *protocol =
&wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
+ int n;
if (wsi->vhost->listen_accept_role &&
lws_role_by_name(wsi->vhost->listen_accept_role))
@@ -1666,21 +1667,28 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
protocol = p1;
}
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
lws_bind_protocol(wsi, protocol, __func__);
- lwsl_info("falling back on vh %s to protocol %s\n", wsi->vhost->name,
- protocol->name);
- if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT,
- wsi->user_space, NULL, 0))
+
+ lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
+
+ lws_header_table_detach(wsi, 0);
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
+ if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
+ n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
+
+ lwsl_notice("%s: vh %s, role %s, protocol %s, cb %d, ah %p\n",
+ __func__, wsi->vhost->name, role->name, protocol->name, n,
+ wsi->http.ah);
+
+ if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
return 1;
- lws_role_transition(wsi, 0, LRS_ESTABLISHED, role);
- lws_header_table_detach(wsi, 1);
-
- lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- if (wsi->protocol->callback(wsi, LWS_CALLBACK_RAW_RX, wsi->user_space,
- obuf, olen))
+ n = LWS_CALLBACK_RAW_RX;
+ if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)])
+ n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)];
+ if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen))
return 1;
return 0;
@@ -1734,8 +1742,12 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
*/
raw_transition:
- if (lws_http_to_fallback(wsi, obuf, olen))
+ if (lws_http_to_fallback(wsi, obuf, olen)) {
+ lwsl_info("%s: fallback -> close\n", __func__);
goto bail_nuke_ah;
+ }
+
+ (*buf) = obuf + olen;
return 0;
}
@@ -1970,7 +1982,6 @@ bail_nuke_ah:
return 1;
}
-
LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
lws_http_transaction_completed(struct lws *wsi)
{
diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c
index 77049e701..a1661cabb 100644
--- a/lib/roles/listen/ops-listen.c
+++ b/lib/roles/listen/ops-listen.c
@@ -181,6 +181,8 @@ struct lws_role_ops role_ops_listen = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { 0, 0 },
+ /* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c
index 659c9bd93..5563af157 100644
--- a/lib/roles/pipe/ops-pipe.c
+++ b/lib/roles/pipe/ops-pipe.c
@@ -89,6 +89,8 @@ struct lws_role_ops role_ops_pipe = {
/* destroy_role */ NULL,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { 0, 0 },
+ /* rx_cb clnt, srv */ { 0, 0 },
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* protocol_bind_cb c,s */ { 0, 0 },
diff --git a/lib/roles/private.h b/lib/roles/private.h
index 5f93b86e8..6eddf3af9 100644
--- a/lib/roles/private.h
+++ b/lib/roles/private.h
@@ -231,6 +231,16 @@ struct lws_role_ops {
int (*client_bind)(struct lws *wsi,
const struct lws_client_connect_info *i);
+ /*
+ * the callback reasons for adoption for client, server
+ * (just client applies if no concept of client or server)
+ */
+ uint16_t adoption_cb[2];
+ /*
+ * the callback reasons for adoption for client, server
+ * (just client applies if no concept of client or server)
+ */
+ uint16_t rx_cb[2];
/*
* the callback reasons for WRITEABLE for client, server
* (just client applies if no concept of client or server)
@@ -297,6 +307,12 @@ extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen,
#define lwsi_role_dbus(wsi) (0)
#endif
+#if defined(LWS_ROLE_RAW_PROXY)
+ #include "roles/raw-proxy/private.h"
+#else
+ #define lwsi_role_raw_proxy(wsi) (0)
+#endif
+
enum {
LWS_HP_RET_BAIL_OK,
LWS_HP_RET_BAIL_DIE,
diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c
index 075a2ee8b..b45422ca4 100644
--- a/lib/roles/raw-file/ops-raw-file.c
+++ b/lib/roles/raw-file/ops-raw-file.c
@@ -98,6 +98,10 @@ struct lws_role_ops role_ops_raw_file = {
NULL,
#endif
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT_FILE,
+ LWS_CALLBACK_RAW_ADOPT_FILE },
+ /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE,
+ LWS_CALLBACK_RAW_RX_FILE },
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 },
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 },
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL,
diff --git a/lib/roles/raw-proxy/ops-raw-proxy.c b/lib/roles/raw-proxy/ops-raw-proxy.c
new file mode 100644
index 000000000..0a75ef583
--- /dev/null
+++ b/lib/roles/raw-proxy/ops-raw-proxy.c
@@ -0,0 +1,217 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010-2018 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include
+
+static int
+rops_handle_POLLIN_raw_proxy(struct lws_context_per_thread *pt, struct lws *wsi,
+ struct lws_pollfd *pollfd)
+{
+ struct lws_tokens ebuf;
+ int n, buffered;
+
+ /* pending truncated sends have uber priority */
+
+ if (lws_has_buffered_out(wsi)) {
+ if (!(pollfd->revents & LWS_POLLOUT))
+ return LWS_HPI_RET_HANDLED;
+
+ /* drain the output buflist */
+ if (lws_issue_raw(wsi, NULL, 0) < 0)
+ goto fail;
+ /*
+ * we can't afford to allow input processing to send
+ * something new, so spin around he event loop until
+ * he doesn't have any partials
+ */
+ return LWS_HPI_RET_HANDLED;
+ }
+
+ if ((pollfd->revents & pollfd->events & LWS_POLLIN) &&
+ /* any tunnel has to have been established... */
+ lwsi_state(wsi) != LRS_SSL_ACK_PENDING &&
+ !(wsi->favoured_pollin &&
+ (pollfd->revents & pollfd->events & LWS_POLLOUT))) {
+
+ buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
+ switch (ebuf.len) {
+ case 0:
+ lwsl_info("%s: read 0 len\n", __func__);
+ wsi->seen_zero_length_recv = 1;
+ lws_change_pollfd(wsi, LWS_POLLIN, 0);
+
+ /*
+ * we need to go to fail here, since it's the only
+ * chance we get to understand that the socket has
+ * closed
+ */
+ // goto try_pollout;
+ goto fail;
+
+ case LWS_SSL_CAPABLE_ERROR:
+ goto fail;
+ case LWS_SSL_CAPABLE_MORE_SERVICE:
+ goto try_pollout;
+ }
+ n = user_callback_handle_rxflow(wsi->protocol->callback,
+ wsi, lwsi_role_client(wsi) ?
+ LWS_CALLBACK_RAW_PROXY_CLI_RX :
+ LWS_CALLBACK_RAW_PROXY_SRV_RX,
+ wsi->user_space, ebuf.token,
+ ebuf.len);
+ if (n < 0) {
+ lwsl_info("LWS_CALLBACK_RAW_PROXY_*_RX fail\n");
+ goto fail;
+ }
+
+ if (lws_buflist_aware_consume(wsi, &ebuf, ebuf.len, buffered))
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ } else
+ if (wsi->favoured_pollin &&
+ (pollfd->revents & pollfd->events & LWS_POLLOUT))
+ /* we balanced the last favouring of pollin */
+ wsi->favoured_pollin = 0;
+
+try_pollout:
+
+ if (!(pollfd->revents & LWS_POLLOUT))
+ return LWS_HPI_RET_HANDLED;
+
+ if (lws_handle_POLLOUT_event(wsi, pollfd)) {
+ lwsl_debug("POLLOUT event closed it\n");
+ return LWS_HPI_RET_PLEASE_CLOSE_ME;
+ }
+
+#if !defined(LWS_NO_CLIENT)
+ if (lws_client_socket_service(wsi, pollfd, NULL))
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+#endif
+
+ return LWS_HPI_RET_HANDLED;
+
+fail:
+ lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail");
+
+ return LWS_HPI_RET_WSI_ALREADY_DIED;
+}
+
+static int
+rops_adoption_bind_raw_proxy(struct lws *wsi, int type,
+ const char *vh_prot_name)
+{
+ /* no http but socket... must be raw skt */
+ if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) ||
+ !(type & LWS_ADOPT_FLAG_RAW_PROXY) || (type & _LWS_ADOPT_FINISH))
+ return 0; /* no match */
+
+ if (type & LWS_ADOPT_FLAG_UDP)
+ /*
+ * these can be >128 bytes, so just alloc for UDP
+ */
+ wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct");
+
+ lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ?
+ LRS_SSL_INIT : LRS_ESTABLISHED,
+ &role_ops_raw_proxy);
+
+ if (vh_prot_name)
+ lws_bind_protocol(wsi, wsi->protocol, __func__);
+ else
+ /* this is the only time he will transition */
+ lws_bind_protocol(wsi,
+ &wsi->vhost->protocols[wsi->vhost->raw_protocol_index],
+ __func__);
+
+ return 1; /* bound */
+}
+
+static int
+rops_client_bind_raw_proxy(struct lws *wsi,
+ const struct lws_client_connect_info *i)
+{
+ if (!i) {
+
+ /* finalize */
+
+ if (!wsi->user_space && wsi->stash->method)
+ if (lws_ensure_user_space(wsi))
+ return 1;
+
+ return 0;
+ }
+
+ /* we are a fallback if nothing else matched */
+
+ lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED,
+ &role_ops_raw_proxy);
+
+ return 1;
+}
+
+static int
+rops_handle_POLLOUT_raw_proxy(struct lws *wsi)
+{
+ if (lwsi_state(wsi) == LRS_ESTABLISHED)
+ return LWS_HP_RET_USER_SERVICE;
+
+ if (lwsi_role_client(wsi))
+ return LWS_HP_RET_USER_SERVICE;
+
+ return LWS_HP_RET_BAIL_OK;
+}
+
+struct lws_role_ops role_ops_raw_proxy = {
+ /* role name */ "raw-proxy",
+ /* alpn id */ NULL,
+ /* 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_proxy,
+ /* handle_POLLOUT */ rops_handle_POLLOUT_raw_proxy,
+ /* perform_user_POLLOUT */ NULL,
+ /* callback_on_writable */ NULL,
+ /* tx_credit */ NULL,
+ /* write_role_protocol */ NULL,
+ /* encapsulation_parent */ NULL,
+ /* alpn_negotiated */ NULL,
+ /* close_via_role_protocol */ NULL,
+ /* close_role */ NULL,
+ /* close_kill_connection */ NULL,
+ /* destroy_role */ NULL,
+ /* adoption_bind */ rops_adoption_bind_raw_proxy,
+ /* client_bind */ rops_client_bind_raw_proxy,
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_ADOPT,
+ LWS_CALLBACK_RAW_PROXY_SRV_ADOPT },
+ /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_RX,
+ LWS_CALLBACK_RAW_PROXY_SRV_RX },
+ /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE,
+ LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE, },
+ /* close cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_CLOSE,
+ LWS_CALLBACK_RAW_PROXY_SRV_CLOSE },
+ /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL,
+ LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL },
+ /* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL,
+ LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL },
+ /* file_handle */ 0,
+};
diff --git a/lib/roles/raw-proxy/private.h b/lib/roles/raw-proxy/private.h
new file mode 100644
index 000000000..6f169d98c
--- /dev/null
+++ b/lib/roles/raw-proxy/private.h
@@ -0,0 +1,41 @@
+/*
+ * libwebsockets - small server side websockets and web server implementation
+ *
+ * Copyright (C) 2010 - 2018 Andy Green
+ *
+ * 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
+ *
+ * This is included from core/private.h if LWS_ROLE_RAW_PROXY
+ */
+
+extern struct lws_role_ops role_ops_raw_proxy;
+
+#define lwsi_role_raw_proxy(wsi) (wsi->role_ops == &role_ops_raw_proxy)
+
+#if 0
+struct lws_vhost_role_ws {
+ const struct lws_extension *extensions;
+};
+
+struct lws_pt_role_ws {
+ struct lws *rx_draining_ext_list;
+ struct lws *tx_draining_ext_list;
+};
+
+struct _lws_raw_proxy_related {
+ struct lws *wsi_onward;
+};
+#endif
diff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c
index 8b94de4be..cc6ed615e 100644
--- a/lib/roles/raw-skt/ops-raw-skt.c
+++ b/lib/roles/raw-skt/ops-raw-skt.c
@@ -223,6 +223,10 @@ struct lws_role_ops role_ops_raw_skt = {
#else
NULL,
#endif
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT,
+ LWS_CALLBACK_RAW_ADOPT },
+ /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX,
+ LWS_CALLBACK_RAW_RX },
/* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 },
/* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 },
/* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL,
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 1cbc6ac6a..307cfc91e 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -2009,6 +2009,10 @@ struct lws_role_ops role_ops_ws = {
/* destroy_role */ rops_destroy_role_ws,
/* adoption_bind */ NULL,
/* client_bind */ NULL,
+ /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
+ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },
+ /* rx_cb clnt, srv */ { LWS_CALLBACK_CLIENT_RECEIVE,
+ LWS_CALLBACK_RECEIVE },
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE,
LWS_CALLBACK_SERVER_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,
diff --git a/lib/tls/tls-server.c b/lib/tls/tls-server.c
index 9525f0f78..a3591eee0 100644
--- a/lib/tls/tls-server.c
+++ b/lib/tls/tls-server.c
@@ -297,7 +297,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
*/
wsi->tls.ssl = NULL;
- if (lws_check_opt(context->options,
+ if (lws_check_opt(wsi->vhost->options,
LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) {
lwsl_info("%s: redirecting from http "
"to https\n", __func__);
@@ -305,7 +305,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
goto notls_accepted;
}
- if (lws_check_opt(context->options,
+ if (lws_check_opt(wsi->vhost->options,
LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER)) {
lwsl_info("%s: allowing unencrypted "
"http service on tls port\n",
@@ -313,7 +313,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
goto notls_accepted;
}
- if (lws_check_opt(context->options,
+ if (lws_check_opt(wsi->vhost->options,
LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) {
if (lws_http_to_fallback(wsi, NULL, 0))
goto fail;
@@ -323,7 +323,8 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
}
lwsl_notice("%s: client did not send a valid "
- "tls hello\n", __func__);
+ "tls hello (default vhost %s)\n",
+ __func__, wsi->vhost->name);
goto fail;
}
if (!n) {
@@ -424,6 +425,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
notls_accepted:
lwsi_set_state(wsi, LRS_ESTABLISHED);
+
return 0;
fail:
diff --git a/libwebsockets.dox b/libwebsockets.dox
index 6fa749fab..15bd4e845 100644
--- a/libwebsockets.dox
+++ b/libwebsockets.dox
@@ -145,6 +145,7 @@ INPUT = include/libwebsockets.h \
./READMEs/README.ci.md \
./READMEs/README.content-security-policy.md \
./READMEs/README.contributing.md \
+ ./READMEs/README.http-fallback.md \
./READMEs/README.release-policy.md \
./READMEs/README.unix-domain-reverse-proxy.md \
./READMEs/README.vulnerability-reporting.md \
diff --git a/minimal-examples/raw/README.md b/minimal-examples/raw/README.md
index f6ce5ae03..ea3bd7a04 100644
--- a/minimal-examples/raw/README.md
+++ b/minimal-examples/raw/README.md
@@ -2,7 +2,10 @@
---|---
minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected
minimal-raw-adopt-udp|Shows how to create a udp socket and read and write on it
+minimal-raw-fallback-http|Shows how to run a normal http(s) server that falls back to a specified role + protocol
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-netcat|Writes stdin to a remote server and prints results on stdout
+minimal-raw-proxy-fallback|Shows how to run a normal http(s) server that falls back to a proxied connection to a specified IP and port
+minimal-raw-proxy|Shows how to set up a vhost so it listens for connections and proxies them to a specified IP and port
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-fallback-http-server/README.md b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md
index 35c840867..9a827c547 100644
--- a/minimal-examples/raw/minimal-raw-fallback-http-server/README.md
+++ b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md
@@ -19,7 +19,7 @@ Commandline option|Meaning
-d |Debug verbosity in decimal, eg, -d15
-s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT`
-h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then)
--r|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http.
+-u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http.
```
$ ./lws-minimal-raw-fallback-http-server
@@ -37,5 +37,5 @@ content on an http(s) listening socket.
|none|served|no tls|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG
|-s|echos http GET|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT
|-s -h|served|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER
-|-s -r|redirected to https|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS
+|-s -u|redirected to https|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS
diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c
index cf37f0d43..6272f4839 100644
--- a/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c
+++ b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c
@@ -125,7 +125,7 @@ int main(int argc, const char **argv)
info.ssl_cert_filepath = "localhost-100y.cert";
info.ssl_private_key_filepath = "localhost-100y.key";
- if (lws_cmdline_option(argc, argv, "-r"))
+ if (lws_cmdline_option(argc, argv, "-u"))
info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
if (lws_cmdline_option(argc, argv, "-h"))
diff --git a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
index 2850d0418..ab5b061bb 100644
--- a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
+++ b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
@@ -141,13 +141,7 @@ int main(int argc, const char **argv)
struct addrinfo h, *r, *rp;
struct lws_vhost *vhost;
const char *p;
- int n = 0, logs = 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 */;
+ int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
signal(SIGINT, sigint_handler);
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt b/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt
new file mode 100644
index 000000000..c0f72cee4
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-minimal-raw-proxy-fallback)
+set(SRCS minimal-raw-proxy-fallback.c)
+
+# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR
+# to the lws plugins dir so it can pick up the plugin source. Eg,
+# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins
+
+# 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_ROLE_RAW_PROXY 1 requirements)
+
+if (requirements)
+ add_executable(${SAMP} ${SRCS})
+
+ if (LWS_PLUGINS_DIR)
+ include_directories(${LWS_PLUGINS_DIR})
+ endif()
+
+ 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-proxy-fallback/README.md b/minimal-examples/raw/minimal-raw-proxy-fallback/README.md
new file mode 100644
index 000000000..f673f461a
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/README.md
@@ -0,0 +1,49 @@
+# lws minimal ws server raw proxy fallback
+
+This demonstrates how a vhost doing normal http or http(s) duty can be also be
+bound to a specific role and protocol as a fallback if the incoming protocol is
+unexpected for tls or http. The example makes the fallback role + protocol
+an lws plugin that performs raw packet proxying.
+
+By default the fallback in the example will proxy 127.0.0.1:22, which is usually
+your ssh server listen port, on 127.0.0.1:7681. You should be able to ssh into
+port 7681 the same as you can port 22. At the same time, you should be able to
+visit http://127.0.0.1:7681 in a browser (and if you give -s, to
+https://127.0.0.1:7681 while your ssh client can still connect to the same
+port.
+
+## build
+
+To build this standalone, you must tell cmake where the lws source tree
+./plugins directory can be found, since it relies on including the source
+of the raw-proxy plugin.
+
+```
+ $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+-r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22
+-s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT`
+-h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then)
+-u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http.
+```
+ $ ./lws-minimal-raw-proxy
+[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy-fallback
+[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
+[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22
+...
+```
+
+```
+ $ ssh -p7681 me@127.0.0.1
+Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1
+[me@learn ~]$
+```
+
+At the same time, visiting http(s)://127.0.0.1:7681 in a browser works fine.
+
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert
new file mode 100644
index 000000000..6f372db40
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+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
+e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE=
+-----END CERTIFICATE-----
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key
new file mode 100644
index 000000000..148f8598e
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+fBrpEY1IATtPq1taBZZogRqI3rOkkPk=
+-----END PRIVATE KEY-----
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c b/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c
new file mode 100644
index 000000000..abe58c8b1
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c
@@ -0,0 +1,134 @@
+/*
+ * lws-minimal-raw-proxy-fallback
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * This demonstrates a normal http / https server which if it receives something
+ * it can't make sense of at the start, falls back to becoming a raw tcp proxy
+ * to a specified address and port.
+ *
+ * Incoming connections cause an outgoing connection to be initiated, and if
+ * successfully established then traffic coming in one side is placed on a
+ * ringbuffer and sent out the opposite side as soon as possible.
+ *
+ * If it receives expected packets for an http(s) connection, it acts like a
+ * normal h1 / h2 webserver.
+ */
+
+#include
+#include
+#include
+#include
+
+#define LWS_PLUGIN_STATIC
+#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c"
+
+static struct lws_protocols protocols[] = {
+ LWS_PLUGIN_PROTOCOL_RAW_PROXY,
+ { NULL, NULL, 0, 0 } /* terminator */
+};
+
+static const struct lws_http_mount mount = {
+ /* .mount_next */ NULL, /* linked-list "next" */
+ /* .mountpoint */ "/", /* mountpoint URL */
+ /* .origin */ "./mount-origin", /* serve from dir */
+ /* .def */ "index.html", /* default filename */
+ /* .protocol */ NULL,
+ /* .cgienv */ NULL,
+ /* .extra_mimetypes */ NULL,
+ /* .interpret */ NULL,
+ /* .cgi_timeout */ 0,
+ /* .cache_max_age */ 0,
+ /* .auth_mask */ 0,
+ /* .cache_reusable */ 0,
+ /* .cache_revalidate */ 0,
+ /* .cache_intermediaries */ 0,
+ /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */
+ /* .mountpoint_len */ 1, /* char count */
+ /* .basic_auth_login_file */ NULL,
+};
+
+static int interrupted;
+
+void sigint_handler(int sig)
+{
+ interrupted = 1;
+}
+
+static struct lws_protocol_vhost_options pvo1 = {
+ NULL,
+ NULL,
+ "onward", /* pvo name */
+ "ipv4:127.0.0.1:22" /* pvo value */
+};
+
+static const struct lws_protocol_vhost_options pvo = {
+ NULL, /* "next" pvo linked-list */
+ &pvo1, /* "child" pvo linked-list */
+ "raw-proxy", /* protocol name we belong to on this vhost */
+ "" /* ignored */
+};
+
+
+int main(int argc, const char **argv)
+{
+ int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+ struct lws_context_creation_info info;
+ struct lws_context *context;
+ char outward[256];
+ const char *p;
+
+ signal(SIGINT, sigint_handler);
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS minimal raw proxy fallback | visit http://localhost:7681\n");
+
+ if ((p = lws_cmdline_option(argc, argv, "-r"))) {
+ lws_strncpy(outward, p, sizeof(outward));
+ pvo1.value = outward;
+ }
+
+ memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+ info.port = 7681;
+ info.protocols = protocols;
+ info.pvo = &pvo;
+ info.mounts = &mount;
+ info.error_document_404 = "/404.html";
+ info.options =
+ LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE |
+ LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG;
+ info.listen_accept_role = "raw-proxy";
+ info.listen_accept_protocol = "raw-proxy";
+
+ if (lws_cmdline_option(argc, argv, "-s")) {
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
+ LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
+ info.ssl_cert_filepath = "localhost-100y.cert";
+ info.ssl_private_key_filepath = "localhost-100y.key";
+
+ if (lws_cmdline_option(argc, argv, "-u"))
+ info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
+
+ if (lws_cmdline_option(argc, argv, "-h"))
+ info.options |= LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER;
+ }
+
+ 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/raw/minimal-raw-proxy-fallback/mount-origin/404.html b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html
new file mode 100644
index 000000000..3e5a14b9f
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html
@@ -0,0 +1,9 @@
+
+
+
+
+
404
+ Sorry, that file doesn't exist.
+
+
+
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico
new file mode 100644
index 000000000..c0cc2e3df
Binary files /dev/null and b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico differ
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html
new file mode 100644
index 000000000..573e51545
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+ Hello from the minimal raw fallback http server example.
+
+ You can confirm the 404 page handler by going to this
+ nonexistant page.
+
+
+
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg
new file mode 100644
index 000000000..7baea649f
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg
@@ -0,0 +1,120 @@
+
+
diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg
new file mode 100644
index 000000000..cd128f1d2
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt b/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt
new file mode 100644
index 000000000..da033dde9
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-minimal-raw-proxy)
+set(SRCS minimal-raw-proxy.c)
+
+# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR
+# to the lws plugins dir so it can pick up the plugin source. Eg,
+# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins
+
+# 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_ROLE_RAW_PROXY 1 requirements)
+
+if (requirements)
+ add_executable(${SAMP} ${SRCS})
+
+ if (LWS_PLUGINS_DIR)
+ include_directories(${LWS_PLUGINS_DIR})
+ endif()
+
+ 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-proxy/README.md b/minimal-examples/raw/minimal-raw-proxy/README.md
new file mode 100644
index 000000000..53793a8b8
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy/README.md
@@ -0,0 +1,41 @@
+# lws minimal ws server raw proxy
+
+This demonstrates how a vhost can be bound to a specific role and protocol,
+with the example using a lws plugin that performs raw packet proxying.
+
+By default the example will proxy 127.0.0.1:22, usually your ssh server
+listen port, on 127.0.0.1:7681. You should be able to ssh into port 7681
+the same as you can port 22. But your ssh server is only listening on port 22...
+
+## build
+
+To build this standalone, you must tell cmake where the lws source tree
+./plugins directory can be found, since it relies on including the source
+of the raw-proxy plugin.
+
+```
+ $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make
+```
+
+## usage
+
+Commandline option|Meaning
+---|---
+-d |Debug verbosity in decimal, eg, -d15
+-r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22
+
+```
+ $ ./lws-minimal-raw-proxy
+[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy | nc localhost 7681
+[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
+[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22
+...
+```
+
+```
+ $ ssh -p7681 me@127.0.0.1
+Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1
+[me@learn ~]$
+```
+
+
diff --git a/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c b/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c
new file mode 100644
index 000000000..aa952e2c4
--- /dev/null
+++ b/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c
@@ -0,0 +1,91 @@
+/*
+ * lws-minimal-raw-proxy
+ *
+ * Copyright (C) 2018 Andy Green
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * This demonstrates a vhost that acts as a raw tcp proxy. Incoming connections
+ * cause an outgoing connection to be initiated, and if successfully established
+ * then traffic coming in one side is placed on a ringbuffer and sent out the
+ * opposite side as soon as possible.
+ */
+
+#include
+#include
+#include
+#include
+
+#define LWS_PLUGIN_STATIC
+#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c"
+
+static struct lws_protocols protocols[] = {
+ LWS_PLUGIN_PROTOCOL_RAW_PROXY,
+ { NULL, NULL, 0, 0 } /* terminator */
+};
+
+static int interrupted;
+
+void sigint_handler(int sig)
+{
+ interrupted = 1;
+}
+
+static struct lws_protocol_vhost_options pvo1 = {
+ NULL,
+ NULL,
+ "onward", /* pvo name */
+ "ipv4:127.0.0.1:22" /* pvo value */
+};
+
+static const struct lws_protocol_vhost_options pvo = {
+ NULL, /* "next" pvo linked-list */
+ &pvo1, /* "child" pvo linked-list */
+ "raw-proxy", /* protocol name we belong to on this vhost */
+ "" /* ignored */
+};
+
+
+int main(int argc, const char **argv)
+{
+ int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+ struct lws_context_creation_info info;
+ struct lws_context *context;
+ char outward[256];
+ const char *p;
+
+ signal(SIGINT, sigint_handler);
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS minimal raw proxy\n");
+
+ if ((p = lws_cmdline_option(argc, argv, "-r"))) {
+ lws_strncpy(outward, p, sizeof(outward));
+ pvo1.value = outward;
+ }
+
+ memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+ info.port = 7681;
+ info.protocols = protocols;
+ info.pvo = &pvo;
+ info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG;
+ info.listen_accept_role = "raw-proxy";
+ info.listen_accept_protocol = "raw-proxy";
+
+ 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/raw/minimal-raw-vhost/minimal-raw-vhost.c b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
index 05043d086..77839c3f6 100644
--- a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
+++ b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
@@ -95,7 +95,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
break;
}
- return 0;
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static struct lws_protocols protocols[] = {
diff --git a/plugins/raw-proxy/README.md b/plugins/raw-proxy/README.md
new file mode 100644
index 000000000..bc5987c24
--- /dev/null
+++ b/plugins/raw-proxy/README.md
@@ -0,0 +1,66 @@
+# raw-proxy plugin
+
+## Enabling for build
+
+```
+$ cmake .. -DLWS_ROLE_RAW_PROXY=1
+```
+
+## configuration pvo
+
+|pvo|value meaning|
+|---|---|
+|onward|The onward proxy destination, in the form `ipv4:addr[:port]`|
+
+## Note for vhost selection
+
+Notice that since it proxies the packets "raw", there's no SNI or Host:
+header to resolve amongst multiple vhosts on the same listen port. So the
+vhost you associate with this protocol must be alone on its own port.
+
+It's also possible to apply this or other role + protocols as a fallback after
+http[s] processing rejected the first packet from an incoming connection.
+See `./READMEs/README-http-fallback.md`
+
+## Note for packet size
+
+For throughput, since often one side is localhost that can handle larger
+packets easily, you should create the context used with this plugin with
+
+```
+ info.pt_serv_buf_size = 8192;
+```
+
+lwsws already does this.
+
+## Using with C
+
+See the minimal example `./minimal-example/raw/minimal-raw-proxy` for
+a working example of a vhost that accepts connections and then
+proxies them using this plugin. The example is almost all boilerplate
+for setting up the context and the pvo.
+
+## Using with lwsws
+
+For a usage where the plugin "owns" the whole vhost, you should enable the
+plugin protocol on the vhost as usual, and specify the "onward" pvo with:
+
+```
+ "ws-protocols": [{
+ "raw-proxy": {
+ "status": "ok",
+ "onward": "ipv4:remote.address.com:port"
+ }
+ }],
+```
+
+and then define the vhost with:
+
+```
+ "apply-listen-accept": "1",
+ "listen-accept-role": "raw-proxy",
+ "listen-accept-protocol": "raw-proxy"
+```
+
+which tells it to apply the role and protocol as soon as a connection is
+accepted on the vhost.
diff --git a/plugins/raw-proxy/protocol_lws_raw_proxy.c b/plugins/raw-proxy/protocol_lws_raw_proxy.c
new file mode 100644
index 000000000..8f52e661b
--- /dev/null
+++ b/plugins/raw-proxy/protocol_lws_raw_proxy.c
@@ -0,0 +1,574 @@
+/*
+ * libwebsockets - plugin for raw proxying
+ *
+ * Copyright (C) 2010-2018 Andy Green
+ *
+ * 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
+#endif
+
+#include
+#include
+#include
+
+#define RING_DEPTH 8
+
+struct packet {
+ void *payload;
+ uint32_t len;
+ uint32_t ticket;
+};
+
+enum {
+ ACC,
+ ONW
+};
+
+/*
+ * Because both sides of the connection want to share this, we allocate it
+ * during accepted adoption and both sides have a pss that is just a wrapper
+ * pointing to this.
+ *
+ * The last one of the accepted side and the onward side to close frees it.
+ * This removes any chance of one side or the other having an invalidated
+ * pointer to the pss.
+ */
+
+struct conn {
+ struct lws *wsi[2];
+
+ /* rings containing unsent rx from accepted and onward sides */
+ struct lws_ring *r[2];
+ uint32_t t[2]; /* ring tail */
+
+ uint32_t ticket_next;
+ uint32_t ticket_retired;
+
+ char rx_enabled[2];
+ char closed[2];
+ char established[2];
+};
+
+struct raw_pss {
+ struct conn *conn;
+};
+
+/* one of these is created for each vhost our protocol is used with */
+
+struct raw_vhd {
+ char addr[128];
+ uint16_t port;
+ char ipv6;
+};
+
+static void
+__destroy_packet(void *_pkt)
+{
+ struct packet *pkt = _pkt;
+
+ free(pkt->payload);
+ pkt->payload = NULL;
+ pkt->len = 0;
+}
+
+static void
+destroy_conn(struct raw_vhd *vhd, struct raw_pss *pss)
+{
+ struct conn *conn = pss->conn;
+
+ if (conn->r[ACC])
+ lws_ring_destroy(conn->r[ACC]);
+ if (conn->r[ONW])
+ lws_ring_destroy(conn->r[ONW]);
+
+ pss->conn = NULL;
+
+ free(conn);
+}
+
+static int
+connect_client(struct raw_vhd *vhd, struct raw_pss *pss)
+{
+ struct lws_client_connect_info i;
+ char host[128];
+ struct lws *cwsi;
+
+ lws_snprintf(host, sizeof(host), "%s:%u", vhd->addr, vhd->port);
+
+ memset(&i, 0, sizeof(i));
+
+ i.method = "RAW";
+ i.context = lws_get_context(pss->conn->wsi[ACC]);
+ i.port = vhd->port;
+ i.address = vhd->addr;
+ i.host = host;
+ i.origin = host;
+ i.ssl_connection = 0;
+ i.vhost = lws_get_vhost(pss->conn->wsi[ACC]);
+ i.local_protocol_name = "raw-proxy";
+ i.protocol = "raw-proxy";
+ i.path = "/";
+ /*
+ * The "onward" client wsi has its own pss but shares the "conn"
+ * created when the inbound connection was accepted. We need to stash
+ * the address of the shared conn and apply it to the client psss
+ * when the client connection completes.
+ */
+ i.opaque_user_data = pss->conn;
+ i.pwsi = &pss->conn->wsi[ONW];
+
+ lwsl_info("%s: onward: %s:%d%s\n", __func__, i.address, i.port, i.path);
+
+ cwsi = lws_client_connect_via_info(&i);
+ if (!cwsi)
+ lwsl_err("%s: client connect failed early\n", __func__);
+
+ return !cwsi;
+}
+
+static int
+flow_control(struct conn *conn, int side, int enable)
+{
+ if (conn->closed[side] ||
+ enable == conn->rx_enabled[side] ||
+ !conn->established[side])
+ return 0;
+
+ if (lws_rx_flow_control(conn->wsi[side], enable))
+ return 1;
+
+ conn->rx_enabled[side] = enable;
+ lwsl_info("%s: %s side: %s\n", __func__, side ? "ONW" : "ACC",
+ enable ? "rx enabled" : "rx flow controlled");
+
+ return 0;
+}
+
+static int
+callback_raw_proxy(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));
+ const struct packet *ppkt;
+ struct conn *conn = NULL;
+ struct lws_tokenize ts;
+ lws_tokenize_elem e;
+ struct packet pkt;
+ const char *cp;
+ int n;
+
+ if (pss)
+ conn = pss->conn;
+
+ 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));
+ if (lws_pvo_get_str(in, "onward", &cp)) {
+ lwsl_err("%s: vh %s: pvo 'onward' required\n", __func__,
+ lws_get_vhost_name(lws_get_vhost(wsi)));
+
+ return -1;
+ }
+ lws_tokenize_init(&ts, cp, LWS_TOKENIZE_F_DOT_NONTERM |
+ LWS_TOKENIZE_F_MINUS_NONTERM |
+ LWS_TOKENIZE_F_NO_FLOATS);
+
+ if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
+ goto bad_onward;
+ if (!strncmp(ts.token, "ipv6", ts.token_len))
+ vhd->ipv6 = 1;
+ else
+ if (strncmp(ts.token, "ipv4", ts.token_len))
+ goto bad_onward;
+
+ /* then the colon */
+ if (lws_tokenize(&ts) != LWS_TOKZE_DELIMITER)
+ goto bad_onward;
+
+ e = lws_tokenize(&ts);
+ if (!vhd->ipv6) {
+ if (e != LWS_TOKZE_TOKEN ||
+ ts.token_len + 1 >= (int)sizeof(vhd->addr))
+ goto bad_onward;
+
+ lws_strncpy(vhd->addr, ts.token, ts.token_len + 1);
+ e = lws_tokenize(&ts);
+ if (e == LWS_TOKZE_DELIMITER) {
+ /* there should be a port then */
+ e = lws_tokenize(&ts);
+ if (e != LWS_TOKZE_INTEGER)
+ goto bad_onward;
+ vhd->port = atoi(ts.token);
+ e = lws_tokenize(&ts);
+ }
+ if (e != LWS_TOKZE_ENDED)
+ goto bad_onward;
+ } else
+ lws_strncpy(vhd->addr, ts.token, sizeof(vhd->addr));
+
+ lwsl_notice("%s: vh %s: onward %s:%s:%d\n", __func__,
+ lws_get_vhost_name(lws_get_vhost(wsi)),
+ vhd->ipv6 ? "ipv6": "ipv4", vhd->addr, vhd->port);
+ break;
+
+bad_onward:
+ lwsl_err("%s: onward pvo format must be ipv4:addr[:port] "
+ " or ipv6:addr, not '%s'\n", __func__, cp);
+ return -1;
+
+ case LWS_CALLBACK_PROTOCOL_DESTROY:
+ break;
+
+ /* callbacks related to client "onward side" */
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
+ in ? (char *)in : "(null)");
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_CLI_ADOPT:
+ lwsl_debug("LWS_CALLBACK_RAW_CLI_ADOPT: pss %p\n", pss);
+ if (conn)
+ break;
+ conn = pss->conn = lws_get_opaque_user_data(wsi);
+ conn->established[ONW] = 1;
+ /* they start enabled */
+ conn->rx_enabled[ACC] = 1;
+ conn->rx_enabled[ONW] = 1;
+
+ /* he disabled his rx while waiting for use to be established */
+ flow_control(conn, ACC, 1);
+
+ lws_callback_on_writable(wsi);
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_CLI_CLOSE:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_CLOSE\n");
+ if (!conn)
+ break;
+
+ conn->closed[ONW] = 1;
+
+ if (conn->closed[ACC])
+ destroy_conn(vhd, pss);
+
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_CLI_RX:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_RX: %d\n", (int)len);
+
+ if (!pss || !conn->wsi[ACC] || conn->closed[ACC]) {
+ lwsl_info(" pss %p, wsi[ACC] %p, closed[ACC] %d\n",
+ pss, conn->wsi[ACC], conn->closed[ACC]);
+ return -1;
+ }
+ pkt.payload = malloc(len);
+ if (!pkt.payload) {
+ lwsl_notice("OOM: dropping\n");
+ return -1;
+ }
+ pkt.len = len;
+ pkt.ticket = conn->ticket_next++;
+
+ memcpy(pkt.payload, in, len);
+ if (!lws_ring_insert(conn->r[ONW], &pkt, 1)) {
+ __destroy_packet(&pkt);
+ lwsl_notice("dropping!\n");
+ return -1;
+ }
+
+ lwsl_debug("After onward RX: acc free: %d...\n",
+ (int)lws_ring_get_count_free_elements(conn->r[ONW]));
+
+ if (conn->rx_enabled[ONW] &&
+ lws_ring_get_count_free_elements(conn->r[ONW]) < 2)
+ flow_control(conn, ONW, 0);
+
+ if (!conn->closed[ACC])
+ lws_callback_on_writable(conn->wsi[ACC]);
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE\n");
+
+ ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]);
+ if (!ppkt) {
+ lwsl_info("%s: CLI_WRITABLE had nothing in acc ring\n",
+ __func__);
+ break;
+ }
+
+ if (ppkt->ticket != conn->ticket_retired + 1) {
+ lwsl_info("%s: acc ring has %d but next %d\n", __func__,
+ ppkt->ticket, conn->ticket_retired + 1);
+ lws_callback_on_writable(conn->wsi[ACC]);
+ break;
+ }
+
+ n = lws_write(wsi, ppkt->payload, ppkt->len, LWS_WRITE_RAW);
+ if (n < 0) {
+ lwsl_info("%s: WRITEABLE: %d\n", __func__, n);
+
+ return -1;
+ }
+
+ conn->ticket_retired = ppkt->ticket;
+ lws_ring_consume(conn->r[ACC], &conn->t[ACC], NULL, 1);
+ lws_ring_update_oldest_tail(conn->r[ACC], conn->t[ACC]);
+
+ lwsl_debug("acc free: %d...\n",
+ (int)lws_ring_get_count_free_elements(conn->r[ACC]));
+
+ if (!conn->rx_enabled[ACC] &&
+ lws_ring_get_count_free_elements(conn->r[ACC]) > 2)
+ flow_control(conn, ACC, 1);
+
+ ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]);
+ lwsl_debug("%s: CLI_WRITABLE: next acc pkt %p idx %d vs %d\n",
+ __func__, ppkt, ppkt ? ppkt->ticket : 0,
+ conn->ticket_retired + 1);
+
+ if (ppkt && ppkt->ticket == conn->ticket_retired + 1)
+ lws_callback_on_writable(wsi);
+ else {
+ /*
+ * defer checking for accepted side closing until we
+ * sent everything in the ring to onward
+ */
+ if (conn->closed[ACC])
+ /*
+ * there is never going to be any more... but
+ * we may have some tx still in tx buflist /
+ * partial
+ */
+ return lws_raw_transaction_completed(wsi);
+
+ if (lws_ring_get_element(conn->r[ONW], &conn->t[ONW]))
+ lws_callback_on_writable(conn->wsi[ACC]);
+ }
+ break;
+
+ /* callbacks related to raw socket descriptor "accepted side" */
+
+ case LWS_CALLBACK_RAW_PROXY_SRV_ADOPT:
+ lwsl_debug("LWS_CALLBACK_RAW_SRV_ADOPT\n");
+
+ conn = pss->conn = malloc(sizeof(struct conn));
+ if (!pss->conn)
+ return -1;
+ memset(conn, 0, sizeof(*conn));
+
+ conn->wsi[ACC] = wsi;
+ conn->ticket_next = 1;
+
+ conn->r[ACC] = lws_ring_create(sizeof(struct packet),
+ RING_DEPTH, __destroy_packet);
+ if (!conn->r[ACC]) {
+ lwsl_err("%s: OOM\n", __func__);
+ return -1;
+ }
+ conn->r[ONW] = lws_ring_create(sizeof(struct packet),
+ RING_DEPTH, __destroy_packet);
+ if (!conn->r[ONW]) {
+ lws_ring_destroy(conn->r[ACC]);
+ conn->r[ACC] = NULL;
+ lwsl_err("%s: OOM\n", __func__);
+
+ return -1;
+ }
+
+ conn->established[ACC] = 1;
+
+ /* disable any rx until the client side is up */
+ flow_control(conn, ACC, 0);
+
+ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+
+ /* try to create the onward client connection */
+ connect_client(vhd, pss);
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_SRV_CLOSE:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_CLOSE:\n");
+
+ if (!conn)
+ break;
+
+ conn->closed[ACC] = 1;
+ if (conn->closed[ONW])
+ destroy_conn(vhd, pss);
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_SRV_RX:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_RX: rx %d\n", (int)len);
+
+ if (!conn || !conn->wsi[ONW]) {
+ lwsl_err("%s: LWS_CALLBACK_RAW_PROXY_SRV_RX: "
+ "conn->wsi[ONW] NULL\n", __func__);
+ return -1;
+ }
+ if (conn->closed[ONW]) {
+ lwsl_info(" closed[ONW] %d\n", conn->closed[ONW]);
+ return -1;
+ }
+
+ pkt.payload = malloc(len);
+ if (!pkt.payload) {
+ lwsl_notice("OOM: dropping\n");
+ return -1;
+ }
+ pkt.len = len;
+ pkt.ticket = conn->ticket_next++;
+
+ memcpy(pkt.payload, in, len);
+ if (!lws_ring_insert(conn->r[ACC], &pkt, 1)) {
+ __destroy_packet(&pkt);
+ lwsl_notice("dropping!\n");
+ return -1;
+ }
+
+ lwsl_debug("After acc RX: acc free: %d...\n",
+ (int)lws_ring_get_count_free_elements(conn->r[ACC]));
+
+ if (conn->rx_enabled[ACC] &&
+ lws_ring_get_count_free_elements(conn->r[ACC]) <= 2)
+ flow_control(conn, ACC, 0);
+
+ if (conn->established[ONW] && !conn->closed[ONW])
+ lws_callback_on_writable(conn->wsi[ONW]);
+ break;
+
+ case LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE:
+ lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n");
+
+ if (!conn->established[ONW] || conn->closed[ONW])
+ break;
+
+ ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]);
+ if (!ppkt) {
+ lwsl_info("%s: SRV_WRITABLE nothing in onw ring\n",
+ __func__);
+ break;
+ }
+
+ if (ppkt->ticket != conn->ticket_retired + 1) {
+ lwsl_info("%s: onw ring has %d but next %d\n", __func__,
+ ppkt->ticket, conn->ticket_retired + 1);
+ lws_callback_on_writable(conn->wsi[ONW]);
+ break;
+ }
+
+ n = lws_write(wsi, ppkt->payload, ppkt->len, LWS_WRITE_RAW);
+ if (n < 0) {
+ lwsl_info("%s: WRITEABLE: %d\n", __func__, n);
+
+ return -1;
+ }
+
+ conn->ticket_retired = ppkt->ticket;
+ lws_ring_consume(conn->r[ONW], &conn->t[ONW], NULL, 1);
+ lws_ring_update_oldest_tail(conn->r[ONW], conn->t[ONW]);
+
+ lwsl_debug("onward free: %d... waiting %d\n",
+ (int)lws_ring_get_count_free_elements(conn->r[ONW]),
+ (int)lws_ring_get_count_waiting_elements(conn->r[ONW],
+ &conn->t[ONW]));
+
+ if (!conn->rx_enabled[ONW] &&
+ lws_ring_get_count_free_elements(conn->r[ONW]) > 2)
+ flow_control(conn, ONW, 1);
+
+ ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]);
+ lwsl_debug("%s: SRV_WRITABLE: next onw pkt %p idx %d vs %d\n",
+ __func__, ppkt, ppkt ? ppkt->ticket : 0,
+ conn->ticket_retired + 1);
+
+ if (ppkt && ppkt->ticket == conn->ticket_retired + 1)
+ lws_callback_on_writable(wsi);
+ else {
+ /*
+ * defer checking for onward side closing until we
+ * sent everything in the ring to accepted side
+ */
+ if (conn->closed[ONW])
+ /*
+ * there is never going to be any more... but
+ * we may have some tx still in tx buflist /
+ * partial
+ */
+ return lws_raw_transaction_completed(wsi);
+
+ if (lws_ring_get_element(conn->r[ACC], &conn->t[ACC]))
+ lws_callback_on_writable(conn->wsi[ONW]);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
+}
+
+#define LWS_PLUGIN_PROTOCOL_RAW_PROXY { \
+ "raw-proxy", \
+ callback_raw_proxy, \
+ sizeof(struct raw_pss), \
+ 8192, \
+ 8192, NULL, 0 \
+ }
+
+#if !defined (LWS_PLUGIN_STATIC)
+
+static const struct lws_protocols protocols[] = {
+ LWS_PLUGIN_PROTOCOL_RAW_PROXY
+};
+
+LWS_EXTERN LWS_VISIBLE int
+init_protocol_lws_raw_proxy(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 = LWS_ARRAY_SIZE(protocols);
+ c->extensions = NULL;
+ c->count_extensions = 0;
+
+ return 0;
+}
+
+LWS_EXTERN LWS_VISIBLE int
+destroy_protocol_lws_raw_proxy(struct lws_context *context)
+{
+ return 0;
+}
+#endif
+
+
diff --git a/test-apps/test-server.c b/test-apps/test-server.c
index 2319cb49f..5c2f085bc 100644
--- a/test-apps/test-server.c
+++ b/test-apps/test-server.c
@@ -26,7 +26,7 @@
int close_testing;
int max_poll_elements;
-int debug_level = 7;
+int debug_level = LLL_USER | 7;
#ifdef EXTERNAL_POLL
struct lws_pollfd *pollfds;