xref: /unit/src/nxt_http_parse.c (revision 623)
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 
37417Svbart@nginx.com #define NXT_HTTP_FIELD_HASH_INIT        159406
38417Svbart@nginx.com #define nxt_http_field_hash_char(h, c)  (((h) << 4) + (h) + (c))
39417Svbart@nginx.com #define nxt_http_field_hash_end(h)      (((h) >> 16) ^ (h))
40112Smax.romanov@nginx.com 
4116Svbart@nginx.com 
4216Svbart@nginx.com typedef enum {
4316Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
4416Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
4516Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
4616Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
4716Svbart@nginx.com 
4816Svbart@nginx.com     /* traps below are used for extended check only */
4916Svbart@nginx.com 
5016Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
5116Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
5216Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
5316Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
5416Svbart@nginx.com     NXT_HTTP_TARGET_PLUS,        /*  +  */
5516Svbart@nginx.com } nxt_http_target_traps_e;
5616Svbart@nginx.com 
5716Svbart@nginx.com 
5816Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
5916Svbart@nginx.com     /* \0                               \n        \r       */
6016Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
6116Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
6252Svbart@nginx.com 
6352Svbart@nginx.com     /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
6416Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 9,  0, 0, 6, 5,
6552Svbart@nginx.com 
6652Svbart@nginx.com     /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
6716Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
6816Svbart@nginx.com };
6916Svbart@nginx.com 
7016Svbart@nginx.com 
7116Svbart@nginx.com nxt_inline nxt_http_target_traps_e
7216Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
7316Svbart@nginx.com {
7416Svbart@nginx.com     u_char      *p;
7516Svbart@nginx.com     nxt_uint_t  trap;
7616Svbart@nginx.com 
7716Svbart@nginx.com     p = *pos;
7816Svbart@nginx.com 
79409Svbart@nginx.com     while (nxt_fast_path(end - p >= 10)) {
8016Svbart@nginx.com 
81409Svbart@nginx.com #define nxt_target_test_char(ch)                                              \
82409Svbart@nginx.com                                                                               \
83409Svbart@nginx.com         trap = nxt_http_target_chars[ch];                                     \
8416Svbart@nginx.com                                                                               \
85409Svbart@nginx.com         if (nxt_slow_path(trap != 0)) {                                       \
86409Svbart@nginx.com             *pos = &(ch);                                                     \
87409Svbart@nginx.com             return trap;                                                      \
8816Svbart@nginx.com         }
8916Svbart@nginx.com 
90409Svbart@nginx.com /* enddef */
91409Svbart@nginx.com 
92409Svbart@nginx.com         nxt_target_test_char(p[0]);
93409Svbart@nginx.com         nxt_target_test_char(p[1]);
94409Svbart@nginx.com         nxt_target_test_char(p[2]);
95409Svbart@nginx.com         nxt_target_test_char(p[3]);
9616Svbart@nginx.com 
97409Svbart@nginx.com         nxt_target_test_char(p[4]);
98409Svbart@nginx.com         nxt_target_test_char(p[5]);
99409Svbart@nginx.com         nxt_target_test_char(p[6]);
100409Svbart@nginx.com         nxt_target_test_char(p[7]);
10116Svbart@nginx.com 
102409Svbart@nginx.com         nxt_target_test_char(p[8]);
103409Svbart@nginx.com         nxt_target_test_char(p[9]);
10416Svbart@nginx.com 
105409Svbart@nginx.com         p += 10;
10616Svbart@nginx.com     }
10716Svbart@nginx.com 
108410Svbart@nginx.com     while (p != end) {
109410Svbart@nginx.com         nxt_target_test_char(*p); p++;
110410Svbart@nginx.com     }
111410Svbart@nginx.com 
112409Svbart@nginx.com     return NXT_HTTP_TARGET_AGAIN;
11316Svbart@nginx.com }
11416Svbart@nginx.com 
11516Svbart@nginx.com 
11616Svbart@nginx.com nxt_int_t
117417Svbart@nginx.com nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp)
118417Svbart@nginx.com {
119417Svbart@nginx.com     rp->mem_pool = mp;
120417Svbart@nginx.com 
121417Svbart@nginx.com     rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
122417Svbart@nginx.com     if (nxt_slow_path(rp->fields == NULL)){
123417Svbart@nginx.com         return NXT_ERROR;
124417Svbart@nginx.com     }
125417Svbart@nginx.com 
126417Svbart@nginx.com     rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
127417Svbart@nginx.com 
128417Svbart@nginx.com     return NXT_OK;
129417Svbart@nginx.com }
130417Svbart@nginx.com 
131417Svbart@nginx.com 
132417Svbart@nginx.com nxt_int_t
13316Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
13416Svbart@nginx.com {
13516Svbart@nginx.com     nxt_int_t  rc;
13616Svbart@nginx.com 
13716Svbart@nginx.com     if (rp->handler == NULL) {
13816Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13916Svbart@nginx.com     }
14016Svbart@nginx.com 
14116Svbart@nginx.com     do {
14216Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
14316Svbart@nginx.com     } while (rc == NXT_OK);
14416Svbart@nginx.com 
14516Svbart@nginx.com     return rc;
14616Svbart@nginx.com }
14716Svbart@nginx.com 
14816Svbart@nginx.com 
149422Svbart@nginx.com nxt_int_t
150422Svbart@nginx.com nxt_http_parse_fields(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
151422Svbart@nginx.com {
152422Svbart@nginx.com     nxt_int_t  rc;
153422Svbart@nginx.com 
154422Svbart@nginx.com     if (rp->handler == NULL) {
155422Svbart@nginx.com         rp->handler = &nxt_http_parse_field_name;
156422Svbart@nginx.com     }
157422Svbart@nginx.com 
158422Svbart@nginx.com     do {
159422Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
160422Svbart@nginx.com     } while (rc == NXT_OK);
161422Svbart@nginx.com 
162422Svbart@nginx.com     return rc;
163422Svbart@nginx.com }
164422Svbart@nginx.com 
165422Svbart@nginx.com 
16616Svbart@nginx.com static nxt_int_t
16716Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
16816Svbart@nginx.com     u_char *end)
16916Svbart@nginx.com {
17016Svbart@nginx.com     u_char                   *p, ch, *after_slash;
17116Svbart@nginx.com     nxt_int_t                rc;
172481Svbart@nginx.com     nxt_http_ver_t           ver;
17316Svbart@nginx.com     nxt_http_target_traps_e  trap;
17416Svbart@nginx.com 
17516Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
17616Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
17716Svbart@nginx.com 
17816Svbart@nginx.com     p = *pos;
17916Svbart@nginx.com 
18016Svbart@nginx.com     rp->method.start = p;
18116Svbart@nginx.com 
182409Svbart@nginx.com     for ( ;; ) {
183409Svbart@nginx.com 
184409Svbart@nginx.com         while (nxt_fast_path(end - p >= 8)) {
18516Svbart@nginx.com 
186409Svbart@nginx.com #define nxt_method_test_char(ch)                                              \
187409Svbart@nginx.com                                                                               \
188409Svbart@nginx.com             if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
189409Svbart@nginx.com                 p = &(ch);                                                    \
190409Svbart@nginx.com                 goto method_unusual_char;                                     \
19116Svbart@nginx.com             }
19216Svbart@nginx.com 
193409Svbart@nginx.com /* enddef */
194409Svbart@nginx.com 
195409Svbart@nginx.com             nxt_method_test_char(p[0]);
196409Svbart@nginx.com             nxt_method_test_char(p[1]);
197409Svbart@nginx.com             nxt_method_test_char(p[2]);
198409Svbart@nginx.com             nxt_method_test_char(p[3]);
19916Svbart@nginx.com 
200409Svbart@nginx.com             nxt_method_test_char(p[4]);
201409Svbart@nginx.com             nxt_method_test_char(p[5]);
202409Svbart@nginx.com             nxt_method_test_char(p[6]);
203409Svbart@nginx.com             nxt_method_test_char(p[7]);
20416Svbart@nginx.com 
205409Svbart@nginx.com             p += 8;
206409Svbart@nginx.com         }
20716Svbart@nginx.com 
208410Svbart@nginx.com         while (p != end) {
209410Svbart@nginx.com             nxt_method_test_char(*p); p++;
210410Svbart@nginx.com         }
211410Svbart@nginx.com 
212*623Svbart@nginx.com         rp->method.length = p - rp->method.start;
213*623Svbart@nginx.com 
214409Svbart@nginx.com         return NXT_AGAIN;
215409Svbart@nginx.com 
216409Svbart@nginx.com     method_unusual_char:
217409Svbart@nginx.com 
218409Svbart@nginx.com         ch = *p;
21916Svbart@nginx.com 
22016Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
22116Svbart@nginx.com             rp->method.length = p - rp->method.start;
22216Svbart@nginx.com             break;
22316Svbart@nginx.com         }
22416Svbart@nginx.com 
22516Svbart@nginx.com         if (ch == '_' || ch == '-') {
226409Svbart@nginx.com             p++;
22716Svbart@nginx.com             continue;
22816Svbart@nginx.com         }
22916Svbart@nginx.com 
23016Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
23116Svbart@nginx.com             rp->method.start++;
232409Svbart@nginx.com             p++;
23316Svbart@nginx.com             continue;
23416Svbart@nginx.com         }
23516Svbart@nginx.com 
236*623Svbart@nginx.com         rp->method.length = p - rp->method.start;
237*623Svbart@nginx.com 
238480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
23916Svbart@nginx.com     }
24016Svbart@nginx.com 
24116Svbart@nginx.com     p++;
24216Svbart@nginx.com 
24316Svbart@nginx.com     if (nxt_slow_path(p == end)) {
24416Svbart@nginx.com         return NXT_AGAIN;
24516Svbart@nginx.com     }
24616Svbart@nginx.com 
24716Svbart@nginx.com     /* target */
24816Svbart@nginx.com 
24916Svbart@nginx.com     ch = *p;
25016Svbart@nginx.com 
25116Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
25216Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
25316Svbart@nginx.com 
25416Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
25516Svbart@nginx.com             return rc;
25616Svbart@nginx.com         }
25716Svbart@nginx.com     }
25816Svbart@nginx.com 
25916Svbart@nginx.com     rp->target_start = p;
26016Svbart@nginx.com 
26116Svbart@nginx.com     after_slash = p + 1;
26216Svbart@nginx.com 
26316Svbart@nginx.com     for ( ;; ) {
26416Svbart@nginx.com         p++;
26516Svbart@nginx.com 
26616Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
26716Svbart@nginx.com 
26816Svbart@nginx.com         switch (trap) {
26916Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
27016Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
27116Svbart@nginx.com                 rp->complex_target = 1;
27216Svbart@nginx.com                 goto rest_of_target;
27316Svbart@nginx.com             }
27416Svbart@nginx.com 
27516Svbart@nginx.com             after_slash = p + 1;
27616Svbart@nginx.com 
27716Svbart@nginx.com             rp->exten_start = 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 
28616Svbart@nginx.com             rp->exten_start = p + 1;
28716Svbart@nginx.com             continue;
28816Svbart@nginx.com 
28916Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
29016Svbart@nginx.com             rp->args_start = 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_PLUS:
30216Svbart@nginx.com             rp->plus_in_target = 1;
30316Svbart@nginx.com             continue;
30416Svbart@nginx.com 
30516Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
30616Svbart@nginx.com             rp->complex_target = 1;
30716Svbart@nginx.com             goto rest_of_target;
30816Svbart@nginx.com 
30916Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
310621Svbart@nginx.com             rp->target_end = p;
31116Svbart@nginx.com             return NXT_AGAIN;
31216Svbart@nginx.com 
31316Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
314621Svbart@nginx.com             rp->target_end = p;
315480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
31616Svbart@nginx.com         }
31716Svbart@nginx.com 
31816Svbart@nginx.com         nxt_unreachable();
31916Svbart@nginx.com     }
32016Svbart@nginx.com 
32116Svbart@nginx.com rest_of_target:
32216Svbart@nginx.com 
32316Svbart@nginx.com     for ( ;; ) {
32416Svbart@nginx.com         p++;
32516Svbart@nginx.com 
32619Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
32716Svbart@nginx.com 
32816Svbart@nginx.com         switch (trap) {
32916Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
33016Svbart@nginx.com             rp->target_end = p;
33116Svbart@nginx.com             goto space_after_target;
33216Svbart@nginx.com 
33316Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
33416Svbart@nginx.com             rp->complex_target = 1;
33516Svbart@nginx.com             continue;
33616Svbart@nginx.com 
33716Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
338621Svbart@nginx.com             rp->target_end = p;
33916Svbart@nginx.com             return NXT_AGAIN;
34016Svbart@nginx.com 
34116Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
342621Svbart@nginx.com             rp->target_end = p;
343480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
34416Svbart@nginx.com 
34516Svbart@nginx.com         default:
34616Svbart@nginx.com             continue;
34716Svbart@nginx.com         }
34816Svbart@nginx.com 
34916Svbart@nginx.com         nxt_unreachable();
35016Svbart@nginx.com     }
35116Svbart@nginx.com 
35216Svbart@nginx.com space_after_target:
35316Svbart@nginx.com 
35416Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
355410Svbart@nginx.com 
356410Svbart@nginx.com         do {
357410Svbart@nginx.com             p++;
358410Svbart@nginx.com 
359410Svbart@nginx.com             if (p == end) {
360410Svbart@nginx.com                 return NXT_AGAIN;
361410Svbart@nginx.com             }
362410Svbart@nginx.com 
363410Svbart@nginx.com         } while (*p == ' ');
364410Svbart@nginx.com 
365410Svbart@nginx.com         if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
366410Svbart@nginx.com 
367410Svbart@nginx.com             switch (end - p) {
368410Svbart@nginx.com             case 8:
369410Svbart@nginx.com                 if (p[7] < '0' || p[7] > '9') {
370410Svbart@nginx.com                     break;
371410Svbart@nginx.com                 }
372410Svbart@nginx.com                 /* Fall through. */
373410Svbart@nginx.com             case 7:
374410Svbart@nginx.com                 if (p[6] != '.') {
375410Svbart@nginx.com                     break;
376410Svbart@nginx.com                 }
377410Svbart@nginx.com                 /* Fall through. */
378410Svbart@nginx.com             case 6:
379410Svbart@nginx.com                 if (p[5] < '0' || p[5] > '9') {
380410Svbart@nginx.com                     break;
381410Svbart@nginx.com                 }
382410Svbart@nginx.com                 /* Fall through. */
383410Svbart@nginx.com             default:
384410Svbart@nginx.com                 return NXT_AGAIN;
385410Svbart@nginx.com             }
386410Svbart@nginx.com         }
387410Svbart@nginx.com 
388410Svbart@nginx.com         rp->space_in_target = 1;
389410Svbart@nginx.com         goto rest_of_target;
39016Svbart@nginx.com     }
39116Svbart@nginx.com 
39216Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
39316Svbart@nginx.com 
394482Svbart@nginx.com     if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) {
395482Svbart@nginx.com 
396482Svbart@nginx.com         if (p[1] == ' ') {
397482Svbart@nginx.com             /* surplus space after tartet */
398482Svbart@nginx.com             p++;
399482Svbart@nginx.com             goto space_after_target;
400482Svbart@nginx.com         }
401482Svbart@nginx.com 
402482Svbart@nginx.com         rp->space_in_target = 1;
403482Svbart@nginx.com         goto rest_of_target;
404482Svbart@nginx.com     }
405482Svbart@nginx.com 
406481Svbart@nginx.com     nxt_memcpy(ver.str, &p[1], 8);
40716Svbart@nginx.com 
408482Svbart@nginx.com     if (nxt_fast_path(ver.ui64 == http11.ui64
409482Svbart@nginx.com                       || ver.ui64 == http10.ui64
410482Svbart@nginx.com                       || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0
411482Svbart@nginx.com                           && ver.s.minor >= '0' && ver.s.minor <= '9')))
41216Svbart@nginx.com     {
413481Svbart@nginx.com         rp->version.ui64 = ver.ui64;
41416Svbart@nginx.com 
41516Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
41616Svbart@nginx.com             p += 10;
41716Svbart@nginx.com 
41816Svbart@nginx.com             if (nxt_slow_path(p == end)) {
41916Svbart@nginx.com                 return NXT_AGAIN;
42016Svbart@nginx.com             }
42116Svbart@nginx.com 
42216Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
423480Svbart@nginx.com                 return NXT_HTTP_PARSE_INVALID;
42416Svbart@nginx.com             }
42516Svbart@nginx.com 
42616Svbart@nginx.com             *pos = p + 1;
427112Smax.romanov@nginx.com 
428112Smax.romanov@nginx.com         } else {
429112Smax.romanov@nginx.com             *pos = p + 10;
430112Smax.romanov@nginx.com         }
431112Smax.romanov@nginx.com 
432112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
433112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
434112Smax.romanov@nginx.com 
435112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
436112Smax.romanov@nginx.com                 return rc;
437112Smax.romanov@nginx.com             }
438112Smax.romanov@nginx.com 
43916Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
44016Svbart@nginx.com         }
44116Svbart@nginx.com 
442112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
443112Smax.romanov@nginx.com 
444112Smax.romanov@nginx.com         if (rp->args_start != NULL) {
445112Smax.romanov@nginx.com             rp->path.length = rp->args_start - rp->target_start - 1;
446112Smax.romanov@nginx.com 
447112Smax.romanov@nginx.com             rp->args.start = rp->args_start;
448112Smax.romanov@nginx.com             rp->args.length = rp->target_end - rp->args_start;
449112Smax.romanov@nginx.com 
450112Smax.romanov@nginx.com         } else {
451112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
452112Smax.romanov@nginx.com         }
453112Smax.romanov@nginx.com 
454112Smax.romanov@nginx.com         if (rp->exten_start) {
455613Svbart@nginx.com             rp->exten.length = rp->path.start + rp->path.length
456613Svbart@nginx.com                                - rp->exten_start;
457112Smax.romanov@nginx.com             rp->exten.start = rp->exten_start;
458112Smax.romanov@nginx.com         }
459112Smax.romanov@nginx.com 
46016Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
46116Svbart@nginx.com     }
46216Svbart@nginx.com 
463482Svbart@nginx.com     if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0
464482Svbart@nginx.com         && ver.s.major >= '0' && ver.s.major <= '9'
465482Svbart@nginx.com         && ver.s.point == '.'
466482Svbart@nginx.com         && ver.s.minor >= '0' && ver.s.minor <= '9')
467482Svbart@nginx.com     {
468622Svbart@nginx.com         rp->version.ui64 = ver.ui64;
469482Svbart@nginx.com         return NXT_HTTP_PARSE_UNSUPPORTED_VERSION;
47016Svbart@nginx.com     }
47116Svbart@nginx.com 
472482Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
47316Svbart@nginx.com }
47416Svbart@nginx.com 
47516Svbart@nginx.com 
47616Svbart@nginx.com static nxt_int_t
47716Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
47816Svbart@nginx.com     u_char *end)
47916Svbart@nginx.com {
48016Svbart@nginx.com     u_char  *p, ch;
48116Svbart@nginx.com 
48216Svbart@nginx.com     p = *pos;
48316Svbart@nginx.com 
48416Svbart@nginx.com     ch = *p;
48516Svbart@nginx.com 
48616Svbart@nginx.com     if (ch == ' ') {
48716Svbart@nginx.com         /* skip surplus spaces before target */
48816Svbart@nginx.com 
48916Svbart@nginx.com         do {
49016Svbart@nginx.com             p++;
49116Svbart@nginx.com 
49216Svbart@nginx.com             if (nxt_slow_path(p == end)) {
49316Svbart@nginx.com                 return NXT_AGAIN;
49416Svbart@nginx.com             }
49516Svbart@nginx.com 
49616Svbart@nginx.com             ch = *p;
49716Svbart@nginx.com 
49816Svbart@nginx.com         } while (ch == ' ');
49916Svbart@nginx.com 
50016Svbart@nginx.com         if (ch == '/') {
50116Svbart@nginx.com             *pos = p;
50216Svbart@nginx.com             return NXT_OK;
50316Svbart@nginx.com         }
50416Svbart@nginx.com     }
50516Svbart@nginx.com 
50616Svbart@nginx.com     /* absolute path or '*' */
50716Svbart@nginx.com 
50816Svbart@nginx.com     /* TODO */
50916Svbart@nginx.com 
510480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
51116Svbart@nginx.com }
51216Svbart@nginx.com 
51316Svbart@nginx.com 
51416Svbart@nginx.com static nxt_int_t
51516Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
51616Svbart@nginx.com     u_char *end)
51716Svbart@nginx.com {
518417Svbart@nginx.com     u_char    *p, c;
519417Svbart@nginx.com     size_t    len;
520417Svbart@nginx.com     uint32_t  hash;
52116Svbart@nginx.com 
52216Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
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" "0123456789\0\0\0\0\0\0"
52516Svbart@nginx.com 
52616Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
527454Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_"
52816Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
52916Svbart@nginx.com 
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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
53216Svbart@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"
53316Svbart@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";
53416Svbart@nginx.com 
535417Svbart@nginx.com     p = *pos + rp->field_name.length;
536417Svbart@nginx.com     hash = rp->field_hash;
53716Svbart@nginx.com 
538417Svbart@nginx.com     while (nxt_fast_path(end - p >= 8)) {
539409Svbart@nginx.com 
540417Svbart@nginx.com #define nxt_field_name_test_char(ch)                                          \
541409Svbart@nginx.com                                                                               \
54219Svbart@nginx.com         c = normal[ch];                                                       \
54319Svbart@nginx.com                                                                               \
54419Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
545417Svbart@nginx.com             p = &(ch);                                                        \
54619Svbart@nginx.com             goto name_end;                                                    \
54719Svbart@nginx.com         }                                                                     \
54819Svbart@nginx.com                                                                               \
549417Svbart@nginx.com         hash = nxt_http_field_hash_char(hash, c);
550409Svbart@nginx.com 
551409Svbart@nginx.com /* enddef */
55216Svbart@nginx.com 
553417Svbart@nginx.com         nxt_field_name_test_char(p[0]);
554417Svbart@nginx.com         nxt_field_name_test_char(p[1]);
555417Svbart@nginx.com         nxt_field_name_test_char(p[2]);
556417Svbart@nginx.com         nxt_field_name_test_char(p[3]);
55716Svbart@nginx.com 
558417Svbart@nginx.com         nxt_field_name_test_char(p[4]);
559417Svbart@nginx.com         nxt_field_name_test_char(p[5]);
560417Svbart@nginx.com         nxt_field_name_test_char(p[6]);
561417Svbart@nginx.com         nxt_field_name_test_char(p[7]);
562417Svbart@nginx.com 
563417Svbart@nginx.com         p += 8;
56419Svbart@nginx.com     }
56516Svbart@nginx.com 
566417Svbart@nginx.com     while (nxt_fast_path(p != end)) {
567417Svbart@nginx.com         nxt_field_name_test_char(*p); p++;
56819Svbart@nginx.com     }
56916Svbart@nginx.com 
570417Svbart@nginx.com     len = p - *pos;
571417Svbart@nginx.com 
572417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
573480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
574417Svbart@nginx.com     }
575417Svbart@nginx.com 
576417Svbart@nginx.com     rp->field_hash = hash;
577417Svbart@nginx.com     rp->field_name.length = len;
578417Svbart@nginx.com 
57916Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
58016Svbart@nginx.com 
58116Svbart@nginx.com     return NXT_AGAIN;
58219Svbart@nginx.com 
58319Svbart@nginx.com name_end:
58419Svbart@nginx.com 
585417Svbart@nginx.com     if (nxt_fast_path(*p == ':')) {
586417Svbart@nginx.com         if (nxt_slow_path(p == *pos)) {
587480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
58819Svbart@nginx.com         }
58919Svbart@nginx.com 
590417Svbart@nginx.com         len = p - *pos;
591417Svbart@nginx.com 
592417Svbart@nginx.com         if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
593480Svbart@nginx.com             return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
594417Svbart@nginx.com         }
59519Svbart@nginx.com 
596417Svbart@nginx.com         rp->field_hash = hash;
597417Svbart@nginx.com 
598417Svbart@nginx.com         rp->field_name.length = len;
599417Svbart@nginx.com         rp->field_name.start = *pos;
600417Svbart@nginx.com 
601417Svbart@nginx.com         *pos = p + 1;
60219Svbart@nginx.com 
60319Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
60419Svbart@nginx.com     }
60519Svbart@nginx.com 
606417Svbart@nginx.com     if (nxt_slow_path(p != *pos)) {
607480Svbart@nginx.com         return NXT_HTTP_PARSE_INVALID;
60859Svbart@nginx.com     }
60919Svbart@nginx.com 
61019Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
61116Svbart@nginx.com }
61216Svbart@nginx.com 
61316Svbart@nginx.com 
61416Svbart@nginx.com static nxt_int_t
61516Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
61616Svbart@nginx.com     u_char *end)
61716Svbart@nginx.com {
618574Svbart@nginx.com     u_char  *p, *start, ch;
619417Svbart@nginx.com     size_t  len;
62016Svbart@nginx.com 
62116Svbart@nginx.com     p = *pos;
62216Svbart@nginx.com 
62316Svbart@nginx.com     for ( ;; ) {
62416Svbart@nginx.com         if (nxt_slow_path(p == end)) {
62516Svbart@nginx.com             *pos = p;
62616Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
62716Svbart@nginx.com             return NXT_AGAIN;
62816Svbart@nginx.com         }
62916Svbart@nginx.com 
630577Svbart@nginx.com         ch = *p;
631577Svbart@nginx.com 
632577Svbart@nginx.com         if (ch != ' ' && ch != '\t') {
63316Svbart@nginx.com             break;
63416Svbart@nginx.com         }
63516Svbart@nginx.com 
63616Svbart@nginx.com         p++;
63716Svbart@nginx.com     }
63816Svbart@nginx.com 
639574Svbart@nginx.com     start = p;
64016Svbart@nginx.com 
64167Svbart@nginx.com     p += rp->field_value.length;
64216Svbart@nginx.com 
643576Svbart@nginx.com     for ( ;; ) {
644576Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
645417Svbart@nginx.com 
646576Svbart@nginx.com         if (nxt_slow_path(p == end)) {
647576Svbart@nginx.com             *pos = start;
648576Svbart@nginx.com 
649576Svbart@nginx.com             len = p - start;
650574Svbart@nginx.com 
651576Svbart@nginx.com             if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
652576Svbart@nginx.com                 return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
653576Svbart@nginx.com             }
654417Svbart@nginx.com 
655576Svbart@nginx.com             rp->field_value.length = len;
656576Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
657576Svbart@nginx.com             return NXT_AGAIN;
65816Svbart@nginx.com         }
65916Svbart@nginx.com 
660576Svbart@nginx.com         ch = *p;
661576Svbart@nginx.com 
662576Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
663576Svbart@nginx.com             break;
664576Svbart@nginx.com         }
66516Svbart@nginx.com 
666576Svbart@nginx.com         if (ch != '\t') {
667576Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
668576Svbart@nginx.com         }
66916Svbart@nginx.com 
670576Svbart@nginx.com         p++;
67116Svbart@nginx.com     }
67216Svbart@nginx.com 
673574Svbart@nginx.com     *pos = p;
674574Svbart@nginx.com 
675574Svbart@nginx.com     if (nxt_fast_path(p != start)) {
676577Svbart@nginx.com 
677577Svbart@nginx.com         while (p[-1] == ' ' || p[-1] == '\t') {
67816Svbart@nginx.com             p--;
67916Svbart@nginx.com         }
68016Svbart@nginx.com     }
68116Svbart@nginx.com 
682574Svbart@nginx.com     len = p - start;
683417Svbart@nginx.com 
684417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
685480Svbart@nginx.com         return NXT_HTTP_PARSE_TOO_LARGE_FIELD;
686417Svbart@nginx.com     }
687417Svbart@nginx.com 
688417Svbart@nginx.com     rp->field_value.length = len;
689574Svbart@nginx.com     rp->field_value.start = start;
69016Svbart@nginx.com 
69116Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
69216Svbart@nginx.com }
69316Svbart@nginx.com 
69416Svbart@nginx.com 
69516Svbart@nginx.com static u_char *
69616Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
69716Svbart@nginx.com {
698409Svbart@nginx.com     while (nxt_fast_path(end - p >= 16)) {
699409Svbart@nginx.com 
700409Svbart@nginx.com #define nxt_field_end_test_char(ch)                                           \
701409Svbart@nginx.com                                                                               \
702611Svbart@nginx.com         /* Values below 0x20 become more than 0xDF. */                        \
703611Svbart@nginx.com         if (nxt_slow_path((u_char) ((ch) - 0x20) > 0x5E)) {                   \
704409Svbart@nginx.com             return &(ch);                                                     \
705409Svbart@nginx.com         }
706409Svbart@nginx.com 
707409Svbart@nginx.com /* enddef */
708409Svbart@nginx.com 
709409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
710409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
711409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
712409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
71316Svbart@nginx.com 
714409Svbart@nginx.com         nxt_field_end_test_char(p[4]);
715409Svbart@nginx.com         nxt_field_end_test_char(p[5]);
716409Svbart@nginx.com         nxt_field_end_test_char(p[6]);
717409Svbart@nginx.com         nxt_field_end_test_char(p[7]);
718409Svbart@nginx.com 
719409Svbart@nginx.com         nxt_field_end_test_char(p[8]);
720409Svbart@nginx.com         nxt_field_end_test_char(p[9]);
721409Svbart@nginx.com         nxt_field_end_test_char(p[10]);
722409Svbart@nginx.com         nxt_field_end_test_char(p[11]);
723409Svbart@nginx.com 
724409Svbart@nginx.com         nxt_field_end_test_char(p[12]);
725409Svbart@nginx.com         nxt_field_end_test_char(p[13]);
726409Svbart@nginx.com         nxt_field_end_test_char(p[14]);
727409Svbart@nginx.com         nxt_field_end_test_char(p[15]);
728409Svbart@nginx.com 
729409Svbart@nginx.com         p += 16;
73016Svbart@nginx.com     }
73116Svbart@nginx.com 
732409Svbart@nginx.com     while (nxt_fast_path(end - p >= 4)) {
73319Svbart@nginx.com 
734409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
735409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
736409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
737409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
73816Svbart@nginx.com 
739409Svbart@nginx.com         p += 4;
74019Svbart@nginx.com     }
74119Svbart@nginx.com 
74219Svbart@nginx.com     switch (end - p) {
74316Svbart@nginx.com     case 3:
744409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
74539Svbart@nginx.com         /* Fall through. */
74616Svbart@nginx.com     case 2:
747409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
74839Svbart@nginx.com         /* Fall through. */
74916Svbart@nginx.com     case 1:
750409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
75139Svbart@nginx.com         /* Fall through. */
75216Svbart@nginx.com     case 0:
75316Svbart@nginx.com         break;
75416Svbart@nginx.com     default:
75516Svbart@nginx.com         nxt_unreachable();
75616Svbart@nginx.com     }
75716Svbart@nginx.com 
75816Svbart@nginx.com     return p;
75916Svbart@nginx.com }
76016Svbart@nginx.com 
76116Svbart@nginx.com 
76216Svbart@nginx.com static nxt_int_t
76316Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
76416Svbart@nginx.com     u_char *end)
76516Svbart@nginx.com {
76660Svbart@nginx.com     u_char            *p;
76760Svbart@nginx.com     nxt_http_field_t  *field;
76816Svbart@nginx.com 
76916Svbart@nginx.com     p = *pos;
77016Svbart@nginx.com 
77116Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
77216Svbart@nginx.com         p++;
77316Svbart@nginx.com 
77416Svbart@nginx.com         if (nxt_slow_path(p == end)) {
77516Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
77616Svbart@nginx.com             return NXT_AGAIN;
77716Svbart@nginx.com         }
77816Svbart@nginx.com     }
77916Svbart@nginx.com 
78016Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
78116Svbart@nginx.com         *pos = p + 1;
78216Svbart@nginx.com 
78367Svbart@nginx.com         if (rp->field_name.length != 0) {
78460Svbart@nginx.com             field = nxt_list_add(rp->fields);
78516Svbart@nginx.com 
78660Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
78760Svbart@nginx.com                 return NXT_ERROR;
78816Svbart@nginx.com             }
78916Svbart@nginx.com 
790417Svbart@nginx.com             field->hash = nxt_http_field_hash_end(rp->field_hash);
791417Svbart@nginx.com             field->skip = 0;
79260Svbart@nginx.com 
793417Svbart@nginx.com             field->name_length = rp->field_name.length;
794417Svbart@nginx.com             field->value_length = rp->field_value.length;
795417Svbart@nginx.com             field->name = rp->field_name.start;
796417Svbart@nginx.com             field->value = rp->field_value.start;
79767Svbart@nginx.com 
798417Svbart@nginx.com             rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
79967Svbart@nginx.com 
80067Svbart@nginx.com             rp->field_name.length = 0;
80167Svbart@nginx.com             rp->field_value.length = 0;
80216Svbart@nginx.com 
80316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
80416Svbart@nginx.com             return NXT_OK;
80516Svbart@nginx.com         }
80616Svbart@nginx.com 
80716Svbart@nginx.com         return NXT_DONE;
80816Svbart@nginx.com     }
80916Svbart@nginx.com 
810480Svbart@nginx.com     return NXT_HTTP_PARSE_INVALID;
81116Svbart@nginx.com }
81216Svbart@nginx.com 
81316Svbart@nginx.com 
814112Smax.romanov@nginx.com #define                                                                       \
815112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
816112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
817112Smax.romanov@nginx.com 
818112Smax.romanov@nginx.com 
819112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
820112Smax.romanov@nginx.com 
821112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
822611Svbart@nginx.com     0xFE, 0xDB, 0xFF, 0xFF,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
823112Smax.romanov@nginx.com 
824112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
825611Svbart@nginx.com     0xD6, 0x37, 0xFF, 0x7F,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
826112Smax.romanov@nginx.com 
827112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
828611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
829112Smax.romanov@nginx.com 
830112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
831611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
832112Smax.romanov@nginx.com 
833611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
834611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
835611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
836611Svbart@nginx.com     0xFF, 0xFF, 0xFF, 0xFF,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
837112Smax.romanov@nginx.com };
838112Smax.romanov@nginx.com 
839112Smax.romanov@nginx.com 
840112Smax.romanov@nginx.com static nxt_int_t
841112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
842112Smax.romanov@nginx.com {
843112Smax.romanov@nginx.com     u_char  *p, *u, c, ch, high;
844112Smax.romanov@nginx.com     enum {
845112Smax.romanov@nginx.com         sw_normal = 0,
846112Smax.romanov@nginx.com         sw_slash,
847112Smax.romanov@nginx.com         sw_dot,
848112Smax.romanov@nginx.com         sw_dot_dot,
849112Smax.romanov@nginx.com         sw_quoted,
850112Smax.romanov@nginx.com         sw_quoted_second,
851112Smax.romanov@nginx.com     } state, saved_state;
852112Smax.romanov@nginx.com 
853112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
854112Smax.romanov@nginx.com 
855112Smax.romanov@nginx.com     state = sw_normal;
856112Smax.romanov@nginx.com     saved_state = sw_normal;
857112Smax.romanov@nginx.com     p = rp->target_start;
858112Smax.romanov@nginx.com 
859112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
860112Smax.romanov@nginx.com 
861112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
862112Smax.romanov@nginx.com         return NXT_ERROR;
863112Smax.romanov@nginx.com     }
864112Smax.romanov@nginx.com 
865112Smax.romanov@nginx.com     rp->path.length = 0;
866112Smax.romanov@nginx.com     rp->path.start = u;
867112Smax.romanov@nginx.com 
868112Smax.romanov@nginx.com     high = '\0';
869112Smax.romanov@nginx.com     rp->exten_start = NULL;
870112Smax.romanov@nginx.com     rp->args_start = NULL;
871112Smax.romanov@nginx.com 
872112Smax.romanov@nginx.com     while (p < rp->target_end) {
873112Smax.romanov@nginx.com 
874112Smax.romanov@nginx.com         ch = *p++;
875112Smax.romanov@nginx.com 
876112Smax.romanov@nginx.com     again:
877112Smax.romanov@nginx.com 
878112Smax.romanov@nginx.com         switch (state) {
879112Smax.romanov@nginx.com 
880112Smax.romanov@nginx.com         case sw_normal:
881112Smax.romanov@nginx.com 
882112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
883112Smax.romanov@nginx.com                 *u++ = ch;
884112Smax.romanov@nginx.com                 continue;
885112Smax.romanov@nginx.com             }
886112Smax.romanov@nginx.com 
887112Smax.romanov@nginx.com             switch (ch) {
888112Smax.romanov@nginx.com             case '/':
889112Smax.romanov@nginx.com                 rp->exten_start = NULL;
890112Smax.romanov@nginx.com                 state = sw_slash;
891112Smax.romanov@nginx.com                 *u++ = ch;
892112Smax.romanov@nginx.com                 continue;
893112Smax.romanov@nginx.com             case '%':
894112Smax.romanov@nginx.com                 saved_state = state;
895112Smax.romanov@nginx.com                 state = sw_quoted;
896112Smax.romanov@nginx.com                 continue;
897112Smax.romanov@nginx.com             case '?':
898112Smax.romanov@nginx.com                 rp->args_start = p;
899112Smax.romanov@nginx.com                 goto args;
900112Smax.romanov@nginx.com             case '#':
901112Smax.romanov@nginx.com                 goto done;
902112Smax.romanov@nginx.com             case '.':
903112Smax.romanov@nginx.com                 rp->exten_start = u + 1;
904112Smax.romanov@nginx.com                 *u++ = ch;
905112Smax.romanov@nginx.com                 continue;
906112Smax.romanov@nginx.com             case '+':
907112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
908112Smax.romanov@nginx.com                 /* Fall through. */
909112Smax.romanov@nginx.com             default:
910112Smax.romanov@nginx.com                 *u++ = ch;
911112Smax.romanov@nginx.com                 continue;
912112Smax.romanov@nginx.com             }
913112Smax.romanov@nginx.com 
914112Smax.romanov@nginx.com             break;
915112Smax.romanov@nginx.com 
916112Smax.romanov@nginx.com         case sw_slash:
917112Smax.romanov@nginx.com 
918112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
919112Smax.romanov@nginx.com                 state = sw_normal;
920112Smax.romanov@nginx.com                 *u++ = ch;
921112Smax.romanov@nginx.com                 continue;
922112Smax.romanov@nginx.com             }
923112Smax.romanov@nginx.com 
924112Smax.romanov@nginx.com             switch (ch) {
925112Smax.romanov@nginx.com             case '/':
926112Smax.romanov@nginx.com                 continue;
927112Smax.romanov@nginx.com             case '.':
928112Smax.romanov@nginx.com                 state = sw_dot;
929112Smax.romanov@nginx.com                 *u++ = ch;
930112Smax.romanov@nginx.com                 continue;
931112Smax.romanov@nginx.com             case '%':
932112Smax.romanov@nginx.com                 saved_state = state;
933112Smax.romanov@nginx.com                 state = sw_quoted;
934112Smax.romanov@nginx.com                 continue;
935112Smax.romanov@nginx.com             case '?':
936112Smax.romanov@nginx.com                 rp->args_start = p;
937112Smax.romanov@nginx.com                 goto args;
938112Smax.romanov@nginx.com             case '#':
939112Smax.romanov@nginx.com                 goto done;
940112Smax.romanov@nginx.com             case '+':
941112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
942112Smax.romanov@nginx.com                 /* Fall through. */
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 '?':
973112Smax.romanov@nginx.com                 rp->args_start = p;
974112Smax.romanov@nginx.com                 goto args;
975112Smax.romanov@nginx.com             case '#':
976112Smax.romanov@nginx.com                 goto done;
977112Smax.romanov@nginx.com             case '+':
978112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
979112Smax.romanov@nginx.com                 /* Fall through. */
980112Smax.romanov@nginx.com             default:
981112Smax.romanov@nginx.com                 state = sw_normal;
982112Smax.romanov@nginx.com                 *u++ = ch;
983112Smax.romanov@nginx.com                 continue;
984112Smax.romanov@nginx.com             }
985112Smax.romanov@nginx.com 
986112Smax.romanov@nginx.com             break;
987112Smax.romanov@nginx.com 
988112Smax.romanov@nginx.com         case sw_dot_dot:
989112Smax.romanov@nginx.com 
990112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
991112Smax.romanov@nginx.com                 state = sw_normal;
992112Smax.romanov@nginx.com                 *u++ = ch;
993112Smax.romanov@nginx.com                 continue;
994112Smax.romanov@nginx.com             }
995112Smax.romanov@nginx.com 
996112Smax.romanov@nginx.com             switch (ch) {
997112Smax.romanov@nginx.com             case '/':
998112Smax.romanov@nginx.com                 state = sw_slash;
999112Smax.romanov@nginx.com                 u -= 5;
1000112Smax.romanov@nginx.com                 for ( ;; ) {
1001112Smax.romanov@nginx.com                     if (u < rp->path.start) {
1002480Svbart@nginx.com                         return NXT_HTTP_PARSE_INVALID;
1003112Smax.romanov@nginx.com                     }
1004112Smax.romanov@nginx.com                     if (*u == '/') {
1005112Smax.romanov@nginx.com                         u++;
1006112Smax.romanov@nginx.com                         break;
1007112Smax.romanov@nginx.com                     }
1008112Smax.romanov@nginx.com                     u--;
1009112Smax.romanov@nginx.com                 }
1010112Smax.romanov@nginx.com                 break;
1011112Smax.romanov@nginx.com 
1012112Smax.romanov@nginx.com             case '%':
1013112Smax.romanov@nginx.com                 saved_state = state;
1014112Smax.romanov@nginx.com                 state = sw_quoted;
1015112Smax.romanov@nginx.com                 continue;
1016112Smax.romanov@nginx.com             case '?':
1017112Smax.romanov@nginx.com                 rp->args_start = p;
1018112Smax.romanov@nginx.com                 goto args;
1019112Smax.romanov@nginx.com             case '#':
1020112Smax.romanov@nginx.com                 goto done;
1021112Smax.romanov@nginx.com             case '+':
1022112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1023112Smax.romanov@nginx.com                 /* Fall through. */
1024112Smax.romanov@nginx.com             default:
1025112Smax.romanov@nginx.com                 state = sw_normal;
1026112Smax.romanov@nginx.com                 *u++ = ch;
1027112Smax.romanov@nginx.com                 continue;
1028112Smax.romanov@nginx.com             }
1029112Smax.romanov@nginx.com 
1030112Smax.romanov@nginx.com             break;
1031112Smax.romanov@nginx.com 
1032112Smax.romanov@nginx.com         case sw_quoted:
1033112Smax.romanov@nginx.com             rp->quoted_target = 1;
1034112Smax.romanov@nginx.com 
1035112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1036112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
1037112Smax.romanov@nginx.com                 state = sw_quoted_second;
1038112Smax.romanov@nginx.com                 continue;
1039112Smax.romanov@nginx.com             }
1040112Smax.romanov@nginx.com 
1041112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1042112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1043112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1044112Smax.romanov@nginx.com                 state = sw_quoted_second;
1045112Smax.romanov@nginx.com                 continue;
1046112Smax.romanov@nginx.com             }
1047112Smax.romanov@nginx.com 
1048480Svbart@nginx.com             return NXT_HTTP_PARSE_INVALID;
1049112Smax.romanov@nginx.com 
1050112Smax.romanov@nginx.com         case sw_quoted_second:
1051112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1052112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1053112Smax.romanov@nginx.com 
1054112Smax.romanov@nginx.com                 if (ch == '%' || ch == '#') {
1055112Smax.romanov@nginx.com                     state = sw_normal;
1056112Smax.romanov@nginx.com                     *u++ = ch;
1057112Smax.romanov@nginx.com                     continue;
1058112Smax.romanov@nginx.com 
1059112Smax.romanov@nginx.com                 } else if (ch == '\0') {
1060480Svbart@nginx.com                     return NXT_HTTP_PARSE_INVALID;
1061112Smax.romanov@nginx.com                 }
1062112Smax.romanov@nginx.com 
1063112Smax.romanov@nginx.com                 state = saved_state;
1064112Smax.romanov@nginx.com                 goto again;
1065112Smax.romanov@nginx.com             }
1066112Smax.romanov@nginx.com 
1067112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1068112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1069112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1070112Smax.romanov@nginx.com 
1071112Smax.romanov@nginx.com                 if (ch == '?') {
1072112Smax.romanov@nginx.com                     state = sw_normal;
1073112Smax.romanov@nginx.com                     *u++ = ch;
1074112Smax.romanov@nginx.com                     continue;
1075112Smax.romanov@nginx.com 
1076112Smax.romanov@nginx.com                 } else if (ch == '+') {
1077112Smax.romanov@nginx.com                     rp->plus_in_target = 1;
1078