xref: /unit/src/nxt_http_parse.c (revision 1171)
116Svbart@nginx.com 
216Svbart@nginx.com /*
316Svbart@nginx.com  * Copyright (C) NGINX, Inc.
416Svbart@nginx.com  * Copyright (C) Valentin V. Bartenev
516Svbart@nginx.com  */
616Svbart@nginx.com 
716Svbart@nginx.com #include <nxt_main.h>
816Svbart@nginx.com 
916Svbart@nginx.com 
1016Svbart@nginx.com static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp,
1116Svbart@nginx.com     u_char **pos, u_char *end);
1216Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp,
1316Svbart@nginx.com     u_char **pos, u_char *end);
1416Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp,
1516Svbart@nginx.com     u_char **pos, u_char *end);
1616Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp,
1716Svbart@nginx.com     u_char **pos, u_char *end);
1816Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end);
1916Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
2016Svbart@nginx.com     u_char **pos, u_char *end);
2116Svbart@nginx.com 
22417Svbart@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
23417Svbart@nginx.com 
24417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
25417Svbart@nginx.com static void *nxt_http_field_hash_alloc(void *pool, size_t size);
26417Svbart@nginx.com static void nxt_http_field_hash_free(void *pool, void *p);
27417Svbart@nginx.com 
28417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
29417Svbart@nginx.com     void *data);
3016Svbart@nginx.com 
31417Svbart@nginx.com 
32611Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_NAME         0xFF
33417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_VALUE        NXT_INT32_T_MAX
34417Svbart@nginx.com 
35417Svbart@nginx.com #define NXT_HTTP_FIELD_LVLHSH_SHIFT     5
36417Svbart@nginx.com 
3716Svbart@nginx.com 
3816Svbart@nginx.com typedef enum {
3916Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
4016Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
4116Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
4216Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
4316Svbart@nginx.com 
4416Svbart@nginx.com     /* traps below are used for extended check only */
4516Svbart@nginx.com 
4616Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
4716Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
4816Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
4916Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
5016Svbart@nginx.com } nxt_http_target_traps_e;
5116Svbart@nginx.com 
5216Svbart@nginx.com 
5316Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
5416Svbart@nginx.com     /* \0                               \n        \r       */
5516Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
5616Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
5752Svbart@nginx.com 
5852Svbart@nginx.com     /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
591170Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 0,  0, 0, 6, 5,
6052Svbart@nginx.com 
6152Svbart@nginx.com     /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
6216Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
6316Svbart@nginx.com };
6416Svbart@nginx.com 
6516Svbart@nginx.com 
6616Svbart@nginx.com nxt_inline nxt_http_target_traps_e
6716Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
6816Svbart@nginx.com {
6916Svbart@nginx.com     u_char      *p;
7016Svbart@nginx.com     nxt_uint_t  trap;
7116Svbart@nginx.com 
7216Svbart@nginx.com     p = *pos;
7316Svbart@nginx.com 
74409Svbart@nginx.com     while (nxt_fast_path(end - p >= 10)) {
7516Svbart@nginx.com 
76409Svbart@nginx.com #define nxt_target_test_char(ch)                                              \
77409Svbart@nginx.com                                                                               \
78409Svbart@nginx.com         trap = nxt_http_target_chars[ch];                                     \
7916Svbart@nginx.com                                                                               \
80409Svbart@nginx.com         if (nxt_slow_path(trap != 0)) {                                       \
81409Svbart@nginx.com             *pos = &(ch);                                                     \
82409Svbart@nginx.com             return trap;                                                      \
8316Svbart@nginx.com         }
8416Svbart@nginx.com 
85409Svbart@nginx.com /* enddef */
86409Svbart@nginx.com 
87409Svbart@nginx.com         nxt_target_test_char(p[0]);
88409Svbart@nginx.com         nxt_target_test_char(p[1]);
89409Svbart@nginx.com         nxt_target_test_char(p[2]);
90409Svbart@nginx.com         nxt_target_test_char(p[3]);
9116Svbart@nginx.com 
92409Svbart@nginx.com         nxt_target_test_char(p[4]);
93409Svbart@nginx.com         nxt_target_test_char(p[5]);
94409Svbart@nginx.com         nxt_target_test_char(p[6]);
95409Svbart@nginx.com         nxt_target_test_char(p[7]);
9616Svbart@nginx.com 
97409Svbart@nginx.com         nxt_target_test_char(p[8]);
98409Svbart@nginx.com         nxt_target_test_char(p[9]);
9916Svbart@nginx.com 
100409Svbart@nginx.com         p += 10;
10116Svbart@nginx.com     }
10216Svbart@nginx.com 
103410Svbart@nginx.com     while (p != end) {
104410Svbart@nginx.com         nxt_target_test_char(*p); p++;
105410Svbart@nginx.com     }
106410Svbart@nginx.com 
107409Svbart@nginx.com     return NXT_HTTP_TARGET_AGAIN;
10816Svbart@nginx.com }
10916Svbart@nginx.com 
11016Svbart@nginx.com 
11116Svbart@nginx.com nxt_int_t
112417Svbart@nginx.com nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp)
113417Svbart@nginx.com {
114417Svbart@nginx.com     rp->mem_pool = mp;
115417Svbart@nginx.com 
116417Svbart@nginx.com     rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
1171008Szelenkov@nginx.com     if (nxt_slow_path(rp->fields == NULL)) {
118417Svbart@nginx.com         return NXT_ERROR;
119417Svbart@nginx.com     }
120417Svbart@nginx.com 
121417Svbart@nginx.com     rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
122417Svbart@nginx.com 
123417Svbart@nginx.com     return NXT_OK;
124417Svbart@nginx.com }
125417Svbart@nginx.com 
126417Svbart@nginx.com 
127417Svbart@nginx.com nxt_int_t
12816Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
12916Svbart@nginx.com {
13016Svbart@nginx.com     nxt_int_t  rc;
13116Svbart@nginx.com 
13216Svbart@nginx.com     if (rp->handler == NULL) {
13316Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13416Svbart@nginx.com     }
13516Svbart@nginx.com 
13616Svbart@nginx.com     do {
13716Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
13816Svbart@nginx.com     } while (rc == NXT_OK);
13916Svbart@nginx.com 
14016Svbart@nginx.com     return rc;
14116Svbart@nginx.com }
14216Svbart@nginx.com 
14316Svbart@nginx.com 
144422Svbart@nginx.com nxt_int_t
145422Svbart@nginx.com nxt_http_parse_fields(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
146422Svbart@nginx.com {
147422Svbart@nginx.com     nxt_int_t  rc;
148422Svbart@nginx.com 
149422Svbart@nginx.com     if (rp->handler == NULL) {
150422Svbart@nginx.com         rp->handler = &nxt_http_parse_field_name;
151422Svbart@nginx.com     }
152422Svbart@nginx.com 
153422Svbart@nginx.com     do {
154422Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
155422Svbart@nginx.com     } while (rc == NXT_OK);
156422Svbart@nginx.com 
157422Svbart@nginx.com     return rc;
158422Svbart@nginx.com }
159422Svbart@nginx.com 
160422Svbart@nginx.com 
16116Svbart@nginx.com static nxt_int_t
16216Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
16316Svbart@nginx.com     u_char *end)
16416Svbart@nginx.com {
1651168Svbart@nginx.com     u_char                   *p, ch, *after_slash, *exten, *args;
16616Svbart@nginx.com     nxt_int_t                rc;
167*1171Svbart@nginx.com     nxt_bool_t               rest;
168481Svbart@nginx.com     nxt_http_ver_t           ver;
16916Svbart@nginx.com     nxt_http_target_traps_e  trap;
17016Svbart@nginx.com 
17116Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
17216Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
17316Svbart@nginx.com 
17416Svbart@nginx.com     p = *pos;
17516Svbart@nginx.com 
17616Svbart@nginx.com     rp->method.start = p;
17716Svbart@nginx.com 
178409Svbart@nginx.com     for ( ;; ) {
179409Svbart@nginx.com 
180409Svbart@nginx.com         while (nxt_fast_path(end - p >= 8)) {
18116Svbart@nginx.com 
182409Svbart@nginx.com #define nxt_method_test_char(ch)                                              \
183409Svbart@nginx.com                                                                               \
184409Svbart@nginx.com             if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
185409Svbart@nginx.com                 p = &(ch);                                                    \
186409Svbart@nginx.com                 goto method_unusual_char;                                     \
18716Svbart@nginx.com             }
18816Svbart@nginx.com 
189409Svbart@nginx.com /* enddef */
190409Svbart@nginx.com 
191409Svbart@nginx.com             nxt_method_test_char(p[0]);
192409Svbart@nginx.com             nxt_method_test_char(p[1]);
193409Svbart@nginx.com             nxt_method_test_char(p[2]);
194409Svbart@nginx.com             nxt_method_test_char(p[3]);
19516Svbart@nginx.com 
196409Svbart@nginx.com             nxt_method_test_char(p[4]);
197409Svbart@nginx.com             nxt_method_test_char(p[5]);
198409Svbart@nginx.com             nxt_method_test_char(p[6]);
199409Svbart@nginx.com             nxt_method_test_char(p[7]);
20016Svbart@nginx.com 
201409Svbart@nginx.com             p += 8;
202409Svbart@nginx.com         }
20316Svbart@nginx.com 
204410Svbart@nginx.com         while (p != end) {
205410Svbart@nginx.com             nxt_method_test_char(*p); p++;
206410Svbart@nginx.com         }
207410Svbart@nginx.com 
208623Svbart@nginx.com         rp->method.length = p - rp->method.start;
209623Svbart@nginx.com 
210409Svbart@nginx.com         return NXT_AGAIN;
211409Svbart@nginx.com 
212409Svbart@nginx.com     method_unusual_char:
213409Svbart@nginx.com 
214409Svbart@nginx.com         ch = *p;
21516Svbart@nginx.com 
21616Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
21716Svbart@nginx.com             rp->method.length = p - rp->method.start;
21816Svbart@nginx.com             break;
21916Svbart@nginx.com         }
22016Svbart@nginx.com 
22116Svbart@nginx.com         if (ch == '_' || ch == '-') {
222409Svbart@nginx.com             p++;
22316Svbart@nginx.com             continue;
22416Svbart@nginx.com         }
22516Svbart@nginx.com 
226704Sigor@sysoev.ru         if (rp->method.start == p && (ch == '\r' || ch == '\n')) {
22716Svbart@nginx.com             rp->method.start++;
228409Svbart@nginx.com             p++;
22916Svbart@nginx.com             continue;
23016Svbart@nginx.com         }
23116Svbart@nginx.com 
232623Svbart@nginx.com         rp->method.length = p - rp->method.start;
233623Svbart@nginx.com 
234480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
23516Svbart@nginx.com     }
23616Svbart@nginx.com 
23716Svbart@nginx.com     p++;
23816Svbart@nginx.com 
23916Svbart@nginx.com     if (nxt_slow_path(p == end)) {
24016Svbart@nginx.com         return NXT_AGAIN;
24116Svbart@nginx.com     }
24216Svbart@nginx.com 
24316Svbart@nginx.com     /* target */
24416Svbart@nginx.com 
24516Svbart@nginx.com     ch = *p;
24616Svbart@nginx.com 
24716Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
24816Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
24916Svbart@nginx.com 
25016Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
25116Svbart@nginx.com             return rc;
25216Svbart@nginx.com         }
25316Svbart@nginx.com     }
25416Svbart@nginx.com 
25516Svbart@nginx.com     rp->target_start = p;
25616Svbart@nginx.com 
25716Svbart@nginx.com     after_slash = p + 1;
2581168Svbart@nginx.com     exten = NULL;
2591168Svbart@nginx.com     args = NULL;
260*1171Svbart@nginx.com     rest = 0;
261*1171Svbart@nginx.com 
262*1171Svbart@nginx.com continue_target:
26316Svbart@nginx.com 
26416Svbart@nginx.com     for ( ;; ) {
26516Svbart@nginx.com         p++;
26616Svbart@nginx.com 
26716Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
26816Svbart@nginx.com 
26916Svbart@nginx.com         switch (trap) {
27016Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
27116Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
27216Svbart@nginx.com                 rp->complex_target = 1;
27316Svbart@nginx.com                 goto rest_of_target;
27416Svbart@nginx.com             }
27516Svbart@nginx.com 
27616Svbart@nginx.com             after_slash = p + 1;
2771168Svbart@nginx.com             exten = NULL;
27816Svbart@nginx.com             continue;
27916Svbart@nginx.com 
28016Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
28116Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
28216Svbart@nginx.com                 rp->complex_target = 1;
28316Svbart@nginx.com                 goto rest_of_target;
28416Svbart@nginx.com             }
28516Svbart@nginx.com 
2861168Svbart@nginx.com             exten = p + 1;
28716Svbart@nginx.com             continue;
28816Svbart@nginx.com 
28916Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
2901168Svbart@nginx.com             args = p + 1;
29116Svbart@nginx.com             goto rest_of_target;
29216Svbart@nginx.com 
29316Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
29416Svbart@nginx.com             rp->target_end = p;
29516Svbart@nginx.com             goto space_after_target;
29616Svbart@nginx.com 
29716Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
29816Svbart@nginx.com             rp->quoted_target = 1;
29916Svbart@nginx.com             goto rest_of_target;
30016Svbart@nginx.com 
30116Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
30216Svbart@nginx.com             rp->complex_target = 1;
30316Svbart@nginx.com             goto rest_of_target;
30416Svbart@nginx.com 
30516Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
306621Svbart@nginx.com             rp->target_end = p;
30716Svbart@nginx.com             return NXT_AGAIN;
30816Svbart@nginx.com 
30916Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
310621Svbart@nginx.com             rp->target_end = p;
311480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
31216Svbart@nginx.com         }
31316Svbart@nginx.com 
31416Svbart@nginx.com         nxt_unreachable();
31516Svbart@nginx.com     }
31616Svbart@nginx.com 
31716Svbart@nginx.com rest_of_target:
31816Svbart@nginx.com 
319*1171Svbart@nginx.com     rest = 1;
320*1171Svbart@nginx.com 
32116Svbart@nginx.com     for ( ;; ) {
32216Svbart@nginx.com         p++;
32316Svbart@nginx.com 
32419Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
32516Svbart@nginx.com 
32616Svbart@nginx.com         switch (trap) {
32716Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
32816Svbart@nginx.com             rp->target_end = p;
32916Svbart@nginx.com             goto space_after_target;
33016Svbart@nginx.com 
33116Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
33216Svbart@nginx.com             rp->complex_target = 1;
33316Svbart@nginx.com             continue;
33416Svbart@nginx.com 
33516Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
336621Svbart@nginx.com             rp->target_end = p;
33716Svbart@nginx.com             return NXT_AGAIN;
33816Svbart@nginx.com 
33916Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
340621Svbart@nginx.com             rp->target_end = p;
341480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
34216Svbart@nginx.com 
34316Svbart@nginx.com         default:
34416Svbart@nginx.com             continue;
34516Svbart@nginx.com         }
34616Svbart@nginx.com 
34716Svbart@nginx.com         nxt_unreachable();
34816Svbart@nginx.com     }
34916Svbart@nginx.com 
35016Svbart@nginx.com space_after_target:
35116Svbart@nginx.com 
35216Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
353410Svbart@nginx.com 
354410Svbart@nginx.com         do {
355410Svbart@nginx.com             p++;
356410Svbart@nginx.com 
357410Svbart@nginx.com             if (p == end) {
358410Svbart@nginx.com                 return NXT_AGAIN;
359410Svbart@nginx.com             }
360410Svbart@nginx.com 
361410Svbart@nginx.com         } while (*p == ' ');
362410Svbart@nginx.com 
363410Svbart@nginx.com         if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
364410Svbart@nginx.com 
365410Svbart@nginx.com             switch (end - p) {
366410Svbart@nginx.com             case 8:
367410Svbart@nginx.com                 if (p[7] < '0' || p[7] > '9') {
368410Svbart@nginx.com                     break;
369410Svbart@nginx.com                 }
370410Svbart@nginx.com                 /* Fall through. */
371410Svbart@nginx.com             case 7:
372410Svbart@nginx.com                 if (p[6] != '.') {
373410Svbart@nginx.com                     break;
374410Svbart@nginx.com                 }
375410Svbart@nginx.com                 /* Fall through. */
376410Svbart@nginx.com             case 6:
377410Svbart@nginx.com                 if (p[5] < '0' || p[5] > '9') {
378410Svbart@nginx.com                     break;
379410Svbart@nginx.com                 }
380410Svbart@nginx.com                 /* Fall through. */
381410Svbart@nginx.com             default:
382410Svbart@nginx.com                 return NXT_AGAIN;
383410Svbart@nginx.com             }
384410Svbart@nginx.com         }
385410Svbart@nginx.com 
386410Svbart@nginx.com         rp->space_in_target = 1;
387*1171Svbart@nginx.com 
388*1171Svbart@nginx.com         if (rest) {
389*1171Svbart@nginx.com             goto rest_of_target;
390*1171Svbart@nginx.com         }
391*1171Svbart@nginx.com 
392*1171Svbart@nginx.com         goto continue_target;
39316Svbart@nginx.com     }
39416Svbart@nginx.com 
39516Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
39616Svbart@nginx.com 
397482Svbart@nginx.com     if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) {
398482Svbart@nginx.com 
399482Svbart@nginx.com         if (p[1] == ' ') {
400482Svbart@nginx.com             /* surplus space after tartet */
401482Svbart@nginx.com             p++;
402482Svbart@nginx.com             goto space_after_target;
403482Svbart@nginx.com         }
404482Svbart@nginx.com 
405482Svbart@nginx.com         rp->space_in_target = 1;
406*1171Svbart@nginx.com 
407*1171Svbart@nginx.com         if (rest) {
408*1171Svbart@nginx.com             goto rest_of_target;
409*1171Svbart@nginx.com         }
410*1171Svbart@nginx.com 
411*1171Svbart@nginx.com         goto continue_target;
412482Svbart@nginx.com     }
413482Svbart@nginx.com 
414481Svbart@nginx.com     nxt_memcpy(ver.str, &p[1], 8);
41516Svbart@nginx.com 
416482Svbart@nginx.com     if (nxt_fast_path(ver.ui64 == http11.ui64
417482Svbart@nginx.com                       || ver.ui64 == http10.ui64
418482Svbart@nginx.com                       || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0
419482Svbart@nginx.com                           && ver.s.minor >= '0' && ver.s.minor <= '9')))
42016Svbart@nginx.com     {
421481Svbart@nginx.com         rp->version.ui64 = ver.ui64;
42216Svbart@nginx.com 
42316Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
42416Svbart@nginx.com             p += 10;
42516Svbart@nginx.com 
42616Svbart@nginx.com             if (nxt_slow_path(p == end)) {
42716Svbart@nginx.com                 return NXT_AGAIN;
42816Svbart@nginx.com             }
42916Svbart@nginx.com 
43016Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
431480Svbart@nginx.com                 return NXT_HTTP_PARSE_INVALID;
43216Svbart@nginx.com             }
43316Svbart@nginx.com 
43416Svbart@nginx.com             *pos = p + 1;
435112Smax.romanov@nginx.com 
436112Smax.romanov@nginx.com         } else {
437112Smax.romanov@nginx.com             *pos = p + 10;
438112Smax.romanov@nginx.com         }
439112Smax.romanov@nginx.com 
440112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
441112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
442112Smax.romanov@nginx.com 
443112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
444112Smax.romanov@nginx.com                 return rc;
445112Smax.romanov@nginx.com             }
446112Smax.romanov@nginx.com 
44716Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
44816Svbart@nginx.com         }
44916Svbart@nginx.com 
450112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
451112Smax.romanov@nginx.com 
4521168Svbart@nginx.com         if (args != NULL) {
4531168Svbart@nginx.com             rp->path.length = args - rp->target_start - 1;
454112Smax.romanov@nginx.com 
4551168Svbart@nginx.com             rp->args.length = rp->target_end - args;
4561168Svbart@nginx.com             rp->args.start = args;
457112Smax.romanov@nginx.com 
458112Smax.romanov@nginx.com         } else {
459112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
460112Smax.romanov@nginx.com         }
461112Smax.romanov@nginx.com 
4621168Svbart@nginx.com         if (exten != NULL) {
4631168Svbart@nginx.com             rp->exten.length = (rp->path.start + rp->path.length) - exten;
4641168Svbart@nginx.com             rp->exten.start = exten;
465112Smax.romanov@nginx.com         }
466112Smax.romanov@nginx.com 
46716Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
46816Svbart@nginx.com     }
46916Svbart@nginx.com 
470482Svbart@nginx.com     if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0
471482Svbart@nginx.com         && ver.s.major >= '0' && ver.s.major <= '9'
472482Svbart@nginx.com         && ver.s.point == '.'
473482Svbart@nginx.com         && ver.s.minor >= '0' && ver.s.minor <= '9')
474482Svbart@nginx.com     {
475622Svbart@nginx.com         rp->version.ui64 = ver.ui64;
476482Svbart@nginx.com         return NXT_HTTP_PARSE_UNSUPPORTED_VERSION;
47716Svbart@nginx.com     }
47816Svbart@nginx.com 
479482Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
48016Svbart@nginx.com }
48116Svbart@nginx.com 
48216Svbart@nginx.com 
48316Svbart@nginx.com static nxt_int_t
48416Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
48516Svbart@nginx.com     u_char *end)
48616Svbart@nginx.com {
48716Svbart@nginx.com     u_char  *p, ch;
48816Svbart@nginx.com 
48916Svbart@nginx.com     p = *pos;
49016Svbart@nginx.com 
49116Svbart@nginx.com     ch = *p;
49216Svbart@nginx.com 
49316Svbart@nginx.com     if (ch == ' ') {
49416Svbart@nginx.com         /* skip surplus spaces before target */
49516Svbart@nginx.com 
49616Svbart@nginx.com         do {
49716Svbart@nginx.com             p++;
49816Svbart@nginx.com 
49916Svbart@nginx.com             if (nxt_slow_path(p == end)) {
50016Svbart@nginx.com                 return NXT_AGAIN;
50116Svbart@nginx.com             }
50216Svbart@nginx.com 
50316Svbart@nginx.com             ch = *p;
50416Svbart@nginx.com 
50516Svbart@nginx.com         } while (ch == ' ');
50616Svbart@nginx.com 
50716Svbart@nginx.com         if (ch == '/') {
50816Svbart@nginx.com             *pos = p;
50916Svbart@nginx.com             return NXT_OK;
51016Svbart@nginx.com         }
51116Svbart@nginx.com     }
51216Svbart@nginx.com 
51316Svbart@nginx.com     /* absolute path or '*' */
51416Svbart@nginx.com 
51516Svbart@nginx.com     /* TODO */
51616Svbart@nginx.com 
517480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
51816Svbart@nginx.com }
51916Svbart@nginx.com 
52016Svbart@nginx.com 
52116Svbart@nginx.com static nxt_int_t
52216Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
52316Svbart@nginx.com     u_char *end)
52416Svbart@nginx.com {
525417Svbart@nginx.com     u_char    *p, c;
526417Svbart@nginx.com     size_t    len;
527417Svbart@nginx.com     uint32_t  hash;
52816Svbart@nginx.com 
52916Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
53016Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
53116Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
53216Svbart@nginx.com 
53316Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
534454Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_"
53516Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
53616Svbart@nginx.com 
53716Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
53816Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
53916Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
54016Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
54116Svbart@nginx.com 
542417Svbart@nginx.com     p = *pos + rp->field_name.length;
543417Svbart@nginx.com     hash = rp->field_hash;
54416Svbart@nginx.com 
545417Svbart@nginx.com     while (nxt_fast_path(end - p >= 8)) {
546409Svbart@nginx.com 
547417Svbart@nginx.com #define nxt_field_name_test_char(ch)                                          \
548409Svbart@nginx.com                                                                               \
54919Svbart@nginx.com         c = normal[ch];                                                       \
55019Svbart@nginx.com                                                                               \
55119Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
552417Svbart@nginx.com             p = &(ch);                                                        \
55319Svbart@nginx.com             goto name_end;                                                    \
55419Svbart@nginx.com         }                                                                     \
55519Svbart@nginx.com                                                                               \
556417Svbart@nginx.com         hash = nxt_http_field_hash_char(hash, c);
557409Svbart@nginx.com 
558409Svbart@nginx.com /* enddef */
55916Svbart@nginx.com 
560417Svbart@nginx.com         nxt_field_name_test_char(p[0]);
561417Svbart@nginx.com         nxt_field_name_test_char(p[1]);
562417Svbart@nginx.com         nxt_field_name_test_char(p[2]);
563417Svbart@nginx.com         nxt_field_name_test_char(p[3]);
56416Svbart@nginx.com 
565417Svbart@nginx.com         nxt_field_name_test_char(p[4]);
566417Svbart@nginx.com         nxt_field_name_test_char(p[5]);
567417Svbart@nginx.com         nxt_field_name_test_char(p[6]);
568417Svbart@nginx.com         nxt_field_name_test_char(p[7]);
569417Svbart@nginx.com 
570417Svbart@nginx.com         p += 8;
57119Svbart@nginx.com     }
57216Svbart@nginx.com 
573417Svbart@nginx.com     while (nxt_fast_path(p != end)) {
574417Svbart@nginx.com         nxt_field_name_test_char(*p); p++;
57519Svbart@nginx.com     }
57616Svbart@nginx.com 
577417Svbart@nginx.com     len = p - *pos;
578417Svbart@nginx.com 
579417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
580480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
581417Svbart@nginx.com     }
582417Svbart@nginx.com 
583417Svbart@nginx.com     rp->field_hash = hash;
584417Svbart@nginx.com     rp->field_name.length = len;
585417Svbart@nginx.com 
58616Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
58716Svbart@nginx.com 
58816Svbart@nginx.com     return NXT_AGAIN;
58919Svbart@nginx.com 
59019Svbart@nginx.com name_end:
59119Svbart@nginx.com 
592417Svbart@nginx.com     if (nxt_fast_path(*p == ':')) {
593417Svbart@nginx.com         if (nxt_slow_path(p == *pos)) {
594480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
59519Svbart@nginx.com         }
59619Svbart@nginx.com 
597417Svbart@nginx.com         len = p - *pos;
598417Svbart@nginx.com 
599417Svbart@nginx.com         if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
600480Svbart@nginx.com             return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
601417Svbart@nginx.com         }
60219Svbart@nginx.com 
603417Svbart@nginx.com         rp->field_hash = hash;
604417Svbart@nginx.com 
605417Svbart@nginx.com         rp->field_name.length = len;
606417Svbart@nginx.com         rp->field_name.start = *pos;
607417Svbart@nginx.com 
608417Svbart@nginx.com         *pos = p + 1;
60919Svbart@nginx.com 
61019Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
61119Svbart@nginx.com     }
61219Svbart@nginx.com 
613417Svbart@nginx.com     if (nxt_slow_path(p != *pos)) {
614480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
61559Svbart@nginx.com     }
61619Svbart@nginx.com 
61719Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
61816Svbart@nginx.com }
61916Svbart@nginx.com 
62016Svbart@nginx.com 
62116Svbart@nginx.com static nxt_int_t
62216Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
62316Svbart@nginx.com     u_char *end)
62416Svbart@nginx.com {
625574Svbart@nginx.com     u_char  *p, *start, ch;
626417Svbart@nginx.com     size_t  len;
62716Svbart@nginx.com 
62816Svbart@nginx.com     p = *pos;
62916Svbart@nginx.com 
63016Svbart@nginx.com     for ( ;; ) {
63116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
63216Svbart@nginx.com             *pos = p;
63316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
63416Svbart@nginx.com             return NXT_AGAIN;
63516Svbart@nginx.com         }
63616Svbart@nginx.com 
637577Svbart@nginx.com         ch = *p;
638577Svbart@nginx.com 
639577Svbart@nginx.com         if (ch != ' ' && ch != '\t') {
64016Svbart@nginx.com             break;
64116Svbart@nginx.com         }
64216Svbart@nginx.com 
64316Svbart@nginx.com         p++;
64416Svbart@nginx.com     }
64516Svbart@nginx.com 
646574Svbart@nginx.com     start = p;
64716Svbart@nginx.com 
64867Svbart@nginx.com     p += rp->field_value.length;
64916Svbart@nginx.com 
650576Svbart@nginx.com     for ( ;; ) {
651576Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
652417Svbart@nginx.com 
653576Svbart@nginx.com         if (nxt_slow_path(p == end)) {
654576Svbart@nginx.com             *pos = start;
655576Svbart@nginx.com 
656576Svbart@nginx.com             len = p - start;
657574Svbart@nginx.com 
658576Svbart@nginx.com             if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
659576Svbart@nginx.com                 return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
660576Svbart@nginx.com             }
661417Svbart@nginx.com 
662576Svbart@nginx.com             rp->field_value.length = len;
663576Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
664576Svbart@nginx.com             return NXT_AGAIN;
66516Svbart@nginx.com         }
66616Svbart@nginx.com 
667576Svbart@nginx.com         ch = *p;
668576Svbart@nginx.com 
669576Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
670576Svbart@nginx.com             break;
671576Svbart@nginx.com         }
67216Svbart@nginx.com 
673576Svbart@nginx.com         if (ch != '\t') {
674576Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
675576Svbart@nginx.com         }
67616Svbart@nginx.com 
677576Svbart@nginx.com         p++;
67816Svbart@nginx.com     }
67916Svbart@nginx.com 
680574Svbart@nginx.com     *pos = p;
681574Svbart@nginx.com 
682574Svbart@nginx.com     if (nxt_fast_path(p != start)) {
683577Svbart@nginx.com 
684577Svbart@nginx.com         while (p[-1] == ' ' || p[-1] == '\t') {
68516Svbart@nginx.com             p--;
68616Svbart@nginx.com         }
68716Svbart@nginx.com     }
68816Svbart@nginx.com 
689574Svbart@nginx.com     len = p - start;
690417Svbart@nginx.com 
691417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
692480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
693417Svbart@nginx.com     }
694417Svbart@nginx.com 
695417Svbart@nginx.com     rp->field_value.length = len;
696574Svbart@nginx.com     rp->field_value.start = start;
69716Svbart@nginx.com 
69816Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
69916Svbart@nginx.com }
70016Svbart@nginx.com 
70116Svbart@nginx.com 
70216Svbart@nginx.com static u_char *
70316Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
70416Svbart@nginx.com {
705409Svbart@nginx.com     while (nxt_fast_path(end - p >= 16)) {
706409Svbart@nginx.com 
707409Svbart@nginx.com #define nxt_field_end_test_char(ch)                                           \
708409Svbart@nginx.com                                                                               \
709712Svbart@nginx.com         if (nxt_slow_path((ch) < 0x20)) {                                     \
710409Svbart@nginx.com             return &(ch);                                                     \
711409Svbart@nginx.com         }
712409Svbart@nginx.com 
713409Svbart@nginx.com /* enddef */
714409Svbart@nginx.com 
715409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
716409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
717409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
718409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
71916Svbart@nginx.com 
720409Svbart@nginx.com         nxt_field_end_test_char(p[4]);
721409Svbart@nginx.com         nxt_field_end_test_char(p[5]);
722409Svbart@nginx.com         nxt_field_end_test_char(p[6]);
723409Svbart@nginx.com         nxt_field_end_test_char(p[7]);
724409Svbart@nginx.com 
725409Svbart@nginx.com         nxt_field_end_test_char(p[8]);
726409Svbart@nginx.com         nxt_field_end_test_char(p[9]);
727409Svbart@nginx.com         nxt_field_end_test_char(p[10]);
728409Svbart@nginx.com         nxt_field_end_test_char(p[11]);
729409Svbart@nginx.com 
730409Svbart@nginx.com         nxt_field_end_test_char(p[12]);
731409Svbart@nginx.com         nxt_field_end_test_char(p[13]);
732409Svbart@nginx.com         nxt_field_end_test_char(p[14]);
733409Svbart@nginx.com         nxt_field_end_test_char(p[15]);
734409Svbart@nginx.com 
735409Svbart@nginx.com         p += 16;
73616Svbart@nginx.com     }
73716Svbart@nginx.com 
738409Svbart@nginx.com     while (nxt_fast_path(end - p >= 4)) {
73919Svbart@nginx.com 
740409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
741409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
742409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
743409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
74416Svbart@nginx.com 
745409Svbart@nginx.com         p += 4;
74619Svbart@nginx.com     }
74719Svbart@nginx.com 
74819Svbart@nginx.com     switch (end - p) {
74916Svbart@nginx.com     case 3:
750409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
75139Svbart@nginx.com         /* Fall through. */
75216Svbart@nginx.com     case 2:
753409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
75439Svbart@nginx.com         /* Fall through. */
75516Svbart@nginx.com     case 1:
756409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
75739Svbart@nginx.com         /* Fall through. */
75816Svbart@nginx.com     case 0:
75916Svbart@nginx.com         break;
76016Svbart@nginx.com     default:
76116Svbart@nginx.com         nxt_unreachable();
76216Svbart@nginx.com     }
76316Svbart@nginx.com 
76416Svbart@nginx.com     return p;
76516Svbart@nginx.com }
76616Svbart@nginx.com 
76716Svbart@nginx.com 
76816Svbart@nginx.com static nxt_int_t
76916Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
77016Svbart@nginx.com     u_char *end)
77116Svbart@nginx.com {
77260Svbart@nginx.com     u_char            *p;
77360Svbart@nginx.com     nxt_http_field_t  *field;
77416Svbart@nginx.com 
77516Svbart@nginx.com     p = *pos;
77616Svbart@nginx.com 
77716Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
77816Svbart@nginx.com         p++;
77916Svbart@nginx.com 
78016Svbart@nginx.com         if (nxt_slow_path(p == end)) {
78116Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
78216Svbart@nginx.com             return NXT_AGAIN;
78316Svbart@nginx.com         }
78416Svbart@nginx.com     }
78516Svbart@nginx.com 
78616Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
78716Svbart@nginx.com         *pos = p + 1;
78816Svbart@nginx.com 
78967Svbart@nginx.com         if (rp->field_name.length != 0) {
79060Svbart@nginx.com             field = nxt_list_add(rp->fields);
79116Svbart@nginx.com 
79260Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
79360Svbart@nginx.com                 return NXT_ERROR;
79416Svbart@nginx.com             }
79516Svbart@nginx.com 
796417Svbart@nginx.com             field->hash = nxt_http_field_hash_end(rp->field_hash);
797417Svbart@nginx.com             field->skip = 0;
79860Svbart@nginx.com 
799417Svbart@nginx.com             field->name_length = rp->field_name.length;
800417Svbart@nginx.com             field->value_length = rp->field_value.length;
801417Svbart@nginx.com             field->name = rp->field_name.start;
802417Svbart@nginx.com             field->value = rp->field_value.start;
80367Svbart@nginx.com 
804417Svbart@nginx.com             rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
80567Svbart@nginx.com 
80667Svbart@nginx.com             rp->field_name.length = 0;
80767Svbart@nginx.com             rp->field_value.length = 0;
80816Svbart@nginx.com 
80916Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
81016Svbart@nginx.com             return NXT_OK;
81116Svbart@nginx.com         }
81216Svbart@nginx.com 
81316Svbart@nginx.com         return NXT_DONE;
81416Svbart@nginx.com     }
81516Svbart@nginx.com 
816480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
81716Svbart@nginx.com }
81816Svbart@nginx.com 
81916Svbart@nginx.com 
820112Smax.romanov@nginx.com #define                                                                       \
821112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
822112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
823112Smax.romanov@nginx.com 
824112Smax.romanov@nginx.com 
825112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
826112Smax.romanov@nginx.com 
827112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
828611Svbart@nginx.com     0xFE, 0xDB, 0xFF, 0xFF,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
829112Smax.romanov@nginx.com 
830112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
831611Svbart@nginx.com     0xD6, 0x37, 0xFF, 0x7F,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
832112Smax.romanov@nginx.com 
833112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
834611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
835112Smax.romanov@nginx.com 
836112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
837611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
838112Smax.romanov@nginx.com 
839611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
840611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
841611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
842611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
843112Smax.romanov@nginx.com };
844112Smax.romanov@nginx.com 
845112Smax.romanov@nginx.com 
846112Smax.romanov@nginx.com static nxt_int_t
847112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
848112Smax.romanov@nginx.com {
8491168Svbart@nginx.com     u_char  *p, *u, c, ch, high, *exten, *args;
8501168Svbart@nginx.com 
851112Smax.romanov@nginx.com     enum {
852112Smax.romanov@nginx.com         sw_normal = 0,
853112Smax.romanov@nginx.com         sw_slash,
854112Smax.romanov@nginx.com         sw_dot,
855112Smax.romanov@nginx.com         sw_dot_dot,
856112Smax.romanov@nginx.com         sw_quoted,
857112Smax.romanov@nginx.com         sw_quoted_second,
858112Smax.romanov@nginx.com     } state, saved_state;
859112Smax.romanov@nginx.com 
860112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
861112Smax.romanov@nginx.com 
862112Smax.romanov@nginx.com     state = sw_normal;
863112Smax.romanov@nginx.com     saved_state = sw_normal;
864112Smax.romanov@nginx.com     p = rp->target_start;
865112Smax.romanov@nginx.com 
866112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
867112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
868112Smax.romanov@nginx.com         return NXT_ERROR;
869112Smax.romanov@nginx.com     }
870112Smax.romanov@nginx.com 
871112Smax.romanov@nginx.com     rp->path.length = 0;
872112Smax.romanov@nginx.com     rp->path.start = u;
873112Smax.romanov@nginx.com 
874112Smax.romanov@nginx.com     high = '\0';
8751168Svbart@nginx.com     exten = NULL;
8761168Svbart@nginx.com     args = NULL;
877112Smax.romanov@nginx.com 
878112Smax.romanov@nginx.com     while (p < rp->target_end) {
879112Smax.romanov@nginx.com 
880112Smax.romanov@nginx.com         ch = *p++;
881112Smax.romanov@nginx.com 
882112Smax.romanov@nginx.com     again:
883112Smax.romanov@nginx.com 
884112Smax.romanov@nginx.com         switch (state) {
885112Smax.romanov@nginx.com 
886112Smax.romanov@nginx.com         case sw_normal:
887112Smax.romanov@nginx.com 
888112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
889112Smax.romanov@nginx.com                 *u++ = ch;
890112Smax.romanov@nginx.com                 continue;
891112Smax.romanov@nginx.com             }
892112Smax.romanov@nginx.com 
893112Smax.romanov@nginx.com             switch (ch) {
894112Smax.romanov@nginx.com             case '/':
8951168Svbart@nginx.com                 exten = NULL;
896112Smax.romanov@nginx.com                 state = sw_slash;
897112Smax.romanov@nginx.com                 *u++ = ch;
898112Smax.romanov@nginx.com                 continue;
899112Smax.romanov@nginx.com             case '%':
900112Smax.romanov@nginx.com                 saved_state = state;
901112Smax.romanov@nginx.com                 state = sw_quoted;
902112Smax.romanov@nginx.com                 continue;
903112Smax.romanov@nginx.com             case '?':
9041168Svbart@nginx.com                 args = p;
905112Smax.romanov@nginx.com                 goto args;
906112Smax.romanov@nginx.com             case '#':
907112Smax.romanov@nginx.com                 goto done;
908112Smax.romanov@nginx.com             case '.':
9091168Svbart@nginx.com                 exten = u + 1;
910112Smax.romanov@nginx.com                 *u++ = ch;
911112Smax.romanov@nginx.com                 continue;
912112Smax.romanov@nginx.com             default:
913112Smax.romanov@nginx.com                 *u++ = ch;
914112Smax.romanov@nginx.com                 continue;
915112Smax.romanov@nginx.com             }
916112Smax.romanov@nginx.com 
917112Smax.romanov@nginx.com             break;
918112Smax.romanov@nginx.com 
919112Smax.romanov@nginx.com         case sw_slash:
920112Smax.romanov@nginx.com 
921112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
922112Smax.romanov@nginx.com                 state = sw_normal;
923112Smax.romanov@nginx.com                 *u++ = ch;
924112Smax.romanov@nginx.com                 continue;
925112Smax.romanov@nginx.com             }
926112Smax.romanov@nginx.com 
927112Smax.romanov@nginx.com             switch (ch) {
928112Smax.romanov@nginx.com             case '/':
929112Smax.romanov@nginx.com                 continue;
930112Smax.romanov@nginx.com             case '.':
931112Smax.romanov@nginx.com                 state = sw_dot;
932112Smax.romanov@nginx.com                 *u++ = ch;
933112Smax.romanov@nginx.com                 continue;
934112Smax.romanov@nginx.com             case '%':
935112Smax.romanov@nginx.com                 saved_state = state;
936112Smax.romanov@nginx.com                 state = sw_quoted;
937112Smax.romanov@nginx.com                 continue;
938112Smax.romanov@nginx.com             case '?':
9391168Svbart@nginx.com                 args = p;
940112Smax.romanov@nginx.com                 goto args;
941112Smax.romanov@nginx.com             case '#':
942112Smax.romanov@nginx.com                 goto done;
943112Smax.romanov@nginx.com             default:
944112Smax.romanov@nginx.com                 state = sw_normal;
945112Smax.romanov@nginx.com                 *u++ = ch;
946112Smax.romanov@nginx.com                 continue;
947112Smax.romanov@nginx.com             }
948112Smax.romanov@nginx.com 
949112Smax.romanov@nginx.com             break;
950112Smax.romanov@nginx.com 
951112Smax.romanov@nginx.com         case sw_dot:
952112Smax.romanov@nginx.com 
953112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
954112Smax.romanov@nginx.com                 state = sw_normal;
955112Smax.romanov@nginx.com                 *u++ = ch;
956112Smax.romanov@nginx.com                 continue;
957112Smax.romanov@nginx.com             }
958112Smax.romanov@nginx.com 
959112Smax.romanov@nginx.com             switch (ch) {
960112Smax.romanov@nginx.com             case '/':
961112Smax.romanov@nginx.com                 state = sw_slash;
962112Smax.romanov@nginx.com                 u--;
963112Smax.romanov@nginx.com                 continue;
964112Smax.romanov@nginx.com             case '.':
965112Smax.romanov@nginx.com                 state = sw_dot_dot;
966112Smax.romanov@nginx.com                 *u++ = ch;
967112Smax.romanov@nginx.com                 continue;
968112Smax.romanov@nginx.com             case '%':
969112Smax.romanov@nginx.com                 saved_state = state;
970112Smax.romanov@nginx.com                 state = sw_quoted;
971112Smax.romanov@nginx.com                 continue;
972112Smax.romanov@nginx.com             case '?':
9731168Svbart@nginx.com                 args = p;
974112Smax.romanov@nginx.com                 goto args;
975112Smax.romanov@nginx.com             case '#':
976112Smax.romanov@nginx.com                 goto done;
977112Smax.romanov@nginx.com             default:
978112Smax.romanov@nginx.com                 state = sw_normal;
979112Smax.romanov@nginx.com                 *u++ = ch;
980112Smax.romanov@nginx.com                 continue;
981112Smax.romanov@nginx.com             }
982112Smax.romanov@nginx.com 
983112Smax.romanov@nginx.com             break;
984112Smax.romanov@nginx.com 
985112Smax.romanov@nginx.com         case sw_dot_dot:
986112Smax.romanov@nginx.com 
987112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
988112Smax.romanov@nginx.com                 state = sw_normal;
989112Smax.romanov@nginx.com                 *u++ = ch;
990112Smax.romanov@nginx.com                 continue;
991112Smax.romanov@nginx.com             }
992112Smax.romanov@nginx.com 
993112Smax.romanov@nginx.com             switch (ch) {
994112Smax.romanov@nginx.com             case '/':
995112Smax.romanov@nginx.com                 state = sw_slash;
996112Smax.romanov@nginx.com                 u -= 5;
997112Smax.romanov@nginx.com                 for ( ;; ) {
998112Smax.romanov@nginx.com                     if (u < rp->path.start) {
999480Svbart@nginx.com                         return NXT_HTTP_PARSE_INVALID;
1000112Smax.romanov@nginx.com                     }
1001112Smax.romanov@nginx.com                     if (*u == '/') {
1002112Smax.romanov@nginx.com                         u++;
1003112Smax.romanov@nginx.com                         break;
1004112Smax.romanov@nginx.com                     }
1005112Smax.romanov@nginx.com                     u--;
1006112Smax.romanov@nginx.com                 }
1007112Smax.romanov@nginx.com                 break;
1008112Smax.romanov@nginx.com 
1009112Smax.romanov@nginx.com             case '%':
1010112Smax.romanov@nginx.com                 saved_state = state;
1011112Smax.romanov@nginx.com                 state = sw_quoted;
1012112Smax.romanov@nginx.com                 continue;
1013112Smax.romanov@nginx.com             case '?':
10141168Svbart@nginx.com                 args = p;
1015112Smax.romanov@nginx.com                 goto args;
1016112Smax.romanov@nginx.com             case '#':
1017112Smax.romanov@nginx.com                 goto done;
1018112Smax.romanov@nginx.com             default:
1019112Smax.romanov@nginx.com                 state = sw_normal;
1020112Smax.romanov@nginx.com                 *u++ = ch;
1021112Smax.romanov@nginx.com                 continue;
1022112Smax.romanov@nginx.com             }
1023112Smax.romanov@nginx.com 
1024112Smax.romanov@nginx.com             break;
1025112Smax.romanov@nginx.com 
1026112Smax.romanov@nginx.com         case sw_quoted:
1027112Smax.romanov@nginx.com             rp->quoted_target = 1;
1028112Smax.romanov@nginx.com 
1029112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1030112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
1031112Smax.romanov@nginx.com                 state = sw_quoted_second;
1032112Smax.romanov@nginx.com                 continue;
1033112Smax.romanov@nginx.com             }
1034112Smax.romanov@nginx.com 
1035112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1036112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1037112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1038112Smax.romanov@nginx.com                 state = sw_quoted_second;
1039112Smax.romanov@nginx.com                 continue;
1040112Smax.romanov@nginx.com             }
1041112Smax.romanov@nginx.com 
1042480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
1043112Smax.romanov@nginx.com 
1044112Smax.romanov@nginx.com         case sw_quoted_second:
1045112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1046112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1047112Smax.romanov@nginx.com 
10481167Svbart@nginx.com                 if (ch == '%') {
1049112Smax.romanov@nginx.com                     state = sw_normal;
10501167Svbart@nginx.com                     *u++ = '%';
10511167Svbart@nginx.com 
10521167Svbart@nginx.com                     if (rp->encoded_slashes) {
10531167Svbart@nginx.com                         *u++ = '2';
10541167Svbart@nginx.com                         *u++ = '5';
10551167Svbart@nginx.com                     }
10561167Svbart@nginx.com 
1057112Smax.romanov@nginx.com                     continue;
10581167Svbart@nginx.com                 }
1059112Smax.romanov@nginx.com 
10601167Svbart@nginx.com                 if (ch == '#') {
10611167Svbart@nginx.com                     state = sw_normal;
10621167Svbart@nginx.com                     *u++ = '#';
10631167Svbart@nginx.com                     continue;
10641167Svbart@nginx.com                 }
10651167Svbart@nginx.com 
10661167Svbart@nginx.com                 if (ch == '\0') {
1067480Svbart@nginx.com                     return NXT_HTTP_PARSE_INVALID;
1068112Smax.romanov@nginx.com                 }
1069112Smax.romanov@nginx.com 
1070112Smax.romanov@nginx.com                 state = saved_state;
1071112Smax.romanov@nginx.com                 goto again;
1072112Smax.romanov@nginx.com             }
1073112Smax.romanov@nginx.com 
1074112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1075112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1076112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1077112Smax.romanov@nginx.com 
1078112Smax.romanov@nginx.com                 if (ch == '?') {
1079112Smax.romanov@nginx.com                     state = sw_normal;
1080112Smax.romanov@nginx.com                     *u++ = ch;
1081112Smax.romanov@nginx.com                     continue;
10821167Svbart@nginx.com                 }
1083112Smax.romanov@nginx.com 
10841167Svbart@nginx.com                 if (ch == '/' && rp->encoded_slashes) {
10851167Svbart@nginx.com                     state = sw_normal;
10861167Svbart@nginx.com                     *u++ = '%';
10871167Svbart@nginx.com                     *u++ = '2';
10881167Svbart@nginx.com                     *u++ = p[-1];  /* 'f' or 'F' */
10891167Svbart@nginx.com                     continue;
10901167Svbart@nginx.com                 }
10911167Svbart@nginx.com 
1092112Smax.romanov@nginx.com                 state = saved_state;
1093112Smax.romanov@nginx.com                 goto again;
1094112Smax.romanov@nginx.com             }
1095112Smax.romanov@nginx.com 
1096480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
1097112Smax.romanov@nginx.com         }
1098112Smax.romanov@nginx.com     }
1099112Smax.romanov@nginx.com 
1100112Smax.romanov@nginx.com     if (state >= sw_quoted) {
1101480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
1102112Smax.romanov@nginx.com     }
1103112Smax.romanov@nginx.com 
1104112Smax.romanov@nginx.com args:
1105112Smax.romanov@nginx.com 
1106112Smax.romanov@nginx.com     for (/* void */; p < rp->target_end; p++) {
1107112Smax.romanov@nginx.com         if (*p == '#') {
1108112Smax.romanov@nginx.com             break;
1109112Smax.romanov@nginx.com         }
1110112Smax.romanov@nginx.com     }
1111112Smax.romanov@nginx.com 
11121168Svbart@nginx.com     if (args != NULL) {
11131168Svbart@nginx.com         rp->args.length = p - args;
11141168Svbart@nginx.com         rp->args.start = args;
1115112Smax.romanov@nginx.com     }
1116112Smax.romanov@nginx.com 
1117112Smax.romanov@nginx.com done:
1118112Smax.romanov@nginx.com 
1119112Smax.romanov@nginx.com     rp->path.length = u - rp->path.start;
1120112Smax.romanov@nginx.com 
11211168Svbart@nginx.com     if (exten) {
11221168Svbart@nginx.com         rp->exten.length = u - exten;
11231168Svbart@nginx.com         rp->exten.start = exten;
1124112Smax.romanov@nginx.com     }
1125112Smax.romanov@nginx.com 
1126112Smax.romanov@nginx.com     return NXT_OK;
1127112Smax.romanov@nginx.com }
1128417Svbart@nginx.com 
1129417Svbart@nginx.com 
11301126Smax.romanov@nginx.com const nxt_lvlhsh_proto_t  nxt_http_fields_hash_proto  nxt_aligned(64) = {
1131417Svbart@nginx.com     NXT_LVLHSH_BUCKET_SIZE(64),
1132417Svbart@nginx.com     { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 },
1133417Svbart@nginx.com     nxt_http_field_hash_test,
1134417Svbart@nginx.com     nxt_http_field_hash_alloc,
1135417Svbart@nginx.com     nxt_http_field_hash_free,
1136417Svbart@nginx.com };
1137417Svbart@nginx.com 
1138417Svbart@nginx.com 
1139417Svbart@nginx.com static nxt_int_t
1140417Svbart@nginx.com nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
1141417Svbart@nginx.com {
1142417Svbart@nginx.com     nxt_http_field_proc_t  *field;
1143417Svbart@nginx.com 
1144417Svbart@nginx.com     field = data;
1145417Svbart@nginx.com 
1146417Svbart@nginx.com     if (nxt_strcasestr_eq(&lhq->key, &field->name)) {
1147417Svbart@nginx.com         return NXT_OK;
1148417Svbart@nginx.com     }
1149417Svbart@nginx.com 
1150417Svbart@nginx.com     return NXT_DECLINED;
1151417Svbart@nginx.com }
1152417Svbart@nginx.com 
1153417Svbart@nginx.com 
1154417Svbart@nginx.com static void *
1155417Svbart@nginx.com nxt_http_field_hash_alloc(void *pool, size_t size)
1156417Svbart@nginx.com {
1157417Svbart@nginx.com     return nxt_mp_align(pool, size, size);
1158417Svbart@nginx.com }
1159417Svbart@nginx.com 
1160417Svbart@nginx.com 
1161417Svbart@nginx.com static void
1162417Svbart@nginx.com nxt_http_field_hash_free(void *pool, void *p)
1163417Svbart@nginx.com {
1164417Svbart@nginx.com     nxt_mp_free(pool, p);
1165417Svbart@nginx.com }
1166417Svbart@nginx.com 
1167417Svbart@nginx.com 
1168417Svbart@nginx.com static nxt_int_t
1169417Svbart@nginx.com nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data)
1170417Svbart@nginx.com {
1171417Svbart@nginx.com     return NXT_OK;
1172417Svbart@nginx.com }
1173417Svbart@nginx.com 
1174417Svbart@nginx.com 
1175417Svbart@nginx.com nxt_int_t
1176417Svbart@nginx.com nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
1177417Svbart@nginx.com     nxt_http_field_proc_t items[], nxt_uint_t count)
1178417Svbart@nginx.com {
1179417Svbart@nginx.com     u_char              ch;
1180417Svbart@nginx.com     uint32_t            key;
1181417Svbart@nginx.com     nxt_str_t           *name;
1182417Svbart@nginx.com     nxt_int_t           ret;
1183417Svbart@nginx.com     nxt_uint_t          i, j;
1184417Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
1185417Svbart@nginx.com 
1186417Svbart@nginx.com     lhq.replace = 0;
1187417Svbart@nginx.com     lhq.proto = &nxt_http_fields_hash_proto;
1188417Svbart@nginx.com     lhq.pool = mp;
1189417Svbart@nginx.com 
1190417Svbart@nginx.com     for (i = 0; i < count; i++) {
1191417Svbart@nginx.com         key = NXT_HTTP_FIELD_HASH_INIT;
1192417Svbart@nginx.com         name = &items[i].name;
1193417Svbart@nginx.com 
1194417Svbart@nginx.com         for (j = 0; j < name->length; j++) {
1195417Svbart@nginx.com             ch = nxt_lowcase(name->start[j]);
1196417Svbart@nginx.com             key = nxt_http_field_hash_char(key, ch);
1197417Svbart@nginx.com         }
1198417Svbart@nginx.com 
1199611Svbart@nginx.com         lhq.key_hash = nxt_http_field_hash_end(key) & 0xFFFF;
1200417Svbart@nginx.com         lhq.key = *name;
1201417Svbart@nginx.com         lhq.value = &items[i];
1202417Svbart@nginx.com 
1203417Svbart@nginx.com         ret = nxt_lvlhsh_insert(hash, &lhq);
1204417Svbart@nginx.com 
1205417Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
1206417Svbart@nginx.com             return NXT_ERROR;
1207417Svbart@nginx.com         }
1208417Svbart@nginx.com     }
1209417Svbart@nginx.com 
1210417Svbart@nginx.com     return NXT_OK;
1211417Svbart@nginx.com }
1212417Svbart@nginx.com 
1213417Svbart@nginx.com 
1214417Svbart@nginx.com nxt_uint_t
1215417Svbart@nginx.com nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
1216417Svbart@nginx.com     nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level)
1217417Svbart@nginx.com {
1218417Svbart@nginx.com     u_char              ch;
1219417Svbart@nginx.com     uint32_t            key, mask;
1220417Svbart@nginx.com     nxt_str_t           *name;
1221417Svbart@nginx.com     nxt_uint_t          colls, i, j;
1222417Svbart@nginx.com     nxt_lvlhsh_proto_t  proto;
1223417Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
1224417Svbart@nginx.com 
1225417Svbart@nginx.com     proto = nxt_http_fields_hash_proto;
1226417Svbart@nginx.com     proto.test = nxt_http_field_hash_collision;
1227417Svbart@nginx.com 
1228417Svbart@nginx.com     lhq.replace = 0;
1229417Svbart@nginx.com     lhq.proto = &proto;
1230417Svbart@nginx.com     lhq.pool = mp;
1231417Svbart@nginx.com 
1232611Svbart@nginx.com     mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xFFFF;
1233417Svbart@nginx.com 
1234417Svbart@nginx.com     colls = 0;
1235417Svbart@nginx.com 
1236417Svbart@nginx.com     for (i = 0; i < count; i++) {
1237417Svbart@nginx.com         key = NXT_HTTP_FIELD_HASH_INIT;
1238417Svbart@nginx.com         name = &items[i].name;
1239417Svbart@nginx.com 
1240417Svbart@nginx.com         for (j = 0; j < name->length; j++) {
1241417Svbart@nginx.com             ch = nxt_lowcase(name->start[j]);
1242417Svbart@nginx.com             key = nxt_http_field_hash_char(key, ch);
1243417Svbart@nginx.com         }
1244417Svbart@nginx.com 
1245417Svbart@nginx.com         lhq.key_hash = nxt_http_field_hash_end(key) & mask;
1246418Svbart@nginx.com         lhq.value = &items[i];
1247417Svbart@nginx.com 
1248417Svbart@nginx.com         if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) {
1249417Svbart@nginx.com             colls++;
1250417Svbart@nginx.com         }
1251417Svbart@nginx.com     }
1252417Svbart@nginx.com 
1253417Svbart@nginx.com     return colls;
1254417Svbart@nginx.com }
1255417Svbart@nginx.com 
1256417Svbart@nginx.com 
1257417Svbart@nginx.com nxt_int_t
1258417Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx)
1259417Svbart@nginx.com {
12601126Smax.romanov@nginx.com     nxt_int_t         ret;
12611126Smax.romanov@nginx.com     nxt_http_field_t  *field;
1262417Svbart@nginx.com 
1263417Svbart@nginx.com     nxt_list_each(field, fields) {
1264417Svbart@nginx.com 
12651126Smax.romanov@nginx.com         ret = nxt_http_field_process(field, hash, ctx);
1266417Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
1267417Svbart@nginx.com             return ret;
1268417Svbart@nginx.com         }
1269417Svbart@nginx.com 
1270417Svbart@nginx.com     } nxt_list_loop;
1271417Svbart@nginx.com 
1272417Svbart@nginx.com     return NXT_OK;
1273417Svbart@nginx.com }
1274