mongrel2 0.52.1 → 0.52.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,12 +31,9 @@ describe Mongrel2::Connection do
31
31
  response_sock = double( "response socket", options: OpenStruct.new )
32
32
 
33
33
  expect( CZTop::Socket::PULL ).to receive( :new ).and_return( request_sock )
34
- expect( request_sock.options ).to receive( :linger= ).with( 0 )
35
34
  expect( request_sock ).to receive( :connect ).with( TEST_SEND_SPEC )
36
35
 
37
36
  expect( CZTop::Socket::PUB ).to receive( :new ).and_return( response_sock )
38
- expect( response_sock.options ).to receive( :linger= ).with( 0 )
39
- expect( response_sock.options ).to_not receive( :identity= )
40
37
  expect( response_sock ).to receive( :connect ).with( TEST_RECV_SPEC )
41
38
 
42
39
  expect( @conn.request_sock ).to eq( request_sock )
@@ -13,9 +13,6 @@ require 'mongrel2/control'
13
13
 
14
14
  describe Mongrel2::Control do
15
15
 
16
- before( :all ) do
17
- end
18
-
19
16
  before( :each ) do
20
17
  @ctx = double( "ZMQ::Context" )
21
18
  @socket = double( "ZMQ REQ socket", :connect => nil, :options => OpenStruct.new )
@@ -34,6 +31,7 @@ describe Mongrel2::Control do
34
31
  expect( @control.stop ).to eq( [{ :msg => "signal sent to server" }] )
35
32
  end
36
33
 
34
+
37
35
  it "sends a 'reload' command to the control port when #reload is called" do
38
36
  expect( @socket ).to receive( :<< ).with( "12:6:reload,0:}]" )
39
37
  expect( @socket ).to receive( :receive ).
@@ -41,6 +39,7 @@ describe Mongrel2::Control do
41
39
  expect( @control.reload ).to eq( [{ :msg => "signal sent to server" }] )
42
40
  end
43
41
 
42
+
44
43
  it "sends a 'terminate' command to the control port when #terminate is called" do
45
44
  expect( @socket ).to receive( :<< ).with( "15:9:terminate,0:}]" )
46
45
  expect( @socket ).to receive( :receive ).
@@ -48,6 +47,7 @@ describe Mongrel2::Control do
48
47
  expect( @control.terminate ).to eq( [{ :msg => "signal sent to server" }] )
49
48
  end
50
49
 
50
+
51
51
  it "sends a 'help' command to the control port when #help is called" do
52
52
  expect( @socket ).to receive( :<< ).with( "10:4:help,0:}]" )
53
53
  expect( @socket ).to receive( :receive ).and_return(
@@ -75,6 +75,7 @@ describe Mongrel2::Control do
75
75
  ])
76
76
  end
77
77
 
78
+
78
79
  it "sends a 'uuid' command to the control port when #uuid is called" do
79
80
  expect( @socket ).to receive( :<< ).with( "10:4:uuid,0:}]" )
80
81
  expect( @socket ).to receive( :receive ).and_return(
@@ -84,6 +85,7 @@ describe Mongrel2::Control do
84
85
  expect( @control.uuid ).to eq( [{ :uuid => '34D8E57C-3E91-4F24-9BBE-0B53C1827CB4' }] )
85
86
  end
86
87
 
88
+
87
89
  it "sends an 'info' command to the control port when #info is called" do
88
90
  expect( @socket ).to receive( :<< ).with( "10:4:info,0:}]" )
89
91
  expect( @socket ).to receive( :receive ).and_return(
@@ -106,6 +108,7 @@ describe Mongrel2::Control do
106
108
  }])
107
109
  end
108
110
 
111
+
109
112
  it "sends a 'status' command with a 'what' option set to 'tasks' to the control port " +
110
113
  "when #tasklist is called" do
111
114
 
@@ -129,6 +132,7 @@ describe Mongrel2::Control do
129
132
  ])
130
133
  end
131
134
 
135
+
132
136
  it "sends an 'status' command with a 'what' option set to 'net' to the control port " +
133
137
  "when #conn_status is called" do
134
138
 
