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