1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lib/core-net/dummy-callback.c
Andy Green 8a7e0edb7d lws_spawn_piped: break out from cgi
The vfork optimized spawn, stdxxx and terminal handling in the cgi
implementation is quite mature and sophisticated, and useful for
other things unrelated to cgi.  Break it out into its own public
api under LWS_WITH_SPAWN, off by default.

Expand it so the parent wsi is optional, and the role and protocol
bindings for stdxxx pipes can be set.  Allow optional sul timeout
and external lws_dll2 owner for extant children.

Remove inline style from minimal http-server-cgi
2020-02-21 17:32:41 +00:00

827 lines
22 KiB
C

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.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->mux_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
struct lws_proxy_pkt {
struct lws_dll2 pkt_list;
size_t len;
char binary;
char first;
char final;
/* data follows */
};
#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
int
lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_proxy_pkt *pkt;
struct lws_dll2 *dll;
switch (reason) {
/* h1 ws proxying... child / client / onward */
case LWS_CALLBACK_CLIENT_ESTABLISHED:
if (!wsi->h1_ws_proxied || !wsi->parent)
break;
lws_process_ws_upgrade2(wsi->parent);
#if defined(LWS_WITH_HTTP2)
if (wsi->parent->mux_substream)
lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__);
#endif
break;
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
return 1;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
case LWS_CALLBACK_CLIENT_CLOSED:
lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent);
if (wsi->parent)
lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
break;
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
{
unsigned char **p = (unsigned char **)in, *end = (*p) + len,
tmp[128];
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_COOKIE, p, end);
proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
WSI_TOKEN_HTTP_SET_COOKIE, p, end);
break;
}
case LWS_CALLBACK_CLIENT_RECEIVE:
wsi->parent->ws->proxy_buffered += len;
if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
lwsl_err("%s: proxied ws connection excessive buffering: dropping\n",
__func__);
return -1;
}
pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
if (!pkt)
return -1;
pkt->len = len;
pkt->first = lws_is_first_fragment(wsi);
pkt->final = lws_is_final_fragment(wsi);
pkt->binary = lws_frame_is_binary(wsi);
memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
lws_callback_on_writable(wsi->parent);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
if (!dll)
break;
pkt = (struct lws_proxy_pkt *)dll;
if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
LWS_PRE, pkt->len, lws_write_ws_flags(
pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
pkt->first, pkt->final)) < 0)
return -1;
lws_dll2_remove(dll);
lws_free(pkt);
if (lws_dll2_get_head(&wsi->ws->proxy_owner))
lws_callback_on_writable(wsi);
break;
/* h1 ws proxying... parent / server / incoming */
case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
return 1;
case LWS_CALLBACK_CLOSED:
lwsl_user("%s: closed\n", __func__);
return -1;
case LWS_CALLBACK_RECEIVE:
pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
if (!pkt)
return -1;
pkt->len = len;
pkt->first = lws_is_first_fragment(wsi);
pkt->final = lws_is_final_fragment(wsi);
pkt->binary = lws_frame_is_binary(wsi);
memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
lws_callback_on_writable(wsi->child_list);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
if (!dll)
break;
pkt = (struct lws_proxy_pkt *)dll;
if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
LWS_PRE, pkt->len, lws_write_ws_flags(
pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
pkt->first, pkt->final)) < 0)
return -1;
wsi->ws->proxy_buffered -= pkt->len;
lws_dll2_remove(dll);
lws_free(pkt);
if (lws_dll2_get_head(&wsi->ws->proxy_owner))
lws_callback_on_writable(wsi);
break;
default:
return 0;
}
return 0;
}
const struct lws_protocols lws_ws_proxy = {
"lws-ws-proxy",
lws_callback_ws_proxy,
0,
8192,
8192, NULL, 0
};
#endif
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[LWS_PRE + 32 + 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:
#if defined(LWS_WITH_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_WITH_SERVER)
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
#if defined(LWS_WITH_HTTP_PROXY)
if (wsi->child_list) {
lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len);
break;
}
#endif
/* fallthru */
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
if (lws_http_transaction_completed(wsi))
return -1;
break;
#endif
#if defined(LWS_WITH_HTTP_PROXY)
case LWS_CALLBACK_HTTP_BODY:
if (wsi->child_list) {
lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len);
if (lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len) < 0)
return -1;
lws_callback_on_writable(wsi->child_list);
}
break;
#endif
case LWS_CALLBACK_HTTP_WRITEABLE:
// lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
#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 && wsi->http.cgi &&
wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
lws_rx_flow_control(
wsi->http.cgi->lsp->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->mux_substream) {
memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
lwsl_debug("writing chunk term and exiting\n");
lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 5, LWS_WRITE_HTTP);
} else
lws_write(wsi, (unsigned char *)buf +
LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
/* always close after sending it */
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
}
#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;
n = LWS_WRITE_HTTP_HEADERS;
if (!wsi->http.prh_content_length)
n |= LWS_WRITE_H2_STREAM_END;
lwsl_debug("%s: %p: issuing proxy headers: clen %d\n",
__func__, wsi, (int)wsi->http.prh_content_length);
n = lws_write(wsi, wsi->http.pending_return_headers +
LWS_PRE,
wsi->http.pending_return_headers_len, n);
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; }
/* h1 http proxying... */
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);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
proxy_header(parent, wsi, end, 256,
WSI_TOKEN_HTTP_LOCATION, &p, end);
if (!parent->mux_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->mux_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.prh_content_length = -1;
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
parent->http.prh_content_length = atoll(
lws_hdr_simple_ptr(wsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH));
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 %d headers (len %d)\n", __func__,
lws_http_client_http_response(wsi),
(int)parent->http.prh_content_length);
/*
* so at this point, the onward client connection can bear
* traffic. We might be doing a POST and have pending cached
* inbound stuff to send, it can go now.
*/
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_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);
proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
WSI_TOKEN_HTTP_COOKIE, 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->mux_substream) &&
!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;
}
if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
LWS_WRITE_HTTP_FINAL);
if (lws_http_transaction_completed(wsi))
return -1;
return 0;
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->lsp->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;
}