Coverage Report

Created: 2026-03-19 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/roles/http/header.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2020 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
#include "lextable-strings.h"
27
28
29
const unsigned char *
30
lws_token_to_string(enum lws_token_indexes token)
31
0
{
32
0
  if ((unsigned int)token >= LWS_ARRAY_SIZE(set))
33
0
    return NULL;
34
35
0
  return (unsigned char *)set[token];
36
0
}
37
38
/*
39
 * Return http header index if one matches slen chars of s, or -1
40
 */
41
42
int
43
lws_http_string_to_known_header(const char *s, size_t slen)
44
0
{
45
0
  int n;
46
47
0
  for (n = 0; n < (int)LWS_ARRAY_SIZE(set); n++)
48
0
    if (!strncmp(set[n], s, slen))
49
0
      return n;
50
51
0
  return LWS_HTTP_NO_KNOWN_HEADER;
52
0
}
53
54
#ifdef LWS_WITH_HTTP2
55
int
56
lws_wsi_is_h2(struct lws *wsi)
57
0
{
58
0
  return wsi->upgraded_to_http2 ||
59
0
         wsi->mux_substream ||
60
0
#if defined(LWS_WITH_CLIENT)
61
0
         wsi->client_mux_substream ||
62
0
#endif
63
0
         lwsi_role_h2(wsi) ||
64
0
         lwsi_role_h2_ENCAPSULATION(wsi);
65
0
}
66
#endif
67
68
int
69
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
70
          const unsigned char *value, int length,
71
          unsigned char **p, unsigned char *end)
72
0
{
73
0
#ifdef LWS_WITH_HTTP2
74
0
  if (lws_wsi_is_h2(wsi))
75
0
    return lws_add_http2_header_by_name(wsi, name,
76
0
                value, length, p, end);
77
#else
78
  (void)wsi;
79
#endif
80
0
  if (name) {
81
0
    char has_colon = 0;
82
0
    while (*p < end && *name) {
83
0
      has_colon = has_colon || *name == ':';
84
0
      *((*p)++) = *name++;
85
0
    }
86
0
    if (*p + (has_colon ? 1 : 2) >= end)
87
0
      return 1;
88
0
    if (!has_colon)
89
0
      *((*p)++) = ':';
90
0
    *((*p)++) = ' ';
91
0
  }
92
0
  if (*p + length + 3 >= end)
93
0
    return 1;
94
95
0
  if (value)
96
0
    memcpy(*p, value, (unsigned int)length);
97
0
  *p += length;
98
0
  *((*p)++) = '\x0d';
99
0
  *((*p)++) = '\x0a';
100
101
0
  return 0;
102
0
}
103
104
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
105
           unsigned char *end)
106
0
{
107
0
#ifdef LWS_WITH_HTTP2
108
0
  if (lws_wsi_is_h2(wsi))
109
0
    return 0;
110
#else
111
  (void)wsi;
112
#endif
113
0
  if ((lws_intptr_t)(end - *p) < 3)
114
0
    return 1;
115
0
  *((*p)++) = '\x0d';
116
0
  *((*p)++) = '\x0a';
117
118
0
  return 0;
119
0
}
120
121
int
122
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
123
             unsigned char **pp, unsigned char *end)
124
0
{
125
0
  unsigned char *p;
126
0
  int len;
127
128
0
  if (lws_finalize_http_header(wsi, pp, end))
129
0
    return 1;
130
131
0
  p = *pp;
132
0
  len = lws_ptr_diff(p, start);
133
134
0
  if (lws_write(wsi, start, (unsigned int)len, LWS_WRITE_HTTP_HEADERS) != len)
135
0
    return 1;
136
137
0
  return 0;
138
0
}
139
140
int
141
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
142
           const unsigned char *value, int length,
143
           unsigned char **p, unsigned char *end)
