Coverage Report

Created: 2026-04-09 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/core-net/dummy-callback.c
Line
Count
Source
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
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
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
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
    /*
125
     * If the parent has started to close, don't try to
126
     * upgrade it, just let it go.
127
     */
128
    if ((lwsi_state(wsi->parent) & 0xff) >= (LRS_RETURNED_CLOSE & 0xff))
129
      return -1;
130
131
    if (lws_process_ws_upgrade2(wsi->parent))
132
      return -1;
133
134
#if defined(LWS_WITH_HTTP2)
135
    if (wsi->parent->mux_substream)
136
      lwsl_wsi_info(wsi, "proxied h2 -> h1 ws established");
137
#endif
138
    break;
139
140
  case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
141
    return 1;
142
143
  case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
144
  case LWS_CALLBACK_CLIENT_CLOSED:
145
    lwsl_wsi_info(wsi, "client closed: parent %s",
146
           lws_wsi_tag(wsi->parent));
147
    if (wsi->parent)
148
                       lws_set_timeout(wsi->parent, 1, LWS_TO_KILL_ASYNC);
149
    break;
150
151
  case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
152
  {
153
    unsigned char **p = (unsigned char **)in, *end = (*p) + len,
154
            tmp[MAXHDRVAL];
155
    char peer[64];
156
157
    proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
158
            WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
159
160
    proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
161
            WSI_TOKEN_HTTP_COOKIE, p, end);
162
163
    proxy_header(wsi, wsi->parent, tmp, sizeof(tmp),
164
            WSI_TOKEN_HTTP_SET_COOKIE, p, end);
165
166
    lws_get_peer_simple(wsi->parent, peer, sizeof(peer));
167
    
168
    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
169
             (uint8_t *)peer, (int)strlen(peer), p, end))
170
                  lwsl_wsi_notice(wsi, "unable to append forwarded_for");
171
172
    break;
173
  }
174
175
  case LWS_CALLBACK_CLIENT_RECEIVE:
176
    wsi->parent->ws->proxy_buffered += len;
177
    if (wsi->parent->ws->proxy_buffered > 10 * 1024 * 1024) {
178
      lwsl_wsi_err(wsi, "proxied ws connection "
179
            "excessive buffering: dropping");
180
      return -1;
181
    }
182
    pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
183
    if (!pkt)
184
      return -1;
185
186
    pkt->len = len;
187
    pkt->first = (char)lws_is_first_fragment(wsi);
188
    pkt->final = (char)lws_is_final_fragment(wsi);
189
    pkt->binary = (char)lws_frame_is_binary(wsi);
190
191
    memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
192
193
    lws_dll2_add_tail(&pkt->pkt_list, &wsi->parent->ws->proxy_owner);
194
    lws_callback_on_writable(wsi->parent);
195
    break;
196
197
  case LWS_CALLBACK_CLIENT_WRITEABLE:
198
    dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
199
    if (!dll)
200
      break;
201
202
    pkt = (struct lws_proxy_pkt *)dll;
203
    if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
204
            LWS_PRE, pkt->len, lws_write_ws_flags(
205
        pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
206
          pkt->first, pkt->final)) < 0)
207
      return -1;
208
209
    lws_dll2_remove(dll);
210
    lws_free(pkt);
211
212
    if (lws_dll2_get_head(&wsi->ws->proxy_owner))
213
      lws_callback_on_writable(wsi);
214
    break;
215
216
  /* h1 ws proxying... parent / server / incoming */
217
218
  case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
219
    return 1;
220
221
  case LWS_CALLBACK_CLOSED:
222
    lwsl_wsi_info(wsi, "closed");
223
    return -1;
224
225
  case LWS_CALLBACK_RECEIVE:
226
227
               if (!wsi->child_list) {
228
                       lwsl_wsi_warn(wsi, "Proxy Srv side RX: no child");
229
                       break;
230
               }
231
               if (!wsi->child_list->ws) {
232
                       lwsl_wsi_warn(wsi, "Proxy Srv side RX: child does not have ws");
233
                       break;
234
               }
235
236
    pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__);
237
    if (!pkt)
238
      return -1;
239
240
    pkt->len = len;
241
    pkt->first = (char)lws_is_first_fragment(wsi);
242
    pkt->final = (char)lws_is_final_fragment(wsi);
