Coverage Report

Created: 2026-08-31 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/h1.c
Line
Count
Source
1
/*
2
 * HTTP/1 protocol analyzer
3
 *
4
 * Copyright 2000-2017 Willy Tarreau <w@1wt.eu>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#include <ctype.h>
14
15
#include <import/sha1.h>
16
17
#include <haproxy/api.h>
18
#include <haproxy/base64.h>
19
#include <haproxy/cfgparse.h>
20
#include <haproxy/h1.h>
21
#include <haproxy/http-hdr.h>
22
#include <haproxy/tools.h>
23
24
/* by default, RFC9112#6.1 applies, t-e combined with c-l represents a risk of
25
 * smuggling if it crosses another 1.0 agent so we must close at the end of the
26
 * transaction. But it may cause difficulties to some very old broken devices.
27
 */
28
int h1_do_not_close_on_insecure_t_e = 0;
29
30
/* Parse the Transfer-Encoding: header field of an HTTP/1 request, looking for
31
 * "chunked" encoding to perform some checks (it must be the last encoding for
32
 * the request and must not be performed twice for any message). The
33
 * H1_MF_TE_CHUNKED is set if a valid "chunked" encoding is found. The
34
 * H1_MF_TE_OTHER flag is set if any other encoding is found. The H1_MF_XFER_ENC
35
 * flag is always set. The H1_MF_CHNK is set when "chunked" encoding is the last
36
 * one. Note that transfer codings are case-insensitive (cf RFC7230#4). This
37
 * function returns -2 for a fatal error, -1 for an error that may be hiidden by
38
 * config, 0 if the whole header can be dropped (not used yet), or >0 if the
39
 * value can be indexed.
40
 */
41
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value)
42
2.60k
{
43
2.60k
  char *e, *n;
44
2.60k
  struct ist word;
45
2.60k
  int ret = 1;
46
47
  /* Reject empty header */
48
2.60k
  if (istptr(value) == istend(value)) {
49
11
    ret = -1;
50
11
    goto end;
51
11
  }
52
53
2.59k
  h1m->flags |= H1_MF_XFER_ENC;
54
55
2.59k
  word.ptr = value.ptr - 1; // -1 for next loop's pre-increment
56
2.59k
  e = istend(value);
57
58
7.17k
  while (++word.ptr < e) {
59
    /* skip leading delimiter and blanks */
60
4.58k
    if (HTTP_IS_LWS(*word.ptr))
61
503
      continue;
62
63
4.08k
    n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
64
65
    /* a comma at the end means the last value is empty */
66
4.08k
    if (n+1 == e)
67
19
      ret = -1;
68
4.08k
    word.len = n - word.ptr;
69
70
    /* trim trailing blanks */
71
4.99k
    while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
72
911
      word.len--;
73
74
4.08k
    h1m->flags &= ~H1_MF_CHNK;
75
76
    /* empty values are forbidden */
77
4.08k
    if (!word.len)
78
389
      ret = -1;
79
3.69k
    else if (isteqi(word, ist("chunked"))) {
80
1.44k
      if (h1m->flags & H1_MF_TE_CHUNKED) {
81
        /* cf RFC7230#3.3.1 : A sender MUST NOT apply
82
         * chunked more than once to a message body
83
         * (i.e., chunking an already chunked message is
84
         * not allowed)
85
         */
86
291
        ret = -1;
87
291
      }
88
1.44k
      h1m->flags |= (H1_MF_TE_CHUNKED|H1_MF_CHNK);
89
1.44k
    }
90
2.25k
    else {
91
2.25k
      if ((h1m->flags & (H1_MF_RESP|H1_MF_TE_CHUNKED)) == H1_MF_TE_CHUNKED) {
92
        /* cf RFC7230#3.3.1 : If any transfer coding
93
         * other than chunked is applied to a request
94
         * payload body, the sender MUST apply chunked
95
         * as the final transfer coding to ensure that
96
         * the message is properly framed.
97
         */
98
1
        ret = -2;
99
1
        goto end;
100
1
      }
101
2.25k
      h1m->flags |= H1_MF_TE_OTHER;
102
2.25k
    }
103
104
4.08k
    word.ptr = n;
105
4.08k
  }
106
107
2.60k
  end:
108
2.60k
  return ret;
109
2.59k
}
110
111
/* Validate the authority and the host header value for CONNECT method. If there
112
 * is hast header, its value is normalized. 0 is returned on success, -1 if the
113
 * authority is invalid and -2 if the host is invalid.
114
 */
115
static int h1_validate_connect_authority(struct ist scheme, struct ist authority, struct ist *host_hdr)
116
287
{
117
287
  struct ist uri_host, uri_port, host, host_port;
118
119
287
  if (isttest(scheme) || !isttest(authority))
120
0
    goto invalid_authority;
121
287
  uri_host = authority;
122
287
  uri_port = http_get_host_port(authority);
123
287
  if (!istlen(uri_port))
124
30
    goto invalid_authority;
125
257
  uri_host.len -= (istlen(uri_port) + 1);
126
127
257
  if (!host_hdr || !isttest(*host_hdr))
128
15
    goto end;
129
130
  /* Get the port of the host header value, if any */
131
242
  host = *host_hdr;
132
242
  host_port = http_get_host_port(*host_hdr);
133
242
  if (isttest(host_port))
134
132
    host.len -= (istlen(host_port) + 1);
135
136
242
  if (istlen(host_port)) {
137
121
    if (!isteqi(host, uri_host) || !isteq(host_port, uri_port))
138
108
      goto invalid_host;
139
13
    if (http_is_default_port(IST_NULL, uri_port))
140
2
      *host_hdr = host; /* normalize */
141
13
  }
142
121
  else {
143
121
    if (!http_is_default_port(IST_NULL, uri_port) || !isteqi(host, uri_host))
144
105
      goto invalid_host;
145
121
  }
146
147
44
  end:
148
44
  return 0;
149
150
30
  invalid_authority:
151
30
  return -1;
152
153
213
  invalid_host:
154
213
  return -2;
155
242
}
156
157
158
/* Validate the authority and the host header value for non-CONNECT method, when
159
 * an absolute-URI is detected but when it does not exactly match the host
160
 * value. The idea is to detect default port (http or https). authority and host
161
 * are defined here. 0 is returned on success, -1 if the host is does not match
162
 * the authority.
163
 */
164
static int h1_validate_mismatch_authority(struct ist scheme, struct ist authority, struct ist host_hdr)
165
258
{
166
258
  struct ist uri_host, uri_port, host, host_port;
167
168
258
  if (!isttest(scheme))
169
0
    goto mismatch;
170
171
258
  uri_host = authority;
172
258
  uri_port = http_get_host_port(authority);
173
258
  if (isttest(uri_port))
174
128
    uri_host.len -= (istlen(uri_port) + 1);
175
176
258
  host = host_hdr;
177
258
  host_port = http_get_host_port(host_hdr);
178
258
  if (isttest(host_port))
179
133
      host.len -= (istlen(host_port) + 1);
180
181
258
  if (!isttest(uri_port) && !isttest(host_port)) {
182
    /* No port on both: we already know the authority does not match
183
     * the host value
184
     */
185
14
    goto mismatch;
186
14
  }
187
244
  else if (isttest(uri_port) && !http_is_default_port(scheme, uri_port)) {
188
    /* here there is no port for the host value and the port for the
189
     * authority is not the default one
190
     */
191
52
    goto mismatch;
192
52
  }
193
192
  else if (isttest(host_port) && !http_is_default_port(scheme, host_port)) {
194
    /* here there is no port for the authority and the port for the
195
     * host value is not the default one
196
     */
197
38
    goto mismatch;
198
38
  }
199
154
  else {
200
    /* the authority or the host value contain a default port and
201
     * there is no port on the other value
202
     */
203
154
    if (!isteqi(uri_host, host))
204
83
      goto mismatch;
205
154
  }
206
207
71
  return 0;
208
209
187
  mismatch:
210
187
  return -1;
211
258
}
212
213
214
/* Parse the Connection: header of an HTTP/1 request, looking for "close",
215
 * "keep-alive", and "upgrade" values, and updating h1m->flags according to
216
 * what was found there. Note that flags are only added, not removed, so the
217
 * function is safe for being called multiple times if multiple occurrences
218
 * are found. If the flag H1_MF_CLEAN_CONN_HDR, the header value is cleaned
219
 * up from "keep-alive" and "close" values. To do so, the header value is
220
 * rewritten in place and its length is updated.
221
 */
