mongrel 1.0.5 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mongrel might be problematic. Click here for more details.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +1 -2
- data/LICENSE +2 -2
- data/Manifest +11 -10
- data/README +27 -39
- data/TODO +5 -0
- data/bin/mongrel_rails +1 -1
- data/ext/http11/http11.c +1 -195
- data/ext/http11_java/Http11Service.java +13 -0
- data/ext/http11_java/org/jruby/mongrel/Http11.java +266 -0
- data/ext/http11_java/org/jruby/mongrel/Http11Parser.java +572 -0
- data/lib/mongrel.rb +75 -545
- data/lib/mongrel/command.rb +3 -2
- data/lib/mongrel/configurator.rb +1 -1
- data/lib/mongrel/const.rb +110 -0
- data/lib/mongrel/gems.rb +23 -0
- data/lib/mongrel/handlers.rb +3 -7
- data/lib/mongrel/header_out.rb +28 -0
- data/lib/mongrel/http_request.rb +155 -0
- data/lib/mongrel/http_response.rb +163 -0
- data/lib/mongrel/init.rb +3 -6
- data/lib/mongrel/uri_classifier.rb +76 -0
- data/mongrel.gemspec +263 -258
- data/test/test_configurator.rb +1 -0
- data/test/test_redirect_handler.rb +3 -1
- data/test/test_request_progress.rb +3 -1
- data/test/test_uriclassifier.rb +1 -1
- data/test/test_ws.rb +11 -6
- data/test/testhelp.rb +1 -0
- metadata +67 -34
- metadata.gz.sig +1 -1
- data/ext/http11/MANIFEST +0 -0
- data/ext/http11/tst.h +0 -40
- data/ext/http11/tst_cleanup.c +0 -23
- data/ext/http11/tst_delete.c +0 -146
- data/ext/http11/tst_grow_node_free_list.c +0 -38
- data/ext/http11/tst_init.c +0 -41
- data/ext/http11/tst_insert.c +0 -218
- data/ext/http11/tst_search.c +0 -73
- data/lib/mutex_fix.rb +0 -34
- data/test/jruby_socket.rb +0 -39
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
v1.0.5. Fix security flaw of DirHandler reported on mailing list.
|
2
1
|
|
3
|
-
v1.
|
2
|
+
v1.1. Pure Ruby URIClassifier. More modular architecture. JRuby support. Move C URIClassifier into mongrel_experimental project.
|
4
3
|
|
5
4
|
v1.0.3. Fix user-switching bug; make people upgrade to the latest from the RC.
|
6
5
|
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
|
2
|
-
<zedshaw at zedshaw dot com> You can redistribute it
|
3
|
-
either the terms of the
|
2
|
+
<zedshaw at zedshaw dot com> and contributors. You can redistribute it
|
3
|
+
and/or modify it under either the terms of the GPL2 or the conditions below:
|
4
4
|
|
5
5
|
1. You may make and give away verbatim copies of the source form of the
|
6
6
|
software without restriction, provided that you duplicate all of the
|
data/Manifest
CHANGED
@@ -22,33 +22,33 @@ ext/http11/http11_parser.h
|
|
22
22
|
ext/http11/http11_parser.java.rl
|
23
23
|
ext/http11/http11_parser.rl
|
24
24
|
ext/http11/http11_parser_common.rl
|
25
|
-
ext/
|
26
|
-
ext/
|
27
|
-
ext/
|
28
|
-
ext/http11/tst_delete.c
|
29
|
-
ext/http11/tst_grow_node_free_list.c
|
30
|
-
ext/http11/tst_init.c
|
31
|
-
ext/http11/tst_insert.c
|
32
|
-
ext/http11/tst_search.c
|
25
|
+
ext/http11_java/Http11Service.java
|
26
|
+
ext/http11_java/org/jruby/mongrel/Http11.java
|
27
|
+
ext/http11_java/org/jruby/mongrel/Http11Parser.java
|
33
28
|
lib/mongrel/camping.rb
|
34
29
|
lib/mongrel/cgi.rb
|
35
30
|
lib/mongrel/command.rb
|
36
31
|
lib/mongrel/configurator.rb
|
32
|
+
lib/mongrel/const.rb
|
37
33
|
lib/mongrel/debug.rb
|
34
|
+
lib/mongrel/gems.rb
|
38
35
|
lib/mongrel/handlers.rb
|
36
|
+
lib/mongrel/header_out.rb
|
37
|
+
lib/mongrel/http_request.rb
|
38
|
+
lib/mongrel/http_response.rb
|
39
39
|
lib/mongrel/init.rb
|
40
40
|
lib/mongrel/mime_types.yml
|
41
41
|
lib/mongrel/rails.rb
|
42
42
|
lib/mongrel/stats.rb
|
43
43
|
lib/mongrel/tcphack.rb
|
44
|
+
lib/mongrel/uri_classifier.rb
|
44
45
|
lib/mongrel.rb
|
45
|
-
lib/mutex_fix.rb
|
46
46
|
LICENSE
|
47
47
|
Manifest
|
48
48
|
mongrel-public_cert.pem
|
49
|
+
mongrel.gemspec
|
49
50
|
README
|
50
51
|
setup.rb
|
51
|
-
test/jruby_socket.rb
|
52
52
|
test/mime.yaml
|
53
53
|
test/mongrel.conf
|
54
54
|
test/test_cgi_wrapper.rb
|
@@ -65,4 +65,5 @@ test/test_stats.rb
|
|
65
65
|
test/test_uriclassifier.rb
|
66
66
|
test/test_ws.rb
|
67
67
|
test/testhelp.rb
|
68
|
+
TODO
|
68
69
|
tools/trickletest.rb
|
data/README
CHANGED
@@ -1,60 +1,53 @@
|
|
1
1
|
= Mongrel: Simple Fast Mostly Ruby Web Server
|
2
2
|
|
3
|
-
Mongrel is a small library that provides a very fast HTTP 1.1 server for Ruby
|
4
|
-
web applications. It is not particular to any framework, and is intended to
|
5
|
-
be just enough to get a web application running behind a more complete and robust
|
6
|
-
web server.
|
3
|
+
Mongrel is a small library that provides a very fast HTTP 1.1 server for Ruby web applications. It is not particular to any framework, and is intended to be just enough to get a web application running behind a more complete and robust web server.
|
7
4
|
|
8
|
-
What makes Mongrel so fast is the careful use of
|
9
|
-
HTTP 1.1 protocol parsing and fast URI lookup. This combination makes the server
|
10
|
-
scream without too many portability issues.
|
5
|
+
What makes Mongrel so fast is the careful use of an Ragel extension to provide fast, accurate HTTP 1.1 protocol parsing. This makes the server scream without too many portability issues.
|
11
6
|
|
12
|
-
|
7
|
+
See http://mongrel.rubyforge.org for more information.
|
8
|
+
|
9
|
+
== License
|
10
|
+
|
11
|
+
Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed under the Ruby license and the GPL2. See the include LICENSE file for details.
|
13
12
|
|
14
13
|
== Quick Start
|
15
14
|
|
16
|
-
|
17
|
-
have the mongrel_rails command available in your PATH. Then you just do the following:
|
15
|
+
The easiest way to get started with Mongrel is to install it via RubyGems and then run a Ruby on Rails application. You can do this easily:
|
18
16
|
|
19
|
-
|
20
|
-
> mongrel_rails start
|
17
|
+
$ gem install mongrel
|
21
18
|
|
22
|
-
|
23
|
-
in production mode. To get help do:
|
19
|
+
Now you should have the mongrel_rails command available in your PATH, so just do the following:
|
24
20
|
|
25
|
-
|
21
|
+
$ cd myrailsapp
|
22
|
+
$ mongrel_rails start
|
26
23
|
|
27
|
-
|
24
|
+
This will start it in the foreground so you can play with it. It runs your application in production mode. To get help do:
|
28
25
|
|
29
|
-
|
26
|
+
$ mongrel_rails start -h
|
30
27
|
|
31
|
-
|
28
|
+
Finally, you can then start in background mode:
|
32
29
|
|
33
|
-
|
30
|
+
$ mongrel_rails start -d
|
31
|
+
|
32
|
+
And you can stop it whenever you like with:
|
34
33
|
|
35
|
-
|
36
|
-
PID of the process you ran into log/mongrel.pid.
|
34
|
+
$ mongrel_rails stop
|
37
35
|
|
38
|
-
|
39
|
-
changing to a different directory, adding more MIME types, and setting processor
|
40
|
-
threads and timeouts.
|
36
|
+
All of which should be done from your application's directory. It writes the PID of the process you ran into log/mongrel.pid.
|
41
37
|
|
38
|
+
There are also many more new options for configuring the rails runner including changing to a different directory, adding more MIME types, and setting processor threads and timeouts.
|
42
39
|
|
43
40
|
== Install
|
44
41
|
|
45
|
-
It doesn't explicitly require Camping, but if you want to run the examples/camping/
|
46
|
-
examples then you'll need to install Camping 1.2 at least (and redcloth I think).
|
47
|
-
These are all available from RubyGems.
|
42
|
+
It doesn't explicitly require Camping, but if you want to run the examples/camping/ examples then you'll need to install Camping 1.2 at least (and redcloth I think). These are all available from RubyGems.
|
48
43
|
|
49
|
-
The library consists of a C extension so you'll need a C compiler or at least a friend
|
50
|
-
who can build it for you.
|
44
|
+
The library consists of a C extension so you'll need a C compiler or at least a friend who can build it for you.
|
51
45
|
|
52
46
|
Finally, the source includes a setup.rb for those who hate RubyGems.
|
53
47
|
|
54
48
|
== Usage
|
55
49
|
|
56
|
-
The examples/simpletest.rb file has the following code as the simplest
|
57
|
-
example:
|
50
|
+
The examples/simpletest.rb file has the following code as the simplest example:
|
58
51
|
|
59
52
|
require 'mongrel'
|
60
53
|
|
@@ -72,15 +65,10 @@ example:
|
|
72
65
|
h.register("/files", Mongrel::DirHandler.new("."))
|
73
66
|
h.run.join
|
74
67
|
|
75
|
-
If you run this and access port 3000 with a browser it will say
|
76
|
-
"hello!". If you access it with any url other than "/test" it will
|
77
|
-
give a simple 404. Check out the Mongrel::Error404Handler for a
|
78
|
-
basic way to give a more complex 404 message.
|
68
|
+
If you run this and access port 3000 with a browser it will say "hello!". If you access it with any url other than "/test" it will give a simple 404. Check out the Mongrel::Error404Handler for a basic way to give a more complex 404 message.
|
79
69
|
|
80
|
-
This also shows the DirHandler with directory listings. This is still
|
81
|
-
rough but it should work for basic hosting. *File extension to mime
|
82
|
-
type mapping is missing though.*
|
70
|
+
This also shows the DirHandler with directory listings. This is still rough but it should work for basic hosting. *File extension to mime type mapping is missing though.*
|
83
71
|
|
84
72
|
== Contact
|
85
73
|
|
86
|
-
E-mail
|
74
|
+
E-mail the Mongrel list at http://rubyforge.org/mailman/listinfo/mongrel-users and someone will help you. Comments about the API are welcome.
|
data/TODO
ADDED
data/bin/mongrel_rails
CHANGED
@@ -124,7 +124,7 @@ module Mongrel
|
|
124
124
|
end
|
125
125
|
|
126
126
|
config.run
|
127
|
-
config.log "Mongrel available at #{@address}:#{@port}"
|
127
|
+
config.log "Mongrel #{Mongrel::Const::MONGREL_VERSION} available at #{@address}:#{@port}"
|
128
128
|
|
129
129
|
if config.defaults[:daemon]
|
130
130
|
config.write_pid_file
|
data/ext/http11/http11.c
CHANGED
@@ -8,11 +8,9 @@
|
|
8
8
|
#include <string.h>
|
9
9
|
#include "http11_parser.h"
|
10
10
|
#include <ctype.h>
|
11
|
-
#include "tst.h"
|
12
11
|
|
13
12
|
static VALUE mMongrel;
|
14
13
|
static VALUE cHttpParser;
|
15
|
-
static VALUE cURIClassifier;
|
16
14
|
static VALUE eHttpParserError;
|
17
15
|
|
18
16
|
#define id_handler_map rb_intern("@handler_map")
|
@@ -363,191 +361,6 @@ VALUE HttpParser_nread(VALUE self)
|
|
363
361
|
return INT2FIX(http->nread);
|
364
362
|
}
|
365
363
|
|
366
|
-
|
367
|
-
void URIClassifier_free(void *data)
|
368
|
-
{
|
369
|
-
TRACE();
|
370
|
-
|
371
|
-
if(data) {
|
372
|
-
tst_cleanup((struct tst *)data);
|
373
|
-
}
|
374
|
-
}
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
VALUE URIClassifier_alloc(VALUE klass)
|
379
|
-
{
|
380
|
-
VALUE obj;
|
381
|
-
struct tst *tst = tst_init(TRIE_INCREASE);
|
382
|
-
TRACE();
|
383
|
-
assert(tst && "failed to initialize trie structure");
|
384
|
-
|
385
|
-
obj = Data_Wrap_Struct(klass, NULL, URIClassifier_free, tst);
|
386
|
-
|
387
|
-
return obj;
|
388
|
-
}
|
389
|
-
|
390
|
-
/**
|
391
|
-
* call-seq:
|
392
|
-
* URIClassifier.new -> URIClassifier
|
393
|
-
*
|
394
|
-
* Initializes a new URIClassifier object that you can use to associate URI sequences
|
395
|
-
* with objects. You can actually use it with any string sequence and any objects,
|
396
|
-
* but it's mostly used with URIs.
|
397
|
-
*
|
398
|
-
* It uses TST from http://www.octavian.org/cs/software.html to build an ternary search
|
399
|
-
* trie to hold all of the URIs. It uses this to do an initial search for the a URI
|
400
|
-
* prefix, and then to break the URI into SCRIPT_NAME and PATH_INFO portions. It actually
|
401
|
-
* will do two searches most of the time in order to find the right handler for the
|
402
|
-
* registered prefix portion.
|
403
|
-
*
|
404
|
-
*/
|
405
|
-
VALUE URIClassifier_init(VALUE self)
|
406
|
-
{
|
407
|
-
VALUE hash;
|
408
|
-
|
409
|
-
/* we create an internal hash to protect stuff from the GC */
|
410
|
-
hash = rb_hash_new();
|
411
|
-
rb_ivar_set(self, id_handler_map, hash);
|
412
|
-
|
413
|
-
return self;
|
414
|
-
}
|
415
|
-
|
416
|
-
|
417
|
-
/**
|
418
|
-
* call-seq:
|
419
|
-
* uc.register("/someuri", SampleHandler.new) -> nil
|
420
|
-
*
|
421
|
-
* Registers the SampleHandler (one for all requests) with the "/someuri".
|
422
|
-
* When URIClassifier::resolve is called with "/someuri" it'll return
|
423
|
-
* SampleHandler immediately. When called with "/someuri/iwant" it'll also
|
424
|
-
* return SomeHandler immediatly, with no additional searches, but it will
|
425
|
-
* return path info with "/iwant".
|
426
|
-
*
|
427
|
-
* You actually can reuse this class to register nearly anything and
|
428
|
-
* quickly resolve it. This could be used for caching, fast mapping, etc.
|
429
|
-
* The downside is it uses much more memory than a Hash, but it can be
|
430
|
-
* a lot faster. It's main advantage is that it works on prefixes, which
|
431
|
-
* is damn hard to get right with a Hash.
|
432
|
-
*/
|
433
|
-
VALUE URIClassifier_register(VALUE self, VALUE uri, VALUE handler)
|
434
|
-
{
|
435
|
-
int rc = 0;
|
436
|
-
void *ptr = NULL;
|
437
|
-
struct tst *tst = NULL;
|
438
|
-
DATA_GET(self, struct tst, tst);
|
439
|
-
|
440
|
-
rc = tst_insert((unsigned char *)StringValueCStr(uri), (void *)handler , tst, 0, &ptr);
|
441
|
-
|
442
|
-
if(rc == TST_DUPLICATE_KEY) {
|
443
|
-
rb_raise(rb_eStandardError, "Handler already registered with that name");
|
444
|
-
} else if(rc == TST_ERROR) {
|
445
|
-
rb_raise(rb_eStandardError, "Memory error registering handler");
|
446
|
-
} else if(rc == TST_NULL_KEY) {
|
447
|
-
rb_raise(rb_eStandardError, "URI was empty");
|
448
|
-
}
|
449
|
-
|
450
|
-
rb_hash_aset(rb_ivar_get(self, id_handler_map), uri, handler);
|
451
|
-
|
452
|
-
return Qnil;
|
453
|
-
}
|
454
|
-
|
455
|
-
|
456
|
-
/**
|
457
|
-
* call-seq:
|
458
|
-
* uc.unregister("/someuri")
|
459
|
-
*
|
460
|
-
* Yep, just removes this uri and it's handler from the trie.
|
461
|
-
*/
|
462
|
-
VALUE URIClassifier_unregister(VALUE self, VALUE uri)
|
463
|
-
{
|
464
|
-
void *handler = NULL;
|
465
|
-
struct tst *tst = NULL;
|
466
|
-
DATA_GET(self, struct tst, tst);
|
467
|
-
|
468
|
-
handler = tst_delete((unsigned char *)StringValueCStr(uri), tst);
|
469
|
-
|
470
|
-
if(handler) {
|
471
|
-
rb_hash_delete(rb_ivar_get(self, id_handler_map), uri);
|
472
|
-
|
473
|
-
return (VALUE)handler;
|
474
|
-
} else {
|
475
|
-
return Qnil;
|
476
|
-
}
|
477
|
-
}
|
478
|
-
|
479
|
-
|
480
|
-
/**
|
481
|
-
* call-seq:
|
482
|
-
* uc.resolve("/someuri") -> "/someuri", "", handler
|
483
|
-
* uc.resolve("/someuri/pathinfo") -> "/someuri", "/pathinfo", handler
|
484
|
-
* uc.resolve("/notfound/orhere") -> nil, nil, nil
|
485
|
-
* uc.resolve("/") -> "/", "/", handler # if uc.register("/", handler)
|
486
|
-
* uc.resolve("/path/from/root") -> "/", "/path/from/root", handler # if uc.register("/", handler)
|
487
|
-
*
|
488
|
-
* Attempts to resolve either the whole URI or at the longest prefix, returning
|
489
|
-
* the prefix (as script_info), path (as path_info), and registered handler
|
490
|
-
* (usually an HttpHandler). If it doesn't find a handler registered at the longest
|
491
|
-
* match then it returns nil,nil,nil.
|
492
|
-
*
|
493
|
-
* Because the resolver uses a trie you are able to register a handler at *any* character
|
494
|
-
* in the URI and it will be handled as long as it's the longest prefix. So, if you
|
495
|
-
* registered handler #1 at "/something/lik", and #2 at "/something/like/that", then a
|
496
|
-
* a search for "/something/like" would give you #1. A search for "/something/like/that/too"
|
497
|
-
* would give you #2.
|
498
|
-
*
|
499
|
-
* This is very powerful since it means you can also attach handlers to parts of the ;
|
500
|
-
* (semi-colon) separated path params, any part of the path, use off chars, anything really.
|
501
|
-
* It also means that it's very efficient to do this only taking as long as the URI has
|
502
|
-
* characters.
|
503
|
-
*
|
504
|
-
* A slight modification to the CGI 1.2 standard is given for handlers registered to "/".
|
505
|
-
* CGI expects all CGI scripts to be at some script path, so it doesn't really say anything
|
506
|
-
* about a script that handles the root. To make this work, the resolver will detect that
|
507
|
-
* the requested handler is at "/", and return that for script_name, and then simply return
|
508
|
-
* the full URI back as path_info.
|
509
|
-
*
|
510
|
-
* It expects strings with no embedded '\0' characters. Don't try other string-like stuff yet.
|
511
|
-
*/
|
512
|
-
VALUE URIClassifier_resolve(VALUE self, VALUE uri)
|
513
|
-
{
|
514
|
-
void *handler = NULL;
|
515
|
-
int pref_len = 0;
|
516
|
-
struct tst *tst = NULL;
|
517
|
-
VALUE result;
|
518
|
-
unsigned char *uri_str = NULL;
|
519
|
-
|
520
|
-
DATA_GET(self, struct tst, tst);
|
521
|
-
uri_str = (unsigned char *)StringValueCStr(uri);
|
522
|
-
|
523
|
-
handler = tst_search(uri_str, tst, TST_LONGEST_MATCH, &pref_len);
|
524
|
-
|
525
|
-
/* setup for multiple return values */
|
526
|
-
result = rb_ary_new();
|
527
|
-
|
528
|
-
if(handler) {
|
529
|
-
rb_ary_push(result, rb_str_substr (uri, 0, pref_len));
|
530
|
-
/* compensate for a script_name="/" where we need to add the "/" to path_info to keep it consistent */
|
531
|
-
if(pref_len == 1 && uri_str[0] == '/') {
|
532
|
-
/* matches the root URI so we have to use the whole URI as the path_info */
|
533
|
-
rb_ary_push(result, uri);
|
534
|
-
} else {
|
535
|
-
/* matches a script so process like normal */
|
536
|
-
rb_ary_push(result, rb_str_substr(uri, pref_len, RSTRING(uri)->len));
|
537
|
-
}
|
538
|
-
|
539
|
-
rb_ary_push(result, (VALUE)handler);
|
540
|
-
} else {
|
541
|
-
/* not found so push back nothing */
|
542
|
-
rb_ary_push(result, Qnil);
|
543
|
-
rb_ary_push(result, Qnil);
|
544
|
-
rb_ary_push(result, Qnil);
|
545
|
-
}
|
546
|
-
|
547
|
-
return result;
|
548
|
-
}
|
549
|
-
|
550
|
-
|
551
364
|
void Init_http11()
|
552
365
|
{
|
553
366
|
|
@@ -571,7 +384,7 @@ void Init_http11()
|
|
571
384
|
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
572
385
|
DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
|
573
386
|
DEF_GLOBAL(http_host, "HTTP_HOST");
|
574
|
-
DEF_GLOBAL(mongrel_version, "Mongrel 1.
|
387
|
+
DEF_GLOBAL(mongrel_version, "Mongrel 1.1");
|
575
388
|
DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
|
576
389
|
DEF_GLOBAL(port_80, "80");
|
577
390
|
|
@@ -586,11 +399,4 @@ void Init_http11()
|
|
586
399
|
rb_define_method(cHttpParser, "error?", HttpParser_has_error,0);
|
587
400
|
rb_define_method(cHttpParser, "finished?", HttpParser_is_finished,0);
|
588
401
|
rb_define_method(cHttpParser, "nread", HttpParser_nread,0);
|
589
|
-
|
590
|
-
cURIClassifier = rb_define_class_under(mMongrel, "URIClassifier", rb_cObject);
|
591
|
-
rb_define_alloc_func(cURIClassifier, URIClassifier_alloc);
|
592
|
-
rb_define_method(cURIClassifier, "initialize", URIClassifier_init, 0);
|
593
|
-
rb_define_method(cURIClassifier, "register", URIClassifier_register, 2);
|
594
|
-
rb_define_method(cURIClassifier, "unregister", URIClassifier_unregister, 1);
|
595
|
-
rb_define_method(cURIClassifier, "resolve", URIClassifier_resolve, 1);
|
596
402
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import java.io.IOException;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
5
|
+
|
6
|
+
import org.jruby.mongrel.Http11;
|
7
|
+
|
8
|
+
public class Http11Service implements BasicLibraryService {
|
9
|
+
public boolean basicLoad(final Ruby runtime) throws IOException {
|
10
|
+
Http11.createHttp11(runtime);
|
11
|
+
return true;
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,266 @@
|
|
1
|
+
/***** BEGIN LICENSE BLOCK *****
|
2
|
+
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
|
3
|
+
*
|
4
|
+
* The contents of this file are subject to the Common Public
|
5
|
+
* License Version 1.0 (the "License"); you may not use this file
|
6
|
+
* except in compliance with the License. You may obtain a copy of
|
7
|
+
* the License at http://www.eclipse.org/legal/cpl-v10.html
|
8
|
+
*
|
9
|
+
* Software distributed under the License is distributed on an "AS
|
10
|
+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
11
|
+
* implied. See the License for the specific language governing
|
12
|
+
* rights and limitations under the License.
|
13
|
+
*
|
14
|
+
* Copyright (C) 2007 Ola Bini <ola@ologix.com>
|
15
|
+
*
|
16
|
+
* Alternatively, the contents of this file may be used under the terms of
|
17
|
+
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
18
|
+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
19
|
+
* in which case the provisions of the GPL or the LGPL are applicable instead
|
20
|
+
* of those above. If you wish to allow use of your version of this file only
|
21
|
+
* under the terms of either the GPL or the LGPL, and not to allow others to
|
22
|
+
* use your version of this file under the terms of the CPL, indicate your
|
23
|
+
* decision by deleting the provisions above and replace them with the notice
|
24
|
+
* and other provisions required by the GPL or the LGPL. If you do not delete
|
25
|
+
* the provisions above, a recipient may use your version of this file under
|
26
|
+
* the terms of any one of the CPL, the GPL or the LGPL.
|
27
|
+
***** END LICENSE BLOCK *****/
|
28
|
+
package org.jruby.mongrel;
|
29
|
+
|
30
|
+
import org.jruby.Ruby;
|
31
|
+
import org.jruby.RubyClass;
|
32
|
+
import org.jruby.RubyHash;
|
33
|
+
import org.jruby.RubyModule;
|
34
|
+
import org.jruby.RubyNumeric;
|
35
|
+
import org.jruby.RubyObject;
|
36
|
+
import org.jruby.RubyString;
|
37
|
+
|
38
|
+
import org.jruby.runtime.CallbackFactory;
|
39
|
+
import org.jruby.runtime.ObjectAllocator;
|
40
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
41
|
+
|
42
|
+
import org.jruby.exceptions.RaiseException;
|
43
|
+
|
44
|
+
import org.jruby.util.ByteList;
|
45
|
+
|
46
|
+
/**
|
47
|
+
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
|
48
|
+
*/
|
49
|
+
public class Http11 extends RubyObject {
|
50
|
+
public final static int MAX_FIELD_NAME_LENGTH = 256;
|
51
|
+
public final static String MAX_FIELD_NAME_LENGTH_ERR = "HTTP element FIELD_NAME is longer than the 256 allowed length.";
|
52
|
+
public final static int MAX_FIELD_VALUE_LENGTH = 80 * 1024;
|
53
|
+
public final static String MAX_FIELD_VALUE_LENGTH_ERR = "HTTP element FIELD_VALUE is longer than the 81920 allowed length.";
|
54
|
+
public final static int MAX_REQUEST_URI_LENGTH = 1024 * 12;
|
55
|
+
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
|
56
|
+
public final static int MAX_FRAGMENT_LENGTH = 1024;
|
57
|
+
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
|
58
|
+
public final static int MAX_REQUEST_PATH_LENGTH = 1024;
|
59
|
+
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
|
60
|
+
public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
|
61
|
+
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
|
62
|
+
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
|
63
|
+
public final static String MAX_HEADER_LENGTH_ERR = "HTTP element HEADER is longer than the 114688 allowed length.";
|
64
|
+
|
65
|
+
|
66
|
+
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
67
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
68
|
+
return new Http11(runtime, klass);
|
69
|
+
}
|
70
|
+
};
|
71
|
+
|
72
|
+
public static void createHttp11(Ruby runtime) {
|
73
|
+
RubyModule mMongrel = runtime.defineModule("Mongrel");
|
74
|
+
mMongrel.defineClassUnder("HttpParserError",runtime.getClass("IOError"),runtime.getClass("IOError").getAllocator());
|
75
|
+
|
76
|
+
CallbackFactory cf = runtime.callbackFactory(Http11.class);
|
77
|
+
|
78
|
+
RubyClass cHttpParser = mMongrel.defineClassUnder("HttpParser",runtime.getObject(),ALLOCATOR);
|
79
|
+
cHttpParser.defineFastMethod("initialize",cf.getFastMethod("initialize"));
|
80
|
+
cHttpParser.defineFastMethod("reset",cf.getFastMethod("reset"));
|
81
|
+
cHttpParser.defineFastMethod("finish",cf.getFastMethod("finish"));
|
82
|
+
cHttpParser.defineFastMethod("execute",cf.getFastMethod("execute", IRubyObject.class, IRubyObject.class, IRubyObject.class));
|
83
|
+
cHttpParser.defineFastMethod("error?",cf.getFastMethod("has_error"));
|
84
|
+
cHttpParser.defineFastMethod("finished?",cf.getFastMethod("is_finished"));
|
85
|
+
cHttpParser.defineFastMethod("nread",cf.getFastMethod("nread"));
|
86
|
+
}
|
87
|
+
|
88
|
+
private Ruby runtime;
|
89
|
+
private RubyClass eHttpParserError;
|
90
|
+
private Http11Parser hp;
|
91
|
+
|
92
|
+
public Http11(Ruby runtime, RubyClass clazz) {
|
93
|
+
super(runtime,clazz);
|
94
|
+
this.runtime = runtime;
|
95
|
+
this.eHttpParserError = (RubyClass)runtime.getModule("Mongrel").getConstant("HttpParserError");
|
96
|
+
this.hp = new Http11Parser();
|
97
|
+
this.hp.parser.http_field = http_field;
|
98
|
+
this.hp.parser.request_method = request_method;
|
99
|
+
this.hp.parser.request_uri = request_uri;
|
100
|
+
this.hp.parser.fragment = fragment;
|
101
|
+
this.hp.parser.request_path = request_path;
|
102
|
+
this.hp.parser.query_string = query_string;
|
103
|
+
this.hp.parser.http_version = http_version;
|
104
|
+
this.hp.parser.header_done = header_done;
|
105
|
+
this.hp.parser.init();
|
106
|
+
}
|
107
|
+
|
108
|
+
public void validateMaxLength(int len, int max, String msg) {
|
109
|
+
if(len>max) {
|
110
|
+
throw new RaiseException(runtime, eHttpParserError, msg, true);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
|
115
|
+
public void call(Object data, int field, int flen, int value, int vlen) {
|
116
|
+
RubyHash req = (RubyHash)data;
|
117
|
+
RubyString v,f;
|
118
|
+
validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
|
119
|
+
validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
|
120
|
+
|
121
|
+
v = RubyString.newString(runtime, new ByteList(Http11.this.hp.parser.buffer,value,vlen));
|
122
|
+
f = RubyString.newString(runtime, "HTTP_");
|
123
|
+
ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen);
|
124
|
+
for(int i=0,j=b.realSize;i<j;i++) {
|
125
|
+
if((b.bytes[i]&0xFF) == '-') {
|
126
|
+
b.bytes[i] = (byte)'_';
|
127
|
+
} else {
|
128
|
+
b.bytes[i] = (byte)Character.toUpperCase((char)b.bytes[i]);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
f.cat(b);
|
132
|
+
req.aset(f,v);
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
private Http11Parser.ElementCB request_method = new Http11Parser.ElementCB() {
|
137
|
+
public void call(Object data, int at, int length) {
|
138
|
+
RubyHash req = (RubyHash)data;
|
139
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
140
|
+
req.aset(runtime.newString("REQUEST_METHOD"),val);
|
141
|
+
}
|
142
|
+
};
|
143
|
+
|
144
|
+
private Http11Parser.ElementCB request_uri = new Http11Parser.ElementCB() {
|
145
|
+
public void call(Object data, int at, int length) {
|
146
|
+
RubyHash req = (RubyHash)data;
|
147
|
+
validateMaxLength(length, MAX_REQUEST_URI_LENGTH, MAX_REQUEST_URI_LENGTH_ERR);
|
148
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
149
|
+
req.aset(runtime.newString("REQUEST_URI"),val);
|
150
|
+
}
|
151
|
+
};
|
152
|
+
|
153
|
+
private Http11Parser.ElementCB fragment = new Http11Parser.ElementCB() {
|
154
|
+
public void call(Object data, int at, int length) {
|
155
|
+
RubyHash req = (RubyHash)data;
|
156
|
+
validateMaxLength(length, MAX_FRAGMENT_LENGTH, MAX_FRAGMENT_LENGTH_ERR);
|
157
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
158
|
+
req.aset(runtime.newString("FRAGMENT"),val);
|
159
|
+
}
|
160
|
+
};
|
161
|
+
|
162
|
+
private Http11Parser.ElementCB request_path = new Http11Parser.ElementCB() {
|
163
|
+
public void call(Object data, int at, int length) {
|
164
|
+
RubyHash req = (RubyHash)data;
|
165
|
+
validateMaxLength(length, MAX_REQUEST_PATH_LENGTH, MAX_REQUEST_PATH_LENGTH_ERR);
|
166
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
167
|
+
req.aset(runtime.newString("REQUEST_PATH"),val);
|
168
|
+
}
|
169
|
+
};
|
170
|
+
|
171
|
+
private Http11Parser.ElementCB query_string = new Http11Parser.ElementCB() {
|
172
|
+
public void call(Object data, int at, int length) {
|
173
|
+
RubyHash req = (RubyHash)data;
|
174
|
+
validateMaxLength(length, MAX_QUERY_STRING_LENGTH, MAX_QUERY_STRING_LENGTH_ERR);
|
175
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
176
|
+
req.aset(runtime.newString("QUERY_STRING"),val);
|
177
|
+
}
|
178
|
+
};
|
179
|
+
|
180
|
+
private Http11Parser.ElementCB http_version = new Http11Parser.ElementCB() {
|
181
|
+
public void call(Object data, int at, int length) {
|
182
|
+
RubyHash req = (RubyHash)data;
|
183
|
+
RubyString val = RubyString.newString(runtime,new ByteList(hp.parser.buffer,at,length));
|
184
|
+
req.aset(runtime.newString("HTTP_VERSION"),val);
|
185
|
+
}
|
186
|
+
};
|
187
|
+
|
188
|
+
private Http11Parser.ElementCB header_done = new Http11Parser.ElementCB() {
|
189
|
+
public void call(Object data, int at, int length) {
|
190
|
+
RubyHash req = (RubyHash)data;
|
191
|
+
IRubyObject temp,ctype,clen;
|
192
|
+
|
193
|
+
clen = req.aref(runtime.newString("HTTP_CONTENT_LENGTH"));
|
194
|
+
if(!clen.isNil()) {
|
195
|
+
req.aset(runtime.newString("CONTENT_LENGTH"),clen);
|
196
|
+
}
|
197
|
+
|
198
|
+
ctype = req.aref(runtime.newString("HTTP_CONTENT_TYPE"));
|
199
|
+
if(!ctype.isNil()) {
|
200
|
+
req.aset(runtime.newString("CONTENT_TYPE"),ctype);
|
201
|
+
}
|
202
|
+
|
203
|
+
req.aset(runtime.newString("GATEWAY_INTERFACE"),runtime.newString("CGI/1.2"));
|
204
|
+
if(!(temp = req.aref(runtime.newString("HTTP_HOST"))).isNil()) {
|
205
|
+
String s = temp.toString();
|
206
|
+
int colon = s.indexOf(':');
|
207
|
+
if(colon != -1) {
|
208
|
+
req.aset(runtime.newString("SERVER_NAME"),runtime.newString(s.substring(0,colon)));
|
209
|
+
req.aset(runtime.newString("SERVER_PORT"),runtime.newString(s.substring(colon+1)));
|
210
|
+
} else {
|
211
|
+
req.aset(runtime.newString("SERVER_NAME"),temp);
|
212
|
+
req.aset(runtime.newString("SERVER_PORT"),runtime.newString("80"));
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
req.setInstanceVariable("@http_body", RubyString.newString(runtime, new ByteList(hp.parser.buffer, at, length)));
|
217
|
+
req.aset(runtime.newString("SERVER_PROTOCOL"),runtime.newString("HTTP/1.1"));
|
218
|
+
req.aset(runtime.newString("SERVER_SOFTWARE"),runtime.newString("Mongrel 1.0.1"));
|
219
|
+
}
|
220
|
+
};
|
221
|
+
|
222
|
+
public IRubyObject initialize() {
|
223
|
+
this.hp.parser.init();
|
224
|
+
return this;
|
225
|
+
}
|
226
|
+
|
227
|
+
public IRubyObject reset() {
|
228
|
+
this.hp.parser.init();
|
229
|
+
return runtime.getNil();
|
230
|
+
}
|
231
|
+
|
232
|
+
public IRubyObject finish() {
|
233
|
+
this.hp.finish();
|
234
|
+
return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse();
|
235
|
+
}
|
236
|
+
|
237
|
+
public IRubyObject execute(IRubyObject req_hash, IRubyObject data, IRubyObject start) {
|
238
|
+
int from = 0;
|
239
|
+
from = RubyNumeric.fix2int(start);
|
240
|
+
ByteList d = ((RubyString)data).getByteList();
|
241
|
+
if(from >= d.realSize) {
|
242
|
+
throw new RaiseException(runtime, eHttpParserError, "Requested start is after data buffer end.", true);
|
243
|
+
} else {
|
244
|
+
this.hp.parser.data = req_hash;
|
245
|
+
this.hp.execute(d,from);
|
246
|
+
validateMaxLength(this.hp.parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
|
247
|
+
if(this.hp.has_error()) {
|
248
|
+
throw new RaiseException(runtime, eHttpParserError, "Invalid HTTP format, parsing fails.", true);
|
249
|
+
} else {
|
250
|
+
return runtime.newFixnum(this.hp.parser.nread);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
public IRubyObject has_error() {
|
256
|
+
return this.hp.has_error() ? runtime.getTrue() : runtime.getFalse();
|
257
|
+
}
|
258
|
+
|
259
|
+
public IRubyObject is_finished() {
|
260
|
+
return this.hp.is_finished() ? runtime.getTrue() : runtime.getFalse();
|
261
|
+
}
|
262
|
+
|
263
|
+
public IRubyObject nread() {
|
264
|
+
return runtime.newFixnum(this.hp.parser.nread);
|
265
|
+
}
|
266
|
+
}// Http11
|