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)
31static int
32proxy_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
60static int
61stream_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
90bail:
91	lwsl_wsi_info(wsi, "h2 fin wr failed");
92
93	return -1;
94}
95
96#endif
97
98struct 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)
109int
110lws_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
254const 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
265int
266lws_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		if (wsi->http.cgi) {
690			lwsl_wsi_debug(wsi, "CGI_TERMINATED: %d %" PRIu64,
691				wsi->http.cgi->explicitly_chunked,
692				(uint64_t)wsi->http.cgi->content_length);
693			if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
694			    !wsi->http.cgi->content_length) {
695				/* send terminating chunk */
696				lwsl_wsi_debug(wsi, "LWS_CALLBACK_CGI_TERMINATED: ending");
697				wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
698				lws_callback_on_writable(wsi);
699				lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
700				break;
701			}
702			if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
703				lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
704						      LWS_WRITE_HTTP_FINAL);
705		}
706#if defined(LWS_WITH_SERVER)
707		if (lws_http_transaction_completed(wsi))
708			return -1;
709#endif
710		return 0;
711
712	case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
713		args = (struct lws_cgi_args *)in;
714		args->data[args->len] = '\0';
715		if (!args->stdwsi[LWS_STDIN])
716			return -1;
717		n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
718		if (n < 0)
719			return -1;
720
721#if defined(LWS_WITH_ZLIB)
722		if (wsi->http.cgi->gzip_inflate) {
723			/* gzip handling */
724
725			if (!wsi->http.cgi->gzip_init) {
726				lwsl_wsi_info(wsi, "inflating gzip");
727
728				memset(&wsi->http.cgi->inflate, 0,
729				       sizeof(wsi->http.cgi->inflate));
730
731				if (inflateInit2(&wsi->http.cgi->inflate,
732						 16 + 15) != Z_OK) {
733					lwsl_wsi_err(wsi, "iniflateInit fail");
734					return -1;
735				}
736
737				wsi->http.cgi->gzip_init = 1;
738			}
739
740			wsi->http.cgi->inflate.next_in = args->data;
741			wsi->http.cgi->inflate.avail_in = (unsigned int)args->len;
742
743			do {
744
745				wsi->http.cgi->inflate.next_out =
746						wsi->http.cgi->inflate_buf;
747				wsi->http.cgi->inflate.avail_out =
748					sizeof(wsi->http.cgi->inflate_buf);
749
750				n = inflate(&wsi->http.cgi->inflate,
751					    Z_SYNC_FLUSH);
752
753				switch (n) {
754				case Z_NEED_DICT:
755				case Z_STREAM_ERROR:
756				case Z_DATA_ERROR:
757				case Z_MEM_ERROR:
758					inflateEnd(&wsi->http.cgi->inflate);
759					wsi->http.cgi->gzip_init = 0;
760					lwsl_wsi_err(wsi, "zlib err inflate %d", n);
761					return -1;
762				}
763
764				if (wsi->http.cgi->inflate.avail_out !=
765					   sizeof(wsi->http.cgi->inflate_buf)) {
766					int written;
767
768					written = (int)write(args->stdwsi[LWS_STDIN]->desc.filefd,
769						wsi->http.cgi->inflate_buf,
770						sizeof(wsi->http.cgi->inflate_buf) -
771						wsi->http.cgi->inflate.avail_out);
772
773					if (written != (int)(
774						sizeof(wsi->http.cgi->inflate_buf) -
775						wsi->http.cgi->inflate.avail_out)) {
776						lwsl_wsi_notice(wsi,
777							"CGI_STDIN_DATA: "
778							"sent %d only %d went",
779							n, args->len);
780					}
781
782					if (n == Z_STREAM_END) {
783						lwsl_wsi_err(wsi,
784							    "gzip inflate end");
785						inflateEnd(&wsi->http.cgi->inflate);
786						wsi->http.cgi->gzip_init = 0;
787						break;
788					}
789
790				} else
791					break;
792
793				if (wsi->http.cgi->inflate.avail_out)
794					break;
795
796			} while (1);
797
798			return args->len;
799		}
800#endif /* WITH_ZLIB */
801
802		n = (int)write(n, args->data, (unsigned int)args->len);
803//		lwsl_hexdump_notice(args->data, args->len);
804		if (n < args->len)
805			lwsl_wsi_notice(wsi, "CGI_STDIN_DATA: "
806				    "sent %d only %d went", n, args->len);
807
808		lwsl_wsi_info(wsi, "proxied %d bytes", n);
809
810		if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
811		    args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
812			wsi->http.cgi->post_in_expected -= (unsigned int)n;
813
814			if (!wsi->http.cgi->post_in_expected) {
815				struct lws *siwsi = args->stdwsi[LWS_STDIN];
816
817				/*
818				 * The situation here is that we finished
819				 * proxying the incoming body from the net to
820				 * the STDIN stdwsi... and we want to close it
821				 * so it can understand we are done (necessary
822				 * if no content-length)...
823				 */
824
825				lwsl_wsi_info(siwsi, "expected POST in end: "
826						     "closing stdin fd %d",
827						     siwsi->desc.sockfd);
828
829				/*
830				 * We don't want the child / parent relationship
831				 * to be handled in close, since we want the
832				 * rest of the cgi and children to stay up
833				 */
834
835				lws_remove_child_from_any_parent(siwsi);
836				lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC);
837				wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL;
838				lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi);
839			}
840		}
841
842		return n;
843#endif /* WITH_CGI */
844#endif /* ROLE_ H1 / H2 */
845	case LWS_CALLBACK_SSL_INFO:
846		si = in;
847
848		(void)si;
849		lwsl_wsi_notice(wsi, "SSL_INFO: where: 0x%x, ret: 0x%x",
850				si->where, si->ret);
851		break;
852
853#if LWS_MAX_SMP > 1
854	case LWS_CALLBACK_GET_THREAD_ID:
855#ifdef __PTW32_H
856		/* If we use implementation of PThreads for Win that is
857		 * distributed by VCPKG */
858		return (int)(lws_intptr_t)(pthread_self()).p;
859#else
860		return (int)(lws_intptr_t)pthread_self();
861#endif // __PTW32_H
862#endif
863
864	default:
865		break;
866	}
867
868	return 0;
869}
870