222
void h1_parse_connection_header(struct h1m *h1m, struct ist *value)
223
2.27k
{
224
2.27k
  char *e, *n, *p;
225
2.27k
  struct ist word;
226
227
2.27k
  word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
228
2.27k
  p = value->ptr;
229
2.27k
  e = value->ptr + value->len;
230
2.27k
  if (h1m->flags & H1_MF_CLEAN_CONN_HDR)
231
0
    value->len = 0;
232
233
29.6k
  while (++word.ptr < e) {
234
    /* skip leading delimiter and blanks */
235
27.3k
    if (HTTP_IS_LWS(*word.ptr))
236
883
      continue;
237
238
26.4k
    n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
239
26.4k
    word.len = n - word.ptr;
240
241
    /* trim trailing blanks */
242
27.2k
    while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
243
714
      word.len--;
244
245
26.4k
    if (isteqi(word, ist("keep-alive"))) {
246
759
      h1m->flags |= H1_MF_CONN_KAL;
247
759
      if (h1m->flags & H1_MF_CLEAN_CONN_HDR)
248
0
        goto skip_val;
249
759
    }
250
25.7k
    else if (isteqi(word, ist("close"))) {
251
298
      h1m->flags |= H1_MF_CONN_CLO;
252
298
      if (h1m->flags & H1_MF_CLEAN_CONN_HDR)
253
0
        goto skip_val;
254
298
    }
255
25.4k
    else if (isteqi(word, ist("upgrade")))
256
4.70k
      h1m->flags |= H1_MF_CONN_UPG;
257
258
26.4k
    if (h1m->flags & H1_MF_CLEAN_CONN_HDR) {
259
0
      if (value->ptr + value->len == p) {
260
        /* no rewrite done till now */
261
0
        value->len = n - value->ptr;
262
0
      }
263
0
      else {
264
0
        if (value->len)
265
0
          value->ptr[value->len++] = ',';
266
0
        istcat(value, word, e - value->ptr);
267
0
      }
268
0
    }
269
270
26.4k
    skip_val:
271
26.4k
    word.ptr = p = n;
272
26.4k
  }
273
2.27k
}
274
275
/* Parse the Upgrade: header of an HTTP/1 request.
276
 * If "websocket" is found, set H1_MF_UPG_WEBSOCKET flag
277
 * If "h2c" or "h2" found, the value is skipped.
278
 */
279
void h1_parse_upgrade_header(struct h1m *h1m, struct ist *value)
280
1.37k
{
281
1.37k
  char *e, *n, *p;
282
1.37k
  struct ist word;
283
284
1.37k
  word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
285
1.37k
  p = value->ptr;
286
1.37k
  e = value->ptr + value->len;
287
1.37k
  value->len = 0;
288
289
24.3k
  while (++word.ptr < e) {
290
    /* skip leading delimiter and blanks */
291
22.9k
    if (HTTP_IS_LWS(*word.ptr))
292
439
      continue;
293
294
22.5k
    n = http_find_hdr_value_end(word.ptr, e); // next comma or end of line
295
22.5k
    word.len = n - word.ptr;
296
297
    /* trim trailing blanks */
298
23.2k
    while (word.len && HTTP_IS_LWS(word.ptr[word.len-1]))
299
732
      word.len--;
300
301
22.5k
    if (isteqi(word, ist("websocket")))
302
0
      h1m->flags |= H1_MF_UPG_WEBSOCKET;
303
22.5k
    else if (isteqi(word, ist("h2c")) || isteqi(word, ist("h2")))
304
1.98k
      goto skip_val;
305
306
20.5k
    if (value->ptr + value->len == p) {
307
      /* no rewrite done till now */
308
9.69k
      value->len = n - value->ptr;
309
9.69k
    }
310
10.8k
    else {
311
10.8k
      if (value->len)
312
10.3k
        value->ptr[value->len++] = ',';
313
10.8k
      istcat(value, word, e - value->ptr);
314
10.8k
    }
315
316
22.5k
    skip_val:
317
22.5k
    word.ptr = p = n;
318
22.5k
  }
319
320
1.37k
  if (istlen(*value))
321
1.07k
    h1m->flags |= H1_MF_UPG_HDR;
322
1.37k
}
323
324
/* Macros used in the HTTP/1 parser, to check for the expected presence of
325
 * certain bytes (ef: LF) or to skip to next byte and yield in case of failure.
326
 */
327
328
/* Expects to find an LF at <ptr>. If not, set <state> to <where> and jump to
329
 * <bad>.
330
 */
331
#define EXPECT_LF_HERE(ptr, bad, state, where)                  \
332
33.5k
  do {                                                    \
333
33.5k
    if (unlikely(*(ptr) != '\n')) {                 \
334
87
      state = (where);                        \
335
87
      goto bad;                               \
336
87
    }                                               \
337
33.5k
  } while (0)
338
339
/* Increments pointer <ptr>, continues to label <more> if it's still below
340
 * pointer <end>, or goes to <stop> and sets <state> to <where> if the end
341
 * of buffer was reached.
342
 */
343
#define EAT_AND_JUMP_OR_RETURN(ptr, end, more, stop, state, where)        \
344
52.2M
  do {                                                              \
345
52.2M
    if (likely(++(ptr) < (end)))                              \
346
52.2M
      goto more;                                        \
347
52.2M
    else {                                                    \
348
799
      state = (where);                                  \
349
799
      goto stop;                                        \
350
799
    }                                                         \
351
52.2M
  } while (0)
352
353
/* This function parses a contiguous HTTP/1 headers block starting at <start>
354
 * and ending before <stop>, at once, and converts it a list of (name,value)
355
 * pairs representing header fields into the array <hdr> of size <hdr_num>,
356
 * whose last entry will have an empty name and an empty value. If <hdr_num> is
357
 * too small to represent the whole message, an error is returned. Some
358
 * protocol elements such as content-length and transfer-encoding will be
359
 * parsed and stored into h1m as well. <hdr> may be null, in which case only
360
 * the parsing state will be updated. This may be used to restart the parsing
361
 * where it stopped for example.
362
 *
363
 * For now it's limited to the response. If the header block is incomplete,
364
 * 0 is returned, waiting to be called again with more data to try it again.
365
 * The caller is responsible for initializing h1m->state to H1_MSG_RPBEFORE,
366
 * and h1m->next to zero on the first call, the parser will do the rest. If
367
 * an incomplete message is seen, the caller only needs to present h1m->state
368
 * and h1m->next again, with an empty header list so that the parser can start
369
 * again. In this case, it will detect that it interrupted a previous session
370
 * and will first look for the end of the message before reparsing it again and
371
 * indexing it at the same time. This ensures that incomplete messages fed 1
372
 * character at a time are never processed entirely more than exactly twice,
373
 * and that there is no need to store all the internal state and pre-parsed
374
 * headers or start line between calls.
375
 *
376
 * A pointer to a start line descriptor may be passed in <slp>, in which case
377
 * the parser will fill it with whatever it found.
378
 *
379
 * The code derived from the main HTTP/1 parser above but was simplified and
380
 * optimized to process responses produced or forwarded by haproxy. The caller
381
 * is responsible for ensuring that the message doesn't wrap, and should ensure
382
 * it is complete to avoid having to retry the operation after a failed
383
 * attempt. The message is not supposed to be invalid, which is why a few
384
 * properties such as the character set used in the header field names are not
385
 * checked. In case of an unparsable response message, a negative value will be
386
 * returned with h1m->err_pos and h1m->err_state matching the location and
387
 * state where the error was met. Leading blank likes are tolerated but not
388
 * recommended. If flag H1_MF_HDRS_ONLY is set in h1m->flags, only headers are
389
 * parsed and the start line is skipped. It is not required to set h1m->state
390
 * nor h1m->next in this case.
391
 *
392
 * This function returns :
393
 *    -1 in case of error. In this case, h1m->err_state is filled (if h1m is
394
 *       set) with the state the error occurred in and h1m->err_pos with the
395
 *       the position relative to <start>
396
 *    -2 if the output is full (hdr_num reached). err_state and err_pos also
397
 *       indicate where it failed.
398
 *     0 in case of missing data.
399
 *   > 0 on success, it then corresponds to the number of bytes read since
400
 *       <start> so that the caller can go on with the payload.
401
 */
