diff --git a/changelog b/changelog index 347b778ee772272ffa4a0e217853bf82bc1dbb32..be8c81724aa6a1d65a3b67737b8ebaa89f731fd1 100644 --- a/changelog +++ b/changelog @@ -53,6 +53,14 @@ User API additions which lets you set the name of the ECDH curve OpenSSL should use. By default (if you leave ecdh_curve NULL) it will use "prime256v1" +2) MINOR NEWAPI It was already possible to adopt a foreign socket that had not +been read from using lws_adopt_socket() since v1.7. Now you can adopt a +partially-used socket if you don't need SSL, by passing it what you read +so it can drain that before reading from the socket. + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); v1.7.0 diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 6175a3d2d69e13d6c08f51d085e01f97721f61dd..3e9493926f4bd544e28dae4b2c98b82da84901d3 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1712,6 +1712,9 @@ lws_client_connect_via_info(struct lws_client_connect_info * ccinfo); LWS_VISIBLE LWS_EXTERN struct lws * lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len); LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT lws_canonical_hostname(struct lws_context *context); diff --git a/lib/parsers.c b/lib/parsers.c index 2547ec9032b38f1e340d0f80be6d56d5a990df18..c78fb92e6640f4335d2c10fc9d311e09537f02f4 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -81,6 +81,16 @@ lws_header_table_reset(struct lws *wsi) /* since we will restart the ah, our new headers are not completed */ wsi->hdr_parsing_completed = 0; + + /* + * if we inherited pending rx (from socket adoption deferred + * processing), apply and free it. + */ + if (wsi->u.hdr.preamble_rx) { + memcpy(ah->rx, wsi->u.hdr.preamble_rx, wsi->u.hdr.preamble_rx_len); + ah->rxlen = wsi->u.hdr.preamble_rx_len; + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + } } int LWS_WARN_UNUSED_RESULT @@ -179,6 +189,9 @@ int lws_header_table_detach(struct lws *wsi) (void *)wsi, (void *)wsi->u.hdr.ah, wsi->tsi, pt->ah_count_in_use); + if (wsi->u.hdr.preamble_rx) + lws_free_set_NULL(wsi->u.hdr.preamble_rx); + /* may not be detached while he still has unprocessed rx */ if (ah && ah->rxpos != ah->rxlen) { lwsl_err("%s: %p: rxpos:%d, rxlen:%d\n", __func__, wsi, diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index af62e8ff8af5d9af8fbf0e244252d8ff16278d67..315c32e17730e6b0fc152279946c092083347aa5 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -940,6 +940,8 @@ struct _lws_header_related { /* MUST be first in struct */ struct allocated_headers *ah; struct lws *ah_wait_list; + unsigned char *preamble_rx; + unsigned int preamble_rx_len; enum uri_path_states ups; enum uri_esc_states ues; short lextable_pos; diff --git a/lib/server.c b/lib/server.c index cc846d4d64d1a6b2a21f9e2130ae15896d518186..92a8eedce2cab881ed4ea9f48b188092f6c9a815 100644 --- a/lib/server.c +++ b/lib/server.c @@ -821,6 +821,80 @@ fail: return NULL; } +/** + * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it + * @context: lws context + * @accept_fd: fd of already-accepted socket to adopt + * @readbuf: NULL or pointer to data that must be drained before reading from + * accept_fd + * @len: The length of the data held at @readbuf + * + * Either returns new wsi bound to accept_fd, or closes accept_fd and + * returns NULL, having cleaned up any new wsi pieces. + * + * LWS adopts the socket in http serving mode, it's ready to accept an upgrade + * to ws or just serve http. + * + * If your external code did not already read from the socket, you can use + * lws_adopt_socket() instead. + * + * This api is guaranteed to use the data at @readbuf first, before reading from + * the socket. + * + * @readbuf is limited to the size of the ah rx buf, currently 2048 bytes. + */ + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + struct lws *wsi = lws_adopt_socket(context, accept_fd); + struct allocated_headers *ah; + + if (!wsi) + return NULL; + + if (!readbuf) + return wsi; + + if (len > sizeof(ah->rx)) { + lwsl_err("%s: rx in too big\n", __func__); + goto bail; + } + /* + * we can't process the initial read data until we can attach an ah. + * + * if one is available, get it and place the data in his ah rxbuf... + * wsi with ah that have pending rxbuf get auto-POLLIN service. + */ + if (!lws_header_table_attach(wsi)) { + ah = wsi->u.hdr.ah; + memcpy(ah->rx, readbuf, len); + ah->rxpos = 0; + ah->rxlen = len; + + return wsi; + } + + /* + * hum if no ah came, we are on the wait list and must defer + * dealing with this until the ah arrives. + * + * later successful lws_header_table_attach() will apply the + * below to the rx buffer. + */ + + wsi->u.hdr.preamble_rx = lws_malloc(len); + wsi->u.hdr.preamble_rx_len = len; + + return wsi; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); + + return NULL; +} + LWS_VISIBLE int lws_server_socket_service(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pollfd) diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index 13d3f006950aac97c29c2e61ad89d7e5336baab6..640ce8e58dcde342ddf69f4c4a1692f3a0eac7ff 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -790,6 +790,42 @@ LWS adopts the socket in http serving mode, it's ready to accept an upgrade to ws or just serve http. </blockquote> <hr> +<h2>lws_adopt_socket_readbuf - adopt foreign socket and first rx as if listen socket accepted it</h2> +<i>LWS_EXTERN struct lws *</i> +<b>lws_adopt_socket_readbuf</b> +(<i>struct lws_context *</i> <b>context</b>, +<i>lws_sockfd_type</i> <b>accept_fd</b>, +<i>const char *</i> <b>readbuf</b>, +<i>size_t</i> <b>len</b>) +<h3>Arguments</h3> +<dl> +<dt><b>context</b> +<dd>lws context +<dt><b>accept_fd</b> +<dd>fd of already-accepted socket to adopt +<dt><b>readbuf</b> +<dd>NULL or pointer to data that must be drained before reading from +accept_fd +<dt><b>len</b> +<dd>The length of the data held at <tt><b>readbuf</b></tt> +</dl> +<h3>Description</h3> +<blockquote> +Either returns new wsi bound to accept_fd, or closes accept_fd and +returns NULL, having cleaned up any new wsi pieces. +<p> +LWS adopts the socket in http serving mode, it's ready to accept an upgrade +to ws or just serve http. +<p> +If your external code did not already read from the socket, you can use +<b>lws_adopt_socket</b> instead. +<p> +This api is guaranteed to use the data at <tt><b>readbuf</b></tt> first, before reading from +the socket. +<p> +<tt><b>readbuf</b></tt> is limited to the size of the ah rx buf, currently 2048 bytes. +</blockquote> +<hr> <h2>lws_serve_http_file - Send a file back to the client using http</h2> <i>int</i> <b>lws_serve_http_file</b> @@ -1518,6 +1554,7 @@ header. <i>unsigned int</i> <b>count_threads</b>;<br> <i>unsigned int</i> <b>fd_limit_per_thread</b>;<br> <i>unsigned int</i> <b>timeout_secs</b>;<br> + <i>const char *</i> <b>ecdh_curve</b>;<br> };<br> <h3>Members</h3> <dl> @@ -1613,6 +1650,8 @@ limit by the number of threads. library are protected from hanging forever by timeouts. If nonzero, this member lets you set the timeout used in seconds. Otherwise a default timeout is used. +<dt><b>ecdh_curve</b> +<dd>if NULL, defaults to initializing server with "prime256v1" </dl> <hr> <h2>struct lws_client_connect_info - parameters to connect with when using lws_client_connect_via_info()</h2>