• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lib-core.h"
26 
27 /* max individual proxied header payload size */
28 #define MAXHDRVAL 1024
29 
30 #if defined(LWS_WITH_HTTP_PROXY)
31 static int
proxy_header(struct lws * wsi,struct lws * par,unsigned char * temp,int temp_len,int index,unsigned char ** p,unsigned char * end)32 proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
33 	     int temp_len, int index, unsigned char **p, unsigned char *end)
34 {
35 	int n = lws_hdr_total_length(par, (enum lws_token_indexes)index);
36 
37 	if (n < 1) {
38 		lwsl_wsi_debug(wsi, "no index %d:", index);
39 
40 		return 0;
41 	}
42 
43 	if (lws_hdr_copy(par, (char *)temp, temp_len, (enum lws_token_indexes)index) < 0) {
44 		lwsl_wsi_notice(wsi, "unable to copy par hdr idx %d (len %d)",
45 				      index, n);
46 		return -1;
47 	}
48 
49 	lwsl_wsi_debug(wsi, "index %d: %s", index, (char *)temp);
50 
51 	if (lws_add_http_header_by_token(wsi, (enum lws_token_indexes)index, temp, n, p, end)) {
52 		lwsl_wsi_notice(wsi, "unable to append par hdr idx %d (len %d)",
53 				     index, n);
54 		return -1;
55 	}
56 
57 	return 0;
58 }
59 
60 static int
stream_close(struct lws * wsi)61 stream_close(struct lws *wsi)
62 {
63 	char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
64 
65 	if (wsi->http.did_stream_close)
66 		return 0;
67 
68 	wsi->http.did_stream_close = 1;
69 
70 	if (wsi->mux_substream) {
71 		if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
72 			      LWS_WRITE_HTTP_FINAL) < 0)
73 			goto bail;
74 
75 		return 0;
76 	}
77 
78 	*out++ = '0';
79 	*out++ = '\x0d';
80 	*out++ = '\x0a';
81 	*out++ = '\x0d';
82 	*out++ = '\x0a';
83 
84 	if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
85 		      LWS_WRITE_HTTP_FINAL) < 0)
86 		goto bail;
87 
88 	return 0;
89 
90 bail:
91 	lwsl_wsi_info(wsi, "h2 fin wr failed");
92 
93 	return -1;
94 }
95 
96 #endif
97 
98 struct lws_proxy_pkt {
99 	struct lws_dll2 pkt_list;
100 	size_t len;
101 	char binary;
102 	char first;
103 	char final;
104 
105 	/* data follows */
106 };
107 
108 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
109 int
lws_callback_ws_proxy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)110 lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
111 			void *user, void *in, size_t len)
112 {
113 	struct lws_proxy_pkt *pkt;
114 	struct lws_dll2 *dll;
115 
116 	switch (reason) {
117 
118 	/* h1 ws proxying... child / client / onward */
119 
120 	case LWS_CALLBACK_CLIENT_ESTABLISHED:
121 		if (!wsi->h1_ws_proxied || !wsi->parent)
122 			break;
123 
124 		if (lws_process_ws_upgrade2(wsi->parent))
125 			return -1;
126 
127 #if defined(LWS_WITH_HTTP2)
128 		if (wsi->parent->mux_substream)
129 			lwsl_wsi_info(wsi, "proxied h2 -> h1 ws established");
130 #endif
131 		break;
132 
133 	case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
134 		return 1;
135 
136 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
137 	case LWS_CALLBACK_CLIENT_CLOSED:
138 		lwsl_wsi_info(wsi, "client closed: parent %s",
139 				   lws_wsi_tag(wsi->parent));
140 		if (wsi->parent)
141                        lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
142 		break;
143 
144 	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
145 	{
146 		unsigned char **p = (unsigned char **)in, *end = (*p) + len,
147 				    tmp[MAXHDRVAL];
148 
149 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
150 			      WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
151 
152 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
153 			      WSI_TOKEN_HTTP_COOKIE, p, end);
154 
155 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
156 			      WSI_TOKEN_HTTP_SET_COOKIE, p, end);
157 		break;
158 	}
159 
160 	case LWS_CALLBACK_CLIENT_RECEIVE:
161 		wsi->parent->ws->proxy_buffered += len;
162 		if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
163 			lwsl_wsi_err(wsi, "proxied ws connection "
164 					  "excessive buffering: dropping");
165 			return -1;
166 		}
167 		pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
168 		if (!pkt)
169 			return -1;
170 
171 		pkt->len = len;
172 		pkt->first = (char)lws_is_first_fragment(wsi);
173 		pkt->final = (char)lws_is_final_fragment(wsi);
174 		pkt->binary = (char)lws_frame_is_binary(wsi);
175 
176 		memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
177 
178 		lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
179 		lws_callback_on_writable(wsi->parent);
180 		break;
181 
182 	case LWS_CALLBACK_CLIENT_WRITEABLE:
183 		dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
184 		if (!dll)
185 			break;
186 
187 		pkt = (struct lws_proxy_pkt *)dll;
188 		if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
189 			      LWS_PRE, pkt->len, (enum lws_write_protocol)lws_write_ws_flags(
190 				pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
191 					pkt->first, pkt->final)) < 0)
192 			return -1;
193 
194 		lws_dll2_remove(dll);
195 		lws_free(pkt);
196 
197 		if (lws_dll2_get_head(&wsi->ws->proxy_owner))
198 			lws_callback_on_writable(wsi);
199 		break;
200 
201 	/* h1 ws proxying... parent / server / incoming */
202 
203 	case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
204 		return 1;
205 
206 	case LWS_CALLBACK_CLOSED:
207 		lwsl_wsi_info(wsi, "closed");
208 		return -1;
209 
210 	case LWS_CALLBACK_RECEIVE:
211 		pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
212 		if (!pkt)
213 			return -1;
214 
215 		pkt->len = len;
216 		pkt->first = (char)lws_is_first_fragment(wsi);
217 		pkt->final = (char)lws_is_final_fragment(wsi);
218 		pkt->binary = (char)lws_frame_is_binary(wsi);
219 
220 		memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
221 
222 		lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
223 		lws_callback_on_writable(wsi->child_list);
224 		break;
225 
226 	case LWS_CALLBACK_SERVER_WRITEABLE:
227 		dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
228 		if (!dll)
229 			break;
230 
231 		pkt = (struct lws_proxy_pkt *)dll;
232 		if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
233 			      LWS_PRE, pkt->len, (enum lws_write_protocol)lws_write_ws_flags(
234 				pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
235 					pkt->first, pkt->final)) < 0)
236 			return -1;
237 
238 		wsi->ws->proxy_buffered -= pkt->len;
239 
240 		lws_dll2_remove(dll);
241 		lws_free(pkt);
242 
243 		if (lws_dll2_get_head(&wsi->ws->proxy_owner))
244 			lws_callback_on_writable(wsi);
245 		break;
246 
247 	default:
248 		return 0;
249 	}
250 
251 	return 0;
252 }
253 
254 const struct lws_protocols lws_ws_proxy = {
255 		"lws-ws-proxy",
256 		lws_callback_ws_proxy,
257 		0,
258 		8192,
259 		8192, NULL, 0
260 };
261 
262 #endif
263 
264 
265 int
lws_callback_http_dummy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)266 lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
267 			void *user, void *in, size_t len)
268 {
269 	struct lws_ssl_info *si;
270 #ifdef LWS_WITH_CGI
271 	struct lws_cgi_args *args;
272 #endif
273 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
274 	char buf[LWS_PRE + 32 + 8192];
275 	int n;
276 #endif
277 #if defined(LWS_WITH_HTTP_PROXY)
278 	unsigned char **p, *end;
279 	struct lws *parent;
280 #endif
281 
282 	switch (reason) {
283 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
284 	case LWS_CALLBACK_HTTP:
285 #if defined(LWS_WITH_SERVER)
286 		if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
287 			return -1;
288 
289 		if (lws_http_transaction_completed(wsi))
290 #endif
291 			return -1;
292 		break;
293 #if defined(LWS_WITH_SERVER)
294 	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
295 #if defined(LWS_WITH_HTTP_PROXY)
296 		if (wsi->child_list) {
297 			lwsl_wsi_info(wsi, "HTTP_BODY_COMPLETION: %d",
298 					   (int)len);
299 			lws_callback_on_writable(wsi->child_list);
300 			break;
301 		}
302 #endif
303 		if (lws_return_http_status(wsi, 200, NULL))
304 			return -1;
305 		break;
306 
307 		/* fallthru */
308 	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
309 		if (lws_http_transaction_completed(wsi))
310 			return -1;
311 		break;
312 #endif
313 
314 #if defined(LWS_WITH_HTTP_PROXY)
315 	case LWS_CALLBACK_HTTP_BODY:
316 		if (wsi->child_list) {
317 			lwsl_wsi_info(wsi, "HTTP_BODY: stashing %d", (int)len);
318 			if (lws_buflist_append_segment(
319 				     &wsi->http.buflist_post_body, in, len) < 0)
320 				return -1;
321 			lws_client_http_body_pending(wsi->child_list, 1);
322 			lws_callback_on_writable(wsi->child_list);
323 		}
324 		break;
325 #endif
326 
327 	case LWS_CALLBACK_HTTP_WRITEABLE:
328 		// lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
329 #ifdef LWS_WITH_CGI
330 		if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
331 				      LWS_CB_REASON_AUX_BF__CGI)) {
332 			n = lws_cgi_write_split_stdout_headers(wsi);
333 			if (n < 0) {
334 				lwsl_wsi_debug(wsi, "AUX_BF__CGI forcing close");
335 				return -1;
336 			}
337 			if (!n && wsi->http.cgi && wsi->http.cgi->lsp &&
338 			    wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
339 				lws_rx_flow_control(
340 					wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1);
341 
342 			if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
343 				wsi->reason_bf &=
344 					(char)~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
345 			else
346 				wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__CGI;
347 
348 			if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) {
349 				lwsl_wsi_info(wsi, "txn over");
350 				return -1;
351 			}
352 
353 			break;
354 		}
355 
356 		if ((wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) ||
357 		    (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END)) {
358 			if (!wsi->mux_substream) {
359 				memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
360 				lwsl_wsi_debug(wsi, "wr chunk term and exiting");
361 				lws_write(wsi, (unsigned char *)buf +
362 						   LWS_PRE, 5, LWS_WRITE_HTTP);
363 			} else
364 				lws_write(wsi, (unsigned char *)buf +
365 						   LWS_PRE, 0,
366 						   LWS_WRITE_HTTP_FINAL);
367 
368 			/* always close after sending it */
369 			if (lws_http_transaction_completed(wsi))
370 				return -1;
371 			return 0;
372 		}
373 #endif
374 #if defined(LWS_WITH_HTTP_PROXY)
375 
376 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
377 
378 			wsi->reason_bf &=
379 				     (char)~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
380 
381 			n = LWS_WRITE_HTTP_HEADERS;
382 			if (!wsi->http.prh_content_length)
383 				n |= LWS_WRITE_H2_STREAM_END;
384 
385 			lwsl_wsi_debug(wsi, "issuing proxy headers: clen %d",
386 				    (int)wsi->http.prh_content_length);
387 			n = lws_write(wsi, wsi->http.pending_return_headers +
388 					   LWS_PRE,
389 				      wsi->http.pending_return_headers_len,
390 				      (enum lws_write_protocol)n);
391 
392 			lws_free_set_NULL(wsi->http.pending_return_headers);
393 
394 			if (n < 0) {
395 				lwsl_wsi_err(wsi, "EST_CLIENT_HTTP: wr failed");
396 
397 				return -1;
398 			}
399 
400 			lws_callback_on_writable(wsi);
401 			break;
402 		}
403 
404 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
405 			char *px = buf + LWS_PRE;
406 			int lenx = sizeof(buf) - LWS_PRE - 32;
407 
408 			/*
409 			 * our sink is writeable and our source has something
410 			 * to read.  So read a lump of source material of
411 			 * suitable size to send or what's available, whichever
412 			 * is the smaller.
413 			 */
414 			wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY;
415 			if (!lws_get_child(wsi))
416 				break;
417 
418 			/* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
419 			if (lws_http_client_read(lws_get_child(wsi), &px,
420 						 &lenx) < 0) {
421 				lwsl_wsi_info(wsi, "LWS_CB_REASON_AUX_BF__PROXY: "
422 					   "client closed");
423 
424 				stream_close(wsi);
425 
426 				return -1;
427 			}
428 			break;
429 		}
430 
431 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
432 			lwsl_wsi_info(wsi, "PROXY_TRANS_END");
433 
434 			wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
435 
436 			if (stream_close(wsi))
437 				return -1;
438 
439 			if (lws_http_transaction_completed(wsi))
440 				return -1;
441 		}
442 #endif
443 		break;
444 
445 #if defined(LWS_WITH_HTTP_PROXY)
446 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
447 		assert(lws_get_parent(wsi));
448 		if (!lws_get_parent(wsi))
449 			break;
450 		lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
451 		lws_callback_on_writable(lws_get_parent(wsi));
452 		break;
453 
454 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
455 		char *out = buf + LWS_PRE;
456 
457 		assert(lws_get_parent(wsi));
458 
459 		if (wsi->http.proxy_parent_chunked) {
460 
461 			if (len > sizeof(buf) - LWS_PRE - 16) {
462 				lwsl_wsi_err(wsi, "oversize buf %d %d", (int)len,
463 						(int)sizeof(buf) - LWS_PRE - 16);
464 				return -1;
465 			}
466 
467 			/*
468 			 * this only needs dealing with on http/1.1 to allow
469 			 * pipelining
470 			 */
471 			n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
472 			out += n;
473 			memcpy(out, in, len);
474 			out += len;
475 			*out++ = '\x0d';
476 			*out++ = '\x0a';
477 
478 			n = lws_write(lws_get_parent(wsi),
479 				      (unsigned char *)buf + LWS_PRE,
480 				      (size_t)(unsigned int)(len + (unsigned int)n + 2), LWS_WRITE_HTTP);
481 		} else
482 			n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
483 				      len, LWS_WRITE_HTTP);
484 		if (n < 0)
485 			return -1;
486 		break; }
487 
488 	/* h1 http proxying... */
489 
490 	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
491 		unsigned char *start, *p, *end;
492 
493 		/*
494 		 * We want to proxy these headers, but we are being called
495 		 * at the point the onward client was established, which is
496 		 * unrelated to the state or writability of our proxy
497 		 * connection.
498 		 *
499 		 * Therefore produce the headers using the onward client ah
500 		 * while we have it, and stick them on the output buflist to be
501 		 * written on the proxy connection as soon as convenient.
502 		 */
503 
504 		parent = lws_get_parent(wsi);
505 
506 		if (!parent)
507 			return 0;
508 
509 		start = p = (unsigned char *)buf + LWS_PRE;
510 		end = p + sizeof(buf) - LWS_PRE - MAXHDRVAL;
511 
512 		if (lws_add_http_header_status(lws_get_parent(wsi),
513 				lws_http_client_http_response(wsi), &p, end))
514 			return 1;
515 
516 		/*
517 		 * copy these headers from the client connection to the parent
518 		 */
519 
520 		proxy_header(parent, wsi, end, MAXHDRVAL,
521 			     WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
522 		proxy_header(parent, wsi, end, MAXHDRVAL,
523 			     WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
524 		proxy_header(parent, wsi, end, MAXHDRVAL,
525 			     WSI_TOKEN_HTTP_ETAG, &p, end);
526 		proxy_header(parent, wsi, end, MAXHDRVAL,
527 			     WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
528 		proxy_header(parent, wsi, end, MAXHDRVAL,
529 			     WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
530 		proxy_header(parent, wsi, end, MAXHDRVAL,
531 			     WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
532 		proxy_header(parent, wsi, end, MAXHDRVAL,
533 			     WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
534 		proxy_header(parent, wsi, end, MAXHDRVAL,
535 			     WSI_TOKEN_HTTP_LOCATION, &p, end);
536 
537 		if (!parent->mux_substream)
538 			if (lws_add_http_header_by_token(parent,
539 				WSI_TOKEN_CONNECTION, (unsigned char *)"close",
540 				5, &p, end))
541 			return -1;
542 
543 		/*
544 		 * We proxy using h1 only atm, and strip any chunking so it
545 		 * can go back out on h2 just fine.
546 		 *
547 		 * However if we are actually going out on h1, we need to add
548 		 * our own chunking since we still don't know the size.
549 		 */
550 
551 		if (!parent->mux_substream &&
552 		    !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
553 			lwsl_wsi_debug(wsi, "downstream parent chunked");
554 			if (lws_add_http_header_by_token(parent,
555 					WSI_TOKEN_HTTP_TRANSFER_ENCODING,
556 					(unsigned char *)"chunked", 7, &p, end))
557 				return -1;
558 
559 			wsi->http.proxy_parent_chunked = 1;
560 		}
561 
562 		if (lws_finalize_http_header(parent, &p, end))
563 			return 1;
564 
565 		parent->http.prh_content_length = (size_t)-1;
566 		if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
567 			parent->http.prh_content_length = (size_t)atoll(
568 				lws_hdr_simple_ptr(wsi,
569 						WSI_TOKEN_HTTP_CONTENT_LENGTH));
570 
571 		parent->http.pending_return_headers_len = lws_ptr_diff_size_t(p, start);
572 		parent->http.pending_return_headers =
573 			lws_malloc(parent->http.pending_return_headers_len +
574 				    LWS_PRE, "return proxy headers");
575 		if (!parent->http.pending_return_headers)
576 			return -1;
577 
578 		memcpy(parent->http.pending_return_headers + LWS_PRE, start,
579 		       parent->http.pending_return_headers_len);
580 
581 		parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
582 
583 		lwsl_wsi_debug(wsi, "ESTABLISHED_CLIENT_HTTP: "
584 			   "prepared %d headers (len %d)",
585 			   lws_http_client_http_response(wsi),
586 			   (int)parent->http.prh_content_length);
587 
588 		/*
589 		 * so at this point, the onward client connection can bear
590 		 * traffic.  We might be doing a POST and have pending cached
591 		 * inbound stuff to send, it can go now.
592 		 */
593 
594 		lws_callback_on_writable(parent);
595 
596 		break; }
597 
598 	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
599 		lwsl_wsi_info(wsi, "COMPLETED_CLIENT_HTTP: (parent %s)",
600 				   lws_wsi_tag(lws_get_parent(wsi)));
601 		if (!lws_get_parent(wsi))
602 			break;
603 		lws_get_parent(wsi)->reason_bf |=
604 				LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
605 		lws_callback_on_writable(lws_get_parent(wsi));
606 		break;
607 
608 	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
609 		if (!lws_get_parent(wsi))
610 			break;
611 	//	lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
612                lws_set_timeout(lws_get_parent(wsi),
613         		       (enum pending_timeout)LWS_TO_KILL_ASYNC,
614                                (int)PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
615 		break;
616 
617 	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
618 		parent = lws_get_parent(wsi);
619 		if (!parent)
620 			break;
621 
622 		p = (unsigned char **)in;
623 		end = (*p) + len;
624 
625 		/*
626 		 * copy these headers from the parent request to the client
627 		 * connection's request
628 		 */
629 
630 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
631 				WSI_TOKEN_HTTP_ETAG, p, end);
632 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
633 				WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
634 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
635 				WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
636 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
637 				WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
638 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
639 				WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
640 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
641 				WSI_TOKEN_HTTP_COOKIE, p, end);
642 
643 		buf[0] = '\0';
644 		lws_get_peer_simple(parent, buf, sizeof(buf));
645 		if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
646 				(unsigned char *)buf, (int)strlen(buf), p, end))
647 			return -1;
648 
649 		break;
650 #endif
651 
652 #ifdef LWS_WITH_CGI
653 	/* CGI IO events (POLLIN/OUT) appear here, our default policy is:
654 	 *
655 	 *  - POST data goes on subprocess stdin
656 	 *  - subprocess stdout goes on http via writeable callback
657 	 *  - subprocess stderr goes to the logs
658 	 */
659 	case LWS_CALLBACK_CGI:
660 		args = (struct lws_cgi_args *)in;
661 		switch (args->ch) { /* which of stdin/out/err ? */
662 		case LWS_STDIN:
663 			/* TBD stdin rx flow control */
664 			break;
665 		case LWS_STDOUT:
666 			if (args->stdwsi[LWS_STDOUT])
667 				/* quench POLLIN on STDOUT until MASTER got writeable */
668 				lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
669 			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
670 			/* when writing to MASTER would not block */
671 			lws_callback_on_writable(wsi);
672 			break;
673 		case LWS_STDERR:
674 			n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
675 			if (n < 0)
676 				break;
677 			n = (int)read(n, buf, sizeof(buf) - 2);
678 			if (n > 0) {
679 				if (buf[n - 1] != '\n')
680 					buf[n++] = '\n';
681 				buf[n] = '\0';
682 				lwsl_wsi_notice(wsi, "CGI-stderr: %s", buf);
683 			}
684 			break;
685 		}
686 		break;
687 
688 	case LWS_CALLBACK_CGI_TERMINATED:
689 		lwsl_wsi_debug(wsi, "CGI_TERMINATED: %d %" PRIu64,
690 				wsi->http.cgi->explicitly_chunked,
691 				(uint64_t)wsi->http.cgi->content_length);
692 		if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
693 		    !wsi->http.cgi->content_length) {
694 			/* send terminating chunk */
695 			lwsl_wsi_debug(wsi, "LWS_CALLBACK_CGI_TERMINATED: ending");
696 			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
697 			lws_callback_on_writable(wsi);
698 			lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
699 			break;
700 		}
701 		if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
702 			lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
703 						      LWS_WRITE_HTTP_FINAL);
704 #if defined(LWS_WITH_SERVER)
705 		if (lws_http_transaction_completed(wsi))
706 			return -1;
707 #endif
708 		return 0;
709 
710 	case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
711 		args = (struct lws_cgi_args *)in;
712 		args->data[args->len] = '\0';
713 		if (!args->stdwsi[LWS_STDIN])
714 			return -1;
715 		n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
716 		if (n < 0)
717 			return -1;
718 
719 #if defined(LWS_WITH_ZLIB)
720 		if (wsi->http.cgi->gzip_inflate) {
721 			/* gzip handling */
722 
723 			if (!wsi->http.cgi->gzip_init) {
724 				lwsl_wsi_info(wsi, "inflating gzip");
725 
726 				memset(&wsi->http.cgi->inflate, 0,
727 				       sizeof(wsi->http.cgi->inflate));
728 
729 				if (inflateInit2(&wsi->http.cgi->inflate,
730 						 16 + 15) != Z_OK) {
731 					lwsl_wsi_err(wsi, "iniflateInit fail");
732 					return -1;
733 				}
734 
735 				wsi->http.cgi->gzip_init = 1;
736 			}
737 
738 			wsi->http.cgi->inflate.next_in = args->data;
739 			wsi->http.cgi->inflate.avail_in = (unsigned int)args->len;
740 
741 			do {
742 
743 				wsi->http.cgi->inflate.next_out =
744 						wsi->http.cgi->inflate_buf;
745 				wsi->http.cgi->inflate.avail_out =
746 					sizeof(wsi->http.cgi->inflate_buf);
747 
748 				n = inflate(&wsi->http.cgi->inflate,
749 					    Z_SYNC_FLUSH);
750 
751 				switch (n) {
752 				case Z_NEED_DICT:
753 				case Z_STREAM_ERROR:
754 				case Z_DATA_ERROR:
755 				case Z_MEM_ERROR:
756 					inflateEnd(&wsi->http.cgi->inflate);
757 					wsi->http.cgi->gzip_init = 0;
758 					lwsl_wsi_err(wsi, "zlib err inflate %d", n);
759 					return -1;
760 				}
761 
762 				if (wsi->http.cgi->inflate.avail_out !=
763 					   sizeof(wsi->http.cgi->inflate_buf)) {
764 					int written;
765 
766 					written = (int)write(args->stdwsi[LWS_STDIN]->desc.filefd,
767 						wsi->http.cgi->inflate_buf,
768 						sizeof(wsi->http.cgi->inflate_buf) -
769 						wsi->http.cgi->inflate.avail_out);
770 
771 					if (written != (int)(
772 						sizeof(wsi->http.cgi->inflate_buf) -
773 						wsi->http.cgi->inflate.avail_out)) {
774 						lwsl_wsi_notice(wsi,
775 							"CGI_STDIN_DATA: "
776 							"sent %d only %d went",
777 							n, args->len);
778 					}
779 
780 					if (n == Z_STREAM_END) {
781 						lwsl_wsi_err(wsi,
782 							    "gzip inflate end");
783 						inflateEnd(&wsi->http.cgi->inflate);
784 						wsi->http.cgi->gzip_init = 0;
785 						break;
786 					}
787 
788 				} else
789 					break;
790 
791 				if (wsi->http.cgi->inflate.avail_out)
792 					break;
793 
794 			} while (1);
795 
796 			return args->len;
797 		}
798 #endif /* WITH_ZLIB */
799 
800 		n = (int)write(n, args->data, (unsigned int)args->len);
801 //		lwsl_hexdump_notice(args->data, args->len);
802 		if (n < args->len)
803 			lwsl_wsi_notice(wsi, "CGI_STDIN_DATA: "
804 				    "sent %d only %d went", n, args->len);
805 
806 		lwsl_wsi_info(wsi, "proxied %d bytes", n);
807 
808 		if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
809 		    args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
810 			wsi->http.cgi->post_in_expected -= (unsigned int)n;
811 
812 			if (!wsi->http.cgi->post_in_expected) {
813 				struct lws *siwsi = args->stdwsi[LWS_STDIN];
814 
815 				/*
816 				 * The situation here is that we finished
817 				 * proxying the incoming body from the net to
818 				 * the STDIN stdwsi... and we want to close it
819 				 * so it can understand we are done (necessary
820 				 * if no content-length)...
821 				 */
822 
823 				lwsl_wsi_info(siwsi, "expected POST in end: "
824 						     "closing stdin fd %d",
825 						     siwsi->desc.sockfd);
826 
827 				/*
828 				 * We don't want the child / parent relationship
829 				 * to be handled in close, since we want the
830 				 * rest of the cgi and children to stay up
831 				 */
832 
833 				lws_remove_child_from_any_parent(siwsi);
834 				lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC);
835 				wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL;
836 				lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi);
837 			}
838 		}
839 
840 		return n;
841 #endif /* WITH_CGI */
842 #endif /* ROLE_ H1 / H2 */
843 	case LWS_CALLBACK_SSL_INFO:
844 		si = in;
845 
846 		(void)si;
847 		lwsl_wsi_notice(wsi, "SSL_INFO: where: 0x%x, ret: 0x%x",
848 				si->where, si->ret);
849 		break;
850 
851 #if LWS_MAX_SMP > 1
852 	case LWS_CALLBACK_GET_THREAD_ID:
853 #ifdef __PTW32_H
854 		/* If we use implementation of PThreads for Win that is
855 		 * distributed by VCPKG */
856 		return (int)(lws_intptr_t)(pthread_self()).p;
857 #else
858 		return (int)(lws_intptr_t)pthread_self();
859 #endif // __PTW32_H
860 #endif
861 
862 	default:
863 		break;
864 	}
865 
866 	return 0;
867 }
868