xref: /unit/src/nxt_http_parse.c (revision 410)
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 typedef struct {
1167Svbart@nginx.com     nxt_http_field_handler_t    handler;
1267Svbart@nginx.com     uintptr_t                   data;
1360Svbart@nginx.com 
1416Svbart@nginx.com     union {
1567Svbart@nginx.com         uint8_t                 str[8];
1667Svbart@nginx.com         uint64_t                ui64;
1716Svbart@nginx.com     } key[];
1860Svbart@nginx.com } nxt_http_fields_hash_elt_t;
1916Svbart@nginx.com 
2016Svbart@nginx.com 
2116Svbart@nginx.com struct nxt_http_fields_hash_s {
2267Svbart@nginx.com     size_t                      min_length;
2367Svbart@nginx.com     size_t                      max_length;
2467Svbart@nginx.com     void                        *long_fields;
2567Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elts[];
2616Svbart@nginx.com };
2716Svbart@nginx.com 
2816Svbart@nginx.com 
2960Svbart@nginx.com #define nxt_http_fields_hash_next_elt(elt, n)                                 \
3060Svbart@nginx.com     ((nxt_http_fields_hash_elt_t *) ((u_char *) (elt)                         \
3160Svbart@nginx.com                                      + sizeof(nxt_http_fields_hash_elt_t)     \
3260Svbart@nginx.com                                      + n * 8))
3360Svbart@nginx.com 
3460Svbart@nginx.com 
3516Svbart@nginx.com static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp,
3616Svbart@nginx.com     u_char **pos, u_char *end);
3716Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp,
3816Svbart@nginx.com     u_char **pos, u_char *end);
3916Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp,
4016Svbart@nginx.com     u_char **pos, u_char *end);
4116Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp,
4216Svbart@nginx.com     u_char **pos, u_char *end);
4316Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end);
4416Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
4516Svbart@nginx.com     u_char **pos, u_char *end);
4616Svbart@nginx.com 
4767Svbart@nginx.com static void nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash,
4867Svbart@nginx.com     uint64_t key[4], nxt_http_field_t *field);
4967Svbart@nginx.com static void nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
5067Svbart@nginx.com     nxt_http_field_t *field);
5116Svbart@nginx.com 
52112Smax.romanov@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
53112Smax.romanov@nginx.com 
5416Svbart@nginx.com 
5516Svbart@nginx.com typedef enum {
5616Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
5716Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
5816Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
5916Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
6016Svbart@nginx.com 
6116Svbart@nginx.com     /* traps below are used for extended check only */
6216Svbart@nginx.com 
6316Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
6416Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
6516Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
6616Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
6716Svbart@nginx.com     NXT_HTTP_TARGET_PLUS,        /*  +  */
6816Svbart@nginx.com } nxt_http_target_traps_e;
6916Svbart@nginx.com 
7016Svbart@nginx.com 
7116Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
7216Svbart@nginx.com     /* \0                               \n        \r       */
7316Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
7416Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
7552Svbart@nginx.com 
7652Svbart@nginx.com     /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
7716Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 9,  0, 0, 6, 5,
7852Svbart@nginx.com 
7952Svbart@nginx.com     /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
8016Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
8116Svbart@nginx.com };
8216Svbart@nginx.com 
8316Svbart@nginx.com 
8416Svbart@nginx.com nxt_inline nxt_http_target_traps_e
8516Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
8616Svbart@nginx.com {
8716Svbart@nginx.com     u_char      *p;
8816Svbart@nginx.com     nxt_uint_t  trap;
8916Svbart@nginx.com 
9016Svbart@nginx.com     p = *pos;
9116Svbart@nginx.com 
92409Svbart@nginx.com     while (nxt_fast_path(end - p >= 10)) {
9316Svbart@nginx.com 
94409Svbart@nginx.com #define nxt_target_test_char(ch)                                              \
95409Svbart@nginx.com                                                                               \
96409Svbart@nginx.com         trap = nxt_http_target_chars[ch];                                     \
9716Svbart@nginx.com                                                                               \
98409Svbart@nginx.com         if (nxt_slow_path(trap != 0)) {                                       \
99409Svbart@nginx.com             *pos = &(ch);                                                     \
100409Svbart@nginx.com             return trap;                                                      \
10116Svbart@nginx.com         }
10216Svbart@nginx.com 
103409Svbart@nginx.com /* enddef */
104409Svbart@nginx.com 
105409Svbart@nginx.com         nxt_target_test_char(p[0]);
106409Svbart@nginx.com         nxt_target_test_char(p[1]);
107409Svbart@nginx.com         nxt_target_test_char(p[2]);
108409Svbart@nginx.com         nxt_target_test_char(p[3]);
10916Svbart@nginx.com 
110409Svbart@nginx.com         nxt_target_test_char(p[4]);
111409Svbart@nginx.com         nxt_target_test_char(p[5]);
112409Svbart@nginx.com         nxt_target_test_char(p[6]);
113409Svbart@nginx.com         nxt_target_test_char(p[7]);
11416Svbart@nginx.com 
115409Svbart@nginx.com         nxt_target_test_char(p[8]);
116409Svbart@nginx.com         nxt_target_test_char(p[9]);
11716Svbart@nginx.com 
118409Svbart@nginx.com         p += 10;
11916Svbart@nginx.com     }
12016Svbart@nginx.com 
121*410Svbart@nginx.com     while (p != end) {
122*410Svbart@nginx.com         nxt_target_test_char(*p); p++;
123*410Svbart@nginx.com     }
124*410Svbart@nginx.com 
125409Svbart@nginx.com     return NXT_HTTP_TARGET_AGAIN;
12616Svbart@nginx.com }
12716Svbart@nginx.com 
12816Svbart@nginx.com 
12916Svbart@nginx.com nxt_int_t
13016Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
13116Svbart@nginx.com {
13216Svbart@nginx.com     nxt_int_t  rc;
13316Svbart@nginx.com 
13416Svbart@nginx.com     if (rp->handler == NULL) {
13516Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13616Svbart@nginx.com     }
13716Svbart@nginx.com 
13816Svbart@nginx.com     do {
13916Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
14016Svbart@nginx.com     } while (rc == NXT_OK);
14116Svbart@nginx.com 
14216Svbart@nginx.com     return rc;
14316Svbart@nginx.com }
14416Svbart@nginx.com 
14516Svbart@nginx.com 
14616Svbart@nginx.com static nxt_int_t
14716Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
14816Svbart@nginx.com     u_char *end)
14916Svbart@nginx.com {
15016Svbart@nginx.com     u_char                   *p, ch, *after_slash;
15116Svbart@nginx.com     nxt_int_t                rc;
15216Svbart@nginx.com     nxt_http_ver_t           version;
15316Svbart@nginx.com     nxt_http_target_traps_e  trap;
15416Svbart@nginx.com 
15516Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
15616Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
15716Svbart@nginx.com 
15816Svbart@nginx.com     p = *pos;
15916Svbart@nginx.com 
16016Svbart@nginx.com     rp->method.start = p;
16116Svbart@nginx.com 
162409Svbart@nginx.com     for ( ;; ) {
163409Svbart@nginx.com 
164409Svbart@nginx.com         while (nxt_fast_path(end - p >= 8)) {
16516Svbart@nginx.com 
166409Svbart@nginx.com #define nxt_method_test_char(ch)                                              \
167409Svbart@nginx.com                                                                               \
168409Svbart@nginx.com             if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) {                    \
169409Svbart@nginx.com                 p = &(ch);                                                    \
170409Svbart@nginx.com                 goto method_unusual_char;                                     \
17116Svbart@nginx.com             }
17216Svbart@nginx.com 
173409Svbart@nginx.com /* enddef */
174409Svbart@nginx.com 
175409Svbart@nginx.com             nxt_method_test_char(p[0]);
176409Svbart@nginx.com             nxt_method_test_char(p[1]);
177409Svbart@nginx.com             nxt_method_test_char(p[2]);
178409Svbart@nginx.com             nxt_method_test_char(p[3]);
17916Svbart@nginx.com 
180409Svbart@nginx.com             nxt_method_test_char(p[4]);
181409Svbart@nginx.com             nxt_method_test_char(p[5]);
182409Svbart@nginx.com             nxt_method_test_char(p[6]);
183409Svbart@nginx.com             nxt_method_test_char(p[7]);
18416Svbart@nginx.com 
185409Svbart@nginx.com             p += 8;
186409Svbart@nginx.com         }
18716Svbart@nginx.com 
188*410Svbart@nginx.com         while (p != end) {
189*410Svbart@nginx.com             nxt_method_test_char(*p); p++;
190*410Svbart@nginx.com         }
191*410Svbart@nginx.com 
192409Svbart@nginx.com         return NXT_AGAIN;
193409Svbart@nginx.com 
194409Svbart@nginx.com     method_unusual_char:
195409Svbart@nginx.com 
196409Svbart@nginx.com         ch = *p;
19716Svbart@nginx.com 
19816Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
19916Svbart@nginx.com             rp->method.length = p - rp->method.start;
20016Svbart@nginx.com             break;
20116Svbart@nginx.com         }
20216Svbart@nginx.com 
20316Svbart@nginx.com         if (ch == '_' || ch == '-') {
204409Svbart@nginx.com             p++;
20516Svbart@nginx.com             continue;
20616Svbart@nginx.com         }
20716Svbart@nginx.com 
20816Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
20916Svbart@nginx.com             rp->method.start++;
210409Svbart@nginx.com             p++;
21116Svbart@nginx.com             continue;
21216Svbart@nginx.com         }
21316Svbart@nginx.com 
21416Svbart@nginx.com         return NXT_ERROR;
21516Svbart@nginx.com     }
21616Svbart@nginx.com 
21716Svbart@nginx.com     p++;
21816Svbart@nginx.com 
21916Svbart@nginx.com     if (nxt_slow_path(p == end)) {
22016Svbart@nginx.com         return NXT_AGAIN;
22116Svbart@nginx.com     }
22216Svbart@nginx.com 
22316Svbart@nginx.com     /* target */
22416Svbart@nginx.com 
22516Svbart@nginx.com     ch = *p;
22616Svbart@nginx.com 
22716Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
22816Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
22916Svbart@nginx.com 
23016Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
23116Svbart@nginx.com             return rc;
23216Svbart@nginx.com         }
23316Svbart@nginx.com     }
23416Svbart@nginx.com 
23516Svbart@nginx.com     rp->target_start = p;
23616Svbart@nginx.com 
23716Svbart@nginx.com     after_slash = p + 1;
23816Svbart@nginx.com 
23916Svbart@nginx.com     for ( ;; ) {
24016Svbart@nginx.com         p++;
24116Svbart@nginx.com 
24216Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
24316Svbart@nginx.com 
24416Svbart@nginx.com         switch (trap) {
24516Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
24616Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
24716Svbart@nginx.com                 rp->complex_target = 1;
24816Svbart@nginx.com                 goto rest_of_target;
24916Svbart@nginx.com             }
25016Svbart@nginx.com 
25116Svbart@nginx.com             after_slash = p + 1;
25216Svbart@nginx.com 
25316Svbart@nginx.com             rp->exten_start = NULL;
25416Svbart@nginx.com             continue;
25516Svbart@nginx.com 
25616Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
25716Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
25816Svbart@nginx.com                 rp->complex_target = 1;
25916Svbart@nginx.com                 goto rest_of_target;
26016Svbart@nginx.com             }
26116Svbart@nginx.com 
26216Svbart@nginx.com             rp->exten_start = p + 1;
26316Svbart@nginx.com             continue;
26416Svbart@nginx.com 
26516Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
26616Svbart@nginx.com             rp->args_start = p + 1;
26716Svbart@nginx.com             goto rest_of_target;
26816Svbart@nginx.com 
26916Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
27016Svbart@nginx.com             rp->target_end = p;
27116Svbart@nginx.com             goto space_after_target;
27216Svbart@nginx.com 
27316Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
27416Svbart@nginx.com             rp->quoted_target = 1;
27516Svbart@nginx.com             goto rest_of_target;
27616Svbart@nginx.com 
27716Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
27816Svbart@nginx.com             rp->plus_in_target = 1;
27916Svbart@nginx.com             continue;
28016Svbart@nginx.com 
28116Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
28216Svbart@nginx.com             rp->complex_target = 1;
28316Svbart@nginx.com             goto rest_of_target;
28416Svbart@nginx.com 
28516Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
28616Svbart@nginx.com             return NXT_AGAIN;
28716Svbart@nginx.com 
28816Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
28916Svbart@nginx.com             return NXT_ERROR;
29016Svbart@nginx.com         }
29116Svbart@nginx.com 
29216Svbart@nginx.com         nxt_unreachable();
29316Svbart@nginx.com     }
29416Svbart@nginx.com 
29516Svbart@nginx.com rest_of_target:
29616Svbart@nginx.com 
29716Svbart@nginx.com     for ( ;; ) {
29816Svbart@nginx.com         p++;
29916Svbart@nginx.com 
30019Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
30116Svbart@nginx.com 
30216Svbart@nginx.com         switch (trap) {
30316Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
30416Svbart@nginx.com             rp->target_end = p;
30516Svbart@nginx.com             goto space_after_target;
30616Svbart@nginx.com 
30716Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
30816Svbart@nginx.com             rp->complex_target = 1;
30916Svbart@nginx.com             continue;
31016Svbart@nginx.com 
31116Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
31216Svbart@nginx.com             return NXT_AGAIN;
31316Svbart@nginx.com 
31416Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
31516Svbart@nginx.com             return NXT_ERROR;
31616Svbart@nginx.com 
31716Svbart@nginx.com         default:
31816Svbart@nginx.com             continue;
31916Svbart@nginx.com         }
32016Svbart@nginx.com 
32116Svbart@nginx.com         nxt_unreachable();
32216Svbart@nginx.com     }
32316Svbart@nginx.com 
32416Svbart@nginx.com space_after_target:
32516Svbart@nginx.com 
32616Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
327*410Svbart@nginx.com 
328*410Svbart@nginx.com         do {
329*410Svbart@nginx.com             p++;
330*410Svbart@nginx.com 
331*410Svbart@nginx.com             if (p == end) {
332*410Svbart@nginx.com                 return NXT_AGAIN;
333*410Svbart@nginx.com             }
334*410Svbart@nginx.com 
335*410Svbart@nginx.com         } while (*p == ' ');
336*410Svbart@nginx.com 
337*410Svbart@nginx.com         if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) {
338*410Svbart@nginx.com 
339*410Svbart@nginx.com             switch (end - p) {
340*410Svbart@nginx.com             case 8:
341*410Svbart@nginx.com                 if (p[7] < '0' || p[7] > '9') {
342*410Svbart@nginx.com                     break;
343*410Svbart@nginx.com                 }
344*410Svbart@nginx.com                 /* Fall through. */
345*410Svbart@nginx.com             case 7:
346*410Svbart@nginx.com                 if (p[6] != '.') {
347*410Svbart@nginx.com                     break;
348*410Svbart@nginx.com                 }
349*410Svbart@nginx.com                 /* Fall through. */
350*410Svbart@nginx.com             case 6:
351*410Svbart@nginx.com                 if (p[5] < '0' || p[5] > '9') {
352*410Svbart@nginx.com                     break;
353*410Svbart@nginx.com                 }
354*410Svbart@nginx.com                 /* Fall through. */
355*410Svbart@nginx.com             default:
356*410Svbart@nginx.com                 return NXT_AGAIN;
357*410Svbart@nginx.com             }
358*410Svbart@nginx.com         }
359*410Svbart@nginx.com 
360*410Svbart@nginx.com         rp->space_in_target = 1;
361*410Svbart@nginx.com         goto rest_of_target;
36216Svbart@nginx.com     }
36316Svbart@nginx.com 
36416Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
36516Svbart@nginx.com 
36616Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
36716Svbart@nginx.com 
36816Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
36916Svbart@nginx.com                        || version.ui64 == http10.ui64
37016Svbart@nginx.com                        || (p[1] == 'H'
37116Svbart@nginx.com                            && p[2] == 'T'
37216Svbart@nginx.com                            && p[3] == 'T'
37316Svbart@nginx.com                            && p[4] == 'P'
37416Svbart@nginx.com                            && p[5] == '/'
37516Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
37616Svbart@nginx.com                            && p[7] == '.'
37716Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
37816Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
37916Svbart@nginx.com     {
38016Svbart@nginx.com         rp->version.ui64 = version.ui64;
38116Svbart@nginx.com 
38216Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
38316Svbart@nginx.com             p += 10;
38416Svbart@nginx.com 
38516Svbart@nginx.com             if (nxt_slow_path(p == end)) {
38616Svbart@nginx.com                 return NXT_AGAIN;
38716Svbart@nginx.com             }
38816Svbart@nginx.com 
38916Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
39016Svbart@nginx.com                 return NXT_ERROR;
39116Svbart@nginx.com             }
39216Svbart@nginx.com 
39316Svbart@nginx.com             *pos = p + 1;
394112Smax.romanov@nginx.com 
395112Smax.romanov@nginx.com         } else {
396112Smax.romanov@nginx.com             *pos = p + 10;
397112Smax.romanov@nginx.com         }
398112Smax.romanov@nginx.com 
399112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
400112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
401112Smax.romanov@nginx.com 
402112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
403112Smax.romanov@nginx.com                 return rc;
404112Smax.romanov@nginx.com             }
405112Smax.romanov@nginx.com 
40616Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
40716Svbart@nginx.com         }
40816Svbart@nginx.com 
409112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
410112Smax.romanov@nginx.com 
411112Smax.romanov@nginx.com         if (rp->args_start != NULL) {
412112Smax.romanov@nginx.com             rp->path.length = rp->args_start - rp->target_start - 1;
413112Smax.romanov@nginx.com 
414112Smax.romanov@nginx.com             rp->args.start = rp->args_start;
415112Smax.romanov@nginx.com             rp->args.length = rp->target_end - rp->args_start;
416112Smax.romanov@nginx.com 
417112Smax.romanov@nginx.com         } else {
418112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
419112Smax.romanov@nginx.com         }
420112Smax.romanov@nginx.com 
421112Smax.romanov@nginx.com         if (rp->exten_start) {
422112Smax.romanov@nginx.com             rp->exten.length = rp->path.start + rp->path.length -
423112Smax.romanov@nginx.com                                rp->exten_start;
424112Smax.romanov@nginx.com             rp->exten.start = rp->exten_start;
425112Smax.romanov@nginx.com         }
426112Smax.romanov@nginx.com 
42716Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
42816Svbart@nginx.com     }
42916Svbart@nginx.com 
43016Svbart@nginx.com     if (p[1] == ' ') {
43116Svbart@nginx.com         /* surplus space after tartet */
43216Svbart@nginx.com         p++;
43316Svbart@nginx.com         goto space_after_target;
43416Svbart@nginx.com     }
43516Svbart@nginx.com 
43616Svbart@nginx.com     rp->space_in_target = 1;
43716Svbart@nginx.com     goto rest_of_target;
43816Svbart@nginx.com }
43916Svbart@nginx.com 
44016Svbart@nginx.com 
44116Svbart@nginx.com static nxt_int_t
44216Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
44316Svbart@nginx.com     u_char *end)
44416Svbart@nginx.com {
44516Svbart@nginx.com     u_char  *p, ch;
44616Svbart@nginx.com 
44716Svbart@nginx.com     p = *pos;
44816Svbart@nginx.com 
44916Svbart@nginx.com     ch = *p;
45016Svbart@nginx.com 
45116Svbart@nginx.com     if (ch == ' ') {
45216Svbart@nginx.com         /* skip surplus spaces before target */
45316Svbart@nginx.com 
45416Svbart@nginx.com         do {
45516Svbart@nginx.com             p++;
45616Svbart@nginx.com 
45716Svbart@nginx.com             if (nxt_slow_path(p == end)) {
45816Svbart@nginx.com                 return NXT_AGAIN;
45916Svbart@nginx.com             }
46016Svbart@nginx.com 
46116Svbart@nginx.com             ch = *p;
46216Svbart@nginx.com 
46316Svbart@nginx.com         } while (ch == ' ');
46416Svbart@nginx.com 
46516Svbart@nginx.com         if (ch == '/') {
46616Svbart@nginx.com             *pos = p;
46716Svbart@nginx.com             return NXT_OK;
46816Svbart@nginx.com         }
46916Svbart@nginx.com     }
47016Svbart@nginx.com 
47116Svbart@nginx.com     /* absolute path or '*' */
47216Svbart@nginx.com 
47316Svbart@nginx.com     /* TODO */
47416Svbart@nginx.com 
47516Svbart@nginx.com     return NXT_ERROR;
47616Svbart@nginx.com }
47716Svbart@nginx.com 
47816Svbart@nginx.com 
47916Svbart@nginx.com static nxt_int_t
48016Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
48116Svbart@nginx.com     u_char *end)
48216Svbart@nginx.com {
48316Svbart@nginx.com     u_char  *p, ch, c;
48416Svbart@nginx.com     size_t  i, size;
48516Svbart@nginx.com 
48616Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
48716Svbart@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"
48816Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
48916Svbart@nginx.com 
49016Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
49116Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
49216Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
49316Svbart@nginx.com 
49416Svbart@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"
49516Svbart@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"
49616Svbart@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"
49716Svbart@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";
49816Svbart@nginx.com 
49916Svbart@nginx.com     p = *pos;
50016Svbart@nginx.com 
50116Svbart@nginx.com     size = end - p;
50267Svbart@nginx.com     i = rp->field_name.length;
50316Svbart@nginx.com 
504409Svbart@nginx.com     while (nxt_fast_path(size - i >= 8)) {
505409Svbart@nginx.com 
506409Svbart@nginx.com #define nxt_field_name_test_char(i)                                           \
507409Svbart@nginx.com                                                                               \
50819Svbart@nginx.com         ch = p[i];                                                            \
50919Svbart@nginx.com         c = normal[ch];                                                       \
51019Svbart@nginx.com                                                                               \
51119Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
51219Svbart@nginx.com             goto name_end;                                                    \
51319Svbart@nginx.com         }                                                                     \
51419Svbart@nginx.com                                                                               \
515409Svbart@nginx.com         rp->field_key.str[i % 32] = c;
516409Svbart@nginx.com 
517409Svbart@nginx.com /* enddef */
51816Svbart@nginx.com 
519409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
520409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
521409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
522409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
52316Svbart@nginx.com 
524409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
525409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
526409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
527409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
52819Svbart@nginx.com     }
52916Svbart@nginx.com 
53019Svbart@nginx.com     while (nxt_fast_path(i != size)) {
531409Svbart@nginx.com         nxt_field_name_test_char(i); i++;
53219Svbart@nginx.com     }
53316Svbart@nginx.com 
53467Svbart@nginx.com     rp->field_name.length = i;
53516Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
53616Svbart@nginx.com 
53716Svbart@nginx.com     return NXT_AGAIN;
53819Svbart@nginx.com 
53919Svbart@nginx.com name_end:
54019Svbart@nginx.com 
54119Svbart@nginx.com     if (nxt_fast_path(ch == ':')) {
54219Svbart@nginx.com         if (nxt_slow_path(i == 0)) {
54319Svbart@nginx.com             return NXT_ERROR;
54419Svbart@nginx.com         }
54519Svbart@nginx.com 
54619Svbart@nginx.com         *pos = &p[i] + 1;
54719Svbart@nginx.com 
54867Svbart@nginx.com         rp->field_name.length = i;
54967Svbart@nginx.com         rp->field_name.start = p;
55019Svbart@nginx.com 
55119Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
55219Svbart@nginx.com     }
55319Svbart@nginx.com 
55459Svbart@nginx.com     if (nxt_slow_path(i != 0)) {
55559Svbart@nginx.com         return NXT_ERROR;
55659Svbart@nginx.com     }
55719Svbart@nginx.com 
55819Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
55916Svbart@nginx.com }
56016Svbart@nginx.com 
56116Svbart@nginx.com 
56216Svbart@nginx.com static nxt_int_t
56316Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
56416Svbart@nginx.com     u_char *end)
56516Svbart@nginx.com {
56616Svbart@nginx.com     u_char  *p, ch;
56716Svbart@nginx.com 
56816Svbart@nginx.com     p = *pos;
56916Svbart@nginx.com 
57016Svbart@nginx.com     for ( ;; ) {
57116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
57216Svbart@nginx.com             *pos = p;
57316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
57416Svbart@nginx.com             return NXT_AGAIN;
57516Svbart@nginx.com         }
57616Svbart@nginx.com 
57716Svbart@nginx.com         if (*p != ' ') {
57816Svbart@nginx.com             break;
57916Svbart@nginx.com         }
58016Svbart@nginx.com 
58116Svbart@nginx.com         p++;
58216Svbart@nginx.com     }
58316Svbart@nginx.com 
58416Svbart@nginx.com     *pos = p;
58516Svbart@nginx.com 
58667Svbart@nginx.com     p += rp->field_value.length;
58716Svbart@nginx.com 
58816Svbart@nginx.com     for ( ;; ) {
58916Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
59016Svbart@nginx.com 
59116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
59267Svbart@nginx.com             rp->field_value.length = p - *pos;
59316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
59416Svbart@nginx.com             return NXT_AGAIN;
59516Svbart@nginx.com         }
59616Svbart@nginx.com 
59716Svbart@nginx.com         ch = *p;
59816Svbart@nginx.com 
59916Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
60016Svbart@nginx.com             break;
60116Svbart@nginx.com         }
60216Svbart@nginx.com 
60316Svbart@nginx.com         if (ch == '\0') {
60416Svbart@nginx.com             return NXT_ERROR;
60516Svbart@nginx.com         }
60616Svbart@nginx.com     }
60716Svbart@nginx.com 
60816Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
60916Svbart@nginx.com         while (p[-1] == ' ') {
61016Svbart@nginx.com             p--;
61116Svbart@nginx.com         }
61216Svbart@nginx.com     }
61316Svbart@nginx.com 
61467Svbart@nginx.com     rp->field_value.length = p - *pos;
61567Svbart@nginx.com     rp->field_value.start = *pos;
61616Svbart@nginx.com 
61716Svbart@nginx.com     *pos = p;
61816Svbart@nginx.com 
61916Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
62016Svbart@nginx.com }
62116Svbart@nginx.com 
62216Svbart@nginx.com 
62316Svbart@nginx.com static u_char *
62416Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
62516Svbart@nginx.com {
626409Svbart@nginx.com     while (nxt_fast_path(end - p >= 16)) {
627409Svbart@nginx.com 
628409Svbart@nginx.com #define nxt_field_end_test_char(ch)                                           \
629409Svbart@nginx.com                                                                               \
630409Svbart@nginx.com         if (nxt_slow_path((ch) < 0x10)) {                                     \
631409Svbart@nginx.com             return &(ch);                                                     \
632409Svbart@nginx.com         }
633409Svbart@nginx.com 
634409Svbart@nginx.com /* enddef */
635409Svbart@nginx.com 
636409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
637409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
638409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
639409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
64016Svbart@nginx.com 
641409Svbart@nginx.com         nxt_field_end_test_char(p[4]);
642409Svbart@nginx.com         nxt_field_end_test_char(p[5]);
643409Svbart@nginx.com         nxt_field_end_test_char(p[6]);
644409Svbart@nginx.com         nxt_field_end_test_char(p[7]);
645409Svbart@nginx.com 
646409Svbart@nginx.com         nxt_field_end_test_char(p[8]);
647409Svbart@nginx.com         nxt_field_end_test_char(p[9]);
648409Svbart@nginx.com         nxt_field_end_test_char(p[10]);
649409Svbart@nginx.com         nxt_field_end_test_char(p[11]);
650409Svbart@nginx.com 
651409Svbart@nginx.com         nxt_field_end_test_char(p[12]);
652409Svbart@nginx.com         nxt_field_end_test_char(p[13]);
653409Svbart@nginx.com         nxt_field_end_test_char(p[14]);
654409Svbart@nginx.com         nxt_field_end_test_char(p[15]);
655409Svbart@nginx.com 
656409Svbart@nginx.com         p += 16;
65716Svbart@nginx.com     }
65816Svbart@nginx.com 
659409Svbart@nginx.com     while (nxt_fast_path(end - p >= 4)) {
66019Svbart@nginx.com 
661409Svbart@nginx.com         nxt_field_end_test_char(p[0]);
662409Svbart@nginx.com         nxt_field_end_test_char(p[1]);
663409Svbart@nginx.com         nxt_field_end_test_char(p[2]);
664409Svbart@nginx.com         nxt_field_end_test_char(p[3]);
66516Svbart@nginx.com 
666409Svbart@nginx.com         p += 4;
66719Svbart@nginx.com     }
66819Svbart@nginx.com 
66919Svbart@nginx.com     switch (end - p) {
67016Svbart@nginx.com     case 3:
671409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
67239Svbart@nginx.com         /* Fall through. */
67316Svbart@nginx.com     case 2:
674409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
67539Svbart@nginx.com         /* Fall through. */
67616Svbart@nginx.com     case 1:
677409Svbart@nginx.com         nxt_field_end_test_char(*p); p++;
67839Svbart@nginx.com         /* Fall through. */
67916Svbart@nginx.com     case 0:
68016Svbart@nginx.com         break;
68116Svbart@nginx.com     default:
68216Svbart@nginx.com         nxt_unreachable();
68316Svbart@nginx.com     }
68416Svbart@nginx.com 
68516Svbart@nginx.com     return p;
68616Svbart@nginx.com }
68716Svbart@nginx.com 
68816Svbart@nginx.com 
68916Svbart@nginx.com static nxt_int_t
69016Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
69116Svbart@nginx.com     u_char *end)
69216Svbart@nginx.com {
69360Svbart@nginx.com     u_char            *p;
69460Svbart@nginx.com     nxt_http_field_t  *field;
69516Svbart@nginx.com 
69616Svbart@nginx.com     p = *pos;
69716Svbart@nginx.com 
69816Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
69916Svbart@nginx.com         p++;
70016Svbart@nginx.com 
70116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
70216Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
70316Svbart@nginx.com             return NXT_AGAIN;
70416Svbart@nginx.com         }
70516Svbart@nginx.com     }
70616Svbart@nginx.com 
70716Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
70816Svbart@nginx.com         *pos = p + 1;
70916Svbart@nginx.com 
71067Svbart@nginx.com         if (rp->field_name.length != 0) {
71160Svbart@nginx.com             field = nxt_list_add(rp->fields);
71216Svbart@nginx.com 
71360Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
71460Svbart@nginx.com                 return NXT_ERROR;
71516Svbart@nginx.com             }
71616Svbart@nginx.com 
71767Svbart@nginx.com             field->name = rp->field_name;
71867Svbart@nginx.com             field->value = rp->field_value;
71960Svbart@nginx.com 
72067Svbart@nginx.com             nxt_http_fields_hash_lookup(rp->fields_hash, rp->field_key.ui64,
72167Svbart@nginx.com                                         field);
72267Svbart@nginx.com 
72367Svbart@nginx.com             nxt_memzero(rp->field_key.str, 32);
72467Svbart@nginx.com 
72567Svbart@nginx.com             rp->field_name.length = 0;
72667Svbart@nginx.com             rp->field_value.length = 0;
72716Svbart@nginx.com 
72816Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
72916Svbart@nginx.com             return NXT_OK;
73016Svbart@nginx.com         }
73116Svbart@nginx.com 
73216Svbart@nginx.com         return NXT_DONE;
73316Svbart@nginx.com     }
73416Svbart@nginx.com 
73516Svbart@nginx.com     return NXT_ERROR;
73616Svbart@nginx.com }
73716Svbart@nginx.com 
73816Svbart@nginx.com 
73960Svbart@nginx.com nxt_http_fields_hash_t *
74060Svbart@nginx.com nxt_http_fields_hash_create(nxt_http_fields_hash_entry_t *entries,
74165Sigor@sysoev.ru     nxt_mp_t *mp)
74216Svbart@nginx.com {
74360Svbart@nginx.com     size_t                      min_length, max_length, length, size;
74460Svbart@nginx.com     nxt_uint_t                  i, j, n;
74560Svbart@nginx.com     nxt_http_fields_hash_t      *hash;
74660Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
74716Svbart@nginx.com 
74838Svbart@nginx.com     min_length = 32 + 1;
74916Svbart@nginx.com     max_length = 0;
75016Svbart@nginx.com 
75160Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
75260Svbart@nginx.com         length = entries[i].name.length;
75316Svbart@nginx.com 
75416Svbart@nginx.com         if (length > 32) {
75516Svbart@nginx.com             /* TODO */
75616Svbart@nginx.com             return NULL;
75716Svbart@nginx.com         }
75816Svbart@nginx.com 
75916Svbart@nginx.com         min_length = nxt_min(length, min_length);
76016Svbart@nginx.com         max_length = nxt_max(length, max_length);
76116Svbart@nginx.com     }
76216Svbart@nginx.com 
76338Svbart@nginx.com     size = sizeof(nxt_http_fields_hash_t);
76416Svbart@nginx.com 
76538Svbart@nginx.com     if (min_length <= 32) {
76638Svbart@nginx.com         size += (max_length - min_length + 1)
76760Svbart@nginx.com                 * sizeof(nxt_http_fields_hash_elt_t *);
76838Svbart@nginx.com     }
76916Svbart@nginx.com 
77065Sigor@sysoev.ru     hash = nxt_mp_zget(mp, size);
77116Svbart@nginx.com     if (nxt_slow_path(hash == NULL)) {
77216Svbart@nginx.com         return NULL;
77316Svbart@nginx.com     }
77416Svbart@nginx.com 
77516Svbart@nginx.com     hash->min_length = min_length;
77616Svbart@nginx.com     hash->max_length = max_length;
77716Svbart@nginx.com 
77860Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
77960Svbart@nginx.com         length = entries[i].name.length;
78060Svbart@nginx.com         elt = hash->elts[length - min_length];
78116Svbart@nginx.com 
78260Svbart@nginx.com         if (elt != NULL) {
78316Svbart@nginx.com             continue;
78416Svbart@nginx.com         }
78516Svbart@nginx.com 
78616Svbart@nginx.com         n = 1;
78716Svbart@nginx.com 
78860Svbart@nginx.com         for (j = i + 1; entries[j].handler != NULL; j++) {
78960Svbart@nginx.com             if (length == entries[j].name.length) {
79016Svbart@nginx.com                 n++;
79116Svbart@nginx.com             }
79216Svbart@nginx.com         }
79316Svbart@nginx.com 
79460Svbart@nginx.com         size = sizeof(nxt_http_fields_hash_elt_t) + nxt_align_size(length, 8);
79516Svbart@nginx.com 
79665Sigor@sysoev.ru         elt = nxt_mp_zget(mp, n * size + sizeof(nxt_http_fields_hash_elt_t));
79716Svbart@nginx.com 
79860Svbart@nginx.com         if (nxt_slow_path(elt == NULL)) {
79916Svbart@nginx.com             return NULL;
80016Svbart@nginx.com         }
80116Svbart@nginx.com 
80260Svbart@nginx.com         hash->elts[length - min_length] = elt;
80316Svbart@nginx.com 
80460Svbart@nginx.com         for (j = i; entries[j].handler != NULL; j++) {
80560Svbart@nginx.com             if (length != entries[j].name.length) {
80616Svbart@nginx.com                 continue;
80716Svbart@nginx.com             }
80816Svbart@nginx.com 
80967Svbart@nginx.com             elt->handler = entries[j].handler;
81067Svbart@nginx.com             elt->data = entries[j].data;
81116Svbart@nginx.com 
81260Svbart@nginx.com             nxt_memcpy_lowcase(elt->key->str, entries[j].name.start, length);
81316Svbart@nginx.com 
81416Svbart@nginx.com             n--;
81516Svbart@nginx.com 
81616Svbart@nginx.com             if (n == 0) {
81716Svbart@nginx.com                 break;
81816Svbart@nginx.com             }
81916Svbart@nginx.com 
82098Svbart@nginx.com             elt = nxt_pointer_to(elt, size);
82116Svbart@nginx.com         }
82216Svbart@nginx.com     }
82316Svbart@nginx.com 
82416Svbart@nginx.com     return hash;
82516Svbart@nginx.com }
82660Svbart@nginx.com 
82760Svbart@nginx.com 
82867Svbart@nginx.com static void
82967Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, uint64_t key[4],
83060Svbart@nginx.com     nxt_http_field_t *field)
83160Svbart@nginx.com {
83260Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
83360Svbart@nginx.com 
83467Svbart@nginx.com     if (hash == NULL || field->name.length < hash->min_length) {
83567Svbart@nginx.com         goto not_found;
83660Svbart@nginx.com     }
83760Svbart@nginx.com 
83860Svbart@nginx.com     if (field->name.length > hash->max_length) {
83960Svbart@nginx.com 
84060Svbart@nginx.com         if (field->name.length > 32 && hash->long_fields != NULL) {
84167Svbart@nginx.com             nxt_http_fields_hash_lookup_long(hash, field);
84267Svbart@nginx.com             return;
84360Svbart@nginx.com         }
84460Svbart@nginx.com 
84567Svbart@nginx.com         goto not_found;
84660Svbart@nginx.com     }
84760Svbart@nginx.com 
84860Svbart@nginx.com     elt = hash->elts[field->name.length - hash->min_length];
84960Svbart@nginx.com 
85060Svbart@nginx.com     if (elt == NULL) {
85167Svbart@nginx.com         goto not_found;
85260Svbart@nginx.com     }
85360Svbart@nginx.com 
85460Svbart@nginx.com     switch ((field->name.length + 7) / 8) {
85560Svbart@nginx.com     case 1:
85660Svbart@nginx.com         do {
85767Svbart@nginx.com             if (elt->key[0].ui64 == key[0]) {
85867Svbart@nginx.com                 break;
85960Svbart@nginx.com             }
86060Svbart@nginx.com 
86160Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 1);
86260Svbart@nginx.com 
86367Svbart@nginx.com         } while (elt->handler != NULL);
86460Svbart@nginx.com 
86560Svbart@nginx.com         break;
86660Svbart@nginx.com 
86760Svbart@nginx.com     case 2:
86860Svbart@nginx.com         do {
86967Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
87067Svbart@nginx.com                 && elt->key[1].ui64 == key[1])
87160Svbart@nginx.com             {
87267Svbart@nginx.com                 break;
87360Svbart@nginx.com             }
87460Svbart@nginx.com 
87560Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 2);
87660Svbart@nginx.com 
87767Svbart@nginx.com         } while (elt->handler != NULL);
87860Svbart@nginx.com 
87960Svbart@nginx.com         break;
88060Svbart@nginx.com 
88160Svbart@nginx.com     case 3:
88260Svbart@nginx.com         do {
88367Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
88467Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
88567Svbart@nginx.com                 && elt->key[2].ui64 == key[2])
88660Svbart@nginx.com             {
88767Svbart@nginx.com                 break;
88860Svbart@nginx.com             }
88960Svbart@nginx.com 
89060Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 3);
89160Svbart@nginx.com 
89267Svbart@nginx.com         } while (elt->handler != NULL);
89360Svbart@nginx.com 
89460Svbart@nginx.com         break;
89560Svbart@nginx.com 
89660Svbart@nginx.com     case 4:
89760Svbart@nginx.com         do {
89867Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
89967Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
90067Svbart@nginx.com                 && elt->key[2].ui64 == key[2]
90167Svbart@nginx.com                 && elt->key[3].ui64 == key[3])
90260Svbart@nginx.com             {
90367Svbart@nginx.com                 break;
90460Svbart@nginx.com             }
90560Svbart@nginx.com 
90660Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 4);
90760Svbart@nginx.com 
90867Svbart@nginx.com         } while (elt->handler != NULL);
90960Svbart@nginx.com 
91060Svbart@nginx.com         break;
91160Svbart@nginx.com 
91260Svbart@nginx.com     default:
91360Svbart@nginx.com         nxt_unreachable();
91460Svbart@nginx.com     }
91560Svbart@nginx.com 
91667Svbart@nginx.com     field->handler = elt->handler;
91767Svbart@nginx.com     field->data = elt->data;
91867Svbart@nginx.com 
91967Svbart@nginx.com     return;
92067Svbart@nginx.com 
92167Svbart@nginx.com not_found:
92267Svbart@nginx.com 
92367Svbart@nginx.com     field->handler = NULL;
92467Svbart@nginx.com     field->data = 0;
92560Svbart@nginx.com }
92660Svbart@nginx.com 
92760Svbart@nginx.com 
92867Svbart@nginx.com static void
92960Svbart@nginx.com nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
93060Svbart@nginx.com     nxt_http_field_t *field)
93160Svbart@nginx.com {
93260Svbart@nginx.com     /* TODO */
93367Svbart@nginx.com 
93467Svbart@nginx.com     field->handler = NULL;
93567Svbart@nginx.com     field->data = 0;
93660Svbart@nginx.com }
93760Svbart@nginx.com 
93860Svbart@nginx.com 
93960Svbart@nginx.com nxt_int_t
94067Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, void *ctx, nxt_log_t *log)
94160Svbart@nginx.com {
94267Svbart@nginx.com     nxt_int_t         rc;
94367Svbart@nginx.com     nxt_http_field_t  *field;
94460Svbart@nginx.com 
94560Svbart@nginx.com     nxt_list_each(field, fields) {
94660Svbart@nginx.com 
94767Svbart@nginx.com         if (field->handler != NULL) {
94867Svbart@nginx.com             rc = field->handler(ctx, field, log);
94960Svbart@nginx.com 
95060Svbart@nginx.com             if (rc != NXT_OK) {
95160Svbart@nginx.com                 return rc;
95260Svbart@nginx.com             }
95360Svbart@nginx.com         }
95460Svbart@nginx.com 
95560Svbart@nginx.com     } nxt_list_loop;
95660Svbart@nginx.com 
95760Svbart@nginx.com     return NXT_OK;
95860Svbart@nginx.com }
959112Smax.romanov@nginx.com 
960112Smax.romanov@nginx.com 
961112Smax.romanov@nginx.com #define                                                                       \
962112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
963112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
964112Smax.romanov@nginx.com 
965112Smax.romanov@nginx.com 
966112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
967112Smax.romanov@nginx.com 
968112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
969112Smax.romanov@nginx.com     0xfe, 0xdb, 0xff, 0xff,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
970112Smax.romanov@nginx.com 
971112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
972112Smax.romanov@nginx.com     0xd6, 0x37, 0xff, 0x7f,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
973112Smax.romanov@nginx.com 
974112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
975112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
976112Smax.romanov@nginx.com 
977112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
978112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
979112Smax.romanov@nginx.com 
980112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
981112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
982112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
983112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
984112Smax.romanov@nginx.com };
985112Smax.romanov@nginx.com 
986112Smax.romanov@nginx.com 
987112Smax.romanov@nginx.com static nxt_int_t
988112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
989112Smax.romanov@nginx.com {
990112Smax.romanov@nginx.com     u_char  *p, *u, c, ch, high;
991112Smax.romanov@nginx.com     enum {
992112Smax.romanov@nginx.com         sw_normal = 0,
993112Smax.romanov@nginx.com         sw_slash,
994112Smax.romanov@nginx.com         sw_dot,
995112Smax.romanov@nginx.com         sw_dot_dot,
996112Smax.romanov@nginx.com         sw_quoted,
997112Smax.romanov@nginx.com         sw_quoted_second,
998112Smax.romanov@nginx.com     } state, saved_state;
999112Smax.romanov@nginx.com 
1000112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
1001112Smax.romanov@nginx.com 
1002112Smax.romanov@nginx.com     state = sw_normal;
1003112Smax.romanov@nginx.com     saved_state = sw_normal;
1004112Smax.romanov@nginx.com     p = rp->target_start;
1005112Smax.romanov@nginx.com 
1006112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
1007112Smax.romanov@nginx.com 
1008112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
1009112Smax.romanov@nginx.com         return NXT_ERROR;
1010112Smax.romanov@nginx.com     }
1011112Smax.romanov@nginx.com 
1012112Smax.romanov@nginx.com     rp->path.length = 0;
1013112Smax.romanov@nginx.com     rp->path.start = u;
1014112Smax.romanov@nginx.com 
1015112Smax.romanov@nginx.com     high = '\0';
1016112Smax.romanov@nginx.com     rp->exten_start = NULL;
1017112Smax.romanov@nginx.com     rp->args_start = NULL;
1018112Smax.romanov@nginx.com 
1019112Smax.romanov@nginx.com     while (p < rp->target_end) {
1020112Smax.romanov@nginx.com 
1021112Smax.romanov@nginx.com         ch = *p++;
1022112Smax.romanov@nginx.com 
1023112Smax.romanov@nginx.com     again:
1024112Smax.romanov@nginx.com 
1025112Smax.romanov@nginx.com         switch (state) {
1026112Smax.romanov@nginx.com 
1027112Smax.romanov@nginx.com         case sw_normal:
1028112Smax.romanov@nginx.com 
1029112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1030112Smax.romanov@nginx.com                 *u++ = ch;
1031112Smax.romanov@nginx.com                 continue;
1032112Smax.romanov@nginx.com             }
1033112Smax.romanov@nginx.com 
1034112Smax.romanov@nginx.com             switch (ch) {
1035112Smax.romanov@nginx.com             case '/':
1036112Smax.romanov@nginx.com                 rp->exten_start = NULL;
1037112Smax.romanov@nginx.com                 state = sw_slash;
1038112Smax.romanov@nginx.com                 *u++ = ch;
1039112Smax.romanov@nginx.com                 continue;
1040112Smax.romanov@nginx.com             case '%':
1041112Smax.romanov@nginx.com                 saved_state = state;
1042112Smax.romanov@nginx.com                 state = sw_quoted;
1043112Smax.romanov@nginx.com                 continue;
1044112Smax.romanov@nginx.com             case '?':
1045112Smax.romanov@nginx.com                 rp->args_start = p;
1046112Smax.romanov@nginx.com                 goto args;
1047112Smax.romanov@nginx.com             case '#':
1048112Smax.romanov@nginx.com                 goto done;
1049112Smax.romanov@nginx.com             case '.':
1050112Smax.romanov@nginx.com                 rp->exten_start = u + 1;
1051112Smax.romanov@nginx.com                 *u++ = ch;
1052112Smax.romanov@nginx.com                 continue;
1053112Smax.romanov@nginx.com             case '+':
1054112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1055112Smax.romanov@nginx.com                 /* Fall through. */
1056112Smax.romanov@nginx.com             default:
1057112Smax.romanov@nginx.com                 *u++ = ch;
1058112Smax.romanov@nginx.com                 continue;
1059112Smax.romanov@nginx.com             }
1060112Smax.romanov@nginx.com 
1061112Smax.romanov@nginx.com             break;
1062112Smax.romanov@nginx.com 
1063112Smax.romanov@nginx.com         case sw_slash:
1064112Smax.romanov@nginx.com 
1065112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1066112Smax.romanov@nginx.com                 state = sw_normal;
1067112Smax.romanov@nginx.com                 *u++ = ch;
1068112Smax.romanov@nginx.com                 continue;
1069112Smax.romanov@nginx.com             }
1070112Smax.romanov@nginx.com 
1071112Smax.romanov@nginx.com             switch (ch) {
1072112Smax.romanov@nginx.com             case '/':
1073112Smax.romanov@nginx.com                 continue;
1074112Smax.romanov@nginx.com             case '.':
1075112Smax.romanov@nginx.com                 state = sw_dot;
1076112Smax.romanov@nginx.com                 *u++ = ch;
1077112Smax.romanov@nginx.com                 continue;
1078112Smax.romanov@nginx.com             case '%':
1079112Smax.romanov@nginx.com                 saved_state = state;
1080112Smax.romanov@nginx.com                 state = sw_quoted;
1081112Smax.romanov@nginx.com                 continue;
1082112Smax.romanov@nginx.com             case '?':
1083112Smax.romanov@nginx.com                 rp->args_start = p;
1084112Smax.romanov@nginx.com                 goto args;
1085112Smax.romanov@nginx.com             case '#':
1086112Smax.romanov@nginx.com                 goto done;
1087112Smax.romanov@nginx.com             case '+':
1088112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1089112Smax.romanov@nginx.com                 /* Fall through. */
1090112Smax.romanov@nginx.com             default:
1091112Smax.romanov@nginx.com                 state = sw_normal;
1092112Smax.romanov@nginx.com                 *u++ = ch;
1093112Smax.romanov@nginx.com                 continue;
1094112Smax.romanov@nginx.com             }
1095112Smax.romanov@nginx.com 
1096112Smax.romanov@nginx.com             break;
1097112Smax.romanov@nginx.com 
1098112Smax.romanov@nginx.com         case sw_dot:
1099112Smax.romanov@nginx.com 
1100112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1101112Smax.romanov@nginx.com                 state = sw_normal;
1102112Smax.romanov@nginx.com                 *u++ = ch;
1103112Smax.romanov@nginx.com                 continue;
1104112Smax.romanov@nginx.com             }
1105112Smax.romanov@nginx.com 
1106112Smax.romanov@nginx.com             switch (ch) {
1107112Smax.romanov@nginx.com             case '/':
1108112Smax.romanov@nginx.com                 state = sw_slash;
1109112Smax.romanov@nginx.com                 u--;
1110112Smax.romanov@nginx.com                 continue;
1111112Smax.romanov@nginx.com             case '.':
1112112Smax.romanov@nginx.com                 state = sw_dot_dot;
1113112Smax.romanov@nginx.com                 *u++ = ch;
1114112Smax.romanov@nginx.com                 continue;
1115112Smax.romanov@nginx.com             case '%':
1116112Smax.romanov@nginx.com                 saved_state = state;
1117112Smax.romanov@nginx.com                 state = sw_quoted;
1118112Smax.romanov@nginx.com                 continue;
1119112Smax.romanov@nginx.com             case '?':
1120112Smax.romanov@nginx.com                 rp->args_start = p;
1121112Smax.romanov@nginx.com                 goto args;
1122112Smax.romanov@nginx.com             case '#':
1123112Smax.romanov@nginx.com                 goto done;
1124112Smax.romanov@nginx.com             case '+':
1125112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1126112Smax.romanov@nginx.com                 /* Fall through. */
1127112Smax.romanov@nginx.com             default:
1128112Smax.romanov@nginx.com                 state = sw_normal;
1129112Smax.romanov@nginx.com                 *u++ = ch;
1130112Smax.romanov@nginx.com                 continue;
1131112Smax.romanov@nginx.com             }
1132112Smax.romanov@nginx.com 
1133112Smax.romanov@nginx.com             break;
1134112Smax.romanov@nginx.com 
1135112Smax.romanov@nginx.com         case sw_dot_dot:
1136112Smax.romanov@nginx.com 
1137112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1138112Smax.romanov@nginx.com                 state = sw_normal;
1139112Smax.romanov@nginx.com                 *u++ = ch;
1140112Smax.romanov@nginx.com                 continue;
1141112Smax.romanov@nginx.com             }
1142112Smax.romanov@nginx.com 
1143112Smax.romanov@nginx.com             switch (ch) {
1144112Smax.romanov@nginx.com             case '/':
1145112Smax.romanov@nginx.com                 state = sw_slash;
1146112Smax.romanov@nginx.com                 u -= 5;
1147112Smax.romanov@nginx.com                 for ( ;; ) {
1148112Smax.romanov@nginx.com                     if (u < rp->path.start) {
1149112Smax.romanov@nginx.com                         return NXT_ERROR;
1150112Smax.romanov@nginx.com                     }
1151112Smax.romanov@nginx.com                     if (*u == '/') {
1152112Smax.romanov@nginx.com                         u++;
1153112Smax.romanov@nginx.com                         break;
1154112Smax.romanov@nginx.com                     }
1155112Smax.romanov@nginx.com                     u--;
1156112Smax.romanov@nginx.com                 }
1157112Smax.romanov@nginx.com                 break;
1158112Smax.romanov@nginx.com 
1159112Smax.romanov@nginx.com             case '%':
1160112Smax.romanov@nginx.com                 saved_state = state;
1161112Smax.romanov@nginx.com                 state = sw_quoted;
1162112Smax.romanov@nginx.com                 continue;
1163112Smax.romanov@nginx.com             case '?':
1164112Smax.romanov@nginx.com                 rp->args_start = p;
1165112Smax.romanov@nginx.com                 goto args;
1166112Smax.romanov@nginx.com             case '#':
1167112Smax.romanov@nginx.com                 goto done;
1168112Smax.romanov@nginx.com             case '+':
1169112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1170112Smax.romanov@nginx.com                 /* Fall through. */
1171112Smax.romanov@nginx.com             default:
1172112Smax.romanov@nginx.com                 state = sw_normal;
1173112Smax.romanov@nginx.com                 *u++ = ch;
1174112Smax.romanov@nginx.com                 continue;
1175112Smax.romanov@nginx.com             }
1176112Smax.romanov@nginx.com 
1177112Smax.romanov@nginx.com             break;
1178112Smax.romanov@nginx.com 
1179112Smax.romanov@nginx.com         case sw_quoted:
1180112Smax.romanov@nginx.com             rp->quoted_target = 1;
1181112Smax.romanov@nginx.com 
1182112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1183112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
1184112Smax.romanov@nginx.com                 state = sw_quoted_second;
1185112Smax.romanov@nginx.com                 continue;
1186112Smax.romanov@nginx.com             }
1187112Smax.romanov@nginx.com 
1188112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1189112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1190112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1191112Smax.romanov@nginx.com                 state = sw_quoted_second;
1192112Smax.romanov@nginx.com                 continue;
1193112Smax.romanov@nginx.com             }
1194112Smax.romanov@nginx.com 
1195112Smax.romanov@nginx.com             return NXT_ERROR;
1196112Smax.romanov@nginx.com 
1197112Smax.romanov@nginx.com         case sw_quoted_second:
1198112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1199112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1200112Smax.romanov@nginx.com 
1201112Smax.romanov@nginx.com                 if (ch == '%' || ch == '#') {
1202112Smax.romanov@nginx.com                     state = sw_normal;
1203112Smax.romanov@nginx.com                     *u++ = ch;
1204112Smax.romanov@nginx.com                     continue;
1205112Smax.romanov@nginx.com 
1206112Smax.romanov@nginx.com                 } else if (ch == '\0') {
1207112Smax.romanov@nginx.com                     return NXT_ERROR;
1208112Smax.romanov@nginx.com                 }
1209112Smax.romanov@nginx.com 
1210112Smax.romanov@nginx.com                 state = saved_state;
1211112Smax.romanov@nginx.com                 goto again;
1212112Smax.romanov@nginx.com             }
1213112Smax.romanov@nginx.com 
1214112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1215112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1216112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1217112Smax.romanov@nginx.com 
1218112Smax.romanov@nginx.com                 if (ch == '?') {
1219112Smax.romanov@nginx.com                     state = sw_normal;
1220112Smax.romanov@nginx.com                     *u++ = ch;
1221112Smax.romanov@nginx.com                     continue;
1222112Smax.romanov@nginx.com 
1223112Smax.romanov@nginx.com                 } else if (ch == '+') {
1224112Smax.romanov@nginx.com                     rp->plus_in_target = 1;
1225112Smax.romanov@nginx.com                 }
1226112Smax.romanov@nginx.com 
1227112Smax.romanov@nginx.com                 state = saved_state;
1228112Smax.romanov@nginx.com                 goto again;
1229112Smax.romanov@nginx.com             }
1230112Smax.romanov@nginx.com 
1231112Smax.romanov@nginx.com             return NXT_ERROR;
1232112Smax.romanov@nginx.com         }
1233112Smax.romanov@nginx.com     }
1234112Smax.romanov@nginx.com 
1235112Smax.romanov@nginx.com     if (state >= sw_quoted) {
1236112Smax.romanov@nginx.com         return NXT_ERROR;
1237112Smax.romanov@nginx.com     }
1238112Smax.romanov@nginx.com 
1239112Smax.romanov@nginx.com args:
1240112Smax.romanov@nginx.com 
1241112Smax.romanov@nginx.com     for (/* void */; p < rp->target_end; p++) {
1242112Smax.romanov@nginx.com         if (*p == '#') {
1243112Smax.romanov@nginx.com             break;
1244112Smax.romanov@nginx.com         }
1245112Smax.romanov@nginx.com     }
1246112Smax.romanov@nginx.com 
1247112Smax.romanov@nginx.com     if (rp->args_start != NULL) {
1248112Smax.romanov@nginx.com         rp->args.length = p - rp->args_start;
1249112Smax.romanov@nginx.com         rp->args.start = rp->args_start;
1250112Smax.romanov@nginx.com     }
1251112Smax.romanov@nginx.com 
1252112Smax.romanov@nginx.com done:
1253112Smax.romanov@nginx.com 
1254112Smax.romanov@nginx.com     rp->path.length = u - rp->path.start;
1255112Smax.romanov@nginx.com 
1256112Smax.romanov@nginx.com     if (rp->exten_start) {
1257112Smax.romanov@nginx.com         rp->exten.length = u - rp->exten_start;
1258112Smax.romanov@nginx.com         rp->exten.start = rp->exten_start;
1259112Smax.romanov@nginx.com     }
1260112Smax.romanov@nginx.com 
1261112Smax.romanov@nginx.com     return NXT_OK;
1262112Smax.romanov@nginx.com }
1263