@@ -144,6 +148,7 @@ describe Mongrel2::Control do
144
148
  ])
145
149
  end
146
150
 
151
+
147
152
  it "sends a 'time' command to the control port when #time is called" do
148
153
  expect( @socket ).to receive( :<< ).with( "10:4:time,0:}]" )
149
154
  expect( @socket ).to receive( :receive ).
@@ -151,6 +156,7 @@ describe Mongrel2::Control do
151
156
  expect( @control.time ).to eq( Time.at( 1315532674 ) )
152
157
  end
153
158
 
159
+
154
160
  it "sends a 'kill' command with an ID equal to the argument to the control port when #kill " +
155
161
  "is called" do
156
162
  expect( @socket ).to receive( :<< ).with( "19:4:kill,9:2:id,1:0#}]" )
@@ -159,6 +165,7 @@ describe Mongrel2::Control do
159
165
  expect( @control.kill( 0 ) ).to eq( [{ :status => "OK" }] )
160
166
  end
161
167
 
168
+
162
169
  it "sends a 'control_stop' command to the control port when #info is called" do
163
170
  expect( @socket ).to receive( :<< ).with( "19:12:control_stop,0:}]" )
164
171
  expect( @socket ).to receive( :receive ).
@@ -85,6 +85,7 @@ describe Mongrel2::Handler, :db do
85
85
  Mongrel2::Config::Handler.dataset.truncate
86
86
  end
87
87
 
88
+
88
89
  it "raises an exception if no handler with its appid exists in the config DB" do
89
90
  Mongrel2::Config::Handler.dataset.truncate
90
91
  expect {
@@ -95,6 +96,7 @@ describe Mongrel2::Handler, :db do
95
96
  end
96
97
 
97
98
 
99
+
98
100
  it "responds to HTTP requests with a 204 No Content response by default" do
99
101
  request = make_request_object()
100
102
  handler = TestingHandler.new( TEST_UUID, TEST_SEND_SPEC, TEST_RECV_SPEC )
@@ -268,6 +270,7 @@ describe Mongrel2::Handler, :db do
268
270
  handler = TestingHandler.new( TEST_UUID, TEST_SEND_SPEC, TEST_RECV_SPEC )
269
271
  original_conn = handler.conn
270
272
 
273
+ handler.reactor = instance_double( CZTop::Reactor )
271
274
  expect( handler.reactor ).to receive( :unregister ).with( original_conn.request_sock )
272
275
  expect( handler.reactor ).to receive( :register ) do |request_sock, mode, &callback|
273
276
  expect( request_sock ).to be_a( CZTop::Socket )
@@ -300,5 +303,6 @@ describe Mongrel2::Handler, :db do
300
303
  expect( request.body ).to be_closed
301
304
  expect( spoolfile ).to_not exist
302
305
  end
306
+
303
307
  end
304
308
 
@@ -32,6 +32,7 @@ describe Mongrel2::HTTPRequest do
32
32
  expect( result.conn_id ).to eq( @req.conn_id )
33
33
  end
34
34
 
35
+
35
36
  it "remembers its corresponding HTTPResponse if it's created it already" do
36
37
  result = @req.response
37
38
  expect( result ).to be_a( Mongrel2::HTTPResponse )
@@ -39,17 +40,20 @@ describe Mongrel2::HTTPRequest do
39
40
  expect( result.conn_id ).to eq( @req.conn_id )
40
41
  end
41
42
 
43
+
42
44
  it "knows that its connection isn't persistent if it's an HTTP/1.0 request" do
43
45
  @req.headers.version = 'HTTP/1.0'
44
46
  expect( @req ).to_not be_keepalive()
45
47
  end
46
48
 
49
+
47
50
  it "knows that its connection isn't persistent if has a 'close' token in its Connection header" do
48
51
  @req.headers.version = 'HTTP/1.1'
49
52
  @req.headers[ :connection ] = 'violent, close'
50
53
  expect( @req ).to_not be_keepalive()
51
54
  end
52
55
 
56
+
53
57
  it "knows that its connection could be persistent if doesn't have a Connection header, " +
54
58
  "and it's an HTTP/1.1 request" do
55
59
  @req.headers.version = 'HTTP/1.1'
@@ -57,6 +61,7 @@ describe Mongrel2::HTTPRequest do
57
61
  expect( @req ).to be_keepalive()
58
62
  end
59
63
 
64
+
60
65
  it "knows that its connection is persistent if has a Connection header without a 'close' " +
61
66
  "token and it's an HTTP/1.1 request" do
62
67
  @req.headers.version = 'HTTP/1.1'
@@ -64,20 +69,24 @@ describe Mongrel2::HTTPRequest do
64
69
  expect( @req ).to be_keepalive()
65
70
  end
66
71
 
72
+
67
73
  it "knows what its URL scheme was" do
68
74
  expect( @req.scheme ).to eq( 'http' )
69
75
  end
70
76
 
77
+
71
78
  it "falls back to 'http' if the url_scheme isn't provided (mongrel2 <= 1.8.0)" do
72
79
  @req.headers.url_scheme = nil
73
80
  expect( @req.scheme ).to eq( 'http' )
74
81
  end
75
82
 
83
+
76
84
  it "knows that it was an SSL-encrypted request if its scheme was 'https'" do
77
85
  @req.headers.url_scheme = 'https'
78
86
  expect( @req ).to be_secure()
79
87
  end
80
88
 
89
+
81
90
  it "doesn't error when inspecting a bodiless instance" do
82
91
  # I don't remember what circumstances this is guarding against, so this is a bit
83
92
  # artificial
@@ -96,33 +105,40 @@ describe Mongrel2::HTTPRequest do
96
105
  )
