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 22417Svbart@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp); 23417Svbart@nginx.com 24417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data); 25417Svbart@nginx.com static void *nxt_http_field_hash_alloc(void *pool, size_t size); 26417Svbart@nginx.com static void nxt_http_field_hash_free(void *pool, void *p); 27417Svbart@nginx.com 28417Svbart@nginx.com static nxt_int_t nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, 29417Svbart@nginx.com void *data); 3016Svbart@nginx.com 31417Svbart@nginx.com 32611Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_NAME 0xFF 33417Svbart@nginx.com #define NXT_HTTP_MAX_FIELD_VALUE NXT_INT32_T_MAX 34417Svbart@nginx.com 35417Svbart@nginx.com #define NXT_HTTP_FIELD_LVLHSH_SHIFT 5 36417Svbart@nginx.com 3716Svbart@nginx.com 3816Svbart@nginx.com typedef enum { 3916Svbart@nginx.com NXT_HTTP_TARGET_SPACE = 1, /* \s */ 4016Svbart@nginx.com NXT_HTTP_TARGET_HASH, /* # */ 4116Svbart@nginx.com NXT_HTTP_TARGET_AGAIN, 4216Svbart@nginx.com NXT_HTTP_TARGET_BAD, /* \0\r\n */ 4316Svbart@nginx.com 4416Svbart@nginx.com /* traps below are used for extended check only */ 4516Svbart@nginx.com 4616Svbart@nginx.com NXT_HTTP_TARGET_SLASH = 5, /* / */ 4716Svbart@nginx.com NXT_HTTP_TARGET_DOT, /* . */ 4816Svbart@nginx.com NXT_HTTP_TARGET_ARGS_MARK, /* ? */ 4916Svbart@nginx.com NXT_HTTP_TARGET_QUOTE_MARK, /* % */ 5016Svbart@nginx.com NXT_HTTP_TARGET_PLUS, /* + */ 5116Svbart@nginx.com } nxt_http_target_traps_e; 5216Svbart@nginx.com 5316Svbart@nginx.com 5416Svbart@nginx.com static const uint8_t nxt_http_target_chars[256] nxt_aligned(64) = { 5516Svbart@nginx.com /* \0 \n \r */ 5616Svbart@nginx.com 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, 5716Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5852Svbart@nginx.com 5952Svbart@nginx.com /* \s ! " # $ % & ' ( ) * + , - . / */ 6016Svbart@nginx.com 1, 0, 0, 2, 0, 8, 0, 0, 0, 0, 0, 9, 0, 0, 6, 5, 6152Svbart@nginx.com 6252Svbart@nginx.com /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 6316Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 6416Svbart@nginx.com }; 6516Svbart@nginx.com 6616Svbart@nginx.com 6716Svbart@nginx.com nxt_inline nxt_http_target_traps_e 6816Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end) 6916Svbart@nginx.com { 7016Svbart@nginx.com u_char *p; 7116Svbart@nginx.com nxt_uint_t trap; 7216Svbart@nginx.com 7316Svbart@nginx.com p = *pos; 7416Svbart@nginx.com 75409Svbart@nginx.com while (nxt_fast_path(end - p >= 10)) { 7616Svbart@nginx.com 77409Svbart@nginx.com #define nxt_target_test_char(ch) \ 78409Svbart@nginx.com \ 79409Svbart@nginx.com trap = nxt_http_target_chars[ch]; \ 8016Svbart@nginx.com \ 81409Svbart@nginx.com if (nxt_slow_path(trap != 0)) { \ 82409Svbart@nginx.com *pos = &(ch); \ 83409Svbart@nginx.com return trap; \ 8416Svbart@nginx.com } 8516Svbart@nginx.com 86409Svbart@nginx.com /* enddef */ 87409Svbart@nginx.com 88409Svbart@nginx.com nxt_target_test_char(p[0]); 89409Svbart@nginx.com nxt_target_test_char(p[1]); 90409Svbart@nginx.com nxt_target_test_char(p[2]); 91409Svbart@nginx.com nxt_target_test_char(p[3]); 9216Svbart@nginx.com 93409Svbart@nginx.com nxt_target_test_char(p[4]); 94409Svbart@nginx.com nxt_target_test_char(p[5]); 95409Svbart@nginx.com nxt_target_test_char(p[6]); 96409Svbart@nginx.com nxt_target_test_char(p[7]); 9716Svbart@nginx.com 98409Svbart@nginx.com nxt_target_test_char(p[8]); 99409Svbart@nginx.com nxt_target_test_char(p[9]); 10016Svbart@nginx.com 101409Svbart@nginx.com p += 10; 10216Svbart@nginx.com } 10316Svbart@nginx.com 104410Svbart@nginx.com while (p != end) { 105410Svbart@nginx.com nxt_target_test_char(*p); p++; 106410Svbart@nginx.com } 107410Svbart@nginx.com 108409Svbart@nginx.com return NXT_HTTP_TARGET_AGAIN; 10916Svbart@nginx.com } 11016Svbart@nginx.com 11116Svbart@nginx.com 11216Svbart@nginx.com nxt_int_t 113417Svbart@nginx.com nxt_http_parse_request_init(nxt_http_request_parse_t *rp, nxt_mp_t *mp) 114417Svbart@nginx.com { 115417Svbart@nginx.com rp->mem_pool = mp; 116417Svbart@nginx.com 117417Svbart@nginx.com rp->fields = nxt_list_create(mp, 8, sizeof(nxt_http_field_t)); 1181008Szelenkov@nginx.com if (nxt_slow_path(rp->fields == NULL)) { 119417Svbart@nginx.com return NXT_ERROR; 120417Svbart@nginx.com } 121417Svbart@nginx.com 122417Svbart@nginx.com rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; 123417Svbart@nginx.com 124417Svbart@nginx.com return NXT_OK; 125417Svbart@nginx.com } 126417Svbart@nginx.com 127417Svbart@nginx.com 128417Svbart@nginx.com nxt_int_t 12916Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) 13016Svbart@nginx.com { 13116Svbart@nginx.com nxt_int_t rc; 13216Svbart@nginx.com 13316Svbart@nginx.com if (rp->handler == NULL) { 13416Svbart@nginx.com rp->handler = &nxt_http_parse_request_line; 13516Svbart@nginx.com } 13616Svbart@nginx.com 13716Svbart@nginx.com do { 13816Svbart@nginx.com rc = rp->handler(rp, &b->pos, b->free); 13916Svbart@nginx.com } while (rc == NXT_OK); 14016Svbart@nginx.com 14116Svbart@nginx.com return rc; 14216Svbart@nginx.com } 14316Svbart@nginx.com 14416Svbart@nginx.com 145422Svbart@nginx.com nxt_int_t 146422Svbart@nginx.com nxt_http_parse_fields(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) 147422Svbart@nginx.com { 148422Svbart@nginx.com nxt_int_t rc; 149422Svbart@nginx.com 150422Svbart@nginx.com if (rp->handler == NULL) { 151422Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 152422Svbart@nginx.com } 153422Svbart@nginx.com 154422Svbart@nginx.com do { 155422Svbart@nginx.com rc = rp->handler(rp, &b->pos, b->free); 156422Svbart@nginx.com } while (rc == NXT_OK); 157422Svbart@nginx.com 158422Svbart@nginx.com return rc; 159422Svbart@nginx.com } 160422Svbart@nginx.com 161422Svbart@nginx.com 16216Svbart@nginx.com static nxt_int_t 16316Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos, 16416Svbart@nginx.com u_char *end) 16516Svbart@nginx.com { 16616Svbart@nginx.com u_char *p, ch, *after_slash; 16716Svbart@nginx.com nxt_int_t rc; 168481Svbart@nginx.com nxt_http_ver_t ver; 16916Svbart@nginx.com nxt_http_target_traps_e trap; 17016Svbart@nginx.com 17116Svbart@nginx.com static const nxt_http_ver_t http11 = { "HTTP/1.1" }; 17216Svbart@nginx.com static const nxt_http_ver_t http10 = { "HTTP/1.0" }; 17316Svbart@nginx.com 17416Svbart@nginx.com p = *pos; 17516Svbart@nginx.com 17616Svbart@nginx.com rp->method.start = p; 17716Svbart@nginx.com 178409Svbart@nginx.com for ( ;; ) { 179409Svbart@nginx.com 180409Svbart@nginx.com while (nxt_fast_path(end - p >= 8)) { 18116Svbart@nginx.com 182409Svbart@nginx.com #define nxt_method_test_char(ch) \ 183409Svbart@nginx.com \ 184409Svbart@nginx.com if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) { \ 185409Svbart@nginx.com p = &(ch); \ 186409Svbart@nginx.com goto method_unusual_char; \ 18716Svbart@nginx.com } 18816Svbart@nginx.com 189409Svbart@nginx.com /* enddef */ 190409Svbart@nginx.com 191409Svbart@nginx.com nxt_method_test_char(p[0]); 192409Svbart@nginx.com nxt_method_test_char(p[1]); 193409Svbart@nginx.com nxt_method_test_char(p[2]); 194409Svbart@nginx.com nxt_method_test_char(p[3]); 19516Svbart@nginx.com 196409Svbart@nginx.com nxt_method_test_char(p[4]); 197409Svbart@nginx.com nxt_method_test_char(p[5]); 198409Svbart@nginx.com nxt_method_test_char(p[6]); 199409Svbart@nginx.com nxt_method_test_char(p[7]); 20016Svbart@nginx.com 201409Svbart@nginx.com p += 8; 202409Svbart@nginx.com } 20316Svbart@nginx.com 204410Svbart@nginx.com while (p != end) { 205410Svbart@nginx.com nxt_method_test_char(*p); p++; 206410Svbart@nginx.com } 207410Svbart@nginx.com 208623Svbart@nginx.com rp->method.length = p - rp->method.start; 209623Svbart@nginx.com 210409Svbart@nginx.com return NXT_AGAIN; 211409Svbart@nginx.com 212409Svbart@nginx.com method_unusual_char: 213409Svbart@nginx.com 214409Svbart@nginx.com ch = *p; 21516Svbart@nginx.com 21616Svbart@nginx.com if (nxt_fast_path(ch == ' ')) { 21716Svbart@nginx.com rp->method.length = p - rp->method.start; 21816Svbart@nginx.com break; 21916Svbart@nginx.com } 22016Svbart@nginx.com 22116Svbart@nginx.com if (ch == '_' || ch == '-') { 222409Svbart@nginx.com p++; 22316Svbart@nginx.com continue; 22416Svbart@nginx.com } 22516Svbart@nginx.com 226704Sigor@sysoev.ru if (rp->method.start == p && (ch == '\r' || ch == '\n')) { 22716Svbart@nginx.com rp->method.start++; 228409Svbart@nginx.com p++; 22916Svbart@nginx.com continue; 23016Svbart@nginx.com } 23116Svbart@nginx.com 232623Svbart@nginx.com rp->method.length = p - rp->method.start; 233623Svbart@nginx.com 234480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 23516Svbart@nginx.com } 23616Svbart@nginx.com 23716Svbart@nginx.com p++; 23816Svbart@nginx.com 23916Svbart@nginx.com if (nxt_slow_path(p == end)) { 24016Svbart@nginx.com return NXT_AGAIN; 24116Svbart@nginx.com } 24216Svbart@nginx.com 24316Svbart@nginx.com /* target */ 24416Svbart@nginx.com 24516Svbart@nginx.com ch = *p; 24616Svbart@nginx.com 24716Svbart@nginx.com if (nxt_slow_path(ch != '/')) { 24816Svbart@nginx.com rc = nxt_http_parse_unusual_target(rp, &p, end); 24916Svbart@nginx.com 25016Svbart@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 25116Svbart@nginx.com return rc; 25216Svbart@nginx.com } 25316Svbart@nginx.com } 25416Svbart@nginx.com 25516Svbart@nginx.com rp->target_start = p; 25616Svbart@nginx.com 25716Svbart@nginx.com after_slash = p + 1; 25816Svbart@nginx.com 25916Svbart@nginx.com for ( ;; ) { 26016Svbart@nginx.com p++; 26116Svbart@nginx.com 26216Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 26316Svbart@nginx.com 26416Svbart@nginx.com switch (trap) { 26516Svbart@nginx.com case NXT_HTTP_TARGET_SLASH: 26616Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 26716Svbart@nginx.com rp->complex_target = 1; 26816Svbart@nginx.com goto rest_of_target; 26916Svbart@nginx.com } 27016Svbart@nginx.com 27116Svbart@nginx.com after_slash = p + 1; 27216Svbart@nginx.com 27316Svbart@nginx.com rp->exten_start = NULL; 27416Svbart@nginx.com continue; 27516Svbart@nginx.com 27616Svbart@nginx.com case NXT_HTTP_TARGET_DOT: 27716Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 27816Svbart@nginx.com rp->complex_target = 1; 27916Svbart@nginx.com goto rest_of_target; 28016Svbart@nginx.com } 28116Svbart@nginx.com 28216Svbart@nginx.com rp->exten_start = p + 1; 28316Svbart@nginx.com continue; 28416Svbart@nginx.com 28516Svbart@nginx.com case NXT_HTTP_TARGET_ARGS_MARK: 28616Svbart@nginx.com rp->args_start = p + 1; 28716Svbart@nginx.com goto rest_of_target; 28816Svbart@nginx.com 28916Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 29016Svbart@nginx.com rp->target_end = p; 29116Svbart@nginx.com goto space_after_target; 29216Svbart@nginx.com 29316Svbart@nginx.com case NXT_HTTP_TARGET_QUOTE_MARK: 29416Svbart@nginx.com rp->quoted_target = 1; 29516Svbart@nginx.com goto rest_of_target; 29616Svbart@nginx.com 29716Svbart@nginx.com case NXT_HTTP_TARGET_PLUS: 29816Svbart@nginx.com rp->plus_in_target = 1; 29916Svbart@nginx.com continue; 30016Svbart@nginx.com 30116Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 30216Svbart@nginx.com rp->complex_target = 1; 30316Svbart@nginx.com goto rest_of_target; 30416Svbart@nginx.com 30516Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 306621Svbart@nginx.com rp->target_end = p; 30716Svbart@nginx.com return NXT_AGAIN; 30816Svbart@nginx.com 30916Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 310621Svbart@nginx.com rp->target_end = p; 311480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 31216Svbart@nginx.com } 31316Svbart@nginx.com 31416Svbart@nginx.com nxt_unreachable(); 31516Svbart@nginx.com } 31616Svbart@nginx.com 31716Svbart@nginx.com rest_of_target: 31816Svbart@nginx.com 31916Svbart@nginx.com for ( ;; ) { 32016Svbart@nginx.com p++; 32116Svbart@nginx.com 32219Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 32316Svbart@nginx.com 32416Svbart@nginx.com switch (trap) { 32516Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 32616Svbart@nginx.com rp->target_end = p; 32716Svbart@nginx.com goto space_after_target; 32816Svbart@nginx.com 32916Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 33016Svbart@nginx.com rp->complex_target = 1; 33116Svbart@nginx.com continue; 33216Svbart@nginx.com 33316Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 334621Svbart@nginx.com rp->target_end = p; 33516Svbart@nginx.com return NXT_AGAIN; 33616Svbart@nginx.com 33716Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 338621Svbart@nginx.com rp->target_end = p; 339480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 34016Svbart@nginx.com 34116Svbart@nginx.com default: 34216Svbart@nginx.com continue; 34316Svbart@nginx.com } 34416Svbart@nginx.com 34516Svbart@nginx.com nxt_unreachable(); 34616Svbart@nginx.com } 34716Svbart@nginx.com 34816Svbart@nginx.com space_after_target: 34916Svbart@nginx.com 35016Svbart@nginx.com if (nxt_slow_path(end - p < 10)) { 351410Svbart@nginx.com 352410Svbart@nginx.com do { 353410Svbart@nginx.com p++; 354410Svbart@nginx.com 355410Svbart@nginx.com if (p == end) { 356410Svbart@nginx.com return NXT_AGAIN; 357410Svbart@nginx.com } 358410Svbart@nginx.com 359410Svbart@nginx.com } while (*p == ' '); 360410Svbart@nginx.com 361410Svbart@nginx.com if (nxt_memcmp(p, "HTTP/", nxt_min(end - p, 5)) == 0) { 362410Svbart@nginx.com 363410Svbart@nginx.com switch (end - p) { 364410Svbart@nginx.com case 8: 365410Svbart@nginx.com if (p[7] < '0' || p[7] > '9') { 366410Svbart@nginx.com break; 367410Svbart@nginx.com } 368410Svbart@nginx.com /* Fall through. */ 369410Svbart@nginx.com case 7: 370410Svbart@nginx.com if (p[6] != '.') { 371410Svbart@nginx.com break; 372410Svbart@nginx.com } 373410Svbart@nginx.com /* Fall through. */ 374410Svbart@nginx.com case 6: 375410Svbart@nginx.com if (p[5] < '0' || p[5] > '9') { 376410Svbart@nginx.com break; 377410Svbart@nginx.com } 378410Svbart@nginx.com /* Fall through. */ 379410Svbart@nginx.com default: 380410Svbart@nginx.com return NXT_AGAIN; 381410Svbart@nginx.com } 382410Svbart@nginx.com } 383410Svbart@nginx.com 384410Svbart@nginx.com rp->space_in_target = 1; 385410Svbart@nginx.com goto rest_of_target; 38616Svbart@nginx.com } 38716Svbart@nginx.com 38816Svbart@nginx.com /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */ 38916Svbart@nginx.com 390482Svbart@nginx.com if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) { 391482Svbart@nginx.com 392482Svbart@nginx.com if (p[1] == ' ') { 393482Svbart@nginx.com /* surplus space after tartet */ 394482Svbart@nginx.com p++; 395482Svbart@nginx.com goto space_after_target; 396482Svbart@nginx.com } 397482Svbart@nginx.com 398482Svbart@nginx.com rp->space_in_target = 1; 399482Svbart@nginx.com goto rest_of_target; 400482Svbart@nginx.com } 401482Svbart@nginx.com 402481Svbart@nginx.com nxt_memcpy(ver.str, &p[1], 8); 40316Svbart@nginx.com 404482Svbart@nginx.com if (nxt_fast_path(ver.ui64 == http11.ui64 405482Svbart@nginx.com || ver.ui64 == http10.ui64 406482Svbart@nginx.com || (nxt_memcmp(ver.str, "HTTP/1.", 7) == 0 407482Svbart@nginx.com && ver.s.minor >= '0' && ver.s.minor <= '9'))) 40816Svbart@nginx.com { 409481Svbart@nginx.com rp->version.ui64 = ver.ui64; 41016Svbart@nginx.com 41116Svbart@nginx.com if (nxt_fast_path(p[9] == '\r')) { 41216Svbart@nginx.com p += 10; 41316Svbart@nginx.com 41416Svbart@nginx.com if (nxt_slow_path(p == end)) { 41516Svbart@nginx.com return NXT_AGAIN; 41616Svbart@nginx.com } 41716Svbart@nginx.com 41816Svbart@nginx.com if (nxt_slow_path(*p != '\n')) { 419480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 42016Svbart@nginx.com } 42116Svbart@nginx.com 42216Svbart@nginx.com *pos = p + 1; 423112Smax.romanov@nginx.com 424112Smax.romanov@nginx.com } else { 425112Smax.romanov@nginx.com *pos = p + 10; 426112Smax.romanov@nginx.com } 427112Smax.romanov@nginx.com 428112Smax.romanov@nginx.com if (rp->complex_target != 0 || rp->quoted_target != 0) { 429112Smax.romanov@nginx.com rc = nxt_http_parse_complex_target(rp); 430112Smax.romanov@nginx.com 431112Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 432112Smax.romanov@nginx.com return rc; 433112Smax.romanov@nginx.com } 434112Smax.romanov@nginx.com 43516Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 43616Svbart@nginx.com } 43716Svbart@nginx.com 438112Smax.romanov@nginx.com rp->path.start = rp->target_start; 439112Smax.romanov@nginx.com 440112Smax.romanov@nginx.com if (rp->args_start != NULL) { 441112Smax.romanov@nginx.com rp->path.length = rp->args_start - rp->target_start - 1; 442112Smax.romanov@nginx.com 443112Smax.romanov@nginx.com rp->args.start = rp->args_start; 444112Smax.romanov@nginx.com rp->args.length = rp->target_end - rp->args_start; 445112Smax.romanov@nginx.com 446112Smax.romanov@nginx.com } else { 447112Smax.romanov@nginx.com rp->path.length = rp->target_end - rp->target_start; 448112Smax.romanov@nginx.com } 449112Smax.romanov@nginx.com 450112Smax.romanov@nginx.com if (rp->exten_start) { 451613Svbart@nginx.com rp->exten.length = rp->path.start + rp->path.length 452613Svbart@nginx.com - rp->exten_start; 453112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 454112Smax.romanov@nginx.com } 455112Smax.romanov@nginx.com 45616Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 45716Svbart@nginx.com } 45816Svbart@nginx.com 459482Svbart@nginx.com if (nxt_memcmp(ver.s.prefix, "HTTP/", 5) == 0 460482Svbart@nginx.com && ver.s.major >= '0' && ver.s.major <= '9' 461482Svbart@nginx.com && ver.s.point == '.' 462482Svbart@nginx.com && ver.s.minor >= '0' && ver.s.minor <= '9') 463482Svbart@nginx.com { 464622Svbart@nginx.com rp->version.ui64 = ver.ui64; 465482Svbart@nginx.com return NXT_HTTP_PARSE_UNSUPPORTED_VERSION; 46616Svbart@nginx.com } 46716Svbart@nginx.com 468482Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 46916Svbart@nginx.com } 47016Svbart@nginx.com 47116Svbart@nginx.com 47216Svbart@nginx.com static nxt_int_t 47316Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos, 47416Svbart@nginx.com u_char *end) 47516Svbart@nginx.com { 47616Svbart@nginx.com u_char *p, ch; 47716Svbart@nginx.com 47816Svbart@nginx.com p = *pos; 47916Svbart@nginx.com 48016Svbart@nginx.com ch = *p; 48116Svbart@nginx.com 48216Svbart@nginx.com if (ch == ' ') { 48316Svbart@nginx.com /* skip surplus spaces before target */ 48416Svbart@nginx.com 48516Svbart@nginx.com do { 48616Svbart@nginx.com p++; 48716Svbart@nginx.com 48816Svbart@nginx.com if (nxt_slow_path(p == end)) { 48916Svbart@nginx.com return NXT_AGAIN; 49016Svbart@nginx.com } 49116Svbart@nginx.com 49216Svbart@nginx.com ch = *p; 49316Svbart@nginx.com 49416Svbart@nginx.com } while (ch == ' '); 49516Svbart@nginx.com 49616Svbart@nginx.com if (ch == '/') { 49716Svbart@nginx.com *pos = p; 49816Svbart@nginx.com return NXT_OK; 49916Svbart@nginx.com } 50016Svbart@nginx.com } 50116Svbart@nginx.com 50216Svbart@nginx.com /* absolute path or '*' */ 50316Svbart@nginx.com 50416Svbart@nginx.com /* TODO */ 50516Svbart@nginx.com 506480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 50716Svbart@nginx.com } 50816Svbart@nginx.com 50916Svbart@nginx.com 51016Svbart@nginx.com static nxt_int_t 51116Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, 51216Svbart@nginx.com u_char *end) 51316Svbart@nginx.com { 514417Svbart@nginx.com u_char *p, c; 515417Svbart@nginx.com size_t len; 516417Svbart@nginx.com uint32_t hash; 51716Svbart@nginx.com 51816Svbart@nginx.com static const u_char normal[256] nxt_aligned(64) = 51916Svbart@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" 52016Svbart@nginx.com "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" 52116Svbart@nginx.com 52216Svbart@nginx.com /* These 64 bytes should reside in one cache line. */ 523454Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0_" 52416Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" 52516Svbart@nginx.com 52616Svbart@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" 52716Svbart@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" 52816Svbart@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" 52916Svbart@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"; 53016Svbart@nginx.com 531417Svbart@nginx.com p = *pos + rp->field_name.length; 532417Svbart@nginx.com hash = rp->field_hash; 53316Svbart@nginx.com 534417Svbart@nginx.com while (nxt_fast_path(end - p >= 8)) { 535409Svbart@nginx.com 536417Svbart@nginx.com #define nxt_field_name_test_char(ch) \ 537409Svbart@nginx.com \ 53819Svbart@nginx.com c = normal[ch]; \ 53919Svbart@nginx.com \ 54019Svbart@nginx.com if (nxt_slow_path(c == '\0')) { \ 541417Svbart@nginx.com p = &(ch); \ 54219Svbart@nginx.com goto name_end; \ 54319Svbart@nginx.com } \ 54419Svbart@nginx.com \ 545417Svbart@nginx.com hash = nxt_http_field_hash_char(hash, c); 546409Svbart@nginx.com 547409Svbart@nginx.com /* enddef */ 54816Svbart@nginx.com 549417Svbart@nginx.com nxt_field_name_test_char(p[0]); 550417Svbart@nginx.com nxt_field_name_test_char(p[1]); 551417Svbart@nginx.com nxt_field_name_test_char(p[2]); 552417Svbart@nginx.com nxt_field_name_test_char(p[3]); 55316Svbart@nginx.com 554417Svbart@nginx.com nxt_field_name_test_char(p[4]); 555417Svbart@nginx.com nxt_field_name_test_char(p[5]); 556417Svbart@nginx.com nxt_field_name_test_char(p[6]); 557417Svbart@nginx.com nxt_field_name_test_char(p[7]); 558417Svbart@nginx.com 559417Svbart@nginx.com p += 8; 56019Svbart@nginx.com } 56116Svbart@nginx.com 562417Svbart@nginx.com while (nxt_fast_path(p != end)) { 563417Svbart@nginx.com nxt_field_name_test_char(*p); p++; 56419Svbart@nginx.com } 56516Svbart@nginx.com 566417Svbart@nginx.com len = p - *pos; 567417Svbart@nginx.com 568417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { 569480Svbart@nginx.com return NXT_HTTP_PARSE_TOO_LARGE_FIELD; 570417Svbart@nginx.com } 571417Svbart@nginx.com 572417Svbart@nginx.com rp->field_hash = hash; 573417Svbart@nginx.com rp->field_name.length = len; 574417Svbart@nginx.com 57516Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 57616Svbart@nginx.com 57716Svbart@nginx.com return NXT_AGAIN; 57819Svbart@nginx.com 57919Svbart@nginx.com name_end: 58019Svbart@nginx.com 581417Svbart@nginx.com if (nxt_fast_path(*p == ':')) { 582417Svbart@nginx.com if (nxt_slow_path(p == *pos)) { 583480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 58419Svbart@nginx.com } 58519Svbart@nginx.com 586417Svbart@nginx.com len = p - *pos; 587417Svbart@nginx.com 588417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_NAME)) { 589480Svbart@nginx.com return NXT_HTTP_PARSE_TOO_LARGE_FIELD; 590417Svbart@nginx.com } 59119Svbart@nginx.com 592417Svbart@nginx.com rp->field_hash = hash; 593417Svbart@nginx.com 594417Svbart@nginx.com rp->field_name.length = len; 595417Svbart@nginx.com rp->field_name.start = *pos; 596417Svbart@nginx.com 597417Svbart@nginx.com *pos = p + 1; 59819Svbart@nginx.com 59919Svbart@nginx.com return nxt_http_parse_field_value(rp, pos, end); 60019Svbart@nginx.com } 60119Svbart@nginx.com 602417Svbart@nginx.com if (nxt_slow_path(p != *pos)) { 603480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 60459Svbart@nginx.com } 60519Svbart@nginx.com 60619Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 60716Svbart@nginx.com } 60816Svbart@nginx.com 60916Svbart@nginx.com 61016Svbart@nginx.com static nxt_int_t 61116Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos, 61216Svbart@nginx.com u_char *end) 61316Svbart@nginx.com { 614574Svbart@nginx.com u_char *p, *start, ch; 615417Svbart@nginx.com size_t len; 61616Svbart@nginx.com 61716Svbart@nginx.com p = *pos; 61816Svbart@nginx.com 61916Svbart@nginx.com for ( ;; ) { 62016Svbart@nginx.com if (nxt_slow_path(p == end)) { 62116Svbart@nginx.com *pos = p; 62216Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 62316Svbart@nginx.com return NXT_AGAIN; 62416Svbart@nginx.com } 62516Svbart@nginx.com 626577Svbart@nginx.com ch = *p; 627577Svbart@nginx.com 628577Svbart@nginx.com if (ch != ' ' && ch != '\t') { 62916Svbart@nginx.com break; 63016Svbart@nginx.com } 63116Svbart@nginx.com 63216Svbart@nginx.com p++; 63316Svbart@nginx.com } 63416Svbart@nginx.com 635574Svbart@nginx.com start = p; 63616Svbart@nginx.com 63767Svbart@nginx.com p += rp->field_value.length; 63816Svbart@nginx.com 639576Svbart@nginx.com for ( ;; ) { 640576Svbart@nginx.com p = nxt_http_lookup_field_end(p, end); 641417Svbart@nginx.com 642576Svbart@nginx.com if (nxt_slow_path(p == end)) { 643576Svbart@nginx.com *pos = start; 644576Svbart@nginx.com 645576Svbart@nginx.com len = p - start; 646574Svbart@nginx.com 647576Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { 648576Svbart@nginx.com return NXT_HTTP_PARSE_TOO_LARGE_FIELD; 649576Svbart@nginx.com } 650417Svbart@nginx.com 651576Svbart@nginx.com rp->field_value.length = len; 652576Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 653576Svbart@nginx.com return NXT_AGAIN; 65416Svbart@nginx.com } 65516Svbart@nginx.com 656576Svbart@nginx.com ch = *p; 657576Svbart@nginx.com 658576Svbart@nginx.com if (nxt_fast_path(ch == '\r' || ch == '\n')) { 659576Svbart@nginx.com break; 660576Svbart@nginx.com } 66116Svbart@nginx.com 662576Svbart@nginx.com if (ch != '\t') { 663576Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 664576Svbart@nginx.com } 66516Svbart@nginx.com 666576Svbart@nginx.com p++; 66716Svbart@nginx.com } 66816Svbart@nginx.com 669574Svbart@nginx.com *pos = p; 670574Svbart@nginx.com 671574Svbart@nginx.com if (nxt_fast_path(p != start)) { 672577Svbart@nginx.com 673577Svbart@nginx.com while (p[-1] == ' ' || p[-1] == '\t') { 67416Svbart@nginx.com p--; 67516Svbart@nginx.com } 67616Svbart@nginx.com } 67716Svbart@nginx.com 678574Svbart@nginx.com len = p - start; 679417Svbart@nginx.com 680417Svbart@nginx.com if (nxt_slow_path(len > NXT_HTTP_MAX_FIELD_VALUE)) { 681480Svbart@nginx.com return NXT_HTTP_PARSE_TOO_LARGE_FIELD; 682417Svbart@nginx.com } 683417Svbart@nginx.com 684417Svbart@nginx.com rp->field_value.length = len; 685574Svbart@nginx.com rp->field_value.start = start; 68616Svbart@nginx.com 68716Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 68816Svbart@nginx.com } 68916Svbart@nginx.com 69016Svbart@nginx.com 69116Svbart@nginx.com static u_char * 69216Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end) 69316Svbart@nginx.com { 694409Svbart@nginx.com while (nxt_fast_path(end - p >= 16)) { 695409Svbart@nginx.com 696409Svbart@nginx.com #define nxt_field_end_test_char(ch) \ 697409Svbart@nginx.com \ 698712Svbart@nginx.com if (nxt_slow_path((ch) < 0x20)) { \ 699409Svbart@nginx.com return &(ch); \ 700409Svbart@nginx.com } 701409Svbart@nginx.com 702409Svbart@nginx.com /* enddef */ 703409Svbart@nginx.com 704409Svbart@nginx.com nxt_field_end_test_char(p[0]); 705409Svbart@nginx.com nxt_field_end_test_char(p[1]); 706409Svbart@nginx.com nxt_field_end_test_char(p[2]); 707409Svbart@nginx.com nxt_field_end_test_char(p[3]); 70816Svbart@nginx.com 709409Svbart@nginx.com nxt_field_end_test_char(p[4]); 710409Svbart@nginx.com nxt_field_end_test_char(p[5]); 711409Svbart@nginx.com nxt_field_end_test_char(p[6]); 712409Svbart@nginx.com nxt_field_end_test_char(p[7]); 713409Svbart@nginx.com 714409Svbart@nginx.com nxt_field_end_test_char(p[8]); 715409Svbart@nginx.com nxt_field_end_test_char(p[9]); 716409Svbart@nginx.com nxt_field_end_test_char(p[10]); 717409Svbart@nginx.com nxt_field_end_test_char(p[11]); 718409Svbart@nginx.com 719409Svbart@nginx.com nxt_field_end_test_char(p[12]); 720409Svbart@nginx.com nxt_field_end_test_char(p[13]); 721409Svbart@nginx.com nxt_field_end_test_char(p[14]); 722409Svbart@nginx.com nxt_field_end_test_char(p[15]); 723409Svbart@nginx.com 724409Svbart@nginx.com p += 16; 72516Svbart@nginx.com } 72616Svbart@nginx.com 727409Svbart@nginx.com while (nxt_fast_path(end - p >= 4)) { 72819Svbart@nginx.com 729409Svbart@nginx.com nxt_field_end_test_char(p[0]); 730409Svbart@nginx.com nxt_field_end_test_char(p[1]); 731409Svbart@nginx.com nxt_field_end_test_char(p[2]); 732409Svbart@nginx.com nxt_field_end_test_char(p[3]); 73316Svbart@nginx.com 734409Svbart@nginx.com p += 4; 73519Svbart@nginx.com } 73619Svbart@nginx.com 73719Svbart@nginx.com switch (end - p) { 73816Svbart@nginx.com case 3: 739409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 74039Svbart@nginx.com /* Fall through. */ 74116Svbart@nginx.com case 2: 742409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 74339Svbart@nginx.com /* Fall through. */ 74416Svbart@nginx.com case 1: 745409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 74639Svbart@nginx.com /* Fall through. */ 74716Svbart@nginx.com case 0: 74816Svbart@nginx.com break; 74916Svbart@nginx.com default: 75016Svbart@nginx.com nxt_unreachable(); 75116Svbart@nginx.com } 75216Svbart@nginx.com 75316Svbart@nginx.com return p; 75416Svbart@nginx.com } 75516Svbart@nginx.com 75616Svbart@nginx.com 75716Svbart@nginx.com static nxt_int_t 75816Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos, 75916Svbart@nginx.com u_char *end) 76016Svbart@nginx.com { 76160Svbart@nginx.com u_char *p; 76260Svbart@nginx.com nxt_http_field_t *field; 76316Svbart@nginx.com 76416Svbart@nginx.com p = *pos; 76516Svbart@nginx.com 76616Svbart@nginx.com if (nxt_fast_path(*p == '\r')) { 76716Svbart@nginx.com p++; 76816Svbart@nginx.com 76916Svbart@nginx.com if (nxt_slow_path(p == end)) { 77016Svbart@nginx.com rp->handler = &nxt_http_parse_field_end; 77116Svbart@nginx.com return NXT_AGAIN; 77216Svbart@nginx.com } 77316Svbart@nginx.com } 77416Svbart@nginx.com 77516Svbart@nginx.com if (nxt_fast_path(*p == '\n')) { 77616Svbart@nginx.com *pos = p + 1; 77716Svbart@nginx.com 77867Svbart@nginx.com if (rp->field_name.length != 0) { 77960Svbart@nginx.com field = nxt_list_add(rp->fields); 78016Svbart@nginx.com 78160Svbart@nginx.com if (nxt_slow_path(field == NULL)) { 78260Svbart@nginx.com return NXT_ERROR; 78316Svbart@nginx.com } 78416Svbart@nginx.com 785417Svbart@nginx.com field->hash = nxt_http_field_hash_end(rp->field_hash); 786417Svbart@nginx.com field->skip = 0; 78760Svbart@nginx.com 788417Svbart@nginx.com field->name_length = rp->field_name.length; 789417Svbart@nginx.com field->value_length = rp->field_value.length; 790417Svbart@nginx.com field->name = rp->field_name.start; 791417Svbart@nginx.com field->value = rp->field_value.start; 79267Svbart@nginx.com 793417Svbart@nginx.com rp->field_hash = NXT_HTTP_FIELD_HASH_INIT; 79467Svbart@nginx.com 79567Svbart@nginx.com rp->field_name.length = 0; 79667Svbart@nginx.com rp->field_value.length = 0; 79716Svbart@nginx.com 79816Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 79916Svbart@nginx.com return NXT_OK; 80016Svbart@nginx.com } 80116Svbart@nginx.com 80216Svbart@nginx.com return NXT_DONE; 80316Svbart@nginx.com } 80416Svbart@nginx.com 805480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 80616Svbart@nginx.com } 80716Svbart@nginx.com 80816Svbart@nginx.com 809112Smax.romanov@nginx.com #define \ 810112Smax.romanov@nginx.com nxt_http_is_normal(c) \ 811112Smax.romanov@nginx.com (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0)) 812112Smax.romanov@nginx.com 813112Smax.romanov@nginx.com 814112Smax.romanov@nginx.com static const uint8_t nxt_http_normal[32] nxt_aligned(32) = { 815112Smax.romanov@nginx.com 816112Smax.romanov@nginx.com /* \0 \r \n */ 817611Svbart@nginx.com 0xFE, 0xDB, 0xFF, 0xFF, /* 1111 1110 1101 1011 1111 1111 1111 1111 */ 818112Smax.romanov@nginx.com 819112Smax.romanov@nginx.com /* '&%$ #"! /.-, |*)( 7654 3210 ?>=< ;:98 */ 820611Svbart@nginx.com 0xD6, 0x37, 0xFF, 0x7F, /* 1101 0110 0011 0111 1111 1111 0111 1111 */ 821112Smax.romanov@nginx.com 822112Smax.romanov@nginx.com /* GFED CBA@ ONML KJIH WVUT SRQP _^]\ [ZYX */ 823611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 824112Smax.romanov@nginx.com 825112Smax.romanov@nginx.com /* gfed cba` onml kjih wvut srqp ~}| {zyx */ 826611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 827112Smax.romanov@nginx.com 828611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 829611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 830611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 831611Svbart@nginx.com 0xFF, 0xFF, 0xFF, 0xFF, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 832112Smax.romanov@nginx.com }; 833112Smax.romanov@nginx.com 834112Smax.romanov@nginx.com 835112Smax.romanov@nginx.com static nxt_int_t 836112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp) 837112Smax.romanov@nginx.com { 838112Smax.romanov@nginx.com u_char *p, *u, c, ch, high; 839112Smax.romanov@nginx.com enum { 840112Smax.romanov@nginx.com sw_normal = 0, 841112Smax.romanov@nginx.com sw_slash, 842112Smax.romanov@nginx.com sw_dot, 843112Smax.romanov@nginx.com sw_dot_dot, 844112Smax.romanov@nginx.com sw_quoted, 845112Smax.romanov@nginx.com sw_quoted_second, 846112Smax.romanov@nginx.com } state, saved_state; 847112Smax.romanov@nginx.com 848112Smax.romanov@nginx.com nxt_prefetch(nxt_http_normal); 849112Smax.romanov@nginx.com 850112Smax.romanov@nginx.com state = sw_normal; 851112Smax.romanov@nginx.com saved_state = sw_normal; 852112Smax.romanov@nginx.com p = rp->target_start; 853112Smax.romanov@nginx.com 854112Smax.romanov@nginx.com u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1); 855112Smax.romanov@nginx.com 856112Smax.romanov@nginx.com if (nxt_slow_path(u == NULL)) { 857112Smax.romanov@nginx.com return NXT_ERROR; 858112Smax.romanov@nginx.com } 859112Smax.romanov@nginx.com 860112Smax.romanov@nginx.com rp->path.length = 0; 861112Smax.romanov@nginx.com rp->path.start = u; 862112Smax.romanov@nginx.com 863112Smax.romanov@nginx.com high = '\0'; 864112Smax.romanov@nginx.com rp->exten_start = NULL; 865112Smax.romanov@nginx.com rp->args_start = NULL; 866112Smax.romanov@nginx.com 867112Smax.romanov@nginx.com while (p < rp->target_end) { 868112Smax.romanov@nginx.com 869112Smax.romanov@nginx.com ch = *p++; 870112Smax.romanov@nginx.com 871112Smax.romanov@nginx.com again: 872112Smax.romanov@nginx.com 873112Smax.romanov@nginx.com switch (state) { 874112Smax.romanov@nginx.com 875112Smax.romanov@nginx.com case sw_normal: 876112Smax.romanov@nginx.com 877112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 878112Smax.romanov@nginx.com *u++ = ch; 879112Smax.romanov@nginx.com continue; 880112Smax.romanov@nginx.com } 881112Smax.romanov@nginx.com 882112Smax.romanov@nginx.com switch (ch) { 883112Smax.romanov@nginx.com case '/': 884112Smax.romanov@nginx.com rp->exten_start = NULL; 885112Smax.romanov@nginx.com state = sw_slash; 886112Smax.romanov@nginx.com *u++ = ch; 887112Smax.romanov@nginx.com continue; 888112Smax.romanov@nginx.com case '%': 889112Smax.romanov@nginx.com saved_state = state; 890112Smax.romanov@nginx.com state = sw_quoted; 891112Smax.romanov@nginx.com continue; 892112Smax.romanov@nginx.com case '?': 893112Smax.romanov@nginx.com rp->args_start = p; 894112Smax.romanov@nginx.com goto args; 895112Smax.romanov@nginx.com case '#': 896112Smax.romanov@nginx.com goto done; 897112Smax.romanov@nginx.com case '.': 898112Smax.romanov@nginx.com rp->exten_start = u + 1; 899112Smax.romanov@nginx.com *u++ = ch; 900112Smax.romanov@nginx.com continue; 901112Smax.romanov@nginx.com case '+': 902112Smax.romanov@nginx.com rp->plus_in_target = 1; 903112Smax.romanov@nginx.com /* Fall through. */ 904112Smax.romanov@nginx.com default: 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_slash: 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 continue; 922112Smax.romanov@nginx.com case '.': 923112Smax.romanov@nginx.com state = sw_dot; 924112Smax.romanov@nginx.com *u++ = ch; 925112Smax.romanov@nginx.com continue; 926112Smax.romanov@nginx.com case '%': 927112Smax.romanov@nginx.com saved_state = state; 928112Smax.romanov@nginx.com state = sw_quoted; 929112Smax.romanov@nginx.com continue; 930112Smax.romanov@nginx.com case '?': 931112Smax.romanov@nginx.com rp->args_start = p; 932112Smax.romanov@nginx.com goto args; 933112Smax.romanov@nginx.com case '#': 934112Smax.romanov@nginx.com goto done; 935112Smax.romanov@nginx.com case '+': 936112Smax.romanov@nginx.com rp->plus_in_target = 1; 937112Smax.romanov@nginx.com /* Fall through. */ 938112Smax.romanov@nginx.com default: 939112Smax.romanov@nginx.com state = sw_normal; 940112Smax.romanov@nginx.com *u++ = ch; 941112Smax.romanov@nginx.com continue; 942112Smax.romanov@nginx.com } 943112Smax.romanov@nginx.com 944112Smax.romanov@nginx.com break; 945112Smax.romanov@nginx.com 946112Smax.romanov@nginx.com case sw_dot: 947112Smax.romanov@nginx.com 948112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 949112Smax.romanov@nginx.com state = sw_normal; 950112Smax.romanov@nginx.com *u++ = ch; 951112Smax.romanov@nginx.com continue; 952112Smax.romanov@nginx.com } 953112Smax.romanov@nginx.com 954112Smax.romanov@nginx.com switch (ch) { 955112Smax.romanov@nginx.com case '/': 956112Smax.romanov@nginx.com state = sw_slash; 957112Smax.romanov@nginx.com u--; 958112Smax.romanov@nginx.com continue; 959112Smax.romanov@nginx.com case '.': 960112Smax.romanov@nginx.com state = sw_dot_dot; 961112Smax.romanov@nginx.com *u++ = ch; 962112Smax.romanov@nginx.com continue; 963112Smax.romanov@nginx.com case '%': 964112Smax.romanov@nginx.com saved_state = state; 965112Smax.romanov@nginx.com state = sw_quoted; 966112Smax.romanov@nginx.com continue; 967112Smax.romanov@nginx.com case '?': 968112Smax.romanov@nginx.com rp->args_start = p; 969112Smax.romanov@nginx.com goto args; 970112Smax.romanov@nginx.com case '#': 971112Smax.romanov@nginx.com goto done; 972112Smax.romanov@nginx.com case '+': 973112Smax.romanov@nginx.com rp->plus_in_target = 1; 974112Smax.romanov@nginx.com /* Fall through. */ 975112Smax.romanov@nginx.com default: 976112Smax.romanov@nginx.com state = sw_normal; 977112Smax.romanov@nginx.com *u++ = ch; 978112Smax.romanov@nginx.com continue; 979112Smax.romanov@nginx.com } 980112Smax.romanov@nginx.com 981112Smax.romanov@nginx.com break; 982112Smax.romanov@nginx.com 983112Smax.romanov@nginx.com case sw_dot_dot: 984112Smax.romanov@nginx.com 985112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 986112Smax.romanov@nginx.com state = sw_normal; 987112Smax.romanov@nginx.com *u++ = ch; 988112Smax.romanov@nginx.com continue; 989112Smax.romanov@nginx.com } 990112Smax.romanov@nginx.com 991112Smax.romanov@nginx.com switch (ch) { 992112Smax.romanov@nginx.com case '/': 993112Smax.romanov@nginx.com state = sw_slash; 994112Smax.romanov@nginx.com u -= 5; 995112Smax.romanov@nginx.com for ( ;; ) { 996112Smax.romanov@nginx.com if (u < rp->path.start) { 997480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 998112Smax.romanov@nginx.com } 999112Smax.romanov@nginx.com if (*u == '/') { 1000112Smax.romanov@nginx.com u++; 1001112Smax.romanov@nginx.com break; 1002112Smax.romanov@nginx.com } 1003112Smax.romanov@nginx.com u--; 1004112Smax.romanov@nginx.com } 1005112Smax.romanov@nginx.com break; 1006112Smax.romanov@nginx.com 1007112Smax.romanov@nginx.com case '%': 1008112Smax.romanov@nginx.com saved_state = state; 1009112Smax.romanov@nginx.com state = sw_quoted; 1010112Smax.romanov@nginx.com continue; 1011112Smax.romanov@nginx.com case '?': 1012112Smax.romanov@nginx.com rp->args_start = p; 1013112Smax.romanov@nginx.com goto args; 1014112Smax.romanov@nginx.com case '#': 1015112Smax.romanov@nginx.com goto done; 1016112Smax.romanov@nginx.com case '+': 1017112Smax.romanov@nginx.com rp->plus_in_target = 1; 1018112Smax.romanov@nginx.com /* Fall through. */ 1019112Smax.romanov@nginx.com default: 1020112Smax.romanov@nginx.com state = sw_normal; 1021112Smax.romanov@nginx.com *u++ = ch; 1022112Smax.romanov@nginx.com continue; 1023112Smax.romanov@nginx.com } 1024112Smax.romanov@nginx.com 1025112Smax.romanov@nginx.com break; 1026112Smax.romanov@nginx.com 1027112Smax.romanov@nginx.com case sw_quoted: 1028112Smax.romanov@nginx.com rp->quoted_target = 1; 1029112Smax.romanov@nginx.com 1030112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 1031112Smax.romanov@nginx.com high = (u_char) (ch - '0'); 1032112Smax.romanov@nginx.com state = sw_quoted_second; 1033112Smax.romanov@nginx.com continue; 1034112Smax.romanov@nginx.com } 1035112Smax.romanov@nginx.com 1036112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1037112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1038112Smax.romanov@nginx.com high = (u_char) (c - 'a' + 10); 1039112Smax.romanov@nginx.com state = sw_quoted_second; 1040112Smax.romanov@nginx.com continue; 1041112Smax.romanov@nginx.com } 1042112Smax.romanov@nginx.com 1043480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 1044112Smax.romanov@nginx.com 1045112Smax.romanov@nginx.com case sw_quoted_second: 1046112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 1047112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + ch - '0'); 1048112Smax.romanov@nginx.com 1049*1167Svbart@nginx.com if (ch == '%') { 1050112Smax.romanov@nginx.com state = sw_normal; 1051*1167Svbart@nginx.com *u++ = '%'; 1052*1167Svbart@nginx.com 1053*1167Svbart@nginx.com if (rp->encoded_slashes) { 1054*1167Svbart@nginx.com *u++ = '2'; 1055*1167Svbart@nginx.com *u++ = '5'; 1056*1167Svbart@nginx.com } 1057*1167Svbart@nginx.com 1058112Smax.romanov@nginx.com continue; 1059*1167Svbart@nginx.com } 1060112Smax.romanov@nginx.com 1061*1167Svbart@nginx.com if (ch == '#') { 1062*1167Svbart@nginx.com state = sw_normal; 1063*1167Svbart@nginx.com *u++ = '#'; 1064*1167Svbart@nginx.com continue; 1065*1167Svbart@nginx.com } 1066*1167Svbart@nginx.com 1067*1167Svbart@nginx.com if (ch == '\0') { 1068480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 1069112Smax.romanov@nginx.com } 1070112Smax.romanov@nginx.com 1071112Smax.romanov@nginx.com state = saved_state; 1072112Smax.romanov@nginx.com goto again; 1073112Smax.romanov@nginx.com } 1074112Smax.romanov@nginx.com 1075112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1076112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1077112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + c - 'a' + 10); 1078112Smax.romanov@nginx.com 1079112Smax.romanov@nginx.com if (ch == '?') { 1080112Smax.romanov@nginx.com state = sw_normal; 1081112Smax.romanov@nginx.com *u++ = ch; 1082112Smax.romanov@nginx.com continue; 1083*1167Svbart@nginx.com } 1084112Smax.romanov@nginx.com 1085*1167Svbart@nginx.com if (ch == '/' && rp->encoded_slashes) { 1086*1167Svbart@nginx.com state = sw_normal; 1087*1167Svbart@nginx.com *u++ = '%'; 1088*1167Svbart@nginx.com *u++ = '2'; 1089*1167Svbart@nginx.com *u++ = p[-1]; /* 'f' or 'F' */ 1090*1167Svbart@nginx.com continue; 1091*1167Svbart@nginx.com } 1092*1167Svbart@nginx.com 1093*1167Svbart@nginx.com if (ch == '+') { 1094112Smax.romanov@nginx.com rp->plus_in_target = 1; 1095112Smax.romanov@nginx.com } 1096112Smax.romanov@nginx.com 1097112Smax.romanov@nginx.com state = saved_state; 1098112Smax.romanov@nginx.com goto again; 1099112Smax.romanov@nginx.com } 1100112Smax.romanov@nginx.com 1101480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 1102112Smax.romanov@nginx.com } 1103112Smax.romanov@nginx.com } 1104112Smax.romanov@nginx.com 1105112Smax.romanov@nginx.com if (state >= sw_quoted) { 1106480Svbart@nginx.com return NXT_HTTP_PARSE_INVALID; 1107112Smax.romanov@nginx.com } 1108112Smax.romanov@nginx.com 1109112Smax.romanov@nginx.com args: 1110112Smax.romanov@nginx.com 1111112Smax.romanov@nginx.com for (/* void */; p < rp->target_end; p++) { 1112112Smax.romanov@nginx.com if (*p == '#') { 1113112Smax.romanov@nginx.com break; 1114112Smax.romanov@nginx.com } 1115112Smax.romanov@nginx.com } 1116112Smax.romanov@nginx.com 1117112Smax.romanov@nginx.com if (rp->args_start != NULL) { 1118112Smax.romanov@nginx.com rp->args.length = p - rp->args_start; 1119112Smax.romanov@nginx.com rp->args.start = rp->args_start; 1120112Smax.romanov@nginx.com } 1121112Smax.romanov@nginx.com 1122112Smax.romanov@nginx.com done: 1123112Smax.romanov@nginx.com 1124112Smax.romanov@nginx.com rp->path.length = u - rp->path.start; 1125112Smax.romanov@nginx.com 1126112Smax.romanov@nginx.com if (rp->exten_start) { 1127112Smax.romanov@nginx.com rp->exten.length = u - rp->exten_start; 1128112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 1129112Smax.romanov@nginx.com } 1130112Smax.romanov@nginx.com 1131112Smax.romanov@nginx.com return NXT_OK; 1132112Smax.romanov@nginx.com } 1133417Svbart@nginx.com 1134417Svbart@nginx.com 11351126Smax.romanov@nginx.com const nxt_lvlhsh_proto_t nxt_http_fields_hash_proto nxt_aligned(64) = { 1136417Svbart@nginx.com NXT_LVLHSH_BUCKET_SIZE(64), 1137417Svbart@nginx.com { NXT_HTTP_FIELD_LVLHSH_SHIFT, 0, 0, 0, 0, 0, 0, 0 }, 1138417Svbart@nginx.com nxt_http_field_hash_test, 1139417Svbart@nginx.com nxt_http_field_hash_alloc, 1140417Svbart@nginx.com nxt_http_field_hash_free, 1141417Svbart@nginx.com }; 1142417Svbart@nginx.com 1143417Svbart@nginx.com 1144417Svbart@nginx.com static nxt_int_t 1145417Svbart@nginx.com nxt_http_field_hash_test(nxt_lvlhsh_query_t *lhq, void *data) 1146417Svbart@nginx.com { 1147417Svbart@nginx.com nxt_http_field_proc_t *field; 1148417Svbart@nginx.com 1149417Svbart@nginx.com field = data; 1150417Svbart@nginx.com 1151417Svbart@nginx.com if (nxt_strcasestr_eq(&lhq->key, &field->name)) { 1152417Svbart@nginx.com return NXT_OK; 1153417Svbart@nginx.com } 1154417Svbart@nginx.com 1155417Svbart@nginx.com return NXT_DECLINED; 1156417Svbart@nginx.com } 1157417Svbart@nginx.com 1158417Svbart@nginx.com 1159417Svbart@nginx.com static void * 1160417Svbart@nginx.com nxt_http_field_hash_alloc(void *pool, size_t size) 1161417Svbart@nginx.com { 1162417Svbart@nginx.com return nxt_mp_align(pool, size, size); 1163417Svbart@nginx.com } 1164417Svbart@nginx.com 1165417Svbart@nginx.com 1166417Svbart@nginx.com static void 1167417Svbart@nginx.com nxt_http_field_hash_free(void *pool, void *p) 1168417Svbart@nginx.com { 1169417Svbart@nginx.com nxt_mp_free(pool, p); 1170417Svbart@nginx.com } 1171417Svbart@nginx.com 1172417Svbart@nginx.com 1173417Svbart@nginx.com static nxt_int_t 1174417Svbart@nginx.com nxt_http_field_hash_collision(nxt_lvlhsh_query_t *lhq, void *data) 1175417Svbart@nginx.com { 1176417Svbart@nginx.com return NXT_OK; 1177417Svbart@nginx.com } 1178417Svbart@nginx.com 1179417Svbart@nginx.com 1180417Svbart@nginx.com nxt_int_t 1181417Svbart@nginx.com nxt_http_fields_hash(nxt_lvlhsh_t *hash, nxt_mp_t *mp, 1182417Svbart@nginx.com nxt_http_field_proc_t items[], nxt_uint_t count) 1183417Svbart@nginx.com { 1184417Svbart@nginx.com u_char ch; 1185417Svbart@nginx.com uint32_t key; 1186417Svbart@nginx.com nxt_str_t *name; 1187417Svbart@nginx.com nxt_int_t ret; 1188417Svbart@nginx.com nxt_uint_t i, j; 1189417Svbart@nginx.com nxt_lvlhsh_query_t lhq; 1190417Svbart@nginx.com 1191417Svbart@nginx.com lhq.replace = 0; 1192417Svbart@nginx.com lhq.proto = &nxt_http_fields_hash_proto; 1193417Svbart@nginx.com lhq.pool = mp; 1194417Svbart@nginx.com 1195417Svbart@nginx.com for (i = 0; i < count; i++) { 1196417Svbart@nginx.com key = NXT_HTTP_FIELD_HASH_INIT; 1197417Svbart@nginx.com name = &items[i].name; 1198417Svbart@nginx.com 1199417Svbart@nginx.com for (j = 0; j < name->length; j++) { 1200417Svbart@nginx.com ch = nxt_lowcase(name->start[j]); 1201417Svbart@nginx.com key = nxt_http_field_hash_char(key, ch); 1202417Svbart@nginx.com } 1203417Svbart@nginx.com 1204611Svbart@nginx.com lhq.key_hash = nxt_http_field_hash_end(key) & 0xFFFF; 1205417Svbart@nginx.com lhq.key = *name; 1206417Svbart@nginx.com lhq.value = &items[i]; 1207417Svbart@nginx.com 1208417Svbart@nginx.com ret = nxt_lvlhsh_insert(hash, &lhq); 1209417Svbart@nginx.com 1210417Svbart@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1211417Svbart@nginx.com return NXT_ERROR; 1212417Svbart@nginx.com } 1213417Svbart@nginx.com } 1214417Svbart@nginx.com 1215417Svbart@nginx.com return NXT_OK; 1216417Svbart@nginx.com } 1217417Svbart@nginx.com 1218417Svbart@nginx.com 1219417Svbart@nginx.com nxt_uint_t 1220417Svbart@nginx.com nxt_http_fields_hash_collisions(nxt_lvlhsh_t *hash, nxt_mp_t *mp, 1221417Svbart@nginx.com nxt_http_field_proc_t items[], nxt_uint_t count, nxt_bool_t level) 1222417Svbart@nginx.com { 1223417Svbart@nginx.com u_char ch; 1224417Svbart@nginx.com uint32_t key, mask; 1225417Svbart@nginx.com nxt_str_t *name; 1226417Svbart@nginx.com nxt_uint_t colls, i, j; 1227417Svbart@nginx.com nxt_lvlhsh_proto_t proto; 1228417Svbart@nginx.com nxt_lvlhsh_query_t lhq; 1229417Svbart@nginx.com 1230417Svbart@nginx.com proto = nxt_http_fields_hash_proto; 1231417Svbart@nginx.com proto.test = nxt_http_field_hash_collision; 1232417Svbart@nginx.com 1233417Svbart@nginx.com lhq.replace = 0; 1234417Svbart@nginx.com lhq.proto = &proto; 1235417Svbart@nginx.com lhq.pool = mp; 1236417Svbart@nginx.com 1237611Svbart@nginx.com mask = level ? (1 << NXT_HTTP_FIELD_LVLHSH_SHIFT) - 1 : 0xFFFF; 1238417Svbart@nginx.com 1239417Svbart@nginx.com colls = 0; 1240417Svbart@nginx.com 1241417Svbart@nginx.com for (i = 0; i < count; i++) { 1242417Svbart@nginx.com key = NXT_HTTP_FIELD_HASH_INIT; 1243417Svbart@nginx.com name = &items[i].name; 1244417Svbart@nginx.com 1245417Svbart@nginx.com for (j = 0; j < name->length; j++) { 1246417Svbart@nginx.com ch = nxt_lowcase(name->start[j]); 1247417Svbart@nginx.com key = nxt_http_field_hash_char(key, ch); 1248417Svbart@nginx.com } 1249417Svbart@nginx.com 1250417Svbart@nginx.com lhq.key_hash = nxt_http_field_hash_end(key) & mask; 1251418Svbart@nginx.com lhq.value = &items[i]; 1252417Svbart@nginx.com 1253417Svbart@nginx.com if (nxt_lvlhsh_insert(hash, &lhq) == NXT_DECLINED) { 1254417Svbart@nginx.com colls++; 1255417Svbart@nginx.com } 1256417Svbart@nginx.com } 1257417Svbart@nginx.com 1258417Svbart@nginx.com return colls; 1259417Svbart@nginx.com } 1260417Svbart@nginx.com 1261417Svbart@nginx.com 1262417Svbart@nginx.com nxt_int_t 1263417Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, nxt_lvlhsh_t *hash, void *ctx) 1264417Svbart@nginx.com { 12651126Smax.romanov@nginx.com nxt_int_t ret; 12661126Smax.romanov@nginx.com nxt_http_field_t *field; 1267417Svbart@nginx.com 1268417Svbart@nginx.com nxt_list_each(field, fields) { 1269417Svbart@nginx.com 12701126Smax.romanov@nginx.com ret = nxt_http_field_process(field, hash, ctx); 1271417Svbart@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1272417Svbart@nginx.com return ret; 1273417Svbart@nginx.com } 1274417Svbart@nginx.com 1275417Svbart@nginx.com } nxt_list_loop; 1276417Svbart@nginx.com 1277417Svbart@nginx.com return NXT_OK; 1278417Svbart@nginx.com } 1279