144
0
{
145
0
  const unsigned char *name;
146
0
#ifdef LWS_WITH_HTTP2
147
0
  if (lws_wsi_is_h2(wsi))
148
0
    return lws_add_http2_header_by_token(wsi, token, value,
149
0
                 length, p, end);
150
0
#endif
151
0
  name = lws_token_to_string(token);
152
0
  if (!name)
153
0
    return 1;
154
155
0
  return lws_add_http_header_by_name(wsi, name, value, length, p, end);
156
0
}
157
158
int
159
lws_add_http_header_content_length(struct lws *wsi,
160
           lws_filepos_t content_length,
161
           unsigned char **p, unsigned char *end)
162
0
{
163
0
  char b[24];
164
0
  int n;
165
166
0
  n = lws_snprintf(b, sizeof(b) - 1, "%llu", (unsigned long long)content_length);
167
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
168
0
           (unsigned char *)b, n, p, end))
169
0
    return 1;
170
0
  wsi->http.tx_content_length = content_length;
171
0
  wsi->http.tx_content_remain = content_length;
172
173
0
  lwsl_info("%s: %s: tx_content_length/remain %llu\n", __func__,
174
0
      lws_wsi_tag(wsi), (unsigned long long)content_length);
175
176
0
  return 0;
177
0
}
178
179
#if defined(LWS_WITH_SERVER)
180
181
int
182
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
183
          const char *content_type, lws_filepos_t content_len,
184
          unsigned char **p, unsigned char *end)
185
0
{
186
0
  const char *ka[] = { "close", "keep-alive" };
187
0
  int types[] = { HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEP_ALIVE },
188
0
      t = 0;
189
190
0
  if (lws_add_http_header_status(wsi, code, p, end))
191
0
    return 1;
192
193
0
  if (content_type &&
194
0
      lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
195
0
              (unsigned char *)content_type,
196
0
              (int)strlen(content_type), p, end))
197
0
    return 1;
198
199
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
200
  if (!wsi->http.lcs && content_type &&
201
      (!strncmp(content_type, "text/", 5) ||
202
       !strcmp(content_type, "application/javascript") ||
203
       !strcmp(content_type, "image/svg+xml")))
204
    lws_http_compression_apply(wsi, NULL, p, end, 0);
205
#endif
206
207
  /*
208
   * if we decided to compress it, we don't know the content length...
209
   * the compressed data will go out chunked on h1
210
   */
211
0
  if (
212
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
213
      !wsi->http.lcs &&
214
#endif
215
0
       content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN) {
216
0
    if (lws_add_http_header_content_length(wsi, content_len,
217
0
                   p, end))
218
0
      return 1;
219
0
  } else {
220
    /* there was no length... it normally means CONNECTION_CLOSE */
221
#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
222
223
    if (!wsi->mux_substream && wsi->http.lcs) {
224
      /* so...
225
       *  - h1 connection
226
       *  - http compression transform active
227
       *  - did not send content length
228
       *
229
       * then mark as chunked...
230
       */
231
      wsi->http.comp_ctx.chunking = 1;
232
      if (lws_add_http_header_by_token(wsi,
233
          WSI_TOKEN_HTTP_TRANSFER_ENCODING,
234
          (unsigned char *)"chunked", 7, p, end))
235
        return -1;
236
237
      /* ... but h1 compression is chunked, if active we can
238
       * still pipeline
239
       */
240
      if (wsi->http.lcs &&
241
          wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE)
242
        t = 1;
243
    }
244
#endif
245
0
    if (!wsi->mux_substream) {
246
0
      if (lws_add_http_header_by_token(wsi,
247
0
             WSI_TOKEN_CONNECTION,
248
0
             (unsigned char *)ka[t],
249
0
             (int)strlen(ka[t]), p, end))
250
0
        return 1;
251
252
0
      wsi->http.conn_type = (enum http_conn_type)types[t];
253
0
    }
254
0
  }
255
256
0
  return 0;
