xref: /unit/src/nxt_http_parse.c (revision 417)
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 
22*417Svbart@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
23*417Svbart@nginx.com 
24*417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
25*417Svbart@nginx.com static void *nxt_http_field_hash_alloc(void *pool, size_t size);
26*417Svbart@nginx.com static void nxt_http_field_hash_free(void *pool, void *p);
27*417Svbart@nginx.com 
28*417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq,
29*417Svbart@nginx.com     void *data);
3016Svbart@nginx.com 
31*417Svbart@nginx.com 
32*417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_NAME         0xff
33*417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_VALUE        NXT_INT32_T_MAX
34*417Svbart@nginx.com 
35*417Svbart@nginx.com #define NXT_HTTP_FIELD_LVLHSH_SHIFT     5
36*417Svbart@nginx.com 
37*417Svbart@nginx.com #define NXT_HTTP_FIELD_HASH_INIT        159406
38*417Svbart@nginx.com #define nxt_http_field_hash_char(h, c)  (((h) << 4) + (h) + (c))
39*417Svbart@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
117*417Svbart@nginx.com nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp)
118*417Svbart@nginx.com {
119*417Svbart@nginx.com     rp->mem_pool = mp;
120*417Svbart@nginx.com 
121*417Svbart@nginx.com     rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t));
122*417Svbart@nginx.com     if (nxt_slow_path(rp->fields == NULL)){
123*417Svbart@nginx.com         return NXT_ERROR;
124*417Svbart@nginx.com     }
125*417Svbart@nginx.com 
126*417Svbart@nginx.com     rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
127*417Svbart@nginx.com 
128*417Svbart@nginx.com     return NXT_OK;
129*417Svbart@nginx.com }
130*417Svbart@nginx.com 
131*417Svbart@nginx.com 
132*417Svbart@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 
14916Svbart@nginx.com static nxt_int_t
15016Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
15116Svbart@nginx.com     u_char *end)
15216Svbart@nginx.com {
15316Svbart@nginx.com     u_char                   *p, ch, *after_slash;
15416Svbart@nginx.com     nxt_int_t                rc;
15516Svbart@nginx.com     nxt_http_ver_t           version;
15616Svbart@nginx.com     nxt_http_target_traps_e  trap;
15716Svbart@nginx.com 
15816Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
15916Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
16016Svbart@nginx.com 
16116Svbart@nginx.com     p = *pos;
16216Svbart@nginx.com 
16316Svbart@nginx.com     rp->method.start = p;
16416Svbart@nginx.com 
165409Svbart@nginx.com     for ( ;; ) {
166409Svbart@nginx.com 
167409Svbart@nginx.com         while (nxt_fast_path(end - p >= 8)) {
16816Svbart@nginx.com 
169409Svbart@nginx.com #define nxt_method_test_char(ch)                                              \
170409Svbart@nginx.com                                                                               \
171409Svbart@nginx.com             if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
172409Svbart@nginx.com                 p = &(ch);                                                    \
173409Svbart@nginx.com                 goto method_unusual_char;                                     \
17416Svbart@nginx.com             }
17516Svbart@nginx.com 
176409Svbart@nginx.com /* enddef */
177409Svbart@nginx.com 
178409Svbart@nginx.com             nxt_method_test_char(p[0]);
179409Svbart@nginx.com             nxt_method_test_char(p[1]);
180409Svbart@nginx.com             nxt_method_test_char(p[2]);
181409Svbart@nginx.com             nxt_method_test_char(p[3]);
18216Svbart@nginx.com 
183409Svbart@nginx.com             nxt_method_test_char(p[4]);
184409Svbart@nginx.com             nxt_method_test_char(p[5]);
185409Svbart@nginx.com             nxt_method_test_char(p[6]);
186409Svbart@nginx.com             nxt_method_test_char(p[7]);
18716Svbart@nginx.com 
188409Svbart@nginx.com             p += 8;
189409Svbart@nginx.com         }
19016Svbart@nginx.com 
191410Svbart@nginx.com         while (p != end) {
192410Svbart@nginx.com             nxt_method_test_char(*p); p++;
193410Svbart@nginx.com         }
194410Svbart@nginx.com 
195409Svbart@nginx.com         return NXT_AGAIN;
196409Svbart@nginx.com 
197409Svbart@nginx.com     method_unusual_char:
198409Svbart@nginx.com 
199409Svbart@nginx.com         ch = *p;
20016Svbart@nginx.com 
20116Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
20216Svbart@nginx.com             rp->method.length = p - rp->method.start;
20316Svbart@nginx.com             break;
20416Svbart@nginx.com         }
20516Svbart@nginx.com 
20616Svbart@nginx.com         if (ch == '_' || ch == '-') {
207409Svbart@nginx.com             p++;
20816Svbart@nginx.com             continue;
20916Svbart@nginx.com         }
21016Svbart@nginx.com 
21116Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
21216Svbart@nginx.com             rp->method.start++;
213409Svbart@nginx.com             p++;
21416Svbart@nginx.com             continue;
21516Svbart@nginx.com         }
21616Svbart@nginx.com 
21716Svbart@nginx.com         return NXT_ERROR;
21816Svbart@nginx.com     }
21916Svbart@nginx.com 
22016Svbart@nginx.com     p++;
22116Svbart@nginx.com 
22216Svbart@nginx.com     if (nxt_slow_path(p == end)) {
22316Svbart@nginx.com         return NXT_AGAIN;
22416Svbart@nginx.com     }
22516Svbart@nginx.com 
22616Svbart@nginx.com     /* target */
22716Svbart@nginx.com 
22816Svbart@nginx.com     ch = *p;
22916Svbart@nginx.com 
23016Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
23116Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
23216Svbart@nginx.com 
23316Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
23416Svbart@nginx.com             return rc;
23516Svbart@nginx.com         }
23616Svbart@nginx.com     }
23716Svbart@nginx.com 
23816Svbart@nginx.com     rp->target_start = p;
23916Svbart@nginx.com 
24016Svbart@nginx.com     after_slash = p + 1;
24116Svbart@nginx.com 
24216Svbart@nginx.com     for ( ;; ) {
24316Svbart@nginx.com         p++;
24416Svbart@nginx.com 
24516Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
24616Svbart@nginx.com 
24716Svbart@nginx.com         switch (trap) {
24816Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
24916Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
25016Svbart@nginx.com                 rp->complex_target = 1;
25116Svbart@nginx.com                 goto rest_of_target;
25216Svbart@nginx.com             }
25316Svbart@nginx.com 
25416Svbart@nginx.com             after_slash = p + 1;
25516Svbart@nginx.com 
25616Svbart@nginx.com             rp->exten_start = NULL;
25716Svbart@nginx.com             continue;
25816Svbart@nginx.com 
25916Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
26016Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
26116Svbart@nginx.com                 rp->complex_target = 1;
26216Svbart@nginx.com                 goto rest_of_target;
26316Svbart@nginx.com             }
26416Svbart@nginx.com 
26516Svbart@nginx.com             rp->exten_start = p + 1;
26616Svbart@nginx.com             continue;
26716Svbart@nginx.com 
26816Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
26916Svbart@nginx.com             rp->args_start = p + 1;
27016Svbart@nginx.com             goto rest_of_target;
27116Svbart@nginx.com 
27216Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
27316Svbart@nginx.com             rp->target_end = p;
27416Svbart@nginx.com             goto space_after_target;
27516Svbart@nginx.com 
27616Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
27716Svbart@nginx.com             rp->quoted_target = 1;
27816Svbart@nginx.com             goto rest_of_target;
27916Svbart@nginx.com 
28016Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
28116Svbart@nginx.com             rp->plus_in_target = 1;
28216Svbart@nginx.com             continue;
28316Svbart@nginx.com 
28416Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
28516Svbart@nginx.com             rp->complex_target = 1;
28616Svbart@nginx.com             goto rest_of_target;
28716Svbart@nginx.com 
28816Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
28916Svbart@nginx.com             return NXT_AGAIN;
29016Svbart@nginx.com 
29116Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
29216Svbart@nginx.com             return NXT_ERROR;
29316Svbart@nginx.com         }
29416Svbart@nginx.com 
29516Svbart@nginx.com         nxt_unreachable();
29616Svbart@nginx.com     }
29716Svbart@nginx.com 
29816Svbart@nginx.com rest_of_target:
29916Svbart@nginx.com 
30016Svbart@nginx.com     for ( ;; ) {
30116Svbart@nginx.com         p++;
30216Svbart@nginx.com 
30319Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
30416Svbart@nginx.com 
30516Svbart@nginx.com         switch (trap) {
30616Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
30716Svbart@nginx.com             rp->target_end = p;
30816Svbart@nginx.com             goto space_after_target;
30916Svbart@nginx.com 
31016Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
31116Svbart@nginx.com             rp->complex_target = 1;
31216Svbart@nginx.com             continue;
31316Svbart@nginx.com 
31416Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
31516Svbart@nginx.com             return NXT_AGAIN;
31616Svbart@nginx.com 
31716Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
31816Svbart@nginx.com             return NXT_ERROR;
31916Svbart@nginx.com 
32016Svbart@nginx.com         default:
32116Svbart@nginx.com             continue;
32216Svbart@nginx.com         }
32316Svbart@nginx.com 
32416Svbart@nginx.com         nxt_unreachable();
32516Svbart@nginx.com     }
32616Svbart@nginx.com 
32716Svbart@nginx.com space_after_target:
32816Svbart@nginx.com 
32916Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
330410Svbart@nginx.com 
331410Svbart@nginx.com         do {
332410Svbart@nginx.com             p++;
333410Svbart@nginx.com 
334410Svbart@nginx.com             if (p == end) {
335410Svbart@nginx.com                 return NXT_AGAIN;
336410Svbart@nginx.com             }
337410Svbart@nginx.com 
338410Svbart@nginx.com         } while (*p == ' ');
339410Svbart@nginx.com 
340410Svbart@nginx.com         if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
341410Svbart@nginx.com 
342410Svbart@nginx.com             switch (end - p) {
343410Svbart@nginx.com             case 8:
344410Svbart@nginx.com                 if (p[7] < '0' || p[7] > '9') {
345410Svbart@nginx.com                     break;
346410Svbart@nginx.com                 }
347410Svbart@nginx.com                 /* Fall through. */
348410Svbart@nginx.com             case 7:
349410Svbart@nginx.com                 if (p[6] != '.') {
350410Svbart@nginx.com                     break;
351410Svbart@nginx.com                 }
352410Svbart@nginx.com                 /* Fall through. */
353410Svbart@nginx.com             case 6:
354410Svbart@nginx.com                 if (p[5] < '0' || p[5] > '9') {
355410Svbart@nginx.com                     break;
356410Svbart@nginx.com                 }
357410Svbart@nginx.com                 /* Fall through. */
358410Svbart@nginx.com             default:
359410Svbart@nginx.com                 return NXT_AGAIN;
360410Svbart@nginx.com             }
361410Svbart@nginx.com         }
362410Svbart@nginx.com 
363410Svbart@nginx.com         rp->space_in_target = 1;
364410Svbart@nginx.com         goto rest_of_target;
36516Svbart@nginx.com     }
36616Svbart@nginx.com 
36716Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
36816Svbart@nginx.com 
36916Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
37016Svbart@nginx.com 
37116Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
37216Svbart@nginx.com                        || version.ui64 == http10.ui64
37316Svbart@nginx.com                        || (p[1] == 'H'
37416Svbart@nginx.com                            && p[2] == 'T'
37516Svbart@nginx.com                            && p[3] == 'T'
37616Svbart@nginx.com                            && p[4] == 'P'
37716Svbart@nginx.com                            && p[5] == '/'
37816Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
37916Svbart@nginx.com                            && p[7] == '.'
38016Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
38116Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
38216Svbart@nginx.com     {
38316Svbart@nginx.com         rp->version.ui64 = version.ui64;
38416Svbart@nginx.com 
38516Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
38616Svbart@nginx.com             p += 10;
38716Svbart@nginx.com 
38816Svbart@nginx.com             if (nxt_slow_path(p == end)) {
38916Svbart@nginx.com                 return NXT_AGAIN;
39016Svbart@nginx.com             }
39116Svbart@nginx.com 
39216Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
39316Svbart@nginx.com                 return NXT_ERROR;
39416Svbart@nginx.com             }
39516Svbart@nginx.com 
39616Svbart@nginx.com             *pos = p + 1;
397112Smax.romanov@nginx.com 
398112Smax.romanov@nginx.com         } else {
399112Smax.romanov@nginx.com             *pos = p + 10;
400112Smax.romanov@nginx.com         }
401112Smax.romanov@nginx.com 
402112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
403112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
404112Smax.romanov@nginx.com 
405112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
406112Smax.romanov@nginx.com                 return rc;
407112Smax.romanov@nginx.com             }
408112Smax.romanov@nginx.com 
40916Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
41016Svbart@nginx.com         }
41116Svbart@nginx.com 
412112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
413112Smax.romanov@nginx.com 
414112Smax.romanov@nginx.com         if (rp->args_start != NULL) {
415112Smax.romanov@nginx.com             rp->path.length = rp->args_start - rp->target_start - 1;
416112Smax.romanov@nginx.com 
417112Smax.romanov@nginx.com             rp->args.start = rp->args_start;
418112Smax.romanov@nginx.com             rp->args.length = rp->target_end - rp->args_start;
419112Smax.romanov@nginx.com 
420112Smax.romanov@nginx.com         } else {
421112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
422112Smax.romanov@nginx.com         }
423112Smax.romanov@nginx.com 
424112Smax.romanov@nginx.com         if (rp->exten_start) {
425112Smax.romanov@nginx.com             rp->exten.length = rp->path.start + rp->path.length -
426112Smax.romanov@nginx.com                                rp->exten_start;
427112Smax.romanov@nginx.com             rp->exten.start = rp->exten_start;
428112Smax.romanov@nginx.com         }
429112Smax.romanov@nginx.com 
43016Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
43116Svbart@nginx.com     }
43216Svbart@nginx.com 
43316Svbart@nginx.com     if (p[1] == ' ') {
43416Svbart@nginx.com         /* surplus space after tartet */
43516Svbart@nginx.com         p++;
43616Svbart@nginx.com         goto space_after_target;
43716Svbart@nginx.com     }
43816Svbart@nginx.com 
43916Svbart@nginx.com     rp->space_in_target = 1;
44016Svbart@nginx.com     goto rest_of_target;
44116Svbart@nginx.com }
44216Svbart@nginx.com 
44316Svbart@nginx.com 
44416Svbart@nginx.com static nxt_int_t
44516Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
44616Svbart@nginx.com     u_char *end)
44716Svbart@nginx.com {
44816Svbart@nginx.com     u_char  *p, ch;
44916Svbart@nginx.com 
45016Svbart@nginx.com     p = *pos;
45116Svbart@nginx.com 
45216Svbart@nginx.com     ch = *p;
45316Svbart@nginx.com 
45416Svbart@nginx.com     if (ch == ' ') {
45516Svbart@nginx.com         /* skip surplus spaces before target */
45616Svbart@nginx.com 
45716Svbart@nginx.com         do {
45816Svbart@nginx.com             p++;
45916Svbart@nginx.com 
46016Svbart@nginx.com             if (nxt_slow_path(p == end)) {
46116Svbart@nginx.com                 return NXT_AGAIN;
46216Svbart@nginx.com             }
46316Svbart@nginx.com 
46416Svbart@nginx.com             ch = *p;
46516Svbart@nginx.com 
46616Svbart@nginx.com         } while (ch == ' ');
46716Svbart@nginx.com 
46816Svbart@nginx.com         if (ch == '/') {
46916Svbart@nginx.com             *pos = p;
47016Svbart@nginx.com             return NXT_OK;
47116Svbart@nginx.com         }
47216Svbart@nginx.com     }
47316Svbart@nginx.com 
47416Svbart@nginx.com     /* absolute path or '*' */
47516Svbart@nginx.com 
47616Svbart@nginx.com     /* TODO */
47716Svbart@nginx.com 
47816Svbart@nginx.com     return NXT_ERROR;
47916Svbart@nginx.com }
48016Svbart@nginx.com 
48116Svbart@nginx.com 
48216Svbart@nginx.com static nxt_int_t
48316Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
48416Svbart@nginx.com     u_char *end)
48516Svbart@nginx.com {
486*417Svbart@nginx.com     u_char    *p, c;
487*417Svbart@nginx.com     size_t    len;
488*417Svbart@nginx.com     uint32_t  hash;
48916Svbart@nginx.com 
49016Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
49116Svbart@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"
49216Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
49316Svbart@nginx.com 
49416Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
49516Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
49616Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
49716Svbart@nginx.com 
49816Svbart@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"
49916Svbart@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"
50016Svbart@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"
50116Svbart@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";
50216Svbart@nginx.com 
503*417Svbart@nginx.com     p = *pos + rp->field_name.length;
504*417Svbart@nginx.com     hash = rp->field_hash;
50516Svbart@nginx.com 
506*417Svbart@nginx.com     while (nxt_fast_path(end - p >= 8)) {
507409Svbart@nginx.com 
508*417Svbart@nginx.com #define nxt_field_name_test_char(ch)                                          \
509409Svbart@nginx.com                                                                               \
51019Svbart@nginx.com         c = normal[ch];                                                       \
51119Svbart@nginx.com                                                                               \
51219Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
513*417Svbart@nginx.com             p = &(ch);                                                        \
51419Svbart@nginx.com             goto name_end;                                                    \
51519Svbart@nginx.com         }                                                                     \
51619Svbart@nginx.com                                                                               \
517*417Svbart@nginx.com         hash = nxt_http_field_hash_char(hash, c);
518409Svbart@nginx.com 
519409Svbart@nginx.com /* enddef */
52016Svbart@nginx.com 
521*417Svbart@nginx.com         nxt_field_name_test_char(p[0]);
522*417Svbart@nginx.com         nxt_field_name_test_char(p[1]);
523*417Svbart@nginx.com         nxt_field_name_test_char(p[2]);
524*417Svbart@nginx.com         nxt_field_name_test_char(p[3]);
52516Svbart@nginx.com 
526*417Svbart@nginx.com         nxt_field_name_test_char(p[4]);
527*417Svbart@nginx.com         nxt_field_name_test_char(p[5]);
528*417Svbart@nginx.com         nxt_field_name_test_char(p[6]);
529*417Svbart@nginx.com         nxt_field_name_test_char(p[7]);
530*417Svbart@nginx.com 
531*417Svbart@nginx.com         p += 8;
53219Svbart@nginx.com     }
53316Svbart@nginx.com 
534*417Svbart@nginx.com     while (nxt_fast_path(p != end)) {
535*417Svbart@nginx.com         nxt_field_name_test_char(*p); p++;
53619Svbart@nginx.com     }
53716Svbart@nginx.com 
538*417Svbart@nginx.com     len = p - *pos;
539*417Svbart@nginx.com 
540*417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
541*417Svbart@nginx.com         return NXT_ERROR;
542*417Svbart@nginx.com     }
543*417Svbart@nginx.com 
544*417Svbart@nginx.com     rp->field_hash = hash;
545*417Svbart@nginx.com     rp->field_name.length = len;
546*417Svbart@nginx.com 
54716Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
54816Svbart@nginx.com 
54916Svbart@nginx.com     return NXT_AGAIN;
55019Svbart@nginx.com 
55119Svbart@nginx.com name_end:
55219Svbart@nginx.com 
553*417Svbart@nginx.com     if (nxt_fast_path(*p == ':')) {
554*417Svbart@nginx.com         if (nxt_slow_path(p == *pos)) {
55519Svbart@nginx.com             return NXT_ERROR;
55619Svbart@nginx.com         }
55719Svbart@nginx.com 
558*417Svbart@nginx.com         len = p - *pos;
559*417Svbart@nginx.com 
560*417Svbart@nginx.com         if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) {
561*417Svbart@nginx.com             return NXT_ERROR;
562*417Svbart@nginx.com         }
56319Svbart@nginx.com 
564*417Svbart@nginx.com         rp->field_hash = hash;
565*417Svbart@nginx.com 
566*417Svbart@nginx.com         rp->field_name.length = len;
567*417Svbart@nginx.com         rp->field_name.start = *pos;
568*417Svbart@nginx.com 
569*417Svbart@nginx.com         *pos = p + 1;
57019Svbart@nginx.com 
57119Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
57219Svbart@nginx.com     }
57319Svbart@nginx.com 
574*417Svbart@nginx.com     if (nxt_slow_path(p != *pos)) {
57559Svbart@nginx.com         return NXT_ERROR;
57659Svbart@nginx.com     }
57719Svbart@nginx.com 
57819Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
57916Svbart@nginx.com }
58016Svbart@nginx.com 
58116Svbart@nginx.com 
58216Svbart@nginx.com static nxt_int_t
58316Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
58416Svbart@nginx.com     u_char *end)
58516Svbart@nginx.com {
58616Svbart@nginx.com     u_char  *p, ch;
587*417Svbart@nginx.com     size_t  len;
58816Svbart@nginx.com 
58916Svbart@nginx.com     p = *pos;
59016Svbart@nginx.com 
59116Svbart@nginx.com     for ( ;; ) {
59216Svbart@nginx.com         if (nxt_slow_path(p == end)) {
59316Svbart@nginx.com             *pos = p;
59416Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
59516Svbart@nginx.com             return NXT_AGAIN;
59616Svbart@nginx.com         }
59716Svbart@nginx.com 
59816Svbart@nginx.com         if (*p != ' ') {
59916Svbart@nginx.com             break;
60016Svbart@nginx.com         }
60116Svbart@nginx.com 
60216Svbart@nginx.com         p++;
60316Svbart@nginx.com     }
60416Svbart@nginx.com 
60516Svbart@nginx.com     *pos = p;
60616Svbart@nginx.com 
60767Svbart@nginx.com     p += rp->field_value.length;
60816Svbart@nginx.com 
60916Svbart@nginx.com     for ( ;; ) {
61016Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
61116Svbart@nginx.com 
61216Svbart@nginx.com         if (nxt_slow_path(p == end)) {
613*417Svbart@nginx.com             len = p - *pos;
614*417Svbart@nginx.com 
615*417Svbart@nginx.com             if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
616*417Svbart@nginx.com                 return NXT_ERROR;
617*417Svbart@nginx.com             }
618*417Svbart@nginx.com 
619*417Svbart@nginx.com             rp->field_value.length = len;
62016Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
62116Svbart@nginx.com             return NXT_AGAIN;
62216Svbart@nginx.com         }
62316Svbart@nginx.com 
62416Svbart@nginx.com         ch = *p;
62516Svbart@nginx.com 
62616Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
62716Svbart@nginx.com             break;
62816Svbart@nginx.com         }
62916Svbart@nginx.com 
63016Svbart@nginx.com         if (ch == '\0') {
63116Svbart@nginx.com             return NXT_ERROR;
63216Svbart@nginx.com         }
63316Svbart@nginx.com     }
63416Svbart@nginx.com 
63516Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
63616Svbart@nginx.com         while (p[-1] == ' ') {
63716Svbart@nginx.com             p--;
63816Svbart@nginx.com         }
63916Svbart@nginx.com     }
64016Svbart@nginx.com 
641*417Svbart@nginx.com     len = p - *pos;
642*417Svbart@nginx.com 
643*417Svbart@nginx.com     if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) {
644*417Svbart@nginx.com         return NXT_ERROR;
645*417Svbart@nginx.com     }
646*417Svbart@nginx.com 
647*417Svbart@nginx.com     rp->field_value.length = len;
64867Svbart@nginx.com     rp->field_value.start = *pos;
64916Svbart@nginx.com 
65016Svbart@nginx.com     *pos = p;
65116Svbart@nginx.com 
65216Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
65316Svbart@nginx.com }
65416Svbart@nginx.com 
65516Svbart@nginx.com 
65616Svbart@nginx.com static u_char *
65716Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
65816Svbart@nginx.com {
659409Svbart@nginx.com     while (nxt_fast_path(end - p >= 16)) {
660409Svbart@nginx.com 
661409Svbart@nginx.com #define nxt_field_end_test_char(ch)                                           \
662409Svbart@nginx.com                                                                               \
663409Svbart@nginx.com         if (nxt_slow_path((ch) < 0x10)) {                                     \
664409Svbart@nginx.com             return &(ch);                                                     \
665409Svbart@nginx.com         }
666409Svbart@nginx.com 
667409Svbart@nginx.com /* enddef */
668409Svbart@nginx.com 
669409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
670409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
671409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
672409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
67316Svbart@nginx.com 
674409Svbart@nginx.com         nxt_field_end_test_char(p[4]);
675409Svbart@nginx.com         nxt_field_end_test_char(p[5]);
676409Svbart@nginx.com         nxt_field_end_test_char(p[6]);
677409Svbart@nginx.com         nxt_field_end_test_char(p[7]);
678409Svbart@nginx.com 
679409Svbart@nginx.com         nxt_field_end_test_char(p[8]);
680409Svbart@nginx.com         nxt_field_end_test_char(p[9]);
681409Svbart@nginx.com         nxt_field_end_test_char(p[10]);
682409Svbart@nginx.com         nxt_field_end_test_char(p[11]);
683409Svbart@nginx.com 
684409Svbart@nginx.com         nxt_field_end_test_char(p[12]);
685409Svbart@nginx.com         nxt_field_end_test_char(p[13]);
686409Svbart@nginx.com         nxt_field_end_test_char(p[14]);
687409Svbart@nginx.com         nxt_field_end_test_char(p[15]);
688409Svbart@nginx.com 
689409Svbart@nginx.com         p += 16;
69016Svbart@nginx.com     }
69116Svbart@nginx.com 
692409Svbart@nginx.com     while (nxt_fast_path(end - p >= 4)) {
69319Svbart@nginx.com 
694409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
695409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
696409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
697409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
69816Svbart@nginx.com 
699409Svbart@nginx.com         p += 4;
70019Svbart@nginx.com     }
70119Svbart@nginx.com 
70219Svbart@nginx.com     switch (end - p) {
70316Svbart@nginx.com     case 3:
704409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
70539Svbart@nginx.com         /* Fall through. */
70616Svbart@nginx.com     case 2:
707409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
70839Svbart@nginx.com         /* Fall through. */
70916Svbart@nginx.com     case 1:
710409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
71139Svbart@nginx.com         /* Fall through. */
71216Svbart@nginx.com     case 0:
71316Svbart@nginx.com         break;
71416Svbart@nginx.com     default:
71516Svbart@nginx.com         nxt_unreachable();
71616Svbart@nginx.com     }
71716Svbart@nginx.com 
71816Svbart@nginx.com     return p;
71916Svbart@nginx.com }
72016Svbart@nginx.com 
72116Svbart@nginx.com 
72216Svbart@nginx.com static nxt_int_t
72316Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
72416Svbart@nginx.com     u_char *end)
72516Svbart@nginx.com {
72660Svbart@nginx.com     u_char            *p;
72760Svbart@nginx.com     nxt_http_field_t  *field;
72816Svbart@nginx.com 
72916Svbart@nginx.com     p = *pos;
73016Svbart@nginx.com 
73116Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
73216Svbart@nginx.com         p++;
73316Svbart@nginx.com 
73416Svbart@nginx.com         if (nxt_slow_path(p == end)) {
73516Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
73616Svbart@nginx.com             return NXT_AGAIN;
73716Svbart@nginx.com         }
73816Svbart@nginx.com     }
73916Svbart@nginx.com 
74016Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
74116Svbart@nginx.com         *pos = p + 1;
74216Svbart@nginx.com 
74367Svbart@nginx.com         if (rp->field_name.length != 0) {
74460Svbart@nginx.com             field = nxt_list_add(rp->fields);
74516Svbart@nginx.com 
74660Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
74760Svbart@nginx.com                 return NXT_ERROR;
74816Svbart@nginx.com             }
74916Svbart@nginx.com 
750*417Svbart@nginx.com             field->hash = nxt_http_field_hash_end(rp->field_hash);
751*417Svbart@nginx.com             field->skip = 0;
75260Svbart@nginx.com 
753*417Svbart@nginx.com             field->name_length = rp->field_name.length;
754*417Svbart@nginx.com             field->value_length = rp->field_value.length;
755*417Svbart@nginx.com             field->name = rp->field_name.start;
756*417Svbart@nginx.com             field->value = rp->field_value.start;
75767Svbart@nginx.com 
758*417Svbart@nginx.com             rp->field_hash = NXT_HTTP_FIELD_HASH_INIT;
75967Svbart@nginx.com 
76067Svbart@nginx.com             rp->field_name.length = 0;
76167Svbart@nginx.com             rp->field_value.length = 0;
76216Svbart@nginx.com 
76316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
76416Svbart@nginx.com             return NXT_OK;
76516Svbart@nginx.com         }
76616Svbart@nginx.com 
76716Svbart@nginx.com         return NXT_DONE;
76816Svbart@nginx.com     }
76916Svbart@nginx.com 
77016Svbart@nginx.com     return NXT_ERROR;
77116Svbart@nginx.com }
77216Svbart@nginx.com 
77316Svbart@nginx.com 
774112Smax.romanov@nginx.com #define                                                                       \
775112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
776112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
777112Smax.romanov@nginx.com 
778112Smax.romanov@nginx.com 
779112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
780112Smax.romanov@nginx.com 
781112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
782112Smax.romanov@nginx.com     0xfe, 0xdb, 0xff, 0xff,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
783112Smax.romanov@nginx.com 
784112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
785112Smax.romanov@nginx.com     0xd6, 0x37, 0xff, 0x7f,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
786112Smax.romanov@nginx.com 
787112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
788112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
789112Smax.romanov@nginx.com 
790112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
791112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
792112Smax.romanov@nginx.com 
793112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
794112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
795112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
796112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
797112Smax.romanov@nginx.com };
798112Smax.romanov@nginx.com 
799112Smax.romanov@nginx.com 
800112Smax.romanov@nginx.com static nxt_int_t
801112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
802112Smax.romanov@nginx.com {
803112Smax.romanov@nginx.com     u_char  *p, *u, c, ch, high;
804112Smax.romanov@nginx.com     enum {
805112Smax.romanov@nginx.com         sw_normal = 0,
806112Smax.romanov@nginx.com         sw_slash,
807112Smax.romanov@nginx.com         sw_dot,
808112Smax.romanov@nginx.com         sw_dot_dot,
809112Smax.romanov@nginx.com         sw_quoted,
810112Smax.romanov@nginx.com         sw_quoted_second,
811112Smax.romanov@nginx.com     } state, saved_state;
812112Smax.romanov@nginx.com 
813112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
814112Smax.romanov@nginx.com 
815112Smax.romanov@nginx.com     state = sw_normal;
816112Smax.romanov@nginx.com     saved_state = sw_normal;
817112Smax.romanov@nginx.com     p = rp->target_start;
818112Smax.romanov@nginx.com 
819112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
820112Smax.romanov@nginx.com 
821112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
822112Smax.romanov@nginx.com         return NXT_ERROR;
823112Smax.romanov@nginx.com     }
824112Smax.romanov@nginx.com 
825112Smax.romanov@nginx.com     rp->path.length = 0;
826112Smax.romanov@nginx.com     rp->path.start = u;
827112Smax.romanov@nginx.com 
828112Smax.romanov@nginx.com     high = '\0';
829112Smax.romanov@nginx.com     rp->exten_start = NULL;
830112Smax.romanov@nginx.com     rp->args_start = NULL;
831112Smax.romanov@nginx.com 
832112Smax.romanov@nginx.com     while (p < rp->target_end) {
833112Smax.romanov@nginx.com 
834112Smax.romanov@nginx.com         ch = *p++;
835112Smax.romanov@nginx.com 
836112Smax.romanov@nginx.com     again:
837112Smax.romanov@nginx.com 
838112Smax.romanov@nginx.com         switch (state) {
839112Smax.romanov@nginx.com 
840112Smax.romanov@nginx.com         case sw_normal:
841112Smax.romanov@nginx.com 
842112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
843112Smax.romanov@nginx.com                 *u++ = ch;
844112Smax.romanov@nginx.com                 continue;
845112Smax.romanov@nginx.com             }
846112Smax.romanov@nginx.com 
847112Smax.romanov@nginx.com             switch (ch) {
848112Smax.romanov@nginx.com             case '/':
849112Smax.romanov@nginx.com                 rp->exten_start = NULL;
850112Smax.romanov@nginx.com                 state = sw_slash;
851112Smax.romanov@nginx.com                 *u++ = ch;
852112Smax.romanov@nginx.com                 continue;
853112Smax.romanov@nginx.com             case '%':
854112Smax.romanov@nginx.com                 saved_state = state;
855112Smax.romanov@nginx.com                 state = sw_quoted;
856112Smax.romanov@nginx.com                 continue;
857112Smax.romanov@nginx.com             case '?':
858112Smax.romanov@nginx.com                 rp->args_start = p;
859112Smax.romanov@nginx.com                 goto args;
860112Smax.romanov@nginx.com             case '#':
861112Smax.romanov@nginx.com                 goto done;
862112Smax.romanov@nginx.com             case '.':
863112Smax.romanov@nginx.com                 rp->exten_start = u + 1;
864112Smax.romanov@nginx.com                 *u++ = ch;
865112Smax.romanov@nginx.com                 continue;
866112Smax.romanov@nginx.com             case '+':
867112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
868112Smax.romanov@nginx.com                 /* Fall through. */
869112Smax.romanov@nginx.com             default:
870112Smax.romanov@nginx.com                 *u++ = ch;
871112Smax.romanov@nginx.com                 continue;
872112Smax.romanov@nginx.com             }
873112Smax.romanov@nginx.com 
874112Smax.romanov@nginx.com             break;
875112Smax.romanov@nginx.com 
876112Smax.romanov@nginx.com         case sw_slash:
877112Smax.romanov@nginx.com 
878112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
879112Smax.romanov@nginx.com                 state = sw_normal;
880112Smax.romanov@nginx.com                 *u++ = ch;
881112Smax.romanov@nginx.com                 continue;
882112Smax.romanov@nginx.com             }
883112Smax.romanov@nginx.com 
884112Smax.romanov@nginx.com             switch (ch) {
885112Smax.romanov@nginx.com             case '/':
886112Smax.romanov@nginx.com                 continue;
887112Smax.romanov@nginx.com             case '.':
888112Smax.romanov@nginx.com                 state = sw_dot;
889112Smax.romanov@nginx.com                 *u++ = ch;
890112Smax.romanov@nginx.com                 continue;
891112Smax.romanov@nginx.com             case '%':
892112Smax.romanov@nginx.com                 saved_state = state;
893112Smax.romanov@nginx.com                 state = sw_quoted;
894112Smax.romanov@nginx.com                 continue;
895112Smax.romanov@nginx.com             case '?':
896112Smax.romanov@nginx.com                 rp->args_start = p;
897112Smax.romanov@nginx.com                 goto args;
898112Smax.romanov@nginx.com             case '#':
899112Smax.romanov@nginx.com                 goto done;
900112Smax.romanov@nginx.com             case '+':
901112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
902112Smax.romanov@nginx.com                 /* Fall through. */
903112Smax.romanov@nginx.com             default:
904112Smax.romanov@nginx.com                 state = sw_normal;
905112Smax.romanov@nginx.com                 *u++ = ch;
906112Smax.romanov@nginx.com                 continue;
907112Smax.romanov@nginx.com             }
908112Smax.romanov@nginx.com 
909112Smax.romanov@nginx.com             break;
910112Smax.romanov@nginx.com 
911112Smax.romanov@nginx.com         case sw_dot:
912112Smax.romanov@nginx.com 
913112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
914112Smax.romanov@nginx.com                 state = sw_normal;
915112Smax.romanov@nginx.com                 *u++ = ch;
916112Smax.romanov@nginx.com                 continue;
917112Smax.romanov@nginx.com             }
918112Smax.romanov@nginx.com 
919112Smax.romanov@nginx.com             switch (ch) {
920112Smax.romanov@nginx.com             case '/':
921112Smax.romanov@nginx.com                 state = sw_slash;
922112Smax.romanov@nginx.com                 u--;
923112Smax.romanov@nginx.com                 continue;
924112Smax.romanov@nginx.com             case '.':
925112Smax.romanov@nginx.com                 state = sw_dot_dot;
926112Smax.romanov@nginx.com                 *u++ = ch;
927112Smax.romanov@nginx.com                 continue;
928112Smax.romanov@nginx.com             case '%':
929112Smax.romanov@nginx.com                 saved_state = state;
930112Smax.romanov@nginx.com                 state = sw_quoted;
931112Smax.romanov@nginx.com                 continue;
932112Smax.romanov@nginx.com             case '?':
933112Smax.romanov@nginx.com                 rp->args_start = p;
934112Smax.romanov@nginx.com                 goto args;
935112Smax.romanov@nginx.com             case '#':
936112Smax.romanov@nginx.com                 goto done;
937112Smax.romanov@nginx.com             case '+':
938112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
939112Smax.romanov@nginx.com                 /* Fall through. */
940112Smax.romanov@nginx.com             default:
941112Smax.romanov@nginx.com                 state = sw_normal;
942112Smax.romanov@nginx.com                 *u++ = ch;
943112Smax.romanov@nginx.com                 continue;
944112Smax.romanov@nginx.com             }
945112Smax.romanov@nginx.com 
946112Smax.romanov@nginx.com             break;
947112Smax.romanov@nginx.com 
948112Smax.romanov@nginx.com         case sw_dot_dot:
949112Smax.romanov@nginx.com 
950112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
951112Smax.romanov@nginx.com                 state = sw_normal;
952112Smax.romanov@nginx.com                 *u++ = ch;
953112Smax.romanov@nginx.com                 continue;
954112Smax.romanov@nginx.com             }
955112Smax.romanov@nginx.com 
956112Smax.romanov@nginx.com             switch (ch) {
957112Smax.romanov@nginx.com             case '/':
958112Smax.romanov@nginx.com                 state = sw_slash;
959112Smax.romanov@nginx.com                 u -= 5;
960112Smax.romanov@nginx.com                 for ( ;; ) {
961112Smax.romanov@nginx.com                     if (u < rp->path.start) {
962112Smax.romanov@nginx.com                         return NXT_ERROR;
963112Smax.romanov@nginx.com                     }
964112Smax.romanov@nginx.com                     if (*u == '/') {
965112Smax.romanov@nginx.com                         u++;
966112Smax.romanov@nginx.com                         break;
967112Smax.romanov@nginx.com                     }
968112Smax.romanov@nginx.com                     u--;
969112Smax.romanov@nginx.com                 }
970112Smax.romanov@nginx.com                 break;
971112Smax.romanov@nginx.com 
972112Smax.romanov@nginx.com             case '%':
973112Smax.romanov@nginx.com                 saved_state = state;
974112Smax.romanov@nginx.com                 state = sw_quoted;
975112Smax.romanov@nginx.com                 continue;
976112Smax.romanov@nginx.com             case '?':
977112Smax.romanov@nginx.com                 rp->args_start = p;
978112Smax.romanov@nginx.com                 goto args;
979112Smax.romanov@nginx.com             case '#':
980112Smax.romanov@nginx.com                 goto done;
981112Smax.romanov@nginx.com             case '+':
982112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
983112Smax.romanov@nginx.com                 /* Fall through. */
984112Smax.romanov@nginx.com             default:
985112Smax.romanov@nginx.com                 state = sw_normal;
986112Smax.romanov@nginx.com                 *u++ = ch;
987112Smax.romanov@nginx.com                 continue;
988112Smax.romanov@nginx.com             }
989112Smax.romanov@nginx.com 
990112Smax.romanov@nginx.com             break;
991112Smax.romanov@nginx.com 
992112Smax.romanov@nginx.com         case sw_quoted:
993112Smax.romanov@nginx.com             rp->quoted_target = 1;
994112Smax.romanov@nginx.com 
995112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
996112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
997112Smax.romanov@nginx.com                 state = sw_quoted_second;
998112Smax.romanov@nginx.com                 continue;
999112Smax.romanov@nginx.com             }
1000112Smax.romanov@nginx.com 
1001112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1002112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1003112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1004112Smax.romanov@nginx.com                 state = sw_quoted_second;
1005112Smax.romanov@nginx.com                 continue;
1006112Smax.romanov@nginx.com             }
1007112Smax.romanov@nginx.com 
1008112Smax.romanov@nginx.com             return NXT_ERROR;
1009112Smax.romanov@nginx.com 
1010112Smax.romanov@nginx.com         case sw_quoted_second:
1011112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1012112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1013112Smax.romanov@nginx.com 
1014112Smax.romanov@nginx.com                 if (ch == '%' || ch == '#') {
1015112Smax.romanov@nginx.com                     state = sw_normal;
1016112Smax.romanov@nginx.com                     *u++ = ch;
1017112Smax.romanov@nginx.com                     continue;
1018112Smax.romanov@nginx.com 
1019112Smax.romanov@nginx.com                 } else if (ch == '\0') {
1020112Smax.romanov@nginx.com                     return NXT_ERROR;
1021112Smax.romanov@nginx.com                 }
1022112Smax.romanov@nginx.com 
1023112Smax.romanov@nginx.com                 state = saved_state;
1024112Smax.romanov@nginx.com                 goto again;
1025112Smax.romanov@nginx.com             }
1026112Smax.romanov@nginx.com 
1027112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1028112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1029112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1030112Smax.romanov@nginx.com 
1031112Smax.romanov@nginx.com                 if (ch == '?') {
1032112Smax.romanov@nginx.com                     state = sw_normal;
1033112Smax.romanov@nginx.com                     *u++ = ch;
1034112Smax.romanov@nginx.com                     continue;
1035112Smax.romanov@nginx.com 
1036112Smax.romanov@nginx.com                 } else if (ch == '+') {
1037112Smax.romanov@nginx.com                     rp->plus_in_target = 1;
1038112Smax.romanov@nginx.com                 }
1039112Smax.romanov@nginx.com 
1040112Smax.romanov@nginx.com                 state = saved_state;
1041112Smax.romanov@nginx.com                 goto again;
1042112Smax.romanov@nginx.com             }
1043112Smax.romanov@nginx.com 
1044112Smax.romanov@nginx.com             return NXT_ERROR;
1045112Smax.romanov@nginx.com         }
1046112Smax.romanov@nginx.com     }
1047112Smax.romanov@nginx.com 
1048112Smax.romanov@nginx.com     if (state >= sw_quoted) {
1049112Smax.romanov@nginx.com         return NXT_ERROR;
1050112Smax.romanov@nginx.com     }
1051112Smax.romanov@nginx.com 
1052112Smax.romanov@nginx.com args:
1053112Smax.romanov@nginx.com 
1054112Smax.romanov@nginx.com     for (/* void */; p < rp->target_end; p++) {
1055112Smax.romanov@nginx.com         if (*p == '#') {
1056112Smax.romanov@nginx.com             break;
1057112Smax.romanov@nginx.com         }
1058112Smax.romanov@nginx.com     }
1059112Smax.romanov@nginx.com 
1060112Smax.romanov@nginx.com     if (rp->args_start != NULL) {
1061112Smax.romanov@nginx.com         rp->args.length = p - rp->args_start;
1062112Smax.romanov@nginx.com         rp->args.start = rp->args_start;
1063112Smax.romanov@nginx.com     }
1064112Smax.romanov@nginx.com 
1065112Smax.romanov@nginx.com done:
1066112Smax.romanov@nginx.com 
1067112Smax.romanov@nginx.com     rp->path.length = u - rp->path.start;
1068112Smax.romanov@nginx.com 
1069112Smax.romanov@nginx.com     if (rp->exten_start) {
1070112Smax.romanov@nginx.com         rp->exten.length = u - rp->exten_start;
1071112Smax.romanov@nginx.com         rp->exten.start = rp->exten_start;
1072112Smax.romanov@nginx.com     }
1073112Smax.romanov@nginx.com 
1074112Smax.romanov@nginx.com     return NXT_OK;
1075112Smax.romanov@nginx.com }
1076*417Svbart@nginx.com 
1077*417Svbart@nginx.com 
1078*417Svbart@nginx.com static const nxt_lvlhsh_proto_t  nxt_http_fields_hash_proto  nxt_aligned(64) = {
1079*417Svbart@nginx.com     NXT_LVLHSH_BUCKET_SIZE(64),
1080*417Svbart@nginx.com     { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 },
1081*417Svbart@nginx.com     nxt_http_field_hash_test,
1082*417Svbart@nginx.com     nxt_http_field_hash_alloc,
1083*417Svbart@nginx.com     nxt_http_field_hash_free,
1084*417Svbart@nginx.com };
1085*417Svbart@nginx.com 
1086*417Svbart@nginx.com 
1087*417Svbart@nginx.com static nxt_int_t
1088*417Svbart@nginx.com nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
1089*417Svbart@nginx.com {
1090*417Svbart@nginx.com     nxt_http_field_proc_t  *field;
1091*417Svbart@nginx.com 
1092*417Svbart@nginx.com     field = data;
1093*417Svbart@nginx.com 
1094*417Svbart@nginx.com     if (nxt_strcasestr_eq(&lhq->key, &field->name)) {
1095*417Svbart@nginx.com         return NXT_OK;
1096*417Svbart@nginx.com     }
1097*417Svbart@nginx.com 
1098*417Svbart@nginx.com     return NXT_DECLINED;
1099*417Svbart@nginx.com }
1100*417Svbart@nginx.com 
1101*417Svbart@nginx.com 
1102*417Svbart@nginx.com static void *
1103*417Svbart@nginx.com nxt_http_field_hash_alloc(void *pool, size_t size)
1104*417Svbart@nginx.com {
1105*417Svbart@nginx.com     return nxt_mp_align(pool, size, size);
1106*417Svbart@nginx.com }
1107*417Svbart@nginx.com 
1108*417Svbart@nginx.com 
1109*417Svbart@nginx.com static void
1110*417Svbart@nginx.com nxt_http_field_hash_free(void *pool, void *p)
1111*417Svbart@nginx.com {
1112*417Svbart@nginx.com     nxt_mp_free(pool, p);
1113*417Svbart@nginx.com }
1114*417Svbart@nginx.com 
1115*417Svbart@nginx.com 
1116*417Svbart@nginx.com static nxt_int_t
1117*417Svbart@nginx.com nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data)
1118*417Svbart@nginx.com {
1119*417Svbart@nginx.com     return NXT_OK;
1120*417Svbart@nginx.com }
1121*417Svbart@nginx.com 
1122*417Svbart@nginx.com 
1123*417Svbart@nginx.com nxt_int_t
1124*417Svbart@nginx.com nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
1125*417Svbart@nginx.com     nxt_http_field_proc_t items[], nxt_uint_t count)
1126*417Svbart@nginx.com {
1127*417Svbart@nginx.com     u_char              ch;
1128*417Svbart@nginx.com     uint32_t            key;
1129*417Svbart@nginx.com     nxt_str_t           *name;
1130*417Svbart@nginx.com     nxt_int_t           ret;
1131*417Svbart@nginx.com     nxt_uint_t          i, j;
1132*417Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
1133*417Svbart@nginx.com 
1134*417Svbart@nginx.com     lhq.replace = 0;
1135*417Svbart@nginx.com     lhq.proto = &nxt_http_fields_hash_proto;
1136*417Svbart@nginx.com     lhq.pool = mp;
1137*417Svbart@nginx.com 
1138*417Svbart@nginx.com     for (i = 0; i < count; i++) {
1139*417Svbart@nginx.com         key = NXT_HTTP_FIELD_HASH_INIT;
1140*417Svbart@nginx.com         name = &items[i].name;
1141*417Svbart@nginx.com 
1142*417Svbart@nginx.com         for (j = 0; j < name->length; j++) {
1143*417Svbart@nginx.com             ch = nxt_lowcase(name->start[j]);
1144*417Svbart@nginx.com             key = nxt_http_field_hash_char(key, ch);
1145*417Svbart@nginx.com         }
1146*417Svbart@nginx.com 
1147*417Svbart@nginx.com         lhq.key_hash = nxt_http_field_hash_end(key) & 0xffff;
1148*417Svbart@nginx.com         lhq.key = *name;
1149*417Svbart@nginx.com         lhq.value = &items[i];
1150*417Svbart@nginx.com 
1151*417Svbart@nginx.com         ret = nxt_lvlhsh_insert(hash, &lhq);
1152*417Svbart@nginx.com 
1153*417Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
1154*417Svbart@nginx.com             return NXT_ERROR;
1155*417Svbart@nginx.com         }
1156*417Svbart@nginx.com     }
1157*417Svbart@nginx.com 
1158*417Svbart@nginx.com     return NXT_OK;
1159*417Svbart@nginx.com }
1160*417Svbart@nginx.com 
1161*417Svbart@nginx.com 
1162*417Svbart@nginx.com nxt_uint_t
1163*417Svbart@nginx.com nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp,
1164*417Svbart@nginx.com     nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level)
1165*417Svbart@nginx.com {
1166*417Svbart@nginx.com     u_char              ch;
1167*417Svbart@nginx.com     uint32_t            key, mask;
1168*417Svbart@nginx.com     nxt_str_t           *name;
1169*417Svbart@nginx.com     nxt_uint_t          colls, i, j;
1170*417Svbart@nginx.com     nxt_lvlhsh_proto_t  proto;
1171*417Svbart@nginx.com     nxt_lvlhsh_query_t  lhq;
1172*417Svbart@nginx.com 
1173*417Svbart@nginx.com     proto = nxt_http_fields_hash_proto;
1174*417Svbart@nginx.com     proto.test = nxt_http_field_hash_collision;
1175*417Svbart@nginx.com 
1176*417Svbart@nginx.com     lhq.replace = 0;
1177*417Svbart@nginx.com     lhq.proto = &proto;
1178*417Svbart@nginx.com     lhq.pool = mp;
1179*417Svbart@nginx.com 
1180*417Svbart@nginx.com     mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xffff;
1181*417Svbart@nginx.com 
1182*417Svbart@nginx.com     colls = 0;
1183*417Svbart@nginx.com 
1184*417Svbart@nginx.com     for (i = 0; i < count; i++) {
1185*417Svbart@nginx.com         key = NXT_HTTP_FIELD_HASH_INIT;
1186*417Svbart@nginx.com         name = &items[i].name;
1187*417Svbart@nginx.com 
1188*417Svbart@nginx.com         for (j = 0; j < name->length; j++) {
1189*417Svbart@nginx.com             ch = nxt_lowcase(name->start[j]);
1190*417Svbart@nginx.com             key = nxt_http_field_hash_char(key, ch);
1191*417Svbart@nginx.com         }
1192*417Svbart@nginx.com 
1193*417Svbart@nginx.com         lhq.key_hash = nxt_http_field_hash_end(key) & mask;
1194*417Svbart@nginx.com 
1195*417Svbart@nginx.com         if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) {
1196*417Svbart@nginx.com             colls++;
1197*417Svbart@nginx.com         }
1198*417Svbart@nginx.com     }
1199*417Svbart@nginx.com 
1200*417Svbart@nginx.com     return colls;
1201*417Svbart@nginx.com }
1202*417Svbart@nginx.com 
1203*417Svbart@nginx.com 
1204*417Svbart@nginx.com nxt_int_t
1205*417Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx)
1206*417Svbart@nginx.com {
1207*417Svbart@nginx.com     nxt_int_t              ret;
1208*417Svbart@nginx.com     nxt_http_field_t       *field;
1209*417Svbart@nginx.com     nxt_lvlhsh_query_t     lhq;
1210*417Svbart@nginx.com     nxt_http_field_proc_t  *proc;
1211*417Svbart@nginx.com 
1212*417Svbart@nginx.com     lhq.proto = &nxt_http_fields_hash_proto;
1213*417Svbart@nginx.com 
1214*417Svbart@nginx.com     nxt_list_each(field, fields) {
1215*417Svbart@nginx.com 
1216*417Svbart@nginx.com         lhq.key_hash = field->hash;
1217*417Svbart@nginx.com         lhq.key.length = field->name_length;
1218*417Svbart@nginx.com         lhq.key.start = field->name;
1219*417Svbart@nginx.com 
1220*417Svbart@nginx.com         if (nxt_lvlhsh_find(hash, &lhq) != NXT_OK) {
1221*417Svbart@nginx.com             continue;
1222*417Svbart@nginx.com         }
1223*417Svbart@nginx.com 
1224*417Svbart@nginx.com         proc = lhq.value;
1225*417Svbart@nginx.com 
1226*417Svbart@nginx.com         ret = proc->handler(ctx, field, proc->data);
1227*417Svbart@nginx.com 
1228*417Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
1229*417Svbart@nginx.com             return ret;
1230*417Svbart@nginx.com         }
1231*417Svbart@nginx.com 
1232*417Svbart@nginx.com     } nxt_list_loop;
1233*417Svbart@nginx.com 
1234*417Svbart@nginx.com     return NXT_OK;
1235*417Svbart@nginx.com }
1236