402
int h1_headers_to_hdr_list(char *start, const char *stop,
403
                           struct http_hdr *hdr, unsigned int hdr_num,
404
                           struct h1m *h1m, union h1_sl *slp)
405
5.73k
{
406
5.73k
  enum h1m_state state;
407
5.73k
  register char *ptr;
408
5.73k
  register const char *end;
409
5.73k
  unsigned int hdr_count;
410
5.73k
  unsigned int skip; /* number of bytes skipped at the beginning */
411
5.73k
  unsigned int sol;  /* start of line */
412
5.73k
  unsigned int col;  /* position of the colon */
413
5.73k
  unsigned int eol;  /* end of line */
414
5.73k
  unsigned int sov;  /* start of value */
415
5.73k
  union h1_sl sl;
416
5.73k
  int skip_update;
417
5.73k
  int restarting;
418
5.73k
  int host_idx;
419
5.73k
  struct ist n, v;       /* header name and value during parsing */
420
421
5.73k
  skip = 0; // do it only once to keep track of the leading CRLF.
422
423
5.73k
 try_again:
424
5.73k
  hdr_count = sol = col = eol = sov = 0;
425
5.73k
  sl.st.status = 0;
426
5.73k
  skip_update = restarting = 0;
427
5.73k
  host_idx = -1;
428
429
5.73k
  if (h1m->flags & H1_MF_HDRS_ONLY) {
430
80
    state = H1_MSG_HDR_FIRST;
431
80
    h1m->next = 0;
432
80
  }
433
5.65k
  else {
434
5.65k
    state = h1m->state;
435
5.65k
    if (h1m->state != H1_MSG_RQBEFORE && h1m->state != H1_MSG_RPBEFORE)
436
0
      restarting = 1;
437
5.65k
  }
438
439
5.73k
  ptr   = start + h1m->next;
440
5.73k
  end   = stop;
441
442
5.73k
  if (unlikely(ptr >= end))
443
0
    goto http_msg_ood;
444
445
  /* don't update output if hdr is NULL or if we're restarting */
446
5.73k
  if (!hdr || restarting)
447
0
    skip_update = 1;
448
449
5.73k
  switch (state)  {
450
2.56k
  case H1_MSG_RQBEFORE:
451
3.14k
  http_msg_rqbefore:
452
3.14k
    if (likely(HTTP_IS_TOKEN(*ptr))) {
453
      /* we have a start of message, we may have skipped some
454
       * heading CRLF. Skip them now.
455
       */
456
2.52k
      skip += ptr - start;
457
2.52k
      start = ptr;
458
459
2.52k
      sol = 0;
460
2.52k
      sl.rq.m.ptr = ptr;
461
2.52k
      hdr_count = 0;
462
2.52k
      state = H1_MSG_RQMETH;
463
2.52k
      goto http_msg_rqmeth;
464
2.52k
    }
465
466
618
    if (unlikely(!HTTP_IS_CRLF(*ptr))) {
467
8
      state = H1_MSG_RQBEFORE;
468
8
      goto http_msg_invalid;
469
8
    }
470
471
610
    if (unlikely(*ptr == '\n'))
472
198
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore, http_msg_ood, state, H1_MSG_RQBEFORE);
473
412
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore_cr, http_msg_ood, state, H1_MSG_RQBEFORE_CR);
474
    /* stop here */
475
476
0
  case H1_MSG_RQBEFORE_CR:
477
404
  http_msg_rqbefore_cr:
478
404
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RQBEFORE_CR);
479
390
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqbefore, http_msg_ood, state, H1_MSG_RQBEFORE);
480
    /* stop here */
481
482
0
  case H1_MSG_RQMETH:
483
11.7M
  http_msg_rqmeth:
484
11.7M
    if (likely(HTTP_IS_TOKEN(*ptr)))
485
11.7M
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth, http_msg_ood, state, H1_MSG_RQMETH);
486
487
2.52k
    if (likely(HTTP_IS_SPHT(*ptr))) {
488
2.48k
      sl.rq.m.len = ptr - sl.rq.m.ptr;
489
2.48k
      sl.rq.meth = find_http_meth(start, sl.rq.m.len);
490
2.48k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth_sp, http_msg_ood, state, H1_MSG_RQMETH_SP);
491
2.48k
    }
492
493
39
    if (likely(HTTP_IS_CRLF(*ptr))) {
494
      /* HTTP 0.9 request */
495
36
      sl.rq.m.len = ptr - sl.rq.m.ptr;
496
36
      sl.rq.meth = find_http_meth(sl.rq.m.ptr, sl.rq.m.len);
497
41
    http_msg_req09_uri:
498
41
      sl.rq.u.ptr = ptr;
499
257
    http_msg_req09_uri_e:
500
257
      sl.rq.u.len = ptr - sl.rq.u.ptr;
501
263
    http_msg_req09_ver:
502
263
      sl.rq.v = ist2(ptr, 0);
503
263
      goto http_msg_rqline_eol;
504
257
    }
505
3
    state = H1_MSG_RQMETH;
506
3
    goto http_msg_invalid;
507
508
0
  case H1_MSG_RQMETH_SP:
509
2.69k
  http_msg_rqmeth_sp:
510
2.69k
    if (likely(!HTTP_IS_LWS(*ptr))) {
511
2.43k
      sl.rq.u.ptr = ptr;
512
2.43k
      goto http_msg_rquri;
513
2.43k
    }
514
255
    if (likely(HTTP_IS_SPHT(*ptr)))
515
250
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqmeth_sp, http_msg_ood, state, H1_MSG_RQMETH_SP);
516
    /* so it's a CR/LF, meaning an HTTP 0.9 request */
517
5
    goto http_msg_req09_uri;
518
519
5
  case H1_MSG_RQURI:
520
2.43k
  http_msg_rquri:
521
2.43k
#ifdef HA_UNALIGNED_LE
522
    /* speedup: skip bytes not between 0x24 and 0x7e inclusive */
523
3.98M
    while (ptr <= end - sizeof(int)) {
524
3.98M
      if (is_char4_outside(*(uint *)ptr, 0x24, 0x7e))
525
2.23k
        break;
526
527
3.98M
      ptr += sizeof(int);
528
3.98M
    }
529
2.43k
#endif
530
2.43k
    if (ptr >= end) {
531
1
      state = H1_MSG_RQURI;
532
1
      goto http_msg_ood;
533
1
    }
