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 static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, 1116Svbart@nginx.com u_char **pos, u_char *end); 1216Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp, 1316Svbart@nginx.com u_char **pos, u_char *end); 1416Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp, 1516Svbart@nginx.com u_char **pos, u_char *end); 1616Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp, 1716Svbart@nginx.com u_char **pos, u_char *end); 1816Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end); 1916Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp, 2016Svbart@nginx.com u_char **pos, u_char *end); 2116Svbart@nginx.com 22*417Svbart@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp); 23*417Svbart@nginx.com 24*417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data); 25*417Svbart@nginx.com static void *nxt_http_field_hash_alloc(void *pool, size_t size); 26*417Svbart@nginx.com static void nxt_http_field_hash_free(void *pool, void *p); 27*417Svbart@nginx.com 28*417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, 29*417Svbart@nginx.com void *data); 3016Svbart@nginx.com 31*417Svbart@nginx.com 32*417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_NAME 0xff 33*417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_VALUE NXT_INT32_T_MAX 34*417Svbart@nginx.com 35*417Svbart@nginx.com #define NXT_HTTP_FIELD_LVLHSH_SHIFT 5 36*417Svbart@nginx.com 37*417Svbart@nginx.com #define NXT_HTTP_FIELD_HASH_INIT 159406 38*417Svbart@nginx.com #define nxt_http_field_hash_char(h, c) (((h) << 4) + (h) + (c)) 39*417Svbart@nginx.com #define nxt_http_field_hash_end(h) (((h) >> 16) ^ (h)) 40112Smax.romanov@nginx.com 4116Svbart@nginx.com 4216Svbart@nginx.com typedef enum { 4316Svbart@nginx.com NXT_HTTP_TARGET_SPACE = 1, /* \s */ 4416Svbart@nginx.com NXT_HTTP_TARGET_HASH, /* # */ 4516Svbart@nginx.com NXT_HTTP_TARGET_AGAIN, 4616Svbart@nginx.com NXT_HTTP_TARGET_BAD, /* \0\r\n */ 4716Svbart@nginx.com 4816Svbart@nginx.com /* traps below are used for extended check only */ 4916Svbart@nginx.com 5016Svbart@nginx.com NXT_HTTP_TARGET_SLASH = 5, /* / */ 5116Svbart@nginx.com NXT_HTTP_TARGET_DOT, /* . */ 5216Svbart@nginx.com NXT_HTTP_TARGET_ARGS_MARK, /* ? */ 5316Svbart@nginx.com NXT_HTTP_TARGET_QUOTE_MARK, /* % */ 5416Svbart@nginx.com NXT_HTTP_TARGET_PLUS, /* + */ 5516Svbart@nginx.com } nxt_http_target_traps_e; 5616Svbart@nginx.com 5716Svbart@nginx.com 5816Svbart@nginx.com static const uint8_t nxt_http_target_chars[256] nxt_aligned(64) = { 5916Svbart@nginx.com /* \0 \n \r */ 6016Svbart@nginx.com 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, 6116Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6252Svbart@nginx.com 6352Svbart@nginx.com /* \s ! " # $ % & ' ( ) * + , - . / */ 6416Svbart@nginx.com 1, 0, 0, 2, 0, 8, 0, 0, 0, 0, 0, 9, 0, 0, 6, 5, 6552Svbart@nginx.com 6652Svbart@nginx.com /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 6716Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 6816Svbart@nginx.com }; 6916Svbart@nginx.com 7016Svbart@nginx.com 7116Svbart@nginx.com nxt_inline nxt_http_target_traps_e 7216Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end) 7316Svbart@nginx.com { 7416Svbart@nginx.com u_char *p; 7516Svbart@nginx.com nxt_uint_t trap; 7616Svbart@nginx.com 7716Svbart@nginx.com p = *pos; 7816Svbart@nginx.com 79409Svbart@nginx.com while (nxt_fast_path(end - p >= 10)) { 8016Svbart@nginx.com 81409Svbart@nginx.com #define nxt_target_test_char(ch) \ 82409Svbart@nginx.com \ 83409Svbart@nginx.com trap = nxt_http_target_chars[ch]; \ 8416Svbart@nginx.com \ 85409Svbart@nginx.com if (nxt_slow_path(trap != 0)) { \ 86409Svbart@nginx.com *pos = &(ch); \ 87409Svbart@nginx.com return trap; \ 8816Svbart@nginx.com } 8916Svbart@nginx.com 90409Svbart@nginx.com /* enddef */ 91409Svbart@nginx.com 92409Svbart@nginx.com nxt_target_test_char(p[0]); 93409Svbart@nginx.com nxt_target_test_char(p[1]); 94409Svbart@nginx.com nxt_target_test_char(p[2]); 95409Svbart@nginx.com nxt_target_test_char(p[3]); 9616Svbart@nginx.com 97409Svbart@nginx.com nxt_target_test_char(p[4]); 98409Svbart@nginx.com nxt_target_test_char(p[5]); 99409Svbart@nginx.com nxt_target_test_char(p[6]); 100409Svbart@nginx.com nxt_target_test_char(p[7]); 10116Svbart@nginx.com 102409Svbart@nginx.com nxt_target_test_char(p[8]); 103409Svbart@nginx.com nxt_target_test_char(p[9]); 10416Svbart@nginx.com 105409Svbart@nginx.com p += 10; 10616Svbart@nginx.com } 10716Svbart@nginx.com 108410Svbart@nginx.com while (p != end) { 109410Svbart@nginx.com nxt_target_test_char(*p); p++; 110410Svbart@nginx.com } 111410Svbart@nginx.com 112409Svbart@nginx.com return NXT_HTTP_TARGET_AGAIN; 11316Svbart@nginx.com } 11416Svbart@nginx.com 11516Svbart@nginx.com 11616Svbart@nginx.com nxt_int_t 117*417Svbart@nginx.com nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp) 118*417Svbart@nginx.com { 119*417Svbart@nginx.com rp->mem_pool = mp; 120*417Svbart@nginx.com 121*417Svbart@nginx.com rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t)); 122*417Svbart@nginx.com if (nxt_slow_path(rp->fields == NULL)){ 123*417Svbart@nginx.com return NXT_ERROR; 124*417Svbart@nginx.com } 125*417Svbart@nginx.com 126*417Svbart@nginx.com rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; 127*417Svbart@nginx.com 128*417Svbart@nginx.com return NXT_OK; 129*417Svbart@nginx.com } 130*417Svbart@nginx.com 131*417Svbart@nginx.com 132*417Svbart@nginx.com nxt_int_t 13316Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) 13416Svbart@nginx.com { 13516Svbart@nginx.com nxt_int_t rc; 13616Svbart@nginx.com 13716Svbart@nginx.com if (rp->handler == NULL) { 13816Svbart@nginx.com rp->handler = &nxt_http_parse_request_line; 13916Svbart@nginx.com } 14016Svbart@nginx.com 14116Svbart@nginx.com do { 14216Svbart@nginx.com rc = rp->handler(rp, &b->pos, b->free); 14316Svbart@nginx.com } while (rc == NXT_OK); 14416Svbart@nginx.com 14516Svbart@nginx.com return rc; 14616Svbart@nginx.com } 14716Svbart@nginx.com 14816Svbart@nginx.com 14916Svbart@nginx.com static nxt_int_t 15016Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos, 15116Svbart@nginx.com u_char *end) 15216Svbart@nginx.com { 15316Svbart@nginx.com u_char *p, ch, *after_slash; 15416Svbart@nginx.com nxt_int_t rc; 15516Svbart@nginx.com nxt_http_ver_t version; 15616Svbart@nginx.com nxt_http_target_traps_e trap; 15716Svbart@nginx.com 15816Svbart@nginx.com static const nxt_http_ver_t http11 = { "HTTP/1.1" }; 15916Svbart@nginx.com static const nxt_http_ver_t http10 = { "HTTP/1.0" }; 16016Svbart@nginx.com 16116Svbart@nginx.com p = *pos; 16216Svbart@nginx.com 16316Svbart@nginx.com rp->method.start = p; 16416Svbart@nginx.com 165409Svbart@nginx.com for ( ;; ) { 166409Svbart@nginx.com 167409Svbart@nginx.com while (nxt_fast_path(end - p >= 8)) { 16816Svbart@nginx.com 169409Svbart@nginx.com #define nxt_method_test_char(ch) \ 170409Svbart@nginx.com \ 171409Svbart@nginx.com if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) { \ 172409Svbart@nginx.com p = &(ch); \ 173409Svbart@nginx.com goto method_unusual_char; \ 17416Svbart@nginx.com } 17516Svbart@nginx.com 176409Svbart@nginx.com /* enddef */ 177409Svbart@nginx.com 178409Svbart@nginx.com nxt_method_test_char(p[0]); 179409Svbart@nginx.com nxt_method_test_char(p[1]); 180409Svbart@nginx.com nxt_method_test_char(p[2]); 181409Svbart@nginx.com nxt_method_test_char(p[3]); 18216Svbart@nginx.com 183409Svbart@nginx.com nxt_method_test_char(p[4]); 184409Svbart@nginx.com nxt_method_test_char(p[5]); 185409Svbart@nginx.com nxt_method_test_char(p[6]); 186409Svbart@nginx.com nxt_method_test_char(p[7]); 18716Svbart@nginx.com 188409Svbart@nginx.com p += 8; 189409Svbart@nginx.com } 19016Svbart@nginx.com 191410Svbart@nginx.com while (p != end) { 192410Svbart@nginx.com nxt_method_test_char(*p); p++; 193410Svbart@nginx.com } 194410Svbart@nginx.com 195409Svbart@nginx.com return NXT_AGAIN; 196409Svbart@nginx.com 197409Svbart@nginx.com method_unusual_char: 198409Svbart@nginx.com 199409Svbart@nginx.com ch = *p; 20016Svbart@nginx.com 20116Svbart@nginx.com if (nxt_fast_path(ch == ' ')) { 20216Svbart@nginx.com rp->method.length = p - rp->method.start; 20316Svbart@nginx.com break; 20416Svbart@nginx.com } 20516Svbart@nginx.com 20616Svbart@nginx.com if (ch == '_' || ch == '-') { 207409Svbart@nginx.com p++; 20816Svbart@nginx.com continue; 20916Svbart@nginx.com } 21016Svbart@nginx.com 21116Svbart@nginx.com if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) { 21216Svbart@nginx.com rp->method.start++; 213409Svbart@nginx.com p++; 21416Svbart@nginx.com continue; 21516Svbart@nginx.com } 21616Svbart@nginx.com 21716Svbart@nginx.com return NXT_ERROR; 21816Svbart@nginx.com } 21916Svbart@nginx.com 22016Svbart@nginx.com p++; 22116Svbart@nginx.com 22216Svbart@nginx.com if (nxt_slow_path(p == end)) { 22316Svbart@nginx.com return NXT_AGAIN; 22416Svbart@nginx.com } 22516Svbart@nginx.com 22616Svbart@nginx.com /* target */ 22716Svbart@nginx.com 22816Svbart@nginx.com ch = *p; 22916Svbart@nginx.com 23016Svbart@nginx.com if (nxt_slow_path(ch != '/')) { 23116Svbart@nginx.com rc = nxt_http_parse_unusual_target(rp, &p, end); 23216Svbart@nginx.com 23316Svbart@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 23416Svbart@nginx.com return rc; 23516Svbart@nginx.com } 23616Svbart@nginx.com } 23716Svbart@nginx.com 23816Svbart@nginx.com rp->target_start = p; 23916Svbart@nginx.com 24016Svbart@nginx.com after_slash = p + 1; 24116Svbart@nginx.com 24216Svbart@nginx.com for ( ;; ) { 24316Svbart@nginx.com p++; 24416Svbart@nginx.com 24516Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 24616Svbart@nginx.com 24716Svbart@nginx.com switch (trap) { 24816Svbart@nginx.com case NXT_HTTP_TARGET_SLASH: 24916Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 25016Svbart@nginx.com rp->complex_target = 1; 25116Svbart@nginx.com goto rest_of_target; 25216Svbart@nginx.com } 25316Svbart@nginx.com 25416Svbart@nginx.com after_slash = p + 1; 25516Svbart@nginx.com 25616Svbart@nginx.com rp->exten_start = NULL; 25716Svbart@nginx.com continue; 25816Svbart@nginx.com 25916Svbart@nginx.com case NXT_HTTP_TARGET_DOT: 26016Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 26116Svbart@nginx.com rp->complex_target = 1; 26216Svbart@nginx.com goto rest_of_target; 26316Svbart@nginx.com } 26416Svbart@nginx.com 26516Svbart@nginx.com rp->exten_start = p + 1; 26616Svbart@nginx.com continue; 26716Svbart@nginx.com 26816Svbart@nginx.com case NXT_HTTP_TARGET_ARGS_MARK: 26916Svbart@nginx.com rp->args_start = p + 1; 27016Svbart@nginx.com goto rest_of_target; 27116Svbart@nginx.com 27216Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 27316Svbart@nginx.com rp->target_end = p; 27416Svbart@nginx.com goto space_after_target; 27516Svbart@nginx.com 27616Svbart@nginx.com case NXT_HTTP_TARGET_QUOTE_MARK: 27716Svbart@nginx.com rp->quoted_target = 1; 27816Svbart@nginx.com goto rest_of_target; 27916Svbart@nginx.com 28016Svbart@nginx.com case NXT_HTTP_TARGET_PLUS: 28116Svbart@nginx.com rp->plus_in_target = 1; 28216Svbart@nginx.com continue; 28316Svbart@nginx.com 28416Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 28516Svbart@nginx.com rp->complex_target = 1; 28616Svbart@nginx.com goto rest_of_target; 28716Svbart@nginx.com 28816Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 28916Svbart@nginx.com return NXT_AGAIN; 29016Svbart@nginx.com 29116Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 29216Svbart@nginx.com return NXT_ERROR; 29316Svbart@nginx.com } 29416Svbart@nginx.com 29516Svbart@nginx.com nxt_unreachable(); 29616Svbart@nginx.com } 29716Svbart@nginx.com 29816Svbart@nginx.com rest_of_target: 29916Svbart@nginx.com 30016Svbart@nginx.com for ( ;; ) { 30116Svbart@nginx.com p++; 30216Svbart@nginx.com 30319Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 30416Svbart@nginx.com 30516Svbart@nginx.com switch (trap) { 30616Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 30716Svbart@nginx.com rp->target_end = p; 30816Svbart@nginx.com goto space_after_target; 30916Svbart@nginx.com 31016Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 31116Svbart@nginx.com rp->complex_target = 1; 31216Svbart@nginx.com continue; 31316Svbart@nginx.com 31416Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 31516Svbart@nginx.com return NXT_AGAIN; 31616Svbart@nginx.com 31716Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 31816Svbart@nginx.com return NXT_ERROR; 31916Svbart@nginx.com 32016Svbart@nginx.com default: 32116Svbart@nginx.com continue; 32216Svbart@nginx.com } 32316Svbart@nginx.com 32416Svbart@nginx.com nxt_unreachable(); 32516Svbart@nginx.com } 32616Svbart@nginx.com 32716Svbart@nginx.com space_after_target: 32816Svbart@nginx.com 32916Svbart@nginx.com if (nxt_slow_path(end - p < 10)) { 330410Svbart@nginx.com 331410Svbart@nginx.com do { 332410Svbart@nginx.com p++; 333410Svbart@nginx.com 334410Svbart@nginx.com if (p == end) { 335410Svbart@nginx.com return NXT_AGAIN; 336410Svbart@nginx.com } 337410Svbart@nginx.com 338410Svbart@nginx.com } while (*p == ' '); 339410Svbart@nginx.com 340410Svbart@nginx.com if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) { 341410Svbart@nginx.com 342410Svbart@nginx.com switch (end - p) { 343410Svbart@nginx.com case 8: 344410Svbart@nginx.com if (p[7] < '0' || p[7] > '9') { 345410Svbart@nginx.com break; 346410Svbart@nginx.com } 347410Svbart@nginx.com /* Fall through. */ 348410Svbart@nginx.com case 7: 349410Svbart@nginx.com if (p[6] != '.') { 350410Svbart@nginx.com break; 351410Svbart@nginx.com } 352410Svbart@nginx.com /* Fall through. */ 353410Svbart@nginx.com case 6: 354410Svbart@nginx.com if (p[5] < '0' || p[5] > '9') { 355410Svbart@nginx.com break; 356410Svbart@nginx.com } 357410Svbart@nginx.com /* Fall through. */ 358410Svbart@nginx.com default: 359410Svbart@nginx.com return NXT_AGAIN; 360410Svbart@nginx.com } 361410Svbart@nginx.com } 362410Svbart@nginx.com 363410Svbart@nginx.com rp->space_in_target = 1; 364410Svbart@nginx.com goto rest_of_target; 36516Svbart@nginx.com } 36616Svbart@nginx.com 36716Svbart@nginx.com /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */ 36816Svbart@nginx.com 36916Svbart@nginx.com nxt_memcpy(version.str, &p[1], 8); 37016Svbart@nginx.com 37116Svbart@nginx.com if (nxt_fast_path((version.ui64 == http11.ui64 37216Svbart@nginx.com || version.ui64 == http10.ui64 37316Svbart@nginx.com || (p[1] == 'H' 37416Svbart@nginx.com && p[2] == 'T' 37516Svbart@nginx.com && p[3] == 'T' 37616Svbart@nginx.com && p[4] == 'P' 37716Svbart@nginx.com && p[5] == '/' 37816Svbart@nginx.com && p[6] >= '0' && p[6] <= '9' 37916Svbart@nginx.com && p[7] == '.' 38016Svbart@nginx.com && p[8] >= '0' && p[8] <= '9')) 38116Svbart@nginx.com && (p[9] == '\r' || p[9] == '\n'))) 38216Svbart@nginx.com { 38316Svbart@nginx.com rp->version.ui64 = version.ui64; 38416Svbart@nginx.com 38516Svbart@nginx.com if (nxt_fast_path(p[9] == '\r')) { 38616Svbart@nginx.com p += 10; 38716Svbart@nginx.com 38816Svbart@nginx.com if (nxt_slow_path(p == end)) { 38916Svbart@nginx.com return NXT_AGAIN; 39016Svbart@nginx.com } 39116Svbart@nginx.com 39216Svbart@nginx.com if (nxt_slow_path(*p != '\n')) { 39316Svbart@nginx.com return NXT_ERROR; 39416Svbart@nginx.com } 39516Svbart@nginx.com 39616Svbart@nginx.com *pos = p + 1; 397112Smax.romanov@nginx.com 398112Smax.romanov@nginx.com } else { 399112Smax.romanov@nginx.com *pos = p + 10; 400112Smax.romanov@nginx.com } 401112Smax.romanov@nginx.com 402112Smax.romanov@nginx.com if (rp->complex_target != 0 || rp->quoted_target != 0) { 403112Smax.romanov@nginx.com rc = nxt_http_parse_complex_target(rp); 404112Smax.romanov@nginx.com 405112Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 406112Smax.romanov@nginx.com return rc; 407112Smax.romanov@nginx.com } 408112Smax.romanov@nginx.com 40916Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 41016Svbart@nginx.com } 41116Svbart@nginx.com 412112Smax.romanov@nginx.com rp->path.start = rp->target_start; 413112Smax.romanov@nginx.com 414112Smax.romanov@nginx.com if (rp->args_start != NULL) { 415112Smax.romanov@nginx.com rp->path.length = rp->args_start - rp->target_start - 1; 416112Smax.romanov@nginx.com 417112Smax.romanov@nginx.com rp->args.start = rp->args_start; 418112Smax.romanov@nginx.com rp->args.length = rp->target_end - rp->args_start; 419112Smax.romanov@nginx.com 420112Smax.romanov@nginx.com } else { 421112Smax.romanov@nginx.com rp->path.length = rp->target_end - rp->target_start; 422112Smax.romanov@nginx.com } 423112Smax.romanov@nginx.com 424112Smax.romanov@nginx.com if (rp->exten_start) { 425112Smax.romanov@nginx.com rp->exten.length = rp->path.start + rp->path.length - 426112Smax.romanov@nginx.com rp->exten_start; 427112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 428112Smax.romanov@nginx.com } 429112Smax.romanov@nginx.com 43016Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 43116Svbart@nginx.com } 43216Svbart@nginx.com 43316Svbart@nginx.com if (p[1] == ' ') { 43416Svbart@nginx.com /* surplus space after tartet */ 43516Svbart@nginx.com p++; 43616Svbart@nginx.com goto space_after_target; 43716Svbart@nginx.com } 43816Svbart@nginx.com 43916Svbart@nginx.com rp->space_in_target = 1; 44016Svbart@nginx.com goto rest_of_target; 44116Svbart@nginx.com } 44216Svbart@nginx.com 44316Svbart@nginx.com 44416Svbart@nginx.com static nxt_int_t 44516Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos, 44616Svbart@nginx.com u_char *end) 44716Svbart@nginx.com { 44816Svbart@nginx.com u_char *p, ch; 44916Svbart@nginx.com 45016Svbart@nginx.com p = *pos; 45116Svbart@nginx.com 45216Svbart@nginx.com ch = *p; 45316Svbart@nginx.com 45416Svbart@nginx.com if (ch == ' ') { 45516Svbart@nginx.com /* skip surplus spaces before target */ 45616Svbart@nginx.com 45716Svbart@nginx.com do { 45816Svbart@nginx.com p++; 45916Svbart@nginx.com 46016Svbart@nginx.com if (nxt_slow_path(p == end)) { 46116Svbart@nginx.com return NXT_AGAIN; 46216Svbart@nginx.com } 46316Svbart@nginx.com 46416Svbart@nginx.com ch = *p; 46516Svbart@nginx.com 46616Svbart@nginx.com } while (ch == ' '); 46716Svbart@nginx.com 46816Svbart@nginx.com if (ch == '/') { 46916Svbart@nginx.com *pos = p; 47016Svbart@nginx.com return NXT_OK; 47116Svbart@nginx.com } 47216Svbart@nginx.com } 47316Svbart@nginx.com 47416Svbart@nginx.com /* absolute path or '*' */ 47516Svbart@nginx.com 47616Svbart@nginx.com /* TODO */ 47716Svbart@nginx.com 47816Svbart@nginx.com return NXT_ERROR; 47916Svbart@nginx.com } 48016Svbart@nginx.com 48116Svbart@nginx.com 48216Svbart@nginx.com static nxt_int_t 48316Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, 48416Svbart@nginx.com u_char *end) 48516Svbart@nginx.com { 486*417Svbart@nginx.com u_char *p, c; 487*417Svbart@nginx.com size_t len; 488*417Svbart@nginx.com uint32_t hash; 48916Svbart@nginx.com 49016Svbart@nginx.com static const u_char normal[256] nxt_aligned(64) = 49116Svbart@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" 49216Svbart@nginx.com "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" 49316Svbart@nginx.com 49416Svbart@nginx.com /* These 64 bytes should reside in one cache line. */ 49516Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" 49616Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" 49716Svbart@nginx.com 49816Svbart@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" 49916Svbart@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" 50016Svbart@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" 50116Svbart@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"; 50216Svbart@nginx.com 503*417Svbart@nginx.com p = *pos + rp->field_name.length; 504*417Svbart@nginx.com hash = rp->field_hash; 50516Svbart@nginx.com 506*417Svbart@nginx.com while (nxt_fast_path(end - p >= 8)) { 507409Svbart@nginx.com 508*417Svbart@nginx.com #define nxt_field_name_test_char(ch) \ 509409Svbart@nginx.com \ 51019Svbart@nginx.com c = normal[ch]; \ 51119Svbart@nginx.com \ 51219Svbart@nginx.com if (nxt_slow_path(c == '\0')) { \ 513*417Svbart@nginx.com p = &(ch); \ 51419Svbart@nginx.com goto name_end; \ 51519Svbart@nginx.com } \ 51619Svbart@nginx.com \ 517*417Svbart@nginx.com hash = nxt_http_field_hash_char(hash, c); 518409Svbart@nginx.com 519409Svbart@nginx.com /* enddef */ 52016Svbart@nginx.com 521*417Svbart@nginx.com nxt_field_name_test_char(p[0]); 522*417Svbart@nginx.com nxt_field_name_test_char(p[1]); 523*417Svbart@nginx.com nxt_field_name_test_char(p[2]); 524*417Svbart@nginx.com nxt_field_name_test_char(p[3]); 52516Svbart@nginx.com 526*417Svbart@nginx.com nxt_field_name_test_char(p[4]); 527*417Svbart@nginx.com nxt_field_name_test_char(p[5]); 528*417Svbart@nginx.com nxt_field_name_test_char(p[6]); 529*417Svbart@nginx.com nxt_field_name_test_char(p[7]); 530*417Svbart@nginx.com 531*417Svbart@nginx.com p += 8; 53219Svbart@nginx.com } 53316Svbart@nginx.com 534*417Svbart@nginx.com while (nxt_fast_path(p != end)) { 535*417Svbart@nginx.com nxt_field_name_test_char(*p); p++; 53619Svbart@nginx.com } 53716Svbart@nginx.com 538*417Svbart@nginx.com len = p - *pos; 539*417Svbart@nginx.com 540*417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { 541*417Svbart@nginx.com return NXT_ERROR; 542*417Svbart@nginx.com } 543*417Svbart@nginx.com 544*417Svbart@nginx.com rp->field_hash = hash; 545*417Svbart@nginx.com rp->field_name.length = len; 546*417Svbart@nginx.com 54716Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 54816Svbart@nginx.com 54916Svbart@nginx.com return NXT_AGAIN; 55019Svbart@nginx.com 55119Svbart@nginx.com name_end: 55219Svbart@nginx.com 553*417Svbart@nginx.com if (nxt_fast_path(*p == ':')) { 554*417Svbart@nginx.com if (nxt_slow_path(p == *pos)) { 55519Svbart@nginx.com return NXT_ERROR; 55619Svbart@nginx.com } 55719Svbart@nginx.com 558*417Svbart@nginx.com len = p - *pos; 559*417Svbart@nginx.com 560*417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { 561*417Svbart@nginx.com return NXT_ERROR; 562*417Svbart@nginx.com } 56319Svbart@nginx.com 564*417Svbart@nginx.com rp->field_hash = hash; 565*417Svbart@nginx.com 566*417Svbart@nginx.com rp->field_name.length = len; 567*417Svbart@nginx.com rp->field_name.start = *pos; 568*417Svbart@nginx.com 569*417Svbart@nginx.com *pos = p + 1; 57019Svbart@nginx.com 57119Svbart@nginx.com return nxt_http_parse_field_value(rp, pos, end); 57219Svbart@nginx.com } 57319Svbart@nginx.com 574*417Svbart@nginx.com if (nxt_slow_path(p != *pos)) { 57559Svbart@nginx.com return NXT_ERROR; 57659Svbart@nginx.com } 57719Svbart@nginx.com 57819Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 57916Svbart@nginx.com } 58016Svbart@nginx.com 58116Svbart@nginx.com 58216Svbart@nginx.com static nxt_int_t 58316Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos, 58416Svbart@nginx.com u_char *end) 58516Svbart@nginx.com { 58616Svbart@nginx.com u_char *p, ch; 587*417Svbart@nginx.com size_t len; 58816Svbart@nginx.com 58916Svbart@nginx.com p = *pos; 59016Svbart@nginx.com 59116Svbart@nginx.com for ( ;; ) { 59216Svbart@nginx.com if (nxt_slow_path(p == end)) { 59316Svbart@nginx.com *pos = p; 59416Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 59516Svbart@nginx.com return NXT_AGAIN; 59616Svbart@nginx.com } 59716Svbart@nginx.com 59816Svbart@nginx.com if (*p != ' ') { 59916Svbart@nginx.com break; 60016Svbart@nginx.com } 60116Svbart@nginx.com 60216Svbart@nginx.com p++; 60316Svbart@nginx.com } 60416Svbart@nginx.com 60516Svbart@nginx.com *pos = p; 60616Svbart@nginx.com 60767Svbart@nginx.com p += rp->field_value.length; 60816Svbart@nginx.com 60916Svbart@nginx.com for ( ;; ) { 61016Svbart@nginx.com p = nxt_http_lookup_field_end(p, end); 61116Svbart@nginx.com 61216Svbart@nginx.com if (nxt_slow_path(p == end)) { 613*417Svbart@nginx.com len = p - *pos; 614*417Svbart@nginx.com 615*417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { 616*417Svbart@nginx.com return NXT_ERROR; 617*417Svbart@nginx.com } 618*417Svbart@nginx.com 619*417Svbart@nginx.com rp->field_value.length = len; 62016Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 62116Svbart@nginx.com return NXT_AGAIN; 62216Svbart@nginx.com } 62316Svbart@nginx.com 62416Svbart@nginx.com ch = *p; 62516Svbart@nginx.com 62616Svbart@nginx.com if (nxt_fast_path(ch == '\r' || ch == '\n')) { 62716Svbart@nginx.com break; 62816Svbart@nginx.com } 62916Svbart@nginx.com 63016Svbart@nginx.com if (ch == '\0') { 63116Svbart@nginx.com return NXT_ERROR; 63216Svbart@nginx.com } 63316Svbart@nginx.com } 63416Svbart@nginx.com 63516Svbart@nginx.com if (nxt_fast_path(p != *pos)) { 63616Svbart@nginx.com while (p[-1] == ' ') { 63716Svbart@nginx.com p--; 63816Svbart@nginx.com } 63916Svbart@nginx.com } 64016Svbart@nginx.com 641*417Svbart@nginx.com len = p - *pos; 642*417Svbart@nginx.com 643*417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { 644*417Svbart@nginx.com return NXT_ERROR; 645*417Svbart@nginx.com } 646*417Svbart@nginx.com 647*417Svbart@nginx.com rp->field_value.length = len; 64867Svbart@nginx.com rp->field_value.start = *pos; 64916Svbart@nginx.com 65016Svbart@nginx.com *pos = p; 65116Svbart@nginx.com 65216Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 65316Svbart@nginx.com } 65416Svbart@nginx.com 65516Svbart@nginx.com 65616Svbart@nginx.com static u_char * 65716Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end) 65816Svbart@nginx.com { 659409Svbart@nginx.com while (nxt_fast_path(end - p >= 16)) { 660409Svbart@nginx.com 661409Svbart@nginx.com #define nxt_field_end_test_char(ch) \ 662409Svbart@nginx.com \ 663409Svbart@nginx.com if (nxt_slow_path((ch) < 0x10)) { \ 664409Svbart@nginx.com return &(ch); \ 665409Svbart@nginx.com } 666409Svbart@nginx.com 667409Svbart@nginx.com /* enddef */ 668409Svbart@nginx.com 669409Svbart@nginx.com nxt_field_end_test_char(p[0]); 670409Svbart@nginx.com nxt_field_end_test_char(p[1]); 671409Svbart@nginx.com nxt_field_end_test_char(p[2]); 672409Svbart@nginx.com nxt_field_end_test_char(p[3]); 67316Svbart@nginx.com 674409Svbart@nginx.com nxt_field_end_test_char(p[4]); 675409Svbart@nginx.com nxt_field_end_test_char(p[5]); 676409Svbart@nginx.com nxt_field_end_test_char(p[6]); 677409Svbart@nginx.com nxt_field_end_test_char(p[7]); 678409Svbart@nginx.com 679409Svbart@nginx.com nxt_field_end_test_char(p[8]); 680409Svbart@nginx.com nxt_field_end_test_char(p[9]); 681409Svbart@nginx.com nxt_field_end_test_char(p[10]); 682409Svbart@nginx.com nxt_field_end_test_char(p[11]); 683409Svbart@nginx.com 684409Svbart@nginx.com nxt_field_end_test_char(p[12]); 685409Svbart@nginx.com nxt_field_end_test_char(p[13]); 686409Svbart@nginx.com nxt_field_end_test_char(p[14]); 687409Svbart@nginx.com nxt_field_end_test_char(p[15]); 688409Svbart@nginx.com 689409Svbart@nginx.com p += 16; 69016Svbart@nginx.com } 69116Svbart@nginx.com 692409Svbart@nginx.com while (nxt_fast_path(end - p >= 4)) { 69319Svbart@nginx.com 694409Svbart@nginx.com nxt_field_end_test_char(p[0]); 695409Svbart@nginx.com nxt_field_end_test_char(p[1]); 696409Svbart@nginx.com nxt_field_end_test_char(p[2]); 697409Svbart@nginx.com nxt_field_end_test_char(p[3]); 69816Svbart@nginx.com 699409Svbart@nginx.com p += 4; 70019Svbart@nginx.com } 70119Svbart@nginx.com 70219Svbart@nginx.com switch (end - p) { 70316Svbart@nginx.com case 3: 704409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 70539Svbart@nginx.com /* Fall through. */ 70616Svbart@nginx.com case 2: 707409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 70839Svbart@nginx.com /* Fall through. */ 70916Svbart@nginx.com case 1: 710409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 71139Svbart@nginx.com /* Fall through. */ 71216Svbart@nginx.com case 0: 71316Svbart@nginx.com break; 71416Svbart@nginx.com default: 71516Svbart@nginx.com nxt_unreachable(); 71616Svbart@nginx.com } 71716Svbart@nginx.com 71816Svbart@nginx.com return p; 71916Svbart@nginx.com } 72016Svbart@nginx.com 72116Svbart@nginx.com 72216Svbart@nginx.com static nxt_int_t 72316Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos, 72416Svbart@nginx.com u_char *end) 72516Svbart@nginx.com { 72660Svbart@nginx.com u_char *p; 72760Svbart@nginx.com nxt_http_field_t *field; 72816Svbart@nginx.com 72916Svbart@nginx.com p = *pos; 73016Svbart@nginx.com 73116Svbart@nginx.com if (nxt_fast_path(*p == '\r')) { 73216Svbart@nginx.com p++; 73316Svbart@nginx.com 73416Svbart@nginx.com if (nxt_slow_path(p == end)) { 73516Svbart@nginx.com rp->handler = &nxt_http_parse_field_end; 73616Svbart@nginx.com return NXT_AGAIN; 73716Svbart@nginx.com } 73816Svbart@nginx.com } 73916Svbart@nginx.com 74016Svbart@nginx.com if (nxt_fast_path(*p == '\n')) { 74116Svbart@nginx.com *pos = p + 1; 74216Svbart@nginx.com 74367Svbart@nginx.com if (rp->field_name.length != 0) { 74460Svbart@nginx.com field = nxt_list_add(rp->fields); 74516Svbart@nginx.com 74660Svbart@nginx.com if (nxt_slow_path(field == NULL)) { 74760Svbart@nginx.com return NXT_ERROR; 74816Svbart@nginx.com } 74916Svbart@nginx.com 750*417Svbart@nginx.com field->hash = nxt_http_field_hash_end(rp->field_hash); 751*417Svbart@nginx.com field->skip = 0; 75260Svbart@nginx.com 753*417Svbart@nginx.com field->name_length = rp->field_name.length; 754*417Svbart@nginx.com field->value_length = rp->field_value.length; 755*417Svbart@nginx.com field->name = rp->field_name.start; 756*417Svbart@nginx.com field->value = rp->field_value.start; 75767Svbart@nginx.com 758*417Svbart@nginx.com rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; 75967Svbart@nginx.com 76067Svbart@nginx.com rp->field_name.length = 0; 76167Svbart@nginx.com rp->field_value.length = 0; 76216Svbart@nginx.com 76316Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 76416Svbart@nginx.com return NXT_OK; 76516Svbart@nginx.com } 76616Svbart@nginx.com 76716Svbart@nginx.com return NXT_DONE; 76816Svbart@nginx.com } 76916Svbart@nginx.com 77016Svbart@nginx.com return NXT_ERROR; 77116Svbart@nginx.com } 77216Svbart@nginx.com 77316Svbart@nginx.com 774112Smax.romanov@nginx.com #define \ 775112Smax.romanov@nginx.com nxt_http_is_normal(c) \ 776112Smax.romanov@nginx.com (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0)) 777112Smax.romanov@nginx.com 778112Smax.romanov@nginx.com 779112Smax.romanov@nginx.com static const uint8_t nxt_http_normal[32] nxt_aligned(32) = { 780112Smax.romanov@nginx.com 781112Smax.romanov@nginx.com /* \0 \r \n */ 782112Smax.romanov@nginx.com 0xfe, 0xdb, 0xff, 0xff, /* 1111 1110 1101 1011 1111 1111 1111 1111 */ 783112Smax.romanov@nginx.com 784112Smax.romanov@nginx.com /* '&%$ #"! /.-, |*)( 7654 3210 ?>=< ;:98 */ 785112Smax.romanov@nginx.com 0xd6, 0x37, 0xff, 0x7f, /* 1101 0110 0011 0111 1111 1111 0111 1111 */ 786112Smax.romanov@nginx.com 787112Smax.romanov@nginx.com /* GFED CBA@ ONML KJIH WVUT SRQP _^]\ [ZYX */ 788112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 789112Smax.romanov@nginx.com 790112Smax.romanov@nginx.com /* gfed cba` onml kjih wvut srqp ~}| {zyx */ 791112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 792112Smax.romanov@nginx.com 793112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 794112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 795112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 796112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 797112Smax.romanov@nginx.com }; 798112Smax.romanov@nginx.com 799112Smax.romanov@nginx.com 800112Smax.romanov@nginx.com static nxt_int_t 801112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp) 802112Smax.romanov@nginx.com { 803112Smax.romanov@nginx.com u_char *p, *u, c, ch, high; 804112Smax.romanov@nginx.com enum { 805112Smax.romanov@nginx.com sw_normal = 0, 806112Smax.romanov@nginx.com sw_slash, 807112Smax.romanov@nginx.com sw_dot, 808112Smax.romanov@nginx.com sw_dot_dot, 809112Smax.romanov@nginx.com sw_quoted, 810112Smax.romanov@nginx.com sw_quoted_second, 811112Smax.romanov@nginx.com } state, saved_state; 812112Smax.romanov@nginx.com 813112Smax.romanov@nginx.com nxt_prefetch(nxt_http_normal); 814112Smax.romanov@nginx.com 815112Smax.romanov@nginx.com state = sw_normal; 816112Smax.romanov@nginx.com saved_state = sw_normal; 817112Smax.romanov@nginx.com p = rp->target_start; 818112Smax.romanov@nginx.com 819112Smax.romanov@nginx.com u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1); 820112Smax.romanov@nginx.com 821112Smax.romanov@nginx.com if (nxt_slow_path(u == NULL)) { 822112Smax.romanov@nginx.com return NXT_ERROR; 823112Smax.romanov@nginx.com } 824112Smax.romanov@nginx.com 825112Smax.romanov@nginx.com rp->path.length = 0; 826112Smax.romanov@nginx.com rp->path.start = u; 827112Smax.romanov@nginx.com 828112Smax.romanov@nginx.com high = '\0'; 829112Smax.romanov@nginx.com rp->exten_start = NULL; 830112Smax.romanov@nginx.com rp->args_start = NULL; 831112Smax.romanov@nginx.com 832112Smax.romanov@nginx.com while (p < rp->target_end) { 833112Smax.romanov@nginx.com 834112Smax.romanov@nginx.com ch = *p++; 835112Smax.romanov@nginx.com 836112Smax.romanov@nginx.com again: 837112Smax.romanov@nginx.com 838112Smax.romanov@nginx.com switch (state) { 839112Smax.romanov@nginx.com 840112Smax.romanov@nginx.com case sw_normal: 841112Smax.romanov@nginx.com 842112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 843112Smax.romanov@nginx.com *u++ = ch; 844112Smax.romanov@nginx.com continue; 845112Smax.romanov@nginx.com } 846112Smax.romanov@nginx.com 847112Smax.romanov@nginx.com switch (ch) { 848112Smax.romanov@nginx.com case '/': 849112Smax.romanov@nginx.com rp->exten_start = NULL; 850112Smax.romanov@nginx.com state = sw_slash; 851112Smax.romanov@nginx.com *u++ = ch; 852112Smax.romanov@nginx.com continue; 853112Smax.romanov@nginx.com case '%': 854112Smax.romanov@nginx.com saved_state = state; 855112Smax.romanov@nginx.com state = sw_quoted; 856112Smax.romanov@nginx.com continue; 857112Smax.romanov@nginx.com case '?': 858112Smax.romanov@nginx.com rp->args_start = p; 859112Smax.romanov@nginx.com goto args; 860112Smax.romanov@nginx.com case '#': 861112Smax.romanov@nginx.com goto done; 862112Smax.romanov@nginx.com case '.': 863112Smax.romanov@nginx.com rp->exten_start = u + 1; 864112Smax.romanov@nginx.com *u++ = ch; 865112Smax.romanov@nginx.com continue; 866112Smax.romanov@nginx.com case '+': 867112Smax.romanov@nginx.com rp->plus_in_target = 1; 868112Smax.romanov@nginx.com /* Fall through. */ 869112Smax.romanov@nginx.com default: 870112Smax.romanov@nginx.com *u++ = ch; 871112Smax.romanov@nginx.com continue; 872112Smax.romanov@nginx.com } 873112Smax.romanov@nginx.com 874112Smax.romanov@nginx.com break; 875112Smax.romanov@nginx.com 876112Smax.romanov@nginx.com case sw_slash: 877112Smax.romanov@nginx.com 878112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 879112Smax.romanov@nginx.com state = sw_normal; 880112Smax.romanov@nginx.com *u++ = ch; 881112Smax.romanov@nginx.com continue; 882112Smax.romanov@nginx.com } 883112Smax.romanov@nginx.com 884112Smax.romanov@nginx.com switch (ch) { 885112Smax.romanov@nginx.com case '/': 886112Smax.romanov@nginx.com continue; 887112Smax.romanov@nginx.com case '.': 888112Smax.romanov@nginx.com state = sw_dot; 889112Smax.romanov@nginx.com *u++ = ch; 890112Smax.romanov@nginx.com continue; 891112Smax.romanov@nginx.com case '%': 892112Smax.romanov@nginx.com saved_state = state; 893112Smax.romanov@nginx.com state = sw_quoted; 894112Smax.romanov@nginx.com continue; 895112Smax.romanov@nginx.com case '?': 896112Smax.romanov@nginx.com rp->args_start = p; 897112Smax.romanov@nginx.com goto args; 898112Smax.romanov@nginx.com case '#': 899112Smax.romanov@nginx.com goto done; 900112Smax.romanov@nginx.com case '+': 901112Smax.romanov@nginx.com rp->plus_in_target = 1; 902112Smax.romanov@nginx.com /* Fall through. */ 903112Smax.romanov@nginx.com default: 904112Smax.romanov@nginx.com state = sw_normal; 905112Smax.romanov@nginx.com *u++ = ch; 906112Smax.romanov@nginx.com continue; 907112Smax.romanov@nginx.com } 908112Smax.romanov@nginx.com 909112Smax.romanov@nginx.com break; 910112Smax.romanov@nginx.com 911112Smax.romanov@nginx.com case sw_dot: 912112Smax.romanov@nginx.com 913112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 914112Smax.romanov@nginx.com state = sw_normal; 915112Smax.romanov@nginx.com *u++ = ch; 916112Smax.romanov@nginx.com continue; 917112Smax.romanov@nginx.com } 918112Smax.romanov@nginx.com 919112Smax.romanov@nginx.com switch (ch) { 920112Smax.romanov@nginx.com case '/': 921112Smax.romanov@nginx.com state = sw_slash; 922112Smax.romanov@nginx.com u--; 923112Smax.romanov@nginx.com continue; 924112Smax.romanov@nginx.com case '.': 925112Smax.romanov@nginx.com state = sw_dot_dot; 926112Smax.romanov@nginx.com *u++ = ch; 927112Smax.romanov@nginx.com continue; 928112Smax.romanov@nginx.com case '%': 929112Smax.romanov@nginx.com saved_state = state; 930112Smax.romanov@nginx.com state = sw_quoted; 931112Smax.romanov@nginx.com continue; 932112Smax.romanov@nginx.com case '?': 933112Smax.romanov@nginx.com rp->args_start = p; 934112Smax.romanov@nginx.com goto args; 935112Smax.romanov@nginx.com case '#': 936112Smax.romanov@nginx.com goto done; 937112Smax.romanov@nginx.com case '+': 938112Smax.romanov@nginx.com rp->plus_in_target = 1; 939112Smax.romanov@nginx.com /* Fall through. */ 940112Smax.romanov@nginx.com default: 941112Smax.romanov@nginx.com state = sw_normal; 942112Smax.romanov@nginx.com *u++ = ch; 943112Smax.romanov@nginx.com continue; 944112Smax.romanov@nginx.com } 945112Smax.romanov@nginx.com 946112Smax.romanov@nginx.com break; 947112Smax.romanov@nginx.com 948112Smax.romanov@nginx.com case sw_dot_dot: 949112Smax.romanov@nginx.com 950112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 951112Smax.romanov@nginx.com state = sw_normal; 952112Smax.romanov@nginx.com *u++ = ch; 953112Smax.romanov@nginx.com continue; 954112Smax.romanov@nginx.com } 955112Smax.romanov@nginx.com 956112Smax.romanov@nginx.com switch (ch) { 957112Smax.romanov@nginx.com case '/': 958112Smax.romanov@nginx.com state = sw_slash; 959112Smax.romanov@nginx.com u -= 5; 960112Smax.romanov@nginx.com for ( ;; ) { 961112Smax.romanov@nginx.com if (u < rp->path.start) { 962112Smax.romanov@nginx.com return NXT_ERROR; 963112Smax.romanov@nginx.com } 964112Smax.romanov@nginx.com if (*u == '/') { 965112Smax.romanov@nginx.com u++; 966112Smax.romanov@nginx.com break; 967112Smax.romanov@nginx.com } 968112Smax.romanov@nginx.com u--; 969112Smax.romanov@nginx.com } 970112Smax.romanov@nginx.com break; 971112Smax.romanov@nginx.com 972112Smax.romanov@nginx.com case '%': 973112Smax.romanov@nginx.com saved_state = state; 974112Smax.romanov@nginx.com state = sw_quoted; 975112Smax.romanov@nginx.com continue; 976112Smax.romanov@nginx.com case '?': 977112Smax.romanov@nginx.com rp->args_start = p; 978112Smax.romanov@nginx.com goto args; 979112Smax.romanov@nginx.com case '#': 980112Smax.romanov@nginx.com goto done; 981112Smax.romanov@nginx.com case '+': 982112Smax.romanov@nginx.com rp->plus_in_target = 1; 983112Smax.romanov@nginx.com /* Fall through. */ 984112Smax.romanov@nginx.com default: 985112Smax.romanov@nginx.com state = sw_normal; 986112Smax.romanov@nginx.com *u++ = ch; 987112Smax.romanov@nginx.com continue; 988112Smax.romanov@nginx.com } 989112Smax.romanov@nginx.com 990112Smax.romanov@nginx.com break; 991112Smax.romanov@nginx.com 992112Smax.romanov@nginx.com case sw_quoted: 993112Smax.romanov@nginx.com rp->quoted_target = 1; 994112Smax.romanov@nginx.com 995112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 996112Smax.romanov@nginx.com high = (u_char) (ch - '0'); 997112Smax.romanov@nginx.com state = sw_quoted_second; 998112Smax.romanov@nginx.com continue; 999112Smax.romanov@nginx.com } 1000112Smax.romanov@nginx.com 1001112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1002112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1003112Smax.romanov@nginx.com high = (u_char) (c - 'a' + 10); 1004112Smax.romanov@nginx.com state = sw_quoted_second; 1005112Smax.romanov@nginx.com continue; 1006112Smax.romanov@nginx.com } 1007112Smax.romanov@nginx.com 1008112Smax.romanov@nginx.com return NXT_ERROR; 1009112Smax.romanov@nginx.com 1010112Smax.romanov@nginx.com case sw_quoted_second: 1011112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 1012112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + ch - '0'); 1013112Smax.romanov@nginx.com 1014112Smax.romanov@nginx.com if (ch == '%' || ch == '#') { 1015112Smax.romanov@nginx.com state = sw_normal; 1016112Smax.romanov@nginx.com *u++ = ch; 1017112Smax.romanov@nginx.com continue; 1018112Smax.romanov@nginx.com 1019112Smax.romanov@nginx.com } else if (ch == '\0') { 1020112Smax.romanov@nginx.com return NXT_ERROR; 1021112Smax.romanov@nginx.com } 1022112Smax.romanov@nginx.com 1023112Smax.romanov@nginx.com state = saved_state; 1024112Smax.romanov@nginx.com goto again; 1025112Smax.romanov@nginx.com } 1026112Smax.romanov@nginx.com 1027112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1028112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1029112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + c - 'a' + 10); 1030112Smax.romanov@nginx.com 1031112Smax.romanov@nginx.com if (ch == '?') { 1032112Smax.romanov@nginx.com state = sw_normal; 1033112Smax.romanov@nginx.com *u++ = ch; 1034112Smax.romanov@nginx.com continue; 1035112Smax.romanov@nginx.com 1036112Smax.romanov@nginx.com } else if (ch == '+') { 1037112Smax.romanov@nginx.com rp->plus_in_target = 1; 1038112Smax.romanov@nginx.com } 1039112Smax.romanov@nginx.com 1040112Smax.romanov@nginx.com state = saved_state; 1041112Smax.romanov@nginx.com goto again; 1042112Smax.romanov@nginx.com } 1043112Smax.romanov@nginx.com 1044112Smax.romanov@nginx.com return NXT_ERROR; 1045112Smax.romanov@nginx.com } 1046112Smax.romanov@nginx.com } 1047112Smax.romanov@nginx.com 1048112Smax.romanov@nginx.com if (state >= sw_quoted) { 1049112Smax.romanov@nginx.com return NXT_ERROR; 1050112Smax.romanov@nginx.com } 1051112Smax.romanov@nginx.com 1052112Smax.romanov@nginx.com args: 1053112Smax.romanov@nginx.com 1054112Smax.romanov@nginx.com for (/* void */; p < rp->target_end; p++) { 1055112Smax.romanov@nginx.com if (*p == '#') { 1056112Smax.romanov@nginx.com break; 1057112Smax.romanov@nginx.com } 1058112Smax.romanov@nginx.com } 1059112Smax.romanov@nginx.com 1060112Smax.romanov@nginx.com if (rp->args_start != NULL) { 1061112Smax.romanov@nginx.com rp->args.length = p - rp->args_start; 1062112Smax.romanov@nginx.com rp->args.start = rp->args_start; 1063112Smax.romanov@nginx.com } 1064112Smax.romanov@nginx.com 1065112Smax.romanov@nginx.com done: 1066112Smax.romanov@nginx.com 1067112Smax.romanov@nginx.com rp->path.length = u - rp->path.start; 1068112Smax.romanov@nginx.com 1069112Smax.romanov@nginx.com if (rp->exten_start) { 1070112Smax.romanov@nginx.com rp->exten.length = u - rp->exten_start; 1071112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 1072112Smax.romanov@nginx.com } 1073112Smax.romanov@nginx.com 1074112Smax.romanov@nginx.com return NXT_OK; 1075112Smax.romanov@nginx.com } 1076*417Svbart@nginx.com 1077*417Svbart@nginx.com 1078*417Svbart@nginx.com static const nxt_lvlhsh_proto_t nxt_http_fields_hash_proto nxt_aligned(64) = { 1079*417Svbart@nginx.com NXT_LVLHSH_BUCKET_SIZE(64), 1080*417Svbart@nginx.com { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 }, 1081*417Svbart@nginx.com nxt_http_field_hash_test, 1082*417Svbart@nginx.com nxt_http_field_hash_alloc, 1083*417Svbart@nginx.com nxt_http_field_hash_free, 1084*417Svbart@nginx.com }; 1085*417Svbart@nginx.com 1086*417Svbart@nginx.com 1087*417Svbart@nginx.com static nxt_int_t 1088*417Svbart@nginx.com nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data) 1089*417Svbart@nginx.com { 1090*417Svbart@nginx.com nxt_http_field_proc_t *field; 1091*417Svbart@nginx.com 1092*417Svbart@nginx.com field = data; 1093*417Svbart@nginx.com 1094*417Svbart@nginx.com if (nxt_strcasestr_eq(&lhq->key, &field->name)) { 1095*417Svbart@nginx.com return NXT_OK; 1096*417Svbart@nginx.com } 1097*417Svbart@nginx.com 1098*417Svbart@nginx.com return NXT_DECLINED; 1099*417Svbart@nginx.com } 1100*417Svbart@nginx.com 1101*417Svbart@nginx.com 1102*417Svbart@nginx.com static void * 1103*417Svbart@nginx.com nxt_http_field_hash_alloc(void *pool, size_t size) 1104*417Svbart@nginx.com { 1105*417Svbart@nginx.com return nxt_mp_align(pool, size, size); 1106*417Svbart@nginx.com } 1107*417Svbart@nginx.com 1108*417Svbart@nginx.com 1109*417Svbart@nginx.com static void 1110*417Svbart@nginx.com nxt_http_field_hash_free(void *pool, void *p) 1111*417Svbart@nginx.com { 1112*417Svbart@nginx.com nxt_mp_free(pool, p); 1113*417Svbart@nginx.com } 1114*417Svbart@nginx.com 1115*417Svbart@nginx.com 1116*417Svbart@nginx.com static nxt_int_t 1117*417Svbart@nginx.com nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data) 1118*417Svbart@nginx.com { 1119*417Svbart@nginx.com return NXT_OK; 1120*417Svbart@nginx.com } 1121*417Svbart@nginx.com 1122*417Svbart@nginx.com 1123*417Svbart@nginx.com nxt_int_t 1124*417Svbart@nginx.com nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp, 1125*417Svbart@nginx.com nxt_http_field_proc_t items[], nxt_uint_t count) 1126*417Svbart@nginx.com { 1127*417Svbart@nginx.com u_char ch; 1128*417Svbart@nginx.com uint32_t key; 1129*417Svbart@nginx.com nxt_str_t *name; 1130*417Svbart@nginx.com nxt_int_t ret; 1131*417Svbart@nginx.com nxt_uint_t i, j; 1132*417Svbart@nginx.com nxt_lvlhsh_query_t lhq; 1133*417Svbart@nginx.com 1134*417Svbart@nginx.com lhq.replace = 0; 1135*417Svbart@nginx.com lhq.proto = &nxt_http_fields_hash_proto; 1136*417Svbart@nginx.com lhq.pool = mp; 1137*417Svbart@nginx.com 1138*417Svbart@nginx.com for (i = 0; i < count; i++) { 1139*417Svbart@nginx.com key = NXT_HTTP_FIELD_HASH_INIT; 1140*417Svbart@nginx.com name = &items[i].name; 1141*417Svbart@nginx.com 1142*417Svbart@nginx.com for (j = 0; j < name->length; j++) { 1143*417Svbart@nginx.com ch = nxt_lowcase(name->start[j]); 1144*417Svbart@nginx.com key = nxt_http_field_hash_char(key, ch); 1145*417Svbart@nginx.com } 1146*417Svbart@nginx.com 1147*417Svbart@nginx.com lhq.key_hash = nxt_http_field_hash_end(key) & 0xffff; 1148*417Svbart@nginx.com lhq.key = *name; 1149*417Svbart@nginx.com lhq.value = &items[i]; 1150*417Svbart@nginx.com 1151*417Svbart@nginx.com ret = nxt_lvlhsh_insert(hash, &lhq); 1152*417Svbart@nginx.com 1153*417Svbart@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1154*417Svbart@nginx.com return NXT_ERROR; 1155*417Svbart@nginx.com } 1156*417Svbart@nginx.com } 1157*417Svbart@nginx.com 1158*417Svbart@nginx.com return NXT_OK; 1159*417Svbart@nginx.com } 1160*417Svbart@nginx.com 1161*417Svbart@nginx.com 1162*417Svbart@nginx.com nxt_uint_t 1163*417Svbart@nginx.com nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp, 1164*417Svbart@nginx.com nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level) 1165*417Svbart@nginx.com { 1166*417Svbart@nginx.com u_char ch; 1167*417Svbart@nginx.com uint32_t key, mask; 1168*417Svbart@nginx.com nxt_str_t *name; 1169*417Svbart@nginx.com nxt_uint_t colls, i, j; 1170*417Svbart@nginx.com nxt_lvlhsh_proto_t proto; 1171*417Svbart@nginx.com nxt_lvlhsh_query_t lhq; 1172*417Svbart@nginx.com 1173*417Svbart@nginx.com proto = nxt_http_fields_hash_proto; 1174*417Svbart@nginx.com proto.test = nxt_http_field_hash_collision; 1175*417Svbart@nginx.com 1176*417Svbart@nginx.com lhq.replace = 0; 1177*417Svbart@nginx.com lhq.proto = &proto; 1178*417Svbart@nginx.com lhq.pool = mp; 1179*417Svbart@nginx.com 1180*417Svbart@nginx.com mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xffff; 1181*417Svbart@nginx.com 1182*417Svbart@nginx.com colls = 0; 1183*417Svbart@nginx.com 1184*417Svbart@nginx.com for (i = 0; i < count; i++) { 1185*417Svbart@nginx.com key = NXT_HTTP_FIELD_HASH_INIT; 1186*417Svbart@nginx.com name = &items[i].name; 1187*417Svbart@nginx.com 1188*417Svbart@nginx.com for (j = 0; j < name->length; j++) { 1189*417Svbart@nginx.com ch = nxt_lowcase(name->start[j]); 1190*417Svbart@nginx.com key = nxt_http_field_hash_char(key, ch); 1191*417Svbart@nginx.com } 1192*417Svbart@nginx.com 1193*417Svbart@nginx.com lhq.key_hash = nxt_http_field_hash_end(key) & mask; 1194*417Svbart@nginx.com 1195*417Svbart@nginx.com if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) { 1196*417Svbart@nginx.com colls++; 1197*417Svbart@nginx.com } 1198*417Svbart@nginx.com } 1199*417Svbart@nginx.com 1200*417Svbart@nginx.com return colls; 1201*417Svbart@nginx.com } 1202*417Svbart@nginx.com 1203*417Svbart@nginx.com 1204*417Svbart@nginx.com nxt_int_t 1205*417Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx) 1206*417Svbart@nginx.com { 1207*417Svbart@nginx.com nxt_int_t ret; 1208*417Svbart@nginx.com nxt_http_field_t *field; 1209*417Svbart@nginx.com nxt_lvlhsh_query_t lhq; 1210*417Svbart@nginx.com nxt_http_field_proc_t *proc; 1211*417Svbart@nginx.com 1212*417Svbart@nginx.com lhq.proto = &nxt_http_fields_hash_proto; 1213*417Svbart@nginx.com 1214*417Svbart@nginx.com nxt_list_each(field, fields) { 1215*417Svbart@nginx.com 1216*417Svbart@nginx.com lhq.key_hash = field->hash; 1217*417Svbart@nginx.com lhq.key.length = field->name_length; 1218*417Svbart@nginx.com lhq.key.start = field->name; 1219*417Svbart@nginx.com 1220*417Svbart@nginx.com if (nxt_lvlhsh_find(hash, &lhq) != NXT_OK) { 1221*417Svbart@nginx.com continue; 1222*417Svbart@nginx.com } 1223*417Svbart@nginx.com 1224*417Svbart@nginx.com proc = lhq.value; 1225*417Svbart@nginx.com 1226*417Svbart@nginx.com ret = proc->handler(ctx, field, proc->data); 1227*417Svbart@nginx.com 1228*417Svbart@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1229*417Svbart@nginx.com return ret; 1230*417Svbart@nginx.com } 1231*417Svbart@nginx.com 1232*417Svbart@nginx.com } nxt_list_loop; 1233*417Svbart@nginx.com 1234*417Svbart@nginx.com return NXT_OK; 1235*417Svbart@nginx.com } 1236