xref: /unit/src/nxt_http_parse.c (revision 1170)
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  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
59*1170Svbart@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;
167481Svbart@nginx.com     nxt_http_ver_t           ver;
16816Svbart@nginx.com     nxt_http_target_traps_e  trap;
16916Svbart@nginx.com 
17016Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
17116Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
17216Svbart@nginx.com 
17316Svbart@nginx.com     p = *pos;
17416Svbart@nginx.com 
17516Svbart@nginx.com     rp->method.start = p;
17616Svbart@nginx.com 
177409Svbart@nginx.com     for ( ;; ) {
178409Svbart@nginx.com 
179409Svbart@nginx.com         while (nxt_fast_path(end - p >= 8)) {
18016Svbart@nginx.com 
181409Svbart@nginx.com #define nxt_method_test_char(ch)                                              \
182409Svbart@nginx.com                                                                               \
183409Svbart@nginx.com             if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
184409Svbart@nginx.com                 p = &(ch);                                                    \
185409Svbart@nginx.com                 goto method_unusual_char;                                     \
18616Svbart@nginx.com             }
18716Svbart@nginx.com 
188409Svbart@nginx.com /* enddef */
189409Svbart@nginx.com 
190409Svbart@nginx.com             nxt_method_test_char(p[0]);
191409Svbart@nginx.com             nxt_method_test_char(p[1]);
192409Svbart@nginx.com             nxt_method_test_char(p[2]);
193409Svbart@nginx.com             nxt_method_test_char(p[3]);
19416Svbart@nginx.com 
195409Svbart@nginx.com             nxt_method_test_char(p[4]);
196409Svbart@nginx.com             nxt_method_test_char(p[5]);
197409Svbart@nginx.com             nxt_method_test_char(p[6]);
198409Svbart@nginx.com             nxt_method_test_char(p[7]);
19916Svbart@nginx.com 
200409Svbart@nginx.com             p += 8;
201409Svbart@nginx.com         }
20216Svbart@nginx.com 
203410Svbart@nginx.com         while (p != end) {
204410Svbart@nginx.com             nxt_method_test_char(*p); p++;
205410Svbart@nginx.com         }
206410Svbart@nginx.com 
207623Svbart@nginx.com         rp->method.length = p - rp->method.start;
208623Svbart@nginx.com 
209409Svbart@nginx.com         return NXT_AGAIN;
210409Svbart@nginx.com 
211409Svbart@nginx.com     method_unusual_char:
212409Svbart@nginx.com 
213409Svbart@nginx.com         ch = *p;
21416Svbart@nginx.com 
21516Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
21616Svbart@nginx.com             rp->method.length = p - rp->method.start;
21716Svbart@nginx.com             break;
21816Svbart@nginx.com         }
21916Svbart@nginx.com 
22016Svbart@nginx.com         if (ch == '_' || ch == '-') {
221409Svbart@nginx.com             p++;
22216Svbart@nginx.com             continue;
22316Svbart@nginx.com         }
22416Svbart@nginx.com 
225704Sigor@sysoev.ru         if (rp->method.start == p && (ch == '\r' || ch == '\n')) {
22616Svbart@nginx.com             rp->method.start++;
227409Svbart@nginx.com             p++;
22816Svbart@nginx.com             continue;
22916Svbart@nginx.com         }
23016Svbart@nginx.com 
231623Svbart@nginx.com         rp->method.length = p - rp->method.start;
232623Svbart@nginx.com 
233480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
23416Svbart@nginx.com     }
23516Svbart@nginx.com 
23616Svbart@nginx.com     p++;
23716Svbart@nginx.com 
23816Svbart@nginx.com     if (nxt_slow_path(p == end)) {
23916Svbart@nginx.com         return NXT_AGAIN;
24016Svbart@nginx.com     }
24116Svbart@nginx.com 
24216Svbart@nginx.com     /* target */
24316Svbart@nginx.com 
24416Svbart@nginx.com     ch = *p;
24516Svbart@nginx.com 
24616Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
24716Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
24816Svbart@nginx.com 
24916Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
25016Svbart@nginx.com             return rc;
25116Svbart@nginx.com         }
25216Svbart@nginx.com     }
25316Svbart@nginx.com 
25416Svbart@nginx.com     rp->target_start = p;
25516Svbart@nginx.com 
25616Svbart@nginx.com     after_slash = p + 1;
2571168Svbart@nginx.com     exten = NULL;
2581168Svbart@nginx.com     args = NULL;
25916Svbart@nginx.com 
26016Svbart@nginx.com     for ( ;; ) {
26116Svbart@nginx.com         p++;
26216Svbart@nginx.com 
26316Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
26416Svbart@nginx.com 
26516Svbart@nginx.com         switch (trap) {
26616Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
26716Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
26816Svbart@nginx.com                 rp->complex_target = 1;
26916Svbart@nginx.com                 goto rest_of_target;
27016Svbart@nginx.com             }
27116Svbart@nginx.com 
27216Svbart@nginx.com             after_slash = p + 1;
2731168Svbart@nginx.com             exten = NULL;
27416Svbart@nginx.com             continue;
27516Svbart@nginx.com 
27616Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
27716Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
27816Svbart@nginx.com                 rp->complex_target = 1;
27916Svbart@nginx.com                 goto rest_of_target;
28016Svbart@nginx.com             }
28116Svbart@nginx.com 
2821168Svbart@nginx.com             exten = p + 1;
28316Svbart@nginx.com             continue;
28416Svbart@nginx.com 
28516Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
2861168Svbart@nginx.com             args = p + 1;
28716Svbart@nginx.com             goto rest_of_target;
28816Svbart@nginx.com 
28916Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
29016Svbart@nginx.com             rp->target_end = p;
29116Svbart@nginx.com             goto space_after_target;
29216Svbart@nginx.com 
29316Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
29416Svbart@nginx.com             rp->quoted_target = 1;
29516Svbart@nginx.com             goto rest_of_target;
29616Svbart@nginx.com 
29716Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
29816Svbart@nginx.com             rp->complex_target = 1;
29916Svbart@nginx.com             goto rest_of_target;
30016Svbart@nginx.com 
30116Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
302621Svbart@nginx.com             rp->target_end = p;
30316Svbart@nginx.com             return NXT_AGAIN;
30416Svbart@nginx.com 
30516Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
306621Svbart@nginx.com             rp->target_end = p;
307480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
30816Svbart@nginx.com         }
30916Svbart@nginx.com 
31016Svbart@nginx.com         nxt_unreachable();
31116Svbart@nginx.com     }
31216Svbart@nginx.com 
31316Svbart@nginx.com rest_of_target:
31416Svbart@nginx.com 
31516Svbart@nginx.com     for ( ;; ) {
31616Svbart@nginx.com         p++;
31716Svbart@nginx.com 
31819Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
31916Svbart@nginx.com 
32016Svbart@nginx.com         switch (trap) {
32116Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
32216Svbart@nginx.com             rp->target_end = p;
32316Svbart@nginx.com             goto space_after_target;
32416Svbart@nginx.com 
32516Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
32616Svbart@nginx.com             rp->complex_target = 1;
32716Svbart@nginx.com             continue;
32816Svbart@nginx.com 
32916Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
330621Svbart@nginx.com             rp->target_end = p;
33116Svbart@nginx.com             return NXT_AGAIN;
33216Svbart@nginx.com 
33316Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
334621Svbart@nginx.com             rp->target_end = p;
335480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
33616Svbart@nginx.com 
33716Svbart@nginx.com         default:
33816Svbart@nginx.com             continue;
33916Svbart@nginx.com         }
34016Svbart@nginx.com 
34116Svbart@nginx.com         nxt_unreachable();
34216Svbart@nginx.com     }
34316Svbart@nginx.com 
34416Svbart@nginx.com space_after_target:
34516Svbart@nginx.com 
34616Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
347410Svbart@nginx.com 
348410Svbart@nginx.com         do {
349410Svbart@nginx.com             p++;
350410Svbart@nginx.com 
351410Svbart@nginx.com             if (p == end) {
352410Svbart@nginx.com                 return NXT_AGAIN;
353410Svbart@nginx.com             }
354410Svbart@nginx.com 
355410Svbart@nginx.com         } while (*p == ' ');
356410Svbart@nginx.com 
357410Svbart@nginx.com         if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
358410Svbart@nginx.com 
359410Svbart@nginx.com             switch (end - p) {
360410Svbart@nginx.com             case 8:
361410Svbart@nginx.com                 if (p[7] < '0' || p[7] > '9') {
362410Svbart@nginx.com                     break;
363410Svbart@nginx.com                 }
364410Svbart@nginx.com                 /* Fall through. */
365410Svbart@nginx.com             case 7:
366410Svbart@nginx.com                 if (p[6] != '.') {
367410Svbart@nginx.com                     break;
368410Svbart@nginx.com                 }
369410Svbart@nginx.com                 /* Fall through. */
370410Svbart@nginx.com             case 6:
371410Svbart@nginx.com                 if (p[5] < '0' || p[5] > '9') {
372410Svbart@nginx.com                     break;
373410Svbart@nginx.com                 }
374410Svbart@nginx.com                 /* Fall through. */
375410Svbart@nginx.com             default:
376410Svbart@nginx.com                 return NXT_AGAIN;
377410Svbart@nginx.com             }
378410Svbart@nginx.com         }
379410Svbart@nginx.com 
380410Svbart@nginx.com         rp->space_in_target = 1;
381410Svbart@nginx.com         goto rest_of_target;
38216Svbart@nginx.com     }
38316Svbart@nginx.com 
38416Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
38516Svbart@nginx.com 
386482Svbart@nginx.com     if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) {
387482Svbart@nginx.com 
388482Svbart@nginx.com         if (p[1] == ' ') {
389482Svbart@nginx.com             /* surplus space after tartet */
390482Svbart@nginx.com             p++;
391482Svbart@nginx.com             goto space_after_target;
392482Svbart@nginx.com         }
393482Svbart@nginx.com 
394482Svbart@nginx.com         rp->space_in_target = 1;
395482Svbart@nginx.com         goto rest_of_target;
396482Svbart@nginx.com     }
397482Svbart@nginx.com 
398481Svbart@nginx.com     nxt_memcpy(ver.str, &p[1], 8);
39916Svbart@nginx.com 
400482Svbart@nginx.com     if (nxt_fast_path(ver.ui64 == http11.ui64
401482Svbart@nginx.com                       || ver.ui64 == http10.ui64
402482Svbart@nginx.com                       || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0
403482Svbart@nginx.com                           && ver.s.minor >= '0' && ver.s.minor <= '9')))
40416Svbart@nginx.com     {
405481Svbart@nginx.com         rp->version.ui64 = ver.ui64;
40616Svbart@nginx.com 
40716Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
40816Svbart@nginx.com             p += 10;
40916Svbart@nginx.com 
41016Svbart@nginx.com             if (nxt_slow_path(p == end)) {
41116Svbart@nginx.com                 return NXT_AGAIN;
41216Svbart@nginx.com             }
41316Svbart@nginx.com 
41416Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
415480Svbart@nginx.com                 return NXT_HTTP_PARSE_INVALID;
41616Svbart@nginx.com             }
41716Svbart@nginx.com 
41816Svbart@nginx.com             *pos = p + 1;
419112Smax.romanov@nginx.com 
420112Smax.romanov@nginx.com         } else {
421112Smax.romanov@nginx.com             *pos = p + 10;
422112Smax.romanov@nginx.com         }
423112Smax.romanov@nginx.com 
424112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
425112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
426112Smax.romanov@nginx.com 
427112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
428112Smax.romanov@nginx.com                 return rc;
429112Smax.romanov@nginx.com             }
430112Smax.romanov@nginx.com 
43116Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
43216Svbart@nginx.com         }
43316Svbart@nginx.com 
434112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
435112Smax.romanov@nginx.com 
4361168Svbart@nginx.com         if (args != NULL) {
4371168Svbart@nginx.com             rp->path.length = args - rp->target_start - 1;
438112Smax.romanov@nginx.com 
4391168Svbart@nginx.com             rp->args.length = rp->target_end - args;
4401168Svbart@nginx.com             rp->args.start = args;
441112Smax.romanov@nginx.com 
442112Smax.romanov@nginx.com         } else {
443112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
444112Smax.romanov@nginx.com         }
445112Smax.romanov@nginx.com 
4461168Svbart@nginx.com         if (exten != NULL) {
4471168Svbart@nginx.com             rp->exten.length = (rp->path.start + rp->path.length) - exten;
4481168Svbart@nginx.com             rp->exten.start = exten;
449112Smax.romanov@nginx.com         }
450112Smax.romanov@nginx.com 
45116Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
45216Svbart@nginx.com     }
45316Svbart@nginx.com 
454482Svbart@nginx.com     if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0
455482Svbart@nginx.com         && ver.s.major >= '0' && ver.s.major <= '9'
456482Svbart@nginx.com         && ver.s.point == '.'
457482Svbart@nginx.com         && ver.s.minor >= '0' && ver.s.minor <= '9')
458482Svbart@nginx.com     {
459622Svbart@nginx.com         rp->version.ui64 = ver.ui64;
460482Svbart@nginx.com         return NXT_HTTP_PARSE_UNSUPPORTED_VERSION;
46116Svbart@nginx.com     }
46216Svbart@nginx.com 
463482Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
46416Svbart@nginx.com }
46516Svbart@nginx.com 
46616Svbart@nginx.com 
46716Svbart@nginx.com static nxt_int_t
46816Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
46916Svbart@nginx.com     u_char *end)
47016Svbart@nginx.com {
47116Svbart@nginx.com     u_char  *p, ch;
47216Svbart@nginx.com 
47316Svbart@nginx.com     p = *pos;
47416Svbart@nginx.com 
47516Svbart@nginx.com     ch = *p;
47616Svbart@nginx.com 
47716Svbart@nginx.com     if (ch == ' ') {
47816Svbart@nginx.com         /* skip surplus spaces before target */
47916Svbart@nginx.com 
48016Svbart@nginx.com         do {
48116Svbart@nginx.com             p++;
48216Svbart@nginx.com 
48316Svbart@nginx.com             if (nxt_slow_path(p == end)) {
48416Svbart@nginx.com                 return NXT_AGAIN;
48516Svbart@nginx.com             }
48616Svbart@nginx.com 
48716Svbart@nginx.com             ch = *p;
48816Svbart@nginx.com 
48916Svbart@nginx.com         } while (ch == ' ');
49016Svbart@nginx.com 
49116Svbart@nginx.com         if (ch == '/') {
49216Svbart@nginx.com             *pos = p;
49316Svbart@nginx.com             return NXT_OK;
49416Svbart@nginx.com         }
49516Svbart@nginx.com     }
49616Svbart@nginx.com 
49716Svbart@nginx.com     /* absolute path or '*' */
49816Svbart@nginx.com 
49916Svbart@nginx.com     /* TODO */
50016Svbart@nginx.com 
501480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
50216Svbart@nginx.com }
50316Svbart@nginx.com 
50416Svbart@nginx.com 
50516Svbart@nginx.com static nxt_int_t
50616Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
50716Svbart@nginx.com     u_char *end)
50816Svbart@nginx.com {
509417Svbart@nginx.com     u_char    *p, c;
510417Svbart@nginx.com     size_t    len;
511417Svbart@nginx.com     uint32_t  hash;
51216Svbart@nginx.com 
51316Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
51416Svbart@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"
51516Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
51616Svbart@nginx.com 
51716Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
518454Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_"
51916Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
52016Svbart@nginx.com 
52116Svbart@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"
52216Svbart@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"
52316Svbart@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"
52416Svbart@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";
52516Svbart@nginx.com 
526417Svbart@nginx.com     p = *pos + rp->field_name.length;
527417Svbart@nginx.com     hash = rp->field_hash;
52816Svbart@nginx.com 
529417Svbart@nginx.com     while (nxt_fast_path(end - p >= 8)) {
530409Svbart@nginx.com 
531417Svbart@nginx.com #define nxt_field_name_test_char(ch)                                          \
532409Svbart@nginx.com                                                                               \
53319Svbart@nginx.com         c = normal[ch];                                                       \
53419Svbart@nginx.com                                                                               \
53519Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
536417Svbart@nginx.com             p = &(ch);                                                        \
53719Svbart@nginx.com             goto name_end;                                                    \
53819Svbart@nginx.com         }                                                                     \
53919Svbart@nginx.com                                                                               \
540417Svbart@nginx.com         hash = nxt_http_field_hash_char(hash, c);
541409Svbart@nginx.com 
542409Svbart@nginx.com /* enddef */
54316Svbart@nginx.com 
544417Svbart@nginx.com         nxt_field_name_test_char(p[0]);
545417Svbart@nginx.com         nxt_field_name_test_char(p[1]);
546417Svbart@nginx.com         nxt_field_name_test_char(p[2]);
547417Svbart@nginx.com         nxt_field_name_test_char(p[3]);
54816Svbart@nginx.com 
549417Svbart@nginx.com         nxt_field_name_test_char(p[4]);
550417Svbart@nginx.com         nxt_field_name_test_char(p[5]);
551417Svbart@nginx.com         nxt_field_name_test_char(p[6]);
552417Svbart@nginx.com         nxt_field_name_test_char(p[7]);
553417Svbart@nginx.com 
554417Svbart@nginx.com         p += 8;
55519Svbart@nginx.com     }
55616Svbart@nginx.com 
557417Svbart@nginx.com     while (nxt_fast_path(p != end)) {
558417Svbart@nginx.com         nxt_field_name_test_char(*p); p++;
55919Svbart@nginx.com     }
56016Svbart@nginx.com 
561417Svbart@nginx.com     len = p - *pos;
562417Svbart@nginx.com 
563417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
564480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
565417Svbart@nginx.com     }
566417Svbart@nginx.com 
567417Svbart@nginx.com     rp->field_hash = hash;
568417Svbart@nginx.com     rp->field_name.length = len;
569417Svbart@nginx.com 
57016Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
57116Svbart@nginx.com 
57216Svbart@nginx.com     return NXT_AGAIN;
57319Svbart@nginx.com 
57419Svbart@nginx.com name_end:
57519Svbart@nginx.com 
576417Svbart@nginx.com     if (nxt_fast_path(*p == ':')) {
577417Svbart@nginx.com         if (nxt_slow_path(p == *pos)) {
578480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
57919Svbart@nginx.com         }
58019Svbart@nginx.com 
581417Svbart@nginx.com         len = p - *pos;
582417Svbart@nginx.com 
583417Svbart@nginx.com         if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
584480Svbart@nginx.com             return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
585417Svbart@nginx.com         }
58619Svbart@nginx.com 
587417Svbart@nginx.com         rp->field_hash = hash;
588417Svbart@nginx.com 
589417Svbart@nginx.com         rp->field_name.length = len;
590417Svbart@nginx.com         rp->field_name.start = *pos;
591417Svbart@nginx.com 
592417Svbart@nginx.com         *pos = p + 1;
59319Svbart@nginx.com 
59419Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
59519Svbart@nginx.com     }
59619Svbart@nginx.com 
597417Svbart@nginx.com     if (nxt_slow_path(p != *pos)) {
598480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
59959Svbart@nginx.com     }
60019Svbart@nginx.com 
60119Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
60216Svbart@nginx.com }
60316Svbart@nginx.com 
60416Svbart@nginx.com 
60516Svbart@nginx.com static nxt_int_t
60616Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
60716Svbart@nginx.com     u_char *end)
60816Svbart@nginx.com {
609574Svbart@nginx.com     u_char  *p, *start, ch;
610417Svbart@nginx.com     size_t  len;
61116Svbart@nginx.com 
61216Svbart@nginx.com     p = *pos;
61316Svbart@nginx.com 
61416Svbart@nginx.com     for ( ;; ) {
61516Svbart@nginx.com         if (nxt_slow_path(p == end)) {
61616Svbart@nginx.com             *pos = p;
61716Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
61816Svbart@nginx.com             return NXT_AGAIN;
61916Svbart@nginx.com         }
62016Svbart@nginx.com 
621577Svbart@nginx.com         ch = *p;
622577Svbart@nginx.com 
623577Svbart@nginx.com         if (ch != ' ' && ch != '\t') {
62416Svbart@nginx.com             break;
62516Svbart@nginx.com         }
62616Svbart@nginx.com 
62716Svbart@nginx.com         p++;
62816Svbart@nginx.com     }
62916Svbart@nginx.com 
630574Svbart@nginx.com     start = p;
63116Svbart@nginx.com 
63267Svbart@nginx.com     p += rp->field_value.length;
63316Svbart@nginx.com 
634576Svbart@nginx.com     for ( ;; ) {
635576Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
636417Svbart@nginx.com 
637576Svbart@nginx.com         if (nxt_slow_path(p == end)) {
638576Svbart@nginx.com             *pos = start;
639576Svbart@nginx.com 
640576Svbart@nginx.com             len = p - start;
641574Svbart@nginx.com 
642576Svbart@nginx.com             if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
643576Svbart@nginx.com                 return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
644576Svbart@nginx.com             }
645417Svbart@nginx.com 
646576Svbart@nginx.com             rp->field_value.length = len;
647576Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
648576Svbart@nginx.com             return NXT_AGAIN;
64916Svbart@nginx.com         }
65016Svbart@nginx.com 
651576Svbart@nginx.com         ch = *p;
652576Svbart@nginx.com 
653576Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
654576Svbart@nginx.com             break;
655576Svbart@nginx.com         }
65616Svbart@nginx.com 
657576Svbart@nginx.com         if (ch != '\t') {
658576Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
659576Svbart@nginx.com         }
66016Svbart@nginx.com 
661576Svbart@nginx.com         p++;
66216Svbart@nginx.com     }
66316Svbart@nginx.com 
664574Svbart@nginx.com     *pos = p;
665574Svbart@nginx.com 
666574Svbart@nginx.com     if (nxt_fast_path(p != start)) {
667577Svbart@nginx.com 
668577Svbart@nginx.com         while (p[-1] == ' ' || p[-1] == '\t') {
66916Svbart@nginx.com             p--;
67016Svbart@nginx.com         }
67116Svbart@nginx.com     }
67216Svbart@nginx.com 
673574Svbart@nginx.com     len = p - start;
674417Svbart@nginx.com 
675417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
676480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
677417Svbart@nginx.com     }
678417Svbart@nginx.com 
679417Svbart@nginx.com     rp->field_value.length = len;
680574Svbart@nginx.com     rp->field_value.start = start;
68116Svbart@nginx.com 
68216Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
68316Svbart@nginx.com }
68416Svbart@nginx.com 
68516Svbart@nginx.com 
68616Svbart@nginx.com static u_char *
68716Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
68816Svbart@nginx.com {
689409Svbart@nginx.com     while (nxt_fast_path(end - p >= 16)) {
690409Svbart@nginx.com 
691409Svbart@nginx.com #define nxt_field_end_test_char(ch)                                           \
692409Svbart@nginx.com                                                                               \
693712Svbart@nginx.com         if (nxt_slow_path((ch) < 0x20)) {                                     \
694409Svbart@nginx.com             return &(ch);                                                     \
695409Svbart@nginx.com         }
696409Svbart@nginx.com 
697409Svbart@nginx.com /* enddef */
698409Svbart@nginx.com 
699409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
700409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
701409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
702409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
70316Svbart@nginx.com 
704409Svbart@nginx.com         nxt_field_end_test_char(p[4]);
705409Svbart@nginx.com         nxt_field_end_test_char(p[5]);
706409Svbart@nginx.com         nxt_field_end_test_char(p[6]);
707409Svbart@nginx.com         nxt_field_end_test_char(p[7]);
708409Svbart@nginx.com 
709409Svbart@nginx.com         nxt_field_end_test_char(p[8]);
710409Svbart@nginx.com         nxt_field_end_test_char(p[9]);
711409Svbart@nginx.com         nxt_field_end_test_char(p[10]);
712409Svbart@nginx.com         nxt_field_end_test_char(p[11]);
713409Svbart@nginx.com 
714409Svbart@nginx.com         nxt_field_end_test_char(p[12]);
715409Svbart@nginx.com         nxt_field_end_test_char(p[13]);
716409Svbart@nginx.com         nxt_field_end_test_char(p[14]);
717409Svbart@nginx.com         nxt_field_end_test_char(p[15]);
718409Svbart@nginx.com 
719409Svbart@nginx.com         p += 16;
72016Svbart@nginx.com     }
72116Svbart@nginx.com 
722409Svbart@nginx.com     while (nxt_fast_path(end - p >= 4)) {
72319Svbart@nginx.com 
724409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
725409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
726409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
727409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
72816Svbart@nginx.com 
729409Svbart@nginx.com         p += 4;
73019Svbart@nginx.com     }
73119Svbart@nginx.com 
73219Svbart@nginx.com     switch (end - p) {
73316Svbart@nginx.com     case 3:
734409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
73539Svbart@nginx.com         /* Fall through. */
73616Svbart@nginx.com     case 2:
737409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
73839Svbart@nginx.com         /* Fall through. */
73916Svbart@nginx.com     case 1:
740409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
74139Svbart@nginx.com         /* Fall through. */
74216Svbart@nginx.com     case 0:
74316Svbart@nginx.com         break;
74416Svbart@nginx.com     default:
74516Svbart@nginx.com         nxt_unreachable();
74616Svbart@nginx.com     }
74716Svbart@nginx.com 
74816Svbart@nginx.com     return p;
74916Svbart@nginx.com }
75016Svbart@nginx.com 
75116Svbart@nginx.com 
75216Svbart@nginx.com static nxt_int_t
75316Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
75416Svbart@nginx.com     u_char *end)
75516Svbart@nginx.com {
75660Svbart@nginx.com     u_char            *p;
75760Svbart@nginx.com     nxt_http_field_t  *field;
75816Svbart@nginx.com 
75916Svbart@nginx.com     p = *pos;
76016Svbart@nginx.com 
76116Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
76216Svbart@nginx.com         p++;
76316Svbart@nginx.com 
76416Svbart@nginx.com         if (nxt_slow_path(p == end)) {
76516Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
76616Svbart@nginx.com             return NXT_AGAIN;
76716Svbart@nginx.com         }
76816Svbart@nginx.com     }
76916Svbart@nginx.com 
77016Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
77116Svbart@nginx.com         *pos = p + 1;
77216Svbart@nginx.com 
77367Svbart@nginx.com         if (rp->field_name.length != 0) {
77460Svbart@nginx.com             field = nxt_list_add(rp->fields);
77516Svbart@nginx.com 
77660Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
77760Svbart@nginx.com                 return NXT_ERROR;
77816Svbart@nginx.com             }
77916Svbart@nginx.com 
780417Svbart@nginx.com             field->hash = nxt_http_field_hash_end(rp->field_hash);
781417Svbart@nginx.com             field->skip = 0;
78260Svbart@nginx.com 
783417Svbart@nginx.com             field->name_length = rp->field_name.length;
784417Svbart@nginx.com             field->value_length = rp->field_value.length;
785417Svbart@nginx.com             field->name = rp->field_name.start;
786417Svbart@nginx.com             field->value = rp->field_value.start;
78767Svbart@nginx.com 
788417Svbart@nginx.com             rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
78967Svbart@nginx.com 
79067Svbart@nginx.com             rp->field_name.length = 0;
79167Svbart@nginx.com             rp->field_value.length = 0;
79216Svbart@nginx.com 
79316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
79416Svbart@nginx.com             return NXT_OK;
79516Svbart@nginx.com         }
79616Svbart@nginx.com 
79716Svbart@nginx.com         return NXT_DONE;
79816Svbart@nginx.com     }
79916Svbart@nginx.com 
800480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
80116Svbart@nginx.com }
80216Svbart@nginx.com 
80316Svbart@nginx.com 
804112Smax.romanov@nginx.com #define                                                                       \
805112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
806112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
807112Smax.romanov@nginx.com 
808112Smax.romanov@nginx.com 
809112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
810112Smax.romanov@nginx.com 
811112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
812611Svbart@nginx.com     0xFE, 0xDB, 0xFF, 0xFF,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
813112Smax.romanov@nginx.com 
814112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
815611Svbart@nginx.com     0xD6, 0x37, 0xFF, 0x7F,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
816112Smax.romanov@nginx.com 
817112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
818611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
819112Smax.romanov@nginx.com 
820112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
821611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
822112Smax.romanov@nginx.com 
823611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
824611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
825611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
826611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
827112Smax.romanov@nginx.com };
828112Smax.romanov@nginx.com 
829112Smax.romanov@nginx.com 
830112Smax.romanov@nginx.com static nxt_int_t
831112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
832112Smax.romanov@nginx.com {
8331168Svbart@nginx.com     u_char  *p, *u, c, ch, high, *exten, *args;
8341168Svbart@nginx.com 
835112Smax.romanov@nginx.com     enum {
836112Smax.romanov@nginx.com         sw_normal = 0,
837112Smax.romanov@nginx.com         sw_slash,
838112Smax.romanov@nginx.com         sw_dot,
839112Smax.romanov@nginx.com         sw_dot_dot,
840112Smax.romanov@nginx.com         sw_quoted,
841112Smax.romanov@nginx.com         sw_quoted_second,
842112Smax.romanov@nginx.com     } state, saved_state;
843112Smax.romanov@nginx.com 
844112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
845112Smax.romanov@nginx.com 
846112Smax.romanov@nginx.com     state = sw_normal;
847112Smax.romanov@nginx.com     saved_state = sw_normal;
848112Smax.romanov@nginx.com     p = rp->target_start;
849112Smax.romanov@nginx.com 
850112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
851112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
852112Smax.romanov@nginx.com         return NXT_ERROR;
853112Smax.romanov@nginx.com     }
854112Smax.romanov@nginx.com 
855112Smax.romanov@nginx.com     rp->path.length = 0;
856112Smax.romanov@nginx.com     rp->path.start = u;
857112Smax.romanov@nginx.com 
858112Smax.romanov@nginx.com     high = '\0';
8591168Svbart@nginx.com     exten = NULL;
8601168Svbart@nginx.com     args = NULL;
861112Smax.romanov@nginx.com 
862112Smax.romanov@nginx.com     while (p < rp->target_end) {
863112Smax.romanov@nginx.com 
864112Smax.romanov@nginx.com         ch = *p++;
865112Smax.romanov@nginx.com 
866112Smax.romanov@nginx.com     again:
867112Smax.romanov@nginx.com 
868112Smax.romanov@nginx.com         switch (state) {
869112Smax.romanov@nginx.com 
870112Smax.romanov@nginx.com         case sw_normal:
871112Smax.romanov@nginx.com 
872112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
873112Smax.romanov@nginx.com                 *u++ = ch;
874112Smax.romanov@nginx.com                 continue;
875112Smax.romanov@nginx.com             }
876112Smax.romanov@nginx.com 
877112Smax.romanov@nginx.com             switch (ch) {
878112Smax.romanov@nginx.com             case '/':
8791168Svbart@nginx.com                 exten = NULL;
880112Smax.romanov@nginx.com                 state = sw_slash;
881112Smax.romanov@nginx.com                 *u++ = ch;
882112Smax.romanov@nginx.com                 continue;
883112Smax.romanov@nginx.com             case '%':
884112Smax.romanov@nginx.com                 saved_state = state;
885112Smax.romanov@nginx.com                 state = sw_quoted;
886112Smax.romanov@nginx.com                 continue;
887112Smax.romanov@nginx.com             case '?':
8881168Svbart@nginx.com                 args = p;
889112Smax.romanov@nginx.com                 goto args;
890112Smax.romanov@nginx.com             case '#':
891112Smax.romanov@nginx.com                 goto done;
892112Smax.romanov@nginx.com             case '.':
8931168Svbart@nginx.com                 exten = u + 1;
894112Smax.romanov@nginx.com                 *u++ = ch;
895112Smax.romanov@nginx.com                 continue;
896112Smax.romanov@nginx.com             default:
897112Smax.romanov@nginx.com                 *u++ = ch;
898112Smax.romanov@nginx.com                 continue;
899112Smax.romanov@nginx.com             }
900112Smax.romanov@nginx.com 
901112Smax.romanov@nginx.com             break;
902112Smax.romanov@nginx.com 
903112Smax.romanov@nginx.com         case sw_slash:
904112Smax.romanov@nginx.com 
905112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
906112Smax.romanov@nginx.com                 state = sw_normal;
907112Smax.romanov@nginx.com                 *u++ = ch;
908112Smax.romanov@nginx.com                 continue;
909112Smax.romanov@nginx.com             }
910112Smax.romanov@nginx.com 
911112Smax.romanov@nginx.com             switch (ch) {
912112Smax.romanov@nginx.com             case '/':
913112Smax.romanov@nginx.com                 continue;
914112Smax.romanov@nginx.com             case '.':
915112Smax.romanov@nginx.com                 state = sw_dot;
916112Smax.romanov@nginx.com                 *u++ = ch;
917112Smax.romanov@nginx.com                 continue;
918112Smax.romanov@nginx.com             case '%':
919112Smax.romanov@nginx.com                 saved_state = state;
920112Smax.romanov@nginx.com                 state = sw_quoted;
921112Smax.romanov@nginx.com                 continue;
922112Smax.romanov@nginx.com             case '?':
9231168Svbart@nginx.com                 args = p;
924112Smax.romanov@nginx.com                 goto args;
925112Smax.romanov@nginx.com             case '#':
926112Smax.romanov@nginx.com                 goto done;
927112Smax.romanov@nginx.com             default:
928112Smax.romanov@nginx.com                 state = sw_normal;
929112Smax.romanov@nginx.com                 *u++ = ch;
930112Smax.romanov@nginx.com                 continue;
931112Smax.romanov@nginx.com             }
932112Smax.romanov@nginx.com 
933112Smax.romanov@nginx.com             break;
934112Smax.romanov@nginx.com 
935112Smax.romanov@nginx.com         case sw_dot:
936112Smax.romanov@nginx.com 
937112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
938112Smax.romanov@nginx.com                 state = sw_normal;
939112Smax.romanov@nginx.com                 *u++ = ch;
940112Smax.romanov@nginx.com                 continue;
941112Smax.romanov@nginx.com             }
942112Smax.romanov@nginx.com 
943112Smax.romanov@nginx.com             switch (ch) {
944112Smax.romanov@nginx.com             case '/':
945112Smax.romanov@nginx.com                 state = sw_slash;
946112Smax.romanov@nginx.com                 u--;
947112Smax.romanov@nginx.com                 continue;
948112Smax.romanov@nginx.com             case '.':
949112Smax.romanov@nginx.com                 state = sw_dot_dot;
950112Smax.romanov@nginx.com                 *u++ = ch;
951112Smax.romanov@nginx.com                 continue;
952112Smax.romanov@nginx.com             case '%':
953112Smax.romanov@nginx.com                 saved_state = state;
954112Smax.romanov@nginx.com                 state = sw_quoted;
955112Smax.romanov@nginx.com                 continue;
956112Smax.romanov@nginx.com             case '?':
9571168Svbart@nginx.com                 args = p;
958112Smax.romanov@nginx.com                 goto args;
959112Smax.romanov@nginx.com             case '#':
960112Smax.romanov@nginx.com                 goto done;
961112Smax.romanov@nginx.com             default:
962112Smax.romanov@nginx.com                 state = sw_normal;
963112Smax.romanov@nginx.com                 *u++ = ch;
964112Smax.romanov@nginx.com                 continue;
965112Smax.romanov@nginx.com             }
966112Smax.romanov@nginx.com 
967112Smax.romanov@nginx.com             break;
968112Smax.romanov@nginx.com 
969112Smax.romanov@nginx.com         case sw_dot_dot:
970112Smax.romanov@nginx.com 
971112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
972112Smax.romanov@nginx.com                 state = sw_normal;
973112Smax.romanov@nginx.com                 *u++ = ch;
974112Smax.romanov@nginx.com                 continue;
975112Smax.romanov@nginx.com             }
976112Smax.romanov@nginx.com 
977112Smax.romanov@nginx.com             switch (ch) {
978112Smax.romanov@nginx.com             case '/':
979112Smax.romanov@nginx.com                 state = sw_slash;
980112Smax.romanov@nginx.com                 u -= 5;
981112Smax.romanov@nginx.com                 for ( ;; ) {
982112Smax.romanov@nginx.com                     if (u < rp->path.start) {
983480Svbart@nginx.com                         return NXT_HTTP_PARSE_INVALID;
984112Smax.romanov@nginx.com                     }
985112Smax.romanov@nginx.com                     if (*u == '/') {
986112Smax.romanov@nginx.com                         u++;
987112Smax.romanov@nginx.com                         break;
988112Smax.romanov@nginx.com                     }
989112Smax.romanov@nginx.com                     u--;
990112Smax.romanov@nginx.com                 }
991112Smax.romanov@nginx.com                 break;
992112Smax.romanov@nginx.com 
993112Smax.romanov@nginx.com             case '%':
994112Smax.romanov@nginx.com                 saved_state = state;
995112Smax.romanov@nginx.com                 state = sw_quoted;
996112Smax.romanov@nginx.com                 continue;
997112Smax.romanov@nginx.com             case '?':
9981168Svbart@nginx.com                 args = p;
999112Smax.romanov@nginx.com                 goto args;
1000112Smax.romanov@nginx.com             case '#':
1001112Smax.romanov@nginx.com                 goto done;
1002112Smax.romanov@nginx.com             default:
1003112Smax.romanov@nginx.com                 state = sw_normal;
1004112Smax.romanov@nginx.com                 *u++ = ch;
1005112Smax.romanov@nginx.com                 continue;
1006112Smax.romanov@nginx.com             }
1007112Smax.romanov@nginx.com 
1008112Smax.romanov@nginx.com             break;
1009112Smax.romanov@nginx.com 
1010112Smax.romanov@nginx.com         case sw_quoted:
1011112Smax.romanov@nginx.com             rp->quoted_target = 1;
1012112Smax.romanov@nginx.com 
1013112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1014112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
1015112Smax.romanov@nginx.com                 state = sw_quoted_second;
1016112Smax.romanov@nginx.com                 continue;
1017112Smax.romanov@nginx.com             }
1018112Smax.romanov@nginx.com 
1019112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1020112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1021112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1022112Smax.romanov@nginx.com                 state = sw_quoted_second;
1023112Smax.romanov@nginx.com                 continue;
1024112Smax.romanov@nginx.com             }
1025112Smax.romanov@nginx.com 
1026480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
1027112Smax.romanov@nginx.com 
1028112Smax.romanov@nginx.com         case sw_quoted_second:
1029112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1030112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1031112Smax.romanov@nginx.com 
10321167Svbart@nginx.com                 if (ch == '%') {
1033112Smax.romanov@nginx.com                     state = sw_normal;
10341167Svbart@nginx.com                     *u++ = '%';
10351167Svbart@nginx.com 
10361167Svbart@nginx.com                     if (rp->encoded_slashes) {
10371167Svbart@nginx.com                         *u++ = '2';
10381167Svbart@nginx.com                         *u++ = '5';
10391167Svbart@nginx.com                     }
10401167Svbart@nginx.com 
1041112Smax.romanov@nginx.com                     continue;
10421167Svbart@nginx.com                 }
1043112Smax.romanov@nginx.com 
10441167Svbart@nginx.com                 if (ch == '#') {
10451167Svbart@nginx.com                     state = sw_normal;
10461167Svbart@nginx.com                     *u++ = '#';
10471167Svbart@nginx.com                     continue;
10481167Svbart@nginx.com                 }
10491167Svbart@nginx.com 
10501167Svbart@nginx.com                 if (ch == '\0') {
1051480Svbart@nginx.com                     return NXT_HTTP_PARSE_INVALID;
1052112Smax.romanov@nginx.com                 }
1053112Smax.romanov@nginx.com 
1054112Smax.romanov@nginx.com                 state = saved_state;
1055112Smax.romanov@nginx.com                 goto again;
1056112Smax.romanov@nginx.com             }
1057112Smax.romanov@nginx.com 
1058112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1059112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1060112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1061112Smax.romanov@nginx.com 
1062112Smax.romanov@nginx.com                 if (ch == '?') {
1063112Smax.romanov@nginx.com                     state = sw_normal;
1064112Smax.romanov@nginx.com                     *u++ = ch;
1065112Smax.romanov@nginx.com                     continue;
10661167Svbart@nginx.com                 }
1067112Smax.romanov@nginx.com 
10681167Svbart@nginx.com                 if (ch == '/' && rp->encoded_slashes) {
10691167Svbart@nginx.com                     state = sw_normal;
10701167Svbart@nginx.com                     *u++ = '%';
10711167Svbart@nginx.com                     *u++ = '2';
10721167Svbart@nginx.com                     *u++ = p[-1];  /* 'f' or 'F' */
10731167Svbart@nginx.com                     continue;
10741167Svbart@nginx.com                 }
10751167Svbart@nginx.com 
1076112Smax.romanov@nginx.com                 state = saved_state;
1077112Smax.romanov@nginx.com                 goto again;
1078112Smax.romanov@nginx.com             }
1079112Smax.romanov@nginx.com 
1080480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
1081