xref: /unit/src/nxt_http_parse.c (revision 67)
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*67Svbart@nginx.com     nxt_http_field_handler_t    handler;
12*67Svbart@nginx.com     uintptr_t                   data;
1360Svbart@nginx.com 
1416Svbart@nginx.com     union {
15*67Svbart@nginx.com         uint8_t                 str[8];
16*67Svbart@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 {
22*67Svbart@nginx.com     size_t                      min_length;
23*67Svbart@nginx.com     size_t                      max_length;
24*67Svbart@nginx.com     void                        *long_fields;
25*67Svbart@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 
47*67Svbart@nginx.com static void nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash,
48*67Svbart@nginx.com     uint64_t key[4], nxt_http_field_t *field);
49*67Svbart@nginx.com static void nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
50*67Svbart@nginx.com     nxt_http_field_t *field);
5116Svbart@nginx.com 
5216Svbart@nginx.com 
5316Svbart@nginx.com typedef enum {
5416Svbart@nginx.com     NXT_HTTP_TARGET_SPACE = 1,   /* \s  */
5516Svbart@nginx.com     NXT_HTTP_TARGET_HASH,        /*  #  */
5616Svbart@nginx.com     NXT_HTTP_TARGET_AGAIN,
5716Svbart@nginx.com     NXT_HTTP_TARGET_BAD,         /* \0\r\n */
5816Svbart@nginx.com 
5916Svbart@nginx.com     /* traps below are used for extended check only */
6016Svbart@nginx.com 
6116Svbart@nginx.com     NXT_HTTP_TARGET_SLASH = 5,   /*  /  */
6216Svbart@nginx.com     NXT_HTTP_TARGET_DOT,         /*  .  */
6316Svbart@nginx.com     NXT_HTTP_TARGET_ARGS_MARK,   /*  ?  */
6416Svbart@nginx.com     NXT_HTTP_TARGET_QUOTE_MARK,  /*  %  */
6516Svbart@nginx.com     NXT_HTTP_TARGET_PLUS,        /*  +  */
6616Svbart@nginx.com } nxt_http_target_traps_e;
6716Svbart@nginx.com 
6816Svbart@nginx.com 
6916Svbart@nginx.com static const uint8_t  nxt_http_target_chars[256] nxt_aligned(64) = {
7016Svbart@nginx.com     /* \0                               \n        \r       */
7116Svbart@nginx.com         4, 0, 0, 0,  0, 0, 0, 0,   0, 0, 4, 0,  0, 4, 0, 0,
7216Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0,
7352Svbart@nginx.com 
7452Svbart@nginx.com     /* \s  !  "  #   $  %  &  '    (  )  *  +   ,  -  .  / */
7516Svbart@nginx.com         1, 0, 0, 2,  0, 8, 0, 0,   0, 0, 0, 9,  0, 0, 6, 5,
7652Svbart@nginx.com 
7752Svbart@nginx.com     /*  0  1  2  3   4  5  6  7    8  9  :  ;   <  =  >  ? */
7816Svbart@nginx.com         0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 7,
7916Svbart@nginx.com };
8016Svbart@nginx.com 
8116Svbart@nginx.com 
8216Svbart@nginx.com nxt_inline nxt_http_target_traps_e
8316Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end)
8416Svbart@nginx.com {
8516Svbart@nginx.com     u_char      *p;
8616Svbart@nginx.com     nxt_uint_t  trap;
8716Svbart@nginx.com 
8816Svbart@nginx.com     p = *pos;
8916Svbart@nginx.com 
9016Svbart@nginx.com     for ( ;; ) {
9116Svbart@nginx.com         if (nxt_slow_path(end - p < 10)) {
9216Svbart@nginx.com             return NXT_HTTP_TARGET_AGAIN;
9316Svbart@nginx.com         }
9416Svbart@nginx.com 
9516Svbart@nginx.com #define nxt_http_parse_target_step                                            \
9616Svbart@nginx.com         {                                                                     \
9716Svbart@nginx.com             trap = nxt_http_target_chars[*p];                                 \
9816Svbart@nginx.com                                                                               \
9916Svbart@nginx.com             if (nxt_slow_path(trap != 0)) {                                   \
10016Svbart@nginx.com                 break;                                                        \
10116Svbart@nginx.com             }                                                                 \
10216Svbart@nginx.com                                                                               \
10316Svbart@nginx.com             p++;                                                              \
10416Svbart@nginx.com         }
10516Svbart@nginx.com 
10616Svbart@nginx.com         nxt_http_parse_target_step
10716Svbart@nginx.com         nxt_http_parse_target_step
10816Svbart@nginx.com         nxt_http_parse_target_step
10916Svbart@nginx.com         nxt_http_parse_target_step
11016Svbart@nginx.com 
11116Svbart@nginx.com         nxt_http_parse_target_step
11216Svbart@nginx.com         nxt_http_parse_target_step
11316Svbart@nginx.com         nxt_http_parse_target_step
11416Svbart@nginx.com         nxt_http_parse_target_step
11516Svbart@nginx.com 
11616Svbart@nginx.com         nxt_http_parse_target_step
11716Svbart@nginx.com         nxt_http_parse_target_step
11816Svbart@nginx.com 
11916Svbart@nginx.com #undef nxt_http_parse_target_step
12016Svbart@nginx.com     }
12116Svbart@nginx.com 
12216Svbart@nginx.com     *pos = p;
12316Svbart@nginx.com 
12416Svbart@nginx.com     return trap;
12516Svbart@nginx.com }
12616Svbart@nginx.com 
12716Svbart@nginx.com 
12816Svbart@nginx.com nxt_int_t
12916Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b)
13016Svbart@nginx.com {
13116Svbart@nginx.com     nxt_int_t  rc;
13216Svbart@nginx.com 
13316Svbart@nginx.com     if (rp->handler == NULL) {
13416Svbart@nginx.com         rp->handler = &nxt_http_parse_request_line;
13516Svbart@nginx.com     }
13616Svbart@nginx.com 
13716Svbart@nginx.com     do {
13816Svbart@nginx.com         rc = rp->handler(rp, &b->pos, b->free);
13916Svbart@nginx.com     } while (rc == NXT_OK);
14016Svbart@nginx.com 
14116Svbart@nginx.com     return rc;
14216Svbart@nginx.com }
14316Svbart@nginx.com 
14416Svbart@nginx.com 
14516Svbart@nginx.com static nxt_int_t
14616Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
14716Svbart@nginx.com     u_char *end)
14816Svbart@nginx.com {
14916Svbart@nginx.com     u_char                   *p, ch, *after_slash;
15016Svbart@nginx.com     nxt_int_t                rc;
15116Svbart@nginx.com     nxt_http_ver_t           version;
15216Svbart@nginx.com     nxt_http_target_traps_e  trap;
15316Svbart@nginx.com 
15416Svbart@nginx.com     static const nxt_http_ver_t  http11 = { "HTTP/1.1" };
15516Svbart@nginx.com     static const nxt_http_ver_t  http10 = { "HTTP/1.0" };
15616Svbart@nginx.com 
15716Svbart@nginx.com     p = *pos;
15816Svbart@nginx.com 
15916Svbart@nginx.com     rp->method.start = p;
16016Svbart@nginx.com 
16116Svbart@nginx.com     for ( ;; p++) {
16216Svbart@nginx.com 
16316Svbart@nginx.com         for ( ;; ) {
16416Svbart@nginx.com             if (nxt_slow_path(end - p < 12)) {
16516Svbart@nginx.com                 return NXT_AGAIN;
16616Svbart@nginx.com             }
16716Svbart@nginx.com 
16816Svbart@nginx.com #define nxt_http_parse_request_line_step                                      \
16916Svbart@nginx.com             {                                                                 \
17016Svbart@nginx.com                 ch = *p;                                                      \
17116Svbart@nginx.com                                                                               \
17216Svbart@nginx.com                 if (nxt_slow_path(ch < 'A' || ch > 'Z')) {                    \
17316Svbart@nginx.com                     break;                                                    \
17416Svbart@nginx.com                 }                                                             \
17516Svbart@nginx.com                                                                               \
17616Svbart@nginx.com                 p++;                                                          \
17716Svbart@nginx.com             }
17816Svbart@nginx.com 
17916Svbart@nginx.com             nxt_http_parse_request_line_step
18016Svbart@nginx.com             nxt_http_parse_request_line_step
18116Svbart@nginx.com             nxt_http_parse_request_line_step
18216Svbart@nginx.com             nxt_http_parse_request_line_step
18316Svbart@nginx.com 
18416Svbart@nginx.com             nxt_http_parse_request_line_step
18516Svbart@nginx.com             nxt_http_parse_request_line_step
18616Svbart@nginx.com             nxt_http_parse_request_line_step
18716Svbart@nginx.com             nxt_http_parse_request_line_step
18816Svbart@nginx.com 
18916Svbart@nginx.com #undef nxt_http_parse_request_line_step
19016Svbart@nginx.com         }
19116Svbart@nginx.com 
19216Svbart@nginx.com         if (nxt_fast_path(ch == ' ')) {
19316Svbart@nginx.com             rp->method.length = p - rp->method.start;
19416Svbart@nginx.com             break;
19516Svbart@nginx.com         }
19616Svbart@nginx.com 
19716Svbart@nginx.com         if (ch == '_' || ch == '-') {
19816Svbart@nginx.com             continue;
19916Svbart@nginx.com         }
20016Svbart@nginx.com 
20116Svbart@nginx.com         if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) {
20216Svbart@nginx.com             rp->method.start++;
20316Svbart@nginx.com             continue;
20416Svbart@nginx.com         }
20516Svbart@nginx.com 
20616Svbart@nginx.com         return NXT_ERROR;
20716Svbart@nginx.com     }
20816Svbart@nginx.com 
20916Svbart@nginx.com     p++;
21016Svbart@nginx.com 
21116Svbart@nginx.com     if (nxt_slow_path(p == end)) {
21216Svbart@nginx.com         return NXT_AGAIN;
21316Svbart@nginx.com     }
21416Svbart@nginx.com 
21516Svbart@nginx.com     /* target */
21616Svbart@nginx.com 
21716Svbart@nginx.com     ch = *p;
21816Svbart@nginx.com 
21916Svbart@nginx.com     if (nxt_slow_path(ch != '/')) {
22016Svbart@nginx.com         rc = nxt_http_parse_unusual_target(rp, &p, end);
22116Svbart@nginx.com 
22216Svbart@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
22316Svbart@nginx.com             return rc;
22416Svbart@nginx.com         }
22516Svbart@nginx.com     }
22616Svbart@nginx.com 
22716Svbart@nginx.com     rp->target_start = p;
22816Svbart@nginx.com 
22916Svbart@nginx.com     after_slash = p + 1;
23016Svbart@nginx.com 
23116Svbart@nginx.com     for ( ;; ) {
23216Svbart@nginx.com         p++;
23316Svbart@nginx.com 
23416Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
23516Svbart@nginx.com 
23616Svbart@nginx.com         switch (trap) {
23716Svbart@nginx.com         case NXT_HTTP_TARGET_SLASH:
23816Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
23916Svbart@nginx.com                 rp->complex_target = 1;
24016Svbart@nginx.com                 goto rest_of_target;
24116Svbart@nginx.com             }
24216Svbart@nginx.com 
24316Svbart@nginx.com             after_slash = p + 1;
24416Svbart@nginx.com 
24516Svbart@nginx.com             rp->exten_start = NULL;
24616Svbart@nginx.com             continue;
24716Svbart@nginx.com 
24816Svbart@nginx.com         case NXT_HTTP_TARGET_DOT:
24916Svbart@nginx.com             if (nxt_slow_path(after_slash == p)) {
25016Svbart@nginx.com                 rp->complex_target = 1;
25116Svbart@nginx.com                 goto rest_of_target;
25216Svbart@nginx.com             }
25316Svbart@nginx.com 
25416Svbart@nginx.com             rp->exten_start = p + 1;
25516Svbart@nginx.com             continue;
25616Svbart@nginx.com 
25716Svbart@nginx.com         case NXT_HTTP_TARGET_ARGS_MARK:
25816Svbart@nginx.com             rp->args_start = p + 1;
25916Svbart@nginx.com             goto rest_of_target;
26016Svbart@nginx.com 
26116Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
26216Svbart@nginx.com             rp->target_end = p;
26316Svbart@nginx.com             goto space_after_target;
26416Svbart@nginx.com 
26516Svbart@nginx.com         case NXT_HTTP_TARGET_QUOTE_MARK:
26616Svbart@nginx.com             rp->quoted_target = 1;
26716Svbart@nginx.com             goto rest_of_target;
26816Svbart@nginx.com 
26916Svbart@nginx.com         case NXT_HTTP_TARGET_PLUS:
27016Svbart@nginx.com             rp->plus_in_target = 1;
27116Svbart@nginx.com             continue;
27216Svbart@nginx.com 
27316Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
27416Svbart@nginx.com             rp->complex_target = 1;
27516Svbart@nginx.com             goto rest_of_target;
27616Svbart@nginx.com 
27716Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
27816Svbart@nginx.com             return NXT_AGAIN;
27916Svbart@nginx.com 
28016Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
28116Svbart@nginx.com             return NXT_ERROR;
28216Svbart@nginx.com         }
28316Svbart@nginx.com 
28416Svbart@nginx.com         nxt_unreachable();
28516Svbart@nginx.com     }
28616Svbart@nginx.com 
28716Svbart@nginx.com rest_of_target:
28816Svbart@nginx.com 
28916Svbart@nginx.com     for ( ;; ) {
29016Svbart@nginx.com         p++;
29116Svbart@nginx.com 
29219Svbart@nginx.com         trap = nxt_http_parse_target(&p, end);
29316Svbart@nginx.com 
29416Svbart@nginx.com         switch (trap) {
29516Svbart@nginx.com         case NXT_HTTP_TARGET_SPACE:
29616Svbart@nginx.com             rp->target_end = p;
29716Svbart@nginx.com             goto space_after_target;
29816Svbart@nginx.com 
29916Svbart@nginx.com         case NXT_HTTP_TARGET_HASH:
30016Svbart@nginx.com             rp->complex_target = 1;
30116Svbart@nginx.com             continue;
30216Svbart@nginx.com 
30316Svbart@nginx.com         case NXT_HTTP_TARGET_AGAIN:
30416Svbart@nginx.com             return NXT_AGAIN;
30516Svbart@nginx.com 
30616Svbart@nginx.com         case NXT_HTTP_TARGET_BAD:
30716Svbart@nginx.com             return NXT_ERROR;
30816Svbart@nginx.com 
30916Svbart@nginx.com         default:
31016Svbart@nginx.com             continue;
31116Svbart@nginx.com         }
31216Svbart@nginx.com 
31316Svbart@nginx.com         nxt_unreachable();
31416Svbart@nginx.com     }
31516Svbart@nginx.com 
31616Svbart@nginx.com space_after_target:
31716Svbart@nginx.com 
31816Svbart@nginx.com     if (nxt_slow_path(end - p < 10)) {
31916Svbart@nginx.com         return NXT_AGAIN;
32016Svbart@nginx.com     }
32116Svbart@nginx.com 
32216Svbart@nginx.com     /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
32316Svbart@nginx.com 
32416Svbart@nginx.com     nxt_memcpy(version.str, &p[1], 8);
32516Svbart@nginx.com 
32616Svbart@nginx.com     if (nxt_fast_path((version.ui64 == http11.ui64
32716Svbart@nginx.com                        || version.ui64 == http10.ui64
32816Svbart@nginx.com                        || (p[1] == 'H'
32916Svbart@nginx.com                            && p[2] == 'T'
33016Svbart@nginx.com                            && p[3] == 'T'
33116Svbart@nginx.com                            && p[4] == 'P'
33216Svbart@nginx.com                            && p[5] == '/'
33316Svbart@nginx.com                            && p[6] >= '0' && p[6] <= '9'
33416Svbart@nginx.com                            && p[7] == '.'
33516Svbart@nginx.com                            && p[8] >= '0' && p[8] <= '9'))
33616Svbart@nginx.com                       && (p[9] == '\r' || p[9] == '\n')))
33716Svbart@nginx.com     {
33816Svbart@nginx.com         rp->version.ui64 = version.ui64;
33916Svbart@nginx.com 
34016Svbart@nginx.com         if (nxt_fast_path(p[9] == '\r')) {
34116Svbart@nginx.com             p += 10;
34216Svbart@nginx.com 
34316Svbart@nginx.com             if (nxt_slow_path(p == end)) {
34416Svbart@nginx.com                 return NXT_AGAIN;
34516Svbart@nginx.com             }
34616Svbart@nginx.com 
34716Svbart@nginx.com             if (nxt_slow_path(*p != '\n')) {
34816Svbart@nginx.com                 return NXT_ERROR;
34916Svbart@nginx.com             }
35016Svbart@nginx.com 
35116Svbart@nginx.com             *pos = p + 1;
35216Svbart@nginx.com             return nxt_http_parse_field_name(rp, pos, end);
35316Svbart@nginx.com         }
35416Svbart@nginx.com 
35516Svbart@nginx.com         *pos = p + 10;
35616Svbart@nginx.com         return nxt_http_parse_field_name(rp, pos, end);
35716Svbart@nginx.com     }
35816Svbart@nginx.com 
35916Svbart@nginx.com     if (p[1] == ' ') {
36016Svbart@nginx.com         /* surplus space after tartet */
36116Svbart@nginx.com         p++;
36216Svbart@nginx.com         goto space_after_target;
36316Svbart@nginx.com     }
36416Svbart@nginx.com 
36516Svbart@nginx.com     rp->space_in_target = 1;
36616Svbart@nginx.com     goto rest_of_target;
36716Svbart@nginx.com }
36816Svbart@nginx.com 
36916Svbart@nginx.com 
37016Svbart@nginx.com static nxt_int_t
37116Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos,
37216Svbart@nginx.com     u_char *end)
37316Svbart@nginx.com {
37416Svbart@nginx.com     u_char  *p, ch;
37516Svbart@nginx.com 
37616Svbart@nginx.com     p = *pos;
37716Svbart@nginx.com 
37816Svbart@nginx.com     ch = *p;
37916Svbart@nginx.com 
38016Svbart@nginx.com     if (ch == ' ') {
38116Svbart@nginx.com         /* skip surplus spaces before target */
38216Svbart@nginx.com 
38316Svbart@nginx.com         do {
38416Svbart@nginx.com             p++;
38516Svbart@nginx.com 
38616Svbart@nginx.com             if (nxt_slow_path(p == end)) {
38716Svbart@nginx.com                 return NXT_AGAIN;
38816Svbart@nginx.com             }
38916Svbart@nginx.com 
39016Svbart@nginx.com             ch = *p;
39116Svbart@nginx.com 
39216Svbart@nginx.com         } while (ch == ' ');
39316Svbart@nginx.com 
39416Svbart@nginx.com         if (ch == '/') {
39516Svbart@nginx.com             *pos = p;
39616Svbart@nginx.com             return NXT_OK;
39716Svbart@nginx.com         }
39816Svbart@nginx.com     }
39916Svbart@nginx.com 
40016Svbart@nginx.com     /* absolute path or '*' */
40116Svbart@nginx.com 
40216Svbart@nginx.com     /* TODO */
40316Svbart@nginx.com 
40416Svbart@nginx.com     return NXT_ERROR;
40516Svbart@nginx.com }
40616Svbart@nginx.com 
40716Svbart@nginx.com 
40816Svbart@nginx.com static nxt_int_t
40916Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos,
41016Svbart@nginx.com     u_char *end)
41116Svbart@nginx.com {
41216Svbart@nginx.com     u_char  *p, ch, c;
41316Svbart@nginx.com     size_t  i, size;
41416Svbart@nginx.com 
41516Svbart@nginx.com     static const u_char  normal[256]  nxt_aligned(64) =
41616Svbart@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"
41716Svbart@nginx.com         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
41816Svbart@nginx.com 
41916Svbart@nginx.com         /* These 64 bytes should reside in one cache line. */
42016Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
42116Svbart@nginx.com         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
42216Svbart@nginx.com 
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         "\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"
42616Svbart@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";
42716Svbart@nginx.com 
42816Svbart@nginx.com     p = *pos;
42916Svbart@nginx.com 
43016Svbart@nginx.com     size = end - p;
431*67Svbart@nginx.com     i = rp->field_name.length;
43216Svbart@nginx.com 
43319Svbart@nginx.com #define nxt_http_parse_field_name_step                                        \
43419Svbart@nginx.com     {                                                                         \
43519Svbart@nginx.com         ch = p[i];                                                            \
43619Svbart@nginx.com         c = normal[ch];                                                       \
43719Svbart@nginx.com                                                                               \
43819Svbart@nginx.com         if (nxt_slow_path(c == '\0')) {                                       \
43919Svbart@nginx.com             goto name_end;                                                    \
44019Svbart@nginx.com         }                                                                     \
44119Svbart@nginx.com                                                                               \
442*67Svbart@nginx.com         rp->field_key.str[i % 32] = c;                                        \
44319Svbart@nginx.com         i++;                                                                  \
44419Svbart@nginx.com     }
44516Svbart@nginx.com 
44619Svbart@nginx.com     while (nxt_fast_path(size - i >= 8)) {
44719Svbart@nginx.com         nxt_http_parse_field_name_step
44819Svbart@nginx.com         nxt_http_parse_field_name_step
44919Svbart@nginx.com         nxt_http_parse_field_name_step
45019Svbart@nginx.com         nxt_http_parse_field_name_step
45116Svbart@nginx.com 
45219Svbart@nginx.com         nxt_http_parse_field_name_step
45319Svbart@nginx.com         nxt_http_parse_field_name_step
45419Svbart@nginx.com         nxt_http_parse_field_name_step
45519Svbart@nginx.com         nxt_http_parse_field_name_step
45619Svbart@nginx.com     }
45716Svbart@nginx.com 
45819Svbart@nginx.com     while (nxt_fast_path(i != size)) {
45919Svbart@nginx.com         nxt_http_parse_field_name_step
46019Svbart@nginx.com     }
46116Svbart@nginx.com 
46219Svbart@nginx.com #undef nxt_http_parse_field_name_step
46316Svbart@nginx.com 
464*67Svbart@nginx.com     rp->field_name.length = i;
46516Svbart@nginx.com     rp->handler = &nxt_http_parse_field_name;
46616Svbart@nginx.com 
46716Svbart@nginx.com     return NXT_AGAIN;
46819Svbart@nginx.com 
46919Svbart@nginx.com name_end:
47019Svbart@nginx.com 
47119Svbart@nginx.com     if (nxt_fast_path(ch == ':')) {
47219Svbart@nginx.com         if (nxt_slow_path(i == 0)) {
47319Svbart@nginx.com             return NXT_ERROR;
47419Svbart@nginx.com         }
47519Svbart@nginx.com 
47619Svbart@nginx.com         *pos = &p[i] + 1;
47719Svbart@nginx.com 
478*67Svbart@nginx.com         rp->field_name.length = i;
479*67Svbart@nginx.com         rp->field_name.start = p;
48019Svbart@nginx.com 
48119Svbart@nginx.com         return nxt_http_parse_field_value(rp, pos, end);
48219Svbart@nginx.com     }
48319Svbart@nginx.com 
48459Svbart@nginx.com     if (nxt_slow_path(i != 0)) {
48559Svbart@nginx.com         return NXT_ERROR;
48659Svbart@nginx.com     }
48719Svbart@nginx.com 
48819Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
48916Svbart@nginx.com }
49016Svbart@nginx.com 
49116Svbart@nginx.com 
49216Svbart@nginx.com static nxt_int_t
49316Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos,
49416Svbart@nginx.com     u_char *end)
49516Svbart@nginx.com {
49616Svbart@nginx.com     u_char  *p, ch;
49716Svbart@nginx.com 
49816Svbart@nginx.com     p = *pos;
49916Svbart@nginx.com 
50016Svbart@nginx.com     for ( ;; ) {
50116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
50216Svbart@nginx.com             *pos = p;
50316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
50416Svbart@nginx.com             return NXT_AGAIN;
50516Svbart@nginx.com         }
50616Svbart@nginx.com 
50716Svbart@nginx.com         if (*p != ' ') {
50816Svbart@nginx.com             break;
50916Svbart@nginx.com         }
51016Svbart@nginx.com 
51116Svbart@nginx.com         p++;
51216Svbart@nginx.com     }
51316Svbart@nginx.com 
51416Svbart@nginx.com     *pos = p;
51516Svbart@nginx.com 
516*67Svbart@nginx.com     p += rp->field_value.length;
51716Svbart@nginx.com 
51816Svbart@nginx.com     for ( ;; ) {
51916Svbart@nginx.com         p = nxt_http_lookup_field_end(p, end);
52016Svbart@nginx.com 
52116Svbart@nginx.com         if (nxt_slow_path(p == end)) {
522*67Svbart@nginx.com             rp->field_value.length = p - *pos;
52316Svbart@nginx.com             rp->handler = &nxt_http_parse_field_value;
52416Svbart@nginx.com             return NXT_AGAIN;
52516Svbart@nginx.com         }
52616Svbart@nginx.com 
52716Svbart@nginx.com         ch = *p;
52816Svbart@nginx.com 
52916Svbart@nginx.com         if (nxt_fast_path(ch == '\r' || ch == '\n')) {
53016Svbart@nginx.com             break;
53116Svbart@nginx.com         }
53216Svbart@nginx.com 
53316Svbart@nginx.com         if (ch == '\0') {
53416Svbart@nginx.com             return NXT_ERROR;
53516Svbart@nginx.com         }
53616Svbart@nginx.com     }
53716Svbart@nginx.com 
53816Svbart@nginx.com     if (nxt_fast_path(p != *pos)) {
53916Svbart@nginx.com         while (p[-1] == ' ') {
54016Svbart@nginx.com             p--;
54116Svbart@nginx.com         }
54216Svbart@nginx.com     }
54316Svbart@nginx.com 
544*67Svbart@nginx.com     rp->field_value.length = p - *pos;
545*67Svbart@nginx.com     rp->field_value.start = *pos;
54616Svbart@nginx.com 
54716Svbart@nginx.com     *pos = p;
54816Svbart@nginx.com 
54916Svbart@nginx.com     return nxt_http_parse_field_end(rp, pos, end);
55016Svbart@nginx.com }
55116Svbart@nginx.com 
55216Svbart@nginx.com 
55316Svbart@nginx.com static u_char *
55416Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end)
55516Svbart@nginx.com {
55616Svbart@nginx.com     nxt_uint_t  n;
55716Svbart@nginx.com 
55816Svbart@nginx.com #define nxt_http_lookup_field_end_step                                        \
55916Svbart@nginx.com     {                                                                         \
56019Svbart@nginx.com         if (nxt_slow_path(*p < 0x10)) {                                       \
56116Svbart@nginx.com             return p;                                                         \
56216Svbart@nginx.com         }                                                                     \
56316Svbart@nginx.com                                                                               \
56416Svbart@nginx.com         p++;                                                                  \
56516Svbart@nginx.com     }
56616Svbart@nginx.com 
56719Svbart@nginx.com     for (n = (end - p) / 16; nxt_fast_path(n != 0); n--) {
56819Svbart@nginx.com         nxt_http_lookup_field_end_step
56919Svbart@nginx.com         nxt_http_lookup_field_end_step
57019Svbart@nginx.com         nxt_http_lookup_field_end_step
57119Svbart@nginx.com         nxt_http_lookup_field_end_step
57219Svbart@nginx.com 
57319Svbart@nginx.com         nxt_http_lookup_field_end_step
57419Svbart@nginx.com         nxt_http_lookup_field_end_step
57519Svbart@nginx.com         nxt_http_lookup_field_end_step
57619Svbart@nginx.com         nxt_http_lookup_field_end_step
57719Svbart@nginx.com 
57816Svbart@nginx.com         nxt_http_lookup_field_end_step
57916Svbart@nginx.com         nxt_http_lookup_field_end_step
58016Svbart@nginx.com         nxt_http_lookup_field_end_step
58116Svbart@nginx.com         nxt_http_lookup_field_end_step
58216Svbart@nginx.com 
58316Svbart@nginx.com         nxt_http_lookup_field_end_step
58416Svbart@nginx.com         nxt_http_lookup_field_end_step
58516Svbart@nginx.com         nxt_http_lookup_field_end_step
58616Svbart@nginx.com         nxt_http_lookup_field_end_step
58716Svbart@nginx.com     }
58816Svbart@nginx.com 
58919Svbart@nginx.com     for (n = (end - p) / 4; nxt_fast_path(n != 0); n--) {
59016Svbart@nginx.com         nxt_http_lookup_field_end_step
59119Svbart@nginx.com         nxt_http_lookup_field_end_step
59216Svbart@nginx.com         nxt_http_lookup_field_end_step
59316Svbart@nginx.com         nxt_http_lookup_field_end_step
59419Svbart@nginx.com     }
59519Svbart@nginx.com 
59619Svbart@nginx.com     switch (end - p) {
59716Svbart@nginx.com     case 3:
59816Svbart@nginx.com         nxt_http_lookup_field_end_step
59939Svbart@nginx.com         /* Fall through. */
60016Svbart@nginx.com     case 2:
60116Svbart@nginx.com         nxt_http_lookup_field_end_step
60239Svbart@nginx.com         /* Fall through. */
60316Svbart@nginx.com     case 1:
60416Svbart@nginx.com         nxt_http_lookup_field_end_step
60539Svbart@nginx.com         /* Fall through. */
60616Svbart@nginx.com     case 0:
60716Svbart@nginx.com         break;
60816Svbart@nginx.com     default:
60916Svbart@nginx.com         nxt_unreachable();
61016Svbart@nginx.com     }
61116Svbart@nginx.com 
61216Svbart@nginx.com #undef nxt_http_lookup_field_end_step
61316Svbart@nginx.com 
61416Svbart@nginx.com     return p;
61516Svbart@nginx.com }
61616Svbart@nginx.com 
61716Svbart@nginx.com 
61816Svbart@nginx.com static nxt_int_t
61916Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos,
62016Svbart@nginx.com     u_char *end)
62116Svbart@nginx.com {
62260Svbart@nginx.com     u_char            *p;
62360Svbart@nginx.com     nxt_http_field_t  *field;
62416Svbart@nginx.com 
62516Svbart@nginx.com     p = *pos;
62616Svbart@nginx.com 
62716Svbart@nginx.com     if (nxt_fast_path(*p == '\r')) {
62816Svbart@nginx.com         p++;
62916Svbart@nginx.com 
63016Svbart@nginx.com         if (nxt_slow_path(p == end)) {
63116Svbart@nginx.com             rp->handler = &nxt_http_parse_field_end;
63216Svbart@nginx.com             return NXT_AGAIN;
63316Svbart@nginx.com         }
63416Svbart@nginx.com     }
63516Svbart@nginx.com 
63616Svbart@nginx.com     if (nxt_fast_path(*p == '\n')) {
63716Svbart@nginx.com         *pos = p + 1;
63816Svbart@nginx.com 
639*67Svbart@nginx.com         if (rp->field_name.length != 0) {
64060Svbart@nginx.com             field = nxt_list_add(rp->fields);
64116Svbart@nginx.com 
64260Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
64360Svbart@nginx.com                 return NXT_ERROR;
64416Svbart@nginx.com             }
64516Svbart@nginx.com 
646*67Svbart@nginx.com             field->name = rp->field_name;
647*67Svbart@nginx.com             field->value = rp->field_value;
64860Svbart@nginx.com 
649*67Svbart@nginx.com             nxt_http_fields_hash_lookup(rp->fields_hash, rp->field_key.ui64,
650*67Svbart@nginx.com                                         field);
651*67Svbart@nginx.com 
652*67Svbart@nginx.com             nxt_memzero(rp->field_key.str, 32);
653*67Svbart@nginx.com 
654*67Svbart@nginx.com             rp->field_name.length = 0;
655*67Svbart@nginx.com             rp->field_value.length = 0;
65616Svbart@nginx.com 
65716Svbart@nginx.com             rp->handler = &nxt_http_parse_field_name;
65816Svbart@nginx.com             return NXT_OK;
65916Svbart@nginx.com         }
66016Svbart@nginx.com 
66116Svbart@nginx.com         return NXT_DONE;
66216Svbart@nginx.com     }
66316Svbart@nginx.com 
66416Svbart@nginx.com     return NXT_ERROR;
66516Svbart@nginx.com }
66616Svbart@nginx.com 
66716Svbart@nginx.com 
66860Svbart@nginx.com nxt_http_fields_hash_t *
66960Svbart@nginx.com nxt_http_fields_hash_create(nxt_http_fields_hash_entry_t *entries,
67065Sigor@sysoev.ru     nxt_mp_t *mp)
67116Svbart@nginx.com {
67260Svbart@nginx.com     size_t                      min_length, max_length, length, size;
67360Svbart@nginx.com     nxt_uint_t                  i, j, n;
67460Svbart@nginx.com     nxt_http_fields_hash_t      *hash;
67560Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
67616Svbart@nginx.com 
67738Svbart@nginx.com     min_length = 32 + 1;
67816Svbart@nginx.com     max_length = 0;
67916Svbart@nginx.com 
68060Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
68160Svbart@nginx.com         length = entries[i].name.length;
68216Svbart@nginx.com 
68316Svbart@nginx.com         if (length > 32) {
68416Svbart@nginx.com             /* TODO */
68516Svbart@nginx.com             return NULL;
68616Svbart@nginx.com         }
68716Svbart@nginx.com 
68816Svbart@nginx.com         min_length = nxt_min(length, min_length);
68916Svbart@nginx.com         max_length = nxt_max(length, max_length);
69016Svbart@nginx.com     }
69116Svbart@nginx.com 
69238Svbart@nginx.com     size = sizeof(nxt_http_fields_hash_t);
69316Svbart@nginx.com 
69438Svbart@nginx.com     if (min_length <= 32) {
69538Svbart@nginx.com         size += (max_length - min_length + 1)
69660Svbart@nginx.com                 * sizeof(nxt_http_fields_hash_elt_t *);
69738Svbart@nginx.com     }
69816Svbart@nginx.com 
69965Sigor@sysoev.ru     hash = nxt_mp_zget(mp, size);
70016Svbart@nginx.com     if (nxt_slow_path(hash == NULL)) {
70116Svbart@nginx.com         return NULL;
70216Svbart@nginx.com     }
70316Svbart@nginx.com 
70416Svbart@nginx.com     hash->min_length = min_length;
70516Svbart@nginx.com     hash->max_length = max_length;
70616Svbart@nginx.com 
70760Svbart@nginx.com     for (i = 0; entries[i].handler != NULL; i++) {
70860Svbart@nginx.com         length = entries[i].name.length;
70960Svbart@nginx.com         elt = hash->elts[length - min_length];
71016Svbart@nginx.com 
71160Svbart@nginx.com         if (elt != NULL) {
71216Svbart@nginx.com             continue;
71316Svbart@nginx.com         }
71416Svbart@nginx.com 
71516Svbart@nginx.com         n = 1;
71616Svbart@nginx.com 
71760Svbart@nginx.com         for (j = i + 1; entries[j].handler != NULL; j++) {
71860Svbart@nginx.com             if (length == entries[j].name.length) {
71916Svbart@nginx.com                 n++;
72016Svbart@nginx.com             }
72116Svbart@nginx.com         }
72216Svbart@nginx.com 
72360Svbart@nginx.com         size = sizeof(nxt_http_fields_hash_elt_t) + nxt_align_size(length, 8);
72416Svbart@nginx.com 
72565Sigor@sysoev.ru         elt = nxt_mp_zget(mp, n * size + sizeof(nxt_http_fields_hash_elt_t));
72616Svbart@nginx.com 
72760Svbart@nginx.com         if (nxt_slow_path(elt == NULL)) {
72816Svbart@nginx.com             return NULL;
72916Svbart@nginx.com         }
73016Svbart@nginx.com 
73160Svbart@nginx.com         hash->elts[length - min_length] = elt;
73216Svbart@nginx.com 
73360Svbart@nginx.com         for (j = i; entries[j].handler != NULL; j++) {
73460Svbart@nginx.com             if (length != entries[j].name.length) {
73516Svbart@nginx.com                 continue;
73616Svbart@nginx.com             }
73716Svbart@nginx.com 
738*67Svbart@nginx.com             elt->handler = entries[j].handler;
739*67Svbart@nginx.com             elt->data = entries[j].data;
74016Svbart@nginx.com 
74160Svbart@nginx.com             nxt_memcpy_lowcase(elt->key->str, entries[j].name.start, length);
74216Svbart@nginx.com 
74316Svbart@nginx.com             n--;
74416Svbart@nginx.com 
74516Svbart@nginx.com             if (n == 0) {
74616Svbart@nginx.com                 break;
74716Svbart@nginx.com             }
74816Svbart@nginx.com 
74960Svbart@nginx.com             elt = (nxt_http_fields_hash_elt_t *) ((u_char *) elt + size);
75016Svbart@nginx.com         }
75116Svbart@nginx.com     }
75216Svbart@nginx.com 
75316Svbart@nginx.com     return hash;
75416Svbart@nginx.com }
75560Svbart@nginx.com 
75660Svbart@nginx.com 
757*67Svbart@nginx.com static void
758*67Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, uint64_t key[4],
75960Svbart@nginx.com     nxt_http_field_t *field)
76060Svbart@nginx.com {
76160Svbart@nginx.com     nxt_http_fields_hash_elt_t  *elt;
76260Svbart@nginx.com 
763*67Svbart@nginx.com     if (hash == NULL || field->name.length < hash->min_length) {
764*67Svbart@nginx.com         goto not_found;
76560Svbart@nginx.com     }
76660Svbart@nginx.com 
76760Svbart@nginx.com     if (field->name.length > hash->max_length) {
76860Svbart@nginx.com 
76960Svbart@nginx.com         if (field->name.length > 32 && hash->long_fields != NULL) {
770*67Svbart@nginx.com             nxt_http_fields_hash_lookup_long(hash, field);
771*67Svbart@nginx.com             return;
77260Svbart@nginx.com         }
77360Svbart@nginx.com 
774*67Svbart@nginx.com         goto not_found;
77560Svbart@nginx.com     }
77660Svbart@nginx.com 
77760Svbart@nginx.com     elt = hash->elts[field->name.length - hash->min_length];
77860Svbart@nginx.com 
77960Svbart@nginx.com     if (elt == NULL) {
780*67Svbart@nginx.com         goto not_found;
78160Svbart@nginx.com     }
78260Svbart@nginx.com 
78360Svbart@nginx.com     switch ((field->name.length + 7) / 8) {
78460Svbart@nginx.com     case 1:
78560Svbart@nginx.com         do {
786*67Svbart@nginx.com             if (elt->key[0].ui64 == key[0]) {
787*67Svbart@nginx.com                 break;
78860Svbart@nginx.com             }
78960Svbart@nginx.com 
79060Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 1);
79160Svbart@nginx.com 
792*67Svbart@nginx.com         } while (elt->handler != NULL);
79360Svbart@nginx.com 
79460Svbart@nginx.com         break;
79560Svbart@nginx.com 
79660Svbart@nginx.com     case 2:
79760Svbart@nginx.com         do {
798*67Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
799*67Svbart@nginx.com                 && elt->key[1].ui64 == key[1])
80060Svbart@nginx.com             {
801*67Svbart@nginx.com                 break;
80260Svbart@nginx.com             }
80360Svbart@nginx.com 
80460Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 2);
80560Svbart@nginx.com 
806*67Svbart@nginx.com         } while (elt->handler != NULL);
80760Svbart@nginx.com 
80860Svbart@nginx.com         break;
80960Svbart@nginx.com 
81060Svbart@nginx.com     case 3:
81160Svbart@nginx.com         do {
812*67Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
813*67Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
814*67Svbart@nginx.com                 && elt->key[2].ui64 == key[2])
81560Svbart@nginx.com             {
816*67Svbart@nginx.com                 break;
81760Svbart@nginx.com             }
81860Svbart@nginx.com 
81960Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 3);
82060Svbart@nginx.com 
821*67Svbart@nginx.com         } while (elt->handler != NULL);
82260Svbart@nginx.com 
82360Svbart@nginx.com         break;
82460Svbart@nginx.com 
82560Svbart@nginx.com     case 4:
82660Svbart@nginx.com         do {
827*67Svbart@nginx.com             if (elt->key[0].ui64 == key[0]
828*67Svbart@nginx.com                 && elt->key[1].ui64 == key[1]
829*67Svbart@nginx.com                 && elt->key[2].ui64 == key[2]
830*67Svbart@nginx.com                 && elt->key[3].ui64 == key[3])
83160Svbart@nginx.com             {
832*67Svbart@nginx.com                 break;
83360Svbart@nginx.com             }
83460Svbart@nginx.com 
83560Svbart@nginx.com             elt = nxt_http_fields_hash_next_elt(elt, 4);
83660Svbart@nginx.com 
837*67Svbart@nginx.com         } while (elt->handler != NULL);
83860Svbart@nginx.com 
83960Svbart@nginx.com         break;
84060Svbart@nginx.com 
84160Svbart@nginx.com     default:
84260Svbart@nginx.com         nxt_unreachable();
84360Svbart@nginx.com     }
84460Svbart@nginx.com 
845*67Svbart@nginx.com     field->handler = elt->handler;
846*67Svbart@nginx.com     field->data = elt->data;
847*67Svbart@nginx.com 
848*67Svbart@nginx.com     return;
849*67Svbart@nginx.com 
850*67Svbart@nginx.com not_found:
851*67Svbart@nginx.com 
852*67Svbart@nginx.com     field->handler = NULL;
853*67Svbart@nginx.com     field->data = 0;
85460Svbart@nginx.com }
85560Svbart@nginx.com 
85660Svbart@nginx.com 
857*67Svbart@nginx.com static void
85860Svbart@nginx.com nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash,
85960Svbart@nginx.com     nxt_http_field_t *field)
86060Svbart@nginx.com {
86160Svbart@nginx.com     /* TODO */
862*67Svbart@nginx.com 
863*67Svbart@nginx.com     field->handler = NULL;
864*67Svbart@nginx.com     field->data = 0;
86560Svbart@nginx.com }
86660Svbart@nginx.com 
86760Svbart@nginx.com 
86860Svbart@nginx.com nxt_int_t
869*67Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, void *ctx, nxt_log_t *log)
87060Svbart@nginx.com {
871*67Svbart@nginx.com     nxt_int_t         rc;
872*67Svbart@nginx.com     nxt_http_field_t  *field;
87360Svbart@nginx.com 
87460Svbart@nginx.com     nxt_list_each(field, fields) {
87560Svbart@nginx.com 
876*67Svbart@nginx.com         if (field->handler != NULL) {
877*67Svbart@nginx.com             rc = field->handler(ctx, field, log);
87860Svbart@nginx.com 
87960Svbart@nginx.com             if (rc != NXT_OK) {
88060Svbart@nginx.com                 return rc;
88160Svbart@nginx.com             }
88260Svbart@nginx.com         }
88360Svbart@nginx.com 
88460Svbart@nginx.com     } nxt_list_loop;
88560Svbart@nginx.com 
88660Svbart@nginx.com     return NXT_OK;
88760Svbart@nginx.com }
888