mongrel2 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gemtest +0 -0
  3. data/History.rdoc +4 -0
  4. data/Manifest.txt +66 -0
  5. data/README.rdoc +169 -0
  6. data/Rakefile +77 -0
  7. data/bin/m2sh.rb +600 -0
  8. data/data/mongrel2/bootstrap.html +25 -0
  9. data/data/mongrel2/config.sql +84 -0
  10. data/data/mongrel2/mimetypes.sql +855 -0
  11. data/examples/README.txt +6 -0
  12. data/examples/config.rb +54 -0
  13. data/examples/helloworld-handler.rb +31 -0
  14. data/examples/request-dumper.rb +45 -0
  15. data/examples/request-dumper.tmpl +71 -0
  16. data/examples/run +17 -0
  17. data/lib/mongrel2.rb +62 -0
  18. data/lib/mongrel2/config.rb +212 -0
  19. data/lib/mongrel2/config/directory.rb +78 -0
  20. data/lib/mongrel2/config/dsl.rb +206 -0
  21. data/lib/mongrel2/config/handler.rb +124 -0
  22. data/lib/mongrel2/config/host.rb +88 -0
  23. data/lib/mongrel2/config/log.rb +48 -0
  24. data/lib/mongrel2/config/mimetype.rb +15 -0
  25. data/lib/mongrel2/config/proxy.rb +15 -0
  26. data/lib/mongrel2/config/route.rb +51 -0
  27. data/lib/mongrel2/config/server.rb +58 -0
  28. data/lib/mongrel2/config/setting.rb +15 -0
  29. data/lib/mongrel2/config/statistic.rb +23 -0
  30. data/lib/mongrel2/connection.rb +212 -0
  31. data/lib/mongrel2/constants.rb +159 -0
  32. data/lib/mongrel2/control.rb +165 -0
  33. data/lib/mongrel2/exceptions.rb +59 -0
  34. data/lib/mongrel2/handler.rb +309 -0
  35. data/lib/mongrel2/httprequest.rb +51 -0
  36. data/lib/mongrel2/httpresponse.rb +187 -0
  37. data/lib/mongrel2/jsonrequest.rb +43 -0
  38. data/lib/mongrel2/logging.rb +241 -0
  39. data/lib/mongrel2/mixins.rb +143 -0
  40. data/lib/mongrel2/request.rb +148 -0
  41. data/lib/mongrel2/response.rb +74 -0
  42. data/lib/mongrel2/table.rb +216 -0
  43. data/lib/mongrel2/xmlrequest.rb +36 -0
  44. data/spec/lib/constants.rb +237 -0
  45. data/spec/lib/helpers.rb +246 -0
  46. data/spec/lib/matchers.rb +50 -0
  47. data/spec/mongrel2/config/directory_spec.rb +91 -0
  48. data/spec/mongrel2/config/dsl_spec.rb +218 -0
  49. data/spec/mongrel2/config/handler_spec.rb +118 -0
  50. data/spec/mongrel2/config/host_spec.rb +30 -0
  51. data/spec/mongrel2/config/log_spec.rb +95 -0
  52. data/spec/mongrel2/config/proxy_spec.rb +30 -0
  53. data/spec/mongrel2/config/route_spec.rb +83 -0
  54. data/spec/mongrel2/config/server_spec.rb +84 -0
  55. data/spec/mongrel2/config/setting_spec.rb +30 -0
  56. data/spec/mongrel2/config/statistic_spec.rb +30 -0
  57. data/spec/mongrel2/config_spec.rb +111 -0
  58. data/spec/mongrel2/connection_spec.rb +172 -0
  59. data/spec/mongrel2/constants_spec.rb +32 -0
  60. data/spec/mongrel2/control_spec.rb +192 -0
  61. data/spec/mongrel2/handler_spec.rb +261 -0
  62. data/spec/mongrel2/httpresponse_spec.rb +232 -0
  63. data/spec/mongrel2/logging_spec.rb +76 -0
  64. data/spec/mongrel2/mixins_spec.rb +62 -0
  65. data/spec/mongrel2/request_spec.rb +157 -0
  66. data/spec/mongrel2/response_spec.rb +81 -0
  67. data/spec/mongrel2/table_spec.rb +176 -0
  68. data/spec/mongrel2_spec.rb +34 -0
  69. metadata +294 -0
  70. metadata.gz.sig +0 -0
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
10
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
+ }
12
+
13
+ require 'rspec'
14
+
15
+ require 'spec/lib/helpers'
16
+
17
+ require 'mongrel2'
18
+ require 'mongrel2/httprequest'
19
+ require 'mongrel2/httpresponse'
20
+
21
+
22
+ #####################################################################
23
+ ### C O N T E X T S
24
+ #####################################################################
25
+
26
+ describe Mongrel2::HTTPResponse do
27
+
28
+ before( :all ) do
29
+ setup_logging( :fatal )
30
+ end
31
+
32
+ before( :each ) do
33
+ @response = Mongrel2::HTTPResponse.new( TEST_UUID, 299 )
34
+ end
35
+
36
+ after( :all ) do
37
+ reset_logging()
38
+ end
39
+
40
+
41
+ it "has a headers table" do
42
+ @response.headers.should be_a( Mongrel2::Table )
43
+ end
44
+
45
+ it "is a status 200 response if not set otherwise" do
46
+ @response.status_line.should == 'HTTP/1.1 200 OK'
47
+ end
48
+
49
+ it "sets Date and Content-length headers automatically if they haven't been set" do
50
+ @response.header_data.should =~ /Content-length: 0/i
51
+ @response.header_data.should =~ /Date: #{HTTP_DATE}/i
52
+ end
53
+
54
+ it "doesn't have a body" do
55
+ @response.body.should be_empty()
56
+ end
57
+
58
+ it "knows it hasn't been handled" do
59
+ @response.should_not be_handled()
60
+ end
61
+
62
+ it "stringifies to a valid RFC2616 response string" do
63
+ @response.to_s.should =~ HTTP_RESPONSE
64
+ end
65
+
66
+ it "has some default headers" do
67
+ @response.headers['Server'].should == Mongrel2.version_string( true )
68
+ end
69
+
70
+ it "can be reset to a pristine state" do
71
+ @response.body << "Some stuff we want to get rid of later"
72
+ @response.headers['x-lunch-packed-by'] = 'Your Mom'
73
+ @response.status = HTTP::OK
74
+
75
+ @response.reset
76
+
77
+ @response.should_not be_handled()
78
+ @response.body.should == ''
79
+ @response.headers.should have(1).keys
80
+ end
81
+
82
+
83
+ it "can find the length of its body if it's a String" do
84
+ test_body = 'A string full of stuff'
85
+ @response.body = test_body
86
+
87
+ @response.get_content_length.should == test_body.length
88
+ end
89
+
90
+ it "can find the length of its body if it's a seekable IO" do
91
+ test_body = File.open( __FILE__, 'r' )
92
+ test_body.seek( 0, IO::SEEK_END )
93
+ length = test_body.tell
94
+ test_body.seek( 0, IO::SEEK_SET )
95
+
96
+ @response.body = test_body
97
+
98
+ @response.get_content_length.should == length
99
+ end
100
+
101
+ it "can find the length of its body even if it's an IO that's been set to do a partial read" do
102
+ test_body = File.open( __FILE__, 'r' )
103
+ test_body.seek( 0, IO::SEEK_END )
104
+ length = test_body.tell
105
+ test_body.seek( 100, IO::SEEK_SET )
106
+
107
+ @response.body = test_body
108
+
109
+ @response.get_content_length.should == length - 100
110
+ end
111
+
112
+ it "knows that it has been handled even if the status is set to NOT_FOUND" do
113
+ @response.status = HTTP::NOT_FOUND
114
+ @response.should be_handled()
115
+ end
116
+
117
+ it "knows if it has not yet been handled" do
118
+ @response.should_not be_handled()
119
+ @response.status = HTTP::OK
120
+ @response.should be_handled()
121
+ end
122
+
123
+
124
+ it "knows what category of response it is" do
125
+ @response.status = HTTP::CREATED
126
+ @response.status_category.should == 2
127
+
128
+ @response.status = HTTP::NOT_ACCEPTABLE
129
+ @response.status_category.should == 4
130
+ end
131
+
132
+
133
+ it "knows if its status indicates it is an informational response" do
134
+ @response.status = HTTP::PROCESSING
135
+ @response.status_category.should == 1
136
+ @response.status_is_informational?.should == true
137
+ end
138
+
139
+
140
+ it "knows if its status indicates it is a successful response" do
141
+ @response.status = HTTP::ACCEPTED
142
+ @response.status_category.should == 2
143
+ @response.status_is_successful?.should == true
144
+ end
145
+
146
+
147
+ it "knows if its status indicates it is a redirected response" do
148
+ @response.status = HTTP::SEE_OTHER
149
+ @response.status_category.should == 3
150
+ @response.status_is_redirect?.should == true
151
+ end
152
+
153
+
154
+ it "knows if its status indicates there was a client error" do
155
+ @response.status = HTTP::GONE
156
+ @response.status_category.should == 4
157
+ @response.status_is_clienterror?.should == true
158
+ end
159
+
160
+
161
+ it "knows if its status indicates there was a server error" do
162
+ @response.status = HTTP::VERSION_NOT_SUPPORTED
163
+ @response.status_category.should == 5
164
+ @response.status_is_servererror?.should == true
165
+ end
166
+
167
+
168
+ it "knows what the response content type is" do
169
+ headers = mock( 'headers' )
170
+ @response.stub!( :headers ).and_return( headers )
171
+
172
+ headers.should_receive( :[] ).
173
+ with( :content_type ).
174
+ and_return( 'text/erotica' )
175
+
176
+ @response.content_type.should == 'text/erotica'
177
+ end
178
+
179
+
180
+ it "can modify the response content type" do
181
+ headers = mock( 'headers' )
182
+ @response.stub!( :headers ).and_return( headers )
183
+
184
+ headers.should_receive( :[]= ).
185
+ with( :content_type, 'image/nude' )
186
+
187
+ @response.content_type = 'image/nude'
188
+ end
189
+
190
+
191
+ it "can find the length of its body if it's an IO" do
192
+ test_body_content = 'A string with some stuff in it'
193
+ test_body = StringIO.new( test_body_content )
194
+ @response.body = test_body
195
+
196
+ @response.get_content_length.should == test_body_content.length
197
+ end
198
+
199
+
200
+ it "raises a descriptive error message if it can't get the body's length" do
201
+ @response.body = Object.new
202
+
203
+ lambda {
204
+ @response.get_content_length
205
+ }.should raise_error( Mongrel2::ResponseError, /content length/i )
206
+ end
207
+
208
+
209
+ it "can build a valid HTTP status line for its status" do
210
+ @response.status = HTTP::SEE_OTHER
211
+ @response.status_line.should == "HTTP/1.1 303 See Other"
212
+ end
213
+
214
+
215
+ it "has pipelining disabled by default" do
216
+ @response.should_not be_keepalive()
217
+ end
218
+
219
+
220
+ it "has pipelining disabled if it's explicitly disabled" do
221
+ @response.keepalive = false
222
+ @response.should_not be_keepalive()
223
+ end
224
+
225
+
226
+ it "can be set to allow pipelining" do
227
+ @response.keepalive = true
228
+ @response.should be_keepalive()
229
+ end
230
+
231
+ end
232
+
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
10
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'stringio'
15
+
16
+ require 'spec/lib/helpers'
17
+
18
+ require 'mongrel2'
19
+ require 'mongrel2/logging'
20
+
21
+
22
+ #####################################################################
23
+ ### C O N T E X T S
24
+ #####################################################################
25
+
26
+ describe Mongrel2::Logging, "-extended module" do
27
+
28
+ before( :each ) do
29
+ @extended_module = Module.new do
30
+ extend Mongrel2::Logging
31
+ end
32
+ end
33
+
34
+ it "should have a default Logger" do
35
+ @extended_module.logger.should be_a( Logger )
36
+ @extended_module.default_logger.should equal( @extended_module.logger )
37
+ end
38
+
39
+ it "should know if its default logger is replaced" do
40
+ @extended_module.should be_using_default_logger
41
+ @extended_module.logger = Logger.new( $stderr )
42
+ @extended_module.should_not be_using_default_logger
43
+ end
44
+
45
+ it "has the default logger instance after being reset" do
46
+ @extended_module.reset_logger
47
+ @extended_module.logger.should equal( @extended_module.default_logger )
48
+ end
49
+
50
+ it "has the default log formatter instance after being reset" do
51
+ @extended_module.reset_logger
52
+ @extended_module.logger.formatter.should equal( @extended_module.default_log_formatter )
53
+ end
54
+
55
+
56
+ context "with new defaults" do
57
+
58
+ before( :each ) do
59
+ @sink = StringIO.new
60
+ @logger = Logger.new( @sink )
61
+ @formatter = Mongrel2::Logging::ColorFormatter.new( @logger )
62
+
63
+ @extended_module.default_logger = @logger
64
+ @extended_module.default_log_formatter = @formatter
65
+ end
66
+
67
+ it "uses the new defaults when the logging subsystem is reset" do
68
+ @extended_module.reset_logger
69
+ @extended_module.logger.should equal( @logger )
70
+ end
71
+
72
+ end
73
+
74
+
75
+ end
76
+
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
10
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
+ }
12
+
13
+ require 'rspec'
14
+
15
+ require 'spec/lib/helpers'
16
+
17
+ require 'mongrel2'
18
+ require 'mongrel2/mixins'
19
+
20
+
21
+ #####################################################################
22
+ ### C O N T E X T S
23
+ #####################################################################
24
+
25
+ describe Mongrel2, "mixins" do
26
+
27
+ describe Mongrel2::Loggable do
28
+
29
+ before(:each) do
30
+ @logfile = StringIO.new('')
31
+ Mongrel2.logger = Logger.new( @logfile )
32
+
33
+ @test_class = Class.new do
34
+ include Mongrel2::Loggable
35
+
36
+ def log_test_message( level, msg )
37
+ self.log.send( level, msg )
38
+ end
39
+
40
+ def logdebug_test_message( msg )
41
+ self.log_debug.debug( msg )
42
+ end
43
+ end
44
+ @obj = @test_class.new
45
+ end
46
+
47
+
48
+ it "is able to output to the log via its #log method" do
49
+ @obj.log_test_message( :debug, "debugging message" )
50
+ @logfile.rewind
51
+ @logfile.read.should =~ /debugging message/
52
+ end
53
+
54
+ it "is able to output to the log via its #log_debug method" do
55
+ @obj.logdebug_test_message( "sexydrownwatch" )
56
+ @logfile.rewind
57
+ @logfile.read.should =~ /sexydrownwatch/
58
+ end
59
+ end
60
+
61
+ end
62
+
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
10
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'tnetstring'
15
+
16
+ require 'spec/lib/helpers'
17
+
18
+ require 'mongrel2'
19
+ require 'mongrel2/request'
20
+
21
+
22
+ #####################################################################
23
+ ### C O N T E X T S
24
+ #####################################################################
25
+
26
+ describe Mongrel2::Request do
27
+
28
+ before( :all ) do
29
+ setup_logging( :fatal )
30
+ end
31
+
32
+ after( :all ) do
33
+ reset_logging()
34
+ end
35
+
36
+
37
+ it "can parse a request message" do
38
+
39
+ message = make_request()
40
+ req = Mongrel2::Request.parse( message )
41
+
42
+ req.should be_a( Mongrel2::Request )
43
+ req.sender_id.should == TEST_UUID
44
+ req.conn_id.should == TEST_ID
45
+
46
+ req.headers.should be_a( Mongrel2::Table )
47
+ req.headers['Host'].should == TEST_HEADERS['host']
48
+ end
49
+
50
+ it "can parse a request message with TNetstring headers" do
51
+
52
+ message = make_tn_request()
53
+ req = Mongrel2::Request.parse( message )
54
+
55
+ req.should be_a( Mongrel2::Request )
56
+ req.sender_id.should == TEST_UUID
57
+ req.conn_id.should == TEST_ID
58
+
59
+ req.headers.should be_a( Mongrel2::Table )
60
+ req.headers.host.should == TEST_HEADERS['host']
61
+ end
62
+
63
+ it "can parse a request message with a JSON body" do
64
+
65
+ message = make_json_request()
66
+ req = Mongrel2::Request.parse( message )
67
+
68
+ req.should be_a( Mongrel2::JSONRequest )
69
+ req.sender_id.should == TEST_UUID
70
+ req.conn_id.should == TEST_ID
71
+
72
+ req.headers.should be_a( Mongrel2::Table )
73
+ req.headers.path.should == TEST_JSON_PATH
74
+
75
+ req.data.should == TEST_JSON_BODY
76
+ end
77
+
78
+ it "raises an UnhandledMethodError with the name of the method for METHOD verbs that " +
79
+ "don't look like HTTP ones" do
80
+
81
+ message = make_request( :headers => {'METHOD' => '!DIVULGE'} )
82
+ expect {
83
+ Mongrel2::Request.parse( message )
84
+ }.to raise_error( Mongrel2::UnhandledMethodError, /!DIVULGE/ )
85
+ end
86
+
87
+
88
+ describe "instances" do
89
+
90
+ before( :each ) do
91
+ message = make_json_request() # HTTPRequest overrides the #response method
92
+ @req = Mongrel2::Request.parse( message )
93
+ end
94
+
95
+ it "can return an appropriate response instance for themselves" do
96
+ result = @req.response
97
+ result.should be_a( Mongrel2::Response )
98
+ result.sender_id.should == @req.sender_id
99
+ result.conn_id.should == @req.conn_id
100
+ end
101
+
102
+ end
103
+
104
+
105
+ describe "framework support" do
106
+
107
+ before( :all ) do
108
+ @oldtypes = Mongrel2::Request.request_types
109
+ @original_default_proc = Mongrel2::Request.request_types.default_proc
110
+ end
111
+
112
+ before( :each ) do
113
+ Mongrel2::Request.request_types.default_proc = @original_default_proc
114
+ Mongrel2::Request.request_types.clear
115
+ end
116
+
117
+ after( :all ) do
118
+ Mongrel2::Request.request_types.default_proc = @original_default_proc
119
+ Mongrel2::Request.request_types.replace( @oldtypes )
120
+ end
121
+
122
+
123
+ it "includes a mechanism for overriding the default Request subclass" do
124
+ subclass = Class.new( Mongrel2::Request ) do
125
+ register_request_type self, :__default
126
+ end
127
+
128
+ Mongrel2::Request.subclass_for_method( 'GET' ).should == subclass
129
+ Mongrel2::Request.subclass_for_method( 'POST' ).should == subclass
130
+ Mongrel2::Request.subclass_for_method( 'JSON' ).should == subclass
131
+ end
132
+
133
+ it "includes a mechanism for overriding the Request subclass for a particular request " +
134
+ "method" do
135
+ subclass = Class.new( Mongrel2::Request ) do
136
+ register_request_type self, :GET
137
+ end
138
+
139
+ Mongrel2::Request.subclass_for_method( 'GET' ).should == subclass
140
+ Mongrel2::Request.subclass_for_method( 'POST' ).should_not == subclass
141
+ Mongrel2::Request.subclass_for_method( 'JSON' ).should_not == subclass
142
+ end
143
+
144
+ it "clears any cached method -> subclass lookups when the default subclass changes" do
145
+ Mongrel2::Request.subclass_for_method( 'OPTIONS' ) # cache OPTIONS -> Mongrel2::Request
146
+
147
+ subclass = Class.new( Mongrel2::Request ) do
148
+ register_request_type self, :__default
149
+ end
150
+
151
+ Mongrel2::Request.subclass_for_method( 'OPTIONS' ).should == subclass
152
+ end
153
+
154
+ end
155
+
156
+ end
157
+