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