xref: /unit/src/nxt_http_parse.c (revision 60)
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 {
11*60Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entry;
12*60Svbart@nginx.com 
1316Svbart@nginx.com     union {
14*60Svbart@nginx.com         uint8_t                   str[8];
15*60Svbart@nginx.com         uint64_t                  ui64;
1616Svbart@nginx.com     } key[];
17*60Svbart@nginx.com } nxt_http_fields_hash_elt_t;
1816Svbart@nginx.com 
1916Svbart@nginx.com 
2016Svbart@nginx.com struct nxt_http_fields_hash_s {
2116Svbart@nginx.com     size_t                        min_length;
2216Svbart@nginx.com     size_t                        max_length;
2316Svbart@nginx.com     void                          *long_fields;
24*60Svbart@nginx.com     nxt_http_fields_hash_elt_t    *elts[];
2516Svbart@nginx.com };
2616Svbart@nginx.com 
2716Svbart@nginx.com 
28*60Svbart@nginx.com #define nxt_http_fields_hash_next_elt(elt, n)                                 \
29*60Svbart@nginx.com     ((nxt_http_fields_hash_elt_t *) ((u_char *) (elt)                         \
30*60Svbart@nginx.com                                      + sizeof(nxt_http_fields_hash_elt_t)     \
31*60Svbart@nginx.com                                      + n * 8))
32*60Svbart@nginx.com 
33*60Svbart@nginx.com 
3416Svbart@nginx.com static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp,
3516Svbart@nginx.com     u_char **pos, u_char *end);
3616Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp,
3716Svbart@nginx.com     u_char **pos, u_char *end);
3816Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp,
3916Svbart@nginx.com     u_char **pos, u_char *end);
4016Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp,
4116Svbart@nginx.com     u_char **pos, u_char *end);
4216Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end);
4316Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp,
4416Svbart@nginx.com     u_char **pos, u_char *end);
4516Svbart@nginx.com 
46*60Svbart@nginx.com 
47*60Svbart@nginx.com static nxt_http_fields_hash_entry_t *nxt_http_fields_hash_lookup_long(
48*60Svbart@nginx.com     nxt_http_fields_hash_t *hash, nxt_http_field_t *field);
4916Svbart@nginx.com 
5016Svbart@nginx.com 
5116Svbart@nginx.com typedef enum {
5216Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
5316Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
5416Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
5516Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
5616Svbart@nginx.com 
5716Svbart@nginx.com     /* traps below are used for extended check only */
5816Svbart@nginx.com 
5916Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
6016Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
6116Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
6216Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
6316Svbart@nginx.com     NXT_HTTP_TARGET_PLUS,        /*  +  */
6416Svbart@nginx.com } nxt_http_target_traps_e;
6516Svbart@nginx.com 
6616Svbart@nginx.com 
6716Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
6816Svbart@nginx.com     /* \0                               \n        \r       */
6916Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
7016Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
7152Svbart@nginx.com 
7252Svbart@nginx.com     /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
7316Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 9,  0, 0, 6, 5,
7452Svbart@nginx.com 
7552Svbart@nginx.com     /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
7616Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
7716Svbart@nginx.com };
7816Svbart@nginx.com 
7916Svbart@nginx.com 
8016Svbart@nginx.com nxt_inline nxt_http_target_traps_e
8116Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
8216Svbart@nginx.com {
8316Svbart@nginx.com     u_char      *p;
8416Svbart@nginx.com     nxt_uint_t  trap;
8516Svbart@nginx.com 
8616Svbart@nginx.com     p = *pos;
8716Svbart@nginx.com 
8816Svbart@nginx.com     for ( ;; ) {
8916Svbart@nginx.com         if (nxt_slow_path(end - p < 10)) {
9016Svbart@nginx.com             return NXT_HTTP_TARGET_AGAIN;
9116Svbart@nginx.com         }
9216Svbart@nginx.com 
9316Svbart@nginx.com #define nxt_http_parse_target_step                                            \
9416Svbart@nginx.com         {                                                                     \
9516Svbart@nginx.com             trap = nxt_http_target_chars[*p];                                 \
9616Svbart@nginx.com                                                                               \
9716Svbart@nginx.com             if (nxt_slow_path(trap != 0)) {                                   \
9816Svbart@nginx.com                 break;                                                        \
9916Svbart@nginx.com             }                                                                 \
10016Svbart@nginx.com                                                                               \
10116Svbart@nginx.com             p++;                                                              \
10216Svbart@nginx.com         }
10316Svbart@nginx.com 
10416Svbart@nginx.com         nxt_http_parse_target_step
10516Svbart@nginx.com         nxt_http_parse_target_step
10616Svbart@nginx.com         nxt_http_parse_target_step
10716Svbart@nginx.com         nxt_http_parse_target_step
10816Svbart@nginx.com 
10916Svbart@nginx.com         nxt_http_parse_target_step
11016Svbart@nginx.com         nxt_http_parse_target_step
11116Svbart@nginx.com         nxt_http_parse_target_step
11216Svbart@nginx.com         nxt_http_parse_target_step
11316Svbart@nginx.com 
11416Svbart@nginx.com         nxt_http_parse_target_step
11516Svbart@nginx.com         nxt_http_parse_target_step
11616Svbart@nginx.com 
11716Svbart@nginx.com #undef nxt_http_parse_target_step
11816Svbart@nginx.com     }
11916Svbart@nginx.com 
12016Svbart@nginx.com     *pos = p;
12116Svbart@nginx.com 
12216Svbart@nginx.com     return trap;
12316Svbart@nginx.com }
12416Svbart@nginx.com 
12516Svbart@nginx.com 
12616Svbart@nginx.com nxt_int_t
12716Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
12816Svbart@nginx.com {
12916Svbart@nginx.com     nxt_int_t  rc;
13016Svbart@nginx.com 
13116Svbart@nginx.com     if (rp->handler == NULL) {
13216Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13316Svbart@nginx.com     }
13416Svbart@nginx.com 
13516Svbart@nginx.com     do {
13616Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
13716Svbart@nginx.com     } while (rc == NXT_OK);
13816Svbart@nginx.com 
13916Svbart@nginx.com     return rc;
14016Svbart@nginx.com }
14116Svbart@nginx.com 
14216Svbart@nginx.com 
14316Svbart@nginx.com static nxt_int_t
14416Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
14516Svbart@nginx.com     u_char *end)
14616Svbart@nginx.com {
14716Svbart@nginx.com     u_char                   *p, ch, *after_slash;
14816Svbart@nginx.com     nxt_int_t                rc;
14916Svbart@nginx.com     nxt_http_ver_t           version;
15016Svbart@nginx.com     nxt_http_target_traps_e  trap;
15116Svbart@nginx.com 
15216Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
15316Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
15416Svbart@nginx.com 
15516Svbart@nginx.com     p = *pos;
15616Svbart@nginx.com 
15716Svbart@nginx.com     rp->method.start = p;
15816Svbart@nginx.com 
15916Svbart@nginx.com     for ( ;; p++) {
16016Svbart@nginx.com 
16116Svbart@nginx.com         for ( ;; ) {
16216Svbart@nginx.com             if (nxt_slow_path(end - p < 12)) {
16316Svbart@nginx.com                 return NXT_AGAIN;
16416Svbart@nginx.com             }
16516Svbart@nginx.com 
16616Svbart@nginx.com #define nxt_http_parse_request_line_step                                      \
16716Svbart@nginx.com             {                                                                 \
16816Svbart@nginx.com                 ch = *p;                                                      \
16916Svbart@nginx.com                                                                               \
17016Svbart@nginx.com                 if (nxt_slow_path(ch < 'A' || ch > 'Z')) {                    \
17116Svbart@nginx.com                     break;                                                    \
17216Svbart@nginx.com                 }                                                             \
17316Svbart@nginx.com                                                                               \
17416Svbart@nginx.com                 p++;                                                          \
17516Svbart@nginx.com             }
17616Svbart@nginx.com 
17716Svbart@nginx.com             nxt_http_parse_request_line_step
17816Svbart@nginx.com             nxt_http_parse_request_line_step
17916Svbart@nginx.com             nxt_http_parse_request_line_step
18016Svbart@nginx.com             nxt_http_parse_request_line_step
18116Svbart@nginx.com 
18216Svbart@nginx.com             nxt_http_parse_request_line_step
18316Svbart@nginx.com             nxt_http_parse_request_line_step
18416Svbart@nginx.com             nxt_http_parse_request_line_step
18516Svbart@nginx.com             nxt_http_parse_request_line_step
18616Svbart@nginx.com 
18716Svbart@nginx.com #undef nxt_http_parse_request_line_step
18816Svbart@nginx.com         }
18916Svbart@nginx.com 
19016Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
19116Svbart@nginx.com             rp->method.length = p - rp->method.start;
19216Svbart@nginx.com             break;
19316Svbart@nginx.com         }
19416Svbart@nginx.com 
19516Svbart@nginx.com         if (ch == '_' || ch == '-') {
19616Svbart@nginx.com             continue;
19716Svbart@nginx.com         }
19816Svbart@nginx.com 
19916Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
20016Svbart@nginx.com             rp->method.start++;
20116Svbart@nginx.com             continue;
20216Svbart@nginx.com         }
20316Svbart@nginx.com 
20416Svbart@nginx.com         return NXT_ERROR;
20516Svbart@nginx.com     }
20616Svbart@nginx.com 
20716Svbart@nginx.com     p++;
20816Svbart@nginx.com 
20916Svbart@nginx.com     if (nxt_slow_path(p == end)) {
21016Svbart@nginx.com         return NXT_AGAIN;
21116Svbart@nginx.com     }
21216Svbart@nginx.com 
21316Svbart@nginx.com     /* target */
21416Svbart@nginx.com 
21516Svbart@nginx.com     ch = *p;
21616Svbart@nginx.com 
21716Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
21816Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
21916Svbart@nginx.com 
22016Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
22116Svbart@nginx.com             return rc;
22216Svbart@nginx.com         }
22316Svbart@nginx.com     }
22416Svbart@nginx.com 
22516Svbart@nginx.com     rp->target_start = p;
22616Svbart@nginx.com 
22716Svbart@nginx.com     after_slash = p + 1;
22816Svbart@nginx.com 
22916Svbart@nginx.com     for ( ;; ) {
23016Svbart@nginx.com         p++;
23116Svbart@nginx.com 
23216Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
23316Svbart@nginx.com 
23416Svbart@nginx.com         switch (trap) {
23516Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
23616Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
23716Svbart@nginx.com                 rp->complex_target = 1;
23816Svbart@nginx.com                 goto rest_of_target;
23916Svbart@nginx.com             }
24016Svbart@nginx.com 
24116Svbart@nginx.com             after_slash = p + 1;
24216Svbart@nginx.com 
24316Svbart@nginx.com             rp->exten_start = NULL;
24416Svbart@nginx.com             continue;
24516Svbart@nginx.com 
24616Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
24716Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
24816Svbart@nginx.com                 rp->complex_target = 1;
24916Svbart@nginx.com                 goto rest_of_target;
25016Svbart@nginx.com             }
25116Svbart@nginx.com 
25216Svbart@nginx.com             rp->exten_start = p + 1;
25316Svbart@nginx.com             continue;
25416Svbart@nginx.com 
25516Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
25616Svbart@nginx.com             rp->args_start = p + 1;
25716Svbart@nginx.com             goto rest_of_target;
25816Svbart@nginx.com 
25916Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
26016Svbart@nginx.com             rp->target_end = p;
26116Svbart@nginx.com             goto space_after_target;
26216Svbart@nginx.com 
26316Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
26416Svbart@nginx.com             rp->quoted_target = 1;
26516Svbart@nginx.com             goto rest_of_target;
26616Svbart@nginx.com 
26716Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
26816Svbart@nginx.com             rp->plus_in_target = 1;
26916Svbart@nginx.com             continue;
27016Svbart@nginx.com 
27116Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
27216Svbart@nginx.com             rp->complex_target = 1;
27316Svbart@nginx.com             goto rest_of_target;
27416Svbart@nginx.com 
27516Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
27616Svbart@nginx.com             return NXT_AGAIN;
27716Svbart@nginx.com 
27816Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
27916Svbart@nginx.com             return NXT_ERROR;
28016Svbart@nginx.com         }
28116Svbart@nginx.com 
28216Svbart@nginx.com         nxt_unreachable();
28316Svbart@nginx.com     }
28416Svbart@nginx.com 
28516Svbart@nginx.com rest_of_target:
28616Svbart@nginx.com 
28716Svbart@nginx.com     for ( ;; ) {
28816Svbart@nginx.com         p++;
28916Svbart@nginx.com 
29019Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
29116Svbart@nginx.com 
29216Svbart@nginx.com         switch (trap) {
29316Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
29416Svbart@nginx.com             rp->target_end = p;
29516Svbart@nginx.com             goto space_after_target;
29616Svbart@nginx.com 
29716Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
29816Svbart@nginx.com             rp->complex_target = 1;
29916Svbart@nginx.com             continue;
30016Svbart@nginx.com 
30116Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
30216Svbart@nginx.com             return NXT_AGAIN;
30316Svbart@nginx.com 
30416Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
30516Svbart@nginx.com             return NXT_ERROR;
30616Svbart@nginx.com 
30716Svbart@nginx.com         default:
30816Svbart@nginx.com             continue;
30916Svbart@nginx.com         }
31016Svbart@nginx.com 
31116Svbart@nginx.com         nxt_unreachable();
31216Svbart@nginx.com     }
31316Svbart@nginx.com 
31416Svbart@nginx.com space_after_target:
31516Svbart@nginx.com 
31616Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
31716Svbart@nginx.com         return NXT_AGAIN;
31816Svbart@nginx.com     }
31916Svbart@nginx.com 
32016Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
32116Svbart@nginx.com 
32216Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
32316Svbart@nginx.com 
32416Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
32516Svbart@nginx.com                        || version.ui64 == http10.ui64
32616Svbart@nginx.com                        || (p[1] == 'H'
32716Svbart@nginx.com                            && p[2] == 'T'
32816Svbart@nginx.com                            && p[3] == 'T'
32916Svbart@nginx.com                            && p[4] == 'P'
33016Svbart@nginx.com                            && p[5] == '/'
33116Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
33216Svbart@nginx.com                            && p[7] == '.'
33316Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
33416Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
33516Svbart@nginx.com     {
33616Svbart@nginx.com         rp->version.ui64 = version.ui64;
33716Svbart@nginx.com 
33816Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
33916Svbart@nginx.com             p += 10;
34016Svbart@nginx.com 
34116Svbart@nginx.com             if (nxt_slow_path(p == end)) {
34216Svbart@nginx.com                 return NXT_AGAIN;
34316Svbart@nginx.com             }
34416Svbart@nginx.com 
34516Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
34616Svbart@nginx.com                 return NXT_ERROR;
34716Svbart@nginx.com             }
34816Svbart@nginx.com 
34916Svbart@nginx.com             *pos = p + 1;
35016Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
35116Svbart@nginx.com         }
35216Svbart@nginx.com 
35316Svbart@nginx.com         *pos = p + 10;
35416Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
35516Svbart@nginx.com     }
35616Svbart@nginx.com 
35716Svbart@nginx.com     if (p[1] == ' ') {
35816Svbart@nginx.com         /* surplus space after tartet */
35916Svbart@nginx.com         p++;
36016Svbart@nginx.com         goto space_after_target;
36116Svbart@nginx.com     }
36216Svbart@nginx.com 
36316Svbart@nginx.com     rp->space_in_target = 1;
36416Svbart@nginx.com     goto rest_of_target;
36516Svbart@nginx.com }
36616Svbart@nginx.com 
36716Svbart@nginx.com 
36816Svbart@nginx.com static nxt_int_t
36916Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
37016Svbart@nginx.com     u_char *end)
37116Svbart@nginx.com {
37216Svbart@nginx.com     u_char  *p, ch;
37316Svbart@nginx.com 
37416Svbart@nginx.com     p = *pos;
37516Svbart@nginx.com 
37616Svbart@nginx.com     ch = *p;
37716Svbart@nginx.com 
37816Svbart@nginx.com     if (ch == ' ') {
37916Svbart@nginx.com         /* skip surplus spaces before target */
38016Svbart@nginx.com 
38116Svbart@nginx.com         do {
38216Svbart@nginx.com             p++;
38316Svbart@nginx.com 
38416Svbart@nginx.com             if (nxt_slow_path(p == end)) {
38516Svbart@nginx.com                 return NXT_AGAIN;
38616Svbart@nginx.com             }
38716Svbart@nginx.com 
38816Svbart@nginx.com             ch = *p;
38916Svbart@nginx.com 
39016Svbart@nginx.com         } while (ch == ' ');
39116Svbart@nginx.com 
39216Svbart@nginx.com         if (ch == '/') {
39316Svbart@nginx.com             *pos = p;
39416Svbart@nginx.com             return NXT_OK;
39516Svbart@nginx.com         }
39616Svbart@nginx.com     }
39716Svbart@nginx.com 
39816Svbart@nginx.com     /* absolute path or '*' */
39916Svbart@nginx.com 
40016Svbart@nginx.com     /* TODO */
40116Svbart@nginx.com 
40216Svbart@nginx.com     return NXT_ERROR;
40316Svbart@nginx.com }
40416Svbart@nginx.com 
40516Svbart@nginx.com 
40616Svbart@nginx.com static nxt_int_t
40716Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
40816Svbart@nginx.com     u_char *end)
40916Svbart@nginx.com {
41016Svbart@nginx.com     u_char  *p, ch, c;
41116Svbart@nginx.com     size_t  i, size;
41216Svbart@nginx.com 
41316Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
41416Svbart@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"
41516Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
41616Svbart@nginx.com 
41716Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
41816Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
41916Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
42016Svbart@nginx.com 
42116Svbart@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"
42216Svbart@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"
42316Svbart@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"
42416Svbart@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";
42516Svbart@nginx.com 
42616Svbart@nginx.com     p = *pos;
42716Svbart@nginx.com 
42816Svbart@nginx.com     size = end - p;
429*60Svbart@nginx.com     i = rp->field.name.length;
43016Svbart@nginx.com 
43119Svbart@nginx.com #define nxt_http_parse_field_name_step                                        \
43219Svbart@nginx.com     {                                                                         \
43319Svbart@nginx.com         ch = p[i];                                                            \
43419Svbart@nginx.com         c = normal[ch];                                                       \
43519Svbart@nginx.com                                                                               \
43619Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
43719Svbart@nginx.com             goto name_end;                                                    \
43819Svbart@nginx.com         }                                                                     \
43919Svbart@nginx.com                                                                               \
440*60Svbart@nginx.com         rp->field.key.str[i % 32] = c;                                        \
44119Svbart@nginx.com         i++;                                                                  \
44219Svbart@nginx.com     }
44316Svbart@nginx.com 
44419Svbart@nginx.com     while (nxt_fast_path(size - i >= 8)) {
44519Svbart@nginx.com         nxt_http_parse_field_name_step
44619Svbart@nginx.com         nxt_http_parse_field_name_step
44719Svbart@nginx.com         nxt_http_parse_field_name_step
44819Svbart@nginx.com         nxt_http_parse_field_name_step
44916Svbart@nginx.com 
45019Svbart@nginx.com         nxt_http_parse_field_name_step
45119Svbart@nginx.com         nxt_http_parse_field_name_step
45219Svbart@nginx.com         nxt_http_parse_field_name_step
45319Svbart@nginx.com         nxt_http_parse_field_name_step
45419Svbart@nginx.com     }
45516Svbart@nginx.com 
45619Svbart@nginx.com     while (nxt_fast_path(i != size)) {
45719Svbart@nginx.com         nxt_http_parse_field_name_step
45819Svbart@nginx.com     }
45916Svbart@nginx.com 
46019Svbart@nginx.com #undef nxt_http_parse_field_name_step
46116Svbart@nginx.com 
462*60Svbart@nginx.com     rp->field.name.length = i;
46316Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
46416Svbart@nginx.com 
46516Svbart@nginx.com     return NXT_AGAIN;
46619Svbart@nginx.com 
46719Svbart@nginx.com name_end:
46819Svbart@nginx.com 
46919Svbart@nginx.com     if (nxt_fast_path(ch == ':')) {
47019Svbart@nginx.com         if (nxt_slow_path(i == 0)) {
47119Svbart@nginx.com             return NXT_ERROR;
47219Svbart@nginx.com         }
47319Svbart@nginx.com 
47419Svbart@nginx.com         *pos = &p[i] + 1;
47519Svbart@nginx.com 
476*60Svbart@nginx.com         rp->field.name.length = i;
477*60Svbart@nginx.com         rp->field.name.start = p;
47819Svbart@nginx.com 
47919Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
48019Svbart@nginx.com     }
48119Svbart@nginx.com 
48259Svbart@nginx.com     if (nxt_slow_path(i != 0)) {
48359Svbart@nginx.com         return NXT_ERROR;
48459Svbart@nginx.com     }
48519Svbart@nginx.com 
48619Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
48716Svbart@nginx.com }
48816Svbart@nginx.com 
48916Svbart@nginx.com 
49016Svbart@nginx.com static nxt_int_t
49116Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
49216Svbart@nginx.com     u_char *end)
49316Svbart@nginx.com {
49416Svbart@nginx.com     u_char  *p, ch;
49516Svbart@nginx.com 
49616Svbart@nginx.com     p = *pos;
49716Svbart@nginx.com 
49816Svbart@nginx.com     for ( ;; ) {
49916Svbart@nginx.com         if (nxt_slow_path(p == end)) {
50016Svbart@nginx.com             *pos = p;
50116Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
50216Svbart@nginx.com             return NXT_AGAIN;
50316Svbart@nginx.com         }
50416Svbart@nginx.com 
50516Svbart@nginx.com         if (*p != ' ') {
50616Svbart@nginx.com             break;
50716Svbart@nginx.com         }
50816Svbart@nginx.com 
50916Svbart@nginx.com         p++;
51016Svbart@nginx.com     }
51116Svbart@nginx.com 
51216Svbart@nginx.com     *pos = p;
51316Svbart@nginx.com 
514*60Svbart@nginx.com     p += rp->field.value.length;
51516Svbart@nginx.com 
51616Svbart@nginx.com     for ( ;; ) {
51716Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
51816Svbart@nginx.com 
51916Svbart@nginx.com         if (nxt_slow_path(p == end)) {
520*60Svbart@nginx.com             rp->field.value.length = p - *pos;
52116Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
52216Svbart@nginx.com             return NXT_AGAIN;
52316Svbart@nginx.com         }
52416Svbart@nginx.com 
52516Svbart@nginx.com         ch = *p;
52616Svbart@nginx.com 
52716Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
52816Svbart@nginx.com             break;
52916Svbart@nginx.com         }
53016Svbart@nginx.com 
53116Svbart@nginx.com         if (ch == '\0') {
53216Svbart@nginx.com             return NXT_ERROR;
53316Svbart@nginx.com         }
53416Svbart@nginx.com     }
53516Svbart@nginx.com 
53616Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
53716Svbart@nginx.com         while (p[-1] == ' ') {
53816Svbart@nginx.com             p--;
53916Svbart@nginx.com         }
54016Svbart@nginx.com     }
54116Svbart@nginx.com 
542*60Svbart@nginx.com     rp->field.value.length = p - *pos;
543*60Svbart@nginx.com     rp->field.value.start = *pos;
54416Svbart@nginx.com 
54516Svbart@nginx.com     *pos = p;
54616Svbart@nginx.com 
54716Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
54816Svbart@nginx.com }
54916Svbart@nginx.com 
55016Svbart@nginx.com 
55116Svbart@nginx.com static u_char *
55216Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
55316Svbart@nginx.com {
55416Svbart@nginx.com     nxt_uint_t  n;
55516Svbart@nginx.com 
55616Svbart@nginx.com #define nxt_http_lookup_field_end_step                                        \
55716Svbart@nginx.com     {                                                                         \
55819Svbart@nginx.com         if (nxt_slow_path(*p < 0x10)) {                                       \
55916Svbart@nginx.com             return p;                                                         \
56016Svbart@nginx.com         }                                                                     \
56116Svbart@nginx.com                                                                               \
56216Svbart@nginx.com         p++;                                                                  \
56316Svbart@nginx.com     }
56416Svbart@nginx.com 
56519Svbart@nginx.com     for (n = (end - p) / 16; nxt_fast_path(n != 0); n--) {
56619Svbart@nginx.com         nxt_http_lookup_field_end_step
56719Svbart@nginx.com         nxt_http_lookup_field_end_step
56819Svbart@nginx.com         nxt_http_lookup_field_end_step
56919Svbart@nginx.com         nxt_http_lookup_field_end_step
57019Svbart@nginx.com 
57119Svbart@nginx.com         nxt_http_lookup_field_end_step
57219Svbart@nginx.com         nxt_http_lookup_field_end_step
57319Svbart@nginx.com         nxt_http_lookup_field_end_step
57419Svbart@nginx.com         nxt_http_lookup_field_end_step
57519Svbart@nginx.com 
57616Svbart@nginx.com         nxt_http_lookup_field_end_step
57716Svbart@nginx.com         nxt_http_lookup_field_end_step
57816Svbart@nginx.com         nxt_http_lookup_field_end_step
57916Svbart@nginx.com         nxt_http_lookup_field_end_step
58016Svbart@nginx.com 
58116Svbart@nginx.com         nxt_http_lookup_field_end_step
58216Svbart@nginx.com         nxt_http_lookup_field_end_step
58316Svbart@nginx.com         nxt_http_lookup_field_end_step
58416Svbart@nginx.com         nxt_http_lookup_field_end_step
58516Svbart@nginx.com     }
58616Svbart@nginx.com 
58719Svbart@nginx.com     for (n = (end - p) / 4; nxt_fast_path(n != 0); n--) {
58816Svbart@nginx.com         nxt_http_lookup_field_end_step
58919Svbart@nginx.com         nxt_http_lookup_field_end_step
59016Svbart@nginx.com         nxt_http_lookup_field_end_step
59116Svbart@nginx.com         nxt_http_lookup_field_end_step
59219Svbart@nginx.com     }
59319Svbart@nginx.com 
59419Svbart@nginx.com     switch (end - p) {
59516Svbart@nginx.com     case 3:
59616Svbart@nginx.com         nxt_http_lookup_field_end_step
59739Svbart@nginx.com         /* Fall through. */
59816Svbart@nginx.com     case 2:
59916Svbart@nginx.com         nxt_http_lookup_field_end_step
60039Svbart@nginx.com         /* Fall through. */
60116Svbart@nginx.com     case 1:
60216Svbart@nginx.com         nxt_http_lookup_field_end_step
60339Svbart@nginx.com         /* Fall through. */
60416Svbart@nginx.com     case 0:
60516Svbart@nginx.com         break;
60616Svbart@nginx.com     default:
60716Svbart@nginx.com         nxt_unreachable();
60816Svbart@nginx.com     }
60916Svbart@nginx.com 
61016Svbart@nginx.com #undef nxt_http_lookup_field_end_step
61116Svbart@nginx.com 
61216Svbart@nginx.com     return p;
61316Svbart@nginx.com }
61416Svbart@nginx.com 
61516Svbart@nginx.com 
61616Svbart@nginx.com static nxt_int_t
61716Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
61816Svbart@nginx.com     u_char *end)
61916Svbart@nginx.com {
620*60Svbart@nginx.com     u_char            *p;
621*60Svbart@nginx.com     nxt_http_field_t  *field;
62216Svbart@nginx.com 
62316Svbart@nginx.com     p = *pos;
62416Svbart@nginx.com 
62516Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
62616Svbart@nginx.com         p++;
62716Svbart@nginx.com 
62816Svbart@nginx.com         if (nxt_slow_path(p == end)) {
62916Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
63016Svbart@nginx.com             return NXT_AGAIN;
63116Svbart@nginx.com         }
63216Svbart@nginx.com     }
63316Svbart@nginx.com 
63416Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
63516Svbart@nginx.com         *pos = p + 1;
63616Svbart@nginx.com 
637*60Svbart@nginx.com         if (rp->field.name.length != 0) {
638*60Svbart@nginx.com             field = nxt_list_add(rp->fields);
63916Svbart@nginx.com 
640*60Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
641*60Svbart@nginx.com                 return NXT_ERROR;
64216Svbart@nginx.com             }
64316Svbart@nginx.com 
644*60Svbart@nginx.com             *field = rp->field;
645*60Svbart@nginx.com 
646*60Svbart@nginx.com             nxt_memzero(&rp->field, sizeof(nxt_http_field_t));
64716Svbart@nginx.com 
64816Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
64916Svbart@nginx.com             return NXT_OK;
65016Svbart@nginx.com         }
65116Svbart@nginx.com 
65216Svbart@nginx.com         return NXT_DONE;
65316Svbart@nginx.com     }
65416Svbart@nginx.com 
65516Svbart@nginx.com     return NXT_ERROR;
65616Svbart@nginx.com }
65716Svbart@nginx.com 
65816Svbart@nginx.com 
659*60Svbart@nginx.com nxt_http_fields_hash_t *
660*60Svbart@nginx.com nxt_http_fields_hash_create(nxt_http_fields_hash_entry_t *entries,
661*60Svbart@nginx.com     nxt_mem_pool_t *mp)
66216Svbart@nginx.com {
663*60Svbart@nginx.com     size_t                      min_length, max_length, length, size;
664*60Svbart@nginx.com     nxt_uint_t                  i, j, n;
665*60Svbart@nginx.com     nxt_http_fields_hash_t      *hash;
666*60Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
66716Svbart@nginx.com 
66838Svbart@nginx.com     min_length = 32 + 1;
66916Svbart@nginx.com     max_length = 0;
67016Svbart@nginx.com 
671*60Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
672*60Svbart@nginx.com         length = entries[i].name.length;
67316Svbart@nginx.com 
67416Svbart@nginx.com         if (length > 32) {
67516Svbart@nginx.com             /* TODO */
67616Svbart@nginx.com             return NULL;
67716Svbart@nginx.com         }
67816Svbart@nginx.com 
67916Svbart@nginx.com         min_length = nxt_min(length, min_length);
68016Svbart@nginx.com         max_length = nxt_max(length, max_length);
68116Svbart@nginx.com     }
68216Svbart@nginx.com 
68338Svbart@nginx.com     size = sizeof(nxt_http_fields_hash_t);
68416Svbart@nginx.com 
68538Svbart@nginx.com     if (min_length <= 32) {
68638Svbart@nginx.com         size += (max_length - min_length + 1)
687*60Svbart@nginx.com                 * sizeof(nxt_http_fields_hash_elt_t *);
68838Svbart@nginx.com     }
68916Svbart@nginx.com 
69038Svbart@nginx.com     hash = nxt_mem_zalloc(mp, size);
69116Svbart@nginx.com     if (nxt_slow_path(hash == NULL)) {
69216Svbart@nginx.com         return NULL;
69316Svbart@nginx.com     }
69416Svbart@nginx.com 
69516Svbart@nginx.com     hash->min_length = min_length;
69616Svbart@nginx.com     hash->max_length = max_length;
69716Svbart@nginx.com 
698*60Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
699*60Svbart@nginx.com         length = entries[i].name.length;
700*60Svbart@nginx.com         elt = hash->elts[length - min_length];
70116Svbart@nginx.com 
702*60Svbart@nginx.com         if (elt != NULL) {
70316Svbart@nginx.com             continue;
70416Svbart@nginx.com         }
70516Svbart@nginx.com 
70616Svbart@nginx.com         n = 1;
70716Svbart@nginx.com 
708*60Svbart@nginx.com         for (j = i + 1; entries[j].handler != NULL; j++) {
709*60Svbart@nginx.com             if (length == entries[j].name.length) {
71016Svbart@nginx.com                 n++;
71116Svbart@nginx.com             }
71216Svbart@nginx.com         }
71316Svbart@nginx.com 
714*60Svbart@nginx.com         size = sizeof(nxt_http_fields_hash_elt_t) + nxt_align_size(length, 8);
71516Svbart@nginx.com 
716*60Svbart@nginx.com         elt = nxt_mem_zalloc(mp, n * size
717*60Svbart@nginx.com                                  + sizeof(nxt_http_fields_hash_elt_t));
71816Svbart@nginx.com 
719*60Svbart@nginx.com         if (nxt_slow_path(elt == NULL)) {
72016Svbart@nginx.com             return NULL;
72116Svbart@nginx.com         }
72216Svbart@nginx.com 
723*60Svbart@nginx.com         hash->elts[length - min_length] = elt;
72416Svbart@nginx.com 
725*60Svbart@nginx.com         for (j = i; entries[j].handler != NULL; j++) {
726*60Svbart@nginx.com             if (length != entries[j].name.length) {
72716Svbart@nginx.com                 continue;
72816Svbart@nginx.com             }
72916Svbart@nginx.com 
730*60Svbart@nginx.com             elt->entry = &entries[j];
73116Svbart@nginx.com 
732*60Svbart@nginx.com             nxt_memcpy_lowcase(elt->key->str, entries[j].name.start, length);
73316Svbart@nginx.com 
73416Svbart@nginx.com             n--;
73516Svbart@nginx.com 
73616Svbart@nginx.com             if (n == 0) {
73716Svbart@nginx.com                 break;
73816Svbart@nginx.com             }
73916Svbart@nginx.com 
740*60Svbart@nginx.com             elt = (nxt_http_fields_hash_elt_t *) ((u_char *) elt + size);
74116Svbart@nginx.com         }
74216Svbart@nginx.com     }
74316Svbart@nginx.com 
74416Svbart@nginx.com     return hash;
74516Svbart@nginx.com }
746*60Svbart@nginx.com 
747*60Svbart@nginx.com 
748*60Svbart@nginx.com nxt_http_fields_hash_entry_t *
749*60Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash,
750*60Svbart@nginx.com     nxt_http_field_t *field)
751*60Svbart@nginx.com {
752*60Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
753*60Svbart@nginx.com 
754*60Svbart@nginx.com     if (field->name.length < hash->min_length) {
755*60Svbart@nginx.com         return NULL;
756*60Svbart@nginx.com     }
757*60Svbart@nginx.com 
758*60Svbart@nginx.com     if (field->name.length > hash->max_length) {
759*60Svbart@nginx.com 
760*60Svbart@nginx.com         if (field->name.length > 32 && hash->long_fields != NULL) {
761*60Svbart@nginx.com             return nxt_http_fields_hash_lookup_long(hash, field);
762*60Svbart@nginx.com         }
763*60Svbart@nginx.com 
764*60Svbart@nginx.com         return NULL;
765*60Svbart@nginx.com     }
766*60Svbart@nginx.com 
767*60Svbart@nginx.com     elt = hash->elts[field->name.length - hash->min_length];
768*60Svbart@nginx.com 
769*60Svbart@nginx.com     if (elt == NULL) {
770*60Svbart@nginx.com         return NULL;
771*60Svbart@nginx.com     }
772*60Svbart@nginx.com 
773*60Svbart@nginx.com     switch ((field->name.length + 7) / 8) {
774*60Svbart@nginx.com     case 1:
775*60Svbart@nginx.com         do {
776*60Svbart@nginx.com             if (elt->key[0].ui64 == field->key.ui64[0]) {
777*60Svbart@nginx.com                 return elt->entry;
778*60Svbart@nginx.com             }
779*60Svbart@nginx.com 
780*60Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 1);
781*60Svbart@nginx.com 
782*60Svbart@nginx.com         } while (elt->entry != NULL);
783*60Svbart@nginx.com 
784*60Svbart@nginx.com         break;
785*60Svbart@nginx.com 
786*60Svbart@nginx.com     case 2:
787*60Svbart@nginx.com         do {
788*60Svbart@nginx.com             if (elt->key[0].ui64 == field->key.ui64[0]
789*60Svbart@nginx.com                 && elt->key[1].ui64 == field->key.ui64[1])
790*60Svbart@nginx.com             {
791*60Svbart@nginx.com                 return elt->entry;
792*60Svbart@nginx.com             }
793*60Svbart@nginx.com 
794*60Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 2);
795*60Svbart@nginx.com 
796*60Svbart@nginx.com         } while (elt->entry != NULL);
797*60Svbart@nginx.com 
798*60Svbart@nginx.com         break;
799*60Svbart@nginx.com 
800*60Svbart@nginx.com     case 3:
801*60Svbart@nginx.com         do {
802*60Svbart@nginx.com             if (elt->key[0].ui64 == field->key.ui64[0]
803*60Svbart@nginx.com                 && elt->key[1].ui64 == field->key.ui64[1]
804*60Svbart@nginx.com                 && elt->key[2].ui64 == field->key.ui64[2])
805*60Svbart@nginx.com             {
806*60Svbart@nginx.com                 return elt->entry;
807*60Svbart@nginx.com             }
808*60Svbart@nginx.com 
809*60Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 3);
810*60Svbart@nginx.com 
811*60Svbart@nginx.com         } while (elt->entry != NULL);
812*60Svbart@nginx.com 
813*60Svbart@nginx.com         break;
814*60Svbart@nginx.com 
815*60Svbart@nginx.com     case 4:
816*60Svbart@nginx.com         do {
817*60Svbart@nginx.com             if (elt->key[0].ui64 == field->key.ui64[0]
818*60Svbart@nginx.com                 && elt->key[1].ui64 == field->key.ui64[1]
819*60Svbart@nginx.com                 && elt->key[2].ui64 == field->key.ui64[2]
820*60Svbart@nginx.com                 && elt->key[3].ui64 == field->key.ui64[3])
821*60Svbart@nginx.com             {
822*60Svbart@nginx.com                 return elt->entry;
823*60Svbart@nginx.com             }
824*60Svbart@nginx.com 
825*60Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 4);
826*60Svbart@nginx.com 
827*60Svbart@nginx.com         } while (elt->entry != NULL);
828*60Svbart@nginx.com 
829*60Svbart@nginx.com         break;
830*60Svbart@nginx.com 
831*60Svbart@nginx.com     default:
832*60Svbart@nginx.com         nxt_unreachable();
833*60Svbart@nginx.com     }
834*60Svbart@nginx.com 
835*60Svbart@nginx.com     return NULL;
836*60Svbart@nginx.com }
837*60Svbart@nginx.com 
838*60Svbart@nginx.com 
839*60Svbart@nginx.com static nxt_http_fields_hash_entry_t *
840*60Svbart@nginx.com nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
841*60Svbart@nginx.com     nxt_http_field_t *field)
842*60Svbart@nginx.com {
843*60Svbart@nginx.com     /* TODO */
844*60Svbart@nginx.com     return NULL;
845*60Svbart@nginx.com }
846*60Svbart@nginx.com 
847*60Svbart@nginx.com 
848*60Svbart@nginx.com nxt_int_t
849*60Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, nxt_http_fields_hash_t *hash,
850*60Svbart@nginx.com     void *ctx, nxt_log_t *log)
851*60Svbart@nginx.com {
852*60Svbart@nginx.com     nxt_int_t                     rc;
853*60Svbart@nginx.com     nxt_http_field_t              *field;
854*60Svbart@nginx.com     nxt_http_fields_hash_entry_t  *entry;
855*60Svbart@nginx.com 
856*60Svbart@nginx.com     nxt_list_each(field, fields) {
857*60Svbart@nginx.com         entry = nxt_http_fields_hash_lookup(hash, field);
858*60Svbart@nginx.com 
859*60Svbart@nginx.com         if (entry != NULL) {
860*60Svbart@nginx.com             rc = entry->handler(ctx, field, entry->data, log);
861*60Svbart@nginx.com 
862*60Svbart@nginx.com             if (rc != NXT_OK) {
863*60Svbart@nginx.com                 return rc;
864*60Svbart@nginx.com             }
865*60Svbart@nginx.com         }
866*60Svbart@nginx.com 
867*60Svbart@nginx.com     } nxt_list_loop;
868*60Svbart@nginx.com 
869*60Svbart@nginx.com     return NXT_OK;
870*60Svbart@nginx.com }
871