diff options
author | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2019-03-06 01:07:13 +0100 |
---|---|---|
committer | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2019-03-06 02:02:52 +0100 |
commit | 90210c48627692d281554d6185b5db17a86c852a (patch) | |
tree | 03ed5d7c6fe18975466bf933bfa9586a012f0efa /thirdparty/libwebsockets/lib/core/dummy-callback.c | |
parent | f43ee4aff8e5f72c24cfbd8f1ff90703714857e0 (diff) |
Update libwebsockets to 3.1 (plus UWP patch)
Diffstat (limited to 'thirdparty/libwebsockets/lib/core/dummy-callback.c')
-rw-r--r-- | thirdparty/libwebsockets/lib/core/dummy-callback.c | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/thirdparty/libwebsockets/lib/core/dummy-callback.c b/thirdparty/libwebsockets/lib/core/dummy-callback.c new file mode 100644 index 0000000000..8fd18be9c5 --- /dev/null +++ b/thirdparty/libwebsockets/lib/core/dummy-callback.c @@ -0,0 +1,611 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 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_debug("%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_debug("%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, (int)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_info("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; +} |