xref: /unit/src/nxt_http_parse.c (revision 112)
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 
52*112Smax.romanov@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp);
53*112Smax.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 
9216Svbart@nginx.com     for ( ;; ) {
9316Svbart@nginx.com         if (nxt_slow_path(end - p < 10)) {
9416Svbart@nginx.com             return NXT_HTTP_TARGET_AGAIN;
9516Svbart@nginx.com         }
9616Svbart@nginx.com 
9716Svbart@nginx.com #define nxt_http_parse_target_step                                            \
9816Svbart@nginx.com         {                                                                     \
9916Svbart@nginx.com             trap = nxt_http_target_chars[*p];                                 \
10016Svbart@nginx.com                                                                               \
10116Svbart@nginx.com             if (nxt_slow_path(trap != 0)) {                                   \
10216Svbart@nginx.com                 break;                                                        \
10316Svbart@nginx.com             }                                                                 \
10416Svbart@nginx.com                                                                               \
10516Svbart@nginx.com             p++;                                                              \
10616Svbart@nginx.com         }
10716Svbart@nginx.com 
10816Svbart@nginx.com         nxt_http_parse_target_step
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 
11316Svbart@nginx.com         nxt_http_parse_target_step
11416Svbart@nginx.com         nxt_http_parse_target_step
11516Svbart@nginx.com         nxt_http_parse_target_step
11616Svbart@nginx.com         nxt_http_parse_target_step
11716Svbart@nginx.com 
11816Svbart@nginx.com         nxt_http_parse_target_step
11916Svbart@nginx.com         nxt_http_parse_target_step
12016Svbart@nginx.com 
12116Svbart@nginx.com #undef nxt_http_parse_target_step
12216Svbart@nginx.com     }
12316Svbart@nginx.com 
12416Svbart@nginx.com     *pos = p;
12516Svbart@nginx.com 
12616Svbart@nginx.com     return trap;
12716Svbart@nginx.com }
12816Svbart@nginx.com 
12916Svbart@nginx.com 
13016Svbart@nginx.com nxt_int_t
13116Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
13216Svbart@nginx.com {
13316Svbart@nginx.com     nxt_int_t  rc;
13416Svbart@nginx.com 
13516Svbart@nginx.com     if (rp->handler == NULL) {
13616Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13716Svbart@nginx.com     }
13816Svbart@nginx.com 
13916Svbart@nginx.com     do {
14016Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
14116Svbart@nginx.com     } while (rc == NXT_OK);
14216Svbart@nginx.com 
14316Svbart@nginx.com     return rc;
14416Svbart@nginx.com }
14516Svbart@nginx.com 
14616Svbart@nginx.com 
14716Svbart@nginx.com static nxt_int_t
14816Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
14916Svbart@nginx.com     u_char *end)
15016Svbart@nginx.com {
15116Svbart@nginx.com     u_char                   *p, ch, *after_slash;
15216Svbart@nginx.com     nxt_int_t                rc;
15316Svbart@nginx.com     nxt_http_ver_t           version;
15416Svbart@nginx.com     nxt_http_target_traps_e  trap;
15516Svbart@nginx.com 
15616Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
15716Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
15816Svbart@nginx.com 
15916Svbart@nginx.com     p = *pos;
16016Svbart@nginx.com 
16116Svbart@nginx.com     rp->method.start = p;
16216Svbart@nginx.com 
16316Svbart@nginx.com     for ( ;; p++) {
16416Svbart@nginx.com 
16516Svbart@nginx.com         for ( ;; ) {
16616Svbart@nginx.com             if (nxt_slow_path(end - p < 12)) {
16716Svbart@nginx.com                 return NXT_AGAIN;
16816Svbart@nginx.com             }
16916Svbart@nginx.com 
17016Svbart@nginx.com #define nxt_http_parse_request_line_step                                      \
17116Svbart@nginx.com             {                                                                 \
17216Svbart@nginx.com                 ch = *p;                                                      \
17316Svbart@nginx.com                                                                               \
17416Svbart@nginx.com                 if (nxt_slow_path(ch < 'A' || ch > 'Z')) {                    \
17516Svbart@nginx.com                     break;                                                    \
17616Svbart@nginx.com                 }                                                             \
17716Svbart@nginx.com                                                                               \
17816Svbart@nginx.com                 p++;                                                          \
17916Svbart@nginx.com             }
18016Svbart@nginx.com 
18116Svbart@nginx.com             nxt_http_parse_request_line_step
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 
18616Svbart@nginx.com             nxt_http_parse_request_line_step
18716Svbart@nginx.com             nxt_http_parse_request_line_step
18816Svbart@nginx.com             nxt_http_parse_request_line_step
18916Svbart@nginx.com             nxt_http_parse_request_line_step
19016Svbart@nginx.com 
19116Svbart@nginx.com #undef nxt_http_parse_request_line_step
19216Svbart@nginx.com         }
19316Svbart@nginx.com 
19416Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
19516Svbart@nginx.com             rp->method.length = p - rp->method.start;
19616Svbart@nginx.com             break;
19716Svbart@nginx.com         }
19816Svbart@nginx.com 
19916Svbart@nginx.com         if (ch == '_' || ch == '-') {
20016Svbart@nginx.com             continue;
20116Svbart@nginx.com         }
20216Svbart@nginx.com 
20316Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
20416Svbart@nginx.com             rp->method.start++;
20516Svbart@nginx.com             continue;
20616Svbart@nginx.com         }
20716Svbart@nginx.com 
20816Svbart@nginx.com         return NXT_ERROR;
20916Svbart@nginx.com     }
21016Svbart@nginx.com 
21116Svbart@nginx.com     p++;
21216Svbart@nginx.com 
21316Svbart@nginx.com     if (nxt_slow_path(p == end)) {
21416Svbart@nginx.com         return NXT_AGAIN;
21516Svbart@nginx.com     }
21616Svbart@nginx.com 
21716Svbart@nginx.com     /* target */
21816Svbart@nginx.com 
21916Svbart@nginx.com     ch = *p;
22016Svbart@nginx.com 
22116Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
22216Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
22316Svbart@nginx.com 
22416Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
22516Svbart@nginx.com             return rc;
22616Svbart@nginx.com         }
22716Svbart@nginx.com     }
22816Svbart@nginx.com 
22916Svbart@nginx.com     rp->target_start = p;
23016Svbart@nginx.com 
23116Svbart@nginx.com     after_slash = p + 1;
23216Svbart@nginx.com 
23316Svbart@nginx.com     for ( ;; ) {
23416Svbart@nginx.com         p++;
23516Svbart@nginx.com 
23616Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
23716Svbart@nginx.com 
23816Svbart@nginx.com         switch (trap) {
23916Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
24016Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
24116Svbart@nginx.com                 rp->complex_target = 1;
24216Svbart@nginx.com                 goto rest_of_target;
24316Svbart@nginx.com             }
24416Svbart@nginx.com 
24516Svbart@nginx.com             after_slash = p + 1;
24616Svbart@nginx.com 
24716Svbart@nginx.com             rp->exten_start = NULL;
24816Svbart@nginx.com             continue;
24916Svbart@nginx.com 
25016Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
25116Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
25216Svbart@nginx.com                 rp->complex_target = 1;
25316Svbart@nginx.com                 goto rest_of_target;
25416Svbart@nginx.com             }
25516Svbart@nginx.com 
25616Svbart@nginx.com             rp->exten_start = p + 1;
25716Svbart@nginx.com             continue;
25816Svbart@nginx.com 
25916Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
26016Svbart@nginx.com             rp->args_start = p + 1;
26116Svbart@nginx.com             goto rest_of_target;
26216Svbart@nginx.com 
26316Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
26416Svbart@nginx.com             rp->target_end = p;
26516Svbart@nginx.com             goto space_after_target;
26616Svbart@nginx.com 
26716Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
26816Svbart@nginx.com             rp->quoted_target = 1;
26916Svbart@nginx.com             goto rest_of_target;
27016Svbart@nginx.com 
27116Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
27216Svbart@nginx.com             rp->plus_in_target = 1;
27316Svbart@nginx.com             continue;
27416Svbart@nginx.com 
27516Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
27616Svbart@nginx.com             rp->complex_target = 1;
27716Svbart@nginx.com             goto rest_of_target;
27816Svbart@nginx.com 
27916Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
28016Svbart@nginx.com             return NXT_AGAIN;
28116Svbart@nginx.com 
28216Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
28316Svbart@nginx.com             return NXT_ERROR;
28416Svbart@nginx.com         }
28516Svbart@nginx.com 
28616Svbart@nginx.com         nxt_unreachable();
28716Svbart@nginx.com     }
28816Svbart@nginx.com 
28916Svbart@nginx.com rest_of_target:
29016Svbart@nginx.com 
29116Svbart@nginx.com     for ( ;; ) {
29216Svbart@nginx.com         p++;
29316Svbart@nginx.com 
29419Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
29516Svbart@nginx.com 
29616Svbart@nginx.com         switch (trap) {
29716Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
29816Svbart@nginx.com             rp->target_end = p;
29916Svbart@nginx.com             goto space_after_target;
30016Svbart@nginx.com 
30116Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
30216Svbart@nginx.com             rp->complex_target = 1;
30316Svbart@nginx.com             continue;
30416Svbart@nginx.com 
30516Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
30616Svbart@nginx.com             return NXT_AGAIN;
30716Svbart@nginx.com 
30816Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
30916Svbart@nginx.com             return NXT_ERROR;
31016Svbart@nginx.com 
31116Svbart@nginx.com         default:
31216Svbart@nginx.com             continue;
31316Svbart@nginx.com         }
31416Svbart@nginx.com 
31516Svbart@nginx.com         nxt_unreachable();
31616Svbart@nginx.com     }
31716Svbart@nginx.com 
31816Svbart@nginx.com space_after_target:
31916Svbart@nginx.com 
32016Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
32116Svbart@nginx.com         return NXT_AGAIN;
32216Svbart@nginx.com     }
32316Svbart@nginx.com 
32416Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
32516Svbart@nginx.com 
32616Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
32716Svbart@nginx.com 
32816Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
32916Svbart@nginx.com                        || version.ui64 == http10.ui64
33016Svbart@nginx.com                        || (p[1] == 'H'
33116Svbart@nginx.com                            && p[2] == 'T'
33216Svbart@nginx.com                            && p[3] == 'T'
33316Svbart@nginx.com                            && p[4] == 'P'
33416Svbart@nginx.com                            && p[5] == '/'
33516Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
33616Svbart@nginx.com                            && p[7] == '.'
33716Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
33816Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
33916Svbart@nginx.com     {
34016Svbart@nginx.com         rp->version.ui64 = version.ui64;
34116Svbart@nginx.com 
34216Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
34316Svbart@nginx.com             p += 10;
34416Svbart@nginx.com 
34516Svbart@nginx.com             if (nxt_slow_path(p == end)) {
34616Svbart@nginx.com                 return NXT_AGAIN;
34716Svbart@nginx.com             }
34816Svbart@nginx.com 
34916Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
35016Svbart@nginx.com                 return NXT_ERROR;
35116Svbart@nginx.com             }
35216Svbart@nginx.com 
35316Svbart@nginx.com             *pos = p + 1;
354*112Smax.romanov@nginx.com 
355*112Smax.romanov@nginx.com         } else {
356*112Smax.romanov@nginx.com             *pos = p + 10;
357*112Smax.romanov@nginx.com         }
358*112Smax.romanov@nginx.com 
359*112Smax.romanov@nginx.com         if (rp->complex_target != 0 || rp->quoted_target != 0) {
360*112Smax.romanov@nginx.com             rc = nxt_http_parse_complex_target(rp);
361*112Smax.romanov@nginx.com 
362*112Smax.romanov@nginx.com             if (nxt_slow_path(rc != NXT_OK)) {
363*112Smax.romanov@nginx.com                 return rc;
364*112Smax.romanov@nginx.com             }
365*112Smax.romanov@nginx.com 
36616Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
36716Svbart@nginx.com         }
36816Svbart@nginx.com 
369*112Smax.romanov@nginx.com         rp->path.start = rp->target_start;
370*112Smax.romanov@nginx.com 
371*112Smax.romanov@nginx.com         if (rp->args_start != NULL) {
372*112Smax.romanov@nginx.com             rp->path.length = rp->args_start - rp->target_start - 1;
373*112Smax.romanov@nginx.com 
374*112Smax.romanov@nginx.com             rp->args.start = rp->args_start;
375*112Smax.romanov@nginx.com             rp->args.length = rp->target_end - rp->args_start;
376*112Smax.romanov@nginx.com 
377*112Smax.romanov@nginx.com         } else {
378*112Smax.romanov@nginx.com             rp->path.length = rp->target_end - rp->target_start;
379*112Smax.romanov@nginx.com         }
380*112Smax.romanov@nginx.com 
381*112Smax.romanov@nginx.com         if (rp->exten_start) {
382*112Smax.romanov@nginx.com             rp->exten.length = rp->path.start + rp->path.length -
383*112Smax.romanov@nginx.com                                rp->exten_start;
384*112Smax.romanov@nginx.com             rp->exten.start = rp->exten_start;
385*112Smax.romanov@nginx.com         }
386*112Smax.romanov@nginx.com 
38716Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
38816Svbart@nginx.com     }
38916Svbart@nginx.com 
39016Svbart@nginx.com     if (p[1] == ' ') {
39116Svbart@nginx.com         /* surplus space after tartet */
39216Svbart@nginx.com         p++;
39316Svbart@nginx.com         goto space_after_target;
39416Svbart@nginx.com     }
39516Svbart@nginx.com 
39616Svbart@nginx.com     rp->space_in_target = 1;
39716Svbart@nginx.com     goto rest_of_target;
39816Svbart@nginx.com }
39916Svbart@nginx.com 
40016Svbart@nginx.com 
40116Svbart@nginx.com static nxt_int_t
40216Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
40316Svbart@nginx.com     u_char *end)
40416Svbart@nginx.com {
40516Svbart@nginx.com     u_char  *p, ch;
40616Svbart@nginx.com 
40716Svbart@nginx.com     p = *pos;
40816Svbart@nginx.com 
40916Svbart@nginx.com     ch = *p;
41016Svbart@nginx.com 
41116Svbart@nginx.com     if (ch == ' ') {
41216Svbart@nginx.com         /* skip surplus spaces before target */
41316Svbart@nginx.com 
41416Svbart@nginx.com         do {
41516Svbart@nginx.com             p++;
41616Svbart@nginx.com 
41716Svbart@nginx.com             if (nxt_slow_path(p == end)) {
41816Svbart@nginx.com                 return NXT_AGAIN;
41916Svbart@nginx.com             }
42016Svbart@nginx.com 
42116Svbart@nginx.com             ch = *p;
42216Svbart@nginx.com 
42316Svbart@nginx.com         } while (ch == ' ');
42416Svbart@nginx.com 
42516Svbart@nginx.com         if (ch == '/') {
42616Svbart@nginx.com             *pos = p;
42716Svbart@nginx.com             return NXT_OK;
42816Svbart@nginx.com         }
42916Svbart@nginx.com     }
43016Svbart@nginx.com 
43116Svbart@nginx.com     /* absolute path or '*' */
43216Svbart@nginx.com 
43316Svbart@nginx.com     /* TODO */
43416Svbart@nginx.com 
43516Svbart@nginx.com     return NXT_ERROR;
43616Svbart@nginx.com }
43716Svbart@nginx.com 
43816Svbart@nginx.com 
43916Svbart@nginx.com static nxt_int_t
44016Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
44116Svbart@nginx.com     u_char *end)
44216Svbart@nginx.com {
44316Svbart@nginx.com     u_char  *p, ch, c;
44416Svbart@nginx.com     size_t  i, size;
44516Svbart@nginx.com 
44616Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
44716Svbart@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"
44816Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
44916Svbart@nginx.com 
45016Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
45116Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
45216Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
45316Svbart@nginx.com 
45416Svbart@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"
45516Svbart@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"
45616Svbart@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"
45716Svbart@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";
45816Svbart@nginx.com 
45916Svbart@nginx.com     p = *pos;
46016Svbart@nginx.com 
46116Svbart@nginx.com     size = end - p;
46267Svbart@nginx.com     i = rp->field_name.length;
46316Svbart@nginx.com 
46419Svbart@nginx.com #define nxt_http_parse_field_name_step                                        \
46519Svbart@nginx.com     {                                                                         \
46619Svbart@nginx.com         ch = p[i];                                                            \
46719Svbart@nginx.com         c = normal[ch];                                                       \
46819Svbart@nginx.com                                                                               \
46919Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
47019Svbart@nginx.com             goto name_end;                                                    \
47119Svbart@nginx.com         }                                                                     \
47219Svbart@nginx.com                                                                               \
47367Svbart@nginx.com         rp->field_key.str[i % 32] = c;                                        \
47419Svbart@nginx.com         i++;                                                                  \
47519Svbart@nginx.com     }
47616Svbart@nginx.com 
47719Svbart@nginx.com     while (nxt_fast_path(size - i >= 8)) {
47819Svbart@nginx.com         nxt_http_parse_field_name_step
47919Svbart@nginx.com         nxt_http_parse_field_name_step
48019Svbart@nginx.com         nxt_http_parse_field_name_step
48119Svbart@nginx.com         nxt_http_parse_field_name_step
48216Svbart@nginx.com 
48319Svbart@nginx.com         nxt_http_parse_field_name_step
48419Svbart@nginx.com         nxt_http_parse_field_name_step
48519Svbart@nginx.com         nxt_http_parse_field_name_step
48619Svbart@nginx.com         nxt_http_parse_field_name_step
48719Svbart@nginx.com     }
48816Svbart@nginx.com 
48919Svbart@nginx.com     while (nxt_fast_path(i != size)) {
49019Svbart@nginx.com         nxt_http_parse_field_name_step
49119Svbart@nginx.com     }
49216Svbart@nginx.com 
49319Svbart@nginx.com #undef nxt_http_parse_field_name_step
49416Svbart@nginx.com 
49567Svbart@nginx.com     rp->field_name.length = i;
49616Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
49716Svbart@nginx.com 
49816Svbart@nginx.com     return NXT_AGAIN;
49919Svbart@nginx.com 
50019Svbart@nginx.com name_end:
50119Svbart@nginx.com 
50219Svbart@nginx.com     if (nxt_fast_path(ch == ':')) {
50319Svbart@nginx.com         if (nxt_slow_path(i == 0)) {
50419Svbart@nginx.com             return NXT_ERROR;
50519Svbart@nginx.com         }
50619Svbart@nginx.com 
50719Svbart@nginx.com         *pos = &p[i] + 1;
50819Svbart@nginx.com 
50967Svbart@nginx.com         rp->field_name.length = i;
51067Svbart@nginx.com         rp->field_name.start = p;
51119Svbart@nginx.com 
51219Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
51319Svbart@nginx.com     }
51419Svbart@nginx.com 
51559Svbart@nginx.com     if (nxt_slow_path(i != 0)) {
51659Svbart@nginx.com         return NXT_ERROR;
51759Svbart@nginx.com     }
51819Svbart@nginx.com 
51919Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
52016Svbart@nginx.com }
52116Svbart@nginx.com 
52216Svbart@nginx.com 
52316Svbart@nginx.com static nxt_int_t
52416Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
52516Svbart@nginx.com     u_char *end)
52616Svbart@nginx.com {
52716Svbart@nginx.com     u_char  *p, ch;
52816Svbart@nginx.com 
52916Svbart@nginx.com     p = *pos;
53016Svbart@nginx.com 
53116Svbart@nginx.com     for ( ;; ) {
53216Svbart@nginx.com         if (nxt_slow_path(p == end)) {
53316Svbart@nginx.com             *pos = p;
53416Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
53516Svbart@nginx.com             return NXT_AGAIN;
53616Svbart@nginx.com         }
53716Svbart@nginx.com 
53816Svbart@nginx.com         if (*p != ' ') {
53916Svbart@nginx.com             break;
54016Svbart@nginx.com         }
54116Svbart@nginx.com 
54216Svbart@nginx.com         p++;
54316Svbart@nginx.com     }
54416Svbart@nginx.com 
54516Svbart@nginx.com     *pos = p;
54616Svbart@nginx.com 
54767Svbart@nginx.com     p += rp->field_value.length;
54816Svbart@nginx.com 
54916Svbart@nginx.com     for ( ;; ) {
55016Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
55116Svbart@nginx.com 
55216Svbart@nginx.com         if (nxt_slow_path(p == end)) {
55367Svbart@nginx.com             rp->field_value.length = p - *pos;
55416Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
55516Svbart@nginx.com             return NXT_AGAIN;
55616Svbart@nginx.com         }
55716Svbart@nginx.com 
55816Svbart@nginx.com         ch = *p;
55916Svbart@nginx.com 
56016Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
56116Svbart@nginx.com             break;
56216Svbart@nginx.com         }
56316Svbart@nginx.com 
56416Svbart@nginx.com         if (ch == '\0') {
56516Svbart@nginx.com             return NXT_ERROR;
56616Svbart@nginx.com         }
56716Svbart@nginx.com     }
56816Svbart@nginx.com 
56916Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
57016Svbart@nginx.com         while (p[-1] == ' ') {
57116Svbart@nginx.com             p--;
57216Svbart@nginx.com         }
57316Svbart@nginx.com     }
57416Svbart@nginx.com 
57567Svbart@nginx.com     rp->field_value.length = p - *pos;
57667Svbart@nginx.com     rp->field_value.start = *pos;
57716Svbart@nginx.com 
57816Svbart@nginx.com     *pos = p;
57916Svbart@nginx.com 
58016Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
58116Svbart@nginx.com }
58216Svbart@nginx.com 
58316Svbart@nginx.com 
58416Svbart@nginx.com static u_char *
58516Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
58616Svbart@nginx.com {
58716Svbart@nginx.com     nxt_uint_t  n;
58816Svbart@nginx.com 
58916Svbart@nginx.com #define nxt_http_lookup_field_end_step                                        \
59016Svbart@nginx.com     {                                                                         \
59119Svbart@nginx.com         if (nxt_slow_path(*p < 0x10)) {                                       \
59216Svbart@nginx.com             return p;                                                         \
59316Svbart@nginx.com         }                                                                     \
59416Svbart@nginx.com                                                                               \
59516Svbart@nginx.com         p++;                                                                  \
59616Svbart@nginx.com     }
59716Svbart@nginx.com 
59819Svbart@nginx.com     for (n = (end - p) / 16; nxt_fast_path(n != 0); n--) {
59919Svbart@nginx.com         nxt_http_lookup_field_end_step
60019Svbart@nginx.com         nxt_http_lookup_field_end_step
60119Svbart@nginx.com         nxt_http_lookup_field_end_step
60219Svbart@nginx.com         nxt_http_lookup_field_end_step
60319Svbart@nginx.com 
60419Svbart@nginx.com         nxt_http_lookup_field_end_step
60519Svbart@nginx.com         nxt_http_lookup_field_end_step
60619Svbart@nginx.com         nxt_http_lookup_field_end_step
60719Svbart@nginx.com         nxt_http_lookup_field_end_step
60819Svbart@nginx.com 
60916Svbart@nginx.com         nxt_http_lookup_field_end_step
61016Svbart@nginx.com         nxt_http_lookup_field_end_step
61116Svbart@nginx.com         nxt_http_lookup_field_end_step
61216Svbart@nginx.com         nxt_http_lookup_field_end_step
61316Svbart@nginx.com 
61416Svbart@nginx.com         nxt_http_lookup_field_end_step
61516Svbart@nginx.com         nxt_http_lookup_field_end_step
61616Svbart@nginx.com         nxt_http_lookup_field_end_step
61716Svbart@nginx.com         nxt_http_lookup_field_end_step
61816Svbart@nginx.com     }
61916Svbart@nginx.com 
62019Svbart@nginx.com     for (n = (end - p) / 4; nxt_fast_path(n != 0); n--) {
62116Svbart@nginx.com         nxt_http_lookup_field_end_step
62219Svbart@nginx.com         nxt_http_lookup_field_end_step
62316Svbart@nginx.com         nxt_http_lookup_field_end_step
62416Svbart@nginx.com         nxt_http_lookup_field_end_step
62519Svbart@nginx.com     }
62619Svbart@nginx.com 
62719Svbart@nginx.com     switch (end - p) {
62816Svbart@nginx.com     case 3:
62916Svbart@nginx.com         nxt_http_lookup_field_end_step
63039Svbart@nginx.com         /* Fall through. */
63116Svbart@nginx.com     case 2:
63216Svbart@nginx.com         nxt_http_lookup_field_end_step
63339Svbart@nginx.com         /* Fall through. */
63416Svbart@nginx.com     case 1:
63516Svbart@nginx.com         nxt_http_lookup_field_end_step
63639Svbart@nginx.com         /* Fall through. */
63716Svbart@nginx.com     case 0:
63816Svbart@nginx.com         break;
63916Svbart@nginx.com     default:
64016Svbart@nginx.com         nxt_unreachable();
64116Svbart@nginx.com     }
64216Svbart@nginx.com 
64316Svbart@nginx.com #undef nxt_http_lookup_field_end_step
64416Svbart@nginx.com 
64516Svbart@nginx.com     return p;
64616Svbart@nginx.com }
64716Svbart@nginx.com 
64816Svbart@nginx.com 
64916Svbart@nginx.com static nxt_int_t
65016Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
65116Svbart@nginx.com     u_char *end)
65216Svbart@nginx.com {
65360Svbart@nginx.com     u_char            *p;
65460Svbart@nginx.com     nxt_http_field_t  *field;
65516Svbart@nginx.com 
65616Svbart@nginx.com     p = *pos;
65716Svbart@nginx.com 
65816Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
65916Svbart@nginx.com         p++;
66016Svbart@nginx.com 
66116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
66216Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
66316Svbart@nginx.com             return NXT_AGAIN;
66416Svbart@nginx.com         }
66516Svbart@nginx.com     }
66616Svbart@nginx.com 
66716Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
66816Svbart@nginx.com         *pos = p + 1;
66916Svbart@nginx.com 
67067Svbart@nginx.com         if (rp->field_name.length != 0) {
67160Svbart@nginx.com             field = nxt_list_add(rp->fields);
67216Svbart@nginx.com 
67360Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
67460Svbart@nginx.com                 return NXT_ERROR;
67516Svbart@nginx.com             }
67616Svbart@nginx.com 
67767Svbart@nginx.com             field->name = rp->field_name;
67867Svbart@nginx.com             field->value = rp->field_value;
67960Svbart@nginx.com 
68067Svbart@nginx.com             nxt_http_fields_hash_lookup(rp->fields_hash, rp->field_key.ui64,
68167Svbart@nginx.com                                         field);
68267Svbart@nginx.com 
68367Svbart@nginx.com             nxt_memzero(rp->field_key.str, 32);
68467Svbart@nginx.com 
68567Svbart@nginx.com             rp->field_name.length = 0;
68667Svbart@nginx.com             rp->field_value.length = 0;
68716Svbart@nginx.com 
68816Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
68916Svbart@nginx.com             return NXT_OK;
69016Svbart@nginx.com         }
69116Svbart@nginx.com 
69216Svbart@nginx.com         return NXT_DONE;
69316Svbart@nginx.com     }
69416Svbart@nginx.com 
69516Svbart@nginx.com     return NXT_ERROR;
69616Svbart@nginx.com }
69716Svbart@nginx.com 
69816Svbart@nginx.com 
69960Svbart@nginx.com nxt_http_fields_hash_t *
70060Svbart@nginx.com nxt_http_fields_hash_create(nxt_http_fields_hash_entry_t *entries,
70165Sigor@sysoev.ru     nxt_mp_t *mp)
70216Svbart@nginx.com {
70360Svbart@nginx.com     size_t                      min_length, max_length, length, size;
70460Svbart@nginx.com     nxt_uint_t                  i, j, n;
70560Svbart@nginx.com     nxt_http_fields_hash_t      *hash;
70660Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
70716Svbart@nginx.com 
70838Svbart@nginx.com     min_length = 32 + 1;
70916Svbart@nginx.com     max_length = 0;
71016Svbart@nginx.com 
71160Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
71260Svbart@nginx.com         length = entries[i].name.length;
71316Svbart@nginx.com 
71416Svbart@nginx.com         if (length > 32) {
71516Svbart@nginx.com             /* TODO */
71616Svbart@nginx.com             return NULL;
71716Svbart@nginx.com         }
71816Svbart@nginx.com 
71916Svbart@nginx.com         min_length = nxt_min(length, min_length);
72016Svbart@nginx.com         max_length = nxt_max(length, max_length);
72116Svbart@nginx.com     }
72216Svbart@nginx.com 
72338Svbart@nginx.com     size = sizeof(nxt_http_fields_hash_t);
72416Svbart@nginx.com 
72538Svbart@nginx.com     if (min_length <= 32) {
72638Svbart@nginx.com         size += (max_length - min_length + 1)
72760Svbart@nginx.com                 * sizeof(nxt_http_fields_hash_elt_t *);
72838Svbart@nginx.com     }
72916Svbart@nginx.com 
73065Sigor@sysoev.ru     hash = nxt_mp_zget(mp, size);
73116Svbart@nginx.com     if (nxt_slow_path(hash == NULL)) {
73216Svbart@nginx.com         return NULL;
73316Svbart@nginx.com     }
73416Svbart@nginx.com 
73516Svbart@nginx.com     hash->min_length = min_length;
73616Svbart@nginx.com     hash->max_length = max_length;
73716Svbart@nginx.com 
73860Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
73960Svbart@nginx.com         length = entries[i].name.length;
74060Svbart@nginx.com         elt = hash->elts[length - min_length];
74116Svbart@nginx.com 
74260Svbart@nginx.com         if (elt != NULL) {
74316Svbart@nginx.com             continue;
74416Svbart@nginx.com         }
74516Svbart@nginx.com 
74616Svbart@nginx.com         n = 1;
74716Svbart@nginx.com 
74860Svbart@nginx.com         for (j = i + 1; entries[j].handler != NULL; j++) {
74960Svbart@nginx.com             if (length == entries[j].name.length) {
75016Svbart@nginx.com                 n++;
75116Svbart@nginx.com             }
75216Svbart@nginx.com         }
75316Svbart@nginx.com 
75460Svbart@nginx.com         size = sizeof(nxt_http_fields_hash_elt_t) + nxt_align_size(length, 8);
75516Svbart@nginx.com 
75665Sigor@sysoev.ru         elt = nxt_mp_zget(mp, n * size + sizeof(nxt_http_fields_hash_elt_t));
75716Svbart@nginx.com 
75860Svbart@nginx.com         if (nxt_slow_path(elt == NULL)) {
75916Svbart@nginx.com             return NULL;
76016Svbart@nginx.com         }
76116Svbart@nginx.com 
76260Svbart@nginx.com         hash->elts[length - min_length] = elt;
76316Svbart@nginx.com 
76460Svbart@nginx.com         for (j = i; entries[j].handler != NULL; j++) {
76560Svbart@nginx.com             if (length != entries[j].name.length) {
76616Svbart@nginx.com                 continue;
76716Svbart@nginx.com             }
76816Svbart@nginx.com 
76967Svbart@nginx.com             elt->handler = entries[j].handler;
77067Svbart@nginx.com             elt->data = entries[j].data;
77116Svbart@nginx.com 
77260Svbart@nginx.com             nxt_memcpy_lowcase(elt->key->str, entries[j].name.start, length);
77316Svbart@nginx.com 
77416Svbart@nginx.com             n--;
77516Svbart@nginx.com 
77616Svbart@nginx.com             if (n == 0) {
77716Svbart@nginx.com                 break;
77816Svbart@nginx.com             }
77916Svbart@nginx.com 
78098Svbart@nginx.com             elt = nxt_pointer_to(elt, size);
78116Svbart@nginx.com         }
78216Svbart@nginx.com     }
78316Svbart@nginx.com 
78416Svbart@nginx.com     return hash;
78516Svbart@nginx.com }
78660Svbart@nginx.com 
78760Svbart@nginx.com 
78867Svbart@nginx.com static void
78967Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, uint64_t key[4],
79060Svbart@nginx.com     nxt_http_field_t *field)
79160Svbart@nginx.com {
79260Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
79360Svbart@nginx.com 
79467Svbart@nginx.com     if (hash == NULL || field->name.length < hash->min_length) {
79567Svbart@nginx.com         goto not_found;
79660Svbart@nginx.com     }
79760Svbart@nginx.com 
79860Svbart@nginx.com     if (field->name.length > hash->max_length) {
79960Svbart@nginx.com 
80060Svbart@nginx.com         if (field->name.length > 32 && hash->long_fields != NULL) {
80167Svbart@nginx.com             nxt_http_fields_hash_lookup_long(hash, field);
80267Svbart@nginx.com             return;
80360Svbart@nginx.com         }
80460Svbart@nginx.com 
80567Svbart@nginx.com         goto not_found;
80660Svbart@nginx.com     }
80760Svbart@nginx.com 
80860Svbart@nginx.com     elt = hash->elts[field->name.length - hash->min_length];
80960Svbart@nginx.com 
81060Svbart@nginx.com     if (elt == NULL) {
81167Svbart@nginx.com         goto not_found;
81260Svbart@nginx.com     }
81360Svbart@nginx.com 
81460Svbart@nginx.com     switch ((field->name.length + 7) / 8) {
81560Svbart@nginx.com     case 1:
81660Svbart@nginx.com         do {
81767Svbart@nginx.com             if (elt->key[0].ui64 == key[0]) {
81867Svbart@nginx.com                 break;
81960Svbart@nginx.com             }
82060Svbart@nginx.com 
82160Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 1);
82260Svbart@nginx.com 
82367Svbart@nginx.com         } while (elt->handler != NULL);
82460Svbart@nginx.com 
82560Svbart@nginx.com         break;
82660Svbart@nginx.com 
82760Svbart@nginx.com     case 2:
82860Svbart@nginx.com         do {
82967Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
83067Svbart@nginx.com                 && elt->key[1].ui64 == key[1])
83160Svbart@nginx.com             {
83267Svbart@nginx.com                 break;
83360Svbart@nginx.com             }
83460Svbart@nginx.com 
83560Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 2);
83660Svbart@nginx.com 
83767Svbart@nginx.com         } while (elt->handler != NULL);
83860Svbart@nginx.com 
83960Svbart@nginx.com         break;
84060Svbart@nginx.com 
84160Svbart@nginx.com     case 3:
84260Svbart@nginx.com         do {
84367Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
84467Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
84567Svbart@nginx.com                 && elt->key[2].ui64 == key[2])
84660Svbart@nginx.com             {
84767Svbart@nginx.com                 break;
84860Svbart@nginx.com             }
84960Svbart@nginx.com 
85060Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 3);
85160Svbart@nginx.com 
85267Svbart@nginx.com         } while (elt->handler != NULL);
85360Svbart@nginx.com 
85460Svbart@nginx.com         break;
85560Svbart@nginx.com 
85660Svbart@nginx.com     case 4:
85760Svbart@nginx.com         do {
85867Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
85967Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
86067Svbart@nginx.com                 && elt->key[2].ui64 == key[2]
86167Svbart@nginx.com                 && elt->key[3].ui64 == key[3])
86260Svbart@nginx.com             {
86367Svbart@nginx.com                 break;
86460Svbart@nginx.com             }
86560Svbart@nginx.com 
86660Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 4);
86760Svbart@nginx.com 
86867Svbart@nginx.com         } while (elt->handler != NULL);
86960Svbart@nginx.com 
87060Svbart@nginx.com         break;
87160Svbart@nginx.com 
87260Svbart@nginx.com     default:
87360Svbart@nginx.com         nxt_unreachable();
87460Svbart@nginx.com     }
87560Svbart@nginx.com 
87667Svbart@nginx.com     field->handler = elt->handler;
87767Svbart@nginx.com     field->data = elt->data;
87867Svbart@nginx.com 
87967Svbart@nginx.com     return;
88067Svbart@nginx.com 
88167Svbart@nginx.com not_found:
88267Svbart@nginx.com 
88367Svbart@nginx.com     field->handler = NULL;
88467Svbart@nginx.com     field->data = 0;
88560Svbart@nginx.com }
88660Svbart@nginx.com 
88760Svbart@nginx.com 
88867Svbart@nginx.com static void
88960Svbart@nginx.com nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
89060Svbart@nginx.com     nxt_http_field_t *field)
89160Svbart@nginx.com {
89260Svbart@nginx.com     /* TODO */
89367Svbart@nginx.com 
89467Svbart@nginx.com     field->handler = NULL;
89567Svbart@nginx.com     field->data = 0;
89660Svbart@nginx.com }
89760Svbart@nginx.com 
89860Svbart@nginx.com 
89960Svbart@nginx.com nxt_int_t
90067Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, void *ctx, nxt_log_t *log)
90160Svbart@nginx.com {
90267Svbart@nginx.com     nxt_int_t         rc;
90367Svbart@nginx.com     nxt_http_field_t  *field;
90460Svbart@nginx.com 
90560Svbart@nginx.com     nxt_list_each(field, fields) {
90660Svbart@nginx.com 
90767Svbart@nginx.com         if (field->handler != NULL) {
90867Svbart@nginx.com             rc = field->handler(ctx, field, log);
90960Svbart@nginx.com 
91060Svbart@nginx.com             if (rc != NXT_OK) {
91160Svbart@nginx.com                 return rc;
91260Svbart@nginx.com             }
91360Svbart@nginx.com         }
91460Svbart@nginx.com 
91560Svbart@nginx.com     } nxt_list_loop;
91660Svbart@nginx.com 
91760Svbart@nginx.com     return NXT_OK;
91860Svbart@nginx.com }
919*112Smax.romanov@nginx.com 
920*112Smax.romanov@nginx.com 
921*112Smax.romanov@nginx.com #define                                                                       \
922*112Smax.romanov@nginx.com nxt_http_is_normal(c)                                                         \
923*112Smax.romanov@nginx.com     (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0))
924*112Smax.romanov@nginx.com 
925*112Smax.romanov@nginx.com 
926*112Smax.romanov@nginx.com static const uint8_t  nxt_http_normal[32]  nxt_aligned(32) = {
927*112Smax.romanov@nginx.com 
928*112Smax.romanov@nginx.com                              /*        \0   \r  \n                         */
929*112Smax.romanov@nginx.com     0xfe, 0xdb, 0xff, 0xff,  /* 1111 1110  1101 1011  1111 1111  1111 1111 */
930*112Smax.romanov@nginx.com 
931*112Smax.romanov@nginx.com                              /* '&%$ #"!   /.-, |*)(  7654 3210  ?>=< ;:98 */
932*112Smax.romanov@nginx.com     0xd6, 0x37, 0xff, 0x7f,  /* 1101 0110  0011 0111  1111 1111  0111 1111 */
933*112Smax.romanov@nginx.com 
934*112Smax.romanov@nginx.com                              /* GFED CBA@  ONML KJIH  WVUT SRQP  _^]\ [ZYX */
935*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
936*112Smax.romanov@nginx.com 
937*112Smax.romanov@nginx.com                              /* gfed cba`  onml kjih  wvut srqp   ~}| {zyx */
938*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
939*112Smax.romanov@nginx.com 
940*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
941*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
942*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
943*112Smax.romanov@nginx.com     0xff, 0xff, 0xff, 0xff,  /* 1111 1111  1111 1111  1111 1111  1111 1111 */
944*112Smax.romanov@nginx.com };
945*112Smax.romanov@nginx.com 
946*112Smax.romanov@nginx.com 
947*112Smax.romanov@nginx.com static nxt_int_t
948*112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp)
949*112Smax.romanov@nginx.com {
950*112Smax.romanov@nginx.com     u_char  *p, *u, c, ch, high;
951*112Smax.romanov@nginx.com     enum {
952*112Smax.romanov@nginx.com         sw_normal = 0,
953*112Smax.romanov@nginx.com         sw_slash,
954*112Smax.romanov@nginx.com         sw_dot,
955*112Smax.romanov@nginx.com         sw_dot_dot,
956*112Smax.romanov@nginx.com         sw_quoted,
957*112Smax.romanov@nginx.com         sw_quoted_second,
958*112Smax.romanov@nginx.com     } state, saved_state;
959*112Smax.romanov@nginx.com 
960*112Smax.romanov@nginx.com     nxt_prefetch(nxt_http_normal);
961*112Smax.romanov@nginx.com 
962*112Smax.romanov@nginx.com     state = sw_normal;
963*112Smax.romanov@nginx.com     saved_state = sw_normal;
964*112Smax.romanov@nginx.com     p = rp->target_start;
965*112Smax.romanov@nginx.com 
966*112Smax.romanov@nginx.com     u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1);
967*112Smax.romanov@nginx.com 
968*112Smax.romanov@nginx.com     if (nxt_slow_path(u == NULL)) {
969*112Smax.romanov@nginx.com         return NXT_ERROR;
970*112Smax.romanov@nginx.com     }
971*112Smax.romanov@nginx.com 
972*112Smax.romanov@nginx.com     rp->path.length = 0;
973*112Smax.romanov@nginx.com     rp->path.start = u;
974*112Smax.romanov@nginx.com 
975*112Smax.romanov@nginx.com     high = '\0';
976*112Smax.romanov@nginx.com     rp->exten_start = NULL;
977*112Smax.romanov@nginx.com     rp->args_start = NULL;
978*112Smax.romanov@nginx.com 
979*112Smax.romanov@nginx.com     while (p < rp->target_end) {
980*112Smax.romanov@nginx.com 
981*112Smax.romanov@nginx.com         ch = *p++;
982*112Smax.romanov@nginx.com 
983*112Smax.romanov@nginx.com     again:
984*112Smax.romanov@nginx.com 
985*112Smax.romanov@nginx.com         switch (state) {
986*112Smax.romanov@nginx.com 
987*112Smax.romanov@nginx.com         case sw_normal:
988*112Smax.romanov@nginx.com 
989*112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
990*112Smax.romanov@nginx.com                 *u++ = ch;
991*112Smax.romanov@nginx.com                 continue;
992*112Smax.romanov@nginx.com             }
993*112Smax.romanov@nginx.com 
994*112Smax.romanov@nginx.com             switch (ch) {
995*112Smax.romanov@nginx.com             case '/':
996*112Smax.romanov@nginx.com                 rp->exten_start = NULL;
997*112Smax.romanov@nginx.com                 state = sw_slash;
998*112Smax.romanov@nginx.com                 *u++ = ch;
999*112Smax.romanov@nginx.com                 continue;
1000*112Smax.romanov@nginx.com             case '%':
1001*112Smax.romanov@nginx.com                 saved_state = state;
1002*112Smax.romanov@nginx.com                 state = sw_quoted;
1003*112Smax.romanov@nginx.com                 continue;
1004*112Smax.romanov@nginx.com             case '?':
1005*112Smax.romanov@nginx.com                 rp->args_start = p;
1006*112Smax.romanov@nginx.com                 goto args;
1007*112Smax.romanov@nginx.com             case '#':
1008*112Smax.romanov@nginx.com                 goto done;
1009*112Smax.romanov@nginx.com             case '.':
1010*112Smax.romanov@nginx.com                 rp->exten_start = u + 1;
1011*112Smax.romanov@nginx.com                 *u++ = ch;
1012*112Smax.romanov@nginx.com                 continue;
1013*112Smax.romanov@nginx.com             case '+':
1014*112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1015*112Smax.romanov@nginx.com                 /* Fall through. */
1016*112Smax.romanov@nginx.com             default:
1017*112Smax.romanov@nginx.com                 *u++ = ch;
1018*112Smax.romanov@nginx.com                 continue;
1019*112Smax.romanov@nginx.com             }
1020*112Smax.romanov@nginx.com 
1021*112Smax.romanov@nginx.com             break;
1022*112Smax.romanov@nginx.com 
1023*112Smax.romanov@nginx.com         case sw_slash:
1024*112Smax.romanov@nginx.com 
1025*112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1026*112Smax.romanov@nginx.com                 state = sw_normal;
1027*112Smax.romanov@nginx.com                 *u++ = ch;
1028*112Smax.romanov@nginx.com                 continue;
1029*112Smax.romanov@nginx.com             }
1030*112Smax.romanov@nginx.com 
1031*112Smax.romanov@nginx.com             switch (ch) {
1032*112Smax.romanov@nginx.com             case '/':
1033*112Smax.romanov@nginx.com                 continue;
1034*112Smax.romanov@nginx.com             case '.':
1035*112Smax.romanov@nginx.com                 state = sw_dot;
1036*112Smax.romanov@nginx.com                 *u++ = ch;
1037*112Smax.romanov@nginx.com                 continue;
1038*112Smax.romanov@nginx.com             case '%':
1039*112Smax.romanov@nginx.com                 saved_state = state;
1040*112Smax.romanov@nginx.com                 state = sw_quoted;
1041*112Smax.romanov@nginx.com                 continue;
1042*112Smax.romanov@nginx.com             case '?':
1043*112Smax.romanov@nginx.com                 rp->args_start = p;
1044*112Smax.romanov@nginx.com                 goto args;
1045*112Smax.romanov@nginx.com             case '#':
1046*112Smax.romanov@nginx.com                 goto done;
1047*112Smax.romanov@nginx.com             case '+':
1048*112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1049*112Smax.romanov@nginx.com                 /* Fall through. */
1050*112Smax.romanov@nginx.com             default:
1051*112Smax.romanov@nginx.com                 state = sw_normal;
1052*112Smax.romanov@nginx.com                 *u++ = ch;
1053*112Smax.romanov@nginx.com                 continue;
1054*112Smax.romanov@nginx.com             }
1055*112Smax.romanov@nginx.com 
1056*112Smax.romanov@nginx.com             break;
1057*112Smax.romanov@nginx.com 
1058*112Smax.romanov@nginx.com         case sw_dot:
1059*112Smax.romanov@nginx.com 
1060*112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1061*112Smax.romanov@nginx.com                 state = sw_normal;
1062*112Smax.romanov@nginx.com                 *u++ = ch;
1063*112Smax.romanov@nginx.com                 continue;
1064*112Smax.romanov@nginx.com             }
1065*112Smax.romanov@nginx.com 
1066*112Smax.romanov@nginx.com             switch (ch) {
1067*112Smax.romanov@nginx.com             case '/':
1068*112Smax.romanov@nginx.com                 state = sw_slash;
1069*112Smax.romanov@nginx.com                 u--;
1070*112Smax.romanov@nginx.com                 continue;
1071*112Smax.romanov@nginx.com             case '.':
1072*112Smax.romanov@nginx.com                 state = sw_dot_dot;
1073*112Smax.romanov@nginx.com                 *u++ = ch;
1074*112Smax.romanov@nginx.com                 continue;
1075*112Smax.romanov@nginx.com             case '%':
1076*112Smax.romanov@nginx.com                 saved_state = state;
1077*112Smax.romanov@nginx.com                 state = sw_quoted;
1078*112Smax.romanov@nginx.com                 continue;
1079*112Smax.romanov@nginx.com             case '?':
1080*112Smax.romanov@nginx.com                 rp->args_start = p;
1081*112Smax.romanov@nginx.com                 goto args;
1082*112Smax.romanov@nginx.com             case '#':
1083*112Smax.romanov@nginx.com                 goto done;
1084*112Smax.romanov@nginx.com             case '+':
1085*112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1086*112Smax.romanov@nginx.com                 /* Fall through. */
1087*112Smax.romanov@nginx.com             default:
1088*112Smax.romanov@nginx.com                 state = sw_normal;
1089*112Smax.romanov@nginx.com                 *u++ = ch;
1090*112Smax.romanov@nginx.com                 continue;
1091*112Smax.romanov@nginx.com             }
1092*112Smax.romanov@nginx.com 
1093*112Smax.romanov@nginx.com             break;
1094*112Smax.romanov@nginx.com 
1095*112Smax.romanov@nginx.com         case sw_dot_dot:
1096*112Smax.romanov@nginx.com 
1097*112Smax.romanov@nginx.com             if (nxt_http_is_normal(ch)) {
1098*112Smax.romanov@nginx.com                 state = sw_normal;
1099*112Smax.romanov@nginx.com                 *u++ = ch;
1100*112Smax.romanov@nginx.com                 continue;
1101*112Smax.romanov@nginx.com             }
1102*112Smax.romanov@nginx.com 
1103*112Smax.romanov@nginx.com             switch (ch) {
1104*112Smax.romanov@nginx.com             case '/':
1105*112Smax.romanov@nginx.com                 state = sw_slash;
1106*112Smax.romanov@nginx.com                 u -= 5;
1107*112Smax.romanov@nginx.com                 for ( ;; ) {
1108*112Smax.romanov@nginx.com                     if (u < rp->path.start) {
1109*112Smax.romanov@nginx.com                         return NXT_ERROR;
1110*112Smax.romanov@nginx.com                     }
1111*112Smax.romanov@nginx.com                     if (*u == '/') {
1112*112Smax.romanov@nginx.com                         u++;
1113*112Smax.romanov@nginx.com                         break;
1114*112Smax.romanov@nginx.com                     }
1115*112Smax.romanov@nginx.com                     u--;
1116*112Smax.romanov@nginx.com                 }
1117*112Smax.romanov@nginx.com                 break;
1118*112Smax.romanov@nginx.com 
1119*112Smax.romanov@nginx.com             case '%':
1120*112Smax.romanov@nginx.com                 saved_state = state;
1121*112Smax.romanov@nginx.com                 state = sw_quoted;
1122*112Smax.romanov@nginx.com                 continue;
1123*112Smax.romanov@nginx.com             case '?':
1124*112Smax.romanov@nginx.com                 rp->args_start = p;
1125*112Smax.romanov@nginx.com                 goto args;
1126*112Smax.romanov@nginx.com             case '#':
1127*112Smax.romanov@nginx.com                 goto done;
1128*112Smax.romanov@nginx.com             case '+':
1129*112Smax.romanov@nginx.com                 rp->plus_in_target = 1;
1130*112Smax.romanov@nginx.com                 /* Fall through. */
1131*112Smax.romanov@nginx.com             default:
1132*112Smax.romanov@nginx.com                 state = sw_normal;
1133*112Smax.romanov@nginx.com                 *u++ = ch;
1134*112Smax.romanov@nginx.com                 continue;
1135*112Smax.romanov@nginx.com             }
1136*112Smax.romanov@nginx.com 
1137*112Smax.romanov@nginx.com             break;
1138*112Smax.romanov@nginx.com 
1139*112Smax.romanov@nginx.com         case sw_quoted:
1140*112Smax.romanov@nginx.com             rp->quoted_target = 1;
1141*112Smax.romanov@nginx.com 
1142*112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1143*112Smax.romanov@nginx.com                 high = (u_char) (ch - '0');
1144*112Smax.romanov@nginx.com                 state = sw_quoted_second;
1145*112Smax.romanov@nginx.com                 continue;
1146*112Smax.romanov@nginx.com             }
1147*112Smax.romanov@nginx.com 
1148*112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1149*112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1150*112Smax.romanov@nginx.com                 high = (u_char) (c - 'a' + 10);
1151*112Smax.romanov@nginx.com                 state = sw_quoted_second;
1152*112Smax.romanov@nginx.com                 continue;
1153*112Smax.romanov@nginx.com             }
1154*112Smax.romanov@nginx.com 
1155*112Smax.romanov@nginx.com             return NXT_ERROR;
1156*112Smax.romanov@nginx.com 
1157*112Smax.romanov@nginx.com         case sw_quoted_second:
1158*112Smax.romanov@nginx.com             if (ch >= '0' && ch <= '9') {
1159*112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + ch - '0');
1160*112Smax.romanov@nginx.com 
1161*112Smax.romanov@nginx.com                 if (ch == '%' || ch == '#') {
1162*112Smax.romanov@nginx.com                     state = sw_normal;
1163*112Smax.romanov@nginx.com                     *u++ = ch;
1164*112Smax.romanov@nginx.com                     continue;
1165*112Smax.romanov@nginx.com 
1166*112Smax.romanov@nginx.com                 } else if (ch == '\0') {
1167*112Smax.romanov@nginx.com                     return NXT_ERROR;
1168*112Smax.romanov@nginx.com                 }
1169*112Smax.romanov@nginx.com 
1170*112Smax.romanov@nginx.com                 state = saved_state;
1171*112Smax.romanov@nginx.com                 goto again;
1172*112Smax.romanov@nginx.com             }
1173*112Smax.romanov@nginx.com 
1174*112Smax.romanov@nginx.com             c = (u_char) (ch | 0x20);
1175*112Smax.romanov@nginx.com             if (c >= 'a' && c <= 'f') {
1176*112Smax.romanov@nginx.com                 ch = (u_char) ((high << 4) + c - 'a' + 10);
1177*112Smax.romanov@nginx.com 
1178*112Smax.romanov@nginx.com                 if (ch == '?') {
1179*112Smax.romanov@nginx.com                     state = sw_normal;
1180*112Smax.romanov@nginx.com                     *u++ = ch;
1181*112Smax.romanov@nginx.com                     continue;
1182*112Smax.romanov@nginx.com 
1183*112Smax.romanov@nginx.com                 } else if (ch == '+') {
1184*112Smax.romanov@nginx.com                     rp->plus_in_target = 1;
1185*112Smax.romanov@nginx.com                 }
1186*112Smax.romanov@nginx.com 
1187*112Smax.romanov@nginx.com                 state = saved_state;
1188*112Smax.romanov@nginx.com                 goto again;
1189*112Smax.romanov@nginx.com             }
1190*112Smax.romanov@nginx.com 
1191*112Smax.romanov@nginx.com             return NXT_ERROR;
1192*112Smax.romanov@nginx.com         }
1193*112Smax.romanov@nginx.com     }
1194*112Smax.romanov@nginx.com 
1195*112Smax.romanov@nginx.com     if (state >= sw_quoted) {
1196*112Smax.romanov@nginx.com         return NXT_ERROR;
1197*112Smax.romanov@nginx.com     }
1198*112Smax.romanov@nginx.com 
1199*112Smax.romanov@nginx.com args:
1200*112Smax.romanov@nginx.com 
1201*112Smax.romanov@nginx.com     for (/* void */; p < rp->target_end; p++) {
1202*112Smax.romanov@nginx.com         if (*p == '#') {
1203*112Smax.romanov@nginx.com             break;
1204*112Smax.romanov@nginx.com         }
1205*112Smax.romanov@nginx.com     }
1206*112Smax.romanov@nginx.com 
1207*112Smax.romanov@nginx.com     if (rp->args_start != NULL) {
1208*112Smax.romanov@nginx.com         rp->args.length = p - rp->args_start;
1209*112Smax.romanov@nginx.com         rp->args.start = rp->args_start;
1210*112Smax.romanov@nginx.com     }
1211*112Smax.romanov@nginx.com 
1212*112Smax.romanov@nginx.com done:
1213*112Smax.romanov@nginx.com 
1214*112Smax.romanov@nginx.com     rp->path.length = u - rp->path.start;
1215*112Smax.romanov@nginx.com 
1216*112Smax.romanov@nginx.com     if (rp->exten_start) {
1217*112Smax.romanov@nginx.com         rp->exten.length = u - rp->exten_start;
1218*112Smax.romanov@nginx.com         rp->exten.start = rp->exten_start;
1219*112Smax.romanov@nginx.com     }
1220*112Smax.romanov@nginx.com 
1221*112Smax.romanov@nginx.com     return NXT_OK;
1222*112Smax.romanov@nginx.com }
1223