243
    pkt->binary = (char)lws_frame_is_binary(wsi);
244
245
    memcpy(((uint8_t *)&pkt[1]) + LWS_PRE, in, len);
246
247
    lws_dll2_add_tail(&pkt->pkt_list, &wsi->child_list->ws->proxy_owner);
248
    lws_callback_on_writable(wsi->child_list);
249
    break;
250
251
  case LWS_CALLBACK_SERVER_WRITEABLE:
252
    dll = lws_dll2_get_head(&wsi->ws->proxy_owner);
253
    if (!dll)
254
      break;
255
256
    pkt = (struct lws_proxy_pkt *)dll;
257
    if (lws_write(wsi, ((unsigned char *)&pkt[1]) +
258
            LWS_PRE, pkt->len, lws_write_ws_flags(
259
        pkt->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
260
          pkt->first, pkt->final)) < 0)
261
      return -1;
262
263
    wsi->ws->proxy_buffered -= pkt->len;
264
265
    lws_dll2_remove(dll);
266
    lws_free(pkt);
267
268
    if (lws_dll2_get_head(&wsi->ws->proxy_owner))
269
      lws_callback_on_writable(wsi);
270
    break;
271
272
  default:
273
    return 0;
274
  }
275
276
  return 0;
277
}
278
279
const struct lws_protocols lws_ws_proxy = {
280
    "lws-ws-proxy",
281
    lws_callback_ws_proxy,
282
    0,
283
    8192,
284
    8192, NULL, 0
285
};
286
287
#endif
288
289
290
int
291
lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
292
      void *user, void *in, size_t len)
293
0
{
294
0
  struct lws_ssl_info *si;
295
#ifdef LWS_WITH_CGI
296
  struct lws_cgi_args *args;
297
#endif
298
#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
299
  char buf[LWS_PRE + 32 + 8192];
300
  int n;
301
#endif
302
#if defined(LWS_WITH_HTTP_PROXY)
303
  unsigned char **p, *end;
304
  struct lws *parent;
305
#endif
306
307
0
  switch (reason) {
308
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
309
0
  case LWS_CALLBACK_HTTP:
310
0
#if defined(LWS_WITH_SERVER)
311
0
    if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
312
0
      return -1;
313
314
0
    if (lws_http_transaction_completed(wsi))
315
0
#endif
316
0
      return -1;
317
0
    break;
318
0
#if defined(LWS_WITH_SERVER)
319
0
  case LWS_CALLBACK_HTTP_BODY_COMPLETION:
320
#if defined(LWS_WITH_HTTP_PROXY)
321
    if (wsi->child_list) {
322
      lwsl_wsi_info(wsi, "HTTP_BODY_COMPLETION: %d",
323
             (int)len);
324
      lws_callback_on_writable(wsi->child_list);
325
      break;
326
    }
327
#endif
328
0
    if (lws_return_http_status(wsi, 200, NULL))
329
0
      return -1;
330
0
    break;
331
332
    /* fallthru */
333
0
  case LWS_CALLBACK_HTTP_FILE_COMPLETION:
334
0
    if (lws_http_transaction_completed(wsi))
335
0
      return -1;
336
0
    break;
337
0
#endif
338
339
#if defined(LWS_WITH_HTTP_PROXY)
340
  case LWS_CALLBACK_HTTP_BODY:
341
    if (wsi->child_list) {
342
      lwsl_wsi_info(wsi, "HTTP_BODY: stashing %d", (int)len);
343
      if (lws_buflist_append_segment(
344
             &wsi->http.buflist_post_body, in, len) < 0)
345
        return -1;
346
      lws_client_http_body_pending(wsi->child_list, 1);
347
      lws_callback_on_writable(wsi->child_list);
348
    }
349
    break;
350
#endif
351
352
0
  case LWS_CALLBACK_HTTP_WRITEABLE:
353
    // lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__);
354
#ifdef LWS_WITH_CGI
355
    if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS |
356
              LWS_CB_REASON_AUX_BF__CGI)) {
357
      n = lws_cgi_write_split_stdout_headers(wsi);
358
      if (n < 0) {
359
        lwsl_wsi_debug(wsi, "AUX_BF__CGI forcing close");
360
        return -1;
361
      }
362
      if (!n && wsi->http.cgi && wsi->http.cgi->lsp &&
363
          wsi->http.cgi->lsp->stdwsi[LWS_STDOUT])
364
        lws_rx_flow_control(
365
          wsi->http.cgi->lsp->stdwsi[LWS_STDOUT], 1);
366
367
      if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS)