257
0
}
258
259
static const char * const err400[] = {
260
  "Bad Request",
261
  "Unauthorized",
262
  "Payment Required",
263
  "Forbidden",
264
  "Not Found",
265
  "Method Not Allowed",
266
  "Not Acceptable",
267
  "Proxy Auth Required",
268
  "Request Timeout",
269
  "Conflict",
270
  "Gone",
271
  "Length Required",
272
  "Precondition Failed",
273
  "Request Entity Too Large",
274
  "Request URI too Long",
275
  "Unsupported Media Type",
276
  "Requested Range Not Satisfiable",
277
  "Expectation Failed"
278
};
279
280
static const char * const err500[] = {
281
  "Internal Server Error",
282
  "Not Implemented",
283
  "Bad Gateway",
284
  "Service Unavailable",
285
  "Gateway Timeout",
286
  "HTTP Version Not Supported"
287
};
288
289
/* security best practices from Mozilla Observatory */
290
291
static const
292
struct lws_protocol_vhost_options pvo_hsbph[] = {{
293
  NULL, NULL, "referrer-policy:", "no-referrer"
294
}, {
295
  &pvo_hsbph[0], NULL, "x-frame-options:", "deny"
296
}, {
297
  &pvo_hsbph[1], NULL, "x-xss-protection:", "1; mode=block"
298
}, {
299
  &pvo_hsbph[2], NULL, "x-content-type-options:", "nosniff"
300
}, {
301
  &pvo_hsbph[3], NULL, "content-security-policy:",
302
  "default-src 'none'; img-src 'self' data: ; "
303
    "script-src 'self'; font-src 'self'; "
304
    "style-src 'self'; connect-src 'self' ws: wss:; "
305
    "frame-ancestors 'none'; base-uri 'none';"
306
    "form-action 'self';"
307
}};
308
309
int
310
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
311
         unsigned char **p, unsigned char *end)
312
0
{
313
0
  static const char * const hver[] = {
314
0
    "HTTP/1.0", "HTTP/1.1", "HTTP/2"
315
0
  };
316
0
  const struct lws_protocol_vhost_options *headers, *ho;
317
0
  unsigned int code = _code & LWSAHH_CODE_MASK;
318
0
  const char *description = "", *p1;
319
0
  unsigned char code_and_desc[60];
320
0
  int n;
321
322
0
  wsi->http.response_code = code;
323
#ifdef LWS_WITH_ACCESS_LOG
324
  wsi->http.access_log.response = (int)code;
325
#endif
326
327
0
#ifdef LWS_WITH_HTTP2
328
0
  if (lws_wsi_is_h2(wsi)) {
329
0
    n = lws_add_http2_header_status(wsi, code, p, end);
330
0
    if (n)
331
0
      return n;
332
0
  } else
333
0
#endif
334
0
  {
335
0
    if (code >= 400 && code < (400 + LWS_ARRAY_SIZE(err400)))
336
0
      description = err400[code - 400];
337
0
    if (code >= 500 && code < (500 + LWS_ARRAY_SIZE(err500)))
338
0
      description = err500[code - 500];
339
340
0
    if (code == 100)
341
0
      description = "Continue";
342
0
    if (code == 200)
343
0
      description = "OK";
344
0
    if (code == 304)
345
0
      description = "Not Modified";
346
0
    else
347
0
      if (code >= 300 && code < 400)
348
0
        description = "Redirect";
349
350
0
    if (wsi->http.request_version < LWS_ARRAY_SIZE(hver))
351
0
      p1 = hver[wsi->http.request_version];
352
0
    else
353
0
      p1 = hver[0];
354
355
0
    n = lws_snprintf((char *)code_and_desc,
356
0
         sizeof(code_and_desc) - 1, "%s %u %s",
357
0
         p1, code, description);
358
359
0
    if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p,
360
0
            end))
361
0
      return 1;
362
0
  }
363
364
0
  headers = wsi->a.vhost->headers;
365
366
0
  while (headers) {
367
    /*
368
     * Give mount headers the chance to individually override
369
     * each vhost header
370
     */
371
0
    ho = wsi->http.mount_specific_headers;
372
0
    while (ho) {
373
0
      if (!strcmp(ho->name, headers->name))
374
0
        break;
375
0
      ho = ho->next;
376
0
    }
377
    /* if there was no mount header of same name, use vhost one */
378
0
    if (!ho)
379
0
      ho = headers;
380
381
0
    if (lws_add_http_header_by_name(wsi,
382
0
        (const unsigned char *)ho->name,
383
0
        (unsigned char *)ho->value,
384
0
        (int)strlen(ho->value), p, end))
385
0
      return 1;
386
387
0
    headers = headers->next;
388
0
  }
