116Svbart@nginx.com 216Svbart@nginx.com /* 316Svbart@nginx.com * Copyright (C) NGINX, Inc. 416Svbart@nginx.com * Copyright (C) Valentin V. Bartenev 516Svbart@nginx.com */ 616Svbart@nginx.com 716Svbart@nginx.com #include <nxt_main.h> 816Svbart@nginx.com 916Svbart@nginx.com 1016Svbart@nginx.com typedef struct { 1167Svbart@nginx.com nxt_http_field_handler_t handler; 1267Svbart@nginx.com uintptr_t data; 1360Svbart@nginx.com 1416Svbart@nginx.com union { 1567Svbart@nginx.com uint8_t str[8]; 1667Svbart@nginx.com uint64_t ui64; 1716Svbart@nginx.com } key[]; 1860Svbart@nginx.com } nxt_http_fields_hash_elt_t; 1916Svbart@nginx.com 2016Svbart@nginx.com 2116Svbart@nginx.com struct nxt_http_fields_hash_s { 2267Svbart@nginx.com size_t min_length; 2367Svbart@nginx.com size_t max_length; 2467Svbart@nginx.com void *long_fields; 2567Svbart@nginx.com nxt_http_fields_hash_elt_t *elts[]; 2616Svbart@nginx.com }; 2716Svbart@nginx.com 2816Svbart@nginx.com 2960Svbart@nginx.com #define nxt_http_fields_hash_next_elt(elt, n) \ 3060Svbart@nginx.com ((nxt_http_fields_hash_elt_t *) ((u_char *) (elt) \ 3160Svbart@nginx.com + sizeof(nxt_http_fields_hash_elt_t) \ 3260Svbart@nginx.com + n * 8)) 3360Svbart@nginx.com 3460Svbart@nginx.com 3516Svbart@nginx.com static nxt_int_t nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, 3616Svbart@nginx.com u_char **pos, u_char *end); 3716Svbart@nginx.com static nxt_int_t nxt_http_parse_request_line(nxt_http_request_parse_t *rp, 3816Svbart@nginx.com u_char **pos, u_char *end); 3916Svbart@nginx.com static nxt_int_t nxt_http_parse_field_name(nxt_http_request_parse_t *rp, 4016Svbart@nginx.com u_char **pos, u_char *end); 4116Svbart@nginx.com static nxt_int_t nxt_http_parse_field_value(nxt_http_request_parse_t *rp, 4216Svbart@nginx.com u_char **pos, u_char *end); 4316Svbart@nginx.com static u_char *nxt_http_lookup_field_end(u_char *p, u_char *end); 4416Svbart@nginx.com static nxt_int_t nxt_http_parse_field_end(nxt_http_request_parse_t *rp, 4516Svbart@nginx.com u_char **pos, u_char *end); 4616Svbart@nginx.com 4767Svbart@nginx.com static void nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, 4867Svbart@nginx.com uint64_t key[4], nxt_http_field_t *field); 4967Svbart@nginx.com static void nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash, 5067Svbart@nginx.com nxt_http_field_t *field); 5116Svbart@nginx.com 52112Smax.romanov@nginx.com static nxt_int_t nxt_http_parse_complex_target(nxt_http_request_parse_t *rp); 53112Smax.romanov@nginx.com 5416Svbart@nginx.com 5516Svbart@nginx.com typedef enum { 5616Svbart@nginx.com NXT_HTTP_TARGET_SPACE = 1, /* \s */ 5716Svbart@nginx.com NXT_HTTP_TARGET_HASH, /* # */ 5816Svbart@nginx.com NXT_HTTP_TARGET_AGAIN, 5916Svbart@nginx.com NXT_HTTP_TARGET_BAD, /* \0\r\n */ 6016Svbart@nginx.com 6116Svbart@nginx.com /* traps below are used for extended check only */ 6216Svbart@nginx.com 6316Svbart@nginx.com NXT_HTTP_TARGET_SLASH = 5, /* / */ 6416Svbart@nginx.com NXT_HTTP_TARGET_DOT, /* . */ 6516Svbart@nginx.com NXT_HTTP_TARGET_ARGS_MARK, /* ? */ 6616Svbart@nginx.com NXT_HTTP_TARGET_QUOTE_MARK, /* % */ 6716Svbart@nginx.com NXT_HTTP_TARGET_PLUS, /* + */ 6816Svbart@nginx.com } nxt_http_target_traps_e; 6916Svbart@nginx.com 7016Svbart@nginx.com 7116Svbart@nginx.com static const uint8_t nxt_http_target_chars[256] nxt_aligned(64) = { 7216Svbart@nginx.com /* \0 \n \r */ 7316Svbart@nginx.com 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, 7416Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7552Svbart@nginx.com 7652Svbart@nginx.com /* \s ! " # $ % & ' ( ) * + , - . / */ 7716Svbart@nginx.com 1, 0, 0, 2, 0, 8, 0, 0, 0, 0, 0, 9, 0, 0, 6, 5, 7852Svbart@nginx.com 7952Svbart@nginx.com /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 8016Svbart@nginx.com 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8116Svbart@nginx.com }; 8216Svbart@nginx.com 8316Svbart@nginx.com 8416Svbart@nginx.com nxt_inline nxt_http_target_traps_e 8516Svbart@nginx.com nxt_http_parse_target(u_char **pos, u_char *end) 8616Svbart@nginx.com { 8716Svbart@nginx.com u_char *p; 8816Svbart@nginx.com nxt_uint_t trap; 8916Svbart@nginx.com 9016Svbart@nginx.com p = *pos; 9116Svbart@nginx.com 92*409Svbart@nginx.com while (nxt_fast_path(end - p >= 10)) { 9316Svbart@nginx.com 94*409Svbart@nginx.com #define nxt_target_test_char(ch) \ 95*409Svbart@nginx.com \ 96*409Svbart@nginx.com trap = nxt_http_target_chars[ch]; \ 9716Svbart@nginx.com \ 98*409Svbart@nginx.com if (nxt_slow_path(trap != 0)) { \ 99*409Svbart@nginx.com *pos = &(ch); \ 100*409Svbart@nginx.com return trap; \ 10116Svbart@nginx.com } 10216Svbart@nginx.com 103*409Svbart@nginx.com /* enddef */ 104*409Svbart@nginx.com 105*409Svbart@nginx.com nxt_target_test_char(p[0]); 106*409Svbart@nginx.com nxt_target_test_char(p[1]); 107*409Svbart@nginx.com nxt_target_test_char(p[2]); 108*409Svbart@nginx.com nxt_target_test_char(p[3]); 10916Svbart@nginx.com 110*409Svbart@nginx.com nxt_target_test_char(p[4]); 111*409Svbart@nginx.com nxt_target_test_char(p[5]); 112*409Svbart@nginx.com nxt_target_test_char(p[6]); 113*409Svbart@nginx.com nxt_target_test_char(p[7]); 11416Svbart@nginx.com 115*409Svbart@nginx.com nxt_target_test_char(p[8]); 116*409Svbart@nginx.com nxt_target_test_char(p[9]); 11716Svbart@nginx.com 118*409Svbart@nginx.com p += 10; 11916Svbart@nginx.com } 12016Svbart@nginx.com 121*409Svbart@nginx.com return NXT_HTTP_TARGET_AGAIN; 12216Svbart@nginx.com } 12316Svbart@nginx.com 12416Svbart@nginx.com 12516Svbart@nginx.com nxt_int_t 12616Svbart@nginx.com nxt_http_parse_request(nxt_http_request_parse_t *rp, nxt_buf_mem_t *b) 12716Svbart@nginx.com { 12816Svbart@nginx.com nxt_int_t rc; 12916Svbart@nginx.com 13016Svbart@nginx.com if (rp->handler == NULL) { 13116Svbart@nginx.com rp->handler = &nxt_http_parse_request_line; 13216Svbart@nginx.com } 13316Svbart@nginx.com 13416Svbart@nginx.com do { 13516Svbart@nginx.com rc = rp->handler(rp, &b->pos, b->free); 13616Svbart@nginx.com } while (rc == NXT_OK); 13716Svbart@nginx.com 13816Svbart@nginx.com return rc; 13916Svbart@nginx.com } 14016Svbart@nginx.com 14116Svbart@nginx.com 14216Svbart@nginx.com static nxt_int_t 14316Svbart@nginx.com nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos, 14416Svbart@nginx.com u_char *end) 14516Svbart@nginx.com { 14616Svbart@nginx.com u_char *p, ch, *after_slash; 14716Svbart@nginx.com nxt_int_t rc; 14816Svbart@nginx.com nxt_http_ver_t version; 14916Svbart@nginx.com nxt_http_target_traps_e trap; 15016Svbart@nginx.com 15116Svbart@nginx.com static const nxt_http_ver_t http11 = { "HTTP/1.1" }; 15216Svbart@nginx.com static const nxt_http_ver_t http10 = { "HTTP/1.0" }; 15316Svbart@nginx.com 15416Svbart@nginx.com p = *pos; 15516Svbart@nginx.com 15616Svbart@nginx.com rp->method.start = p; 15716Svbart@nginx.com 158*409Svbart@nginx.com for ( ;; ) { 159*409Svbart@nginx.com 160*409Svbart@nginx.com while (nxt_fast_path(end - p >= 8)) { 16116Svbart@nginx.com 162*409Svbart@nginx.com #define nxt_method_test_char(ch) \ 163*409Svbart@nginx.com \ 164*409Svbart@nginx.com if (nxt_slow_path((ch) < 'A' || (ch) > 'Z')) { \ 165*409Svbart@nginx.com p = &(ch); \ 166*409Svbart@nginx.com goto method_unusual_char; \ 16716Svbart@nginx.com } 16816Svbart@nginx.com 169*409Svbart@nginx.com /* enddef */ 170*409Svbart@nginx.com 171*409Svbart@nginx.com nxt_method_test_char(p[0]); 172*409Svbart@nginx.com nxt_method_test_char(p[1]); 173*409Svbart@nginx.com nxt_method_test_char(p[2]); 174*409Svbart@nginx.com nxt_method_test_char(p[3]); 17516Svbart@nginx.com 176*409Svbart@nginx.com nxt_method_test_char(p[4]); 177*409Svbart@nginx.com nxt_method_test_char(p[5]); 178*409Svbart@nginx.com nxt_method_test_char(p[6]); 179*409Svbart@nginx.com nxt_method_test_char(p[7]); 18016Svbart@nginx.com 181*409Svbart@nginx.com p += 8; 182*409Svbart@nginx.com } 18316Svbart@nginx.com 184*409Svbart@nginx.com return NXT_AGAIN; 185*409Svbart@nginx.com 186*409Svbart@nginx.com method_unusual_char: 187*409Svbart@nginx.com 188*409Svbart@nginx.com ch = *p; 18916Svbart@nginx.com 19016Svbart@nginx.com if (nxt_fast_path(ch == ' ')) { 19116Svbart@nginx.com rp->method.length = p - rp->method.start; 19216Svbart@nginx.com break; 19316Svbart@nginx.com } 19416Svbart@nginx.com 19516Svbart@nginx.com if (ch == '_' || ch == '-') { 196*409Svbart@nginx.com p++; 19716Svbart@nginx.com continue; 19816Svbart@nginx.com } 19916Svbart@nginx.com 20016Svbart@nginx.com if (rp->method.start == p && (ch == NXT_CR || ch == NXT_LF)) { 20116Svbart@nginx.com rp->method.start++; 202*409Svbart@nginx.com p++; 20316Svbart@nginx.com continue; 20416Svbart@nginx.com } 20516Svbart@nginx.com 20616Svbart@nginx.com return NXT_ERROR; 20716Svbart@nginx.com } 20816Svbart@nginx.com 20916Svbart@nginx.com p++; 21016Svbart@nginx.com 21116Svbart@nginx.com if (nxt_slow_path(p == end)) { 21216Svbart@nginx.com return NXT_AGAIN; 21316Svbart@nginx.com } 21416Svbart@nginx.com 21516Svbart@nginx.com /* target */ 21616Svbart@nginx.com 21716Svbart@nginx.com ch = *p; 21816Svbart@nginx.com 21916Svbart@nginx.com if (nxt_slow_path(ch != '/')) { 22016Svbart@nginx.com rc = nxt_http_parse_unusual_target(rp, &p, end); 22116Svbart@nginx.com 22216Svbart@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 22316Svbart@nginx.com return rc; 22416Svbart@nginx.com } 22516Svbart@nginx.com } 22616Svbart@nginx.com 22716Svbart@nginx.com rp->target_start = p; 22816Svbart@nginx.com 22916Svbart@nginx.com after_slash = p + 1; 23016Svbart@nginx.com 23116Svbart@nginx.com for ( ;; ) { 23216Svbart@nginx.com p++; 23316Svbart@nginx.com 23416Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 23516Svbart@nginx.com 23616Svbart@nginx.com switch (trap) { 23716Svbart@nginx.com case NXT_HTTP_TARGET_SLASH: 23816Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 23916Svbart@nginx.com rp->complex_target = 1; 24016Svbart@nginx.com goto rest_of_target; 24116Svbart@nginx.com } 24216Svbart@nginx.com 24316Svbart@nginx.com after_slash = p + 1; 24416Svbart@nginx.com 24516Svbart@nginx.com rp->exten_start = NULL; 24616Svbart@nginx.com continue; 24716Svbart@nginx.com 24816Svbart@nginx.com case NXT_HTTP_TARGET_DOT: 24916Svbart@nginx.com if (nxt_slow_path(after_slash == p)) { 25016Svbart@nginx.com rp->complex_target = 1; 25116Svbart@nginx.com goto rest_of_target; 25216Svbart@nginx.com } 25316Svbart@nginx.com 25416Svbart@nginx.com rp->exten_start = p + 1; 25516Svbart@nginx.com continue; 25616Svbart@nginx.com 25716Svbart@nginx.com case NXT_HTTP_TARGET_ARGS_MARK: 25816Svbart@nginx.com rp->args_start = p + 1; 25916Svbart@nginx.com goto rest_of_target; 26016Svbart@nginx.com 26116Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 26216Svbart@nginx.com rp->target_end = p; 26316Svbart@nginx.com goto space_after_target; 26416Svbart@nginx.com 26516Svbart@nginx.com case NXT_HTTP_TARGET_QUOTE_MARK: 26616Svbart@nginx.com rp->quoted_target = 1; 26716Svbart@nginx.com goto rest_of_target; 26816Svbart@nginx.com 26916Svbart@nginx.com case NXT_HTTP_TARGET_PLUS: 27016Svbart@nginx.com rp->plus_in_target = 1; 27116Svbart@nginx.com continue; 27216Svbart@nginx.com 27316Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 27416Svbart@nginx.com rp->complex_target = 1; 27516Svbart@nginx.com goto rest_of_target; 27616Svbart@nginx.com 27716Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 27816Svbart@nginx.com return NXT_AGAIN; 27916Svbart@nginx.com 28016Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 28116Svbart@nginx.com return NXT_ERROR; 28216Svbart@nginx.com } 28316Svbart@nginx.com 28416Svbart@nginx.com nxt_unreachable(); 28516Svbart@nginx.com } 28616Svbart@nginx.com 28716Svbart@nginx.com rest_of_target: 28816Svbart@nginx.com 28916Svbart@nginx.com for ( ;; ) { 29016Svbart@nginx.com p++; 29116Svbart@nginx.com 29219Svbart@nginx.com trap = nxt_http_parse_target(&p, end); 29316Svbart@nginx.com 29416Svbart@nginx.com switch (trap) { 29516Svbart@nginx.com case NXT_HTTP_TARGET_SPACE: 29616Svbart@nginx.com rp->target_end = p; 29716Svbart@nginx.com goto space_after_target; 29816Svbart@nginx.com 29916Svbart@nginx.com case NXT_HTTP_TARGET_HASH: 30016Svbart@nginx.com rp->complex_target = 1; 30116Svbart@nginx.com continue; 30216Svbart@nginx.com 30316Svbart@nginx.com case NXT_HTTP_TARGET_AGAIN: 30416Svbart@nginx.com return NXT_AGAIN; 30516Svbart@nginx.com 30616Svbart@nginx.com case NXT_HTTP_TARGET_BAD: 30716Svbart@nginx.com return NXT_ERROR; 30816Svbart@nginx.com 30916Svbart@nginx.com default: 31016Svbart@nginx.com continue; 31116Svbart@nginx.com } 31216Svbart@nginx.com 31316Svbart@nginx.com nxt_unreachable(); 31416Svbart@nginx.com } 31516Svbart@nginx.com 31616Svbart@nginx.com space_after_target: 31716Svbart@nginx.com 31816Svbart@nginx.com if (nxt_slow_path(end - p < 10)) { 31916Svbart@nginx.com return NXT_AGAIN; 32016Svbart@nginx.com } 32116Svbart@nginx.com 32216Svbart@nginx.com /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */ 32316Svbart@nginx.com 32416Svbart@nginx.com nxt_memcpy(version.str, &p[1], 8); 32516Svbart@nginx.com 32616Svbart@nginx.com if (nxt_fast_path((version.ui64 == http11.ui64 32716Svbart@nginx.com || version.ui64 == http10.ui64 32816Svbart@nginx.com || (p[1] == 'H' 32916Svbart@nginx.com && p[2] == 'T' 33016Svbart@nginx.com && p[3] == 'T' 33116Svbart@nginx.com && p[4] == 'P' 33216Svbart@nginx.com && p[5] == '/' 33316Svbart@nginx.com && p[6] >= '0' && p[6] <= '9' 33416Svbart@nginx.com && p[7] == '.' 33516Svbart@nginx.com && p[8] >= '0' && p[8] <= '9')) 33616Svbart@nginx.com && (p[9] == '\r' || p[9] == '\n'))) 33716Svbart@nginx.com { 33816Svbart@nginx.com rp->version.ui64 = version.ui64; 33916Svbart@nginx.com 34016Svbart@nginx.com if (nxt_fast_path(p[9] == '\r')) { 34116Svbart@nginx.com p += 10; 34216Svbart@nginx.com 34316Svbart@nginx.com if (nxt_slow_path(p == end)) { 34416Svbart@nginx.com return NXT_AGAIN; 34516Svbart@nginx.com } 34616Svbart@nginx.com 34716Svbart@nginx.com if (nxt_slow_path(*p != '\n')) { 34816Svbart@nginx.com return NXT_ERROR; 34916Svbart@nginx.com } 35016Svbart@nginx.com 35116Svbart@nginx.com *pos = p + 1; 352112Smax.romanov@nginx.com 353112Smax.romanov@nginx.com } else { 354112Smax.romanov@nginx.com *pos = p + 10; 355112Smax.romanov@nginx.com } 356112Smax.romanov@nginx.com 357112Smax.romanov@nginx.com if (rp->complex_target != 0 || rp->quoted_target != 0) { 358112Smax.romanov@nginx.com rc = nxt_http_parse_complex_target(rp); 359112Smax.romanov@nginx.com 360112Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_OK)) { 361112Smax.romanov@nginx.com return rc; 362112Smax.romanov@nginx.com } 363112Smax.romanov@nginx.com 36416Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 36516Svbart@nginx.com } 36616Svbart@nginx.com 367112Smax.romanov@nginx.com rp->path.start = rp->target_start; 368112Smax.romanov@nginx.com 369112Smax.romanov@nginx.com if (rp->args_start != NULL) { 370112Smax.romanov@nginx.com rp->path.length = rp->args_start - rp->target_start - 1; 371112Smax.romanov@nginx.com 372112Smax.romanov@nginx.com rp->args.start = rp->args_start; 373112Smax.romanov@nginx.com rp->args.length = rp->target_end - rp->args_start; 374112Smax.romanov@nginx.com 375112Smax.romanov@nginx.com } else { 376112Smax.romanov@nginx.com rp->path.length = rp->target_end - rp->target_start; 377112Smax.romanov@nginx.com } 378112Smax.romanov@nginx.com 379112Smax.romanov@nginx.com if (rp->exten_start) { 380112Smax.romanov@nginx.com rp->exten.length = rp->path.start + rp->path.length - 381112Smax.romanov@nginx.com rp->exten_start; 382112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 383112Smax.romanov@nginx.com } 384112Smax.romanov@nginx.com 38516Svbart@nginx.com return nxt_http_parse_field_name(rp, pos, end); 38616Svbart@nginx.com } 38716Svbart@nginx.com 38816Svbart@nginx.com if (p[1] == ' ') { 38916Svbart@nginx.com /* surplus space after tartet */ 39016Svbart@nginx.com p++; 39116Svbart@nginx.com goto space_after_target; 39216Svbart@nginx.com } 39316Svbart@nginx.com 39416Svbart@nginx.com rp->space_in_target = 1; 39516Svbart@nginx.com goto rest_of_target; 39616Svbart@nginx.com } 39716Svbart@nginx.com 39816Svbart@nginx.com 39916Svbart@nginx.com static nxt_int_t 40016Svbart@nginx.com nxt_http_parse_unusual_target(nxt_http_request_parse_t *rp, u_char **pos, 40116Svbart@nginx.com u_char *end) 40216Svbart@nginx.com { 40316Svbart@nginx.com u_char *p, ch; 40416Svbart@nginx.com 40516Svbart@nginx.com p = *pos; 40616Svbart@nginx.com 40716Svbart@nginx.com ch = *p; 40816Svbart@nginx.com 40916Svbart@nginx.com if (ch == ' ') { 41016Svbart@nginx.com /* skip surplus spaces before target */ 41116Svbart@nginx.com 41216Svbart@nginx.com do { 41316Svbart@nginx.com p++; 41416Svbart@nginx.com 41516Svbart@nginx.com if (nxt_slow_path(p == end)) { 41616Svbart@nginx.com return NXT_AGAIN; 41716Svbart@nginx.com } 41816Svbart@nginx.com 41916Svbart@nginx.com ch = *p; 42016Svbart@nginx.com 42116Svbart@nginx.com } while (ch == ' '); 42216Svbart@nginx.com 42316Svbart@nginx.com if (ch == '/') { 42416Svbart@nginx.com *pos = p; 42516Svbart@nginx.com return NXT_OK; 42616Svbart@nginx.com } 42716Svbart@nginx.com } 42816Svbart@nginx.com 42916Svbart@nginx.com /* absolute path or '*' */ 43016Svbart@nginx.com 43116Svbart@nginx.com /* TODO */ 43216Svbart@nginx.com 43316Svbart@nginx.com return NXT_ERROR; 43416Svbart@nginx.com } 43516Svbart@nginx.com 43616Svbart@nginx.com 43716Svbart@nginx.com static nxt_int_t 43816Svbart@nginx.com nxt_http_parse_field_name(nxt_http_request_parse_t *rp, u_char **pos, 43916Svbart@nginx.com u_char *end) 44016Svbart@nginx.com { 44116Svbart@nginx.com u_char *p, ch, c; 44216Svbart@nginx.com size_t i, size; 44316Svbart@nginx.com 44416Svbart@nginx.com static const u_char normal[256] nxt_aligned(64) = 44516Svbart@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" 44616Svbart@nginx.com "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" 44716Svbart@nginx.com 44816Svbart@nginx.com /* These 64 bytes should reside in one cache line. */ 44916Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" 45016Svbart@nginx.com "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" 45116Svbart@nginx.com 45216Svbart@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" 45316Svbart@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" 45416Svbart@nginx.com "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 45516Svbart@nginx.com "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 45616Svbart@nginx.com 45716Svbart@nginx.com p = *pos; 45816Svbart@nginx.com 45916Svbart@nginx.com size = end - p; 46067Svbart@nginx.com i = rp->field_name.length; 46116Svbart@nginx.com 462*409Svbart@nginx.com while (nxt_fast_path(size - i >= 8)) { 463*409Svbart@nginx.com 464*409Svbart@nginx.com #define nxt_field_name_test_char(i) \ 465*409Svbart@nginx.com \ 46619Svbart@nginx.com ch = p[i]; \ 46719Svbart@nginx.com c = normal[ch]; \ 46819Svbart@nginx.com \ 46919Svbart@nginx.com if (nxt_slow_path(c == '\0')) { \ 47019Svbart@nginx.com goto name_end; \ 47119Svbart@nginx.com } \ 47219Svbart@nginx.com \ 473*409Svbart@nginx.com rp->field_key.str[i % 32] = c; 474*409Svbart@nginx.com 475*409Svbart@nginx.com /* enddef */ 47616Svbart@nginx.com 477*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 478*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 479*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 480*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 48116Svbart@nginx.com 482*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 483*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 484*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 485*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 48619Svbart@nginx.com } 48716Svbart@nginx.com 48819Svbart@nginx.com while (nxt_fast_path(i != size)) { 489*409Svbart@nginx.com nxt_field_name_test_char(i); i++; 49019Svbart@nginx.com } 49116Svbart@nginx.com 49267Svbart@nginx.com rp->field_name.length = i; 49316Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 49416Svbart@nginx.com 49516Svbart@nginx.com return NXT_AGAIN; 49619Svbart@nginx.com 49719Svbart@nginx.com name_end: 49819Svbart@nginx.com 49919Svbart@nginx.com if (nxt_fast_path(ch == ':')) { 50019Svbart@nginx.com if (nxt_slow_path(i == 0)) { 50119Svbart@nginx.com return NXT_ERROR; 50219Svbart@nginx.com } 50319Svbart@nginx.com 50419Svbart@nginx.com *pos = &p[i] + 1; 50519Svbart@nginx.com 50667Svbart@nginx.com rp->field_name.length = i; 50767Svbart@nginx.com rp->field_name.start = p; 50819Svbart@nginx.com 50919Svbart@nginx.com return nxt_http_parse_field_value(rp, pos, end); 51019Svbart@nginx.com } 51119Svbart@nginx.com 51259Svbart@nginx.com if (nxt_slow_path(i != 0)) { 51359Svbart@nginx.com return NXT_ERROR; 51459Svbart@nginx.com } 51519Svbart@nginx.com 51619Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 51716Svbart@nginx.com } 51816Svbart@nginx.com 51916Svbart@nginx.com 52016Svbart@nginx.com static nxt_int_t 52116Svbart@nginx.com nxt_http_parse_field_value(nxt_http_request_parse_t *rp, u_char **pos, 52216Svbart@nginx.com u_char *end) 52316Svbart@nginx.com { 52416Svbart@nginx.com u_char *p, ch; 52516Svbart@nginx.com 52616Svbart@nginx.com p = *pos; 52716Svbart@nginx.com 52816Svbart@nginx.com for ( ;; ) { 52916Svbart@nginx.com if (nxt_slow_path(p == end)) { 53016Svbart@nginx.com *pos = p; 53116Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 53216Svbart@nginx.com return NXT_AGAIN; 53316Svbart@nginx.com } 53416Svbart@nginx.com 53516Svbart@nginx.com if (*p != ' ') { 53616Svbart@nginx.com break; 53716Svbart@nginx.com } 53816Svbart@nginx.com 53916Svbart@nginx.com p++; 54016Svbart@nginx.com } 54116Svbart@nginx.com 54216Svbart@nginx.com *pos = p; 54316Svbart@nginx.com 54467Svbart@nginx.com p += rp->field_value.length; 54516Svbart@nginx.com 54616Svbart@nginx.com for ( ;; ) { 54716Svbart@nginx.com p = nxt_http_lookup_field_end(p, end); 54816Svbart@nginx.com 54916Svbart@nginx.com if (nxt_slow_path(p == end)) { 55067Svbart@nginx.com rp->field_value.length = p - *pos; 55116Svbart@nginx.com rp->handler = &nxt_http_parse_field_value; 55216Svbart@nginx.com return NXT_AGAIN; 55316Svbart@nginx.com } 55416Svbart@nginx.com 55516Svbart@nginx.com ch = *p; 55616Svbart@nginx.com 55716Svbart@nginx.com if (nxt_fast_path(ch == '\r' || ch == '\n')) { 55816Svbart@nginx.com break; 55916Svbart@nginx.com } 56016Svbart@nginx.com 56116Svbart@nginx.com if (ch == '\0') { 56216Svbart@nginx.com return NXT_ERROR; 56316Svbart@nginx.com } 56416Svbart@nginx.com } 56516Svbart@nginx.com 56616Svbart@nginx.com if (nxt_fast_path(p != *pos)) { 56716Svbart@nginx.com while (p[-1] == ' ') { 56816Svbart@nginx.com p--; 56916Svbart@nginx.com } 57016Svbart@nginx.com } 57116Svbart@nginx.com 57267Svbart@nginx.com rp->field_value.length = p - *pos; 57367Svbart@nginx.com rp->field_value.start = *pos; 57416Svbart@nginx.com 57516Svbart@nginx.com *pos = p; 57616Svbart@nginx.com 57716Svbart@nginx.com return nxt_http_parse_field_end(rp, pos, end); 57816Svbart@nginx.com } 57916Svbart@nginx.com 58016Svbart@nginx.com 58116Svbart@nginx.com static u_char * 58216Svbart@nginx.com nxt_http_lookup_field_end(u_char *p, u_char *end) 58316Svbart@nginx.com { 584*409Svbart@nginx.com while (nxt_fast_path(end - p >= 16)) { 585*409Svbart@nginx.com 586*409Svbart@nginx.com #define nxt_field_end_test_char(ch) \ 587*409Svbart@nginx.com \ 588*409Svbart@nginx.com if (nxt_slow_path((ch) < 0x10)) { \ 589*409Svbart@nginx.com return &(ch); \ 590*409Svbart@nginx.com } 591*409Svbart@nginx.com 592*409Svbart@nginx.com /* enddef */ 593*409Svbart@nginx.com 594*409Svbart@nginx.com nxt_field_end_test_char(p[0]); 595*409Svbart@nginx.com nxt_field_end_test_char(p[1]); 596*409Svbart@nginx.com nxt_field_end_test_char(p[2]); 597*409Svbart@nginx.com nxt_field_end_test_char(p[3]); 59816Svbart@nginx.com 599*409Svbart@nginx.com nxt_field_end_test_char(p[4]); 600*409Svbart@nginx.com nxt_field_end_test_char(p[5]); 601*409Svbart@nginx.com nxt_field_end_test_char(p[6]); 602*409Svbart@nginx.com nxt_field_end_test_char(p[7]); 603*409Svbart@nginx.com 604*409Svbart@nginx.com nxt_field_end_test_char(p[8]); 605*409Svbart@nginx.com nxt_field_end_test_char(p[9]); 606*409Svbart@nginx.com nxt_field_end_test_char(p[10]); 607*409Svbart@nginx.com nxt_field_end_test_char(p[11]); 608*409Svbart@nginx.com 609*409Svbart@nginx.com nxt_field_end_test_char(p[12]); 610*409Svbart@nginx.com nxt_field_end_test_char(p[13]); 611*409Svbart@nginx.com nxt_field_end_test_char(p[14]); 612*409Svbart@nginx.com nxt_field_end_test_char(p[15]); 613*409Svbart@nginx.com 614*409Svbart@nginx.com p += 16; 61516Svbart@nginx.com } 61616Svbart@nginx.com 617*409Svbart@nginx.com while (nxt_fast_path(end - p >= 4)) { 61819Svbart@nginx.com 619*409Svbart@nginx.com nxt_field_end_test_char(p[0]); 620*409Svbart@nginx.com nxt_field_end_test_char(p[1]); 621*409Svbart@nginx.com nxt_field_end_test_char(p[2]); 622*409Svbart@nginx.com nxt_field_end_test_char(p[3]); 62316Svbart@nginx.com 624*409Svbart@nginx.com p += 4; 62519Svbart@nginx.com } 62619Svbart@nginx.com 62719Svbart@nginx.com switch (end - p) { 62816Svbart@nginx.com case 3: 629*409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 63039Svbart@nginx.com /* Fall through. */ 63116Svbart@nginx.com case 2: 632*409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 63339Svbart@nginx.com /* Fall through. */ 63416Svbart@nginx.com case 1: 635*409Svbart@nginx.com nxt_field_end_test_char(*p); p++; 63639Svbart@nginx.com /* Fall through. */ 63716Svbart@nginx.com case 0: 63816Svbart@nginx.com break; 63916Svbart@nginx.com default: 64016Svbart@nginx.com nxt_unreachable(); 64116Svbart@nginx.com } 64216Svbart@nginx.com 64316Svbart@nginx.com return p; 64416Svbart@nginx.com } 64516Svbart@nginx.com 64616Svbart@nginx.com 64716Svbart@nginx.com static nxt_int_t 64816Svbart@nginx.com nxt_http_parse_field_end(nxt_http_request_parse_t *rp, u_char **pos, 64916Svbart@nginx.com u_char *end) 65016Svbart@nginx.com { 65160Svbart@nginx.com u_char *p; 65260Svbart@nginx.com nxt_http_field_t *field; 65316Svbart@nginx.com 65416Svbart@nginx.com p = *pos; 65516Svbart@nginx.com 65616Svbart@nginx.com if (nxt_fast_path(*p == '\r')) { 65716Svbart@nginx.com p++; 65816Svbart@nginx.com 65916Svbart@nginx.com if (nxt_slow_path(p == end)) { 66016Svbart@nginx.com rp->handler = &nxt_http_parse_field_end; 66116Svbart@nginx.com return NXT_AGAIN; 66216Svbart@nginx.com } 66316Svbart@nginx.com } 66416Svbart@nginx.com 66516Svbart@nginx.com if (nxt_fast_path(*p == '\n')) { 66616Svbart@nginx.com *pos = p + 1; 66716Svbart@nginx.com 66867Svbart@nginx.com if (rp->field_name.length != 0) { 66960Svbart@nginx.com field = nxt_list_add(rp->fields); 67016Svbart@nginx.com 67160Svbart@nginx.com if (nxt_slow_path(field == NULL)) { 67260Svbart@nginx.com return NXT_ERROR; 67316Svbart@nginx.com } 67416Svbart@nginx.com 67567Svbart@nginx.com field->name = rp->field_name; 67667Svbart@nginx.com field->value = rp->field_value; 67760Svbart@nginx.com 67867Svbart@nginx.com nxt_http_fields_hash_lookup(rp->fields_hash, rp->field_key.ui64, 67967Svbart@nginx.com field); 68067Svbart@nginx.com 68167Svbart@nginx.com nxt_memzero(rp->field_key.str, 32); 68267Svbart@nginx.com 68367Svbart@nginx.com rp->field_name.length = 0; 68467Svbart@nginx.com rp->field_value.length = 0; 68516Svbart@nginx.com 68616Svbart@nginx.com rp->handler = &nxt_http_parse_field_name; 68716Svbart@nginx.com return NXT_OK; 68816Svbart@nginx.com } 68916Svbart@nginx.com 69016Svbart@nginx.com return NXT_DONE; 69116Svbart@nginx.com } 69216Svbart@nginx.com 69316Svbart@nginx.com return NXT_ERROR; 69416Svbart@nginx.com } 69516Svbart@nginx.com 69616Svbart@nginx.com 69760Svbart@nginx.com nxt_http_fields_hash_t * 69860Svbart@nginx.com nxt_http_fields_hash_create(nxt_http_fields_hash_entry_t *entries, 69965Sigor@sysoev.ru nxt_mp_t *mp) 70016Svbart@nginx.com { 70160Svbart@nginx.com size_t min_length, max_length, length, size; 70260Svbart@nginx.com nxt_uint_t i, j, n; 70360Svbart@nginx.com nxt_http_fields_hash_t *hash; 70460Svbart@nginx.com nxt_http_fields_hash_elt_t *elt; 70516Svbart@nginx.com 70638Svbart@nginx.com min_length = 32 + 1; 70716Svbart@nginx.com max_length = 0; 70816Svbart@nginx.com 70960Svbart@nginx.com for (i = 0; entries[i].handler != NULL; i++) { 71060Svbart@nginx.com length = entries[i].name.length; 71116Svbart@nginx.com 71216Svbart@nginx.com if (length > 32) { 71316Svbart@nginx.com /* TODO */ 71416Svbart@nginx.com return NULL; 71516Svbart@nginx.com } 71616Svbart@nginx.com 71716Svbart@nginx.com min_length = nxt_min(length, min_length); 71816Svbart@nginx.com max_length = nxt_max(length, max_length); 71916Svbart@nginx.com } 72016Svbart@nginx.com 72138Svbart@nginx.com size = sizeof(nxt_http_fields_hash_t); 72216Svbart@nginx.com 72338Svbart@nginx.com if (min_length <= 32) { 72438Svbart@nginx.com size += (max_length - min_length + 1) 72560Svbart@nginx.com * sizeof(nxt_http_fields_hash_elt_t *); 72638Svbart@nginx.com } 72716Svbart@nginx.com 72865Sigor@sysoev.ru hash = nxt_mp_zget(mp, size); 72916Svbart@nginx.com if (nxt_slow_path(hash == NULL)) { 73016Svbart@nginx.com return NULL; 73116Svbart@nginx.com } 73216Svbart@nginx.com 73316Svbart@nginx.com hash->min_length = min_length; 73416Svbart@nginx.com hash->max_length = max_length; 73516Svbart@nginx.com 73660Svbart@nginx.com for (i = 0; entries[i].handler != NULL; i++) { 73760Svbart@nginx.com length = entries[i].name.length; 73860Svbart@nginx.com elt = hash->elts[length - min_length]; 73916Svbart@nginx.com 74060Svbart@nginx.com if (elt != NULL) { 74116Svbart@nginx.com continue; 74216Svbart@nginx.com } 74316Svbart@nginx.com 74416Svbart@nginx.com n = 1; 74516Svbart@nginx.com 74660Svbart@nginx.com for (j = i + 1; entries[j].handler != NULL; j++) { 74760Svbart@nginx.com if (length == entries[j].name.length) { 74816Svbart@nginx.com n++; 74916Svbart@nginx.com } 75016Svbart@nginx.com } 75116Svbart@nginx.com 75260Svbart@nginx.com size = sizeof(nxt_http_fields_hash_elt_t) + nxt_align_size(length, 8); 75316Svbart@nginx.com 75465Sigor@sysoev.ru elt = nxt_mp_zget(mp, n * size + sizeof(nxt_http_fields_hash_elt_t)); 75516Svbart@nginx.com 75660Svbart@nginx.com if (nxt_slow_path(elt == NULL)) { 75716Svbart@nginx.com return NULL; 75816Svbart@nginx.com } 75916Svbart@nginx.com 76060Svbart@nginx.com hash->elts[length - min_length] = elt; 76116Svbart@nginx.com 76260Svbart@nginx.com for (j = i; entries[j].handler != NULL; j++) { 76360Svbart@nginx.com if (length != entries[j].name.length) { 76416Svbart@nginx.com continue; 76516Svbart@nginx.com } 76616Svbart@nginx.com 76767Svbart@nginx.com elt->handler = entries[j].handler; 76867Svbart@nginx.com elt->data = entries[j].data; 76916Svbart@nginx.com 77060Svbart@nginx.com nxt_memcpy_lowcase(elt->key->str, entries[j].name.start, length); 77116Svbart@nginx.com 77216Svbart@nginx.com n--; 77316Svbart@nginx.com 77416Svbart@nginx.com if (n == 0) { 77516Svbart@nginx.com break; 77616Svbart@nginx.com } 77716Svbart@nginx.com 77898Svbart@nginx.com elt = nxt_pointer_to(elt, size); 77916Svbart@nginx.com } 78016Svbart@nginx.com } 78116Svbart@nginx.com 78216Svbart@nginx.com return hash; 78316Svbart@nginx.com } 78460Svbart@nginx.com 78560Svbart@nginx.com 78667Svbart@nginx.com static void 78767Svbart@nginx.com nxt_http_fields_hash_lookup(nxt_http_fields_hash_t *hash, uint64_t key[4], 78860Svbart@nginx.com nxt_http_field_t *field) 78960Svbart@nginx.com { 79060Svbart@nginx.com nxt_http_fields_hash_elt_t *elt; 79160Svbart@nginx.com 79267Svbart@nginx.com if (hash == NULL || field->name.length < hash->min_length) { 79367Svbart@nginx.com goto not_found; 79460Svbart@nginx.com } 79560Svbart@nginx.com 79660Svbart@nginx.com if (field->name.length > hash->max_length) { 79760Svbart@nginx.com 79860Svbart@nginx.com if (field->name.length > 32 && hash->long_fields != NULL) { 79967Svbart@nginx.com nxt_http_fields_hash_lookup_long(hash, field); 80067Svbart@nginx.com return; 80160Svbart@nginx.com } 80260Svbart@nginx.com 80367Svbart@nginx.com goto not_found; 80460Svbart@nginx.com } 80560Svbart@nginx.com 80660Svbart@nginx.com elt = hash->elts[field->name.length - hash->min_length]; 80760Svbart@nginx.com 80860Svbart@nginx.com if (elt == NULL) { 80967Svbart@nginx.com goto not_found; 81060Svbart@nginx.com } 81160Svbart@nginx.com 81260Svbart@nginx.com switch ((field->name.length + 7) / 8) { 81360Svbart@nginx.com case 1: 81460Svbart@nginx.com do { 81567Svbart@nginx.com if (elt->key[0].ui64 == key[0]) { 81667Svbart@nginx.com break; 81760Svbart@nginx.com } 81860Svbart@nginx.com 81960Svbart@nginx.com elt = nxt_http_fields_hash_next_elt(elt, 1); 82060Svbart@nginx.com 82167Svbart@nginx.com } while (elt->handler != NULL); 82260Svbart@nginx.com 82360Svbart@nginx.com break; 82460Svbart@nginx.com 82560Svbart@nginx.com case 2: 82660Svbart@nginx.com do { 82767Svbart@nginx.com if (elt->key[0].ui64 == key[0] 82867Svbart@nginx.com && elt->key[1].ui64 == key[1]) 82960Svbart@nginx.com { 83067Svbart@nginx.com break; 83160Svbart@nginx.com } 83260Svbart@nginx.com 83360Svbart@nginx.com elt = nxt_http_fields_hash_next_elt(elt, 2); 83460Svbart@nginx.com 83567Svbart@nginx.com } while (elt->handler != NULL); 83660Svbart@nginx.com 83760Svbart@nginx.com break; 83860Svbart@nginx.com 83960Svbart@nginx.com case 3: 84060Svbart@nginx.com do { 84167Svbart@nginx.com if (elt->key[0].ui64 == key[0] 84267Svbart@nginx.com && elt->key[1].ui64 == key[1] 84367Svbart@nginx.com && elt->key[2].ui64 == key[2]) 84460Svbart@nginx.com { 84567Svbart@nginx.com break; 84660Svbart@nginx.com } 84760Svbart@nginx.com 84860Svbart@nginx.com elt = nxt_http_fields_hash_next_elt(elt, 3); 84960Svbart@nginx.com 85067Svbart@nginx.com } while (elt->handler != NULL); 85160Svbart@nginx.com 85260Svbart@nginx.com break; 85360Svbart@nginx.com 85460Svbart@nginx.com case 4: 85560Svbart@nginx.com do { 85667Svbart@nginx.com if (elt->key[0].ui64 == key[0] 85767Svbart@nginx.com && elt->key[1].ui64 == key[1] 85867Svbart@nginx.com && elt->key[2].ui64 == key[2] 85967Svbart@nginx.com && elt->key[3].ui64 == key[3]) 86060Svbart@nginx.com { 86167Svbart@nginx.com break; 86260Svbart@nginx.com } 86360Svbart@nginx.com 86460Svbart@nginx.com elt = nxt_http_fields_hash_next_elt(elt, 4); 86560Svbart@nginx.com 86667Svbart@nginx.com } while (elt->handler != NULL); 86760Svbart@nginx.com 86860Svbart@nginx.com break; 86960Svbart@nginx.com 87060Svbart@nginx.com default: 87160Svbart@nginx.com nxt_unreachable(); 87260Svbart@nginx.com } 87360Svbart@nginx.com 87467Svbart@nginx.com field->handler = elt->handler; 87567Svbart@nginx.com field->data = elt->data; 87667Svbart@nginx.com 87767Svbart@nginx.com return; 87867Svbart@nginx.com 87967Svbart@nginx.com not_found: 88067Svbart@nginx.com 88167Svbart@nginx.com field->handler = NULL; 88267Svbart@nginx.com field->data = 0; 88360Svbart@nginx.com } 88460Svbart@nginx.com 88560Svbart@nginx.com 88667Svbart@nginx.com static void 88760Svbart@nginx.com nxt_http_fields_hash_lookup_long(nxt_http_fields_hash_t *hash, 88860Svbart@nginx.com nxt_http_field_t *field) 88960Svbart@nginx.com { 89060Svbart@nginx.com /* TODO */ 89167Svbart@nginx.com 89267Svbart@nginx.com field->handler = NULL; 89367Svbart@nginx.com field->data = 0; 89460Svbart@nginx.com } 89560Svbart@nginx.com 89660Svbart@nginx.com 89760Svbart@nginx.com nxt_int_t 89867Svbart@nginx.com nxt_http_fields_process(nxt_list_t *fields, void *ctx, nxt_log_t *log) 89960Svbart@nginx.com { 90067Svbart@nginx.com nxt_int_t rc; 90167Svbart@nginx.com nxt_http_field_t *field; 90260Svbart@nginx.com 90360Svbart@nginx.com nxt_list_each(field, fields) { 90460Svbart@nginx.com 90567Svbart@nginx.com if (field->handler != NULL) { 90667Svbart@nginx.com rc = field->handler(ctx, field, log); 90760Svbart@nginx.com 90860Svbart@nginx.com if (rc != NXT_OK) { 90960Svbart@nginx.com return rc; 91060Svbart@nginx.com } 91160Svbart@nginx.com } 91260Svbart@nginx.com 91360Svbart@nginx.com } nxt_list_loop; 91460Svbart@nginx.com 91560Svbart@nginx.com return NXT_OK; 91660Svbart@nginx.com } 917112Smax.romanov@nginx.com 918112Smax.romanov@nginx.com 919112Smax.romanov@nginx.com #define \ 920112Smax.romanov@nginx.com nxt_http_is_normal(c) \ 921112Smax.romanov@nginx.com (nxt_fast_path((nxt_http_normal[c / 8] & (1 << (c & 7))) != 0)) 922112Smax.romanov@nginx.com 923112Smax.romanov@nginx.com 924112Smax.romanov@nginx.com static const uint8_t nxt_http_normal[32] nxt_aligned(32) = { 925112Smax.romanov@nginx.com 926112Smax.romanov@nginx.com /* \0 \r \n */ 927112Smax.romanov@nginx.com 0xfe, 0xdb, 0xff, 0xff, /* 1111 1110 1101 1011 1111 1111 1111 1111 */ 928112Smax.romanov@nginx.com 929112Smax.romanov@nginx.com /* '&%$ #"! /.-, |*)( 7654 3210 ?>=< ;:98 */ 930112Smax.romanov@nginx.com 0xd6, 0x37, 0xff, 0x7f, /* 1101 0110 0011 0111 1111 1111 0111 1111 */ 931112Smax.romanov@nginx.com 932112Smax.romanov@nginx.com /* GFED CBA@ ONML KJIH WVUT SRQP _^]\ [ZYX */ 933112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 934112Smax.romanov@nginx.com 935112Smax.romanov@nginx.com /* gfed cba` onml kjih wvut srqp ~}| {zyx */ 936112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 937112Smax.romanov@nginx.com 938112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 939112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 940112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 941112Smax.romanov@nginx.com 0xff, 0xff, 0xff, 0xff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 942112Smax.romanov@nginx.com }; 943112Smax.romanov@nginx.com 944112Smax.romanov@nginx.com 945112Smax.romanov@nginx.com static nxt_int_t 946112Smax.romanov@nginx.com nxt_http_parse_complex_target(nxt_http_request_parse_t *rp) 947112Smax.romanov@nginx.com { 948112Smax.romanov@nginx.com u_char *p, *u, c, ch, high; 949112Smax.romanov@nginx.com enum { 950112Smax.romanov@nginx.com sw_normal = 0, 951112Smax.romanov@nginx.com sw_slash, 952112Smax.romanov@nginx.com sw_dot, 953112Smax.romanov@nginx.com sw_dot_dot, 954112Smax.romanov@nginx.com sw_quoted, 955112Smax.romanov@nginx.com sw_quoted_second, 956112Smax.romanov@nginx.com } state, saved_state; 957112Smax.romanov@nginx.com 958112Smax.romanov@nginx.com nxt_prefetch(nxt_http_normal); 959112Smax.romanov@nginx.com 960112Smax.romanov@nginx.com state = sw_normal; 961112Smax.romanov@nginx.com saved_state = sw_normal; 962112Smax.romanov@nginx.com p = rp->target_start; 963112Smax.romanov@nginx.com 964112Smax.romanov@nginx.com u = nxt_mp_alloc(rp->mem_pool, rp->target_end - p + 1); 965112Smax.romanov@nginx.com 966112Smax.romanov@nginx.com if (nxt_slow_path(u == NULL)) { 967112Smax.romanov@nginx.com return NXT_ERROR; 968112Smax.romanov@nginx.com } 969112Smax.romanov@nginx.com 970112Smax.romanov@nginx.com rp->path.length = 0; 971112Smax.romanov@nginx.com rp->path.start = u; 972112Smax.romanov@nginx.com 973112Smax.romanov@nginx.com high = '\0'; 974112Smax.romanov@nginx.com rp->exten_start = NULL; 975112Smax.romanov@nginx.com rp->args_start = NULL; 976112Smax.romanov@nginx.com 977112Smax.romanov@nginx.com while (p < rp->target_end) { 978112Smax.romanov@nginx.com 979112Smax.romanov@nginx.com ch = *p++; 980112Smax.romanov@nginx.com 981112Smax.romanov@nginx.com again: 982112Smax.romanov@nginx.com 983112Smax.romanov@nginx.com switch (state) { 984112Smax.romanov@nginx.com 985112Smax.romanov@nginx.com case sw_normal: 986112Smax.romanov@nginx.com 987112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 988112Smax.romanov@nginx.com *u++ = ch; 989112Smax.romanov@nginx.com continue; 990112Smax.romanov@nginx.com } 991112Smax.romanov@nginx.com 992112Smax.romanov@nginx.com switch (ch) { 993112Smax.romanov@nginx.com case '/': 994112Smax.romanov@nginx.com rp->exten_start = NULL; 995112Smax.romanov@nginx.com state = sw_slash; 996112Smax.romanov@nginx.com *u++ = ch; 997112Smax.romanov@nginx.com continue; 998112Smax.romanov@nginx.com case '%': 999112Smax.romanov@nginx.com saved_state = state; 1000112Smax.romanov@nginx.com state = sw_quoted; 1001112Smax.romanov@nginx.com continue; 1002112Smax.romanov@nginx.com case '?': 1003112Smax.romanov@nginx.com rp->args_start = p; 1004112Smax.romanov@nginx.com goto args; 1005112Smax.romanov@nginx.com case '#': 1006112Smax.romanov@nginx.com goto done; 1007112Smax.romanov@nginx.com case '.': 1008112Smax.romanov@nginx.com rp->exten_start = u + 1; 1009112Smax.romanov@nginx.com *u++ = ch; 1010112Smax.romanov@nginx.com continue; 1011112Smax.romanov@nginx.com case '+': 1012112Smax.romanov@nginx.com rp->plus_in_target = 1; 1013112Smax.romanov@nginx.com /* Fall through. */ 1014112Smax.romanov@nginx.com default: 1015112Smax.romanov@nginx.com *u++ = ch; 1016112Smax.romanov@nginx.com continue; 1017112Smax.romanov@nginx.com } 1018112Smax.romanov@nginx.com 1019112Smax.romanov@nginx.com break; 1020112Smax.romanov@nginx.com 1021112Smax.romanov@nginx.com case sw_slash: 1022112Smax.romanov@nginx.com 1023112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 1024112Smax.romanov@nginx.com state = sw_normal; 1025112Smax.romanov@nginx.com *u++ = ch; 1026112Smax.romanov@nginx.com continue; 1027112Smax.romanov@nginx.com } 1028112Smax.romanov@nginx.com 1029112Smax.romanov@nginx.com switch (ch) { 1030112Smax.romanov@nginx.com case '/': 1031112Smax.romanov@nginx.com continue; 1032112Smax.romanov@nginx.com case '.': 1033112Smax.romanov@nginx.com state = sw_dot; 1034112Smax.romanov@nginx.com *u++ = ch; 1035112Smax.romanov@nginx.com continue; 1036112Smax.romanov@nginx.com case '%': 1037112Smax.romanov@nginx.com saved_state = state; 1038112Smax.romanov@nginx.com state = sw_quoted; 1039112Smax.romanov@nginx.com continue; 1040112Smax.romanov@nginx.com case '?': 1041112Smax.romanov@nginx.com rp->args_start = p; 1042112Smax.romanov@nginx.com goto args; 1043112Smax.romanov@nginx.com case '#': 1044112Smax.romanov@nginx.com goto done; 1045112Smax.romanov@nginx.com case '+': 1046112Smax.romanov@nginx.com rp->plus_in_target = 1; 1047112Smax.romanov@nginx.com /* Fall through. */ 1048112Smax.romanov@nginx.com default: 1049112Smax.romanov@nginx.com state = sw_normal; 1050112Smax.romanov@nginx.com *u++ = ch; 1051112Smax.romanov@nginx.com continue; 1052112Smax.romanov@nginx.com } 1053112Smax.romanov@nginx.com 1054112Smax.romanov@nginx.com break; 1055112Smax.romanov@nginx.com 1056112Smax.romanov@nginx.com case sw_dot: 1057112Smax.romanov@nginx.com 1058112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 1059112Smax.romanov@nginx.com state = sw_normal; 1060112Smax.romanov@nginx.com *u++ = ch; 1061112Smax.romanov@nginx.com continue; 1062112Smax.romanov@nginx.com } 1063112Smax.romanov@nginx.com 1064112Smax.romanov@nginx.com switch (ch) { 1065112Smax.romanov@nginx.com case '/': 1066112Smax.romanov@nginx.com state = sw_slash; 1067112Smax.romanov@nginx.com u--; 1068112Smax.romanov@nginx.com continue; 1069112Smax.romanov@nginx.com case '.': 1070112Smax.romanov@nginx.com state = sw_dot_dot; 1071112Smax.romanov@nginx.com *u++ = ch; 1072112Smax.romanov@nginx.com continue; 1073112Smax.romanov@nginx.com case '%': 1074112Smax.romanov@nginx.com saved_state = state; 1075112Smax.romanov@nginx.com state = sw_quoted; 1076112Smax.romanov@nginx.com continue; 1077112Smax.romanov@nginx.com case '?': 1078112Smax.romanov@nginx.com rp->args_start = p; 1079112Smax.romanov@nginx.com goto args; 1080112Smax.romanov@nginx.com case '#': 1081112Smax.romanov@nginx.com goto done; 1082112Smax.romanov@nginx.com case '+': 1083112Smax.romanov@nginx.com rp->plus_in_target = 1; 1084112Smax.romanov@nginx.com /* Fall through. */ 1085112Smax.romanov@nginx.com default: 1086112Smax.romanov@nginx.com state = sw_normal; 1087112Smax.romanov@nginx.com *u++ = ch; 1088112Smax.romanov@nginx.com continue; 1089112Smax.romanov@nginx.com } 1090112Smax.romanov@nginx.com 1091112Smax.romanov@nginx.com break; 1092112Smax.romanov@nginx.com 1093112Smax.romanov@nginx.com case sw_dot_dot: 1094112Smax.romanov@nginx.com 1095112Smax.romanov@nginx.com if (nxt_http_is_normal(ch)) { 1096112Smax.romanov@nginx.com state = sw_normal; 1097112Smax.romanov@nginx.com *u++ = ch; 1098112Smax.romanov@nginx.com continue; 1099112Smax.romanov@nginx.com } 1100112Smax.romanov@nginx.com 1101112Smax.romanov@nginx.com switch (ch) { 1102112Smax.romanov@nginx.com case '/': 1103112Smax.romanov@nginx.com state = sw_slash; 1104112Smax.romanov@nginx.com u -= 5; 1105112Smax.romanov@nginx.com for ( ;; ) { 1106112Smax.romanov@nginx.com if (u < rp->path.start) { 1107112Smax.romanov@nginx.com return NXT_ERROR; 1108112Smax.romanov@nginx.com } 1109112Smax.romanov@nginx.com if (*u == '/') { 1110112Smax.romanov@nginx.com u++; 1111112Smax.romanov@nginx.com break; 1112112Smax.romanov@nginx.com } 1113112Smax.romanov@nginx.com u--; 1114112Smax.romanov@nginx.com } 1115112Smax.romanov@nginx.com break; 1116112Smax.romanov@nginx.com 1117112Smax.romanov@nginx.com case '%': 1118112Smax.romanov@nginx.com saved_state = state; 1119112Smax.romanov@nginx.com state = sw_quoted; 1120112Smax.romanov@nginx.com continue; 1121112Smax.romanov@nginx.com case '?': 1122112Smax.romanov@nginx.com rp->args_start = p; 1123112Smax.romanov@nginx.com goto args; 1124112Smax.romanov@nginx.com case '#': 1125112Smax.romanov@nginx.com goto done; 1126112Smax.romanov@nginx.com case '+': 1127112Smax.romanov@nginx.com rp->plus_in_target = 1; 1128112Smax.romanov@nginx.com /* Fall through. */ 1129112Smax.romanov@nginx.com default: 1130112Smax.romanov@nginx.com state = sw_normal; 1131112Smax.romanov@nginx.com *u++ = ch; 1132112Smax.romanov@nginx.com continue; 1133112Smax.romanov@nginx.com } 1134112Smax.romanov@nginx.com 1135112Smax.romanov@nginx.com break; 1136112Smax.romanov@nginx.com 1137112Smax.romanov@nginx.com case sw_quoted: 1138112Smax.romanov@nginx.com rp->quoted_target = 1; 1139112Smax.romanov@nginx.com 1140112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 1141112Smax.romanov@nginx.com high = (u_char) (ch - '0'); 1142112Smax.romanov@nginx.com state = sw_quoted_second; 1143112Smax.romanov@nginx.com continue; 1144112Smax.romanov@nginx.com } 1145112Smax.romanov@nginx.com 1146112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1147112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1148112Smax.romanov@nginx.com high = (u_char) (c - 'a' + 10); 1149112Smax.romanov@nginx.com state = sw_quoted_second; 1150112Smax.romanov@nginx.com continue; 1151112Smax.romanov@nginx.com } 1152112Smax.romanov@nginx.com 1153112Smax.romanov@nginx.com return NXT_ERROR; 1154112Smax.romanov@nginx.com 1155112Smax.romanov@nginx.com case sw_quoted_second: 1156112Smax.romanov@nginx.com if (ch >= '0' && ch <= '9') { 1157112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + ch - '0'); 1158112Smax.romanov@nginx.com 1159112Smax.romanov@nginx.com if (ch == '%' || ch == '#') { 1160112Smax.romanov@nginx.com state = sw_normal; 1161112Smax.romanov@nginx.com *u++ = ch; 1162112Smax.romanov@nginx.com continue; 1163112Smax.romanov@nginx.com 1164112Smax.romanov@nginx.com } else if (ch == '\0') { 1165112Smax.romanov@nginx.com return NXT_ERROR; 1166112Smax.romanov@nginx.com } 1167112Smax.romanov@nginx.com 1168112Smax.romanov@nginx.com state = saved_state; 1169112Smax.romanov@nginx.com goto again; 1170112Smax.romanov@nginx.com } 1171112Smax.romanov@nginx.com 1172112Smax.romanov@nginx.com c = (u_char) (ch | 0x20); 1173112Smax.romanov@nginx.com if (c >= 'a' && c <= 'f') { 1174112Smax.romanov@nginx.com ch = (u_char) ((high << 4) + c - 'a' + 10); 1175112Smax.romanov@nginx.com 1176112Smax.romanov@nginx.com if (ch == '?') { 1177112Smax.romanov@nginx.com state = sw_normal; 1178112Smax.romanov@nginx.com *u++ = ch; 1179112Smax.romanov@nginx.com continue; 1180112Smax.romanov@nginx.com 1181112Smax.romanov@nginx.com } else if (ch == '+') { 1182112Smax.romanov@nginx.com rp->plus_in_target = 1; 1183112Smax.romanov@nginx.com } 1184112Smax.romanov@nginx.com 1185112Smax.romanov@nginx.com state = saved_state; 1186112Smax.romanov@nginx.com goto again; 1187112Smax.romanov@nginx.com } 1188112Smax.romanov@nginx.com 1189112Smax.romanov@nginx.com return NXT_ERROR; 1190112Smax.romanov@nginx.com } 1191112Smax.romanov@nginx.com } 1192112Smax.romanov@nginx.com 1193112Smax.romanov@nginx.com if (state >= sw_quoted) { 1194112Smax.romanov@nginx.com return NXT_ERROR; 1195112Smax.romanov@nginx.com } 1196112Smax.romanov@nginx.com 1197112Smax.romanov@nginx.com args: 1198112Smax.romanov@nginx.com 1199112Smax.romanov@nginx.com for (/* void */; p < rp->target_end; p++) { 1200112Smax.romanov@nginx.com if (*p == '#') { 1201112Smax.romanov@nginx.com break; 1202112Smax.romanov@nginx.com } 1203112Smax.romanov@nginx.com } 1204112Smax.romanov@nginx.com 1205112Smax.romanov@nginx.com if (rp->args_start != NULL) { 1206112Smax.romanov@nginx.com rp->args.length = p - rp->args_start; 1207112Smax.romanov@nginx.com rp->args.start = rp->args_start; 1208112Smax.romanov@nginx.com } 1209112Smax.romanov@nginx.com 1210112Smax.romanov@nginx.com done: 1211112Smax.romanov@nginx.com 1212112Smax.romanov@nginx.com rp->path.length = u - rp->path.start; 1213112Smax.romanov@nginx.com 1214112Smax.romanov@nginx.com if (rp->exten_start) { 1215112Smax.romanov@nginx.com rp->exten.length = u - rp->exten_start; 1216112Smax.romanov@nginx.com rp->exten.start = rp->exten_start; 1217112Smax.romanov@nginx.com } 1218112Smax.romanov@nginx.com 1219112Smax.romanov@nginx.com return NXT_OK; 1220112Smax.romanov@nginx.com } 1221