368
        wsi->reason_bf &=
369
          (char)~LWS_CB_REASON_AUX_BF__CGI_HEADERS;
370
      else
371
        wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__CGI;
372
373
      if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) {
374
        lwsl_wsi_info(wsi, "txn over");
375
        return -1;
376
      }
377
378
      break;
379
    }
380
381
    if ((wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) ||
382
        (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END)) {
383
      if (!wsi->mux_substream) {
384
        memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5);
385
        lwsl_wsi_debug(wsi, "wr chunk term and exiting");
386
        lws_write(wsi, (unsigned char *)buf +
387
               LWS_PRE, 5, LWS_WRITE_HTTP);
388
      } else
389
        lws_write(wsi, (unsigned char *)buf +
390
               LWS_PRE, 0,
391
               LWS_WRITE_HTTP_FINAL);
392
393
      /* always close after sending it */
394
#if defined(LWS_WITH_SERVER)
395
      if (lws_http_transaction_completed(wsi))
396
#endif
397
        return -1;
398
      return 0;
399
    }
400
#endif
401
#if defined(LWS_WITH_HTTP_PROXY)
402
403
    if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_HEADERS) {
404
405
      wsi->reason_bf &=
406
             (char)~LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
407
408
      n = LWS_WRITE_HTTP_HEADERS;
409
      if (!wsi->http.prh_content_length)
410
        n |= LWS_WRITE_H2_STREAM_END;
411
412
      lwsl_wsi_debug(wsi, "issuing proxy headers: clen %d",
413
            (int)wsi->http.prh_content_length);
414
      n = lws_write(wsi, wsi->http.pending_return_headers +
415
             LWS_PRE,
416
              wsi->http.pending_return_headers_len,
417
              (enum lws_write_protocol)n);
418
419
      lws_free_set_NULL(wsi->http.pending_return_headers);
420
421
      if (n < 0) {
422
        lwsl_wsi_err(wsi, "EST_CLIENT_HTTP: wr failed");
423
424
        return -1;
425
      }
426
427
      lws_callback_on_writable(wsi);
428
      break;
429
    }
430
431
    if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY) {
432
      char *px = buf + LWS_PRE;
433
      int lenx = sizeof(buf) - LWS_PRE - 32;
434
435
      /*
436
       * our sink is writeable and our source has something
437
       * to read.  So read a lump of source material of
438
       * suitable size to send or what's available, whichever
439
       * is the smaller.
440
       */
441
      wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY;
442
      if (!lws_get_child(wsi))
443
        break;
444
445
#if defined(LWS_WITH_LATENCY)
446
      lws_usec_t _proxy_rd_start = lws_now_usecs();
447
#endif
448
449
      /* this causes LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ */
450
      if (lws_http_client_read(lws_get_child(wsi), &px,
451
             &lenx) < 0) {
452
        lwsl_wsi_info(wsi, "LWS_CB_REASON_AUX_BF__PROXY: "
453
             "client closed");
454
455
        stream_close(wsi);
456
457
        return -1;
458
      }
459
460
#if defined(LWS_WITH_LATENCY)
461
      {
462
        unsigned int ms = (unsigned int)((lws_now_usecs() - _proxy_rd_start) / 1000);
463
        if (ms > 2) {
464
          struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
465
          lws_latency_note(pt, _proxy_rd_start, 2000, "proxyrd:%dms", ms);
466
        }
467
      }
468
#endif
469
      break;
470
    }
471
472
    if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) {
473
      lwsl_wsi_info(wsi, "PROXY_TRANS_END");
474
475
      wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
476
477
      if (stream_close(wsi))
478
        return -1;
479
480
#if defined(LWS_WITH_SERVER)
481
      if (lws_http_transaction_completed(wsi))
482
#endif
483
        return -1;
484
    }