534
804k
  http_msg_rquri2:
535
804k
    if (likely((unsigned char)(*ptr - 33) <= 93)) { /* 33 to 126 included */
536
802k
      if (*ptr == '#') {
537
1
        if (h1m->err_pos < -1) /* PR_O2_REQBUG_OK not set */
538
1
          goto invalid_char;
539
0
        if (h1m->err_pos == -1) /* PR_O2_REQBUG_OK set: just log */
540
0
          h1m->err_pos = ptr - start + skip;
541
0
      }
542
802k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri2, http_msg_ood, state, H1_MSG_RQURI);
543
802k
    }
544
545
2.41k
    if (likely(HTTP_IS_SPHT(*ptr))) {
546
2.17k
      sl.rq.u.len = ptr - sl.rq.u.ptr;
547
2.17k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri_sp, http_msg_ood, state, H1_MSG_RQURI_SP);
548
2.17k
    }
549
235
    if (likely((unsigned char)*ptr >= 128)) {
550
      /* non-ASCII chars are forbidden unless option
551
       * accept-unsafe-violations-in-http-request is enabled in the frontend.
552
       * In any case, we capture the faulty char.
553
       */
554
12
      if (h1m->err_pos < -1)
555
12
        goto invalid_char;
556
0
      if (h1m->err_pos == -1)
557
0
        h1m->err_pos = ptr - start + skip;
558
0
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri, http_msg_ood, state, H1_MSG_RQURI);
559
0
    }
560
561
223
    if (likely(HTTP_IS_CRLF(*ptr))) {
562
      /* so it's a CR/LF, meaning an HTTP 0.9 request */
563
216
      goto http_msg_req09_uri_e;
564
216
    }
565
566
    /* OK forbidden chars, 0..31 or 127 */
567
20
  invalid_char:
568
20
    state = H1_MSG_RQURI;
569
20
    goto http_msg_invalid;
570
571
0
  case H1_MSG_RQURI_SP:
572
2.40k
  http_msg_rquri_sp:
573
2.40k
    if (likely(!HTTP_IS_LWS(*ptr))) {
574
2.16k
      sl.rq.v.ptr = ptr;
575
2.16k
      goto http_msg_rqver;
576
2.16k
    }
577
238
    if (likely(HTTP_IS_SPHT(*ptr)))
578
232
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rquri_sp, http_msg_ood, state, H1_MSG_RQURI_SP);
579
    /* so it's a CR/LF, meaning an HTTP 0.9 request */
580
6
    goto http_msg_req09_ver;
581
582
583
6
  case H1_MSG_RQVER:
584
2.09M
  http_msg_rqver:
585
2.09M
    if (likely(HTTP_IS_VER_TOKEN(*ptr)))
586
2.08M
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqver, http_msg_ood, state, H1_MSG_RQVER);
587
588
2.16k
    if (likely(HTTP_IS_CRLF(*ptr))) {
589
2.15k
      sl.rq.v.len = ptr - sl.rq.v.ptr;
590
2.41k
    http_msg_rqline_eol:
591
      /* We have seen the end of line. Note that we do not
592
       * necessarily have the \n yet, but at least we know that we
593
       * have EITHER \r OR \n, otherwise the request would not be
594
       * complete. We can then record the request length and return
595
       * to the caller which will be able to register it.
596
       */
597
598
2.41k
      if (likely(!skip_update)) {
599
2.41k
        if ((sl.rq.v.len == 8) &&
600
1.38k
            (*(sl.rq.v.ptr + 5) > '1' ||
601
903
             (*(sl.rq.v.ptr + 5) == '1' && *(sl.rq.v.ptr + 7) >= '1')))
602
767
          h1m->flags |= H1_MF_VER_11;
603
604
2.41k
        if (unlikely(hdr_count >= hdr_num)) {
605
0
          state = H1_MSG_RQVER;
606
0
          goto http_output_full;
607
0
        }
608
2.41k
        if (!(h1m->flags & H1_MF_NO_PHDR))
609
2.41k
          http_set_hdr(&hdr[hdr_count++], ist(":method"), sl.rq.m);
610
611
2.41k
        if (unlikely(hdr_count >= hdr_num)) {
612
0
          state = H1_MSG_RQVER;
613
0
          goto http_output_full;
614
0
        }
615
2.41k
        if (!(h1m->flags & H1_MF_NO_PHDR))
616
2.41k
          http_set_hdr(&hdr[hdr_count++], ist(":path"), sl.rq.u);
617
2.41k
      }
618
619
2.41k
      sol = ptr - start;
620
2.41k
      if (likely(*ptr == '\r'))
621
315
        EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rqline_end, http_msg_ood, state, H1_MSG_RQLINE_END);
622
2.10k
      goto http_msg_rqline_end;
623
2.41k
    }
624
625
    /* neither an HTTP_VER token nor a CRLF */
626
5
    state = H1_MSG_RQVER;
627
5
    goto http_msg_invalid;
628
629
0
  case H1_MSG_RQLINE_END:
630
2.40k
  http_msg_rqline_end:
631
    /* check for HTTP/0.9 request : no version information
632
     * available. sol must point to the first of CR or LF. However
633
     * since we don't save these elements between calls, if we come
634
     * here from a restart, we don't necessarily know. Thus in this
635
     * case we simply start over.
636
     */
637
2.40k
    if (restarting)
638
0
      goto restart;
639
640
2.40k
    if (unlikely(sl.rq.v.len == 0))
641
255
      goto http_msg_last_lf;
642
643
2.15k
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RQLINE_END);
644
2.14k
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_first, http_msg_ood, state, H1_MSG_HDR_FIRST);
645
    /* stop here */
646
647
  /*
648
   * Common states below
649
   */
650
3.09k
  case H1_MSG_RPBEFORE:
651
3.66k
  http_msg_rpbefore:
652
3.66k
    if (likely(HTTP_IS_TOKEN(*ptr))) {
653
      /* we have a start of message, we may have skipped some
654
       * heading CRLF. Skip them now.
655
       */
656
3.06k
      skip += ptr - start;
657
3.06k
      start = ptr;
658
659
3.06k
      sol = 0;
660
3.06k
      sl.st.v.ptr = ptr;
661
3.06k
      hdr_count = 0;
662
3.06k
      state = H1_MSG_RPVER;
663
3.06k
      goto http_msg_rpver;
664
3.06k
    }
665
666
607
    if (unlikely(!HTTP_IS_CRLF(*ptr))) {
667
3
      state = H1_MSG_RPBEFORE;
668
3
      goto http_msg_invalid;
669
3
    }
670
671
604
    if (unlikely(*ptr == '\n'))
672
201
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpbefore, http_msg_ood, state, H1_MSG_RPBEFORE);
673
403
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpbefore_cr, http_msg_ood, state, H1_MSG_RPBEFORE_CR);
674
    /* stop here */
675
676
0
  case H1_MSG_RPBEFORE_CR:
677
398
  http_msg_rpbefore_cr:
678
398
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RPBEFORE_CR);
679
383
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpbefore, http_msg_ood, state, H1_MSG_RPBEFORE);
680
    /* stop here */
681
682
0
  case H1_MSG_RPVER:
683
1.61M
  http_msg_rpver:
684
1.61M
    if (likely(HTTP_IS_VER_TOKEN(*ptr)))
685
1.61M
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpver, http_msg_ood, state, H1_MSG_RPVER);
686
687
3.05k
    if (likely(HTTP_IS_SPHT(*ptr))) {
688
3.04k
      sl.st.v.len = ptr - sl.st.v.ptr;
689
690
3.04k
      if ((sl.st.v.len == 8) &&
691
1.57k
          (*(sl.st.v.ptr + 5) > '1' ||
692
1.01k
           (*(sl.st.v.ptr + 5) == '1' && *(sl.st.v.ptr + 7) >= '1')))
693
890
        h1m->flags |= H1_MF_VER_11;
694
695
3.04k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpver_sp, http_msg_ood, state, H1_MSG_RPVER_SP);
696
3.04k
    }