389
390
0
  if (wsi->a.vhost->options &
391
0
      LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE) {
392
0
    headers = &pvo_hsbph[LWS_ARRAY_SIZE(pvo_hsbph) - 1];
393
0
    while (headers) {
394
0
      if (lws_add_http_header_by_name(wsi,
395
0
          (const unsigned char *)headers->name,
396
0
          (unsigned char *)headers->value,
397
0
          (int)strlen(headers->value), p, end))
398
0
        return 1;
399
400
0
      headers = headers->next;
401
0
    }
402
0
  }
403
404
0
  if (wsi->a.context->server_string &&
405
0
      !(_code & LWSAHH_FLAG_NO_SERVER_NAME)) {
406
0
    assert(wsi->a.context->server_string_len > 0);
407
0
    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
408
0
        (unsigned char *)wsi->a.context->server_string,
409
0
        wsi->a.context->server_string_len, p, end))
410
0
      return 1;
411
0
  }
412
413
0
  if (wsi->a.vhost->options & LWS_SERVER_OPTION_STS)
414
0
    if (lws_add_http_header_by_name(wsi, (unsigned char *)
415
0
        "Strict-Transport-Security:",
416
0
        (unsigned char *)"max-age=15768000 ; "
417
0
        "includeSubDomains", 36, p, end))
418
0
      return 1;
419
420
0
  if (*p >= (end - 2)) {
421
0
    lwsl_err("%s: reached end of buffer\n", __func__);
422
423
0
    return 1;
424
0
  }
425
426
0
  return 0;
427
0
}
428
429
int
430
lws_return_http_status(struct lws *wsi, unsigned int code,
431
           const char *html_body)
432
0
{
433
0
  struct lws_context *context = lws_get_context(wsi);
434
0
  struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
435
0
  unsigned char *p = pt->serv_buf + LWS_PRE;
436
0
  unsigned char *start = p;
437
0
  unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
438
0
  char *body = (char *)start + context->pt_serv_buf_size - 512;
439
0
  int n = 0, m = 0, len;
440
0
  char slen[20];
441
442
0
  if (!wsi->a.vhost) {
443
0
    lwsl_err("%s: wsi not bound to vhost\n", __func__);
444
445
0
    return 1;
446
0
  }
447
0
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
448
0
  if (!wsi->handling_404 &&
449
0
      wsi->a.vhost->http.error_document_404 &&
450
0
      code == HTTP_STATUS_NOT_FOUND)
451
    /* we should do a redirect, and do the 404 there */
452
0
    if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
453
0
             (uint8_t *)wsi->a.vhost->http.error_document_404,
454
0
             (int)strlen(wsi->a.vhost->http.error_document_404),
455
0
             &p, end) > 0)
456
0
      return 0;
457
0
#endif
458
459
  /* if the redirect failed, just do a simple status */
460
0
  p = start;
461
462
0
  if (!html_body)
463
0
    html_body = "";
464
465
0
  if (lws_add_http_header_status(wsi, code, &p, end))
466
0
    return 1;
467
468
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
469
0
           (unsigned char *)"text/html", 9,
470
0
           &p, end))
471
0
    return 1;
472
473
0
  len = lws_snprintf(body, 510, "<html><head>"
474
0
    "<meta charset=utf-8 http-equiv=\"Content-Language\" "
475
0
      "content=\"en\"/>"
476
0
    "<link rel=\"stylesheet\" type=\"text/css\" "
477
0
      "href=\"/error.css\"/>"
478
0
    "</head><body><h1>%u</h1>%s</body></html>", code, html_body);
479
480
481
0
  n = lws_snprintf(slen, 12, "%d", len);
482
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
483
0
           (unsigned char *)slen, n, &p, end))
484
0
    return 1;
485
486
0
  if (lws_finalize_http_header(wsi, &p, end))
487
0
    return 1;
