mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
* Moved various constants around. * Created the remaining feasible CGI variables people need. * Now create a REQUEST_URI which other CGI variables derive from. Implemented a simple DirHandler for browsing a directory and sending the files.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@20 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
1b9b3bcb73
commit
788e4f4902
6 changed files with 330 additions and 132 deletions
|
@ -12,5 +12,6 @@ end
|
|||
|
||||
h = Mongrel::HttpServer.new("0.0.0.0", "3000")
|
||||
h.register("/test", SimpleHandler.new)
|
||||
h.register("/files", Mongrel::DirHandler.new("."))
|
||||
h.run.join
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ static int id_handler_map;
|
|||
|
||||
static VALUE global_http_prefix;
|
||||
static VALUE global_request_method;
|
||||
static VALUE global_path_info;
|
||||
static VALUE global_request_uri;
|
||||
static VALUE global_query_string;
|
||||
static VALUE global_http_version;
|
||||
|
||||
|
@ -45,11 +45,11 @@ void request_method(void *data, const char *at, size_t length)
|
|||
rb_hash_aset(req, global_request_method, val);
|
||||
}
|
||||
|
||||
void path_info(void *data, const char *at, size_t length)
|
||||
void request_uri(void *data, const char *at, size_t length)
|
||||
{
|
||||
VALUE req = (VALUE)data;
|
||||
VALUE val = rb_str_new(at, length);
|
||||
rb_hash_aset(req, global_path_info, val);
|
||||
rb_hash_aset(req, global_request_uri, val);
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,7 +87,7 @@ VALUE HttpParser_alloc(VALUE klass)
|
|||
TRACE();
|
||||
hp->http_field = http_field;
|
||||
hp->request_method = request_method;
|
||||
hp->path_info = path_info;
|
||||
hp->request_uri = request_uri;
|
||||
hp->query_string = query_string;
|
||||
hp->http_version = http_version;
|
||||
|
||||
|
@ -279,8 +279,9 @@ VALUE URIClassifier_init(VALUE self)
|
|||
*
|
||||
* Registers the SampleHandler (one for all requests) with the "/someuri".
|
||||
* When URIClassifier::resolve is called with "/someuri" it'll return
|
||||
* SampleHandler immediately. When "/someuri/pathhere" is called it'll
|
||||
* find SomeHandler after a second search, and setup PATH_INFO="/pathhere".
|
||||
* SampleHandler immediately. When called with "/someuri/iwant" it'll also
|
||||
* return SomeHandler immediatly, with no additional searches, but it will
|
||||
* return path info with "/iwant".
|
||||
*
|
||||
* You actually can reuse this class to register nearly anything and
|
||||
* quickly resolve it. This could be used for caching, fast mapping, etc.
|
||||
|
@ -357,7 +358,7 @@ VALUE URIClassifier_unregister(VALUE self, VALUE uri)
|
|||
* It also means that it's very efficient to do this only taking as long as the URI has
|
||||
* characters.
|
||||
*
|
||||
* It expects strings. Don't try other string-line stuff yet.
|
||||
* It expects strings with no embedded '\0' characters. Don't try other string-line stuff yet.
|
||||
*/
|
||||
VALUE URIClassifier_resolve(VALUE self, VALUE uri)
|
||||
{
|
||||
|
@ -401,8 +402,8 @@ void Init_http11()
|
|||
rb_global_variable(&global_http_prefix);
|
||||
global_request_method = rb_str_new2("REQUEST_METHOD");
|
||||
rb_global_variable(&global_request_method);
|
||||
global_path_info = rb_str_new2("PATH_INFO");
|
||||
rb_global_variable(&global_path_info);
|
||||
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");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#line 1 "ext/http11/http11_parser.rl"
|
||||
#line 1 "http11_parser.rl"
|
||||
#include "http11_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
@ -9,28 +9,28 @@
|
|||
#define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
|
||||
|
||||
/** machine **/
|
||||
#line 100 "ext/http11/http11_parser.rl"
|
||||
#line 98 "http11_parser.rl"
|
||||
|
||||
|
||||
/** Data **/
|
||||
|
||||
#line 18 "ext/http11/http11_parser.c"
|
||||
#line 18 "http11_parser.c"
|
||||
static int http_parser_start = 0;
|
||||
|
||||
static int http_parser_first_final = 56;
|
||||
|
||||
static int http_parser_error = 1;
|
||||
|
||||
#line 104 "ext/http11/http11_parser.rl"
|
||||
#line 102 "http11_parser.rl"
|
||||
|
||||
int http_parser_init(http_parser *parser) {
|
||||
int cs = 0;
|
||||
|
||||
#line 30 "ext/http11/http11_parser.c"
|
||||
#line 30 "http11_parser.c"
|
||||
{
|
||||
cs = http_parser_start;
|
||||
}
|
||||
#line 108 "ext/http11/http11_parser.rl"
|
||||
#line 106 "http11_parser.rl"
|
||||
parser->cs = cs;
|
||||
parser->body_start = NULL;
|
||||
parser->content_len = 0;
|
||||
|
@ -50,7 +50,7 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len)
|
|||
pe = buffer+len;
|
||||
|
||||
|
||||
#line 54 "ext/http11/http11_parser.c"
|
||||
#line 54 "http11_parser.c"
|
||||
{
|
||||
p -= 1;
|
||||
if ( ++p == pe )
|
||||
|
@ -70,14 +70,14 @@ case 0:
|
|||
st1:
|
||||
goto _out1;
|
||||
tr13:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st2;
|
||||
st2:
|
||||
if ( ++p == pe )
|
||||
goto _out2;
|
||||
case 2:
|
||||
#line 81 "ext/http11/http11_parser.c"
|
||||
#line 81 "http11_parser.c"
|
||||
if ( (*p) == 69 )
|
||||
goto st3;
|
||||
goto st1;
|
||||
|
@ -117,7 +117,7 @@ case 7:
|
|||
goto tr33;
|
||||
goto st1;
|
||||
tr33:
|
||||
#line 29 "ext/http11/http11_parser.rl"
|
||||
#line 29 "http11_parser.rl"
|
||||
{
|
||||
if(parser->request_method != NULL)
|
||||
parser->request_method(parser->data, parser->mark, p - parser->mark);
|
||||
|
@ -127,7 +127,7 @@ st8:
|
|||
if ( ++p == pe )
|
||||
goto _out8;
|
||||
case 8:
|
||||
#line 131 "ext/http11/http11_parser.c"
|
||||
#line 131 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 42: goto tr27;
|
||||
case 43: goto tr28;
|
||||
|
@ -144,26 +144,26 @@ case 8:
|
|||
goto tr28;
|
||||
goto st1;
|
||||
tr27:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st9;
|
||||
st9:
|
||||
if ( ++p == pe )
|
||||
goto _out9;
|
||||
case 9:
|
||||
#line 155 "ext/http11/http11_parser.c"
|
||||
#line 155 "http11_parser.c"
|
||||
if ( (*p) == 32 )
|
||||
goto tr34;
|
||||
goto st1;
|
||||
tr34:
|
||||
#line 33 "ext/http11/http11_parser.rl"
|
||||
#line 33 "http11_parser.rl"
|
||||
{
|
||||
if(parser->path_info != NULL)
|
||||
parser->path_info(parser->data, parser->mark, p - parser->mark);
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
goto st10;
|
||||
tr48:
|
||||
#line 37 "ext/http11/http11_parser.rl"
|
||||
#line 37 "http11_parser.rl"
|
||||
{
|
||||
if(parser->query_string != NULL)
|
||||
parser->query_string(parser->data, parser->mark, p - parser->mark);
|
||||
|
@ -173,19 +173,19 @@ st10:
|
|||
if ( ++p == pe )
|
||||
goto _out10;
|
||||
case 10:
|
||||
#line 177 "ext/http11/http11_parser.c"
|
||||
#line 177 "http11_parser.c"
|
||||
if ( (*p) == 72 )
|
||||
goto tr11;
|
||||
goto st1;
|
||||
tr11:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st11;
|
||||
st11:
|
||||
if ( ++p == pe )
|
||||
goto _out11;
|
||||
case 11:
|
||||
#line 189 "ext/http11/http11_parser.c"
|
||||
#line 189 "http11_parser.c"
|
||||
if ( (*p) == 84 )
|
||||
goto st12;
|
||||
goto st1;
|
||||
|
@ -243,7 +243,7 @@ case 18:
|
|||
goto st18;
|
||||
goto st1;
|
||||
tr37:
|
||||
#line 42 "ext/http11/http11_parser.rl"
|
||||
#line 42 "http11_parser.rl"
|
||||
{
|
||||
if(parser->http_version != NULL)
|
||||
parser->http_version(parser->data, parser->mark, p - parser->mark);
|
||||
|
@ -253,7 +253,7 @@ st19:
|
|||
if ( ++p == pe )
|
||||
goto _out19;
|
||||
case 19:
|
||||
#line 257 "ext/http11/http11_parser.c"
|
||||
#line 257 "http11_parser.c"
|
||||
if ( (*p) == 10 )
|
||||
goto st20;
|
||||
goto st1;
|
||||
|
@ -293,7 +293,7 @@ case 21:
|
|||
goto tr40;
|
||||
goto st1;
|
||||
tr40:
|
||||
#line 46 "ext/http11/http11_parser.rl"
|
||||
#line 46 "http11_parser.rl"
|
||||
{
|
||||
parser->body_start = p+1; goto _out56;
|
||||
}
|
||||
|
@ -302,17 +302,17 @@ st56:
|
|||
if ( ++p == pe )
|
||||
goto _out56;
|
||||
case 56:
|
||||
#line 306 "ext/http11/http11_parser.c"
|
||||
#line 306 "http11_parser.c"
|
||||
goto st1;
|
||||
tr36:
|
||||
#line 16 "ext/http11/http11_parser.rl"
|
||||
#line 16 "http11_parser.rl"
|
||||
{ parser->field_start = p; }
|
||||
goto st22;
|
||||
st22:
|
||||
if ( ++p == pe )
|
||||
goto _out22;
|
||||
case 22:
|
||||
#line 316 "ext/http11/http11_parser.c"
|
||||
#line 316 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 33: goto st22;
|
||||
case 58: goto tr32;
|
||||
|
@ -338,7 +338,7 @@ case 22:
|
|||
goto st22;
|
||||
goto st1;
|
||||
tr32:
|
||||
#line 17 "ext/http11/http11_parser.rl"
|
||||
#line 17 "http11_parser.rl"
|
||||
{
|
||||
parser->field_len = (p - parser->field_start);
|
||||
}
|
||||
|
@ -347,24 +347,24 @@ st23:
|
|||
if ( ++p == pe )
|
||||
goto _out23;
|
||||
case 23:
|
||||
#line 351 "ext/http11/http11_parser.c"
|
||||
#line 351 "http11_parser.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr56;
|
||||
goto tr55;
|
||||
tr55:
|
||||
#line 21 "ext/http11/http11_parser.rl"
|
||||
#line 21 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st24;
|
||||
st24:
|
||||
if ( ++p == pe )
|
||||
goto _out24;
|
||||
case 24:
|
||||
#line 363 "ext/http11/http11_parser.c"
|
||||
#line 363 "http11_parser.c"
|
||||
if ( (*p) == 13 )
|
||||
goto tr51;
|
||||
goto st24;
|
||||
tr51:
|
||||
#line 22 "ext/http11/http11_parser.rl"
|
||||
#line 22 "http11_parser.rl"
|
||||
{
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data,
|
||||
|
@ -374,9 +374,9 @@ tr51:
|
|||
}
|
||||
goto st25;
|
||||
tr56:
|
||||
#line 21 "ext/http11/http11_parser.rl"
|
||||
#line 21 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
#line 22 "ext/http11/http11_parser.rl"
|
||||
#line 22 "http11_parser.rl"
|
||||
{
|
||||
if(parser->http_field != NULL) {
|
||||
parser->http_field(parser->data,
|
||||
|
@ -389,7 +389,7 @@ st25:
|
|||
if ( ++p == pe )
|
||||
goto _out25;
|
||||
case 25:
|
||||
#line 393 "ext/http11/http11_parser.c"
|
||||
#line 393 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 10: goto st26;
|
||||
case 13: goto tr51;
|
||||
|
@ -424,14 +424,14 @@ case 26:
|
|||
goto tr42;
|
||||
goto st24;
|
||||
tr42:
|
||||
#line 16 "ext/http11/http11_parser.rl"
|
||||
#line 16 "http11_parser.rl"
|
||||
{ parser->field_start = p; }
|
||||
goto st27;
|
||||
st27:
|
||||
if ( ++p == pe )
|
||||
goto _out27;
|
||||
case 27:
|
||||
#line 435 "ext/http11/http11_parser.c"
|
||||
#line 435 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 13: goto tr51;
|
||||
case 33: goto st27;
|
||||
|
@ -458,14 +458,14 @@ case 27:
|
|||
goto st27;
|
||||
goto st24;
|
||||
tr28:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st28;
|
||||
st28:
|
||||
if ( ++p == pe )
|
||||
goto _out28;
|
||||
case 28:
|
||||
#line 469 "ext/http11/http11_parser.c"
|
||||
#line 469 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 43: goto st28;
|
||||
case 58: goto st29;
|
||||
|
@ -483,14 +483,14 @@ case 28:
|
|||
goto st28;
|
||||
goto st1;
|
||||
tr30:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st29;
|
||||
st29:
|
||||
if ( ++p == pe )
|
||||
goto _out29;
|
||||
case 29:
|
||||
#line 494 "ext/http11/http11_parser.c"
|
||||
#line 494 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr34;
|
||||
case 37: goto st30;
|
||||
|
@ -531,14 +531,14 @@ case 31:
|
|||
goto st29;
|
||||
goto st1;
|
||||
tr29:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st32;
|
||||
st32:
|
||||
if ( ++p == pe )
|
||||
goto _out32;
|
||||
case 32:
|
||||
#line 542 "ext/http11/http11_parser.c"
|
||||
#line 542 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr34;
|
||||
case 37: goto st34;
|
||||
|
@ -599,17 +599,17 @@ case 35:
|
|||
goto st33;
|
||||
goto st1;
|
||||
tr46:
|
||||
#line 33 "ext/http11/http11_parser.rl"
|
||||
#line 33 "http11_parser.rl"
|
||||
{
|
||||
if(parser->path_info != NULL)
|
||||
parser->path_info(parser->data, parser->mark, p - parser->mark);
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
goto st36;
|
||||
st36:
|
||||
if ( ++p == pe )
|
||||
goto _out36;
|
||||
case 36:
|
||||
#line 613 "ext/http11/http11_parser.c"
|
||||
#line 613 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr48;
|
||||
case 37: goto tr54;
|
||||
|
@ -624,14 +624,14 @@ case 36:
|
|||
goto st1;
|
||||
goto tr53;
|
||||
tr53:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st37;
|
||||
st37:
|
||||
if ( ++p == pe )
|
||||
goto _out37;
|
||||
case 37:
|
||||
#line 635 "ext/http11/http11_parser.c"
|
||||
#line 635 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 32: goto tr48;
|
||||
case 37: goto st38;
|
||||
|
@ -646,14 +646,14 @@ case 37:
|
|||
goto st1;
|
||||
goto st37;
|
||||
tr54:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st38;
|
||||
st38:
|
||||
if ( ++p == pe )
|
||||
goto _out38;
|
||||
case 38:
|
||||
#line 657 "ext/http11/http11_parser.c"
|
||||
#line 657 "http11_parser.c"
|
||||
if ( (*p) < 65 ) {
|
||||
if ( 48 <= (*p) && (*p) <= 57 )
|
||||
goto st39;
|
||||
|
@ -677,14 +677,14 @@ case 39:
|
|||
goto st37;
|
||||
goto st1;
|
||||
tr14:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st40;
|
||||
st40:
|
||||
if ( ++p == pe )
|
||||
goto _out40;
|
||||
case 40:
|
||||
#line 688 "ext/http11/http11_parser.c"
|
||||
#line 688 "http11_parser.c"
|
||||
if ( (*p) == 69 )
|
||||
goto st41;
|
||||
goto st1;
|
||||
|
@ -696,14 +696,14 @@ case 41:
|
|||
goto st7;
|
||||
goto st1;
|
||||
tr15:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st42;
|
||||
st42:
|
||||
if ( ++p == pe )
|
||||
goto _out42;
|
||||
case 42:
|
||||
#line 707 "ext/http11/http11_parser.c"
|
||||
#line 707 "http11_parser.c"
|
||||
if ( (*p) == 69 )
|
||||
goto st43;
|
||||
goto st1;
|
||||
|
@ -722,14 +722,14 @@ case 44:
|
|||
goto st7;
|
||||
goto st1;
|
||||
tr16:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st45;
|
||||
st45:
|
||||
if ( ++p == pe )
|
||||
goto _out45;
|
||||
case 45:
|
||||
#line 733 "ext/http11/http11_parser.c"
|
||||
#line 733 "http11_parser.c"
|
||||
if ( (*p) == 80 )
|
||||
goto st46;
|
||||
goto st1;
|
||||
|
@ -769,14 +769,14 @@ case 50:
|
|||
goto st7;
|
||||
goto st1;
|
||||
tr17:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st51;
|
||||
st51:
|
||||
if ( ++p == pe )
|
||||
goto _out51;
|
||||
case 51:
|
||||
#line 780 "ext/http11/http11_parser.c"
|
||||
#line 780 "http11_parser.c"
|
||||
switch( (*p) ) {
|
||||
case 79: goto st52;
|
||||
case 85: goto st41;
|
||||
|
@ -790,14 +790,14 @@ case 52:
|
|||
goto st41;
|
||||
goto st1;
|
||||
tr18:
|
||||
#line 14 "ext/http11/http11_parser.rl"
|
||||
#line 14 "http11_parser.rl"
|
||||
{ MARK(parser, p); }
|
||||
goto st53;
|
||||
st53:
|
||||
if ( ++p == pe )
|
||||
goto _out53;
|
||||
case 53:
|
||||
#line 801 "ext/http11/http11_parser.c"
|
||||
#line 801 "http11_parser.c"
|
||||
if ( (*p) == 82 )
|
||||
goto st54;
|
||||
goto st1;
|
||||
|
@ -875,15 +875,15 @@ case 55:
|
|||
|
||||
_out: {}
|
||||
}
|
||||
#line 127 "ext/http11/http11_parser.rl"
|
||||
#line 125 "http11_parser.rl"
|
||||
|
||||
parser->cs = cs;
|
||||
parser->nread = p - buffer;
|
||||
if(parser->body_start) {
|
||||
/* final \r\n combo encountered so stop right here */
|
||||
|
||||
#line 886 "ext/http11/http11_parser.c"
|
||||
#line 133 "ext/http11/http11_parser.rl"
|
||||
#line 886 "http11_parser.c"
|
||||
#line 131 "http11_parser.rl"
|
||||
parser->nread++;
|
||||
}
|
||||
|
||||
|
@ -895,8 +895,8 @@ int http_parser_finish(http_parser *parser)
|
|||
int cs = parser->cs;
|
||||
|
||||
|
||||
#line 899 "ext/http11/http11_parser.c"
|
||||
#line 144 "ext/http11/http11_parser.rl"
|
||||
#line 899 "http11_parser.c"
|
||||
#line 142 "http11_parser.rl"
|
||||
|
||||
parser->cs = cs;
|
||||
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
||||
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
||||
|
||||
|
@ -19,7 +23,7 @@ typedef struct http_parser {
|
|||
|
||||
field_cb http_field;
|
||||
element_cb request_method;
|
||||
element_cb path_info;
|
||||
element_cb request_uri;
|
||||
element_cb query_string;
|
||||
element_cb http_version;
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
if(parser->request_method != NULL)
|
||||
parser->request_method(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
action path_info {
|
||||
if(parser->path_info != NULL)
|
||||
parser->path_info(parser->data, parser->mark, p - parser->mark);
|
||||
action request_uri {
|
||||
if(parser->request_uri != NULL)
|
||||
parser->request_uri(parser->data, parser->mark, p - parser->mark);
|
||||
}
|
||||
action query_string {
|
||||
if(parser->query_string != NULL)
|
||||
|
@ -70,23 +70,21 @@
|
|||
|
||||
# URI schemes and absolute paths
|
||||
scheme = ( alpha | digit | "+" | "-" | "." )* ;
|
||||
absolute_uri = (scheme ":" (uchar | reserved )*) >mark %path_info;
|
||||
absolute_uri = (scheme ":" (uchar | reserved )*) >mark %request_uri;
|
||||
|
||||
path = (pchar+ ( "/" pchar* )*) ;
|
||||
query = ( uchar | reserved )* >mark %query_string ;
|
||||
param = ( pchar | "/" )* ;
|
||||
params = (param ( ";" param )*) ;
|
||||
rel_path = (path? (";" params)?) %path_info ("?" query)? ;
|
||||
rel_path = (path? (";" params)?) %request_uri ("?" query)? ;
|
||||
absolute_path = ("/" rel_path) >mark ;
|
||||
|
||||
Request_URI = ("*" >mark %path_info | absolute_uri | absolute_path) ;
|
||||
Request_URI = ("*" >mark %request_uri | absolute_uri | absolute_path) ;
|
||||
Method = ("OPTIONS"| "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE") >mark %request_method;
|
||||
|
||||
http_number = (digit+ "." digit+) ;
|
||||
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
||||
Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ;
|
||||
|
||||
|
||||
|
||||
field_name = (token - ":")+ >start_field %write_field;
|
||||
|
||||
|
@ -96,7 +94,7 @@
|
|||
|
||||
Request = Request_Line (message_header)* $0 ( CRLF $1 @done );
|
||||
|
||||
main := Request;
|
||||
main := Request;
|
||||
}%%
|
||||
|
||||
/** Data **/
|
||||
|
|
290
lib/mongrel.rb
290
lib/mongrel.rb
|
@ -8,6 +8,9 @@ require 'stringio'
|
|||
# functionality to service web application requests fast as possible.
|
||||
module Mongrel
|
||||
|
||||
# Every standard HTTP code mapped to the appropriate message. These are
|
||||
# used so frequently that they are placed directly in Mongrel for easy
|
||||
# access rather than Mongrel::Const.
|
||||
HTTP_STATUS_CODES = {
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
|
@ -48,19 +51,89 @@ module Mongrel
|
|||
505 => 'HTTP Version not supported'
|
||||
}
|
||||
|
||||
# Frequently used constants when constructing requests or responses. Many times
|
||||
# the constant just refers to a string with the same contents. Using these constants
|
||||
# gave about a 3% to 10% performance improvement over using the strings directly.
|
||||
# Symbols did not really improve things much compared to constants.
|
||||
#
|
||||
# While Mongrel does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
|
||||
# REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
|
||||
# too taxing on performance.
|
||||
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'
|
||||
|
||||
# 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.2.2'
|
||||
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
|
||||
# The basic max request size we'll try to read.
|
||||
CHUNK_SIZE=(16 * 1024)
|
||||
|
||||
end
|
||||
|
||||
|
||||
# When a handler is found for a registered URI then this class is constructed
|
||||
# and passed to your HttpHandler::process method. You should assume that
|
||||
# *one* handler processes all requests. Included in the HttpReqeust is a
|
||||
# HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
|
||||
# which is a string containing the request body (raw for now).
|
||||
#
|
||||
# Mongrel really only support small-ish request bodies right now since really
|
||||
# Mongrel really only supports small-ish request bodies right now since really
|
||||
# huge ones have to be completely read off the wire and put into a string.
|
||||
# Later there will be several options for efficiently handling large file
|
||||
# uploads.
|
||||
class HttpRequest
|
||||
attr_reader :body, :params
|
||||
|
||||
|
||||
# You don't really call this. It's made for you.
|
||||
# Main thing it does is hook up the params, and store any remaining
|
||||
# body data into the HttpRequest.body attribute.
|
||||
|
@ -68,13 +141,14 @@ module Mongrel
|
|||
@body = initial_body || ""
|
||||
@params = params
|
||||
@socket = socket
|
||||
|
||||
|
||||
# fix up the CGI requirements
|
||||
params['CONTENT_LENGTH'] = params['HTTP_CONTENT_LENGTH'] || 0
|
||||
params[Const::CONTENT_LENGTH] = params[Const::HTTP_CONTENT_LENGTH] || 0
|
||||
params[Const::CONTENT_TYPE] ||= params[Const::HTTP_CONTENT_TYPE]
|
||||
|
||||
# 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['HTTP_CONTENT_LENGTH'].to_i
|
||||
clen = params[Const::HTTP_CONTENT_LENGTH].to_i
|
||||
if @body.length < clen
|
||||
@body << @socket.read(clen - @body.length)
|
||||
end
|
||||
|
@ -82,6 +156,13 @@ module Mongrel
|
|||
end
|
||||
|
||||
|
||||
# This class implements a simple way of constructing the HTTP headers dynamically
|
||||
# via a Hash syntax. Think of it as a write-only Hash. Refer to HttpResponse for
|
||||
# information on how this is used.
|
||||
#
|
||||
# One consequence of this write-only nature is that you can write multiple headers
|
||||
# by just doing them twice (which is sometimes needed in HTTP), but that the normal
|
||||
# semantics for Hash (where doing an insert replaces) is not there.
|
||||
class HeaderOut
|
||||
attr_reader :out
|
||||
|
||||
|
@ -89,6 +170,7 @@ module Mongrel
|
|||
@out = out
|
||||
end
|
||||
|
||||
# Simply writes "#{key}: #{value}" to an output buffer.
|
||||
def[]=(key,value)
|
||||
@out.write(key)
|
||||
@out.write(": ")
|
||||
|
@ -97,7 +179,35 @@ module Mongrel
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Writes and controls your response to the client using the HTTP/1.1 specification.
|
||||
# You use it by simply doing:
|
||||
#
|
||||
# response.start(200) do |head,out|
|
||||
# head['Content-Type'] = 'text/plain'
|
||||
# out.write("hello\n")
|
||||
# end
|
||||
#
|
||||
# The parameter to start is the response code--which Mongrel will translate for you
|
||||
# based on HTTP_STATUS_CODES. The head parameter is how you write custom headers.
|
||||
# The out parameter is where you write your body. The default status code for
|
||||
# HttpResponse.start is 200 so the above example is redundant.
|
||||
#
|
||||
# As you can see, it's just like using a Hash and as you do this it writes the proper
|
||||
# header to the output on the fly. You can even intermix specifying headers and
|
||||
# writing content. The HttpResponse class with write the things in the proper order
|
||||
# once the HttpResponse.block is ended.
|
||||
#
|
||||
# You may also work the HttpResponse object directly using the various attributes available
|
||||
# for the raw socket, body, header, and status codes. If you do this you're on your own.
|
||||
# A design decision was made to force the client to not pipeline requests. HTTP/1.1
|
||||
# pipelining really kills the performance due to how it has to be handled and how
|
||||
# unclear the standard is. To fix this the HttpResponse gives a "Connection: close"
|
||||
# header which forces the client to close right away. The bonus for this is that it
|
||||
# gives a pretty nice speed boost to most clients since they can close their connection
|
||||
# immediately.
|
||||
#
|
||||
# One additional caveat is that you don't have to specify the Content-length header
|
||||
# as the HttpResponse will write this for you based on the out length.
|
||||
class HttpResponse
|
||||
attr_reader :socket
|
||||
attr_reader :body
|
||||
|
@ -112,12 +222,25 @@ module Mongrel
|
|||
@header = HeaderOut.new(StringIO.new)
|
||||
end
|
||||
|
||||
# Receives a block passing it the header and body for you to work with.
|
||||
# When the block is finished it writes everything you've done to
|
||||
# the socket in the proper order. This lets you intermix header and
|
||||
# body content as needed.
|
||||
def start(status=200)
|
||||
@status = status
|
||||
yield @header, @body
|
||||
finished
|
||||
end
|
||||
|
||||
|
||||
# Primarily used in exception handling to reset the response output in order to write
|
||||
# an alternative response.
|
||||
def reset
|
||||
@header.out.rewind
|
||||
@body.rewind
|
||||
end
|
||||
|
||||
# This takes whatever has been done to header and body and then writes it in the
|
||||
# proper format to make an HTTP/1.1 response.
|
||||
def finished
|
||||
@header.out.rewind
|
||||
@body.rewind
|
||||
|
@ -136,32 +259,11 @@ module Mongrel
|
|||
# a response. Look at the HttpRequest and HttpResponse objects for how
|
||||
# to use them.
|
||||
class HttpHandler
|
||||
attr_accessor :script_name
|
||||
|
||||
def process(request, response)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The server normally returns a 404 response if a URI is requested, but it
|
||||
# also returns a lame empty message. This lets you do a 404 response
|
||||
# with a custom message for special URIs.
|
||||
class Error404Handler < HttpHandler
|
||||
|
||||
# Sets the message to return. This is constructed once for the handler
|
||||
# so it's pretty efficient.
|
||||
def initialize(msg)
|
||||
@response = HttpServer::ERROR_404_RESPONSE + msg
|
||||
end
|
||||
|
||||
# Just kicks back the standard 404 response with your special message.
|
||||
def process(request, response)
|
||||
response.socket.write(@response)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier
|
||||
# make up the majority of how the server functions. It's a very simple class that just
|
||||
# has a thread accepting connections and a simple HttpServer.process_client function
|
||||
|
@ -183,16 +285,6 @@ module Mongrel
|
|||
class HttpServer
|
||||
attr_reader :acceptor
|
||||
|
||||
# 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/0.2\r\n\r\nNOT FOUND"
|
||||
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
|
||||
|
||||
# The basic max request size we'll try to read.
|
||||
CHUNK_SIZE=(16 * 1024)
|
||||
|
||||
PATH_INFO="PATH_INFO"
|
||||
SCRIPT_NAME="SCRIPT_NAME"
|
||||
|
||||
# Creates a working server on host:port (strange things happen if port isn't a Number).
|
||||
# Use HttpServer::run to start the server.
|
||||
#
|
||||
|
@ -210,8 +302,13 @@ module Mongrel
|
|||
# Future versions of Mongrel will make this more dynamic (hopefully).
|
||||
def initialize(host, port, num_processors=20)
|
||||
@socket = TCPServer.new(host, port)
|
||||
|
||||
@classifier = URIClassifier.new
|
||||
@req_queue = Queue.new
|
||||
@host = host
|
||||
@port = port
|
||||
@num_procesors = num_processors
|
||||
|
||||
num_processors.times {|i| Thread.new do
|
||||
while client = @req_queue.deq
|
||||
process_client(client)
|
||||
|
@ -223,30 +320,35 @@ module Mongrel
|
|||
|
||||
# Does the majority of the IO processing. It has been written in Ruby using
|
||||
# about 7 different IO processing strategies and no matter how it's done
|
||||
# the performance just does not improve. Ruby's use of select to implement
|
||||
# threads means that it will most likely never improve, so the only remaining
|
||||
# approach is to write all or some of this function in C. That will be the
|
||||
# focus of future releases.
|
||||
# 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)
|
||||
begin
|
||||
parser = HttpParser.new
|
||||
params = {}
|
||||
data = client.readpartial(CHUNK_SIZE)
|
||||
data = client.readpartial(Const::CHUNK_SIZE)
|
||||
|
||||
while true
|
||||
nread = parser.execute(params, data)
|
||||
if parser.finished?
|
||||
script_name, path_info, handler = @classifier.resolve(params[PATH_INFO])
|
||||
script_name, path_info, handler = @classifier.resolve(params[Const::REQUEST_URI])
|
||||
|
||||
if handler
|
||||
params[PATH_INFO] = path_info
|
||||
params[SCRIPT_NAME] = script_name
|
||||
params[Const::PATH_INFO] = path_info
|
||||
params[Const::SCRIPT_NAME] = script_name
|
||||
params[Const::GATEWAY_INTERFACE]=Const::GATEWAY_INTERFACE_VALUE
|
||||
params[Const::REMOTE_ADDR]=client.peeraddr
|
||||
params[Const::SERVER_NAME]=@host
|
||||
params[Const::SERVER_PORT]=@port
|
||||
params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE
|
||||
params[Const::SERVER_SOFTWARE]=Const::MONGREL_VERSION
|
||||
|
||||
request = HttpRequest.new(params, data[nread ... data.length], client)
|
||||
response = HttpResponse.new(client)
|
||||
handler.process(request, response)
|
||||
else
|
||||
client.write(ERROR_404_RESPONSE)
|
||||
client.write(Const::ERROR_404_RESPONSE)
|
||||
end
|
||||
|
||||
break
|
||||
|
@ -254,7 +356,7 @@ module Mongrel
|
|||
# gotta stream and read again until we can get the parser to be character safe
|
||||
# TODO: make this more efficient since this means we're parsing a lot repeatedly
|
||||
parser.reset
|
||||
data << client.readpartial(CHUNK_SIZE)
|
||||
data << client.readpartial(Const::CHUNK_SIZE)
|
||||
end
|
||||
end
|
||||
rescue EOFError
|
||||
|
@ -274,6 +376,7 @@ module Mongrel
|
|||
# Runs the thing. It returns the thread used so you can "join" it. You can also
|
||||
# access the HttpServer::acceptor attribute to get the thread later.
|
||||
def run
|
||||
BasicSocket.do_not_reverse_lookup=true
|
||||
@acceptor = Thread.new do
|
||||
while true
|
||||
@req_queue << @socket.accept
|
||||
|
@ -295,4 +398,95 @@ module Mongrel
|
|||
@classifier.unregister(uri)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The server normally returns a 404 response if a URI is requested, but it
|
||||
# also returns a lame empty message. This lets you do a 404 response
|
||||
# with a custom message for special URIs.
|
||||
class Error404Handler < HttpHandler
|
||||
|
||||
# Sets the message to return. This is constructed once for the handler
|
||||
# so it's pretty efficient.
|
||||
def initialize(msg)
|
||||
@response = HttpServer::ERROR_404_RESPONSE + msg
|
||||
end
|
||||
|
||||
# Just kicks back the standard 404 response with your special message.
|
||||
def process(request, response)
|
||||
response.socket.write(@response)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# Serves the contents of a directory. You give it the path to the root
|
||||
# where the files are located, and it tries to find the files based on
|
||||
# the PATH_INFO inside the directory. If the requested path is a
|
||||
# directory then it returns a simple directory listing.
|
||||
#
|
||||
# It does a simple protection against going outside it's root path by
|
||||
# converting all paths to an absolute expanded path, and then making sure
|
||||
# that the final expanded path includes the root path. If it doesn't
|
||||
# than it simply gives a 404.
|
||||
class DirHandler < HttpHandler
|
||||
|
||||
def initialize(path, listing_allowed=true)
|
||||
@path = File.expand_path(path)
|
||||
@listing_allowed=listing_allowed
|
||||
puts "DIR: #@path"
|
||||
end
|
||||
|
||||
def send_dir_listing(base, dir, response)
|
||||
if @listing_allowed
|
||||
response.start(200) do |head,out|
|
||||
head['Content-Type'] = "text/html"
|
||||
out << "<html><head><title>Directory Listing</title></head><body>"
|
||||
Dir.entries(dir).each do |child|
|
||||
out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>"
|
||||
end
|
||||
out << "</body></html>"
|
||||
end
|
||||
else
|
||||
response.start(403) do |head,out|
|
||||
out.write("Directory listings not allowed")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def send_file(req, response)
|
||||
response.start(200) do |head,out|
|
||||
open(req, "r") do |f|
|
||||
out.write(f.read)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def process(request, response)
|
||||
req = File.expand_path("." + request.params['PATH_INFO'], @path)
|
||||
puts "FIND: #{req}"
|
||||
if req.index(@path) != 0 or !File.exist? req
|
||||
# not found, return a 404
|
||||
response.start(404) do |head,out|
|
||||
out << "File not found"
|
||||
end
|
||||
else
|
||||
begin
|
||||
if File.directory? req
|
||||
send_dir_listing(request.params["REQUEST_URI"],req, response)
|
||||
else
|
||||
send_file(req, response)
|
||||
end
|
||||
rescue => details
|
||||
response.reset
|
||||
response.start(403) do |head,out|
|
||||
out << "Error accessing file"
|
||||
end
|
||||
STDERR.puts "ERROR: #{details}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue