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