485
#endif
486
0
    if (wsi->http.deferred_transaction_completed) {
487
0
      uint8_t zero = 0;
488
0
      lws_write(wsi, &zero, 0, LWS_WRITE_HTTP_FINAL);
489
0
#if defined(LWS_WITH_SERVER)
490
0
      if (lws_http_transaction_completed(wsi))
491
0
#endif
492
0
        return -1;
493
0
      return 0;
494
0
    }
495
496
0
    break;
497
498
#if defined(LWS_WITH_HTTP_PROXY)
499
  case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
500
    assert(lws_get_parent(wsi));
501
    if (!lws_get_parent(wsi))
502
      break;
503
    lws_get_parent(wsi)->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY;
504
    lws_callback_on_writable(lws_get_parent(wsi));
505
    break;
506
507
  case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: {
508
    char *out = buf + LWS_PRE;
509
510
    assert(lws_get_parent(wsi));
511
512
#if defined(LWS_WITH_LATENCY)
513
    lws_usec_t _proxy_wr_start = lws_now_usecs();
514
#endif
515
516
    if (wsi->http.proxy_parent_chunked) {
517
518
      if (len > sizeof(buf) - LWS_PRE - 16) {
519
        lwsl_wsi_err(wsi, "oversize buf %d %d", (int)len,
520
            (int)sizeof(buf) - LWS_PRE - 16);
521
        return -1;
522
      }
523
524
      /*
525
       * this only needs dealing with on http/1.1 to allow
526
       * pipelining
527
       */
528
      n = lws_snprintf(out, 14, "%X\x0d\x0a", (int)len);
529
      out += n;
530
      memcpy(out, in, len);
531
      out += len;
532
      *out++ = '\x0d';
533
      *out++ = '\x0a';
534
535
      n = lws_write(lws_get_parent(wsi),
536
              (unsigned char *)buf + LWS_PRE,
537
              (size_t)(unsigned int)(len + (unsigned int)n + 2), LWS_WRITE_HTTP);
538
    } else
539
      n = lws_write(lws_get_parent(wsi), (unsigned char *)in,
540
              len, LWS_WRITE_HTTP);
541
542
#if defined(LWS_WITH_LATENCY)
543
    {
544
      unsigned int ms = (unsigned int)((lws_now_usecs() - _proxy_wr_start) / 1000);
545
      if (ms > 2) {
546
        struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
547
        lws_latency_note(pt, _proxy_wr_start, 2000, "proxywr:%dms", ms);
548
      }
549
    }
550
#endif
551
552
    if (n < 0)
553
      return -1;
554
    break; }
555
556
  /* h1 http proxying... */
557
558
  case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: {
559
    unsigned char *start, *p, *end;
560
561
    /*
562
     * We want to proxy these headers, but we are being called
563
     * at the point the onward client was established, which is
564
     * unrelated to the state or writability of our proxy
565
     * connection.
566
     *
567
     * Therefore produce the headers using the onward client ah
568
     * while we have it, and stick them on the output buflist to be
569
     * written on the proxy connection as soon as convenient.
570
     */
571
572
    parent = lws_get_parent(wsi);
573
574
    if (!parent)
575
      return 0;
576
577
    start = p = (unsigned char *)buf + LWS_PRE;
578
    end = p + sizeof(buf) - LWS_PRE - MAXHDRVAL;
579
580
    if (lws_add_http_header_status(lws_get_parent(wsi),
581
        lws_http_client_http_response(wsi), &p, end))
582
      return 1;
583
584
    /*
585
     * copy these headers from the client connection to the parent
586
     */
587
588
    proxy_header(parent, wsi, end, MAXHDRVAL,
589
           WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end);
590
    proxy_header(parent, wsi, end, MAXHDRVAL,
591
           WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end);
592
    proxy_header(parent, wsi, end, MAXHDRVAL,
593
           WSI_TOKEN_HTTP_ETAG, &p, end);
594
    proxy_header(parent, wsi, end, MAXHDRVAL,
595
           WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end);
596
    proxy_header(parent, wsi, end, MAXHDRVAL,
597
           WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end);
598
    proxy_header(parent, wsi, end, MAXHDRVAL,
599
           WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end);
600
    proxy_header(parent, wsi, end, MAXHDRVAL,
601
           WSI_TOKEN_HTTP_SET_COOKIE, &p, end);
602
    proxy_header(parent, wsi, end, MAXHDRVAL,
603
           WSI_TOKEN_HTTP_LOCATION, &p, end);
604
605
    if (!parent->mux_substream)
606
      if (lws_add_http_header_by_token(parent,
607
        WSI_TOKEN_CONNECTION, (unsigned char *)"close",
608
        5, &p, end))
609
      return -1;
610
611
    /*
612
     * We proxy using h1 only atm, and strip any chunking so it
613
     * can go back out on h2 just fine.
614
     *
615
     * However if we are actually going out on h1, we need to add
616
     * our own chunking since we still don't know the size.
617
     */
618
619
    if (!parent->mux_substream &&
620
        !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
621
      lwsl_wsi_debug(wsi, "downstream parent chunked");
622
      if (lws_add_http_header_by_token(parent,
623
          WSI_TOKEN_HTTP_TRANSFER_ENCODING,
624
          (unsigned char *)"chunked", 7, &p, end))
625
        return -1;
626
627
      wsi->http.proxy_parent_chunked = 1;
628
    }
629
630
    if (lws_finalize_http_header(parent, &p, end))
631
      return 1;
632
633
    parent->http.prh_content_length = (size_t)-1;
634
    if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH))