97
106
  end
98
107
 
108
+
99
109
  it "provides a convenience method for fetching the 'Content-type' header" do
100
110
  expect( @req.content_type ).to eq( 'application/x-pdf' )
101
111
  end
102
112
 
113
+
103
114
  it "provides a convenience method for resetting the 'Content-type' header" do
104
115
  @req.content_type = 'application/json'
105
116
  expect( @req.content_type ).to eq( 'application/json' )
106
117
  end
107
118
 
119
+
108
120
  it "provides a convenience method for fetching the 'Content-encoding' header" do
109
121
  expect( @req.content_encoding ).to eq( 'gzip' )
110
122
  end
111
123
 
124
+
112
125
  it "provides a convenience method for resetting the 'Content-encoding' header" do
113
126
  @req.content_encoding = 'identity'
114
127
  expect( @req.content_encoding ).to eq( 'identity' )
115
128
  end
116
129
 
130
+
117
131
  it "provides a convenience method for fetching the request's Content-length header" do
118
132
  expect( @req.content_length ).to eq( 28113 )
119
133
  end
120
134
 
135
+
121
136
  it "returns 0 as the content_length if the request doesn't have a Content-length header" do
122
137
  @req.headers.delete( :content_length )
123
138
  expect( @req.content_length ).to eq( 0 )
124
139
  end
125
140
 
141
+
126
142
  it "raises an exception if the Content-length header contains something other than an integer" do
127
143
  @req.headers.content_length = 'Lots'