697
9
    state = H1_MSG_RPVER;
698
9
    goto http_msg_invalid;
699
700
0
  case H1_MSG_RPVER_SP:
701
3.26k
  http_msg_rpver_sp:
702
3.26k
    if (likely(!HTTP_IS_LWS(*ptr))) {
703
3.01k
      sl.st.status = 0;
704
3.01k
      sl.st.c.ptr = ptr;
705
3.01k
      goto http_msg_rpcode;
706
3.01k
    }
707
245
    if (likely(HTTP_IS_SPHT(*ptr)))
708
242
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpver_sp, http_msg_ood, state, H1_MSG_RPVER_SP);
709
    /* so it's a CR/LF, this is invalid */
710
3
    state = H1_MSG_RPVER_SP;
711
3
    goto http_msg_invalid;
712
713
0
  case H1_MSG_RPCODE:
714
6.86k
  http_msg_rpcode:
715
6.86k
    if (likely(HTTP_IS_DIGIT(*ptr))) {
716
3.85k
      if (ptr - sl.st.c.ptr >= 3) {
717
        /* more than 3 digits */
718
2
        if (h1m->err_pos == -1) /* only capture the error pointer */
719
0
          h1m->err_pos = ptr - start + skip;
720
2
        else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {
721
          /* strict checks or risk of overflow */
722
2
          state = H1_MSG_RPCODE;
723
2
          goto http_msg_invalid;
724
2
        }
725
2
      }
726
3.85k
      sl.st.status = sl.st.status * 10 + *ptr - '0';
727
3.85k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode, http_msg_ood, state, H1_MSG_RPCODE);
728
3.85k
    }
729
730
3.01k
    if (unlikely(!HTTP_IS_LWS(*ptr))) {
731
10
      state = H1_MSG_RPCODE;
732
10
      goto http_msg_invalid;
733
10
    }
734
735
3.00k
    if (likely(HTTP_IS_SPHT(*ptr))) {
736
122
      sl.st.c.len = ptr - sl.st.c.ptr;
737
122
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode_sp, http_msg_ood, state, H1_MSG_RPCODE_SP);
738
122
    }
739
740
    /* so it's a CR/LF, so there is no reason phrase */
741
2.88k
    sl.st.c.len = ptr - sl.st.c.ptr;
742
743
2.89k
  http_msg_rsp_reason:
744
2.89k
    sl.st.r = ist2(ptr, 0);
745
2.89k
    goto http_msg_rpline_eol;
746
747
0
  case H1_MSG_RPCODE_SP:
748
340
  http_msg_rpcode_sp:
749
340
    if (likely(!HTTP_IS_LWS(*ptr))) {
750
101
      sl.st.r.ptr = ptr;
751
101
      goto http_msg_rpreason;
752
101
    }
753
239
    if (likely(HTTP_IS_SPHT(*ptr)))
754
221
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpcode_sp, http_msg_ood, state, H1_MSG_RPCODE_SP);
755
    /* so it's a CR/LF, so there is no reason phrase */
756
18
    goto http_msg_rsp_reason;
757
758
18
  case H1_MSG_RPREASON:
759
196k
  http_msg_rpreason:
760
196k
    if (likely(!HTTP_IS_CRLF(*ptr)))
761
196k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpreason, http_msg_ood, state, H1_MSG_RPREASON);
762
87
    sl.st.r.len = ptr - sl.st.r.ptr;
763
2.98k
  http_msg_rpline_eol:
764
    /* We have seen the end of line. Note that we do not
765
     * necessarily have the \n yet, but at least we know that we
766
     * have EITHER \r OR \n, otherwise the response would not be
767
     * complete. We can then record the response length and return
768
     * to the caller which will be able to register it.
769
     */
770
771
2.98k
    if (likely(!skip_update)) {
772
2.98k
      if (unlikely(hdr_count >= hdr_num)) {
773
0
        state = H1_MSG_RPREASON;
774
0
        goto http_output_full;
775
0
      }
776
2.98k
      if (!(h1m->flags & H1_MF_NO_PHDR))
777
2.98k
        http_set_hdr(&hdr[hdr_count++], ist(":status"), sl.st.c);
778
2.98k
    }
779
780
2.98k
    sol = ptr - start;
781
2.98k
    if (likely(*ptr == '\r'))
782
70
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_rpline_end, http_msg_ood, state, H1_MSG_RPLINE_END);
783
2.91k
    goto http_msg_rpline_end;
784
785
2.91k
  case H1_MSG_RPLINE_END:
786
2.98k
  http_msg_rpline_end:
787
    /* sol must point to the first of CR or LF. */
788
2.98k
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_RPLINE_END);
789
2.97k
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_first, http_msg_ood, state, H1_MSG_HDR_FIRST);
790
    /* stop here */
791
792
80
  case H1_MSG_HDR_FIRST:
793
5.16k
  http_msg_hdr_first:
794
5.16k
    sol = ptr - start;
795
5.16k
    if (likely(!HTTP_IS_CRLF(*ptr))) {
796
4.76k
      goto http_msg_hdr_name;
797
4.76k
    }
798
799
403
    if (likely(*ptr == '\r'))
800
9
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_last_lf, http_msg_ood, state, H1_MSG_LAST_LF);
801
394
    goto http_msg_last_lf;
802
803
394
  case H1_MSG_HDR_NAME:
804
9.06M
  http_msg_hdr_name:
805
    /* assumes sol points to the first char */
806
9.06M
    if (likely(HTTP_IS_TOKEN(*ptr))) {
807
9.04M
      if (!skip_update) {
808
        /* turn it to lower case if needed */
809
9.04M
        if (isupper((unsigned char)*ptr) && h1m->flags & H1_MF_TOLOWER)
810
0
          *ptr = tolower((unsigned char)*ptr);
811
9.04M
      }
812
9.04M
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_name, http_msg_ood, state, H1_MSG_HDR_NAME);
813
9.04M
    }
814
815
22.8k
    if (likely(*ptr == ':')) {
816
22.4k
      col = ptr - start;
817
22.4k
      if (col <= sol) {
818
19
        state = H1_MSG_HDR_NAME;
819
19
        goto http_msg_invalid;
820
19
      }
821
22.4k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, H1_MSG_HDR_L1_SP);
822
22.4k
    }
823
824
380
    if (likely(h1m->err_pos < -1) || *ptr == '\n') {
825
380
      state = H1_MSG_HDR_NAME;
826
380
      goto http_msg_invalid;
827
380
    }
828
829
0
    if (h1m->err_pos == -1) /* capture the error pointer */
830
0
      h1m->err_pos = ptr - start + skip; /* >= 0 now */
831
832
    /* and we still accept this non-token character */
833
0
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_name, http_msg_ood, state, H1_MSG_HDR_NAME);
834
835
0
  case H1_MSG_HDR_L1_SP:
836
26.4k
  http_msg_hdr_l1_sp:
837
    /* assumes sol points to the first char */
838
26.4k
    if (likely(HTTP_IS_SPHT(*ptr)))
839
3.60k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_sp, http_msg_ood, state, H1_MSG_HDR_L1_SP);
840
841
    /* header value can be basically anything except CR/LF */
842
22.8k
    sov = ptr - start;
843
844
22.8k
    if (likely(!HTTP_IS_CRLF(*ptr))) {
845
12.6k
      goto http_msg_hdr_val;
846
12.6k
    }
847
848
10.1k
    if (likely(*ptr == '\r'))
