1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

Start cleanup and modernization

This commit is contained in:
Evan Phoenix 2011-09-14 22:07:27 -07:00
parent 812ce631df
commit f748edfade
11 changed files with 293 additions and 324 deletions

View file

@ -4,5 +4,7 @@
# Thank You. # Thank You.
# #
require 'rubygems'
# load rakefile extensions (tasks) # load rakefile extensions (tasks)
Dir['tasks/*.rake'].sort.each { |f| load f } Dir['tasks/*.rake'].sort.each { |f| load f }

View file

@ -8,12 +8,7 @@
#include <string.h> #include <string.h>
#include "http11_parser.h" #include "http11_parser.h"
#ifndef RSTRING_PTR #ifndef MANAGED_STRINGS
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(s) (RSTRING(s)->len)
#endif
#ifndef RSTRING_PTR #ifndef RSTRING_PTR
#define RSTRING_PTR(s) (RSTRING(s)->ptr) #define RSTRING_PTR(s) (RSTRING(s)->ptr)
@ -22,8 +17,11 @@
#define RSTRING_LEN(s) (RSTRING(s)->len) #define RSTRING_LEN(s) (RSTRING(s)->len)
#endif #endif
static VALUE mMongrel; #define rb_extract_chars(e, sz) (*sz = RSTRING_LEN(e), RSTRING_PTR(e))
static VALUE cHttpParser; #define rb_free_chars(e) /* nothing */
#endif
static VALUE eHttpParserError; static VALUE eHttpParserError;
#define id_handler_map rb_intern("@handler_map") #define id_handler_map rb_intern("@handler_map")
@ -36,21 +34,7 @@ static VALUE global_request_uri;
static VALUE global_fragment; static VALUE global_fragment;
static VALUE global_query_string; static VALUE global_query_string;
static VALUE global_http_version; static VALUE global_http_version;
static VALUE global_content_length;
static VALUE global_http_content_length;
static VALUE global_request_path; static VALUE global_request_path;
static VALUE global_content_type;
static VALUE global_http_content_type;
static VALUE global_gateway_interface;
static VALUE global_gateway_interface_value;
static VALUE global_server_name;
static VALUE global_server_port;
static VALUE global_server_protocol;
static VALUE global_server_protocol_value;
static VALUE global_http_host;
static VALUE global_mongrel_version;
static VALUE global_server_software;
static VALUE global_port_80;
#define TRIE_INCREASE 30 #define TRIE_INCREASE 30
@ -61,7 +45,7 @@ static VALUE global_port_80;
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); } #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR); }
/** Defines global strings in the init method. */ /** Defines global strings in the init method. */
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N) #define DEF_GLOBAL(N, val) global_##N = rb_str_new2(val); rb_global_variable(&global_##N)
/* Defines the maximum allowed lengths for various input elements.*/ /* Defines the maximum allowed lengths for various input elements.*/
@ -149,7 +133,7 @@ static void init_common_fields(void)
for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) { for(i = 0; i < ARRAY_SIZE(common_http_fields); cf++, i++) {
memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1); memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
cf->value = rb_obj_freeze(rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len)); cf->value = rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len);
rb_global_variable(&cf->value); rb_global_variable(&cf->value);
} }
@ -184,9 +168,9 @@ static VALUE find_common_field_value(const char *field, size_t flen)
#endif /* !HAVE_QSORT_BSEARCH */ #endif /* !HAVE_QSORT_BSEARCH */
} }
void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) void http_field(http_parser* hp, const char *field, size_t flen,
const char *value, size_t vlen)
{ {
VALUE req = (VALUE)data;
VALUE v = Qnil; VALUE v = Qnil;
VALUE f = Qnil; VALUE f = Qnil;
@ -201,122 +185,83 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
/* /*
* We got a strange header that we don't have a memoized value for. * We got a strange header that we don't have a memoized value for.
* Fallback to creating a new string to use as a hash key. * Fallback to creating a new string to use as a hash key.
*
* using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
* in my testing, because: there's no minimum allocation length (and
* no check for it, either), RSTRING_LEN(f) does not need to be
* written twice, and and RSTRING_PTR(f) will already be
* null-terminated for us.
*/ */
f = rb_str_new(NULL, HTTP_PREFIX_LEN + flen);
memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN); size_t new_size = HTTP_PREFIX_LEN + flen;
memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen); assert(new_size < BUFFER_LEN);
assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0'); /* paranoia */
/* fprintf(stderr, "UNKNOWN HEADER <%s>\n", RSTRING_PTR(f)); */ memcpy(hp->buf, HTTP_PREFIX, HTTP_PREFIX_LEN);
memcpy(hp->buf + HTTP_PREFIX_LEN, field, flen);
f = rb_str_new(hp->buf, new_size);
} }
rb_hash_aset(req, f, v); rb_hash_aset(hp->request, f, v);
} }
void request_method(void *data, const char *at, size_t length) void request_method(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = Qnil; VALUE val = Qnil;
val = rb_str_new(at, length); val = rb_str_new(at, length);
rb_hash_aset(req, global_request_method, val); rb_hash_aset(hp->request, global_request_method, val);
} }
void request_uri(void *data, const char *at, size_t length) void request_uri(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = Qnil; VALUE val = Qnil;
VALIDATE_MAX_LENGTH(length, REQUEST_URI); VALIDATE_MAX_LENGTH(length, REQUEST_URI);
val = rb_str_new(at, length); val = rb_str_new(at, length);
rb_hash_aset(req, global_request_uri, val); rb_hash_aset(hp->request, global_request_uri, val);
} }
void fragment(void *data, const char *at, size_t length) void fragment(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = Qnil; VALUE val = Qnil;
VALIDATE_MAX_LENGTH(length, FRAGMENT); VALIDATE_MAX_LENGTH(length, FRAGMENT);
val = rb_str_new(at, length); val = rb_str_new(at, length);
rb_hash_aset(req, global_fragment, val); rb_hash_aset(hp->request, global_fragment, val);
} }
void request_path(void *data, const char *at, size_t length) void request_path(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = Qnil; VALUE val = Qnil;
VALIDATE_MAX_LENGTH(length, REQUEST_PATH); VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
val = rb_str_new(at, length); val = rb_str_new(at, length);
rb_hash_aset(req, global_request_path, val); rb_hash_aset(hp->request, global_request_path, val);
} }
void query_string(void *data, const char *at, size_t length) void query_string(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = Qnil; VALUE val = Qnil;
VALIDATE_MAX_LENGTH(length, QUERY_STRING); VALIDATE_MAX_LENGTH(length, QUERY_STRING);
val = rb_str_new(at, length); val = rb_str_new(at, length);
rb_hash_aset(req, global_query_string, val); rb_hash_aset(hp->request, global_query_string, val);
} }
void http_version(void *data, const char *at, size_t length) void http_version(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data;
VALUE val = rb_str_new(at, length); VALUE val = rb_str_new(at, length);
rb_hash_aset(req, global_http_version, val); rb_hash_aset(hp->request, global_http_version, val);
} }
/** Finalizes the request header to have a bunch of stuff that's /** Finalizes the request header to have a bunch of stuff that's
needed. */ needed. */
void header_done(void *data, const char *at, size_t length) void header_done(http_parser* hp, const char *at, size_t length)
{ {
VALUE req = (VALUE)data; VALUE req = hp->request;
VALUE temp = Qnil;
VALUE ctype = Qnil;
VALUE clen = Qnil;
char *colon = NULL;
clen = rb_hash_aref(req, global_http_content_length);
if(clen != Qnil) {
rb_hash_aset(req, global_content_length, clen);
}
ctype = rb_hash_aref(req, global_http_content_type);
if(ctype != Qnil) {
rb_hash_aset(req, global_content_type, ctype);
}
rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
if(colon != NULL) {
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
rb_hash_aset(req, global_server_port,
rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
RSTRING_LEN(temp)));
} else {
rb_hash_aset(req, global_server_name, temp);
rb_hash_aset(req, global_server_port, global_port_80);
}
}
/* grab the initial body and stuff it into an ivar */ /* grab the initial body and stuff it into an ivar */
rb_ivar_set(req, id_http_body, rb_str_new(at, length)); rb_ivar_set(req, id_http_body, rb_str_new(at, length));
rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
rb_hash_aset(req, global_server_software, global_mongrel_version);
} }
@ -328,10 +273,12 @@ void HttpParser_free(void *data) {
} }
} }
void HttpParser_mark(http_parser* hp) {
if(hp->request) rb_gc_mark(hp->request);
}
VALUE HttpParser_alloc(VALUE klass) VALUE HttpParser_alloc(VALUE klass)
{ {
VALUE obj;
http_parser *hp = ALLOC_N(http_parser, 1); http_parser *hp = ALLOC_N(http_parser, 1);
TRACE(); TRACE();
hp->http_field = http_field; hp->http_field = http_field;
@ -342,14 +289,13 @@ VALUE HttpParser_alloc(VALUE klass)
hp->query_string = query_string; hp->query_string = query_string;
hp->http_version = http_version; hp->http_version = http_version;
hp->header_done = header_done; hp->header_done = header_done;
hp->request = Qnil;
http_parser_init(hp); http_parser_init(hp);
obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp); return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
return obj;
} }
/** /**
* call-seq: * call-seq:
* parser.new -> parser * parser.new -> parser
@ -427,15 +373,16 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
DATA_GET(self, http_parser, http); DATA_GET(self, http_parser, http);
from = FIX2INT(start); from = FIX2INT(start);
dptr = RSTRING_PTR(data); dptr = rb_extract_chars(data, &dlen);
dlen = RSTRING_LEN(data);
if(from >= dlen) { if(from >= dlen) {
rb_free_chars(dptr);
rb_raise(eHttpParserError, "Requested start is after data buffer end."); rb_raise(eHttpParserError, "Requested start is after data buffer end.");
} else { } else {
http->data = (void *)req_hash; http->request = req_hash;
http_parser_execute(http, dptr, dlen, from); http_parser_execute(http, dptr, dlen, from);
rb_free_chars(dptr);
VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER); VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);
if(http_parser_has_error(http)) { if(http_parser_has_error(http)) {
@ -496,7 +443,7 @@ VALUE HttpParser_nread(VALUE self)
void Init_http11() void Init_http11()
{ {
mMongrel = rb_define_module("Mongrel"); VALUE mMongrel = rb_define_module("Mongrel");
DEF_GLOBAL(request_method, "REQUEST_METHOD"); DEF_GLOBAL(request_method, "REQUEST_METHOD");
DEF_GLOBAL(request_uri, "REQUEST_URI"); DEF_GLOBAL(request_uri, "REQUEST_URI");
@ -504,24 +451,11 @@ void Init_http11()
DEF_GLOBAL(query_string, "QUERY_STRING"); DEF_GLOBAL(query_string, "QUERY_STRING");
DEF_GLOBAL(http_version, "HTTP_VERSION"); DEF_GLOBAL(http_version, "HTTP_VERSION");
DEF_GLOBAL(request_path, "REQUEST_PATH"); DEF_GLOBAL(request_path, "REQUEST_PATH");
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
DEF_GLOBAL(content_type, "CONTENT_TYPE");
DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
DEF_GLOBAL(server_name, "SERVER_NAME");
DEF_GLOBAL(server_port, "SERVER_PORT");
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
DEF_GLOBAL(http_host, "HTTP_HOST");
DEF_GLOBAL(mongrel_version, "Mongrel 1.2.0.beta.1"); /* XXX Why is this defined here? */
DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
DEF_GLOBAL(port_80, "80");
eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError); eHttpParserError = rb_define_class_under(mMongrel, "HttpParserError", rb_eIOError);
rb_global_variable(&eHttpParserError);
cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject); VALUE cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
rb_define_alloc_func(cHttpParser, HttpParser_alloc); rb_define_alloc_func(cHttpParser, HttpParser_alloc);
rb_define_method(cHttpParser, "initialize", HttpParser_init,0); rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
rb_define_method(cHttpParser, "reset", HttpParser_reset,0); rb_define_method(cHttpParser, "reset", HttpParser_reset,0);