128
144
  expect {
@@ -23,15 +23,18 @@ describe Mongrel2::HTTPResponse do
23
23
  expect( @response.headers ).to be_a( Mongrel2::Table )
24
24
  end
25
25
 
26
+
26
27
  it "allows headers to be set when the response is created" do
27
28
  response = Mongrel2::HTTPResponse.new( TEST_UUID, 299, :content_type => 'image/jpeg' )
28
29
  expect( response.headers.content_type ).to eq( 'image/jpeg' )
29
30
  end
30
31
 
32
+
31
33
  it "is a No Content response if not set otherwise" do
32
34
  expect( @response.status_line ).to eq( 'HTTP/1.1 204 No Content' )
33
35
  end
34
36
 
37
+
35
38
  it "returns an empty response if its status is set to NO_CONTENT" do
36
39
  @response.puts "The response body"
37
40
  @response.status = HTTP::NO_CONTENT
@@ -40,6 +43,7 @@ describe Mongrel2::HTTPResponse do
40
43
  expect( @response.to_s ).to_not match( /The response body/i )
41
44
  end
42
45
 
46
+
43
47
  it "sets Date, Content-type, and Content-length headers automatically if they haven't been set" do
44
48
  @response << "Some stuff."
45
49
 
@@ -48,6 +52,7 @@ describe Mongrel2::HTTPResponse do
48
52
  expect( @response.header_data ).to match( /Date: #{HTTP_DATE}/i )
49
53
  end
50
54
 
55
+
51
56
  it "re-calculates the automatically-added headers when re-rendered" do
52
57
  expect( @response.header_data ).to match( /Content-length: 0\b/i )
53
58
  @response.status = HTTP::OK
@@ -55,18 +60,22 @@ describe Mongrel2::HTTPResponse do
55
60
  expect( @response.header_data ).to match( /Content-length: 10\b/i )
56
61
  end
57
62
 
63
+
58
64
  it "doesn't have a body" do
59
65
  expect( @response.body.size ).to eq( 0 )
60
66
  end
61
67
 
68
+
62
69
  it "stringifies to a valid RFC2616 response string" do
63
70
  expect( @response.to_s ).to match( HTTP_RESPONSE )
64
71
  end
65
72
 
73
+
66
74
  it "has some default headers" do
67
75
  expect( @response.headers['Server'] ).to eq( Mongrel2.version_string( true ) )
68
76
  end
69
77
 
78
+
70
79
  it "can be reset to a pristine state" do
71
80
  @response.body << "Some stuff we want to get rid of later"
72
81
  @response.headers['x-lunch-packed-by'] = 'Your Mom'
@@ -80,15 +89,18 @@ describe Mongrel2::HTTPResponse do
80
89
  expect( @response.headers.size ).to eq( 1 )
81
90
  end
82
91
 
92
+
83
93
  it "sets its status line to 200 OK if the body is set and the status hasn't yet been set" do
84
94
  @response << "Some stuff"
85
95
  expect( @response.status_line ).to eq( 'HTTP/1.1 200 OK' )
86
96
  end
87
97
 
98
+
88
99
  it "sets its status line to 204 No Content if the body is set and the status hasn't yet been set" do
89
100
  expect( @response.status_line ).to eq( 'HTTP/1.1 204 No Content' )
90
101
  end
91
102
 
103
+
92
104
  it "can find the length of its body if it's a String" do
93
105
  test_body = 'A string full of stuff'
94
106
  @response.body = test_body
@@ -96,6 +108,7 @@ describe Mongrel2::HTTPResponse do
96
108
  expect( @response.get_content_length ).to eq( test_body.length )
97
109
  end
98
110
 
111
+
99
112
  it "can find the length of its body if it's a String with multi-byte characters in it" do
100
113
  test_body = 'Хорошая собака, Стрелке! Очень хорошо.'
101
114
  @response << test_body
@@ -103,6 +116,7 @@ describe Mongrel2::HTTPResponse do
103
116
  expect( @response.get_content_length ).to eq( test_body.bytesize )
104
117
  end
105
118
 
119
+
106
120
  it "can find the length of its body if it's a seekable IO" do
107
121
  test_body = File.open( __FILE__, 'r' )
108
122
  test_body.seek( 0, IO::SEEK_END )
@@ -114,6 +128,7 @@ describe Mongrel2::HTTPResponse do
114
128
  expect( @response.get_content_length ).to eq( length )
115
129
  end
116
130
 
131
+
117
132
  it "can find the length of its body even if it's an IO that's been set to do a partial read" do
118
133
  test_body = File.open( __FILE__, 'r' )
119
134
  test_body.seek( 0, IO::SEEK_END )
@@ -125,12 +140,14 @@ describe Mongrel2::HTTPResponse do
125
140
  expect( @response.get_content_length ).to eq( length - 100 )
126
141
  end
127
142
 
143
+
128
144
  it "knows whether or not it has been handled" do
129
145
  expect( @response ).to_not be_handled()
130
146
  @response.status = HTTP::OK
131
147
  expect( @response ).to be_handled()
132
148
  end
133
149
 
150
+
134
151
  it "knows that it has been handled even if the status is set to NOT_FOUND" do
135
152
  @response.reset
136
153
  @response.status = HTTP::NOT_FOUND
@@ -271,6 +288,7 @@ describe Mongrel2::HTTPResponse do
271
288
  expect( @response ).to be_keepalive()
272
289
  end
273
290
 
291
+
274
292
  it "has a puts method for appending objects to the body" do
275
293
  @response.puts( :something_to_sable )
276
294
  @response.body.rewind
@@ -56,6 +56,7 @@ describe Mongrel2::Request, :db do
56
56
  expect( req.headers['Host'] ).to eq( TEST_HEADERS['host'] )
57
57
  end
58
58
 
59
+
59
60
  it "can parse a request message with TNetstring headers" do
60
61
 
61
62
  message = make_tn_request()
@@ -69,6 +70,7 @@ describe Mongrel2::Request, :db do
69
70
  expect( req.headers.host ).to eq( TEST_HEADERS['host'] )
70
71
  end
71
72
 
73
+
72
74
  it "can parse a request message with a JSON body" do
73
75
 
74
76
  message = make_json_request()
@@ -84,6 +86,7 @@ describe Mongrel2::Request, :db do
84
86
  expect( req.data ).to eq( TEST_JSON_BODY )
85
87
  end
86
88
 
89
+
87
90
  it "raises an UnhandledMethodError with the name of the method for METHOD verbs that " +
88
91
  "don't look like HTTP ones" do
89
92
 
@@ -91,6 +94,7 @@ describe Mongrel2::Request, :db do
91
94
  expect { Mongrel2::Request.parse(message) }.to raise_error( Mongrel2::UnhandledMethodError, /!DIVULGE/ )
92
95
  end
93
96
 
97
+
94
98
  it "knows what kind of response it should return" do
95
99
  expect( Mongrel2::Request.response_class ).to eq( Mongrel2::Response )
96
100
  end
@@ -103,6 +107,7 @@ describe Mongrel2::Request, :db do
103
107
  @req = Mongrel2::Request.parse( message )
104
108
  end
105
109
 
110
+
106
111
  it "can return an appropriate response instance for themselves" do
107
112
  result = @req.response
108
113
  expect( result ).to be_a( Mongrel2::Response )
@@ -110,22 +115,26 @@ describe Mongrel2::Request, :db do
110
115
  expect( result.conn_id ).to eq( @req.conn_id )
111
116
  end
112
117
 
118
+
113
119
  it "remembers its response if it's already made one" do
114
120
  expect( @req.response ).to equal( @req.response )
115
121
  end
116
122
 
123
+
117
124
  it "allows the entity body to be replaced by assigning a String" do
118
125
  @req.body = 'something else'
119
126
  expect( @req.body ).to be_a( StringIO )
120
127
  expect( @req.body.string ).to eq( 'something else' )
121
128
  end
122
129
 
130
+
123
131
  it "doesn't try to wrap non-stringish entity body replacements in a StringIO" do
124
132
  testobj = Object.new
125
133
  @req.body = testobj
126
134
  expect( @req.body ).to be( testobj )
127
135
  end
128
136
 
137
+
129
138
  it "provides a convenience method for fetching the requestor's IP address" do
130
139
  @req.headers.merge!(
131
140
  'X-Forwarded-For' => '127.0.0.1'
@@ -133,6 +142,7 @@ describe Mongrel2::Request, :db do
133
142
  expect( @req.remote_ip.to_s ).to eq( '127.0.0.1' )
134
143
  end
135
144
 
145
+
136
146
  it "fetching the requestor's IP address even when travelling via proxies" do
137
147
  @req.headers.merge!(
138
148
  'X-Forwarded-For' => [ '127.0.0.1', '8.8.8.8', '4.4.4.4' ]
@@ -140,6 +150,7 @@ describe Mongrel2::Request, :db do
140
150
  expect( @req.remote_ip.to_s ).to eq( '127.0.0.1' )
141
151
  end
142
152
 
153
+
143
154
  it "can look up the chroot directory of the server the request is from" do
144
155
  Mongrel2::Config::Server.first.update( chroot: '/usr/local/www' )
145
156
  expect( @req.server_chroot ).to be_a( Pathname )
@@ -165,6 +176,7 @@ describe Mongrel2::Request, :db do
165
176
  expect( req.body.string.encoding ).to be( Encoding::ISO_8859_1 )
166
177
  end
167
178
 
179
+
168
180
  it "keeps the data as ascii-8bit if no charset is in the content-type header" do
169
181
  body = "some data".encode( 'binary' )
170
182
  req = @factory.post( '/form', body, content_type: 'application/octet-stream' )
@@ -172,6 +184,7 @@ describe Mongrel2::Request, :db do
172
184
  expect( req.body.string.encoding ).to be( Encoding::ASCII_8BIT )
173
185
  end
174
186
 
187
+
175
188
  it "keeps the data as ascii-8bit if there is no content-type header" do
176
189
  body = "some data".encode( 'binary' )
177
190
  req = @factory.post( '/form', body )
@@ -210,6 +223,7 @@ describe Mongrel2::Request, :db do
210
223
  expect( Mongrel2::Request.subclass_for_method( 'JSON' ) ).to eq( subclass )
211
224
  end
212
225
 
226
+
213
227
  it "includes a mechanism for overriding the Request subclass for a particular request " +
214
228
  "method" do
215
229
  subclass = Class.new( Mongrel2::Request ) do
@@ -221,6 +235,7 @@ describe Mongrel2::Request, :db do
221
235
  expect( Mongrel2::Request.subclass_for_method( 'JSON' ) ).to_not eq( subclass )
222
236
  end
223
237
 
238
+
224
239
  it "clears any cached method -> subclass lookups when the default subclass changes" do
225
240
  Mongrel2::Request.subclass_for_method( 'OPTIONS' ) # cache OPTIONS -> Mongrel2::Request
226
241
 
@@ -242,6 +257,7 @@ describe Mongrel2::Request, :db do
242
257
  @spoolpath = @spoolfile.path.slice( Dir.tmpdir.length + 1..-1 )
243
258
  end
244
259
 
260
+
245
261
  it "knows if it's an 'async upload started' notification" do
246
262
  req = @factory.post( '/form', '', x_mongrel2_upload_start: @spoolpath )
247
263
 
@@ -249,6 +265,7 @@ describe Mongrel2::Request, :db do
249
265
  expect( req ).to_not be_upload_done()
250
266
  end
251
267
 
268
+
252
269
  it "knows if it's an 'async upload done' notification" do
253
270
  req = @factory.post( '/form', '',
254
271
  x_mongrel2_upload_start: @spoolpath,
@@ -259,6 +276,7 @@ describe Mongrel2::Request, :db do
259
276
  expect( req ).to be_valid_upload()
260
277
  end
261
278
 
279
+
262
280
  it "knows if it's not a valid 'async upload done' notification" do
263
281
  req = @factory.post( '/form', '',
264
282
  x_mongrel2_upload_start: @spoolpath,
@@ -269,6 +287,7 @@ describe Mongrel2::Request, :db do
269
287
  expect( req ).to_not be_valid_upload()
270
288
  end
271
289
 
290
+
272
291
  it "raises an exception if the uploaded file fetched with mismatched headers" do
273
292
  req = @factory.post( '/form', '',
274
293
  x_mongrel2_upload_start: @spoolpath,
@@ -279,6 +298,7 @@ describe Mongrel2::Request, :db do
279
298
  }.to raise_error( Mongrel2::UploadError, /upload headers/i )
280
299
  end
281
300
 
301
+
282
302
  it "can return a Pathname object for the uploaded file if it's valid" do
283
303
  req = @factory.post( '/form', '',
284
304
  x_mongrel2_upload_start: @spoolpath,
@@ -290,6 +310,7 @@ describe Mongrel2::Request, :db do
290
310
  expect( req.uploaded_file.to_s ).to eq( @spoolfile.path )
291
311
  end
292
312
 
313
+
293
314
  it "sets the body of the request to the uploaded File if it's valid" do
294
315
  req = @factory.post( '/form', '',
295
316
  x_mongrel2_upload_start: @spoolpath,