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 #if defined(LWS_WITH_HTTP_PROXY)
28 static int
proxy_header(struct lws * wsi,struct lws * par,unsigned char * temp,int temp_len,int index,unsigned char ** p,unsigned char * end)29 proxy_header(struct lws *wsi, struct lws *par, unsigned char *temp,
30 	     int temp_len, int index, unsigned char **p, unsigned char *end)
31 {
32 	int n = lws_hdr_total_length(par, index);
33 
34 	if (n < 1) {
35 		lwsl_debug("%s: no index %d:\n", __func__, index);
36 		return 0;
37 	}
38 
39 	if (lws_hdr_copy(par, (char *)temp, temp_len, index) < 0)
40 		return -1;
41 
42 	lwsl_debug("%s: index %d: %s\n", __func__, index, (char *)temp);
43 
44 	if (lws_add_http_header_by_token(wsi, index, temp, n, p, end))
45 		return -1;
46 
47 	return 0;
48 }
49 
50 static int
stream_close(struct lws * wsi)51 stream_close(struct lws *wsi)
52 {
53 	char buf[LWS_PRE + 6], *out = buf + LWS_PRE;
54 
55 	if (wsi->http.did_stream_close)
56 		return 0;
57 
58 	wsi->http.did_stream_close = 1;
59 
60 	if (wsi->mux_substream) {
61 		if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
62 			      LWS_WRITE_HTTP_FINAL) < 0) {
63 			lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n",
64 				  __func__);
65 
66 			return -1;
67 		}
68 	} else {
69 		*out++ = '0';
70 		*out++ = '\x0d';
71 		*out++ = '\x0a';
72 		*out++ = '\x0d';
73 		*out++ = '\x0a';
74 
75 		if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5,
76 			      LWS_WRITE_HTTP_FINAL) < 0) {
77 			lwsl_err("%s: COMPL_CLIENT_HTTP: "
78 				 "h2 final write failed\n", __func__);
79 
80 			return -1;
81 		}
82 	}
83 
84 	return 0;
85 }
86 
87 #endif
88 
89 struct lws_proxy_pkt {
90 	struct lws_dll2 pkt_list;
91 	size_t len;
92 	char binary;
93 	char first;
94 	char final;
95 
96 	/* data follows */
97 };
98 
99 #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS)
100 int
lws_callback_ws_proxy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)101 lws_callback_ws_proxy(struct lws *wsi, enum lws_callback_reasons reason,
102 			void *user, void *in, size_t len)
103 {
104 	struct lws_proxy_pkt *pkt;
105 	struct lws_dll2 *dll;
106 
107 	switch (reason) {
108 
109 	/* h1 ws proxying... child / client / onward */
110 
111 	case LWS_CALLBACK_CLIENT_ESTABLISHED:
112 		if (!wsi->h1_ws_proxied || !wsi->parent)
113 			break;
114 
115 		lws_process_ws_upgrade2(wsi->parent);
116 
117 #if defined(LWS_WITH_HTTP2)
118 		if (wsi->parent->mux_substream)
119 			lwsl_info("%s: proxied h2 -> h1 ws established\n", __func__);
120 #endif
121 		break;
122 
123 	case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
124 		return 1;
125 
126 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
127 	case LWS_CALLBACK_CLIENT_CLOSED:
128 		lwsl_user("%s: client closed: parent %p\n", __func__, wsi->parent);
129 		if (wsi->parent)
130                        lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
131 		break;
132 
133 	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
134 	{
135 		unsigned char **p = (unsigned char **)in, *end = (*p) + len,
136 				    tmp[128];
137 
138 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
139 			      WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
140 
141 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
142 			      WSI_TOKEN_HTTP_COOKIE, p, end);
143 
144 		proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
145 			      WSI_TOKEN_HTTP_SET_COOKIE, p, end);
146 		break;
147 	}
148 
149 	case LWS_CALLBACK_CLIENT_RECEIVE:
150 		wsi->parent->ws->proxy_buffered += len;
151 		if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
152 			lwsl_err("%s: proxied ws connection excessive buffering: dropping\n",
153 					__func__);
154 			return -1;
155 		}
156 		pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
157 		if (!pkt)
158 			return -1;
159 
160 		pkt->len = len;
161 		pkt->first = lws_is_first_fragment(wsi);
162 		pkt->final = lws_is_final_fragment(wsi);
163 		pkt->binary = lws_frame_is_binary(wsi);
164 
165 		memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
166 
167 		lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
168 		lws_callback_on_writable(wsi->parent);
169 		break;
170 
171 	case LWS_CALLBACK_CLIENT_WRITEABLE:
172 		dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
173 		if (!dll)
174 			break;
175 
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)
181 			return -1;
182 
183 		lws_dll2_remove(dll);
184 		lws_free(pkt);
185 
186 		if (lws_dll2_get_head(&wsi->ws->proxy_owner))
187 			lws_callback_on_writable(wsi);
188 		break;
189 
190 	/* h1 ws proxying... parent / server / incoming */
191 
192 	case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
193 		return 1;
194 
195 	case LWS_CALLBACK_CLOSED:
196 		lwsl_user("%s: closed\n", __func__);
197 		return -1;
198 
199 	case LWS_CALLBACK_RECEIVE:
200 		pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
201 		if (!pkt)
202 			return -1;
203 
204 		pkt->len = len;
205 		pkt->first = lws_is_first_fragment(wsi);
206 		pkt->final = lws_is_final_fragment(wsi);
207 		pkt->binary = lws_frame_is_binary(wsi);
208 
209 		memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
210 
211 		lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
212 		lws_callback_on_writable(wsi->child_list);
213 		break;
214 
215 	case LWS_CALLBACK_SERVER_WRITEABLE:
216 		dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
217 		if (!dll)
218 			break;
219 
220 		pkt = (struct lws_proxy_pkt *)dll;
221 		if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
222 			      LWS_PRE, pkt->len, lws_write_ws_flags(
223 				pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
224 					pkt->first, pkt->final)) < 0)
225 			return -1;
226 
227 		wsi->ws->proxy_buffered -= pkt->len;
228 
229 		lws_dll2_remove(dll);
230 		lws_free(pkt);
231 
232 		if (lws_dll2_get_head(&wsi->ws->proxy_owner))
233 			lws_callback_on_writable(wsi);
234 		break;
235 
236 	default:
237 		return 0;
238 	}
239 
240 	return 0;
241 }
242 
243 const struct lws_protocols lws_ws_proxy = {
244 		"lws-ws-proxy",
245 		lws_callback_ws_proxy,
246 		0,
247 		8192,
248 		8192, NULL, 0
249 };
250 
251 #endif
252 
253 int
lws_callback_http_dummy(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)254 lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
255 			void *user, void *in, size_t len)
256 {
257 	struct lws_ssl_info *si;
258 #ifdef LWS_WITH_CGI
259 	struct lws_cgi_args *args;
260 #endif
261 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
262 	char buf[LWS_PRE + 32 + 8192];
263 	int n;
264 #endif
265 #if defined(LWS_WITH_HTTP_PROXY)
266 	unsigned char **p, *end;
267 	struct lws *parent;
268 #endif
269 
270 	switch (reason) {
271 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
272 	case LWS_CALLBACK_HTTP:
273 #if defined(LWS_WITH_SERVER)
274 		if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
275 			return -1;
276 
277 		if (lws_http_transaction_completed(wsi))
278 #endif
279 			return -1;
280 		break;
281 #if defined(LWS_WITH_SERVER)
282 	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
283 #if defined(LWS_WITH_HTTP_PROXY)
284 		if (wsi->child_list) {
285 			lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len);
286 			break;
287 		}
288 #endif
289 		/* fallthru */
290 	case LWS_CALLBACK_HTTP_FILE_COMPLETION:
291 		if (lws_http_transaction_completed(wsi))
292 			return -1;
293 		break;
294 #endif
295 
296 #if defined(LWS_WITH_HTTP_PROXY)
297 	case LWS_CALLBACK_HTTP_BODY:
298 		if (wsi->child_list) {
299 			lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len);
300 			if (lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len) < 0)
301 				return -1;
302 			lws_callback_on_writable(wsi->child_list);
303 		}
304 		break;
305 #endif
306 
307 	case LWS_CALLBACK_HTTP_WRITEABLE:
308 		// lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
309 #ifdef LWS_WITH_CGI
310 		if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
311 				      LWS_CB_REASON_AUX_BF__CGI)) {
312 			n = lws_cgi_write_split_stdout_headers(wsi);
313 			if (n < 0) {
314 				lwsl_debug("AUX_BF__CGI forcing close\n");
315 				return -1;
316 			}
317 			if (!n && wsi->http.cgi &&
318 			    wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
319 				lws_rx_flow_control(
320 					wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1);
321 
322 			if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
323 				wsi->reason_bf &=
324 					~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
325 			else
326 				wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI;
327 
328 			if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over)
329 				return -1;
330 			break;
331 		}
332 
333 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) {
334 			if (!wsi->mux_substream) {
335 				memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
336 				lwsl_debug("writing chunk term and exiting\n");
337 				lws_write(wsi, (unsigned char *)buf +
338 						   LWS_PRE, 5, LWS_WRITE_HTTP);
339 			} else
340 				lws_write(wsi, (unsigned char *)buf +
341 						   LWS_PRE, 0,
342 						   LWS_WRITE_HTTP_FINAL);
343 
344 			/* always close after sending it */
345 			if (lws_http_transaction_completed(wsi))
346 				return -1;
347 			return 0;
348 		}
349 #endif
350 #if defined(LWS_WITH_HTTP_PROXY)
351 
352 		if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
353 
354 			wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
355 
356 			n = LWS_WRITE_HTTP_HEADERS;
357 			if (!wsi->http.prh_content_length)
358 				n |= LWS_WRITE_H2_STREAM_END;
359 
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 +
363 					   LWS_PRE,
364 				      wsi->http.pending_return_headers_len, n);
365 
366 			lws_free_set_NULL(wsi->http.pending_return_headers);
367 
368 			if (n < 0) {
369 				lwsl_err("%s: EST_CLIENT_HTTP: write failed\n",
370 					 __func__);
371 				return -1;
372 			}
373 
374 			lws_callback_on_writable(wsi);
375 			break;
376 		}
377 
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;
381 
382 			/*
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
386 			 * is the smaller.
387 			 */
388 			wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY;
389 			if (!lws_get_child(wsi))
390 				break;
391 
392 			/* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
393 			if (lws_http_client_read(lws_get_child(wsi), &px,
394 						 &lenx) < 0) {
395 				lwsl_info("%s: LWS_CB_REASON_AUX_BF__PROXY: "
396 					   "client closed\n", __func__);
397 
398 				stream_close(wsi);
399 
400 				return -1;
401 			}
402 			break;
403 		}
404 
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",
407 				   __func__);
408 
409 			wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
410 
411 			if (stream_close(wsi))
412 				return -1;
413 
414 			if (lws_http_transaction_completed(wsi))
415 				return -1;
416 		}
417 #endif
418 		break;
419 
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))
424 			break;
425 		lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
426 		lws_callback_on_writable(lws_get_parent(wsi));
427 		break;
428 
429 	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
430 		char *out = buf + LWS_PRE;
431 
432 		assert(lws_get_parent(wsi));
433 
434 		if (wsi->http.proxy_parent_chunked) {
435 
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);
439 				return -1;
440 			}
441 
442 			/*
443 			 * this only needs dealing with on http/1.1 to allow
444 			 * pipelining
445 			 */
446 			n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
447 			out += n;
448 			memcpy(out, in, len);
449 			out += len;
450 			*out++ = '\x0d';
451 			*out++ = '\x0a';
452 
453 			n = lws_write(lws_get_parent(wsi),
454 				      (unsigned char *)buf + LWS_PRE,
455 				      len + n + 2, LWS_WRITE_HTTP);
456 		} else
457 			n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
458 				      len, LWS_WRITE_HTTP);
459 		if (n < 0)
460 			return -1;
461 		break; }
462 
463 	/* h1 http proxying... */
464 
465 	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
466 		unsigned char *start, *p, *end;
467 
468 		/*
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
472 		 * connection.
473 		 *
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.
477 		 */
478 
479 		parent = lws_get_parent(wsi);
480 
481 		if (!parent)
482 			return 0;
483 
484 		start = p = (unsigned char *)buf + LWS_PRE;
485 		end = p + sizeof(buf) - LWS_PRE - 256;
486 
487 		if (lws_add_http_header_status(lws_get_parent(wsi),
488 				lws_http_client_http_response(wsi), &p, end))
489 			return 1;
490 
491 		/*
492 		 * copy these headers from the client connection to the parent
493 		 */
494 
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);
511 
512 		if (!parent->mux_substream)
513 			if (lws_add_http_header_by_token(parent,
514 				WSI_TOKEN_CONNECTION, (unsigned char *)"close",
515 				5, &p, end))
516 			return -1;
517 
518 		/*
519 		 * We proxy using h1 only atm, and strip any chunking so it
520 		 * can go back out on h2 just fine.
521 		 *
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.
524 		 */
525 
526 		if (!parent->mux_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))
532 				return -1;
533 
534 			wsi->http.proxy_parent_chunked = 1;
535 		}
536 
537 		if (lws_finalize_http_header(parent, &p, end))
538 			return 1;
539 
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));
545 
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)
551 			return -1;
552 
553 		memcpy(parent->http.pending_return_headers + LWS_PRE, start,
554 		       parent->http.pending_return_headers_len);
555 
556 		parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
557 
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);
562 
563 		/*
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.
567 		 */
568 
569 		lws_callback_on_writable(parent);
570 
571 		break; }
572 
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))
577 			break;
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));
581 		break;
582 
583 	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
584 		if (!lws_get_parent(wsi))
585 			break;
586 	//	lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
587                lws_set_timeout(lws_get_parent(wsi), LWS_TO_KILL_ASYNC,
588                                PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
589 		break;
590 
591 	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
592 		parent = lws_get_parent(wsi);
593 		if (!parent)
594 			break;
595 
596 		p = (unsigned char **)in;
597 		end = (*p) + len;
598 
599 		/*
600 		 * copy these headers from the parent request to the client
601 		 * connection's request
602 		 */
603 
604 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
605 				WSI_TOKEN_HTTP_ETAG, p, end);
606 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
607 				WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
608 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
609 				WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
610 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
611 				WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
612 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
613 				WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
614 		proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
615 				WSI_TOKEN_HTTP_COOKIE, p, end);
616 
617 		buf[0] = '\0';
618 		lws_get_peer_simple(parent, buf, sizeof(buf));
619 		if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
620 				(unsigned char *)buf, (int)strlen(buf), p, end))
621 			return -1;
622 
623 		break;
624 #endif
625 
626 #ifdef LWS_WITH_CGI
627 	/* CGI IO events (POLLIN/OUT) appear here, our default policy is:
628 	 *
629 	 *  - POST data goes on subprocess stdin
630 	 *  - subprocess stdout goes on http via writeable callback
631 	 *  - subprocess stderr goes to the logs
632 	 */
633 	case LWS_CALLBACK_CGI:
634 		args = (struct lws_cgi_args *)in;
635 		switch (args->ch) { /* which of stdin/out/err ? */
636 		case LWS_STDIN:
637 			/* TBD stdin rx flow control */
638 			break;
639 		case LWS_STDOUT:
640 			/* quench POLLIN on STDOUT until MASTER got writeable */
641 			lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
642 			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
643 			/* when writing to MASTER would not block */
644 			lws_callback_on_writable(wsi);
645 			break;
646 		case LWS_STDERR:
647 			n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
648 			if (n < 0)
649 				break;
650 			n = read(n, buf, sizeof(buf) - 2);
651 			if (n > 0) {
652 				if (buf[n - 1] != '\n')
653 					buf[n++] = '\n';
654 				buf[n] = '\0';
655 				lwsl_notice("CGI-stderr: %s\n", buf);
656 			}
657 			break;
658 		}
659 		break;
660 
661 	case LWS_CALLBACK_CGI_TERMINATED:
662 		lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: %d %" PRIu64 "\n",
663 				wsi->http.cgi->explicitly_chunked,
664 				(uint64_t)wsi->http.cgi->content_length);
665 		if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
666 		    !wsi->http.cgi->content_length) {
667 			/* send terminating chunk */
668 			lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: ending\n");
669 			wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
670 			lws_callback_on_writable(wsi);
671 			lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
672 			break;
673 		}
674 		if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
675 			lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
676 						      LWS_WRITE_HTTP_FINAL);
677 
678 		if (lws_http_transaction_completed(wsi))
679 			return -1;
680 		return 0;
681 
682 	case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
683 		args = (struct lws_cgi_args *)in;
684 		args->data[args->len] = '\0';
685 		if (!args->stdwsi[LWS_STDIN])
686 			return -1;
687 		n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
688 		if (n < 0)
689 			return -1;
690 
691 #if defined(LWS_WITH_ZLIB)
692 		if (wsi->http.cgi->gzip_inflate) {
693 			/* gzip handling */
694 
695 			if (!wsi->http.cgi->gzip_init) {
696 				lwsl_info("inflating gzip\n");
697 
698 				memset(&wsi->http.cgi->inflate, 0,
699 				       sizeof(wsi->http.cgi->inflate));
700 
701 				if (inflateInit2(&wsi->http.cgi->inflate,
702 						 16 + 15) != Z_OK) {
703 					lwsl_err("%s: iniflateInit failed\n",
704 						 __func__);
705 					return -1;
706 				}
707 
708 				wsi->http.cgi->gzip_init = 1;
709 			}
710 
711 			wsi->http.cgi->inflate.next_in = args->data;
712 			wsi->http.cgi->inflate.avail_in = args->len;
713 
714 			do {
715 
716 				wsi->http.cgi->inflate.next_out =
717 						wsi->http.cgi->inflate_buf;
718 				wsi->http.cgi->inflate.avail_out =
719 					sizeof(wsi->http.cgi->inflate_buf);
720 
721 				n = inflate(&wsi->http.cgi->inflate,
722 					    Z_SYNC_FLUSH);
723 
724 				switch (n) {
725 				case Z_NEED_DICT:
726 				case Z_STREAM_ERROR:
727 				case Z_DATA_ERROR:
728 				case Z_MEM_ERROR:
729 					inflateEnd(&wsi->http.cgi->inflate);
730 					wsi->http.cgi->gzip_init = 0;
731 					lwsl_err("zlib error inflate %d\n", n);
732 					return -1;
733 				}
734 
735 				if (wsi->http.cgi->inflate.avail_out !=
736 					   sizeof(wsi->http.cgi->inflate_buf)) {
737 					int written;
738 
739 					written = write(args->stdwsi[LWS_STDIN]->desc.filefd,
740 						wsi->http.cgi->inflate_buf,
741 						sizeof(wsi->http.cgi->inflate_buf) -
742 						wsi->http.cgi->inflate.avail_out);
743 
744 					if (written != (int)(
745 						sizeof(wsi->http.cgi->inflate_buf) -
746 						wsi->http.cgi->inflate.avail_out)) {
747 						lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
748 							"sent %d only %d went", n, args->len);
749 					}
750 
751 					if (n == Z_STREAM_END) {
752 						lwsl_err("gzip inflate end\n");
753 						inflateEnd(&wsi->http.cgi->inflate);
754 						wsi->http.cgi->gzip_init = 0;
755 						break;
756 					}
757 
758 				} else
759 					break;
760 
761 				if (wsi->http.cgi->inflate.avail_out)
762 					break;
763 
764 			} while (1);
765 
766 			return args->len;
767 		}
768 #endif /* WITH_ZLIB */
769 
770 		n = write(n, args->data, args->len);
771 //		lwsl_hexdump_notice(args->data, args->len);
772 		if (n < args->len)
773 			lwsl_notice("LWS_CALLBACK_CGI_STDIN_DATA: "
774 				    "sent %d only %d went", n, args->len);
775 
776 		if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
777 		    args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
778 			wsi->http.cgi->post_in_expected -= n;
779 			if (!wsi->http.cgi->post_in_expected) {
780 				struct lws *siwsi = args->stdwsi[LWS_STDIN];
781 
782 				lwsl_debug("%s: expected POST in end: "
783 					   "closing stdin wsi %p, fd %d\n",
784 					   __func__, siwsi, siwsi->desc.sockfd);
785 
786 				__remove_wsi_socket_from_fds(siwsi);
787 				lwsi_set_state(siwsi, LRS_DEAD_SOCKET);
788 				siwsi->socket_is_permanently_unusable = 1;
789 //				lws_remove_child_from_any_parent(siwsi);
790 				if (wsi->context->event_loop_ops->
791 							close_handle_manually) {
792 
793 					wsi->context->event_loop_ops->
794 						close_handle_manually(siwsi);
795 					siwsi->told_event_loop_closed = 1;
796 				} else {
797 					compatible_close(siwsi->desc.sockfd);
798 					__lws_free_wsi(siwsi);
799 				}
800 				wsi->http.cgi->lsp->pipe_fds[LWS_STDIN][1] = -1;
801 
802 //				args->stdwsi[LWS_STDIN] = NULL;
803 			}
804 		}
805 
806 		return n;
807 #endif /* WITH_CGI */
808 #endif /* ROLE_ H1 / H2 */
809 	case LWS_CALLBACK_SSL_INFO:
810 		si = in;
811 
812 		(void)si;
813 		lwsl_notice("LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n",
814 			    si->where, si->ret);
815 		break;
816 
817 #if LWS_MAX_SMP > 1
818 	case LWS_CALLBACK_GET_THREAD_ID:
819 		return (int)(unsigned long long)pthread_self();
820 #endif
821 
822 	default:
823 		break;
824 	}
825 
826 	return 0;
827 }
828