849
1.50k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_lf, http_msg_ood, state, H1_MSG_HDR_L1_LF);
850
8.65k
    goto http_msg_hdr_l1_lf;
851
852
8.65k
  case H1_MSG_HDR_L1_LF:
853
10.1k
  http_msg_hdr_l1_lf:
854
10.1k
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_HDR_L1_LF);
855
10.1k
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l1_lws, http_msg_ood, state, H1_MSG_HDR_L1_LWS);
856
857
0
  case H1_MSG_HDR_L1_LWS:
858
10.1k
  http_msg_hdr_l1_lws:
859
10.1k
    if (likely(HTTP_IS_SPHT(*ptr))) {
860
430
      if (!skip_update) {
861
        /* replace HT,CR,LF with spaces */
862
1.08k
        for (; start + sov < ptr; sov++)
863
654
          start[sov] = ' ';
864
430
      }
865
430
      goto http_msg_hdr_l1_sp;
866
430
    }
867
    /* we had a header consisting only in spaces ! */
868
9.70k
    eol = sov;
869
9.70k
    goto http_msg_complete_header;
870
871
0
  case H1_MSG_HDR_VAL:
872
13.5k
  http_msg_hdr_val:
873
    /* assumes sol points to the first char, and sov
874
     * points to the first character of the value.
875
     */
876
877
    /* speedup: we'll skip packs of 4 or 8 bytes not containing bytes 0x0D
878
     * and lower. In fact since most of the time is spent in the loop, we
879
     * also remove the sign bit test so that bytes 0x8e..0x0d break the
880
     * loop, but we don't care since they're very rare in header values.
881
     */
882
13.5k
#ifdef HA_UNALIGNED_LE64
883
2.14M
    while (ptr <= end - sizeof(long)) {
884
2.14M
      if (is_char8_below_opt(*(ulong *)ptr, 0x0e))
885
11.4k
        goto http_msg_hdr_val2;
886
2.13M
      ptr += sizeof(long);
887
2.13M
    }
888
2.08k
#endif
889
2.08k
#ifdef HA_UNALIGNED_LE
890
2.62k
    while (ptr <= end - sizeof(int)) {
891
1.27k
      if (is_char4_below_opt(*(uint *)ptr, 0x0e))
892
741
        goto http_msg_hdr_val2;
893
537
      ptr += sizeof(int);
894
537
    }
895
1.34k
#endif
896
1.34k
    if (ptr >= end) {
897
5
      state = H1_MSG_HDR_VAL;
898
5
      goto http_msg_ood;
899
5
    }
900
26.6M
  http_msg_hdr_val2:
901
26.6M
    if (likely(!*ptr)) {
902
      /* RFC9110 clarified that NUL is explicitly forbidden in header values
903
       * (like CR and LF).
904
       */
905
8
      if (h1m->err_pos < -1) { /* PR_O2_REQBUG_OK not set */
906
8
        state = H1_MSG_HDR_VAL;
907
8
        goto http_msg_invalid;
908
8
      }
909
0
      if (h1m->err_pos == -1) /* PR_O2_REQBUG_OK set: just log */
910
0
        h1m->err_pos = ptr - start + skip;
911
0
    }
912
26.6M
    if (likely(!HTTP_IS_CRLF(*ptr)))
913
26.6M
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_val2, http_msg_ood, state, H1_MSG_HDR_VAL);
914
915
13.4k
    eol = ptr - start;
916
    /* Note: we could also copy eol into ->eoh so that we have the
917
     * real header end in case it ends with lots of LWS, but is this
918
     * really needed ?
919
     */
920
13.4k
    if (likely(*ptr == '\r'))
921
2.22k
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l2_lf, http_msg_ood, state, H1_MSG_HDR_L2_LF);
922
11.2k
    goto http_msg_hdr_l2_lf;
923
924
11.2k
  case H1_MSG_HDR_L2_LF:
925
13.4k
  http_msg_hdr_l2_lf:
926
13.4k
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_HDR_L2_LF);
927
13.4k
    EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_hdr_l2_lws, http_msg_ood, state, H1_MSG_HDR_L2_LWS);
928
929
0
  case H1_MSG_HDR_L2_LWS:
930
13.4k
  http_msg_hdr_l2_lws:
931
13.4k
    if (unlikely(HTTP_IS_SPHT(*ptr))) {
932
840
      if (!skip_update) {
933
        /* LWS: replace HT,CR,LF with spaces */
934
1.91k
        for (; start + eol < ptr; eol++)
935
1.07k
          start[eol] = ' ';
936
840
      }
937
840
      goto http_msg_hdr_val;
938
840
    }
939
22.2k
  http_msg_complete_header:
940
    /*
941
     * It was a new header, so the last one is finished. Assumes
942
     * <sol> points to the first char of the name, <col> to the
943
     * colon, <sov> points to the first character of the value and
944
     * <eol> to the first CR or LF so we know how the line ends. We
945
     * will trim spaces around the value. It's possible to do it by
946
     * adjusting <eol> and <sov> which are no more used after this.
947
     * We can add the header field to the list.
948
     */
949
22.2k
    if (likely(!skip_update)) {
950
22.2k
      while (sov < eol && HTTP_IS_LWS(start[sov]))
951
0
        sov++;
952
953
23.2k
      while (eol - 1 > sov && HTTP_IS_LWS(start[eol - 1]))
954
975
        eol--;
955
956
957
22.2k
      n = ist2(start + sol, col - sol);
958
22.2k
      v = ist2(start + sov, eol - sov);
959
960
22.2k
      do {
961
22.2k
        int ret;
962
963
22.2k
        if (unlikely(hdr_count >= hdr_num)) {
964
2
          state = H1_MSG_HDR_L2_LWS;
965
2
          goto http_output_full;
966
2
        }
967
968
        /* Skip headers whose names contain forbidden
969
         * chars. When any is detected, h1m->err_pos >= 0,
970
         * so we recheck the name only when an error was
971
         * detected.
972
         */
973
22.2k
        if (unlikely(h1m->err_pos >= 0)) {
974
0
          size_t i = 0;
975
0
          while (i < n.len && HTTP_IS_TOKEN(n.ptr[i]))
976
0
            i++;
977
978
0
          if (i < n.len)
979
0
            break;
980
0
        }
981
982
22.2k
        if (isteqi(n, ist("transfer-encoding"))) {
983
2.60k
          ret = h1_parse_xfer_enc_header(h1m, v);
984
2.60k
          if (ret < 0) {
985
            /* For the response only, don't report error if PR_O2_RSPBUG_OK is set
986
             * and the error can be hidden */
987
61
            if (ret == -2 || !(h1m->flags & H1_MF_RESP) || (h1m->err_pos < -1)) {
988
61
              state = H1_MSG_HDR_L2_LWS;
989
61
              ptr = v.ptr; /* Set ptr on the error */
990
61
              goto http_msg_invalid;
991
61
            }
992
0
            if (h1m->err_pos == -1)
993
0
              h1m->err_pos = ptr - start + skip;
994
0
          }
995
2.54k
          else if (ret == 0) {
996
            /* skip it */
997
0
            break;
998
0
          }
999
2.60k
        }
1000
19.6k
        else if (isteqi(n, ist("content-length"))) {
1001
1.59k
          unsigned long long body_len = h1m->body_len;
1002
1003
1.59k
          ret = http_parse_cont_len_header(&v, &body_len, (h1m->flags & H1_MF_CLEN));
1004
1.59k
          if (ret < 0) {
1005
255
            state = H1_MSG_HDR_L2_LWS;
1006
255
            ptr = v.ptr; /* Set ptr on the error */
1007
255
            goto http_msg_invalid;
1008
255
          }
1009
1.33k
          else if (ret == 0) {
1010
            /* skip it */
1011
328
            break;
1012
328
          }
1013
1.01k
          h1m->flags |= H1_MF_CLEN;
1014
1.01k
          h1m->curr_len = h1m->body_len = body_len;
1015
1.01k
        }
1016
18.0k
        else if (isteqi(n, ist("connection"))) {
1017
2.27k
          h1_parse_connection_header(h1m, &v);
1018
2.27k
          if (!v.len) {
1019
            /* skip it */
1020
230
            break;
1021
230
          }
1022
2.27k
        }
1023
15.8k
        else if (isteqi(n, ist("upgrade"))) {
1024
1.37k
          h1_parse_upgrade_header(h1m, &v);
1025
1.37k
          if (!v.len) {
1026
            /* skip it */
1027
300
            break;
1028
300
          }
1029
1.37k
        }
1030
14.4k
        else if (!(h1m->flags & H1_MF_RESP) && isteqi(n, ist("host"))) {
1031
2.60k
          if (host_idx == -1) {
1032
780
            host_idx = hdr_count;
1033
780
            if (http_authority_has_forbidden_char(v)) {
1034
29
              state = H1_MSG_HDR_L2_LWS;
1035
29
              ptr = v.ptr; /* Set ptr on the error */
1036
29
              goto http_msg_invalid;
1037
29
            }
1038
780
          }
1039
1.82k
          else {
1040
1.82k
            if (!isteqi(v, hdr[host_idx].v)) {
1041
61
              state = H1_MSG_HDR_L2_LWS;
1042
61
              ptr = v.ptr; /* Set ptr on the error */
1043
61
              goto http_msg_invalid;
1044
61
            }
1045
            /* if the same host, skip it */
1046
1.76k
            break;
1047
1.82k
          }
1048
2.60k
        }
1049
1050
19.2k
        http_set_hdr(&hdr[hdr_count++], n, v);
1051
19.2k
      } while (0);
1052
22.2k
    }
