From ebeac4b0312015147ef2319b27c903a791f122af Mon Sep 17 00:00:00 2001 From: zedshaw Date: Sun, 19 Mar 2006 05:18:11 +0000 Subject: Beginning of a tuning effort. git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@116 19e92222-5c0b-0410-8929-a290d50e31e9 --- Rakefile | 4 ++- examples/simpletest.rb | 11 +++++++ ext/http11/http11.c | 78 ++++++++++++++++++++++++++++++++++++++------- ext/http11/http11_parser.c | 51 +++++++++++++++-------------- ext/http11/http11_parser.h | 3 +- ext/http11/http11_parser.rl | 7 ++-- lib/mongrel.rb | 76 ++++++++----------------------------------- 7 files changed, 128 insertions(+), 102 deletions(-) diff --git a/Rakefile b/Rakefile index bb74d5b..4aeba02 100644 --- a/Rakefile +++ b/Rakefile @@ -65,7 +65,7 @@ task :package_win32 do spec.required_ruby_version = '>= 1.8.4' spec.add_dependency('win32-service', '>= 0.5.0') - spec.add_dependency('gem_plugin', ">= 0.2") + spec.add_dependency('gem_plugin', ">= 0.2.1") spec.extensions = [] spec.platform = Gem::Platform::WIN32 @@ -79,11 +79,13 @@ task :install do sh %{sudo gem install pkg/mongrel-#{version}} sub_project("mongrel_status", :install) sub_project("mongrel_config", :install) + sub_project("mongrel_console", :install) end task :uninstall => [:clean] do sub_project("mongrel_status", :uninstall) sub_project("mongrel_config", :uninstall) + sub_project("mongrel_console", :uninstall) sh %{sudo gem uninstall mongrel} sub_project("gem_plugin", :uninstall) end diff --git a/examples/simpletest.rb b/examples/simpletest.rb index 438f69e..0d471b4 100644 --- a/examples/simpletest.rb +++ b/examples/simpletest.rb @@ -19,6 +19,16 @@ class SimpleHandler < Mongrel::HttpHandler end end +class DumbHandler < Mongrel::HttpHandler + def process(request, response) + response.start do |head,out| + head["Content-Type"] = "text/html" + out.write("test") + end + end +end + + if ARGV.length != 3 STDERR.puts "usage: simpletest.rb " exit(1) @@ -26,6 +36,7 @@ end h = Mongrel::HttpServer.new(ARGV[0], ARGV[1].to_i) h.register("/", SimpleHandler.new) +h.register("/dumb", DumbHandler.new) h.register("/files", Mongrel::DirHandler.new(ARGV[2])) h.run diff --git a/ext/http11/http11.c b/ext/http11/http11.c index e69d185..1abec65 100644 --- a/ext/http11/http11.c +++ b/ext/http11/http11.c @@ -16,6 +16,21 @@ static VALUE global_request_method; static VALUE global_request_uri; static VALUE global_query_string; static VALUE global_http_version; +static VALUE global_content_length; +static VALUE global_http_content_length; +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_interface_value; +static VALUE global_remote_address; +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; void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) @@ -67,8 +82,34 @@ void http_version(void *data, const char *at, size_t length) rb_hash_aset(req, global_http_version, val); } +/** Finalizes the request header to have a bunch of stuff that's + needed. */ +void header_done(void *data, const char *at, size_t length) +{ + VALUE req = (VALUE)data; + VALUE temp = Qnil; + VALUE host = Qnil; + VALUE port = Qnil; + char *colon = NULL; + + rb_hash_aset(req, global_content_length, rb_hash_aref(req, global_http_content_length)); + rb_hash_aset(req, global_content_type, rb_hash_aref(req, global_http_content_type)); + rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value); + if((temp = rb_hash_aref(req, global_http_host)) != Qnil) { + // ruby better close strings off with a '\0' dammit + colon = strchr(RSTRING(temp)->ptr, ':'); + if(colon != NULL) { + rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING(temp)->ptr)); + rb_hash_aset(req, global_server_port, + rb_str_substr(temp, colon - RSTRING(temp)->ptr+1, + RSTRING(temp)->len)); + } + } + rb_hash_aset(req, global_server_protocol, global_server_protocol_value); + rb_hash_aset(req, global_server_software, global_mongrel_version); +} void HttpParser_free(void *data) { @@ -90,7 +131,8 @@ VALUE HttpParser_alloc(VALUE klass) hp->request_uri = request_uri; hp->query_string = query_string; hp->http_version = http_version; - + hp->header_done = header_done; + obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp); return obj; @@ -406,6 +448,8 @@ VALUE URIClassifier_resolve(VALUE self, VALUE uri) return result; } +#define DEF_GLOBAL(name, val) global_##name = rb_str_new2(val); rb_global_variable(&global_##name) + void Init_http11() { @@ -413,17 +457,27 @@ void Init_http11() mMongrel = rb_define_module("Mongrel"); id_handler_map = rb_intern("@handler_map"); - global_http_prefix = rb_str_new2("HTTP_"); - rb_global_variable(&global_http_prefix); - global_request_method = rb_str_new2("REQUEST_METHOD"); - rb_global_variable(&global_request_method); - global_request_uri = rb_str_new2("REQUEST_URI"); - rb_global_variable(&global_request_uri); - global_query_string = rb_str_new2("QUERY_STRING"); - rb_global_variable(&global_query_string); - global_http_version = rb_str_new2("HTTP_VERSION"); - rb_global_variable(&global_http_version); - + DEF_GLOBAL(http_prefix, "HTTP_"); + DEF_GLOBAL(request_method, "REQUEST_METHOD"); + DEF_GLOBAL(request_uri, "REQUEST_URI"); + DEF_GLOBAL(query_string, "QUERY_STRING"); + DEF_GLOBAL(http_version, "HTTP_VERSION"); + 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(remote_address, "REMOTE_ADDR"); + 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 0.3.12"); + DEF_GLOBAL(server_software, "SERVER_SOFTWARE"); + + cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject); rb_define_alloc_func(cHttpParser, HttpParser_alloc); rb_define_method(cHttpParser, "initialize", HttpParser_init,0); diff --git a/ext/http11/http11_parser.c b/ext/http11/http11_parser.c index c17d907..fcbfa31 100644 --- a/ext/http11/http11_parser.c +++ b/ext/http11/http11_parser.c @@ -9,7 +9,7 @@ #define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F); /** machine **/ -#line 98 "ext/http11/http11_parser.rl" +#line 101 "ext/http11/http11_parser.rl" /** Data **/ @@ -21,7 +21,7 @@ static int http_parser_first_final = 53; static int http_parser_error = 1; -#line 102 "ext/http11/http11_parser.rl" +#line 105 "ext/http11/http11_parser.rl" int http_parser_init(http_parser *parser) { int cs = 0; @@ -30,7 +30,7 @@ int http_parser_init(http_parser *parser) { { cs = http_parser_start; } -#line 106 "ext/http11/http11_parser.rl" +#line 109 "ext/http11/http11_parser.rl" parser->cs = cs; parser->body_start = NULL; parser->content_len = 0; @@ -305,14 +305,17 @@ case 21: tr40: #line 46 "ext/http11/http11_parser.rl" { - parser->body_start = p+1; goto _out53; + parser->body_start = p+1; + if(parser->header_done != NULL) + parser->header_done(parser->data, p, 0); + goto _out53; } goto st53; st53: if ( ++p == pe ) goto _out53; case 53: -#line 316 "ext/http11/http11_parser.c" +#line 319 "ext/http11/http11_parser.c" goto st1; tr36: #line 16 "ext/http11/http11_parser.rl" @@ -322,7 +325,7 @@ st22: if ( ++p == pe ) goto _out22; case 22: -#line 326 "ext/http11/http11_parser.c" +#line 329 "ext/http11/http11_parser.c" switch( (*p) ) { case 33: goto st22; case 58: goto tr32; @@ -357,7 +360,7 @@ st23: if ( ++p == pe ) goto _out23; case 23: -#line 361 "ext/http11/http11_parser.c" +#line 364 "ext/http11/http11_parser.c" if ( (*p) == 13 ) goto tr49; goto tr52; @@ -369,7 +372,7 @@ st24: if ( ++p == pe ) goto _out24; case 24: -#line 373 "ext/http11/http11_parser.c" +#line 376 "ext/http11/http11_parser.c" if ( (*p) == 13 ) goto tr49; goto st24; @@ -381,7 +384,7 @@ st25: if ( ++p == pe ) goto _out25; case 25: -#line 385 "ext/http11/http11_parser.c" +#line 388 "ext/http11/http11_parser.c" switch( (*p) ) { case 43: goto st25; case 58: goto st26; @@ -406,7 +409,7 @@ st26: if ( ++p == pe ) goto _out26; case 26: -#line 410 "ext/http11/http11_parser.c" +#line 413 "ext/http11/http11_parser.c" switch( (*p) ) { case 32: goto tr34; case 37: goto st27; @@ -454,7 +457,7 @@ st29: if ( ++p == pe ) goto _out29; case 29: -#line 458 "ext/http11/http11_parser.c" +#line 461 "ext/http11/http11_parser.c" switch( (*p) ) { case 32: goto tr34; case 37: goto st31; @@ -525,7 +528,7 @@ st33: if ( ++p == pe ) goto _out33; case 33: -#line 529 "ext/http11/http11_parser.c" +#line 532 "ext/http11/http11_parser.c" switch( (*p) ) { case 32: goto tr46; case 37: goto tr51; @@ -547,7 +550,7 @@ st34: if ( ++p == pe ) goto _out34; case 34: -#line 551 "ext/http11/http11_parser.c" +#line 554 "ext/http11/http11_parser.c" switch( (*p) ) { case 32: goto tr46; case 37: goto st35; @@ -569,7 +572,7 @@ st35: if ( ++p == pe ) goto _out35; case 35: -#line 573 "ext/http11/http11_parser.c" +#line 576 "ext/http11/http11_parser.c" if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) goto st36; @@ -600,7 +603,7 @@ st37: if ( ++p == pe ) goto _out37; case 37: -#line 604 "ext/http11/http11_parser.c" +#line 607 "ext/http11/http11_parser.c" if ( (*p) == 69 ) goto st38; goto st1; @@ -619,7 +622,7 @@ st39: if ( ++p == pe ) goto _out39; case 39: -#line 623 "ext/http11/http11_parser.c" +#line 626 "ext/http11/http11_parser.c" if ( (*p) == 69 ) goto st40; goto st1; @@ -645,7 +648,7 @@ st42: if ( ++p == pe ) goto _out42; case 42: -#line 649 "ext/http11/http11_parser.c" +#line 652 "ext/http11/http11_parser.c" if ( (*p) == 80 ) goto st43; goto st1; @@ -692,7 +695,7 @@ st48: if ( ++p == pe ) goto _out48; case 48: -#line 696 "ext/http11/http11_parser.c" +#line 699 "ext/http11/http11_parser.c" switch( (*p) ) { case 79: goto st49; case 85: goto st38; @@ -713,7 +716,7 @@ st50: if ( ++p == pe ) goto _out50; case 50: -#line 717 "ext/http11/http11_parser.c" +#line 720 "ext/http11/http11_parser.c" if ( (*p) == 82 ) goto st51; goto st1; @@ -788,15 +791,15 @@ case 52: _out: {} } -#line 125 "ext/http11/http11_parser.rl" +#line 128 "ext/http11/http11_parser.rl" parser->cs = cs; parser->nread = p - buffer; if(parser->body_start) { /* final \r\n combo encountered so stop right here */ -#line 799 "ext/http11/http11_parser.c" -#line 131 "ext/http11/http11_parser.rl" +#line 802 "ext/http11/http11_parser.c" +#line 134 "ext/http11/http11_parser.rl" parser->nread++; } @@ -808,8 +811,8 @@ int http_parser_finish(http_parser *parser) int cs = parser->cs; -#line 812 "ext/http11/http11_parser.c" -#line 142 "ext/http11/http11_parser.rl" +#line 815 "ext/http11/http11_parser.c" +#line 145 "ext/http11/http11_parser.rl" parser->cs = cs; diff --git a/ext/http11/http11_parser.h b/ext/http11/http11_parser.h index 6ca9cef..8d49a42 100644 --- a/ext/http11/http11_parser.h +++ b/ext/http11/http11_parser.h @@ -26,7 +26,8 @@ typedef struct http_parser { element_cb request_uri; element_cb query_string; element_cb http_version; - + element_cb header_done; + } http_parser; int http_parser_init(http_parser *parser); diff --git a/ext/http11/http11_parser.rl b/ext/http11/http11_parser.rl index 1073d9f..8ef4acb 100644 --- a/ext/http11/http11_parser.rl +++ b/ext/http11/http11_parser.rl @@ -44,7 +44,10 @@ parser->http_version(parser->data, parser->mark, p - parser->mark); } action done { - parser->body_start = p+1; fbreak; + parser->body_start = p+1; + if(parser->header_done != NULL) + parser->header_done(parser->data, p, 0); + fbreak; } @@ -92,7 +95,7 @@ message_header = field_name ":" field_value $0 CRLF >1; - Request = Request_Line (message_header)* ( CRLF @done ); + Request = Request_Line (message_header)* ( CRLF @done); main := Request; }%% diff --git a/lib/mongrel.rb b/lib/mongrel.rb index 32f78bd..d5c994d 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -90,59 +90,20 @@ module Mongrel module Const # This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this. PATH_INFO="PATH_INFO" + # This is the intial part that your handler is identified as by URIClassifier. SCRIPT_NAME="SCRIPT_NAME" + # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME. REQUEST_URI='REQUEST_URI' - # Content length (also available as HTTP_CONTENT_LENGTH). - CONTENT_LENGTH='CONTENT_LENGTH' - - # Content length (also available as CONTENT_LENGTH). - HTTP_CONTENT_LENGTH='HTTP_CONTENT_LENGTH' - - # Content type (also available as HTTP_CONTENT_TYPE). - CONTENT_TYPE='CONTENT_TYPE' - - # Content type (also available as CONTENT_TYPE). - HTTP_CONTENT_TYPE='HTTP_CONTENT_TYPE' - - # Gateway interface key in the HttpRequest parameters. - GATEWAY_INTERFACE='GATEWAY_INTERFACE' - # We claim to support CGI/1.2. - GATEWAY_INTERFACE_VALUE='CGI/1.2' - - # Hosts remote IP address. Mongrel does not do DNS resolves since that slows - # processing down considerably. - REMOTE_ADDR='REMOTE_ADDR' - - # This is not given since Mongrel does not do DNS resolves. It is only here for - # completeness for the CGI standard. - REMOTE_HOST='REMOTE_HOST' - - # The name/host of our server as given by the HttpServer.new(host,port) call. - SERVER_NAME='SERVER_NAME' - - # The port of our server as given by the HttpServer.new(host,port) call. - SERVER_PORT='SERVER_PORT' - - # SERVER_NAME and SERVER_PORT come from this. - HTTP_HOST='HTTP_HOST' - - # Official server protocol key in the HttpRequest parameters. - SERVER_PROTOCOL='SERVER_PROTOCOL' - # Mongrel claims to support HTTP/1.1. - SERVER_PROTOCOL_VALUE='HTTP/1.1' - - # The actual server software being used (it's Mongrel man). - SERVER_SOFTWARE='SERVER_SOFTWARE' - - # Current Mongrel version (used for SERVER_SOFTWARE and other response headers). - MONGREL_VERSION='Mongrel 0.3.10' + MONGREL_VERSION="0.3.12" # 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_VERSION}\r\n\r\nNOT FOUND" + CONTENT_LENGTH="CONTENT_LENGTH" + # 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" @@ -172,27 +133,14 @@ module Mongrel @body = initial_body || "" @params = params @socket = socket - - # fix up the CGI requirements - params[Const::CONTENT_LENGTH] = params[Const::HTTP_CONTENT_LENGTH] || 0 - params[Const::CONTENT_TYPE] = params[Const::HTTP_CONTENT_TYPE] if params[Const::HTTP_CONTENT_TYPE] - params[Const::GATEWAY_INTERFACE]=Const::GATEWAY_INTERFACE_VALUE - params[Const::REMOTE_ADDR]=socket.peeraddr[3] - if params.has_key? Const::HTTP_HOST - host,port = params[Const::HTTP_HOST].split(":") - params[Const::SERVER_NAME]=host - params[Const::SERVER_PORT]=port || 80 - end - params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE - params[Const::SERVER_SOFTWARE]=Const::MONGREL_VERSION - # now, if the initial_body isn't long enough for the content length we have to fill it # TODO: adapt for big ass stuff by writing to a temp file - clen = params[Const::HTTP_CONTENT_LENGTH].to_i + clen = params[Const::CONTENT_LENGTH].to_i if @body.length < clen @body << @socket.read(clen - @body.length) end + end end @@ -354,9 +302,11 @@ module Mongrel # create the worker threads num_processors.times do |i| @processors << Thread.new do + parser = HttpParser.new while client = @req_queue.deq Timeout::timeout(timeout) do - process_client(client) + process_client(client, parser) + parser.reset end end end @@ -370,14 +320,15 @@ module Mongrel # the performance just does not improve. It is currently carefully constructed # to make sure that it gets the best possible performance, but anyone who # thinks they can make it faster is more than welcome to take a crack at it. - def process_client(client) + def process_client(client, parser) begin - parser = HttpParser.new params = {} + data = client.readpartial(Const::CHUNK_SIZE) while true nread = parser.execute(params, data) + if parser.finished? script_name, path_info, handler = @classifier.resolve(params[Const::REQUEST_URI]) @@ -385,6 +336,7 @@ module Mongrel params[Const::PATH_INFO] = path_info params[Const::SCRIPT_NAME] = script_name request = HttpRequest.new(params, data[nread ... data.length], client) + response = HttpResponse.new(client) handler.process(request, response) else -- cgit v1.2.3-24-ge0c7