635
      parent->http.prh_content_length = (size_t)atoll(
636
        lws_hdr_simple_ptr(wsi,
637
            WSI_TOKEN_HTTP_CONTENT_LENGTH));
638
639
    parent->http.pending_return_headers_len = lws_ptr_diff_size_t(p, start);
640
    parent->http.pending_return_headers =
641
      lws_malloc(parent->http.pending_return_headers_len +
642
            LWS_PRE, "return proxy headers");
643
    if (!parent->http.pending_return_headers)
644
      return -1;
645
646
    memcpy(parent->http.pending_return_headers + LWS_PRE, start,
647
           parent->http.pending_return_headers_len);
648
649
    parent->reason_bf |= LWS_CB_REASON_AUX_BF__PROXY_HEADERS;
650
651
    lwsl_wsi_debug(wsi, "ESTABLISHED_CLIENT_HTTP: "
652
         "prepared %d headers (len %d)",
653
         lws_http_client_http_response(wsi),
654
         (int)parent->http.prh_content_length);
655
656
    /*
657
     * so at this point, the onward client connection can bear
658
     * traffic.  We might be doing a POST and have pending cached
659
     * inbound stuff to send, it can go now.
660
     */
661
662
    lws_callback_on_writable(parent);
663
664
    break; }
665
666
  case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
667
    lwsl_wsi_info(wsi, "COMPLETED_CLIENT_HTTP: (parent %s)",
668
           lws_wsi_tag(lws_get_parent(wsi)));
669
    if (!lws_get_parent(wsi))
670
      break;
671
    lws_get_parent(wsi)->reason_bf |=
672
        LWS_CB_REASON_AUX_BF__PROXY_TRANS_END;
673
    lws_callback_on_writable(lws_get_parent(wsi));
674
    break;
675
676
  case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
677
    if (!lws_get_parent(wsi))
678
      break;
679
  //  lwsl_err("%s: LWS_CALLBACK_CLOSED_CLIENT_HTTP\n", __func__);
680
               lws_set_timeout(lws_get_parent(wsi),
681
                   (enum pending_timeout)LWS_TO_KILL_ASYNC,
682
                               (int)PENDING_TIMEOUT_KILLED_BY_PROXY_CLIENT_CLOSE);
683
    break;
684
685
  case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
686
    parent = lws_get_parent(wsi);
687
    if (!parent)
688
      break;
689
690
    p = (unsigned char **)in;
691
    end = (*p) + len;
692
693
    /*
694
     * copy these headers from the parent request to the client
695
     * connection's request
696
     */
697
698
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
699
        WSI_TOKEN_HTTP_ETAG, p, end);
700
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
701
        WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, p, end);
702
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
703
        WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, p, end);
704
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
705
        WSI_TOKEN_HTTP_ACCEPT_ENCODING, p, end);
706
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
707
        WSI_TOKEN_HTTP_CACHE_CONTROL, p, end);
708
    proxy_header(wsi, parent, (unsigned char *)buf, sizeof(buf),
709
        WSI_TOKEN_HTTP_COOKIE, p, end);
710
711
    buf[0] = '\0';