1053
1054
21.8k
    sol = ptr - start;
1055
1056
21.8k
    if (likely(!HTTP_IS_CRLF(*ptr)))
1057
18.5k
      goto http_msg_hdr_name;
1058
1059
3.36k
    if (likely(*ptr == '\r'))
1060
120
      EAT_AND_JUMP_OR_RETURN(ptr, end, http_msg_last_lf, http_msg_ood, state, H1_MSG_LAST_LF);
1061
3.24k
    goto http_msg_last_lf;
1062
1063
3.24k
  case H1_MSG_LAST_LF:
1064
3.98k
  http_msg_last_lf:
1065
3.98k
    EXPECT_LF_HERE(ptr, http_msg_invalid, state, H1_MSG_LAST_LF);
1066
3.96k
    ptr++;
1067
    /* <ptr> now points to the first byte of payload. If needed sol
1068
     * still points to the first of either CR or LF of the empty
1069
     * line ending the headers block.
1070
     */
1071
3.96k
    if (likely(!skip_update)) {
1072
3.96k
      if (unlikely(hdr_count >= hdr_num)) {
1073
1
        state = H1_MSG_LAST_LF;
1074
1
        goto http_output_full;
1075
1
      }
1076
3.96k
      http_set_hdr(&hdr[hdr_count++], ist2(start+sol, 0), ist(""));
1077
3.96k
    }
1078
1079
    /* reaching here we've parsed the whole message. We may detect
1080
     * that we were already continuing an interrupted parsing pass
1081
     * so we were silently looking for the end of message not
1082
     * updating anything before deciding to parse it fully at once.
1083
     * It's guaranteed that we won't match this test twice in a row
1084
     * since restarting will turn zero.
1085
     */
1086
3.96k
    if (restarting)
1087
0
      goto restart;
1088
1089
1090
3.96k
    if (!(h1m->flags & (H1_MF_HDRS_ONLY|H1_MF_RESP))) {
1091
2.15k
      struct http_uri_parser parser = http_uri_parser_init(sl.rq.u);
1092
2.15k
      struct ist scheme, authority = IST_NULL;
1093
2.15k
      int ret;
1094
1095
      /* WT: gcc seems to see a path where sl.rq.u.ptr was used
1096
       * uninitialized, but it doesn't know that the function is
1097
       * called with initial states making this impossible.
1098
       */
1099
2.15k
      ALREADY_CHECKED(sl.rq.u.ptr);
1100
2.15k
      switch (parser.format) {
1101
164
      case URI_PARSER_FORMAT_ASTERISK:
1102
        /* We must take care "PRI * HTTP/2.0" is supported here. check for OTHER methods here is enough */
1103
164
        if ((sl.rq.meth != HTTP_METH_OTHER && sl.rq.meth != HTTP_METH_OPTIONS) || istlen(sl.rq.u) != 1) {
1104
8
          ptr = sl.rq.u.ptr; /* Set ptr on the error */
1105
8
          goto http_msg_invalid;
1106
8
        }
1107
156
        break;
1108
1109
482
      case URI_PARSER_FORMAT_ABSPATH:
1110
482
        if (sl.rq.meth == HTTP_METH_CONNECT) {
1111
1
          ptr = sl.rq.u.ptr; /* Set ptr on the error */
1112
1
          goto http_msg_invalid;
1113
1
        }
1114
481
        break;
1115
1116
1.47k
      case URI_PARSER_FORMAT_ABSURI_OR_AUTHORITY:
1117
1.47k
        scheme = http_parse_scheme(&parser);
1118
1.47k
        authority = http_parse_authority(&parser, 1);
1119
1.47k
        if (http_authority_has_forbidden_char(authority)) {
1120
50
          if (h1m->err_pos < -1) {
1121
50
            state = H1_MSG_LAST_LF;
1122
            /* WT: gcc seems to see a path where sl.rq.u.ptr was used
1123
             * uninitialized, but it doesn't know that the function is
1124
             * called with initial states making this impossible.
1125
             */
1126
50
            ALREADY_CHECKED(sl.rq.u.ptr);
1127
50
            ptr = sl.rq.u.ptr; /* Set ptr on the error */
1128
50
            goto http_msg_invalid;
1129
50
          }
1130
0
          if (h1m->err_pos == -1) /* capture the error pointer */
1131
0
            h1m->err_pos = sl.rq.u.ptr - start + skip; /* >= 0 now */
1132
0
        }
1133
1134
1.42k
        if (!isttest(scheme)) { /* scheme not found: MUST be an authority */
1135
431
          struct ist *host = NULL;
1136
1137
431
          if (sl.rq.meth != HTTP_METH_CONNECT) {
1138
144
            ptr = sl.rq.u.ptr; /* Set ptr on the error */
1139
144
            goto http_msg_invalid;
1140
144
          }
1141
287
          if (host_idx != -1)
1142
248
            host = &hdr[host_idx].v;
1143
287
          ret = h1_validate_connect_authority(scheme, authority, host);
1144
287
          if (ret < 0) {
1145
243
            if (h1m->err_pos < -1) {
1146
243
              state = H1_MSG_LAST_LF;
1147
              /* WT: gcc seems to see a path where sl.rq.u.ptr was used
1148
               * uninitialized, but it doesn't know that the function is
1149
               * called with initial states making this impossible.
1150
               */
1151
243
              ALREADY_CHECKED(sl.rq.u.ptr);
1152
243
              ptr = ((ret == -1) ? sl.rq.u.ptr : host->ptr); /* Set ptr on the error */
1153
243
              goto http_msg_invalid;
1154
243
            }
1155
0
            if (h1m->err_pos == -1) /* capture the error pointer */
1156
0
              h1m->err_pos = ((ret == -1) ? sl.rq.u.ptr : host->ptr) - start + skip; /* >= 0 now */
1157
0
          }
1158
287
        }
1159
998
        else { /* Scheme found:  MUST be an absolute-URI */
1160
998
          struct ist host = IST_NULL;
1161
1162
998
          if (sl.rq.meth == HTTP_METH_CONNECT) {
1163
1
            ptr = sl.rq.u.ptr; /* Set ptr on the error */
1164
1
            goto http_msg_invalid;
1165
1
          }
1166
1167
997
          if (host_idx != -1)
1168
365
            host = hdr[host_idx].v;
1169
          /* For non-CONNECT method, the authority must match the host header value */
1170
997
          if (isttest(host) && !isteqi(authority, host)) {
1171
258
            ret = h1_validate_mismatch_authority(scheme, authority, host);
1172
258
            if (ret < 0) {
1173
187
              if (h1m->err_pos < -1) {
1174
187
                state = H1_MSG_LAST_LF;
1175
187
                ptr = host.ptr; /* Set ptr on the error */
1176
187
                goto http_msg_invalid;
1177
187
              }
1178
0
              if (h1m->err_pos == -1) /* capture the error pointer */
1179
0
                h1m->err_pos = host.ptr - start + skip; /* >= 0 now */
1180
0
            }
1181
258
          }
1182
997
        }
1183
854
        break;
1184
1185
854
      default:
1186
26
        ptr = sl.rq.u.ptr; /* Set ptr on the error */
1187
26
        goto http_msg_invalid;
1188
2.15k
      }
1189
2.15k
    }