488
489
0
#if defined(LWS_WITH_HTTP2)
490
0
  if (wsi->mux_substream) {
491
492
    /*
493
     * for HTTP/2, the headers must be sent separately, since they
494
     * go out in their own frame.  That puts us in a bind that
495
     * we won't always be able to get away with two lws_write()s in
496
     * sequence, since the first may use up the writability due to
497
     * the pipe being choked or SSL_WANT_.
498
     *
499
     * However we do need to send the human-readable body, and the
500
     * END_STREAM.
501
     *
502
     * Solve it by writing the headers now...
503
     */
504
0
    m = lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
505
0
            LWS_WRITE_HTTP_HEADERS);
506
0
    if (m != lws_ptr_diff(p, start))
507
0
      return 1;
508
509
    /*
510
     * ... but stash the body and send it as a priority next
511
     * handle_POLLOUT
512
     */
513
0
    wsi->http.tx_content_length = (unsigned int)len;
514
0
    wsi->http.tx_content_remain = (unsigned int)len;
515
516
0
    wsi->h2.pending_status_body = lws_malloc((unsigned int)len + LWS_PRE + 1,
517
0
              "pending status body");
518
0
    if (!wsi->h2.pending_status_body)
519
0
      return -1;
520
521
0
    strcpy(wsi->h2.pending_status_body + LWS_PRE, body);
522
0
    lws_callback_on_writable(wsi);
523
524
0
    return 0;
525
0
  } else
526
0
#endif
527
0
  {
528
    /*
529
     * for http/1, we can just append the body after the finalized
530
     * headers and send it all in one go.
531
     */
532
533
0
    n = lws_ptr_diff(p, start) + len;
534
0
    memcpy(p, body, (unsigned int)len);
535
0
    m = lws_write(wsi, start, (unsigned int)n, LWS_WRITE_HTTP);
536
0
    if (m != n)
537
0
      return 1;
538
0
  }
539
540
0
  return m != n;
541
0
}
542
543
int
544
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
545
      unsigned char **p, unsigned char *end)
546
0
{
547
0
  unsigned char *start = *p;
548
549
0
  if (lws_add_http_header_status(wsi, (unsigned int)code, p, end))
550
0
    return -1;
551
552
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, loc, len,
553
0
           p, end))
554
0
    return -1;
555
  /*
556
   * if we're going with http/1.1 and keepalive, we have to give fake
557
   * content metadata so the client knows we completed the transaction and
558
   * it can do the redirect...
559
   */
560
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
561
0
           (unsigned char *)"text/html", 9, p,
562
0
           end))
563
0
    return -1;
564
0
  if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
565
0
           (unsigned char *)"0", 1, p, end))
566
0
    return -1;
567
568
0
  if (lws_finalize_http_header(wsi, p, end))
569
0
    return -1;
570
571
0
  return lws_write(wsi, start, lws_ptr_diff_size_t(*p, start),
572
0
       LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
573
0
}
574
#endif
575
576
#if !defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
577
int
578
lws_http_compression_apply(struct lws *wsi, const char *name,
579
         unsigned char **p, unsigned char *end, char decomp)
