diff --git a/CMakeLists.txt b/CMakeLists.txt index bec9b4027b19215978096169e377a16ad8185167..31824282f9fb6d33ec539cf73921e07c30aaf548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out option(LWS_IPV6 "Compile with support for ipv6" OFF) option(LWS_UNIX_SOCK "Compile with support for UNIX domain socket" ON) option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF) -option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying (requires libhubbub)" OFF) +option(LWS_WITH_HTTP_PROXY "Support for HTTP proxying" ON) option(LWS_WITH_ZIP_FOPS "Support serving pre-zipped files" OFF) option(LWS_WITH_SOCKS5 "Allow use of SOCKS5 proxy on client connections" OFF) option(LWS_WITH_GENERIC_SESSIONS "With the Generic Sessions plugin" OFF) @@ -41,6 +41,7 @@ option(LWS_WITH_THREADPOOL "Managed worker thread pool support (relies on pthrea option(LWS_WITH_HTTP_STREAM_COMPRESSION "Support HTTP stream compression" OFF) option(LWS_WITH_HTTP_BROTLI "Also offer brotli http stream compression (requires LWS_WITH_HTTP_STREAM_COMPRESSION)" OFF) option(LWS_WITH_ACME "Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)" OFF) +option(LWS_WITH_HUBBUB "Enable libhubbub rewriting support" OFF) # # TLS library options... all except mbedTLS are basically OpenSSL variants. # @@ -134,6 +135,12 @@ if(NOT DEFINED CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type") endif() +# microsoft... that's why you can't have nice things + +if (WIN32 OR LWS_WITH_ESP32) + set(LWS_UNIX_SOCK 0) +endif() + project(libwebsockets C) set(PACKAGE "libwebsockets") @@ -333,7 +340,8 @@ endif() if (LWS_WITH_HTTP_PROXY AND (LWS_WITHOUT_CLIENT OR LWS_WITHOUT_SERVER)) - message(FATAL_ERROR "You have to enable both client and server for http proxy") + message("You have to enable both client and server for http proxy") + set(LWS_WITH_HTTP_PROXY 0) endif() # Allow the user to override installation directories. @@ -744,6 +752,7 @@ set(HDR_PUBLIC set(SOURCES lib/core/alloc.c lib/core/context.c + lib/core/dummy-callback.c lib/core/libwebsockets.c lib/core/output.c lib/core/pollfd.c @@ -1402,7 +1411,7 @@ if (LWS_WITH_SQLITE3) endif() -if (LWS_WITH_HTTP_PROXY) +if (LWS_WITH_HUBBUB) find_library(LIBHUBBUB_LIBRARIES NAMES hubbub) list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} ) endif() diff --git a/READMEs/README.unix-domain-reverse-proxy.md b/READMEs/README.unix-domain-reverse-proxy.md new file mode 100644 index 0000000000000000000000000000000000000000..ef61968d35791128ee9933825b9bc5057bfee561 --- /dev/null +++ b/READMEs/README.unix-domain-reverse-proxy.md @@ -0,0 +1,95 @@ +## Unix Domain Sockets Reverse Proxy + +### Introduction + +lws is able to use a mount to place reverse proxies into the URL space. + +These are particularly useful when using Unix Domain Sockets, basically +files in the server filesystem, to communicate between lws and a separate +server process and integrate the result into a coherent URL namespace on +the lws side. + +This has the advantage that the actual web server that forwards the +data from the unix socket owner is in a different process than the server +that serves on the unix socket. If it has problems, they do not affect +the actual public-facing web server. The unix domain socket server may +be in a completely different language than the web server. + +Compared to CGI, there are no forks to make a connection to the unix +domain socket server. + +### Mount origin format + +Unix Domain Sockets are effectively "files" in the server filesystem, and +are defined by their filepath. The "server" side that is to be proxied opens +the socket and listens on it, which creates a file in the server filesystem. +The socket understands either http or https protocol. + +Lws can be told to act as a proxy for that at a mountpoint in the lws vhost +url space. + +If your mount is expressed in C code, then the mount type is LWSMPRO_HTTP or +LWSMPRO_HTTPS depending on the protocol the unix socket understands, and the +origin address has the form `+/path/to/unix/socket:/path/inside/mount`. + +The + at the start indicates it is a local unix socket we are proxying, and +the ':' acts as a delimiter for the socket path, since unlike other addresses +the unix socket path can contain '/' itself. + +### Connectivity rules and translations + +Onward proxy connections from lws to the Unix Domain Socket happen using +http/1.1. That implies `transfer-encoding: chunking` in the case that the +length of the output is not known beforehand. + +Lws takes care of stripping any chunking (which is illegal in h2) and +translating between h1 and h2 header formats if the return connection is +actually in http/2. + +The h1 onward proxy connection translates the following headers from the return +connection, which may be h1 or h2: + +Header|Function +---|--- +host|Which vhost +etag|Information on any etag the client has cached for this URI +if-modified-since|Information on the freshness of any etag the client has cached for this URI +accept-language|Which languages the return path client prefers +accept-encoding|Which compression encodings the client can accept +cache-control|Information from the return path client about cache acceptability +x-forwarded-for|The IP address of the return path client + +This implies that the proxied connection can + + - return 301 etc to say the return path client's etag is still valid + + - choose to compress using an acceptable content-encoding + +The following headers are translated from the headers replied via the onward +connection (always h1) back to the return path (which may be h1 or h2) + +Header|Function +---|--- +content-length|If present, an assertion of how much payload is expected +content-type|The mimetype of the payload +etag|The canonical etag for the content at this URI +accept-language|This is returned to the return path client because there is no easy way for the return path client to know what it sent originally. It allows clientside selection of i18n. +content-encoding|Any compression format on the payload (selected from what the client sent in accept-encoding, if anything) +cache-control|The onward server's response about cacheability of its payload + +### h1 -> h2 conversion + +Chunked encoding that may have been used on the outgoing proxy client connection +is removed for h2 return connections (chunked encoding is illegal for h2). + +Headers are converted to all lower-case and hpack format for h2 return connections. + +Header and payload proxying is staged according to when the return connection +(which may be an h2 child stream) is writable. + +### Behaviour is unix domain socket server unavailable + +If the server that listens on the unix domain socket is down or being restarted, +lws understands that it couldn't connect to it and returns a clean 503 response +`HTTP_STATUS_SERVICE_UNAVAILABLE` along with a brief human-readable explanation. + diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h index c0a65cbb8aa568c5dbb913407bc36f99fc7881db..688fbda4e9114a10d43257c25ffe8606723e2d11 100644 --- a/include/libwebsockets/lws-callbacks.h +++ b/include/libwebsockets/lws-callbacks.h @@ -485,7 +485,7 @@ enum lws_callback_reasons { /**< after your client connection completed the websocket upgrade * handshake with the remote server */ - LWS_CALLBACK_CLIENT_CLOSED = 75, + LWS_CALLBACK_CLIENT_CLOSED = 75, /**< when a client websocket session ends */ LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, @@ -497,21 +497,11 @@ enum lws_callback_reasons { * which is typically some hundreds of bytes. So, to add a canned * cookie, your handler code might look similar to: * - * char **p = (char **)in; + * char **p = (char **)in, *end = (*p) + len; * - * if (len < 100) - * return 1; - * - * *p += sprintf(*p, "Cookie: a=b\x0d\x0a"); - * - * return 0; - * - * Notice if you add anything, you just have to take care about - * the CRLF on the line you added. Obviously this callback is - * optional, if you don't handle it everything is fine. - * - * Notice the callback is coming to protocols[0] all the time, - * because there is no specific protocol negotiated yet. + * if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, + * (unsigned char)"a=b", 3, p, end)) + * return -1; * * See LWS_CALLBACK_ADD_HEADERS for adding headers to server * transactions. @@ -798,4 +788,6 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, #define LWS_CB_REASON_AUX_BF__PROXY 2 #define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4 #define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 +#define LWS_CB_REASON_AUX_BF__PROXY_TRANS_END 16 +#define LWS_CB_REASON_AUX_BF__PROXY_HEADERS 32 ///@} diff --git a/include/libwebsockets/lws-logs.h b/include/libwebsockets/lws-logs.h index 2f4c369f3d7ccc03efac7da8ad48e3cc984a3504..d74489205be1fe61c26c8f0cccbee21f37e89647 100644 --- a/include/libwebsockets/lws-logs.h +++ b/include/libwebsockets/lws-logs.h @@ -45,8 +45,9 @@ enum lws_log_levels { LLL_CLIENT = 1 << 8, LLL_LATENCY = 1 << 9, LLL_USER = 1 << 10, + LLL_THREAD = 1 << 11, - LLL_COUNT = 11 /* set to count of valid flags */ + LLL_COUNT = 12 /* set to count of valid flags */ }; LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2); @@ -92,6 +93,7 @@ lwsl_timestamp(int level, char *p, int len); #define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) #define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) #define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) +#define lwsl_thread(...) _lws_log(LLL_THREAD, __VA_ARGS__) #else /* no debug */ #if defined(LWS_WITH_NO_LOGS) @@ -105,6 +107,7 @@ lwsl_timestamp(int level, char *p, int len); #define lwsl_ext(...) do {} while(0) #define lwsl_client(...) do {} while(0) #define lwsl_latency(...) do {} while(0) +#define lwsl_thread(...) do {} while(0) #endif diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index 6a110a6c1ae4bc3cff52468b9868017dc8d94cd6..02fe432d5b0f68032e6f7ced97eb0a653c59e404 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -392,6 +392,17 @@ lws_set_wsi_user(struct lws *wsi, void *user); * \param ads: result pointer for address part * \param port: result pointer for port part * \param path: result pointer for path part + * + * You may also refer to unix socket addresses, using a '+' at the start of + * the address. In this case, the address should end with ':', which is + * treated as the separator between the address and path (the normal separator + * '/' is a valid part of the socket path). Eg, + * + * http://+/var/run/mysocket:/my/path + * + * If the first character after the + is '@', it's interpreted by lws client + * processing as meaning to use linux abstract namespace sockets, the @ is + * replaced with a '\0' before use. */ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_parse_uri(char *p, const char **prot, const char **ads, int *port, @@ -422,6 +433,12 @@ lws_cmdline_option(int argc, const char **argv, const char *val); LWS_VISIBLE LWS_EXTERN unsigned long lws_now_secs(void); +/** + * lws_now_usecs(): return useconds since 1970-1-1 + */ +LWS_VISIBLE LWS_EXTERN lws_usec_t +lws_now_usecs(void); + /** * lws_compare_time_t(): return relationship between two time_t * diff --git a/include/libwebsockets/lws-timeout-timer.h b/include/libwebsockets/lws-timeout-timer.h index b7f04e5821777b12f32522c9bc6d2ef47d65d206..9411866cc4d815caeb6375b1eb0471c8bc7629e3 100644 --- a/include/libwebsockets/lws-timeout-timer.h +++ b/include/libwebsockets/lws-timeout-timer.h @@ -63,6 +63,7 @@ enum pending_timeout { PENDING_TIMEOUT_LAGGING = 28, PENDING_TIMEOUT_THREADPOOL = 29, PENDING_TIMEOUT_THREADPOOL_TASK = 30, + PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE = 31, /****** add new things just above ---^ ******/ diff --git a/include/libwebsockets/lws-write.h b/include/libwebsockets/lws-write.h index 6708b85899457073ec90331ad4c87f77091a7aaf..93e1396e71ee1fa20a1a9140007c5aed3fbdb183 100644 --- a/include/libwebsockets/lws-write.h +++ b/include/libwebsockets/lws-write.h @@ -86,6 +86,14 @@ enum lws_write_protocol { /* flags */ + LWS_WRITE_BUFLIST = 0x20, + /**< Don't actually write it... stick it on the output buflist and + * write it as soon as possible. Useful if you learn you have to + * write something, have the data to write to hand but the timing is + * unrelated as to whether the connection is writable or not, and were + * otherwise going to have to allocate a temp buffer and write it + * later anyway */ + LWS_WRITE_NO_FIN = 0x40, /**< This part of the message is not the end of the message */ diff --git a/lib/core/adopt.c b/lib/core/adopt.c index 5eafefd2d5c1ae792187c07b20d68dcc3544fef8..99ca856ccd33778583b12837392fa239c88cbba2 100644 --- a/lib/core/adopt.c +++ b/lib/core/adopt.c @@ -241,6 +241,9 @@ bail: parent->child_list = new_wsi->sibling_list; if (new_wsi->user_space) lws_free(new_wsi->user_space); + + vh->context->count_wsi_allocated--; + lws_vhost_unbind_wsi(new_wsi); lws_free(new_wsi); diff --git a/lib/core/connect.c b/lib/core/connect.c index 6e730f3dab544fa40da0280ad0e8ab6b2f0392df..2b6517c5e80274f6f188efa0651d8f63af08a441 100644 --- a/lib/core/connect.c +++ b/lib/core/connect.c @@ -42,7 +42,7 @@ lws_client_stash_destroy(struct lws *wsi) LWS_VISIBLE struct lws * lws_client_connect_via_info(const struct lws_client_connect_info *i) { - struct lws *wsi; + struct lws *wsi, *safe = NULL; const struct lws_protocols *p; const char *local = i->protocol; #if LWS_MAX_SMP > 1 @@ -219,6 +219,21 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) goto bail1; } + /* + * at this point user callbacks like + * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER will be interested to + * know the parent... eg for proxying we can grab extra headers from + * the parent's incoming ah and add them to the child client handshake + */ + + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + safe = wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } + /* * PHASE 7: Do any role-specific finalization processing. We can still * see important info things via wsi->stash @@ -227,6 +242,12 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) if (wsi->role_ops->client_bind) { int n = wsi->role_ops->client_bind(wsi, NULL); + if (n && i->parent_wsi) { + /* unpick from parent */ + + i->parent_wsi->child_list = safe; + } + if (n < 0) /* we didn't survive, wsi is freed */ goto bail2; @@ -241,14 +262,8 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) if (i->pwsi) *i->pwsi = wsi; - if (i->parent_wsi) { - lwsl_info("%s: created child %p of parent %p\n", __func__, - wsi, i->parent_wsi); - wsi->parent = i->parent_wsi; - wsi->sibling_list = i->parent_wsi->child_list; - i->parent_wsi->child_list = wsi; - } -#ifdef LWS_WITH_HTTP_PROXY + +#if defined(LWS_WITH_HUBBUB) if (i->uri_replace_to) wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb, i->uri_replace_from, diff --git a/lib/core/context.c b/lib/core/context.c index 34d5346caac3d4dd6fbcc12c67c97fd965ec0da7..e3ae08bbbaf3f2b5ebf3d77f8ac095c46c2f80f7 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -339,347 +339,6 @@ next: return 0; } -LWS_VISIBLE int -lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct lws_ssl_info *si; -#ifdef LWS_WITH_CGI - struct lws_cgi_args *args; -#endif -#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) - char buf[128]; - int n; -#endif - - switch (reason) { -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - case LWS_CALLBACK_HTTP: -#ifndef LWS_NO_SERVER - if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) - return -1; - - if (lws_http_transaction_completed(wsi)) -#endif - return -1; - break; -#if !defined(LWS_NO_SERVER) - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - case LWS_CALLBACK_HTTP_FILE_COMPLETION: - if (lws_http_transaction_completed(wsi)) - return -1; - break; -#endif - - case LWS_CALLBACK_HTTP_WRITEABLE: -#ifdef LWS_WITH_CGI - if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | - LWS_CB_REASON_AUX_BF__CGI)) { - n = lws_cgi_write_split_stdout_headers(wsi); - if (n < 0) { - lwsl_debug("AUX_BF__CGI forcing close\n"); - return -1; - } - if (!n) - lws_rx_flow_control( - wsi->http.cgi->stdwsi[LWS_STDOUT], 1); - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) - wsi->reason_bf &= - ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; - else - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; - - if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) - return -1; - break; - } - - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { - if (!wsi->http2_substream) { - memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); - lwsl_debug("writing chunk term and exiting\n"); - n = lws_write(wsi, (unsigned char *)buf + - LWS_PRE, 5, LWS_WRITE_HTTP); - } else - n = lws_write(wsi, (unsigned char *)buf + - LWS_PRE, 0, - LWS_WRITE_HTTP_FINAL); - - /* always close after sending it */ - return -1; - } -#endif -#if defined(LWS_WITH_HTTP_PROXY) - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { - char *px = buf + LWS_PRE; - int lenx = sizeof(buf) - LWS_PRE; - - /* - * our sink is writeable and our source has something - * to read. So read a lump of source material of - * suitable size to send or what's available, whichever - * is the smaller. - */ - wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; - if (!lws_get_child(wsi)) - break; - if (lws_http_client_read(lws_get_child(wsi), &px, - &lenx) < 0) - return -1; - break; - } -#endif - break; - -#if defined(LWS_WITH_HTTP_PROXY) - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - assert(lws_get_parent(wsi)); - if (!lws_get_parent(wsi)) - break; - lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; - lws_callback_on_writable(lws_get_parent(wsi)); - break; - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - assert(lws_get_parent(wsi)); - n = lws_write(lws_get_parent(wsi), (unsigned char *)in, - len, LWS_WRITE_HTTP); - if (n < 0) - return -1; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { - unsigned char *p, *end; - char ctype[64], ctlen = 0; - - p = (unsigned char *)buf + LWS_PRE; - end = p + sizeof(buf) - LWS_PRE; - - if (lws_add_http_header_status(lws_get_parent(wsi), - HTTP_STATUS_OK, &p, end)) - return 1; - if (lws_add_http_header_by_token(lws_get_parent(wsi), - WSI_TOKEN_HTTP_SERVER, - (unsigned char *)"libwebsockets", - 13, &p, end)) - return 1; - - ctlen = lws_hdr_copy(wsi, ctype, sizeof(ctype), - WSI_TOKEN_HTTP_CONTENT_TYPE); - if (ctlen > 0) { - if (lws_add_http_header_by_token(lws_get_parent(wsi), - WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)ctype, ctlen, &p, end)) - return 1; - } - - if (lws_finalize_http_header(lws_get_parent(wsi), &p, end)) - return 1; - - *p = '\0'; - n = lws_write(lws_get_parent(wsi), - (unsigned char *)buf + LWS_PRE, - p - ((unsigned char *)buf + LWS_PRE), - LWS_WRITE_HTTP_HEADERS); - if (n < 0) - return -1; - - break; } - -#endif - -#ifdef LWS_WITH_CGI - /* CGI IO events (POLLIN/OUT) appear here, our default policy is: - * - * - POST data goes on subprocess stdin - * - subprocess stdout goes on http via writeable callback - * - subprocess stderr goes to the logs - */ - case LWS_CALLBACK_CGI: - args = (struct lws_cgi_args *)in; - switch (args->ch) { /* which of stdin/out/err ? */ - case LWS_STDIN: - /* TBD stdin rx flow control */ - break; - case LWS_STDOUT: - /* quench POLLIN on STDOUT until MASTER got writeable */ - lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); - wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; - /* when writing to MASTER would not block */ - lws_callback_on_writable(wsi); - break; - case LWS_STDERR: - n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]); - if (n < 0) - break; - n = read(n, buf, sizeof(buf) - 2); - if (n > 0) { - if (buf[n - 1] != '\n') - buf[n++] = '\n'; - buf[n] = '\0'; - lwsl_notice("CGI-stderr: %s\n", buf); - } - break; - } - break; - - case LWS_CALLBACK_CGI_TERMINATED: - lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n", - wsi->http.cgi->explicitly_chunked, - (uint64_t)wsi->http.cgi->content_length); - if (!wsi->http.cgi->explicitly_chunked && - !wsi->http.cgi->content_length) { - /* send terminating chunk */ - lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n"); - wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; - lws_callback_on_writable(wsi); - lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); - break; - } - return -1; - - case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ - args = (struct lws_cgi_args *)in; - args->data[args->len] = '\0'; - if (!args->stdwsi[LWS_STDIN]) - return -1; - n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]); - if (n < 0) - return -1; - -#if defined(LWS_WITH_ZLIB) - if (wsi->http.cgi->gzip_inflate) { - /* gzip handling */ - - if (!wsi->http.cgi->gzip_init) { - lwsl_err("inflating gzip\n"); - - memset(&wsi->http.cgi->inflate, 0, sizeof(wsi->http.cgi->inflate)); - - if (inflateInit2(&wsi->http.cgi->inflate, 16 + 15) != Z_OK) { - lwsl_err("%s: iniflateInit failed\n", __func__); - return -1; - } - - wsi->http.cgi->gzip_init = 1; - } - - wsi->http.cgi->inflate.next_in = args->data; - wsi->http.cgi->inflate.avail_in = args->len; - - do { - - wsi->http.cgi->inflate.next_out = wsi->http.cgi->inflate_buf; - wsi->http.cgi->inflate.avail_out = sizeof(wsi->http.cgi->inflate_buf); - - n = inflate(&wsi->http.cgi->inflate, Z_SYNC_FLUSH); - lwsl_err("inflate: %d\n", n); - switch (n) { - case Z_NEED_DICT: - case Z_STREAM_ERROR: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&wsi->http.cgi->inflate); - wsi->http.cgi->gzip_init = 0; - lwsl_err("zlib error inflate %d\n", n); - return -1; - } - - if (wsi->http.cgi->inflate.avail_out != sizeof(wsi->http.cgi->inflate_buf)) { - int written; - -// lwsl_hexdump_notice(wsi->http.cgi->inflate_buf, - // sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out); - - written = write(args->stdwsi[LWS_STDIN]->desc.filefd, wsi->http.cgi->inflate_buf, - sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out); - - if (written != (int)(sizeof(wsi->http.cgi->inflate_buf) - wsi->http.cgi->inflate.avail_out)) { - lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " - "sent %d only %d went", n, args->len); - } - lwsl_err("send inflated on fd %d says %d\n", args->stdwsi[LWS_STDIN]->desc.filefd, written); - - if (n == Z_STREAM_END) { - lwsl_err("gzip inflate end\n"); - inflateEnd(&wsi->http.cgi->inflate); - wsi->http.cgi->gzip_init = 0; - - //compatible_close(args->stdwsi[LWS_STDIN]->desc.filefd); - //args->stdwsi[LWS_STDIN]->desc.filefd = -1; - break; - } - - } else - break; - - if (wsi->http.cgi->inflate.avail_out) - break; - - } while (1); - - return args->len; - } -#endif /* WITH_ZLIB */ - - n = write(n, args->data, args->len); -// lwsl_hexdump_notice(args->data, args->len); - if (n < args->len) - lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " - "sent %d only %d went", n, args->len); - - if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] && - args->stdwsi[LWS_STDIN]->desc.filefd > 0) { - wsi->http.cgi->post_in_expected -= n; - if (!wsi->http.cgi->post_in_expected) { - struct lws *siwsi = args->stdwsi[LWS_STDIN]; - - lwsl_debug("%s: expected POST in end: " - "closing stdin wsi %p, fd %d\n", - __func__, siwsi, siwsi->desc.sockfd); - - __remove_wsi_socket_from_fds(siwsi); - lwsi_set_state(siwsi, LRS_DEAD_SOCKET); - siwsi->socket_is_permanently_unusable = 1; - lws_remove_child_from_any_parent(siwsi); - if (wsi->context->event_loop_ops-> - close_handle_manually) { - wsi->context->event_loop_ops-> - close_handle_manually(siwsi); - siwsi->told_event_loop_closed = 1; - } else { - compatible_close(siwsi->desc.sockfd); - __lws_free_wsi(siwsi); - } - wsi->http.cgi->pipe_fds[LWS_STDIN][1] = -1; - - args->stdwsi[LWS_STDIN] = NULL; - } - } - - return n; -#endif /* WITH_CGI */ -#endif /* ROLE_ H1 / H2 */ - case LWS_CALLBACK_SSL_INFO: - si = in; - - (void)si; - lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", - si->where, si->ret); - break; - -#if LWS_MAX_SMP > 1 - case LWS_CALLBACK_GET_THREAD_ID: - return (int)(unsigned long long)pthread_self(); -#endif - - default: - break; - } - - return 0; -} /* list of supported protocols and callbacks */ @@ -1142,6 +801,7 @@ lws_destroy_event_pipe(struct lws *wsi) if (wsi->context->event_loop_ops->wsi_logical_close) { wsi->context->event_loop_ops->wsi_logical_close(wsi); lws_plat_pipe_close(wsi); + wsi->context->count_wsi_allocated--; return; } @@ -1668,13 +1328,14 @@ lws_vhost_destroy1(struct lws_vhost *vh) assert(v->lserv_wsi == NULL); v->lserv_wsi = vh->lserv_wsi; + lwsl_notice("%s: listen skt from %s to %s\n", + __func__, vh->name, v->name); + if (v->lserv_wsi) { lws_vhost_unbind_wsi(vh->lserv_wsi); lws_vhost_bind_wsi(v, v->lserv_wsi); } - lwsl_notice("%s: listen skt from %s to %s\n", - __func__, vh->name, v->name); break; } } lws_end_foreach_ll(v, vhost_next); diff --git a/lib/core/dummy-callback.c b/lib/core/dummy-callback.c new file mode 100644 index 0000000000000000000000000000000000000000..b5fa7656a3d308090665d5ee168246d5a41f5053 --- /dev/null +++ b/lib/core/dummy-callback.c @@ -0,0 +1,605 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#if defined(LWS_WITH_HTTP_PROXY) +static int +proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp, + int temp_len, int index, unsigned char **p, unsigned char *end) +{ + int n = lws_hdr_total_length(par, index); + + if (n < 1) { + lwsl_debug("%s: no index %d:\n", __func__, index); + return 0; + } + + if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0) + return -1; + + lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp); + + if (lws_add_http_header_by_token(wsi, index, temp, n, p, end)) + return -1; + + return 0; +} + +static int +stream_close(struct lws *wsi) +{ + char buf[LWS_PRE + 6], *out = buf + LWS_PRE; + + if (wsi->http.did_stream_close) + return 0; + + wsi->http.did_stream_close = 1; + + if (wsi->http2_substream) { + if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0, + LWS_WRITE_HTTP_FINAL) < 0) { + lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n", + __func__); + + return -1; + } + } else { + *out++ = '0'; + *out++ = '\x0d'; + *out++ = '\x0a'; + *out++ = '\x0d'; + *out++ = '\x0a'; + + if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5, + LWS_WRITE_HTTP_FINAL) < 0) { + lwsl_err("%s: COMPL_CLIENT_HTTP: " + "h2 final write failed\n", __func__); + + return -1; + } + } + + return 0; +} + +#endif + +LWS_VISIBLE int +lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_ssl_info *si; +#ifdef LWS_WITH_CGI + struct lws_cgi_args *args; +#endif +#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) + char buf[8192]; + int n; +#endif +#if defined(LWS_WITH_HTTP_PROXY) + unsigned char **p, *end; + struct lws *parent; +#endif + + switch (reason) { +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + case LWS_CALLBACK_HTTP: +#ifndef LWS_NO_SERVER + if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) + return -1; + + if (lws_http_transaction_completed(wsi)) +#endif + return -1; + break; +#if !defined(LWS_NO_SERVER) + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + if (lws_http_transaction_completed(wsi)) + return -1; + break; +#endif + + case LWS_CALLBACK_HTTP_WRITEABLE: +#ifdef LWS_WITH_CGI + if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | + LWS_CB_REASON_AUX_BF__CGI)) { + n = lws_cgi_write_split_stdout_headers(wsi); + if (n < 0) { + lwsl_debug("AUX_BF__CGI forcing close\n"); + return -1; + } + if (!n) + lws_rx_flow_control( + wsi->http.cgi->stdwsi[LWS_STDOUT], 1); + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) + wsi->reason_bf &= + ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; + else + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; + + if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) + return -1; + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { + if (!wsi->http2_substream) { + memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); + lwsl_debug("writing chunk term and exiting\n"); + n = lws_write(wsi, (unsigned char *)buf + + LWS_PRE, 5, LWS_WRITE_HTTP); + } else + n = lws_write(wsi, (unsigned char *)buf + + LWS_PRE, 0, + LWS_WRITE_HTTP_FINAL); + + /* always close after sending it */ + return -1; + } +#endif +#if defined(LWS_WITH_HTTP_PROXY) + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) { + + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS; + + lwsl_notice("%s: %p: issuing proxy headers\n", + __func__, wsi); + n = lws_write(wsi, wsi->http.pending_return_headers + LWS_PRE, + wsi->http.pending_return_headers_len, + LWS_WRITE_HTTP_HEADERS); + + lws_free_set_NULL(wsi->http.pending_return_headers); + + if (n < 0) { + lwsl_err("%s: EST_CLIENT_HTTP: write failed\n", + __func__); + return -1; + } + lws_callback_on_writable(wsi); + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) { + char *px = buf + LWS_PRE; + int lenx = sizeof(buf) - LWS_PRE - 32; + + /* + * our sink is writeable and our source has something + * to read. So read a lump of source material of + * suitable size to send or what's available, whichever + * is the smaller. + */ + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY; + if (!lws_get_child(wsi)) + break; + + /* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */ + if (lws_http_client_read(lws_get_child(wsi), &px, + &lenx) < 0) { + lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: " + "client closed\n", __func__); + + stream_close(wsi); + + return -1; + } + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) { + lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY_TRANS_END\n", + __func__); + + wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END; + + if (stream_close(wsi)) + return -1; + + if (lws_http_transaction_completed(wsi)) + return -1; + } +#endif + break; + +#if defined(LWS_WITH_HTTP_PROXY) + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + assert(lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY; + lws_callback_on_writable(lws_get_parent(wsi)); + break; + + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: { + char *out = buf + LWS_PRE; + + assert(lws_get_parent(wsi)); + + if (wsi->http.proxy_parent_chunked) { + + if (len > sizeof(buf) - LWS_PRE - 16) { + lwsl_err("oversize buf %d %d\n", (int)len, + (int)sizeof(buf) - LWS_PRE - 16); + return -1; + } + + /* + * this only needs dealing with on http/1.1 to allow + * pipelining + */ + n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len); + out += n; + memcpy(out, in, len); + out += len; + *out++ = '\x0d'; + *out++ = '\x0a'; + + n = lws_write(lws_get_parent(wsi), + (unsigned char *)buf + LWS_PRE, + len + n + 2, LWS_WRITE_HTTP); + } else + n = lws_write(lws_get_parent(wsi), (unsigned char *)in, + len, LWS_WRITE_HTTP); + if (n < 0) + return -1; + break; } + + + /* this handles the proxy case... */ + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: { + unsigned char *start, *p, *end; + + /* + * We want to proxy these headers, but we are being called + * at the point the onward client was established, which is + * unrelated to the state or writability of our proxy + * connection. + * + * Therefore produce the headers using the onward client ah + * while we have it, and stick them on the output buflist to be + * written on the proxy connection as soon as convenient. + */ + + parent = lws_get_parent(wsi); + + if (!parent) + return 0; + + start = p = (unsigned char *)buf + LWS_PRE; + end = p + sizeof(buf) - LWS_PRE - 256; + + if (lws_add_http_header_status(lws_get_parent(wsi), + lws_http_client_http_response(wsi), &p, end)) + return 1; + + /* + * copy these headers from the client connection to the parent + */ + + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end); + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end); + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_ETAG, &p, end); + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end); + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end); + proxy_header(parent, wsi, end, 256, + WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end); + + if (!parent->http2_substream) + if (lws_add_http_header_by_token(parent, + WSI_TOKEN_CONNECTION, (unsigned char *)"close", + 5, &p, end)) + return -1; + + /* + * We proxy using h1 only atm, and strip any chunking so it + * can go back out on h2 just fine. + * + * However if we are actually going out on h1, we need to add + * our own chunking since we still don't know the size. + */ + + if (!parent->http2_substream && + !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + lwsl_debug("downstream parent chunked\n"); + if (lws_add_http_header_by_token(parent, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", 7, &p, end)) + return -1; + + wsi->http.proxy_parent_chunked = 1; + } + + if (lws_finalize_http_header(parent, &p, end)) + return 1; + + parent->http.pending_return_headers_len = lws_ptr_diff(p, start); + parent->http.pending_return_headers = + lws_malloc(parent->http.pending_return_headers_len + LWS_PRE, + "return proxy headers"); + if (!parent->http.pending_return_headers) + return -1; + + memcpy(parent->http.pending_return_headers + LWS_PRE, start, + parent->http.pending_return_headers_len); + + parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS; + + lwsl_notice("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: prepared headers\n", __func__); + lws_callback_on_writable(parent); + + break; } + + case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: + lwsl_info("%s: COMPLETED_CLIENT_HTTP: %p (parent %p)\n", + __func__, wsi, lws_get_parent(wsi)); + if (!lws_get_parent(wsi)) + break; + lws_get_parent(wsi)->reason_bf |= + LWS_CB_REASON_AUX_BF__PROXY_TRANS_END; + lws_callback_on_writable(lws_get_parent(wsi)); + break; + + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: + if (!lws_get_parent(wsi)) + break; + lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__); + lws_set_timeout(lws_get_parent(wsi), LWS_TO_KILL_ASYNC, + PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE); + break; + + case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: + parent = lws_get_parent(wsi); + + if (!parent) + break; + + p = (unsigned char **)in; + end = (*p) + len; + + /* + * copy these headers from the parent request to the client + * connection's request + */ + + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HOST, p, end); + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HTTP_ETAG, p, end); + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end); + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end); + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end); + proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf), + WSI_TOKEN_HTTP_CACHE_CONTROL, p, end); + + buf[0] = '\0'; + lws_get_peer_simple(parent, buf, sizeof(buf)); + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR, + (unsigned char *)buf, strlen(buf), p, end)) + return -1; + + break; + +#endif + +#ifdef LWS_WITH_CGI + /* CGI IO events (POLLIN/OUT) appear here, our default policy is: + * + * - POST data goes on subprocess stdin + * - subprocess stdout goes on http via writeable callback + * - subprocess stderr goes to the logs + */ + case LWS_CALLBACK_CGI: + args = (struct lws_cgi_args *)in; + switch (args->ch) { /* which of stdin/out/err ? */ + case LWS_STDIN: + /* TBD stdin rx flow control */ + break; + case LWS_STDOUT: + /* quench POLLIN on STDOUT until MASTER got writeable */ + lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; + /* when writing to MASTER would not block */ + lws_callback_on_writable(wsi); + break; + case LWS_STDERR: + n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]); + if (n < 0) + break; + n = read(n, buf, sizeof(buf) - 2); + if (n > 0) { + if (buf[n - 1] != '\n') + buf[n++] = '\n'; + buf[n] = '\0'; + lwsl_notice("CGI-stderr: %s\n", buf); + } + break; + } + break; + + case LWS_CALLBACK_CGI_TERMINATED: + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n", + wsi->http.cgi->explicitly_chunked, + (uint64_t)wsi->http.cgi->content_length); + if (!wsi->http.cgi->explicitly_chunked && + !wsi->http.cgi->content_length) { + /* send terminating chunk */ + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n"); + wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; + lws_callback_on_writable(wsi); + lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); + break; + } + return -1; + + case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */ + args = (struct lws_cgi_args *)in; + args->data[args->len] = '\0'; + if (!args->stdwsi[LWS_STDIN]) + return -1; + n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]); + if (n < 0) + return -1; + +#if defined(LWS_WITH_ZLIB) + if (wsi->http.cgi->gzip_inflate) { + /* gzip handling */ + + if (!wsi->http.cgi->gzip_init) { + lwsl_err("inflating gzip\n"); + + memset(&wsi->http.cgi->inflate, 0, sizeof(wsi->http.cgi->inflate)); + + if (inflateInit2(&wsi->http.cgi->inflate, 16 + 15) != Z_OK) { + lwsl_err("%s: iniflateInit failed\n", __func__); + return -1; + } + + wsi->http.cgi->gzip_init = 1; + } + + wsi->http.cgi->inflate.next_in = args->data; + wsi->http.cgi->inflate.avail_in = args->len; + + do { + + wsi->http.cgi->inflate.next_out = + wsi->http.cgi->inflate_buf; + wsi->http.cgi->inflate.avail_out = + sizeof(wsi->http.cgi->inflate_buf); + + n = inflate(&wsi->http.cgi->inflate, + Z_SYNC_FLUSH); + + switch (n) { + case Z_NEED_DICT: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&wsi->http.cgi->inflate); + wsi->http.cgi->gzip_init = 0; + lwsl_err("zlib error inflate %d\n", n); + return -1; + } + + if (wsi->http.cgi->inflate.avail_out != + sizeof(wsi->http.cgi->inflate_buf)) { + int written; + + written = write(args->stdwsi[LWS_STDIN]->desc.filefd, + wsi->http.cgi->inflate_buf, + sizeof(wsi->http.cgi->inflate_buf) - + wsi->http.cgi->inflate.avail_out); + + if (written != (int)( + sizeof(wsi->http.cgi->inflate_buf) - + wsi->http.cgi->inflate.avail_out)) { + lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + } + + if (n == Z_STREAM_END) { + lwsl_err("gzip inflate end\n"); + inflateEnd(&wsi->http.cgi->inflate); + wsi->http.cgi->gzip_init = 0; + break; + } + + } else + break; + + if (wsi->http.cgi->inflate.avail_out) + break; + + } while (1); + + return args->len; + } +#endif /* WITH_ZLIB */ + + n = write(n, args->data, args->len); +// lwsl_hexdump_notice(args->data, args->len); + if (n < args->len) + lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + + if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] && + args->stdwsi[LWS_STDIN]->desc.filefd > 0) { + wsi->http.cgi->post_in_expected -= n; + if (!wsi->http.cgi->post_in_expected) { + struct lws *siwsi = args->stdwsi[LWS_STDIN]; + + lwsl_debug("%s: expected POST in end: " + "closing stdin wsi %p, fd %d\n", + __func__, siwsi, siwsi->desc.sockfd); + + __remove_wsi_socket_from_fds(siwsi); + lwsi_set_state(siwsi, LRS_DEAD_SOCKET); + siwsi->socket_is_permanently_unusable = 1; + lws_remove_child_from_any_parent(siwsi); + if (wsi->context->event_loop_ops-> + close_handle_manually) { + wsi->context->event_loop_ops-> + close_handle_manually(siwsi); + siwsi->told_event_loop_closed = 1; + } else { + compatible_close(siwsi->desc.sockfd); + __lws_free_wsi(siwsi); + } + wsi->http.cgi->pipe_fds[LWS_STDIN][1] = -1; + + args->stdwsi[LWS_STDIN] = NULL; + } + } + + return n; +#endif /* WITH_CGI */ +#endif /* ROLE_ H1 / H2 */ + case LWS_CALLBACK_SSL_INFO: + si = in; + + (void)si; + lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", + si->where, si->ret); + break; + +#if LWS_MAX_SMP > 1 + case LWS_CALLBACK_GET_THREAD_ID: + return (int)(unsigned long long)pthread_self(); +#endif + + default: + break; + } + + return 0; +} diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 91ce98c400e2b56c400735ae49280f7a4dd8f3c3..21a63e09e8409ff6babe8212aadc0b4ea76b8c7f 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -53,6 +53,7 @@ static const char * const log_level_names[] = { "CLIENT", "LATENCY", "USER", + "THREAD", "?", "?" }; @@ -374,6 +375,15 @@ __lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) // lws_dll_dump(&pt->dll_head_hrtimer, "after set_timer_usec"); } +LWS_VISIBLE lws_usec_t +lws_now_usecs(void) +{ + struct timeval now; + + gettimeofday(&now, NULL); + return (now.tv_sec * 1000000ll) + now.tv_usec; +} + LWS_VISIBLE void lws_set_timer_usecs(struct lws *wsi, lws_usec_t usecs) { @@ -867,12 +877,16 @@ just_kill_connection: lwsl_debug("%s: real just_kill_connection: %p (sockfd %d)\n", __func__, wsi, wsi->desc.sockfd); -#ifdef LWS_WITH_HTTP_PROXY +#ifdef LWS_WITH_HUBBUB if (wsi->http.rw) { lws_rewrite_destroy(wsi->http.rw); wsi->http.rw = NULL; } #endif + + if (wsi->http.pending_return_headers) + lws_free_set_NULL(wsi->http.pending_return_headers); + /* * we won't be servicing or receiving anything further from this guy * delete socket from the internal poll list if still present @@ -2029,6 +2043,7 @@ static const char * const colours[] = { "[33m", /* LLL_CLIENT */ "[33;1m", /* LLL_LATENCY */ "[30;1m", /* LLL_USER */ + "[31m", /* LLL_THREAD */ }; LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) @@ -2394,7 +2409,7 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, const char **path) { const char *end; - static const char *slash = "/"; + char unix_skt = 0; /* cut up the location into address, port and path */ *prot = p; @@ -2408,32 +2423,32 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, *p = '\0'; p += 3; } + if (*p == '+') /* unix skt */ + unix_skt = 1; + *ads = p; if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) *port = 80; else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) *port = 443; - if (*p == '[') - { - ++(*ads); - while (*p && *p != ']') - p++; - if (*p) - *p++ = '\0'; - } - else - { - while (*p && *p != ':' && *p != '/') - p++; - } + if (*p == '[') { + ++(*ads); + while (*p && *p != ']') + p++; + if (*p) + *p++ = '\0'; + } else + while (*p && *p != ':' && (unix_skt || *p != '/')) + p++; + if (*p == ':') { *p++ = '\0'; *port = atoi(p); while (*p && *p != '/') p++; } - *path = slash; + *path = "/"; if (*p) { *p++ = '\0'; if (*p) diff --git a/lib/core/output.c b/lib/core/output.c index d43735ade00ef421275e3aabdbbfbc0d0fc3ece2..9baa1d9d7f0a9362983c0f4a0483736fd8460444 100644 --- a/lib/core/output.c +++ b/lib/core/output.c @@ -251,6 +251,10 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len) n = recv(wsi->desc.sockfd, (char *)buf, len, 0); if (n >= 0) { + + if (!n && wsi->unix_skt) + return LWS_SSL_CAPABLE_ERROR; + if (wsi->vhost) wsi->vhost->conn_stats.rx += n; lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n); diff --git a/lib/core/private.h b/lib/core/private.h index af9741fd7a7352353f5eb712ca5f3aae9602bd2a..c25dcc45f76170c0b45c40b27b0492f9fba9c968 100644 --- a/lib/core/private.h +++ b/lib/core/private.h @@ -959,6 +959,7 @@ struct lws { unsigned int on_same_vh_list:1; unsigned int handling_404:1; unsigned int protocol_bind_balance:1; + unsigned int unix_skt:1; unsigned int could_have_pending:1; /* detect back-to-back writes */ unsigned int outer_will_close:1; @@ -1156,7 +1157,8 @@ user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, void *in, size_t len); LWS_EXTERN int -lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd); +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, + int unix_skt); LWS_EXTERN int lws_plat_check_connection_error(struct lws *wsi); @@ -1493,7 +1495,7 @@ void lws_peer_dump_from_wsi(struct lws *wsi); #endif -#ifdef LWS_WITH_HTTP_PROXY +#ifdef LWS_WITH_HUBBUB hubbub_error html_parser_cb(const hubbub_token *token, void *pw); #endif diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 61910c7c1d0692691b7a7ad6ff51decdb8e42050..29bc6aa4c3b95a092d75405674d7e713b6e036b8 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -974,6 +974,26 @@ cleanup: } #endif +static int +rops_close_kill_connection_h1(struct lws *wsi, enum lws_close_status reason) +{ +#if defined(LWS_WITH_HTTP_PROXY) + struct lws *wsi_eff = lws_client_wsi_effective(wsi); + + if (!wsi_eff->http.proxy_clientside) + return 0; + + wsi_eff->http.proxy_clientside = 0; + + if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff, + LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi_eff->user_space, NULL, 0)) + return 0; +#endif + return 0; +} + + struct lws_role_ops role_ops_h1 = { /* role name */ "h1", /* alpn id */ "http/1.1", @@ -993,7 +1013,7 @@ struct lws_role_ops role_ops_h1 = { /* alpn_negotiated */ rops_alpn_negotiated_h1, /* close_via_role_protocol */ NULL, /* close_role */ NULL, - /* close_kill_connection */ NULL, + /* close_kill_connection */ rops_close_kill_connection_h1, /* destroy_role */ rops_destroy_role_h1, #if !defined(LWS_NO_SERVER) /* adoption_bind */ rops_adoption_bind_h1, diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 38e53d578d78c5d6a1df09bcfedde700507ca201..be4b0e1c6d73fa20a758b1742882b6b93a7baf7d 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -219,6 +219,8 @@ bail1: parent_wsi->h2.child_list = wsi->h2.sibling_list; parent_wsi->h2.child_count--; + vh->context->count_wsi_allocated--; + if (wsi->user_space) lws_free_set_NULL(wsi->user_space); vh->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); @@ -381,6 +383,9 @@ lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason) struct lws_h2_netconn *h2n = nwsi->h2.h2n; struct lws_h2_protocol_send *pps; + if (!h2n) + return 0; + if (h2n->type == LWS_H2_FRAME_TYPE_COUNT) return 0; diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 2c6b2675d8bcbc5ccb05249e13ccf32567cc636c..fa1fd93fa86e7d0aedbe48758f75fc112f280487 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -578,6 +578,19 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason) { struct lws *wsi2; +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) { + struct lws *wsi_eff = lws_client_wsi_effective(wsi); + + wsi->http.proxy_clientside = 0; + + if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff, + LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi_eff->user_space, NULL, 0)) + wsi->http.proxy_clientside = 0; + } +#endif + if (wsi->http2_substream && wsi->h2_stream_carries_ws) lws_h2_rst_stream(wsi, 0, "none"); @@ -960,7 +973,8 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) * states. In those cases we will hear about * END_STREAM going out in the POLLOUT handler. */ - if (n || w->h2.send_END_STREAM) { + if (!w->h2.pending_status_body && + (n || w->h2.send_END_STREAM)) { lwsl_info("closing stream after h2 action\n"); lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "h2 end stream"); diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 737e56b4dc10d4fb5ebfcce7e4ab2fd357d9e668..88c7fb1eda59976fbe3152d833adfc5229862b4b 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -37,8 +37,13 @@ lws_client_connect_2(struct lws *wsi) ssize_t plen = 0; #endif struct addrinfo *result; +#if defined(LWS_WITH_UNIX_SOCK) + struct sockaddr_un sau; + char unix_skt = 0; +#endif const char *ads; sockaddr46 sa46; + const struct sockaddr *psa; int n, port; const char *cce = "", *iface; const char *meth = NULL; @@ -183,6 +188,29 @@ create_new_conn: lws_vhost_unlock(wsi->vhost); } + /* + * unix socket destination? + */ + + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); +#if defined(LWS_WITH_UNIX_SOCK) + if (*ads == '+') { + ads++; + memset(&sau, 0, sizeof(sau)); + sau.sun_family = AF_UNIX; + strncpy(sau.sun_path, ads, sizeof(sau.sun_path)); + sau.sun_path[sizeof(sau.sun_path) - 1] = '\0'; + + lwsl_info("%s: Unix skt: %s\n", __func__, ads); + + if (sau.sun_path[0] == '@') + sau.sun_path[0] = '\0'; + + unix_skt = 1; + goto ads_known; + } +#endif + /* * start off allowing ipv6 on connection if vhost allows it */ @@ -341,6 +369,10 @@ create_new_conn: if (result) freeaddrinfo(result); +#if defined(LWS_WITH_UNIX_SOCK) +ads_known: +#endif + /* now we decided on ipv4 or ipv6, set the port */ if (!lws_socket_is_valid(wsi->desc.sockfd)) { @@ -351,12 +383,21 @@ create_new_conn: goto oom4; } +#if defined(LWS_WITH_UNIX_SOCK) + if (unix_skt) { + wsi->unix_skt = 1; + wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + } else +#endif + { + #ifdef LWS_WITH_IPV6 if (wsi->ipv6) wsi->desc.sockfd = socket(AF_INET6, SOCK_STREAM, 0); else #endif wsi->desc.sockfd = socket(AF_INET, SOCK_STREAM, 0); + } if (!lws_socket_is_valid(wsi->desc.sockfd)) { lwsl_warn("Unable to open socket\n"); @@ -364,7 +405,12 @@ create_new_conn: goto oom4; } - if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd)) { + if (lws_plat_set_socket_options(wsi->vhost, wsi->desc.sockfd, +#if defined(LWS_WITH_UNIX_SOCK) + unix_skt)) { +#else + 0)) { +#endif lwsl_err("Failed to set wsi socket options\n"); compatible_close(wsi->desc.sockfd); cce = "set socket opts failed"; @@ -409,18 +455,29 @@ create_new_conn: } } -#ifdef LWS_WITH_IPV6 - if (wsi->ipv6) { - sa46.sa6.sin6_port = htons(port); - n = sizeof(struct sockaddr_in6); +#if defined(LWS_WITH_UNIX_SOCK) + if (unix_skt) { + psa = (const struct sockaddr *)&sau; + n = sizeof(sau); } else #endif + { - sa46.sa4.sin_port = htons(port); - n = sizeof(struct sockaddr); +#ifdef LWS_WITH_IPV6 + if (wsi->ipv6) { + sa46.sa6.sin6_port = htons(port); + n = sizeof(struct sockaddr_in6); + psa = (const struct sockaddr *)&sa46; + } else +#endif + { + sa46.sa4.sin_port = htons(port); + n = sizeof(struct sockaddr); + psa = (const struct sockaddr *)&sa46; + } } - if (connect(wsi->desc.sockfd, (const struct sockaddr *)&sa46, n) == -1 || + if (connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n) == -1 || LWS_ERRNO == LWS_EISCONN) { if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS || @@ -729,7 +786,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port, return *pwsi; } -#ifdef LWS_WITH_HTTP_PROXY +#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) hubbub_error html_parser_cb(const hubbub_token *token, void *pw) { diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index 6732625401b41b57d7666d55f456c026a10c1e63..46a5afe9a822ba6b206e0d5ff94ccdf7394c73cd 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -882,7 +882,7 @@ lws_client_interpret_server_handshake(struct lws *wsi) if (!strncmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE), "text/html", 9)) - wsi->http.perform_rewrite = 1; + wsi->http.perform_rewrite = 0; } #endif @@ -993,7 +993,9 @@ bail2: } wsi->already_did_cce = 1; - lwsl_info("closing connection due to bail2 connection error\n"); + lwsl_info("closing connection (prot %s) " + "due to bail2 connection error: %s\n", wsi->protocol ? + wsi->protocol->name : "unknown", cce); /* closing will free up his parsing allocations */ lws_close_free_wsi(wsi, close_reason, "c hs interp"); @@ -1065,6 +1067,9 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) p += sprintf(p, "Pragma: no-cache\x0d\x0a" "Cache-Control: no-cache\x0d\x0a"); + if (!wsi->client_pipeline) + p += sprintf(p, "connection: close\x0d\x0a"); + p += sprintf(p, "Host: %s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); @@ -1196,7 +1201,7 @@ spin_chunks: wsi->chunk_remaining < n) n = wsi->chunk_remaining; -#ifdef LWS_WITH_HTTP_PROXY +#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) /* hubbub */ if (wsi->http.perform_rewrite) lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n); diff --git a/lib/roles/http/header.c b/lib/roles/http/header.c index 85939343ab29431a7d4f68740303bd43822e7d25..5bf3d371dd8f5d6c6d8cb279266050b739c1a386 100644 --- a/lib/roles/http/header.c +++ b/lib/roles/http/header.c @@ -384,7 +384,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code, #if defined(LWS_WITH_HTTP2) if (wsi->http2_substream) { - unsigned char *body = p + 512; + char *body = (char *)start + context->pt_serv_buf_size - 512; /* * for HTTP/2, the headers must be sent separately, since they @@ -398,7 +398,8 @@ lws_return_http_status(struct lws *wsi, unsigned int code, * * Solve it by writing the headers now... */ - m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + m = lws_write(wsi, start, lws_ptr_diff(p, start), + LWS_WRITE_HTTP_HEADERS); if (m != lws_ptr_diff(p, start)) return 1; @@ -407,8 +408,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code, * handle_POLLOUT */ - len = sprintf((char *)body, - "<html><body><h1>%u</h1>%s</body></html>", + len = sprintf(body, "<html><body><h1>%u</h1>%s</body></html>", code, html_body); wsi->http.tx_content_length = len; wsi->http.tx_content_remain = len; @@ -418,8 +418,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code, if (!wsi->h2.pending_status_body) return -1; - strcpy(wsi->h2.pending_status_body + LWS_PRE, - (const char *)body); + strcpy(wsi->h2.pending_status_body + LWS_PRE, body); lws_callback_on_writable(wsi); return 0; diff --git a/lib/roles/http/private.h b/lib/roles/http/private.h index 366435396d7dcd7583d1054ade0c04c1d02ecf19..ff8b0581cc693291c92cdf921ef919be094be7e0 100644 --- a/lib/roles/http/private.h +++ b/lib/roles/http/private.h @@ -22,7 +22,7 @@ * enabled */ -#if defined(LWS_WITH_HTTP_PROXY) +#if defined(LWS_WITH_HUBBUB) #include <hubbub/hubbub.h> #include <hubbub/parser.h> #endif @@ -137,7 +137,7 @@ struct allocated_headers { -#if defined(LWS_WITH_HTTP_PROXY) +#if defined(LWS_WITH_HUBBUB) struct lws_rewrite { hubbub_parser *parser; hubbub_parser_optparams params; @@ -202,6 +202,9 @@ struct lws_access_log { struct _lws_http_mode_related { struct lws *new_wsi_list; + unsigned char *pending_return_headers; + size_t pending_return_headers_len; + #if defined(LWS_WITH_HTTP_PROXY) struct lws_rewrite *rw; #endif @@ -238,9 +241,12 @@ struct _lws_http_mode_related { #if defined(LWS_WITH_HTTP_PROXY) unsigned int perform_rewrite:1; + unsigned int proxy_clientside:1; + unsigned int proxy_parent_chunked:1; #endif unsigned int deferred_transaction_completed:1; unsigned int content_length_explicitly_zero:1; + unsigned int did_stream_close:1; }; diff --git a/lib/roles/http/server/access-log.c b/lib/roles/http/server/access-log.c index dc016a5be754637ba9163574c7ba103b6602b4de..07e6207fe5bb0769f2da4c7d7bc399a43ed08089 100644 --- a/lib/roles/http/server/access-log.c +++ b/lib/roles/http/server/access-log.c @@ -93,7 +93,7 @@ lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int uri_len, int met pa, da, me, uri, hver[wsi->http.request_version]); - lwsl_notice("%s\n", wsi->http.access_log.header_log); + //lwsl_notice("%s\n", wsi->http.access_log.header_log); l = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_USER_AGENT); if (l) { diff --git a/lib/roles/http/server/rewrite.c b/lib/roles/http/server/rewrite.c index 1afcce151a54d3a1fa1162d70554dc9b82d43df2..3b923ecefe84935e850ee3545e2e6fe46b7d93a6 100644 --- a/lib/roles/http/server/rewrite.c +++ b/lib/roles/http/server/rewrite.c @@ -1,5 +1,6 @@ #include "core/private.h" +#if defined(LWS_WITH_HUBBUB) LWS_EXTERN struct lws_rewrite * lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to) @@ -50,3 +51,4 @@ lws_rewrite_destroy(struct lws_rewrite *r) lws_free(r); } +#endif diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 8cff753bade430fc9c525ddcebc45af2b5e1430c..1bd76938d636b5e226c8704a0feffeab397d2dfc 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -223,7 +223,7 @@ done_list: } #endif #endif - lws_plat_set_socket_options(vhost, sockfd); + lws_plat_set_socket_options(vhost, sockfd, 0); is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface); /* @@ -236,15 +236,24 @@ done_list: compatible_close(sockfd); goto deal; } - vhost->listen_port = is; - - lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is); wsi = lws_zalloc(sizeof(struct lws), "listen wsi"); if (wsi == NULL) { lwsl_err("Out of mem\n"); goto bail; } + +#ifdef LWS_WITH_UNIX_SOCK + if (!LWS_UNIX_SOCK_ENABLED(vhost)) +#endif + { + wsi->unix_skt = 1; + vhost->listen_port = is; + + lwsl_debug("%s: lws_socket_bind says %d\n", __func__, + is); + } + wsi->context = vhost->context; wsi->desc.sockfd = sockfd; lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen); @@ -327,7 +336,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername) vhost = context->vhost_list; while (vhost) { m = (int)strlen(vhost->name); - if (port == vhost->listen_port && + if (port && port == vhost->listen_port && m <= (colon - 2) && servername[colon - m - 1] == '.' && !strncmp(vhost->name, servername + colon - m, m)) { @@ -342,7 +351,7 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername) vhost = context->vhost_list; while (vhost) { - if (port == vhost->listen_port) { + if (port && port == vhost->listen_port) { lwsl_info("%s: vhost match to %s based on port %d\n", __func__, vhost->name, port); return vhost; @@ -784,7 +793,7 @@ lws_unauthorised_basic_auth(struct lws *wsi) { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; unsigned char *start = pt->serv_buf + LWS_PRE, - *p = start, *end = p + 512; + *p = start, *end = p + 2048; char buf[64]; int n; @@ -1011,7 +1020,7 @@ lws_http_action(struct lws *wsi) * URI from the host: header and ignore the path part */ unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, - *end = p + 512; + *end = p + wsi->context->pt_serv_buf_size - LWS_PRE; if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) goto bail_nuke_ah; @@ -1077,9 +1086,10 @@ lws_http_action(struct lws *wsi) (hit->origin_protocol != LWSMPRO_CGI && hit->origin_protocol != LWSMPRO_CALLBACK)) { unsigned char *start = pt->serv_buf + LWS_PRE, - *p = start, *end = p + 512; + *p = start, *end = p + wsi->context->pt_serv_buf_size - + LWS_PRE - 512; - lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin); + lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin); /* > at start indicates deal with by redirect */ if (hit->origin_protocol == LWSMPRO_REDIR_HTTP || @@ -1162,15 +1172,21 @@ lws_http_action(struct lws *wsi) * The mount is a reverse proxy? */ + // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol); + if (hit->origin_protocol == LWSMPRO_HTTPS || hit->origin_protocol == LWSMPRO_HTTP) { struct lws_client_connect_info i; - char ads[96], rpath[256], *pcolon, *pslash, *p; + struct lws *cwsi; + char ads[96], rpath[256], *pcolon, *pslash, *p, unix_skt = 0; int n, na; memset(&i, 0, sizeof(i)); i.context = lws_get_context(wsi); + if (hit->origin[0] == '+') + unix_skt = 1; + pcolon = strchr(hit->origin, ':'); pslash = strchr(hit->origin, '/'); if (!pslash) { @@ -1178,16 +1194,28 @@ lws_http_action(struct lws *wsi) hit->origin); return -1; } - if (pcolon > pslash) - pcolon = NULL; - if (pcolon) - n = pcolon - hit->origin; - else - n = pslash - hit->origin; + if (unix_skt) { + if (!pcolon) { + lwsl_err("Proxy mount origin for unix skt must " + "have address delimited by :\n"); - if (n >= (int)sizeof(ads) - 2) - n = sizeof(ads) - 2; + return -1; + } + n = lws_ptr_diff(pcolon, hit->origin); + pslash = pcolon; + } else { + if (pcolon > pslash) + pcolon = NULL; + + if (pcolon) + n = pcolon - hit->origin; + else + n = pslash - hit->origin; + + if (n >= (int)sizeof(ads) - 2) + n = sizeof(ads) - 2; + } memcpy(ads, hit->origin, n); ads[n] = '\0'; @@ -1217,25 +1245,48 @@ lws_http_action(struct lws *wsi) } } - i.path = rpath; - i.host = i.address; + if (i.address[0] != '+' || + !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)) + i.host = i.address; + else + i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST); i.origin = NULL; i.method = "GET"; + i.alpn = "http/1.1"; i.parent_wsi = wsi; - i.uri_replace_from = hit->origin; - i.uri_replace_to = hit->mountpoint; + i.pwsi = &cwsi; - lwsl_notice("proxying to %s port %d url %s, ssl %d, " + // i.uri_replace_from = hit->origin; + // i.uri_replace_to = hit->mountpoint; + + lwsl_info("proxying to %s port %d url %s, ssl %d, " "from %s, to %s\n", i.address, i.port, i.path, i.ssl_connection, i.uri_replace_from, i.uri_replace_to); if (!lws_client_connect_via_info(&i)) { lwsl_err("proxy connect fail\n"); + + /* + * ... we can't do the proxy action, but we can + * cleanly return him a 503 and a description + */ + + lws_return_http_status(wsi, + HTTP_STATUS_SERVICE_UNAVAILABLE, + "<h1>Service Temporarily Unavailable</h1>" + "The server is temporarily unable to service " + "your request due to maintenance downtime or " + "capacity problems. Please try again later."); + return 1; } + lwsl_info("%s: setting proxy clientside on %p (parent %p)\n", + __func__, cwsi, lws_get_parent(cwsi)); + cwsi->http.proxy_clientside = 1; + return 0; } #endif @@ -1531,7 +1582,8 @@ raw_transition: /* select vhost */ - if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { + if (wsi->vhost->listen_port && + lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) { struct lws_vhost *vhost = lws_select_vhost( context, wsi->vhost->listen_port, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); @@ -1744,7 +1796,7 @@ lws_http_transaction_completed(struct lws *wsi) return 0; } - lwsl_debug("%s: wsi %p\n", __func__, wsi); + lwsl_info("%s: wsi %p\n", __func__, wsi); #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) lws_http_compression_destroy(wsi); @@ -2056,20 +2108,24 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, total_content_length, &p, end)) return -1; } else { - /* ...otherwise, for http 1 it must go chunked. For - * the compression case, the reason is we compress on - * the fly and do not know the compressed content-length - * until it has all been sent. Http/1.1 pipelining must - * be able to know where the transaction boundaries are - * ... so chunking... - */ - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_TRANSFER_ENCODING, - (unsigned char *)"chunked", 7, &p, end)) - return -1; #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) if (wsi->http.lcs) { + + /* ...otherwise, for http 1 it must go chunked. + * For the compression case, the reason is we + * compress on the fly and do not know the + * compressed content-length until it has all + * been sent. Http/1.1 pipelining must be able + * to know where the transaction boundaries are + * ... so chunking... + */ + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING, + (unsigned char *)"chunked", 7, + &p, end)) + return -1; + /* * ...this is fun, isn't it :-) For h1 that is * using an http compression translation, the @@ -2093,7 +2149,8 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, wsi->cache_secs); } else { cc = cache_control; - cclen = sprintf(cache_control, "must-revalidate, %s, max-age=%u", + cclen = sprintf(cache_control, + "must-revalidate, %s, max-age=%u", intermediates[wsi->cache_intermediaries], wsi->cache_secs); diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index bd39640e2d0fe4f3c7a1131730dd093d216fa0a0..92f8cd856e374405964012ffadd62bc02d280f89 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -93,7 +93,7 @@ rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi, break; } - lws_plat_set_socket_options(wsi->vhost, accept_fd); + lws_plat_set_socket_options(wsi->vhost, accept_fd, 0); #if defined(LWS_WITH_IPV6) lwsl_debug("accepted new conn port %u on fd=%d\n", diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 91d8c92dae5927428cc4997b34ff4ce38542d7ea..52d369fed88470efb7a137c10095126a1aea0457 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -1495,7 +1495,11 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason) static int rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi) { + if (!wsi->ws) + return 0; + #if !defined(LWS_WITHOUT_EXTENSIONS) + if (wsi->ws->rx_draining_ext) { struct lws **w = &pt->ws.rx_draining_ext_list; diff --git a/test-apps/test-client.c b/test-apps/test-client.c index d5b6f5f79987dc64c2a05d0be2c03fa32698e643..dbb8ead99520a1b902ecc0652886bfbaa42fd6fc 100644 --- a/test-apps/test-client.c +++ b/test-apps/test-client.c @@ -672,9 +672,12 @@ int main(int argc, char **argv) goto usage; /* add back the leading / on path */ - path[0] = '/'; - lws_strncpy(path + 1, p, sizeof(path) - 1); - i.path = path; + if (p[0] != '/') { + path[0] = '/'; + lws_strncpy(path + 1, p, sizeof(path) - 1); + i.path = path; + } else + i.path = p; if (!strcmp(prot, "http") || !strcmp(prot, "ws")) use_ssl = 0; @@ -682,6 +685,8 @@ int main(int argc, char **argv) if (!use_ssl) use_ssl = LCCSCF_USE_SSL; + lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path); + /* * create the websockets context. This tracks open connections and * knows how to route any traffic and which protocol version to use,