2 * libwebsockets - small server side websockets and web server implementation
4 * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "core/private.h"
24 #if defined(LWS_WITH_HTTP_PROXY)
26 proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
27 int temp_len, int index, unsigned char **p, unsigned char *end)
29 int n = lws_hdr_total_length(par, index);
32 lwsl_debug("%s: no index %d:\n", __func__, index);
36 if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0)
39 lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp);
41 if (lws_add_http_header_by_token(wsi, index, temp, n, p, end))
48 stream_close(struct lws *wsi)
50 char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
52 if (wsi->http.did_stream_close)
55 wsi->http.did_stream_close = 1;
57 if (wsi->http2_substream) {
58 if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
59 LWS_WRITE_HTTP_FINAL) < 0) {
60 lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n",
72 if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
73 LWS_WRITE_HTTP_FINAL) < 0) {
74 lwsl_err("%s: COMPL_CLIENT_HTTP: "
75 "h2 final write failed\n", __func__);
86 struct lws_proxy_pkt {
87 struct lws_dll2 pkt_list;
96 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
98 lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
99 void *user, void *in, size_t len)
101 struct lws_proxy_pkt *pkt;
102 struct lws_dll2 *dll;
106 /* h1 ws proxying... child / client / onward */
108 case LWS_CALLBACK_CLIENT_ESTABLISHED:
109 if (!wsi->h1_ws_proxied || !wsi->parent)
112 lws_process_ws_upgrade2(wsi->parent);
114 #if defined(LWS_WITH_HTTP2)
115 if (wsi->parent->http2_substream)
116 lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__);
120 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
123 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
124 case LWS_CALLBACK_CLIENT_CLOSED:
125 lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent);
127 lws_set_timeout(wsi->parent,
128 PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE,
132 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
134 unsigned char **p = (unsigned char **)in, *end = (*p) + len,
137 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
138 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
140 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
141 WSI_TOKEN_HTTP_COOKIE, p, end);
143 proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
144 WSI_TOKEN_HTTP_SET_COOKIE, p, end);
148 case LWS_CALLBACK_CLIENT_RECEIVE:
149 wsi->parent->ws->proxy_buffered += len;
150 if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
151 lwsl_err("%s: proxied ws connection excessive buffering: dropping\n",
155 pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
159 pkt->pkt_list.prev = pkt->pkt_list.next = NULL;
161 pkt->first = lws_is_first_fragment(wsi);
162 pkt->final = lws_is_final_fragment(wsi);
163 pkt->binary = lws_frame_is_binary(wsi);
165 memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
167 lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
168 lws_callback_on_writable(wsi->parent);
171 case LWS_CALLBACK_CLIENT_WRITEABLE:
172 dll = lws_dll2_get_tail(&wsi->ws->proxy_owner);
176 pkt = (struct lws_proxy_pkt *)dll;
177 if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
178 LWS_PRE, pkt->len, lws_write_ws_flags(
179 pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
180 pkt->first, pkt->final)) < 0)
183 wsi->parent->ws->proxy_buffered -= pkt->len;
185 lws_dll2_remove(dll);
188 if (lws_dll2_get_tail(&wsi->ws->proxy_owner))
189 lws_callback_on_writable(wsi);
192 /* h1 ws proxying... parent / server / incoming */
194 case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
197 case LWS_CALLBACK_CLOSED:
198 lwsl_user("%s: closed\n", __func__);
201 case LWS_CALLBACK_RECEIVE:
202 pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
206 pkt->pkt_list.prev = pkt->pkt_list.next = NULL;
208 pkt->first = lws_is_first_fragment(wsi);
209 pkt->final = lws_is_final_fragment(wsi);
210 pkt->binary = lws_frame_is_binary(wsi);
212 memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
214 lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
215 lws_callback_on_writable(wsi->child_list);
218 case LWS_CALLBACK_SERVER_WRITEABLE:
219 dll = lws_dll2_get_tail(&wsi->ws->proxy_owner);
223 pkt = (struct lws_proxy_pkt *)dll;
224 if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
225 LWS_PRE, pkt->len, lws_write_ws_flags(
226 pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
227 pkt->first, pkt->final)) < 0)
230 lws_dll2_remove(dll);
233 if (lws_dll2_get_tail(&wsi->ws->proxy_owner))
234 lws_callback_on_writable(wsi);
244 const struct lws_protocols lws_ws_proxy = {
246 lws_callback_ws_proxy,
255 lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
256 void *user, void *in, size_t len)
258 struct lws_ssl_info *si;
260 struct lws_cgi_args *args;
262 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
266 #if defined(LWS_WITH_HTTP_PROXY)
267 unsigned char **p, *end;
272 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
273 case LWS_CALLBACK_HTTP:
274 #ifndef LWS_NO_SERVER
275 if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
278 if (lws_http_transaction_completed(wsi))
282 #if !defined(LWS_NO_SERVER)
283 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
284 #if defined(LWS_WITH_HTTP_PROXY)
285 if (wsi->child_list) {
286 lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len);
291 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
292 if (lws_http_transaction_completed(wsi))
297 #if defined(LWS_WITH_HTTP_PROXY)
298 case LWS_CALLBACK_HTTP_BODY:
299 if (wsi->child_list) {
300 lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len);
301 if (lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len) < 0)
303 lws_callback_on_writable(wsi->child_list);
308 case LWS_CALLBACK_HTTP_WRITEABLE:
309 // lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
311 if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
312 LWS_CB_REASON_AUX_BF__CGI)) {
313 n = lws_cgi_write_split_stdout_headers(wsi);
315 lwsl_debug("AUX_BF__CGI forcing close\n");
318 if (!n && wsi->http.cgi && wsi->http.cgi->stdwsi[LWS_STDOUT])
320 wsi->http.cgi->stdwsi[LWS_STDOUT], 1);
322 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
324 ~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
326 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
328 if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over)
333 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
334 if (!wsi->http2_substream) {
335 memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
336 lwsl_debug("writing chunk term and exiting\n");
337 n = lws_write(wsi, (unsigned char *)buf +
338 LWS_PRE, 5, LWS_WRITE_HTTP);
340 n = lws_write(wsi, (unsigned char *)buf +
342 LWS_WRITE_HTTP_FINAL);
344 /* always close after sending it */
345 if (lws_http_transaction_completed(wsi))
350 #if defined(LWS_WITH_HTTP_PROXY)
352 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
354 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
356 n = LWS_WRITE_HTTP_HEADERS;
357 if (!wsi->http.prh_content_length)
358 n |= LWS_WRITE_H2_STREAM_END;
360 lwsl_debug("%s: %p: issuing proxy headers: clen %d\n",
361 __func__, wsi, (int)wsi->http.prh_content_length);
362 n = lws_write(wsi, wsi->http.pending_return_headers +
364 wsi->http.pending_return_headers_len, n);
366 lws_free_set_NULL(wsi->http.pending_return_headers);
369 lwsl_err("%s: EST_CLIENT_HTTP: write failed\n",
374 lws_callback_on_writable(wsi);
378 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
379 char *px = buf + LWS_PRE;
380 int lenx = sizeof(buf) - LWS_PRE - 32;
383 * our sink is writeable and our source has something
384 * to read. So read a lump of source material of
385 * suitable size to send or what's available, whichever
388 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
389 if (!lws_get_child(wsi))
392 /* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
393 if (lws_http_client_read(lws_get_child(wsi), &px,
395 lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: "
396 "client closed\n", __func__);
405 if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
406 lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY_TRANS_END\n",
409 wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
411 if (stream_close(wsi))
414 if (lws_http_transaction_completed(wsi))
420 #if defined(LWS_WITH_HTTP_PROXY)
421 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
422 assert(lws_get_parent(wsi));
423 if (!lws_get_parent(wsi))
425 lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
426 lws_callback_on_writable(lws_get_parent(wsi));
429 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
430 char *out = buf + LWS_PRE;
432 assert(lws_get_parent(wsi));
434 if (wsi->http.proxy_parent_chunked) {
436 if (len > sizeof(buf) - LWS_PRE - 16) {
437 lwsl_err("oversize buf %d %d\n", (int)len,
438 (int)sizeof(buf) - LWS_PRE - 16);
443 * this only needs dealing with on http/1.1 to allow
446 n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
448 memcpy(out, in, len);
453 n = lws_write(lws_get_parent(wsi),
454 (unsigned char *)buf + LWS_PRE,
455 len + n + 2, LWS_WRITE_HTTP);
457 n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
458 len, LWS_WRITE_HTTP);
463 /* h1 http proxying... */
465 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
466 unsigned char *start, *p, *end;
469 * We want to proxy these headers, but we are being called
470 * at the point the onward client was established, which is
471 * unrelated to the state or writability of our proxy
474 * Therefore produce the headers using the onward client ah
475 * while we have it, and stick them on the output buflist to be
476 * written on the proxy connection as soon as convenient.
479 parent = lws_get_parent(wsi);
484 start = p = (unsigned char *)buf + LWS_PRE;
485 end = p + sizeof(buf) - LWS_PRE - 256;
487 if (lws_add_http_header_status(lws_get_parent(wsi),
488 lws_http_client_http_response(wsi), &p, end))
492 * copy these headers from the client connection to the parent
495 proxy_header(parent, wsi, end, 256,
496 WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
497 proxy_header(parent, wsi, end, 256,
498 WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
499 proxy_header(parent, wsi, end, 256,
500 WSI_TOKEN_HTTP_ETAG, &p, end);
501 proxy_header(parent, wsi, end, 256,
502 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
503 proxy_header(parent, wsi, end, 256,
504 WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
505 proxy_header(parent, wsi, end, 256,
506 WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
507 proxy_header(parent, wsi, end, 256,
508 WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
509 proxy_header(parent, wsi, end, 256,
510 WSI_TOKEN_HTTP_LOCATION, &p, end);
512 if (!parent->http2_substream)
513 if (lws_add_http_header_by_token(parent,
514 WSI_TOKEN_CONNECTION, (unsigned char *)"close",
519 * We proxy using h1 only atm, and strip any chunking so it
520 * can go back out on h2 just fine.
522 * However if we are actually going out on h1, we need to add
523 * our own chunking since we still don't know the size.
526 if (!parent->http2_substream &&
527 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
528 lwsl_debug("downstream parent chunked\n");
529 if (lws_add_http_header_by_token(parent,
530 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
531 (unsigned char *)"chunked", 7, &p, end))
534 wsi->http.proxy_parent_chunked = 1;
537 if (lws_finalize_http_header(parent, &p, end))
540 parent->http.prh_content_length = -1;
541 if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
542 parent->http.prh_content_length = atoll(
543 lws_hdr_simple_ptr(wsi,
544 WSI_TOKEN_HTTP_CONTENT_LENGTH));
546 parent->http.pending_return_headers_len = lws_ptr_diff(p, start);
547 parent->http.pending_return_headers =
548 lws_malloc(parent->http.pending_return_headers_len +
549 LWS_PRE, "return proxy headers");
550 if (!parent->http.pending_return_headers)
553 memcpy(parent->http.pending_return_headers + LWS_PRE, start,
554 parent->http.pending_return_headers_len);
556 parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
558 lwsl_debug("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: "
559 "prepared %d headers (len %d)\n", __func__,
560 lws_http_client_http_response(wsi),
561 (int)parent->http.prh_content_length);
564 * so at this point, the onward client connection can bear
565 * traffic. We might be doing a POST and have pending cached
566 * inbound stuff to send, it can go now.
569 lws_callback_on_writable(parent);
573 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
574 lwsl_info("%s: COMPLETED_CLIENT_HTTP: %p (parent %p)\n",
575 __func__, wsi, lws_get_parent(wsi));
576 if (!lws_get_parent(wsi))
578 lws_get_parent(wsi)->reason_bf |=
579 LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
580 lws_callback_on_writable(lws_get_parent(wsi));
583 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
584 if (!lws_get_parent(wsi))
586 lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
587 lws_set_timeout(lws_get_parent(wsi),
588 PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE,
592 case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
593 parent = lws_get_parent(wsi);
597 p = (unsigned char **)in;
601 * copy these headers from the parent request to the client
602 * connection's request
605 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
606 WSI_TOKEN_HTTP_ETAG, p, end);
607 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
608 WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
609 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
610 WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
611 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
612 WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
613 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
614 WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
615 proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
616 WSI_TOKEN_HTTP_COOKIE, p, end);
619 lws_get_peer_simple(parent, buf, sizeof(buf));
620 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
621 (unsigned char *)buf, (int)strlen(buf), p, end))
628 /* CGI IO events (POLLIN/OUT) appear here, our default policy is:
630 * - POST data goes on subprocess stdin
631 * - subprocess stdout goes on http via writeable callback
632 * - subprocess stderr goes to the logs
634 case LWS_CALLBACK_CGI:
635 args = (struct lws_cgi_args *)in;
636 switch (args->ch) { /* which of stdin/out/err ? */
638 /* TBD stdin rx flow control */
641 /* quench POLLIN on STDOUT until MASTER got writeable */
642 lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
643 wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
644 /* when writing to MASTER would not block */
645 lws_callback_on_writable(wsi);
648 n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
651 n = read(n, buf, sizeof(buf) - 2);
653 if (buf[n - 1] != '\n')
656 lwsl_notice("CGI-stderr: %s\n", buf);
662 case LWS_CALLBACK_CGI_TERMINATED:
663 lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
664 wsi->http.cgi->explicitly_chunked,
665 (uint64_t)wsi->http.cgi->content_length);
666 if (!wsi->http.cgi->explicitly_chunked &&
667 !wsi->http.cgi->content_length) {
668 /* send terminating chunk */
669 lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
670 wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
671 lws_callback_on_writable(wsi);
672 lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
675 if (lws_http_transaction_completed(wsi))
679 case LWS_CALLBACK_CGI_STDIN_DATA: /* POST body for stdin */
680 args = (struct lws_cgi_args *)in;
681 args->data[args->len] = '\0';
682 if (!args->stdwsi[LWS_STDIN])
684 n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
688 #if defined(LWS_WITH_ZLIB)
689 if (wsi->http.cgi->gzip_inflate) {
692 if (!wsi->http.cgi->gzip_init) {
693 lwsl_info("inflating gzip\n");
695 memset(&wsi->http.cgi->inflate, 0,
696 sizeof(wsi->http.cgi->inflate));
698 if (inflateInit2(&wsi->http.cgi->inflate,
700 lwsl_err("%s: iniflateInit failed\n",
705 wsi->http.cgi->gzip_init = 1;
708 wsi->http.cgi->inflate.next_in = args->data;
709 wsi->http.cgi->inflate.avail_in = args->len;
713 wsi->http.cgi->inflate.next_out =
714 wsi->http.cgi->inflate_buf;
715 wsi->http.cgi->inflate.avail_out =
716 sizeof(wsi->http.cgi->inflate_buf);
718 n = inflate(&wsi->http.cgi->inflate,
726 inflateEnd(&wsi->http.cgi->inflate);
727 wsi->http.cgi->gzip_init = 0;
728 lwsl_err("zlib error inflate %d\n", n);
732 if (wsi->http.cgi->inflate.avail_out !=
733 sizeof(wsi->http.cgi->inflate_buf)) {
736 written = write(args->stdwsi[LWS_STDIN]->desc.filefd,
737 wsi->http.cgi->inflate_buf,
738 sizeof(wsi->http.cgi->inflate_buf) -
739 wsi->http.cgi->inflate.avail_out);
741 if (written != (int)(
742 sizeof(wsi->http.cgi->inflate_buf) -
743 wsi->http.cgi->inflate.avail_out)) {
744 lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
745 "sent %d only %d went", n, args->len);
748 if (n == Z_STREAM_END) {
749 lwsl_err("gzip inflate end\n");
750 inflateEnd(&wsi->http.cgi->inflate);
751 wsi->http.cgi->gzip_init = 0;
758 if (wsi->http.cgi->inflate.avail_out)
765 #endif /* WITH_ZLIB */
767 n = write(n, args->data, args->len);
768 // lwsl_hexdump_notice(args->data, args->len);
770 lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
771 "sent %d only %d went", n, args->len);
773 if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
774 args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
775 wsi->http.cgi->post_in_expected -= n;
776 if (!wsi->http.cgi->post_in_expected) {
777 struct lws *siwsi = args->stdwsi[LWS_STDIN];
779 lwsl_debug("%s: expected POST in end: "
780 "closing stdin wsi %p, fd %d\n",
781 __func__, siwsi, siwsi->desc.sockfd);
783 __remove_wsi_socket_from_fds(siwsi);
784 lwsi_set_state(siwsi, LRS_DEAD_SOCKET);
785 siwsi->socket_is_permanently_unusable = 1;
786 // lws_remove_child_from_any_parent(siwsi);
787 if (wsi->context->event_loop_ops->
788 close_handle_manually) {
790 wsi->context->event_loop_ops->
791 close_handle_manually(siwsi);
792 siwsi->told_event_loop_closed = 1;
794 compatible_close(siwsi->desc.sockfd);
795 __lws_free_wsi(siwsi);
797 wsi->http.cgi->pipe_fds[LWS_STDIN][1] = -1;
799 // args->stdwsi[LWS_STDIN] = NULL;
804 #endif /* WITH_CGI */
805 #endif /* ROLE_ H1 / H2 */
806 case LWS_CALLBACK_SSL_INFO:
810 lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n",
815 case LWS_CALLBACK_GET_THREAD_ID:
816 return (int)(unsigned long long)pthread_self();