580
0
{
581
0
  (void)wsi;
582
0
  (void)name;
583
0
  (void)p;
584
0
  (void)end;
585
0
  (void)decomp;
586
587
0
  return 0;
588
0
}
589
#endif
590
591
int
592
lws_http_headers_detach(struct lws *wsi)
593
0
{
594
0
  return lws_header_table_detach(wsi, 0);
595
0
}
596
597
#if defined(LWS_WITH_SERVER)
598
599
void
600
lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul)
601
0
{
602
0
  struct allocated_headers *ah;
603
0
  struct lws_context_per_thread *pt = lws_container_of(sul,
604
0
      struct lws_context_per_thread, sul_ah_lifecheck);
605
0
  struct lws *wsi;
606
0
  time_t now;
607
0
  int m;
608
609
0
  now = time(NULL);
610
611
0
  lws_pt_lock(pt, __func__);
612
613
0
  ah = pt->http.ah_list;
614
0
  while (ah) {
615
0
    int len;
616
0
    char buf[256];
617
0
    const unsigned char *c;
618
619
0
    if (!ah->in_use || !ah->wsi || !ah->assigned ||
620
0
        (ah->wsi->a.vhost &&
621
0
         (now - ah->assigned) <
622
0
         ah->wsi->a.vhost->timeout_secs_ah_idle + 360)) {
623
0
      ah = ah->next;
624
0
      continue;
625
0
    }
626
627
    /*
628
     * a single ah session somehow got held for
629
     * an unreasonable amount of time.
630
     *
631
     * Dump info on the connection...
632
     */
633
0
    wsi = ah->wsi;
634
0
    buf[0] = '\0';
635
0
#if !defined(LWS_PLAT_OPTEE)
636
0
    lws_get_peer_simple(wsi, buf, sizeof(buf));
637
#else
638
    buf[0] = '\0';
639
#endif
640
0
    lwsl_notice("%s: ah excessive hold: wsi %p\n"
641
0
          "  peer address: %s\n"
642
0
          "  ah pos %lu\n", __func__, lws_wsi_tag(wsi),
643
0
          buf, (unsigned long)ah->pos);
644
0
    buf[0] = '\0';
645
0
    m = 0;
646
0
    do {
647
0
      c = lws_token_to_string((enum lws_token_indexes)m);
648
0
      if (!c)
649
0
        break;
650
0
      if (!(*c))
651
0
        break;
652
653
0
      len = lws_hdr_total_length(wsi, (enum lws_token_indexes)m);
654
0
      if (!len || len > (int)sizeof(buf) - 1) {
655
0
        m++;
656
0
        continue;
657
0
      }
658
659
0
      if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)m) > 0) {
660
0
        buf[sizeof(buf) - 1] = '\0';
661
662
0
        lwsl_notice("   %s = %s\n",
663
0
              (const char *)c, buf);
664
0
      }
665
0
      m++;
666
0
    } while (1);
667
668
    /* explicitly detach the ah */
669
0
    lws_header_table_detach(wsi, 0);
670
671
    /* ... and then drop the connection */
672
673
0
    __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
674
0
               "excessive ah");
675
676
0
    ah = pt->http.ah_list;
677
0
  }
678
679
0
  lws_pt_unlock(pt);
680
0
}
681
#endif
682
683
int
684
lws_http_zap_header(struct lws *wsi, const char *name)
685
0
{
686
0
  int n = (int)strlen(name);
687
0
  int index;
688
689
0
  if (!wsi->http.ah)
690
0
    return 0;
691
692
0
  index = lws_http_string_to_known_header(name, (size_t)n);
693
0
  if (index != LWS_HTTP_NO_KNOWN_HEADER && index < WSI_TOKEN_COUNT) {
694
0
    wsi->http.ah->frag_index[index] = 0;
695
0
    return 0;
696
0
  }
697
698
0
#if defined(LWS_WITH_CUSTOM_HEADERS)
699
0
  {
700
0
    ah_data_idx_t ll = wsi->http.ah->unk_ll_head, prev = 0;
701
702
0
    while (ll) {
703
0
      if (ll >= wsi->http.ah->data_length)
704
0
        return 1;
705
706
0
      if (n == lws_ser_ru16be(
707
0
        (uint8_t *)&wsi->http.ah->data[ll + UHO_NLEN]) &&
708
0
          !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], (unsigned int)n)) {
709
        /* found it, remove from list */
710
0
        ah_data_idx_t next = lws_ser_ru32be(
711
0
          (uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
712
713
0
        if (!prev)
714
0
          wsi->http.ah->unk_ll_head = next;
715
0
        else
716
0
          lws_ser_wu32be(
717
0
            (uint8_t *)&wsi->http.ah->data[prev + UHO_LL],
718
0
            next);
719
720
0
        if (!next)
721
0
          wsi->http.ah->unk_ll_tail = prev;
722
723
0
        return 0;
724
0
      }
725
726
0
      prev = ll;
727
0
      ll = lws_ser_ru32be((uint8_t *)&wsi->http.ah->data[ll + UHO_LL]);
728
0
    }
729
0
  }
730
0
#endif
731
732
0
  return 0;
733
0
}