From ed4f50016ab0eab1ebbeac2fe1d0fd8712c7ee91 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 21 Apr 2009 11:14:39 -0700 Subject: Move absolute URI parsing into HTTP parser It's part of the HTTP/1.1 (rfc2616), so we might as well handle it in there and set PATH_INFO while we're at it. Also, make "OPTIONS *" test not fail Rack::Lint --- ext/unicorn/http11/http11.c | 13 + ext/unicorn/http11/http11_parser.c | 812 ++++++++++++++++------------- ext/unicorn/http11/http11_parser.rl | 7 + ext/unicorn/http11/http11_parser_common.rl | 9 +- lib/unicorn/http_request.rb | 8 - test/unit/test_http_parser.rb | 14 + test/unit/test_request.rb | 43 +- 7 files changed, 514 insertions(+), 392 deletions(-) diff --git a/ext/unicorn/http11/http11.c b/ext/unicorn/http11/http11.c index 021c80b..893f710 100644 --- a/ext/unicorn/http11/http11.c +++ b/ext/unicorn/http11/http11.c @@ -32,6 +32,7 @@ static VALUE global_query_string; static VALUE global_http_version; static VALUE global_content_length; static VALUE global_request_path; +static VALUE global_path_info; static VALUE global_content_type; static VALUE global_server_name; static VALUE global_server_port; @@ -202,6 +203,13 @@ static void request_uri(void *data, const char *at, size_t length) val = rb_str_new(at, length); rb_hash_aset(req, global_request_uri, val); + + /* "OPTIONS * HTTP/1.1\r\n" is a valid request */ + if (length == 1 && *at == '*') { + val = rb_str_new(NULL, 0); + rb_hash_aset(req, global_request_path, val); + rb_hash_aset(req, global_path_info, val); + } } static void fragment(void *data, const char *at, size_t length) @@ -224,6 +232,10 @@ static void request_path(void *data, const char *at, size_t length) val = rb_str_new(at, length); rb_hash_aset(req, global_request_path, val); + + /* rack says PATH_INFO must start with "/" or be empty */ + if (!(length == 1 && *at == '*')) + rb_hash_aset(req, global_path_info, val); } static void query_string(void *data, const char *at, size_t length) @@ -390,6 +402,7 @@ void Init_http11() DEF_GLOBAL(query_string, "QUERY_STRING"); DEF_GLOBAL(http_version, "HTTP_VERSION"); DEF_GLOBAL(request_path, "REQUEST_PATH"); + DEF_GLOBAL(path_info, "PATH_INFO"); DEF_GLOBAL(content_length, "CONTENT_LENGTH"); DEF_GLOBAL(content_type, "CONTENT_TYPE"); DEF_GLOBAL(server_name, "SERVER_NAME"); diff --git a/ext/unicorn/http11/http11_parser.c b/ext/unicorn/http11/http11_parser.c index 560fa28..376b2f1 100644 --- a/ext/unicorn/http11/http11_parser.c +++ b/ext/unicorn/http11/http11_parser.c @@ -21,6 +21,12 @@ static void snake_upcase_char(char *c) *c = '_'; } +static void downcase_char(char *c) +{ + if (*c >= 'A' && *c <= 'Z') + *c |= 0x20; +} + #define LEN(AT, FPC) (FPC - buffer - parser->AT) #define MARK(M,FPC) (parser->M = (FPC) - buffer) #define PTR_TO(F) (buffer + parser->F) @@ -28,30 +34,30 @@ static void snake_upcase_char(char *c) /** Machine **/ -#line 85 "http11_parser.rl" +#line 92 "http11_parser.rl" /** Data **/ -#line 37 "http11_parser.c" +#line 43 "http11_parser.c" static const int http_parser_start = 1; -static const int http_parser_first_final = 57; +static const int http_parser_first_final = 63; static const int http_parser_error = 0; static const int http_parser_en_main = 1; -#line 89 "http11_parser.rl" +#line 96 "http11_parser.rl" int http_parser_init(http_parser *parser) { int cs = 0; - -#line 50 "http11_parser.c" + +#line 56 "http11_parser.c" { cs = http_parser_start; } -#line 93 "http11_parser.rl" +#line 100 "http11_parser.rl" parser->cs = cs; parser->body_start = 0; parser->content_len = 0; @@ -78,8 +84,8 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len) assert(*pe == '\0' && "pointer does not end on NUL"); assert(pe - p == len - off && "pointers aren't same distance"); - -#line 83 "http11_parser.c" + +#line 89 "http11_parser.c" { if ( p == pe ) goto _test_eof; @@ -103,30 +109,30 @@ st0: cs = 0; goto _out; tr0: -#line 32 "http11_parser.rl" +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st2; st2: if ( ++p == pe ) goto _test_eof2; case 2: -#line 114 "http11_parser.c" +#line 120 "http11_parser.c" switch( (*p) ) { case 32: goto tr2; - case 36: goto st38; - case 95: goto st38; + case 36: goto st44; + case 95: goto st44; } if ( (*p) < 48 ) { if ( 45 <= (*p) && (*p) <= 46 ) - goto st38; + goto st44; } else if ( (*p) > 57 ) { if ( 65 <= (*p) && (*p) <= 90 ) - goto st38; + goto st44; } else - goto st38; + goto st44; goto st0; tr2: -#line 47 "http11_parser.rl" +#line 54 "http11_parser.rl" { if(parser->request_method != NULL) parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p)); @@ -136,92 +142,84 @@ st3: if ( ++p == pe ) goto _test_eof3; case 3: -#line 140 "http11_parser.c" +#line 146 "http11_parser.c" switch( (*p) ) { case 42: goto tr4; - case 43: goto tr5; - case 47: goto tr6; - case 58: goto tr7; + case 47: goto tr5; + case 72: goto tr6; + case 104: goto tr6; } - if ( (*p) < 65 ) { - if ( 45 <= (*p) && (*p) <= 57 ) - goto tr5; - } else if ( (*p) > 90 ) { - if ( 97 <= (*p) && (*p) <= 122 ) - goto tr5; - } else - goto tr5; goto st0; tr4: -#line 32 "http11_parser.rl" +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: -#line 164 "http11_parser.c" +#line 162 "http11_parser.c" switch( (*p) ) { - case 32: goto tr8; - case 35: goto tr9; + case 32: goto tr7; + case 35: goto tr8; } goto st0; -tr8: -#line 51 "http11_parser.rl" +tr7: +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st5; -tr31: -#line 32 "http11_parser.rl" +tr30: +#line 38 "http11_parser.rl" {MARK(mark, p); } -#line 55 "http11_parser.rl" +#line 62 "http11_parser.rl" { if(parser->fragment != NULL) parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st5; -tr34: -#line 55 "http11_parser.rl" +tr33: +#line 62 "http11_parser.rl" { if(parser->fragment != NULL) parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st5; -tr42: -#line 71 "http11_parser.rl" +tr37: +#line 78 "http11_parser.rl" { if(parser->request_path != NULL) parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st5; -tr53: -#line 60 "http11_parser.rl" +tr48: +#line 67 "http11_parser.rl" {MARK(query_start, p); } -#line 61 "http11_parser.rl" +#line 68 "http11_parser.rl" { if(parser->query_string != NULL) parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st5; -tr57: -#line 61 "http11_parser.rl" +tr52: +#line 68 "http11_parser.rl" { if(parser->query_string != NULL) parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); @@ -231,19 +229,19 @@ st5: if ( ++p == pe ) goto _test_eof5; case 5: -#line 235 "http11_parser.c" +#line 233 "http11_parser.c" if ( (*p) == 72 ) - goto tr10; + goto tr9; goto st0; -tr10: -#line 32 "http11_parser.rl" +tr9: +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st6; st6: if ( ++p == pe ) goto _test_eof6; case 6: -#line 247 "http11_parser.c" +#line 245 "http11_parser.c" if ( (*p) == 84 ) goto st7; goto st0; @@ -296,29 +294,29 @@ st13: goto _test_eof13; case 13: if ( (*p) == 13 ) - goto tr18; + goto tr17; if ( 48 <= (*p) && (*p) <= 57 ) goto st13; goto st0; -tr18: -#line 66 "http11_parser.rl" +tr17: +#line 73 "http11_parser.rl" { if(parser->http_version != NULL) parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st14; -tr26: -#line 41 "http11_parser.rl" +tr25: +#line 48 "http11_parser.rl" { MARK(mark, p); } -#line 42 "http11_parser.rl" +#line 49 "http11_parser.rl" { if(parser->http_field != NULL) { parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); } } goto st14; -tr29: -#line 42 "http11_parser.rl" +tr28: +#line 49 "http11_parser.rl" { if(parser->http_field != NULL) { parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p)); @@ -329,7 +327,7 @@ st14: if ( ++p == pe ) goto _test_eof14; case 14: -#line 333 "http11_parser.c" +#line 331 "http11_parser.c" if ( (*p) == 10 ) goto st15; goto st0; @@ -339,161 +337,161 @@ st15: case 15: switch( (*p) ) { case 13: goto st16; - case 33: goto tr21; - case 124: goto tr21; - case 126: goto tr21; + case 33: goto tr20; + case 124: goto tr20; + case 126: goto tr20; } if ( (*p) < 45 ) { if ( (*p) > 39 ) { if ( 42 <= (*p) && (*p) <= 43 ) - goto tr21; + goto tr20; } else if ( (*p) >= 35 ) - goto tr21; + goto tr20; } else if ( (*p) > 46 ) { if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto tr21; + goto tr20; } else if ( (*p) > 90 ) { if ( 94 <= (*p) && (*p) <= 122 ) - goto tr21; + goto tr20; } else - goto tr21; + goto tr20; } else - goto tr21; + goto tr20; goto st0; st16: if ( ++p == pe ) goto _test_eof16; case 16: if ( (*p) == 10 ) - goto tr22; + goto tr21; goto st0; -tr22: -#line 76 "http11_parser.rl" +tr21: +#line 83 "http11_parser.rl" { parser->body_start = p - buffer + 1; if(parser->header_done != NULL) parser->header_done(parser->data, p + 1, pe - p - 1); - {p++; cs = 57; goto _out;} + {p++; cs = 63; goto _out;} } - goto st57; -st57: + goto st63; +st63: if ( ++p == pe ) - goto _test_eof57; -case 57: -#line 385 "http11_parser.c" + goto _test_eof63; +case 63: +#line 383 "http11_parser.c" goto st0; -tr21: -#line 35 "http11_parser.rl" +tr20: +#line 41 "http11_parser.rl" { MARK(field_start, p); } -#line 36 "http11_parser.rl" +#line 42 "http11_parser.rl" { snake_upcase_char((char *)p); } goto st17; -tr23: -#line 36 "http11_parser.rl" +tr22: +#line 42 "http11_parser.rl" { snake_upcase_char((char *)p); } goto st17; st17: if ( ++p == pe ) goto _test_eof17; case 17: -#line 401 "http11_parser.c" +#line 399 "http11_parser.c" switch( (*p) ) { - case 33: goto tr23; - case 58: goto tr24; - case 124: goto tr23; - case 126: goto tr23; + case 33: goto tr22; + case 58: goto tr23; + case 124: goto tr22; + case 126: goto tr22; } if ( (*p) < 45 ) { if ( (*p) > 39 ) { if ( 42 <= (*p) && (*p) <= 43 ) - goto tr23; + goto tr22; } else if ( (*p) >= 35 ) - goto tr23; + goto tr22; } else if ( (*p) > 46 ) { if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto tr23; + goto tr22; } else if ( (*p) > 90 ) { if ( 94 <= (*p) && (*p) <= 122 ) - goto tr23; + goto tr22; } else - goto tr23; + goto tr22; } else - goto tr23; + goto tr22; goto st0; -tr24: -#line 37 "http11_parser.rl" +tr23: +#line 44 "http11_parser.rl" { parser->field_len = LEN(field_start, p); } goto st18; -tr27: -#line 41 "http11_parser.rl" +tr26: +#line 48 "http11_parser.rl" { MARK(mark, p); } goto st18; st18: if ( ++p == pe ) goto _test_eof18; case 18: -#line 440 "http11_parser.c" +#line 438 "http11_parser.c" switch( (*p) ) { - case 13: goto tr26; - case 32: goto tr27; + case 13: goto tr25; + case 32: goto tr26; } - goto tr25; -tr25: -#line 41 "http11_parser.rl" + goto tr24; +tr24: +#line 48 "http11_parser.rl" { MARK(mark, p); } goto st19; st19: if ( ++p == pe ) goto _test_eof19; case 19: -#line 454 "http11_parser.c" +#line 452 "http11_parser.c" if ( (*p) == 13 ) - goto tr29; + goto tr28; goto st19; -tr9: -#line 51 "http11_parser.rl" +tr8: +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st20; -tr43: -#line 71 "http11_parser.rl" +tr38: +#line 78 "http11_parser.rl" { if(parser->request_path != NULL) parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st20; -tr54: -#line 60 "http11_parser.rl" +tr49: +#line 67 "http11_parser.rl" {MARK(query_start, p); } -#line 61 "http11_parser.rl" +#line 68 "http11_parser.rl" { if(parser->query_string != NULL) parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); } goto st20; -tr58: -#line 61 "http11_parser.rl" +tr53: +#line 68 "http11_parser.rl" { if(parser->query_string != NULL) parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p)); } -#line 51 "http11_parser.rl" +#line 58 "http11_parser.rl" { if(parser->request_uri != NULL) parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p)); @@ -503,27 +501,27 @@ st20: if ( ++p == pe ) goto _test_eof20; case 20: -#line 507 "http11_parser.c" +#line 505 "http11_parser.c" switch( (*p) ) { - case 32: goto tr31; + case 32: goto tr30; case 35: goto st0; - case 37: goto tr32; + case 37: goto tr31; case 127: goto st0; } if ( 0 <= (*p) && (*p) <= 31 ) goto st0; - goto tr30; -tr30: -#line 32 "http11_parser.rl" + goto tr29; +tr29: +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st21; st21: if ( ++p == pe ) goto _test_eof21; case 21: -#line 525 "http11_parser.c" +#line 523 "http11_parser.c" switch( (*p) ) { - case 32: goto tr34; + case 32: goto tr33; case 35: goto st0; case 37: goto st22; case 127: goto st0; @@ -531,15 +529,15 @@ case 21: if ( 0 <= (*p) && (*p) <= 31 ) goto st0; goto st21; -tr32: -#line 32 "http11_parser.rl" +tr31: +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st22; st22: if ( ++p == pe ) goto _test_eof22; case 22: -#line 543 "http11_parser.c" +#line 541 "http11_parser.c" if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) goto st23; @@ -563,146 +561,147 @@ case 23: goto st21; goto st0; tr5: -#line 32 "http11_parser.rl" +#line 38 "http11_parser.rl" {MARK(mark, p); } goto st24; st24: if ( ++p == pe ) goto _test_eof24; case 24: -#line 574 "http11_parser.c" - switch( (*p) ) { - case 43: goto st24; - case 58: goto st25; - } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st24; - } else if ( (*p) > 57 ) { - if ( (*p) > 90 ) { - if ( 97 <= (*p) && (*p) <= 122 ) - goto st24; - } else if ( (*p) >= 65 ) - goto st24; - } else - goto st24; - goto st0; -tr7: -#line 32 "http11_parser.rl" - {MARK(mark, p); } - goto st25; -st25: - if ( ++p == pe ) - goto _test_eof25; -case 25: -#line 599 "http11_parser.c" +#line 572 "http11_parser.c" switch( (*p) ) { - case 32: goto tr8; - case 35: goto tr9; - case 37: goto st26; + case 32: goto tr37; + case 35: goto tr38; + case 37: goto st25; + case 59: goto tr40; + case 63: goto tr41; case 127: goto st0; } if ( 0 <= (*p) && (*p) <= 31 ) goto st0; - goto st25; -st26: + goto st24; +st25: if ( ++p == pe ) - goto _test_eof26; -case 26: + goto _test_eof25; +case 25: if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st27; + goto st26; } else if ( (*p) > 70 ) { if ( 97 <= (*p) && (*p) <= 102 ) - goto st27; + goto st26; } else - goto st27; + goto st26; goto st0; -st27: +st26: if ( ++p == pe ) - goto _test_eof27; -case 27: + goto _test_eof26; +case 26: if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st25; + goto st24; } else if ( (*p) > 70 ) { if ( 97 <= (*p) && (*p) <= 102 ) - goto st25; + goto st24; } else - goto st25; + goto st24; goto st0; -tr6: -#line 32 "http11_parser.rl" - {MARK(mark, p); } - goto st28; -st28: +tr40: +#line 78 "http11_parser.rl" + { + if(parser->request_path != NULL) + parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p)); + } + goto st27; +st27: if ( ++p == pe ) - goto _test_eof28; -case 28: -#line 643 "http11_parser.c" + goto _test_eof27; +case 27: +#line 621 "http11_parser.c" switch( (*p) ) { - case 32: goto tr42; - case 35: goto tr43; - case 37: goto st29; - case 59: goto tr45; - case 63: goto tr46; + case 32: goto tr7; + case 35: goto tr8; + case 37: goto st28; + case 63: goto st30; case 127: goto st0; } if ( 0 <= (*p) && (*p) <= 31 ) goto st0; - goto st28; -st29: + goto st27; +st28: if ( ++p == pe ) - goto _test_eof29; -case 29: + goto _test_eof28; +case 28: if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st30; + goto st29; } else if ( (*p) > 70 ) { if ( 97 <= (*p) && (*p) <= 102 ) - goto st30; + goto st29; } else - goto st30; + goto st29; goto st0; -st30: +st29: if ( ++p == pe ) - goto _test_eof30; -case 30: + goto _test_eof29; +case 29: if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) - goto st28; + goto st27; } else if ( (*p) > 70 ) { if ( 97 <= (*p) && (*p) <= 102 ) - goto st28; + goto st27; } else - goto st28; + goto st27; goto st0; -tr45: -#line 71 "http11_parser.rl" +tr41: +#line 78 "http11_parser.rl" { if(parser->request_path != NULL) parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p)); } + goto st30; +st30: + if ( ++p == pe ) + goto _test_eof30; +case 30: +#line 669 "http11_parser.c" + switch( (*p) ) { + case 32: goto tr48; + case 35: goto tr49; + case 37: goto tr50; + case 127: goto st0; + } + if ( 0 <= (*p) && (*p) <= 31 ) + goto st0; + goto tr47; +tr47: +#line 67 "http11_parser.rl" + {MARK(query_start, p); } goto st31; st31: if ( ++p == pe ) goto _test_eof31; case 31: -#line 692 "http11_parser.c" +#line 687 "http11_parser.c" switch( (*p) ) { - case 32: goto tr8; - case 35: goto tr9; + case 32: goto tr52; + case 35: goto tr53; case 37: goto st32; - case 63: goto st34; case 127: goto st0; } if ( 0 <= (*p) && (*p) <= 31 ) goto st0; goto st31; +tr50: +#line 67 "http11_parser.rl" + {MARK(query_start, p); } + goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: +#line 705 "http11_parser.c" if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) goto st33; @@ -725,126 +724,89 @@ case 33: } else goto st31; goto st0; -tr46: -#line 71 "http11_parser.rl" - { - if(parser->request_path != NULL) - parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p)); - } +tr6: +#line 43 "http11_parser.rl" + { downcase_char((char *)p); } goto st34; st34: if ( ++p == pe ) goto _test_eof34; case 34: -#line 740 "http11_parser.c" +#line 736 "http11_parser.c" switch( (*p) ) { - case 32: goto tr53; - case 35: goto tr54; - case 37: goto tr55; - case 127: goto st0; + case 84: goto tr56; + case 116: goto tr56; } - if ( 0 <= (*p) && (*p) <= 31 ) - goto st0; - goto tr52; -tr52: -#line 60 "http11_parser.rl" - {MARK(query_start, p); } + goto st0; +tr56: +#line 43 "http11_parser.rl" + { downcase_char((char *)p); } goto st35; st35: if ( ++p == pe ) goto _test_eof35; case 35: -#line 758 "http11_parser.c" +#line 750 "http11_parser.c" switch( (*p) ) { - case 32: goto tr57; - case 35: goto tr58; - case 37: goto st36; - case 127: goto st0; + case 84: goto tr57; + case 116: goto tr57; } - if ( 0 <= (*p) && (*p) <= 31 ) - goto st0; - goto st35; -tr55: -#line 60 "http11_parser.rl" - {MARK(query_start, p); } + goto st0; +tr57: +#line 43 "http11_parser.rl" + { downcase_char((char *)p); } goto st36; st36: if ( ++p == pe ) goto _test_eof36; case 36: -#line 776 "http11_parser.c" - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st37; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st37; - } else - goto st37; +#line 764 "http11_parser.c" + switch( (*p) ) { + case 80: goto tr58; + case 112: goto tr58; + } goto st0; +tr58: +#line 43 "http11_parser.rl" + { downcase_char((char *)p); } + goto st37; st37: if ( ++p == pe ) goto _test_eof37; case 37: - if ( (*p) < 65 ) { - if ( 48 <= (*p) && (*p) <= 57 ) - goto st35; - } else if ( (*p) > 70 ) { - if ( 97 <= (*p) && (*p) <= 102 ) - goto st35; - } else - goto st35; +#line 778 "http11_parser.c" + switch( (*p) ) { + case 58: goto st38; + case 83: goto tr60; + case 115: goto tr60; + } goto st0; st38: if ( ++p == pe ) goto _test_eof38; case 38: - switch( (*p) ) { - case 32: goto tr2; - case 36: goto st39; - case 95: goto st39; - } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st39; - } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) - goto st39; - } else + if ( (*p) == 47 ) goto st39; goto st0; st39: if ( ++p == pe ) goto _test_eof39; case 39: - switch( (*p) ) { - case 32: goto tr2; - case 36: goto st40; - case 95: goto st40; - } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st40; - } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) - goto st40; - } else + if ( (*p) == 47 ) goto st40; goto st0; st40: if ( ++p == pe ) goto _test_eof40; case 40: - switch( (*p) ) { - case 32: goto tr2; - case 36: goto st41; - case 95: goto st41; - } if ( (*p) < 48 ) { if ( 45 <= (*p) && (*p) <= 46 ) goto st41; } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto st41; + } else if ( (*p) >= 65 ) goto st41; } else goto st41; @@ -854,54 +816,38 @@ st41: goto _test_eof41; case 41: switch( (*p) ) { - case 32: goto tr2; - case 36: goto st42; - case 95: goto st42; + case 47: goto tr5; + case 58: goto st42; } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st42; - } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) - goto st42; + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto st41; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto st41; } else - goto st42; + goto st41; goto st0; st42: if ( ++p == pe ) goto _test_eof42; case 42: - switch( (*p) ) { - case 32: goto tr2; - case 36: goto st43; - case 95: goto st43; - } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st43; - } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) - goto st43; - } else - goto st43; + if ( (*p) == 47 ) + goto tr5; + if ( 48 <= (*p) && (*p) <= 57 ) + goto st42; goto st0; +tr60: +#line 43 "http11_parser.rl" + { downcase_char((char *)p); } + goto st43; st43: if ( ++p == pe ) goto _test_eof43; case 43: - switch( (*p) ) { - case 32: goto tr2; - case 36: goto st44; - case 95: goto st44; - } - if ( (*p) < 48 ) { - if ( 45 <= (*p) && (*p) <= 46 ) - goto st44; - } else if ( (*p) > 57 ) { - if ( 65 <= (*p) && (*p) <= 90 ) - goto st44; - } else - goto st44; +#line 849 "http11_parser.c" + if ( (*p) == 58 ) + goto st38; goto st0; st44: if ( ++p == pe ) @@ -1123,72 +1069,186 @@ st56: if ( ++p == pe ) goto _test_eof56; case 56: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st57; + case 95: goto st57; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st57; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st57; + } else + goto st57; + goto st0; +st57: + if ( ++p == pe ) + goto _test_eof57; +case 57: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st58; + case 95: goto st58; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st58; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st58; + } else + goto st58; + goto st0; +st58: + if ( ++p == pe ) + goto _test_eof58; +case 58: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st59; + case 95: goto st59; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st59; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st59; + } else + goto st59; + goto st0; +st59: + if ( ++p == pe ) + goto _test_eof59; +case 59: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st60; + case 95: goto st60; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st60; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st60; + } else + goto st60; + goto st0; +st60: + if ( ++p == pe ) + goto _test_eof60; +case 60: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st61; + case 95: goto st61; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st61; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st61; + } else + goto st61; + goto st0; +st61: + if ( ++p == pe ) + goto _test_eof61; +case 61: + switch( (*p) ) { + case 32: goto tr2; + case 36: goto st62; + case 95: goto st62; + } + if ( (*p) < 48 ) { + if ( 45 <= (*p) && (*p) <= 46 ) + goto st62; + } else if ( (*p) > 57 ) { + if ( 65 <= (*p) && (*p) <= 90 ) + goto st62; + } else + goto st62; + goto st0; +st62: + if ( ++p == pe ) + goto _test_eof62; +case 62: if ( (*p) == 32 ) goto tr2; goto st0; } - _test_eof2: cs = 2; goto _test_eof; - _test_eof3: cs = 3; goto _test_eof; - _test_eof4: cs = 4; goto _test_eof; - _test_eof5: cs = 5; goto _test_eof; - _test_eof6: cs = 6; goto _test_eof; - _test_eof7: cs = 7; goto _test_eof; - _test_eof8: cs = 8; goto _test_eof; - _test_eof9: cs = 9; goto _test_eof; - _test_eof10: cs = 10; goto _test_eof; - _test_eof11: cs = 11; goto _test_eof; - _test_eof12: cs = 12; goto _test_eof; - _test_eof13: cs = 13; goto _test_eof; - _test_eof14: cs = 14; goto _test_eof; - _test_eof15: cs = 15; goto _test_eof; - _test_eof16: cs = 16; goto _test_eof; - _test_eof57: cs = 57; goto _test_eof; - _test_eof17: cs = 17; goto _test_eof; - _test_eof18: cs = 18; goto _test_eof; - _test_eof19: cs = 19; goto _test_eof; - _test_eof20: cs = 20; goto _test_eof; - _test_eof21: cs = 21; goto _test_eof; - _test_eof22: cs = 22; goto _test_eof; - _test_eof23: cs = 23; goto _test_eof; - _test_eof24: cs = 24; goto _test_eof; - _test_eof25: cs = 25; goto _test_eof; - _test_eof26: cs = 26; goto _test_eof; - _test_eof27: cs = 27; goto _test_eof; - _test_eof28: cs = 28; goto _test_eof; - _test_eof29: cs = 29; goto _test_eof; - _test_eof30: cs = 30; goto _test_eof; - _test_eof31: cs = 31; goto _test_eof; - _test_eof32: cs = 32; goto _test_eof; - _test_eof33: cs = 33; goto _test_eof; - _test_eof34: cs = 34; goto _test_eof; - _test_eof35: cs = 35; goto _test_eof; - _test_eof36: cs = 36; goto _test_eof; - _test_eof37: cs = 37; goto _test_eof; - _test_eof38: cs = 38; goto _test_eof; - _test_eof39: cs = 39; goto _test_eof; - _test_eof40: cs = 40; goto _test_eof; - _test_eof41: cs = 41; goto _test_eof; - _test_eof42: cs = 42; goto _test_eof; - _test_eof43: cs = 43; goto _test_eof; - _test_eof44: cs = 44; goto _test_eof; - _test_eof45: cs = 45; goto _test_eof; - _test_eof46: cs = 46; goto _test_eof; - _test_eof47: cs = 47; goto _test_eof; - _test_eof48: cs = 48; goto _test_eof; - _test_eof49: cs = 49; goto _test_eof; - _test_eof50: cs = 50; goto _test_eof; - _test_eof51: cs = 51; goto _test_eof; - _test_eof52: cs = 52; goto _test_eof; - _test_eof53: cs = 53; goto _test_eof; - _test_eof54: cs = 54; goto _test_eof; - _test_eof55: cs = 55; goto _test_eof; - _test_eof56: cs = 56; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof63: cs = 63; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; + _test_eof30: cs = 30; goto _test_eof; + _test_eof31: cs = 31; goto _test_eof; + _test_eof32: cs = 32; goto _test_eof; + _test_eof33: cs = 33; goto _test_eof; + _test_eof34: cs = 34; goto _test_eof; + _test_eof35: cs = 35; goto _test_eof; + _test_eof36: cs = 36; goto _test_eof; + _test_eof37: cs = 37; goto _test_eof; + _test_eof38: cs = 38; goto _test_eof; + _test_eof39: cs = 39; goto _test_eof; + _test_eof40: cs = 40; goto _test_eof; + _test_eof41: cs = 41; goto _test_eof; + _test_eof42: cs = 42; goto _test_eof; + _test_eof43: cs = 43; goto _test_eof; + _test_eof44: cs = 44; goto _test_eof; + _test_eof45: cs = 45; goto _test_eof; + _test_eof46: cs = 46; goto _test_eof; + _test_eof47: cs = 47; goto _test_eof; + _test_eof48: cs = 48; goto _test_eof; + _test_eof49: cs = 49; goto _test_eof; + _test_eof50: cs = 50; goto _test_eof; + _test_eof51: cs = 51; goto _test_eof; + _test_eof52: cs = 52; goto _test_eof; + _test_eof53: cs = 53; goto _test_eof; + _test_eof54: cs = 54; goto _test_eof; + _test_eof55: cs = 55; goto _test_eof; + _test_eof56: cs = 56; goto _test_eof; + _test_eof57: cs = 57; goto _test_eof; + _test_eof58: cs = 58; goto _test_eof; + _test_eof59: cs = 59; goto _test_eof; + _test_eof60: cs = 60; goto _test_eof; + _test_eof61: cs = 61; goto _test_eof; + _test_eof62: cs = 62; goto _test_eof; _test_eof: {} _out: {} } -#line 120 "http11_parser.rl" +#line 127 "http11_parser.rl" if (!http_parser_has_error(parser)) parser->cs = cs; diff --git a/ext/unicorn/http11/http11_parser.rl b/ext/unicorn/http11/http11_parser.rl index a86e8cd..a9f7a39 100644 --- a/ext/unicorn/http11/http11_parser.rl +++ b/ext/unicorn/http11/http11_parser.rl @@ -19,6 +19,12 @@ static void snake_upcase_char(char *c) *c = '_'; } +static void downcase_char(char *c) +{ + if (*c >= 'A' && *c <= 'Z') + *c |= 0x20; +} + #define LEN(AT, FPC) (FPC - buffer - parser->AT) #define MARK(M,FPC) (parser->M = (FPC) - buffer) #define PTR_TO(F) (buffer + parser->F) @@ -34,6 +40,7 @@ static void snake_upcase_char(char *c) action start_field { MARK(field_start, fpc); } action snake_upcase_field { snake_upcase_char((char *)fpc); } + action downcase_char { downcase_char((char *)fpc); } action write_field { parser->field_len = LEN(field_start, fpc); } diff --git a/ext/unicorn/http11/http11_parser_common.rl b/ext/unicorn/http11/http11_parser_common.rl index ee970b1..20fef92 100644 --- a/ext/unicorn/http11/http11_parser_common.rl +++ b/ext/unicorn/http11/http11_parser_common.rl @@ -24,8 +24,9 @@ token = (ascii -- (CTL | tspecials)); # URI schemes and absolute paths - scheme = ( alpha | digit | "+" | "-" | "." )* ; - absolute_uri = (scheme ":" (uchar | reserved )*); + scheme = ( "http"i ("s"i)? ) $downcase_char; + hostname = (alnum | "-" | ".")+; + host_with_port = (hostname (":" digit*)?); path = ( pchar+ ( "/" pchar* )* ) ; query = ( uchar | reserved )* %query_string ; @@ -33,8 +34,10 @@ params = ( param ( ";" param )* ) ; rel_path = ( path? %request_path (";" params)? ) ("?" %start_query query)?; absolute_path = ( "/"+ rel_path ); + path_uri = absolute_path > mark %request_uri; + Absolute_URI = (scheme "://" host_with_port path_uri); - Request_URI = ( "*" | absolute_uri | absolute_path ) >mark %request_uri; + Request_URI = ((absolute_path | "*") >mark %request_uri) | Absolute_URI; Fragment = ( uchar | reserved )* >mark %fragment; Method = ( upper | digit | safe ){1,20} >mark %request_method; diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb index a3a1d4d..c9c9503 100644 --- a/lib/unicorn/http_request.rb +++ b/lib/unicorn/http_request.rb @@ -1,5 +1,4 @@ require 'tempfile' -require 'uri' require 'stringio' # compiled extension @@ -129,13 +128,6 @@ module Unicorn # that client may be a proxy, gateway, or other intermediary # acting on behalf of the actual source client." @params[Const::REMOTE_ADDR] = socket.unicorn_peeraddr - - # It might be a dumbass full host request header - @params[Const::PATH_INFO] = ( - @params[Const::REQUEST_PATH] ||= - URI.parse(@params[Const::REQUEST_URI]).path) or - raise "No REQUEST_PATH" - @params[Const::QUERY_STRING] ||= '' @params[Const::RACK_INPUT] = @body @params.update(DEF_PARAMS) diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb index f6fdd47..37a539d 100644 --- a/test/unit/test_http_parser.rb +++ b/test/unit/test_http_parser.rb @@ -103,6 +103,20 @@ class HttpParserTest < Test::Unit::TestCase assert_nil req['QUERY_STRING'] end + def test_absolute_uri + parser = HttpParser.new + req = {} + http = "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n" + assert parser.execute(req, http) + assert_equal 'http', req['rack.url_scheme'] + assert_equal '/foo?q=bar', req['REQUEST_URI'] + assert_equal '/foo', req['REQUEST_PATH'] + assert_equal 'q=bar', req['QUERY_STRING'] + + # assert_equal 'example.com', req['HTTP_HOST'] # TODO + # assert_equal 'example.com', req['SERVER_NAME'] # TODO + end + def test_put_body_oneshot parser = HttpParser.new req = {} diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb index 0613326..dbe4069 100644 --- a/test/unit/test_request.rb +++ b/test/unit/test_request.rb @@ -38,23 +38,56 @@ class RequestTest < Test::Unit::TestCase "Host: foo\r\n\r\n") res = env = nil assert_nothing_raised { env = @request.read(client) } - assert_equal '*', env['REQUEST_PATH'] - assert_equal '*', env['PATH_INFO'] + assert_equal '', env['REQUEST_PATH'] + assert_equal '', env['PATH_INFO'] assert_equal '*', env['REQUEST_URI'] - - # assert_nothing_raised { res = @lint.call(env) } # fails Rack lint + assert_nothing_raised { res = @lint.call(env) } end - def test_full_url_path + def test_absolute_uri_with_query client = MockRequest.new("GET http://e:3/x?y=z HTTP/1.1\r\n" \ "Host: foo\r\n\r\n") res = env = nil assert_nothing_raised { env = @request.read(client) } assert_equal '/x', env['REQUEST_PATH'] assert_equal '/x', env['PATH_INFO'] + assert_equal 'y=z', env['QUERY_STRING'] assert_nothing_raised { res = @lint.call(env) } end + def test_absolute_uri_with_fragment + client = MockRequest.new("GET http://e:3/x#frag HTTP/1.1\r\n" \ + "Host: foo\r\n\r\n") + res = env = nil + assert_nothing_raised { env = @request.read(client) } + assert_equal '/x', env['REQUEST_PATH'] + assert_equal '/x', env['PATH_INFO'] + assert_equal '', env['QUERY_STRING'] + assert_equal 'frag', env['FRAGMENT'] + assert_nothing_raised { res = @lint.call(env) } + end + + def test_absolute_uri_with_query_and_fragment + client = MockRequest.new("GET http://e:3/x?a=b#frag HTTP/1.1\r\n" \ + "Host: foo\r\n\r\n") + res = env = nil + assert_nothing_raised { env = @request.read(client) } + assert_equal '/x', env['REQUEST_PATH'] + assert_equal '/x', env['PATH_INFO'] + assert_equal 'a=b', env['QUERY_STRING'] + assert_equal 'frag', env['FRAGMENT'] + assert_nothing_raised { res = @lint.call(env) } + end + + def test_absolute_uri_unsupported_schemes + %w(ssh+http://e/ ftp://e/x http+ssh://e/x).each do |abs_uri| + client = MockRequest.new("GET #{abs_uri} HTTP/1.1\r\n" \ + "Host: foo\r\n\r\n") + assert_raises(HttpParserError) { @request.read(client) } + @request.reset + end + end + def test_x_forwarded_proto_https res = env = nil client = MockRequest.new("GET / HTTP/1.1\r\n" \ -- cgit v1.2.3-24-ge0c7