712
    lws_get_peer_simple(parent, buf, sizeof(buf));
713
    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_X_FORWARDED_FOR,
714
        (unsigned char *)buf, (int)strlen(buf), p, end))
715
      return -1;
716
717
    break;
718
#endif
719
720
#ifdef LWS_WITH_CGI
721
  /* CGI IO events (POLLIN/OUT) appear here, our default policy is:
722
   *
723
   *  - POST data goes on subprocess stdin
724
   *  - subprocess stdout goes on http via writeable callback
725
   *  - subprocess stderr goes to the logs
726
   */
727
  case LWS_CALLBACK_CGI:
728
    args = (struct lws_cgi_args *)in;
729
    switch (args->ch) { /* which of stdin/out/err ? */
730
    case LWS_STDIN:
731
      /* TBD stdin rx flow control */
732
      break;
733
    case LWS_STDOUT:
734
      if (args->stdwsi[LWS_STDOUT])
735
        /* quench POLLIN on STDOUT until MASTER got writeable */
736
        lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0);
737
      wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI;
738
      /* when writing to MASTER would not block */
739
      lws_callback_on_writable(wsi);
740
      break;
741
    case LWS_STDERR:
742
      n = lws_get_socket_fd(args->stdwsi[LWS_STDERR]);
743
      if (n < 0)
744
        break;
745
      n = (int)read(n, buf, sizeof(buf) - 2);
746
      if (n > 0) {
747
        if (buf[n - 1] != '\n')
748
          buf[n++] = '\n';
749
        buf[n] = '\0';
750
        lwsl_wsi_notice(wsi, "CGI-stderr: %s", buf);
751
      }
752
      break;
753
    }
754
    break;
755
756
  case LWS_CALLBACK_CGI_TERMINATED:
757
    if (wsi->http.cgi) {
758
      lwsl_wsi_debug(wsi, "CGI_TERMINATED: %d %" PRIu64,
759
        wsi->http.cgi->explicitly_chunked,
760
        (uint64_t)wsi->http.cgi->content_length);
761
      if (!(wsi->http.cgi->explicitly_chunked && wsi->mux_substream) &&
762
          !wsi->http.cgi->content_length) {
763
        /* send terminating chunk */
764
        lwsl_wsi_debug(wsi, "LWS_CALLBACK_CGI_TERMINATED: ending");
765
        wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END;
766
        lws_callback_on_writable(wsi);
767
        lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3);
768
        break;
769
      }
770
      if (wsi->mux_substream && !wsi->cgi_stdout_zero_length)
771
        lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0,
772
                  LWS_WRITE_HTTP_FINAL);
773
    }
774
#if defined(LWS_WITH_SERVER)
775
    if (lws_http_transaction_completed(wsi))
776
      return -1;
777
#endif
778
    return 0;
779
780
  case LWS_CALLBACK_CGI_STDIN_DATA:  /* POST body for stdin */
781
    args = (struct lws_cgi_args *)in;
782
    args->data[args->len] = '\0';
783
    if (!args->stdwsi[LWS_STDIN])
784
      return -1;
785
    n = lws_get_socket_fd(args->stdwsi[LWS_STDIN]);
786
    if (n < 0)
787
      return -1;
