mongrel2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+