View file

@ -1,3 +1,4 @@
#line 1 "ext/http11/http11_parser.rl" #line 1 "ext/http11/http11_parser.rl"
/** /**
* Copyright (c) 2005 Zed A. Shaw * Copyright (c) 2005 Zed A. Shaw
@ -28,28 +29,31 @@ static void snake_upcase_char(char *c)
/** Machine **/ /** Machine **/
#line 87 "ext/http11/http11_parser.rl"
#line 78 "ext/http11/http11_parser.rl"
/** Data **/ /** Data **/
#line 37 "ext/http11/http11_parser.c" #line 39 "ext/http11/http11_parser.c"
static const int http_parser_start = 1; static const int http_parser_start = 1;
static const int http_parser_first_final = 57; static const int http_parser_first_final = 57;
static const int http_parser_error = 0; static const int http_parser_error = 0;
static const int http_parser_en_main = 1; static const int http_parser_en_main = 1;
#line 91 "ext/http11/http11_parser.rl"
#line 82 "ext/http11/http11_parser.rl"
int http_parser_init(http_parser *parser) { int http_parser_init(http_parser *parser) {
int cs = 0; int cs = 0;
#line 49 "ext/http11/http11_parser.c" #line 52 "ext/http11/http11_parser.c"
{ {
cs = http_parser_start; cs = http_parser_start;
} }
#line 95 "ext/http11/http11_parser.rl"
#line 86 "ext/http11/http11_parser.rl"
parser->cs = cs; parser->cs = cs;
parser->body_start = 0; parser->body_start = 0;
parser->content_len = 0; parser->content_len = 0;
@ -57,8 +61,9 @@ int http_parser_init(http_parser *parser) {
parser->nread = 0; parser->nread = 0;
parser->field_len = 0; parser->field_len = 0;
parser->field_start = 0; parser->field_start = 0;
parser->request = Qnil;
return(1); return 1;
} }
@ -76,7 +81,7 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len,
assert(pe - p == len - off && "pointers aren't same distance"); assert(pe - p == len - off && "pointers aren't same distance");
#line 80 "ext/http11/http11_parser.c" #line 85 "ext/http11/http11_parser.c"
{ {
if ( p == pe ) if ( p == pe )
goto _test_eof; goto _test_eof;
@ -107,7 +112,7 @@ st2:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof2; goto _test_eof2;
case 2: case 2:
#line 111 "ext/http11/http11_parser.c" #line 116 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr2; case 32: goto tr2;
case 36: goto st38; case 36: goto st38;
@ -123,17 +128,16 @@ case 2:
goto st38; goto st38;
goto st0; goto st0;
tr2: tr2:
#line 49 "ext/http11/http11_parser.rl" #line 47 "ext/http11/http11_parser.rl"
{ {
if(parser->request_method != NULL) parser->request_method(parser, PTR_TO(mark), LEN(mark, p));
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st3; goto st3;
st3: st3:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof3; goto _test_eof3;
case 3: case 3:
#line 137 "ext/http11/http11_parser.c" #line 141 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 42: goto tr4; case 42: goto tr4;
case 43: goto tr5; case 43: goto tr5;
@ -157,78 +161,69 @@ st4:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof4; goto _test_eof4;
case 4: case 4:
#line 161 "ext/http11/http11_parser.c" #line 165 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr8; case 32: goto tr8;
case 35: goto tr9; case 35: goto tr9;
} }
goto st0; goto st0;
tr8: tr8:
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
tr31: tr31:
#line 34 "ext/http11/http11_parser.rl" #line 34 "ext/http11/http11_parser.rl"
{ MARK(mark, p); } { MARK(mark, p); }
#line 57 "ext/http11/http11_parser.rl" #line 53 "ext/http11/http11_parser.rl"
{ {
if(parser->fragment != NULL) parser->fragment(parser, PTR_TO(mark), LEN(mark, p));
parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
tr34: tr34:
#line 57 "ext/http11/http11_parser.rl" #line 53 "ext/http11/http11_parser.rl"
{ {
if(parser->fragment != NULL) parser->fragment(parser, PTR_TO(mark), LEN(mark, p));
parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
tr42: tr42:
#line 73 "ext/http11/http11_parser.rl" #line 66 "ext/http11/http11_parser.rl"
{ {
if(parser->request_path != NULL) parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
tr53: tr53:
#line 62 "ext/http11/http11_parser.rl" #line 57 "ext/http11/http11_parser.rl"
{ MARK(query_start, p); } { MARK(query_start, p); }
#line 63 "ext/http11/http11_parser.rl" #line 58 "ext/http11/http11_parser.rl"
{ {
if(parser->query_string != NULL) parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
tr57: tr57:
#line 63 "ext/http11/http11_parser.rl" #line 58 "ext/http11/http11_parser.rl"
{ {
if(parser->query_string != NULL) parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st5; goto st5;
st5: st5:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof5; goto _test_eof5;
case 5: case 5:
#line 232 "ext/http11/http11_parser.c" #line 227 "ext/http11/http11_parser.c"
if ( (*p) == 72 ) if ( (*p) == 72 )
goto tr10; goto tr10;
goto st0; goto st0;
@ -240,7 +235,7 @@ st6:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof6; goto _test_eof6;
case 6: case 6:
#line 244 "ext/http11/http11_parser.c" #line 239 "ext/http11/http11_parser.c"
if ( (*p) == 84 ) if ( (*p) == 84 )
goto st7; goto st7;
goto st0; goto st0;
@ -298,10 +293,9 @@ case 13:
goto st13; goto st13;
goto st0; goto st0;
tr18: tr18:
#line 68 "ext/http11/http11_parser.rl" #line 62 "ext/http11/http11_parser.rl"
{ {
if(parser->http_version != NULL) parser->http_version(parser, PTR_TO(mark), LEN(mark, p));
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st14; goto st14;
tr26: tr26:
@ -309,24 +303,20 @@ tr26:
{ MARK(mark, p); } { MARK(mark, p); }
#line 44 "ext/http11/http11_parser.rl" #line 44 "ext/http11/http11_parser.rl"
{ {
if(parser->http_field != NULL) { parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
}
} }
goto st14; goto st14;
tr29: tr29:
#line 44 "ext/http11/http11_parser.rl" #line 44 "ext/http11/http11_parser.rl"
{ {
if(parser->http_field != NULL) { parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
}
} }
goto st14; goto st14;
st14: st14:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof14; goto _test_eof14;
case 14: case 14:
#line 330 "ext/http11/http11_parser.c" #line 320 "ext/http11/http11_parser.c"
if ( (*p) == 10 ) if ( (*p) == 10 )
goto st15; goto st15;
goto st0; goto st0;
@ -366,11 +356,10 @@ case 16:
goto tr22; goto tr22;
goto st0; goto st0;
tr22: tr22:
#line 78 "ext/http11/http11_parser.rl" #line 70 "ext/http11/http11_parser.rl"
{ {
parser->body_start = p - buffer + 1; parser->body_start = p - buffer + 1;
if(parser->header_done != NULL) parser->header_done(parser, p + 1, pe - p - 1);
parser->header_done(parser->data, p + 1, pe - p - 1);
{p++; cs = 57; goto _out;} {p++; cs = 57; goto _out;}
} }
goto st57; goto st57;
@ -378,7 +367,7 @@ st57:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof57; goto _test_eof57;
case 57: case 57:
#line 382 "ext/http11/http11_parser.c" #line 371 "ext/http11/http11_parser.c"
goto st0; goto st0;
tr21: tr21:
#line 37 "ext/http11/http11_parser.rl" #line 37 "ext/http11/http11_parser.rl"
@ -394,7 +383,7 @@ st17:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof17; goto _test_eof17;
case 17: case 17:
#line 398 "ext/http11/http11_parser.c" #line 387 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 33: goto tr23; case 33: goto tr23;
case 58: goto tr24; case 58: goto tr24;
@ -433,7 +422,7 @@ st18:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof18; goto _test_eof18;
case 18: case 18:
#line 437 "ext/http11/http11_parser.c" #line 426 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 13: goto tr26; case 13: goto tr26;
case 32: goto tr27; case 32: goto tr27;
@ -447,60 +436,53 @@ st19:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof19; goto _test_eof19;
case 19: case 19:
#line 451 "ext/http11/http11_parser.c" #line 440 "ext/http11/http11_parser.c"
if ( (*p) == 13 ) if ( (*p) == 13 )
goto tr29; goto tr29;
goto st19; goto st19;
tr9: tr9:
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st20; goto st20;
tr43: tr43:
#line 73 "ext/http11/http11_parser.rl" #line 66 "ext/http11/http11_parser.rl"
{ {
if(parser->request_path != NULL) parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st20; goto st20;
tr54: tr54:
#line 62 "ext/http11/http11_parser.rl" #line 57 "ext/http11/http11_parser.rl"
{ MARK(query_start, p); } { MARK(query_start, p); }
#line 63 "ext/http11/http11_parser.rl" #line 58 "ext/http11/http11_parser.rl"
{ {
if(parser->query_string != NULL) parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st20; goto st20;
tr58: tr58:
#line 63 "ext/http11/http11_parser.rl" #line 58 "ext/http11/http11_parser.rl"
{ {
if(parser->query_string != NULL) parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
} }
#line 53 "ext/http11/http11_parser.rl" #line 50 "ext/http11/http11_parser.rl"
{ {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
} }
goto st20; goto st20;
st20: st20:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof20; goto _test_eof20;
case 20: case 20:
#line 504 "ext/http11/http11_parser.c" #line 486 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr31; case 32: goto tr31;
case 37: goto tr32; case 37: goto tr32;
@ -522,7 +504,7 @@ st21:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof21; goto _test_eof21;
case 21: case 21:
#line 526 "ext/http11/http11_parser.c" #line 508 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr34; case 32: goto tr34;
case 37: goto st22; case 37: goto st22;
@ -544,7 +526,7 @@ st22:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof22; goto _test_eof22;
case 22: case 22:
#line 548 "ext/http11/http11_parser.c" #line 530 "ext/http11/http11_parser.c"
if ( (*p) < 65 ) { if ( (*p) < 65 ) {
if ( 48 <= (*p) && (*p) <= 57 ) if ( 48 <= (*p) && (*p) <= 57 )
goto st23; goto st23;
@ -575,7 +557,7 @@ st24:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof24; goto _test_eof24;
case 24: case 24:
#line 579 "ext/http11/http11_parser.c" #line 561 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 43: goto st24; case 43: goto st24;
case 58: goto st25; case 58: goto st25;
@ -600,7 +582,7 @@ st25:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof25; goto _test_eof25;
case 25: case 25:
#line 604 "ext/http11/http11_parser.c" #line 586 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr8; case 32: goto tr8;
case 34: goto st0; case 34: goto st0;
@ -647,7 +629,7 @@ st28:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof28; goto _test_eof28;
case 28: case 28:
#line 651 "ext/http11/http11_parser.c" #line 633 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr42; case 32: goto tr42;
case 34: goto st0; case 34: goto st0;
@ -689,17 +671,16 @@ case 30:
goto st28; goto st28;
goto st0; goto st0;
tr45: tr45:
#line 73 "ext/http11/http11_parser.rl" #line 66 "ext/http11/http11_parser.rl"
{ {
if(parser->request_path != NULL) parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
} }
goto st31; goto st31;
st31: st31:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof31; goto _test_eof31;
case 31: case 31:
#line 703 "ext/http11/http11_parser.c" #line 684 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr8; case 32: goto tr8;
case 34: goto st0; case 34: goto st0;
@ -740,17 +721,16 @@ case 33:
goto st31; goto st31;
goto st0; goto st0;
tr46: tr46:
#line 73 "ext/http11/http11_parser.rl" #line 66 "ext/http11/http11_parser.rl"
{ {
if(parser->request_path != NULL) parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
} }
goto st34; goto st34;
st34: st34:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof34; goto _test_eof34;
case 34: case 34:
#line 754 "ext/http11/http11_parser.c" #line 734 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr53; case 32: goto tr53;
case 34: goto st0; case 34: goto st0;
@ -764,14 +744,14 @@ case 34:
goto st0; goto st0;
goto tr52; goto tr52;
tr52: tr52:
#line 62 "ext/http11/http11_parser.rl" #line 57 "ext/http11/http11_parser.rl"
{ MARK(query_start, p); } { MARK(query_start, p); }
goto st35; goto st35;
st35: st35:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof35; goto _test_eof35;
case 35: case 35:
#line 775 "ext/http11/http11_parser.c" #line 755 "ext/http11/http11_parser.c"
switch( (*p) ) { switch( (*p) ) {
case 32: goto tr57; case 32: goto tr57;
case 34: goto st0; case 34: goto st0;
@ -785,14 +765,14 @@ case 35:
goto st0; goto st0;
goto st35; goto st35;
tr55: tr55:
#line 62 "ext/http11/http11_parser.rl" #line 57 "ext/http11/http11_parser.rl"
{ MARK(query_start, p); } { MARK(query_start, p); }
goto st36; goto st36;
st36: st36:
if ( ++p == pe ) if ( ++p == pe )
goto _test_eof36; goto _test_eof36;
case 36: case 36:
#line 796 "ext/http11/http11_parser.c" #line 776 "ext/http11/http11_parser.c"
if ( (*p) < 65 ) { if ( (*p) < 65 ) {
if ( 48 <= (*p) && (*p) <= 57 ) if ( 48 <= (*p) && (*p) <= 57 )
goto st37; goto st37;
@ -1207,7 +1187,8 @@ case 56:
_test_eof: {} _test_eof: {}
_out: {} _out: {}
} }
#line 121 "ext/http11/http11_parser.rl"
#line 113 "ext/http11/http11_parser.rl"
if (!http_parser_has_error(parser)) if (!http_parser_has_error(parser))
parser->cs = cs; parser->cs = cs;

View file

@ -6,14 +6,24 @@
#ifndef http11_parser_h #ifndef http11_parser_h
#define http11_parser_h #define http11_parser_h
#include "ruby.h"
#include <sys/types.h> #include <sys/types.h>
#if defined(_WIN32) #if defined(_WIN32)
#include <stddef.h> #include <stddef.h>
#endif #endif
typedef void (*element_cb)(void *data, const char *at, size_t length); #define BUFFER_LEN 1024
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
struct http_parser;
typedef void (*element_cb)(struct http_parser* hp,
const char *at, size_t length);
typedef void (*field_cb)(struct http_parser* hp,
const char *field, size_t flen,
const char *value, size_t vlen);
typedef struct http_parser { typedef struct http_parser {
int cs; int cs;
@ -25,7 +35,7 @@ typedef struct http_parser {
size_t field_len; size_t field_len;
size_t query_start; size_t query_start;
void *data; VALUE request;
field_cb http_field; field_cb http_field;
element_cb request_method; element_cb request_method;
@ -36,11 +46,14 @@ typedef struct http_parser {
element_cb http_version; element_cb http_version;
element_cb header_done; element_cb header_done;
char buf[BUFFER_LEN];
} http_parser; } http_parser;
int http_parser_init(http_parser *parser); int http_parser_init(http_parser *parser);
int http_parser_finish(http_parser *parser); int http_parser_finish(http_parser *parser);
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off); size_t http_parser_execute(http_parser *parser, const char *data,
size_t len, size_t off);
int http_parser_has_error(http_parser *parser); int http_parser_has_error(http_parser *parser);
int http_parser_is_finished(http_parser *parser); int http_parser_is_finished(http_parser *parser);

View file

@ -42,43 +42,34 @@ static void snake_upcase_char(char *c)
action start_value { MARK(mark, fpc); } action start_value { MARK(mark, fpc); }
action write_value { action write_value {
if(parser->http_field != NULL) { parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
}
} }
action request_method { action request_method {
if(parser->request_method != NULL) parser->request_method(parser, PTR_TO(mark), LEN(mark, fpc));
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
} }
action request_uri { action request_uri {
if(parser->request_uri != NULL) parser->request_uri(parser, PTR_TO(mark), LEN(mark, fpc));
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
} }
action fragment { action fragment {
if(parser->fragment != NULL) parser->fragment(parser, PTR_TO(mark), LEN(mark, fpc));
parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
} }
action start_query { MARK(query_start, fpc); } action start_query { MARK(query_start, fpc); }
action query_string { action query_string {
if(parser->query_string != NULL) parser->query_string(parser, PTR_TO(query_start), LEN(query_start, fpc));
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
} }
action http_version { action http_version {
if(parser->http_version != NULL) parser->http_version(parser, PTR_TO(mark), LEN(mark, fpc));
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
} }
action request_path { action request_path {
if(parser->request_path != NULL) parser->request_path(parser, PTR_TO(mark), LEN(mark,fpc));
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
} }
action done { action done {
parser->body_start = fpc - buffer + 1; parser->body_start = fpc - buffer + 1;
if(parser->header_done != NULL) parser->header_done(parser, fpc + 1, pe - fpc - 1);
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
fbreak; fbreak;
} }
@ -99,8 +90,9 @@ int http_parser_init(http_parser *parser) {
parser->nread = 0; parser->nread = 0;
parser->field_len = 0; parser->field_len = 0;
parser->field_start = 0; parser->field_start = 0;
parser->request = Qnil;
return(1); return 1;
} }

View file

@ -19,8 +19,6 @@ end
# Gem conditional loader # Gem conditional loader
require 'mongrel/gems' require 'mongrel/gems'
Mongrel::Gems.require 'cgi_multipart_eof_fix'
Mongrel::Gems.require 'fastthread'
require 'thread' require 'thread'
# Ruby Mongrel # Ruby Mongrel
@ -71,6 +69,9 @@ module Mongrel
# releases of Mongrel will find other creative ways to make threads faster, but don't # releases of Mongrel will find other creative ways to make threads faster, but don't
# hold your breath until Ruby 1.9 is actually finally useful. # hold your breath until Ruby 1.9 is actually finally useful.
class HttpServer class HttpServer
include Mongrel::Const
attr_reader :acceptor attr_reader :acceptor
attr_reader :workers attr_reader :workers
attr_reader :classifier attr_reader :classifier
@ -117,7 +118,7 @@ module Mongrel
parser = HttpParser.new parser = HttpParser.new
params = HttpParams.new params = HttpParams.new
request = nil request = nil
data = client.readpartial(Const::CHUNK_SIZE) data = client.readpartial(CHUNK_SIZE)
nparsed = 0 nparsed = 0
# Assumption: nparsed will always be less since data will get filled with more # Assumption: nparsed will always be less since data will get filled with more
@ -128,19 +129,42 @@ module Mongrel
nparsed = parser.execute(params, data, nparsed) nparsed = parser.execute(params, data, nparsed)
if parser.finished? if parser.finished?
if not params[Const::REQUEST_PATH]
# it might be a dumbass full host request header if host = params[HTTP_HOST]
uri = URI.parse(params[Const::REQUEST_URI]) if colon = host.index(":")
params[Const::REQUEST_PATH] = uri.path params[SERVER_NAME] = host[0, colon]
params[SERVER_PORT] = host[colon+1, host.size]
else
params[SERVER_NAME] = host
params[SERVER_PORT] = PORT_80
end
end end
raise "No REQUEST PATH" if not params[Const::REQUEST_PATH] if len = params[HTTP_CONTENT_LENGTH]
params[CONTENT_LENGTH] = len
end
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH]) if type = params[HTTP_CONTENT_TYPE]
params[RAW_CONTENT_TYPE] = type
end
params[SERVER_PROTOCOL] = HTTP_11
params[SERVER_SOFTWARE] = MONGREL_VERSION
params[GATEWAY_INTERFACE] = CGI_VER
if not params[REQUEST_PATH]
# it might be a dumbass full host request header
uri = URI.parse(params[REQUEST_URI])
params[REQUEST_PATH] = uri.path
end
raise "No REQUEST PATH" if not params[REQUEST_PATH]
script_name, path_info, handlers = @classifier.resolve(params[REQUEST_PATH])
if handlers if handlers
params[Const::PATH_INFO] = path_info params[PATH_INFO] = path_info
params[Const::SCRIPT_NAME] = script_name params[SCRIPT_NAME] = script_name
# From http://www.ietf.org/rfc/rfc3875 : # From http://www.ietf.org/rfc/rfc3875 :
# "Script authors should be aware that the REMOTE_ADDR and REMOTE_HOST # "Script authors should be aware that the REMOTE_ADDR and REMOTE_HOST
@ -148,7 +172,7 @@ module Mongrel
# ultimate source of the request. They identify the client for the # ultimate source of the request. They identify the client for the
# immediate request to the server; that client may be a proxy, gateway, # immediate request to the server; that client may be a proxy, gateway,
# or other intermediary acting on behalf of the actual source client." # or other intermediary acting on behalf of the actual source client."
params[Const::REMOTE_ADDR] = client.peeraddr.last params[REMOTE_ADDR] = client.peeraddr.last
# select handlers that want more detailed request notification # select handlers that want more detailed request notification
notifiers = handlers.select { |h| h.request_notify } notifiers = handlers.select { |h| h.request_notify }
@ -172,17 +196,17 @@ module Mongrel
end end
else else
# Didn't find it, return a stock 404 response. # Didn't find it, return a stock 404 response.
client.write(Const::ERROR_404_RESPONSE) client.write(ERROR_404_RESPONSE)
end end
break #done break #done
else else
# Parser is not done, queue up more data to read and continue parsing # Parser is not done, queue up more data to read and continue parsing
chunk = client.readpartial(Const::CHUNK_SIZE) chunk = client.readpartial(CHUNK_SIZE)
break if !chunk or chunk.length == 0 # read failed, stop processing break if !chunk or chunk.length == 0 # read failed, stop processing
data << chunk data << chunk
if data.length >= Const::MAX_HEADER if data.length >= MAX_HEADER
raise HttpParserError.new("HEADER is longer than allowed, aborting client early.") raise HttpParserError.new("HEADER is longer than allowed, aborting client early.")
end end
end end
@ -190,7 +214,7 @@ module Mongrel
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
client.close rescue nil client.close rescue nil
rescue HttpParserError => e rescue HttpParserError => e
STDERR.puts "#{Time.now}: HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}" STDERR.puts "#{Time.now}: HTTP parse error, malformed request (#{params[HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}"
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n" STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
rescue Errno::EMFILE rescue Errno::EMFILE
reap_dead_workers('too many files') reap_dead_workers('too many files')
@ -244,18 +268,22 @@ module Mongrel
end end
def configure_socket_options def configure_socket_options
@tcp_defer_accept_opts = nil
@tcp_cork_opts = nil
case RUBY_PLATFORM case RUBY_PLATFORM
when /linux/ when /linux/
# 9 is currently TCP_DEFER_ACCEPT # 9 is currently TCP_DEFER_ACCEPT
$tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1] @tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1]
$tcp_cork_opts = [Socket::SOL_TCP, 3, 1] @tcp_cork_opts = [Socket::SOL_TCP, 3, 1]
when /freebsd(([1-4]\..{1,2})|5\.[0-4])/ when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
# Do nothing, just closing a bug when freebsd <= 5.4 # Do nothing, just closing a bug when freebsd <= 5.4
when /freebsd/ when /freebsd/
# Use the HTTP accept filter if available. # Use the HTTP accept filter if available.
# The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg
unless `/sbin/sysctl -nq net.inet.accf.http`.empty? unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
$tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')] @tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')]
end end
end end
end end
@ -267,19 +295,19 @@ module Mongrel
configure_socket_options configure_socket_options
if defined?($tcp_defer_accept_opts) and $tcp_defer_accept_opts if @tcp_defer_accept_opts
@socket.setsockopt(*$tcp_defer_accept_opts) rescue nil @socket.setsockopt(*@tcp_defer_accept_opts)
end end
tcp_cork_opts = @tcp_cork_opts
@acceptor = Thread.new do @acceptor = Thread.new do
begin begin
while true while true
begin begin
client = @socket.accept client = @socket.accept
if defined?($tcp_cork_opts) and $tcp_cork_opts client.setsockopt(*tcp_cork_opts) if tcp_cork_opts
client.setsockopt(*$tcp_cork_opts) rescue nil
end
worker_list = @workers.list worker_list = @workers.list
@ -359,8 +387,4 @@ module Mongrel
end end
end end
# Load experimental library, if present. We put it here so it can override anything
# in regular Mongrel.
$LOAD_PATH.unshift 'projects/mongrel_experimental/lib/'
Mongrel::Gems.require 'mongrel_experimental', ">=#{Mongrel::Const::MONGREL_VERSION}" Mongrel::Gems.require 'mongrel_experimental', ">=#{Mongrel::Const::MONGREL_VERSION}"

View file

@ -53,29 +53,29 @@ module Mongrel
# REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
# too taxing on performance. # too taxing on performance.
module Const module Const
DATE = "Date".freeze DATE = "Date"
# This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this. # This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this.
PATH_INFO="PATH_INFO".freeze PATH_INFO="PATH_INFO"
# This is the initial part that your handler is identified as by URIClassifier. # This is the initial part that your handler is identified as by URIClassifier.
SCRIPT_NAME="SCRIPT_NAME".freeze SCRIPT_NAME="SCRIPT_NAME"
# The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME. # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
REQUEST_URI='REQUEST_URI'.freeze REQUEST_URI='REQUEST_URI'
REQUEST_PATH='REQUEST_PATH'.freeze REQUEST_PATH='REQUEST_PATH'
MONGREL_VERSION = VERSION = "1.2.0.beta.1".freeze MONGREL_VERSION = VERSION = "1.3.0"
MONGREL_TMP_BASE="mongrel".freeze MONGREL_TMP_BASE="mongrel"
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff. # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
CONTENT_LENGTH="CONTENT_LENGTH".freeze CONTENT_LENGTH="CONTENT_LENGTH"
# A common header for indicating the server is too busy. Not used yet. # A common header for indicating the server is too busy. Not used yet.
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
# The basic max request size we'll try to read. # The basic max request size we'll try to read.
CHUNK_SIZE=(16 * 1024) CHUNK_SIZE=(16 * 1024)
@ -88,23 +88,40 @@ module Mongrel
MAX_BODY=MAX_HEADER MAX_BODY=MAX_HEADER
# A frozen format for this is about 15% faster # A frozen format for this is about 15% faster
STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n".freeze STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n"
CONTENT_TYPE = "Content-Type".freeze CONTENT_TYPE = "Content-Type"
LAST_MODIFIED = "Last-Modified".freeze LAST_MODIFIED = "Last-Modified"
ETAG = "ETag".freeze ETAG = "ETag"
SLASH = "/".freeze SLASH = "/"
REQUEST_METHOD="REQUEST_METHOD".freeze REQUEST_METHOD="REQUEST_METHOD"
GET="GET".freeze GET="GET"
HEAD="HEAD".freeze HEAD="HEAD"
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32) # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
ETAG_FORMAT="\"%x-%x-%x\"".freeze ETAG_FORMAT="\"%x-%x-%x\""
HEADER_FORMAT="%s: %s\r\n".freeze HEADER_FORMAT="%s: %s\r\n"
LINE_END="\r\n".freeze LINE_END="\r\n"
REMOTE_ADDR="REMOTE_ADDR".freeze REMOTE_ADDR="REMOTE_ADDR"
HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR"
HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE".freeze HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE"
HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH"
REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n"
HOST = "HOST".freeze HOST = "HOST"
SERVER_NAME = "SERVER_NAME"
SERVER_PORT = "SERVER_PORT"
HTTP_HOST = "HTTP_HOST"
PORT_80 = "80"
SERVER_PROTOCOL = "SERVER_PROTOCOL"
HTTP_11 = "HTTP/1.1"
SERVER_SOFTWARE = "SERVER_SOFTWARE"
GATEWAY_INTERFACE = "GATEWAY_INTERFACE"
CGI_VER = "CGI/1.2"
HTTP_CONTENT_LENGTH = "HTTP_CONTENT_LENGTH"
HTTP_CONTENT_TYPE = "HTTP_CONTENT_TYPE"
RAW_CONTENT_TYPE = "CONTENT_TYPE"
end end
end end

View file

@ -119,7 +119,7 @@ module Mongrel
# You give it the path to the directory root and and optional listing_allowed and index_html # You give it the path to the directory root and and optional listing_allowed and index_html
def initialize(path, listing_allowed=true, index_html="index.html") def initialize(path, listing_allowed=true, index_html="index.html")
@path = File.expand_path(path) if path @path = (File.expand_path(path) if path)
@listing_allowed = listing_allowed @listing_allowed = listing_allowed
@index_html = index_html @index_html = index_html
@default_content_type = "application/octet-stream".freeze @default_content_type = "application/octet-stream".freeze

View file

@ -4,6 +4,7 @@ module Mongrel
class RegistrationError < RuntimeError class RegistrationError < RuntimeError
end end
class UsageError < RuntimeError class UsageError < RuntimeError
end end
@ -56,8 +57,6 @@ module Mongrel
end end
end end
private
def rebuild def rebuild
if @handler_map.size == 1 and @handler_map[Const::SLASH] if @handler_map.size == 1 and @handler_map[Const::SLASH]
@root_handler = @handler_map.values.first @root_handler = @handler_map.values.first
@ -66,11 +65,16 @@ module Mongrel
routes = @handler_map.keys.sort.sort_by do |uri| routes = @handler_map.keys.sort.sort_by do |uri|
-uri.length -uri.length
end end
@matcher = Regexp.new(routes.map do |uri|
all_possibles = routes.map do |uri|
Regexp.new('^' + Regexp.escape(uri)) Regexp.new('^' + Regexp.escape(uri))
end.join('|')) end.join('|')
@matcher = Regexp.new(all_possibles)
end end
end end
private :rebuild
end end
end end

View file

@ -1,3 +1,5 @@
if ENV['JAVA']
require 'rake/javaextensiontask' require 'rake/javaextensiontask'
# build http11 java extension # build http11 java extension
@ -6,3 +8,5 @@ Rake::JavaExtensionTask.new('http11', HOE.spec) do |ext|
gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' } gs.dependencies.delete gs.dependencies.find { |d| d.name == 'daemons' }
end end
end end
end

View file

@ -21,11 +21,9 @@ class HttpParserTest < Test::Unit::TestCase
assert !parser.error?, "Parser had error" assert !parser.error?, "Parser had error"
assert nread == parser.nread, "Number read returned from execute does not match" assert nread == parser.nread, "Number read returned from execute does not match"
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
assert_equal '/', req['REQUEST_PATH'] assert_equal '/', req['REQUEST_PATH']
assert_equal 'HTTP/1.1', req['HTTP_VERSION'] assert_equal 'HTTP/1.1', req['HTTP_VERSION']
assert_equal '/', req['REQUEST_URI'] assert_equal '/', req['REQUEST_URI']
assert_equal 'CGI/1.2', req['GATEWAY_INTERFACE']
assert_equal 'GET', req['REQUEST_METHOD'] assert_equal 'GET', req['REQUEST_METHOD']
assert_nil req['FRAGMENT'] assert_nil req['FRAGMENT']
assert_nil req['QUERY_STRING'] assert_nil req['QUERY_STRING']