788
789
#if defined(LWS_WITH_ZLIB)
790
    if (wsi->http.cgi->gzip_inflate) {
791
      /* gzip handling */
792
793
      if (!wsi->http.cgi->gzip_init) {
794
        lwsl_wsi_info(wsi, "inflating gzip");
795
796
        memset(&wsi->http.cgi->inflate, 0,
797
               sizeof(wsi->http.cgi->inflate));
798
799
        if (inflateInit2(&wsi->http.cgi->inflate,
800
             16 + 15) != Z_OK) {
801
          lwsl_wsi_err(wsi, "iniflateInit fail");
802
          return -1;
803
        }
804
805
        wsi->http.cgi->gzip_init = 1;
806
      }
807
808
      wsi->http.cgi->inflate.next_in = args->data;
809
      wsi->http.cgi->inflate.avail_in = (unsigned int)args->len;
810
811
      do {
812
813
        wsi->http.cgi->inflate.next_out =
814
            wsi->http.cgi->inflate_buf;
815
        wsi->http.cgi->inflate.avail_out =
816
          sizeof(wsi->http.cgi->inflate_buf);
817
818
        n = inflate(&wsi->http.cgi->inflate,
819
              Z_SYNC_FLUSH);
820
821
        switch (n) {
822
        case Z_NEED_DICT:
823
        case Z_STREAM_ERROR:
824
        case Z_DATA_ERROR:
825
        case Z_MEM_ERROR:
826
          inflateEnd(&wsi->http.cgi->inflate);
827
          wsi->http.cgi->gzip_init = 0;
828
          lwsl_wsi_err(wsi, "zlib err inflate %d", n);
829
          return -1;
830
        }
831
832
        if (wsi->http.cgi->inflate.avail_out !=
833
             sizeof(wsi->http.cgi->inflate_buf)) {
834
          int written;
835
836
          written = (int)write(args->stdwsi[LWS_STDIN]->desc.filefd,
837
            wsi->http.cgi->inflate_buf,
838
            sizeof(wsi->http.cgi->inflate_buf) -
839
            wsi->http.cgi->inflate.avail_out);
840
841
          if (written != (int)(
842
            sizeof(wsi->http.cgi->inflate_buf) -
843
            wsi->http.cgi->inflate.avail_out)) {
844
            lwsl_wsi_notice(wsi,
845
              "CGI_STDIN_DATA: "
846
              "sent %d only %d went",
847
              n, args->len);
848
          }
849
850
          if (n == Z_STREAM_END) {
851
            lwsl_wsi_err(wsi,
852
                  "gzip inflate end");
853
            inflateEnd(&wsi->http.cgi->inflate);
854
            wsi->http.cgi->gzip_init = 0;
855
            break;
856
          }
857
858
        } else
859
          break;
860
861
        if (wsi->http.cgi->inflate.avail_out)
862
          break;
863
864
      } while (1);
865
866
      return args->len;
867
    }
868
#endif /* WITH_ZLIB */
869
870
    n = (int)write(n, args->data, (unsigned int)args->len);
871
//    lwsl_hexdump_notice(args->data, args->len);
872
    if (n < args->len)
873
      lwsl_wsi_notice(wsi, "CGI_STDIN_DATA: "
874
            "sent %d only %d went", n, args->len);
875
876
    lwsl_wsi_info(wsi, "proxied %d bytes", n);
877
878
    if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] &&
879
        args->stdwsi[LWS_STDIN]->desc.filefd > 0) {
880
      wsi->http.cgi->post_in_expected -= (unsigned int)n;
881
882
      if (!wsi->http.cgi->post_in_expected) {
883
        struct lws *siwsi = args->stdwsi[LWS_STDIN];
884
885
        /*
886
         * The situation here is that we finished
887
         * proxying the incoming body from the net to
888
         * the STDIN stdwsi... and we want to close it
889
         * so it can understand we are done (necessary
890
         * if no content-length)...
891
         */
892
893
        lwsl_wsi_info(siwsi, "expected POST in end: "
894
                 "closing stdin fd %d",
895
                 siwsi->desc.sockfd);
896
897
        /*
898
         * We don't want the child / parent relationship
899
         * to be handled in close, since we want the
900
         * rest of the cgi and children to stay up
901
         */
902
903
        lws_remove_child_from_any_parent(siwsi);
904
        lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC);
905
        wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL;
906
        lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi);
907
      }
908
    }
909
910
    return n;
911
#endif /* WITH_CGI */
912
0
#endif /* ROLE_ H1 / H2 */
913
0
  case LWS_CALLBACK_SSL_INFO:
914
0
    si = in;
915
916
0
    (void)si;
917
0
    lwsl_wsi_notice(wsi, "SSL_INFO: where: 0x%x, ret: 0x%x",
918
0
        si->where, si->ret);
919
0
    break;
920
921
#if LWS_MAX_SMP > 1
922
  case LWS_CALLBACK_GET_THREAD_ID:
923
#ifdef __PTW32_H
924
    /* If we use implementation of PThreads for Win that is
925
     * distributed by VCPKG */
926
    return (int)(lws_intptr_t)(pthread_self()).p;
927
#else
928
    return (int)(lws_intptr_t)pthread_self();
929
#endif // __PTW32_H
930
#endif
931
932
0
  default:
933
0
    break;
934
0
  }
935
936
0
  return 0;
937
0
}