1190
1191
3.30k
    state = H1_MSG_DATA;
1192
3.30k
    if (h1m->flags & H1_MF_XFER_ENC) {
1193
1.37k
      if (h1m->flags & H1_MF_CLEN) {
1194
        /* T-E + C-L: force close and remove C-L */
1195
95
        if (!h1_do_not_close_on_insecure_t_e)
1196
95
          h1m->flags |= H1_MF_CONN_CLO;
1197
1198
95
        h1m->flags &= ~H1_MF_CLEN;
1199
95
        h1m->curr_len = h1m->body_len = 0;
1200
95
        hdr_count = http_del_hdr(hdr, ist("content-length"));
1201
95
      }
1202
1.28k
      else if (!(h1m->flags & H1_MF_VER_11)) {
1203
        /* T-E + HTTP/1.0: force close */
1204
582
        h1m->flags |= H1_MF_CONN_CLO;
1205
582
      }
1206
1207
1.37k
      if (h1m->flags & H1_MF_CHNK)
1208
1.13k
        state = H1_MSG_CHUNK_SIZE;
1209
244
      else if (!(h1m->flags & H1_MF_RESP)) {
1210
        /* cf RFC7230#3.3.3 : transfer-encoding in
1211
         * request without chunked encoding is invalid.
1212
         */
1213
6
        goto http_msg_invalid;
1214
6
      }
1215
1.37k
    }
1216
3.30k
    break;
1217
1218
3.30k
  default:
1219
    /* impossible states */
1220
0
    goto http_msg_invalid;
1221
5.73k
  }
1222
1223
  /* Now we've left the headers state and are either in H1_MSG_DATA or
1224
   * H1_MSG_CHUNK_SIZE.
1225
   */
1226
1227
3.30k
  if (slp && !skip_update)
1228
3.30k
    *slp = sl;
1229
1230
3.30k
  h1m->state = state;
1231
3.30k
  h1m->next  = ptr - start + skip;
1232
3.30k
  return h1m->next;
1233
1234
805
 http_msg_ood:
1235
  /* out of data at <ptr> during state <state> */
1236
805
  if (slp && !skip_update)
1237
805
    *slp = sl;
1238
1239
805
  h1m->state = state;
1240
805
  h1m->next  = ptr - start + skip;
1241
805
  return 0;
1242
1243
1.62k
 http_msg_invalid:
1244
  /* invalid message, error at <ptr> */
1245
1.62k
  if (slp && !skip_update)
1246
1.54k
    *slp = sl;
1247
1248
1.62k
  h1m->err_state = h1m->state = state;
1249
1.62k
  h1m->err_pos   = h1m->next  = ptr - start + skip;
1250
1.62k
  return -1;
1251
1252
3
 http_output_full:
1253
  /* no more room to store the current header, error at <ptr> */
1254
3
  if (slp && !skip_update)
1255
3
    *slp = sl;
1256
1257
3
  h1m->err_state = h1m->state = state;
1258
3
  h1m->err_pos   = h1m->next  = ptr - start + skip;
1259
3
  return -2;
1260
1261
0
 restart:
1262
0
  h1m->flags &= H1_MF_RESTART_MASK;
1263
0
  h1m->curr_len = h1m->body_len = h1m->next  = 0;
1264
0
  if (h1m->flags & H1_MF_RESP)
1265
0
    h1m->state = H1_MSG_RPBEFORE;
1266
0
  else
1267
0
    h1m->state = H1_MSG_RQBEFORE;
1268
0
  goto try_again;
1269
5.73k
}
1270
1271
/* Generate a random key for a WebSocket Handshake in respect with rfc6455
1272
 * The key is 128-bits long encoded as a base64 string in <key_out> parameter
1273
 * (25 bytes long).
1274
 */
1275
void h1_generate_random_ws_input_key(char key_out[25])
1276
0
{
1277
  /* generate a random websocket key */
1278
0
  uint64_t rand1, rand2;
1279
0
  char key[16];
1280
1281
0
  ha_random64_pair_hashed(&rand1, &rand2);
1282
0
  memcpy(key, &rand1, 8);
1283
0
  memcpy(&key[8], &rand2, 8);
1284
0
  a2base64(key, 16, key_out, 25);
1285
0
}
1286
1287
0
#define H1_WS_KEY_SUFFIX_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
1288
1289
/*
1290
 * Calculate the WebSocket handshake response key from <key_in>. Following the
1291
 * rfc6455, <key_in> must be 24 bytes longs. The result is  stored in <key_out>
1292
 * as a 29 bytes long string.
1293
 */
1294
void h1_calculate_ws_output_key(const char *key, char *result)
1295
0
{
1296
0
  blk_SHA_CTX sha1_ctx;
1297
0
  char hash_in[60], hash_out[20];
1298
1299
  /* concatenate the key with a fixed suffix */
1300
0
  memcpy(hash_in, key, 24);
1301
0
  memcpy(&hash_in[24], H1_WS_KEY_SUFFIX_GUID, 36);
1302
1303
  /* sha1 the result */
1304
0
  blk_SHA1_Init(&sha1_ctx);
1305
0
  blk_SHA1_Update(&sha1_ctx, hash_in, 60);
1306
0
  blk_SHA1_Final((unsigned char *)hash_out, &sha1_ctx);
1307
1308
  /* encode in base64 the hash */
1309
0
  a2base64(hash_out, 20, result, 29);
1310
0
}
1311
1312
/* config parser for global "h1-do-not-close-on-insecure-transfer-encoding" */
1313
static int cfg_parse_h1_do_not_close_insecure_t_e(char **args, int section_type, struct proxy *curpx,
1314
                                                const struct proxy *defpx, const char *file, int line,
1315
                                                char **err)
1316
0
{
1317
0
  if (too_many_args(0, args, err, NULL))
1318
0
    return -1;
1319
1320
0
  h1_do_not_close_on_insecure_t_e = 1;
1321
0
  return 0;
1322
0
}
1323
1324
/* config keyword parsers */
1325
static struct cfg_kw_list cfg_kws = {{ }, {
1326
  { CFG_GLOBAL, "h1-do-not-close-on-insecure-transfer-encoding", cfg_parse_h1_do_not_close_insecure_t_e },
1327
  { 0, NULL, NULL },
1328
}};
1329
1330
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);