rack 1.5.5 → 1.6.0.beta

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/KNOWN-ISSUES +14 -0
  3. data/README.rdoc +10 -6
  4. data/Rakefile +3 -4
  5. data/SPEC +59 -23
  6. data/lib/rack.rb +2 -1
  7. data/lib/rack/auth/abstract/request.rb +1 -1
  8. data/lib/rack/auth/basic.rb +1 -1
  9. data/lib/rack/auth/digest/md5.rb +1 -1
  10. data/lib/rack/backports/uri/common_18.rb +1 -1
  11. data/lib/rack/builder.rb +19 -4
  12. data/lib/rack/cascade.rb +2 -2
  13. data/lib/rack/chunked.rb +12 -1
  14. data/lib/rack/commonlogger.rb +13 -5
  15. data/lib/rack/conditionalget.rb +14 -2
  16. data/lib/rack/content_length.rb +5 -1
  17. data/lib/rack/deflater.rb +52 -13
  18. data/lib/rack/directory.rb +8 -2
  19. data/lib/rack/etag.rb +14 -6
  20. data/lib/rack/file.rb +10 -14
  21. data/lib/rack/handler.rb +2 -0
  22. data/lib/rack/handler/fastcgi.rb +4 -1
  23. data/lib/rack/handler/mongrel.rb +8 -2
  24. data/lib/rack/handler/scgi.rb +4 -1
  25. data/lib/rack/handler/thin.rb +8 -2
  26. data/lib/rack/handler/webrick.rb +46 -6
  27. data/lib/rack/head.rb +7 -2
  28. data/lib/rack/lint.rb +73 -25
  29. data/lib/rack/lobster.rb +8 -3
  30. data/lib/rack/methodoverride.rb +14 -3
  31. data/lib/rack/mime.rb +1 -15
  32. data/lib/rack/mock.rb +15 -7
  33. data/lib/rack/multipart.rb +2 -2
  34. data/lib/rack/multipart/parser.rb +107 -53
  35. data/lib/rack/multipart/uploaded_file.rb +2 -2
  36. data/lib/rack/nulllogger.rb +21 -2
  37. data/lib/rack/request.rb +38 -24
  38. data/lib/rack/response.rb +5 -0
  39. data/lib/rack/sendfile.rb +10 -5
  40. data/lib/rack/server.rb +45 -17
  41. data/lib/rack/session/abstract/id.rb +7 -6
  42. data/lib/rack/session/cookie.rb +17 -7
  43. data/lib/rack/session/memcache.rb +4 -4
  44. data/lib/rack/session/pool.rb +3 -6
  45. data/lib/rack/showexceptions.rb +20 -11
  46. data/lib/rack/showstatus.rb +1 -1
  47. data/lib/rack/static.rb +27 -30
  48. data/lib/rack/tempfile_reaper.rb +22 -0
  49. data/lib/rack/urlmap.rb +17 -3
  50. data/lib/rack/utils.rb +78 -47
  51. data/lib/rack/utils/okjson.rb +90 -91
  52. data/rack.gemspec +3 -3
  53. data/test/multipart/filename_and_no_name +6 -0
  54. data/test/multipart/invalid_character +6 -0
  55. data/test/spec_builder.rb +13 -4
  56. data/test/spec_chunked.rb +16 -0
  57. data/test/spec_commonlogger.rb +36 -0
  58. data/test/spec_content_length.rb +3 -1
  59. data/test/spec_deflater.rb +283 -148
  60. data/test/spec_etag.rb +11 -2
  61. data/test/spec_file.rb +11 -3
  62. data/test/spec_head.rb +2 -0
  63. data/test/spec_lobster.rb +1 -1
  64. data/test/spec_mock.rb +8 -0
  65. data/test/spec_multipart.rb +111 -49
  66. data/test/spec_request.rb +109 -25
  67. data/test/spec_response.rb +30 -0
  68. data/test/spec_server.rb +20 -5
  69. data/test/spec_session_cookie.rb +45 -2
  70. data/test/spec_session_memcache.rb +1 -1
  71. data/test/spec_showexceptions.rb +29 -36
  72. data/test/spec_showstatus.rb +19 -0
  73. data/test/spec_tempfile_reaper.rb +63 -0
  74. data/test/spec_urlmap.rb +23 -0
  75. data/test/spec_utils.rb +60 -10
  76. data/test/spec_webrick.rb +41 -0
  77. metadata +12 -9
  78. data/test/cgi/lighttpd.errors +0 -1
  79. data/test/multipart/three_files_three_fields +0 -31
@@ -62,7 +62,9 @@ describe Rack::ContentLength do
62
62
  end.new(%w[one two three])
63
63
 
64
64
  app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
65
- content_length(app).call(request)
65
+ response = content_length(app).call(request)
66
+ body.closed.should.equal nil
67
+ response[2].close
66
68
  body.closed.should.equal true
67
69
  end
68
70
 
@@ -6,199 +6,334 @@ require 'rack/mock'
6
6
  require 'zlib'
7
7
 
8
8
  describe Rack::Deflater do
9
- def deflater(app)
10
- Rack::Lint.new Rack::Deflater.new(app)
11
- end
12
9
 
13
- def build_response(status, body, accept_encoding, headers = {})
14
- body = [body] if body.respond_to? :to_str
10
+ def build_response(status, body, accept_encoding, options = {})
11
+ body = [body] if body.respond_to? :to_str
15
12
  app = lambda do |env|
16
- res = [status, {}, body]
17
- res[1]["Content-Type"] = "text/plain" unless res[0] == 304
13
+ res = [status, options['response_headers'] || {}, body]
14
+ res[1]['Content-Type'] = 'text/plain' unless res[0] == 304
18
15
  res
19
16
  end
20
- request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
21
- response = deflater(app).call(request)
22
17
 
23
- return response
24
- end
18
+ request = Rack::MockRequest.env_for('', (options['request_headers'] || {}).merge('HTTP_ACCEPT_ENCODING' => accept_encoding))
19
+ deflater = Rack::Lint.new Rack::Deflater.new(app, options['deflater_options'] || {})
25
20
 
26
- def inflate(buf)
27
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
28
- inflater.inflate(buf) << inflater.finish
21
+ deflater.call(request)
29
22
  end
30
23
 
31
- should "be able to deflate bodies that respond to each" do
32
- body = Object.new
33
- class << body; def each; yield("foo"); yield("bar"); end; end
34
-
35
- response = build_response(200, body, "deflate")
36
-
37
- response[0].should.equal(200)
38
- response[1].should.equal({
39
- "Content-Encoding" => "deflate",
40
- "Vary" => "Accept-Encoding",
41
- "Content-Type" => "text/plain"
42
- })
43
- buf = ''
44
- response[2].each { |part| buf << part }
45
- inflate(buf).should.equal("foobar")
46
- end
47
-
48
- should "flush deflated chunks to the client as they become ready" do
49
- body = Object.new
50
- class << body; def each; yield("foo"); yield("bar"); end; end
24
+ ##
25
+ # Constructs response object and verifies if it yields right results
26
+ #
27
+ # [expected_status] expected response status, e.g. 200, 304
28
+ # [expected_body] expected response body
29
+ # [accept_encoing] what Accept-Encoding header to send and expect, e.g.
30
+ # 'deflate' - accepts and expects deflate encoding in response
31
+ # { 'gzip' => nil } - accepts gzip but expects no encoding in response
32
+ # [options] hash of request options, i.e.
33
+ # 'app_status' - what status dummy app should return (may be changed by deflater at some point)
34
+ # 'app_body' - what body dummy app should return (may be changed by deflater at some point)
35
+ # 'request_headers' - extra reqest headers to be sent
36
+ # 'response_headers' - extra response headers to be returned
37
+ # 'deflater_options' - options passed to deflater middleware
38
+ # [block] useful for doing some extra verification
39
+ def verify(expected_status, expected_body, accept_encoding, options = {}, &block)
40
+ accept_encoding, expected_encoding = if accept_encoding.kind_of?(Hash)
41
+ [accept_encoding.keys.first, accept_encoding.values.first]
42
+ else
43
+ [accept_encoding, accept_encoding.dup]
44
+ end
51
45
 
52
- response = build_response(200, body, "deflate")
46
+ # build response
47
+ status, headers, body = build_response(
48
+ options['app_status'] || expected_status,
49
+ options['app_body'] || expected_body,
50
+ accept_encoding,
51
+ options
52
+ )
53
+
54
+ # verify status
55
+ status.should.equal(expected_status)
56
+
57
+ # verify body
58
+ unless options['skip_body_verify']
59
+ body_text = ''
60
+ body.each { |part| body_text << part }
61
+
62
+ deflated_body = case expected_encoding
63
+ when 'deflate'
64
+ inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
65
+ inflater.inflate(body_text) << inflater.finish
66
+ when 'gzip'
67
+ io = StringIO.new(body_text)
68
+ gz = Zlib::GzipReader.new(io)
69
+ tmp = gz.read
70
+ gz.close
71
+ tmp
72
+ else
73
+ body_text
74
+ end
75
+
76
+ deflated_body.should.equal(expected_body)
77
+ end
53
78
 
54
- response[0].should.equal(200)
55
- response[1].should.equal({
56
- "Content-Encoding" => "deflate",
57
- "Vary" => "Accept-Encoding",
58
- "Content-Type" => "text/plain"
59
- })
60
- buf = []
61
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
62
- response[2].each { |part| buf << inflater.inflate(part) }
63
- buf << inflater.finish
64
- buf.delete_if { |part| part.empty? }
65
- buf.join.should.equal("foobar")
79
+ # yield full response verification
80
+ yield(status, headers, body) if block_given?
66
81
  end
67
82
 
68
- # TODO: This is really just a special case of the above...
69
- should "be able to deflate String bodies" do
70
- response = build_response(200, "Hello world!", "deflate")
83
+ should 'be able to deflate bodies that respond to each' do
84
+ app_body = Object.new
85
+ class << app_body; def each; yield('foo'); yield('bar'); end; end
71
86
 
72
- response[0].should.equal(200)
73
- response[1].should.equal({
74
- "Content-Encoding" => "deflate",
75
- "Vary" => "Accept-Encoding",
76
- "Content-Type" => "text/plain"
77
- })
78
- buf = ''
79
- response[2].each { |part| buf << part }
80
- inflate(buf).should.equal("Hello world!")
87
+ verify(200, 'foobar', 'deflate', { 'app_body' => app_body }) do |status, headers, body|
88
+ headers.should.equal({
89
+ 'Content-Encoding' => 'deflate',
90
+ 'Vary' => 'Accept-Encoding',
91
+ 'Content-Type' => 'text/plain'
92
+ })
93
+ end
81
94
  end
82
95
 
83
- should "be able to gzip bodies that respond to each" do
84
- body = Object.new
85
- class << body; def each; yield("foo"); yield("bar"); end; end
96
+ should 'flush deflated chunks to the client as they become ready' do
97
+ app_body = Object.new
98
+ class << app_body; def each; yield('foo'); yield('bar'); end; end
86
99
 
87
- response = build_response(200, body, "gzip")
100
+ verify(200, app_body, 'deflate', { 'skip_body_verify' => true }) do |status, headers, body|
101
+ headers.should.equal({
102
+ 'Content-Encoding' => 'deflate',
103
+ 'Vary' => 'Accept-Encoding',
104
+ 'Content-Type' => 'text/plain'
105
+ })
88
106
 
89
- response[0].should.equal(200)
90
- response[1].should.equal({
91
- "Content-Encoding" => "gzip",
92
- "Vary" => "Accept-Encoding",
93
- "Content-Type" => "text/plain"
94
- })
107
+ buf = []
108
+ inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
109
+ body.each { |part| buf << inflater.inflate(part) }
110
+ buf << inflater.finish
95
111
 
96
- buf = ''
97
- response[2].each { |part| buf << part }
98
- io = StringIO.new(buf)
99
- gz = Zlib::GzipReader.new(io)
100
- gz.read.should.equal("foobar")
101
- gz.close
112
+ buf.delete_if { |part| part.empty? }.join.should.equal('foobar')
113
+ end
102
114
  end
103
115
 
104
- should "flush gzipped chunks to the client as they become ready" do
105
- body = Object.new
106
- class << body; def each; yield("foo"); yield("bar"); end; end
116
+ # TODO: This is really just a special case of the above...
117
+ should 'be able to deflate String bodies' do
118
+ verify(200, 'Hello world!', 'deflate') do |status, headers, body|
119
+ headers.should.equal({
120
+ 'Content-Encoding' => 'deflate',
121
+ 'Vary' => 'Accept-Encoding',
122
+ 'Content-Type' => 'text/plain'
123
+ })
124
+ end
125
+ end
107
126
 
108
- response = build_response(200, body, "gzip")
127
+ should 'be able to gzip bodies that respond to each' do
128
+ app_body = Object.new
129
+ class << app_body; def each; yield('foo'); yield('bar'); end; end
109
130
 
110
- response[0].should.equal(200)
111
- response[1].should.equal({
112
- "Content-Encoding" => "gzip",
113
- "Vary" => "Accept-Encoding",
114
- "Content-Type" => "text/plain"
115
- })
116
- buf = []
117
- inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
118
- response[2].each { |part| buf << inflater.inflate(part) }
119
- buf << inflater.finish
120
- buf.delete_if { |part| part.empty? }
121
- buf.join.should.equal("foobar")
131
+ verify(200, 'foobar', 'gzip', { 'app_body' => app_body }) do |status, headers, body|
132
+ headers.should.equal({
133
+ 'Content-Encoding' => 'gzip',
134
+ 'Vary' => 'Accept-Encoding',
135
+ 'Content-Type' => 'text/plain'
136
+ })
137
+ end
122
138
  end
123
139
 
124
- should "be able to fallback to no deflation" do
125
- response = build_response(200, "Hello world!", "superzip")
140
+ should 'flush gzipped chunks to the client as they become ready' do
141
+ app_body = Object.new
142
+ class << app_body; def each; yield('foo'); yield('bar'); end; end
143
+
144
+ verify(200, app_body, 'gzip', { 'skip_body_verify' => true }) do |status, headers, body|
145
+ headers.should.equal({
146
+ 'Content-Encoding' => 'gzip',
147
+ 'Vary' => 'Accept-Encoding',
148
+ 'Content-Type' => 'text/plain'
149
+ })
150
+
151
+ buf = []
152
+ inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
153
+ body.each { |part| buf << inflater.inflate(part) }
154
+ buf << inflater.finish
126
155
 
127
- response[0].should.equal(200)
128
- response[1].should.equal({ "Vary" => "Accept-Encoding", "Content-Type" => "text/plain" })
129
- response[2].to_enum.to_a.should.equal(["Hello world!"])
156
+ buf.delete_if { |part| part.empty? }.join.should.equal('foobar')
157
+ end
130
158
  end
131
159
 
132
- should "be able to skip when there is no response entity body" do
133
- response = build_response(304, [], "gzip")
160
+ should 'be able to fallback to no deflation' do
161
+ verify(200, 'Hello world!', 'superzip') do |status, headers, body|
162
+ headers.should.equal({
163
+ 'Vary' => 'Accept-Encoding',
164
+ 'Content-Type' => 'text/plain'
165
+ })
166
+ end
167
+ end
134
168
 
135
- response[0].should.equal(304)
136
- response[1].should.equal({})
137
- response[2].to_enum.to_a.should.equal([])
169
+ should 'be able to skip when there is no response entity body' do
170
+ verify(304, '', { 'gzip' => nil }, { 'app_body' => [] }) do |status, headers, body|
171
+ headers.should.equal({})
172
+ end
138
173
  end
139
174
 
140
- should "handle the lack of an acceptable encoding" do
141
- response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
142
- response1[0].should.equal(406)
143
- response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
144
- response1[2].to_enum.to_a.should.equal(["An acceptable encoding for the requested resource / could not be found."])
175
+ should 'handle the lack of an acceptable encoding' do
176
+ app_body = 'Hello world!'
177
+ not_found_body1 = 'An acceptable encoding for the requested resource / could not be found.'
178
+ not_found_body2 = 'An acceptable encoding for the requested resource /foo/bar could not be found.'
179
+ options1 = {
180
+ 'app_status' => 200,
181
+ 'app_body' => app_body,
182
+ 'request_headers' => {
183
+ 'PATH_INFO' => '/'
184
+ }
185
+ }
186
+ options2 = {
187
+ 'app_status' => 200,
188
+ 'app_body' => app_body,
189
+ 'request_headers' => {
190
+ 'PATH_INFO' => '/foo/bar'
191
+ }
192
+ }
193
+
194
+ verify(406, not_found_body1, 'identity;q=0', options1) do |status, headers, body|
195
+ headers.should.equal({
196
+ 'Content-Type' => 'text/plain',
197
+ 'Content-Length' => not_found_body1.length.to_s
198
+ })
199
+ end
145
200
 
146
- response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
147
- response2[0].should.equal(406)
148
- response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
149
- response2[2].to_enum.to_a.should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
201
+ verify(406, not_found_body2, 'identity;q=0', options2) do |status, headers, body|
202
+ headers.should.equal({
203
+ 'Content-Type' => 'text/plain',
204
+ 'Content-Length' => not_found_body2.length.to_s
205
+ })
206
+ end
150
207
  end
151
208
 
152
- should "handle gzip response with Last-Modified header" do
209
+ should 'handle gzip response with Last-Modified header' do
153
210
  last_modified = Time.now.httpdate
211
+ options = {
212
+ 'response_headers' => {
213
+ 'Content-Type' => 'text/plain',
214
+ 'Last-Modified' => last_modified
215
+ }
216
+ }
217
+
218
+ verify(200, 'Hello World!', 'gzip', options) do |status, headers, body|
219
+ headers.should.equal({
220
+ 'Content-Encoding' => 'gzip',
221
+ 'Vary' => 'Accept-Encoding',
222
+ 'Last-Modified' => last_modified,
223
+ 'Content-Type' => 'text/plain'
224
+ })
225
+ end
226
+ end
154
227
 
155
- app = lambda { |env| [200, { "Content-Type" => "text/plain", "Last-Modified" => last_modified }, ["Hello World!"]] }
156
- request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
157
- response = deflater(app).call(request)
228
+ should 'do nothing when no-transform Cache-Control directive present' do
229
+ options = {
230
+ 'response_headers' => {
231
+ 'Content-Type' => 'text/plain',
232
+ 'Cache-Control' => 'no-transform'
233
+ }
234
+ }
235
+ verify(200, 'Hello World!', { 'gzip' => nil }, options) do |status, headers, body|
236
+ headers.should.not.include 'Content-Encoding'
237
+ end
238
+ end
239
+
240
+ should 'do nothing when Content-Encoding already present' do
241
+ options = {
242
+ 'response_headers' => {
243
+ 'Content-Type' => 'text/plain',
244
+ 'Content-Encoding' => 'gzip'
245
+ }
246
+ }
247
+ verify(200, 'Hello World!', { 'gzip' => nil }, options)
248
+ end
158
249
 
159
- response[0].should.equal(200)
160
- response[1].should.equal({
161
- "Content-Encoding" => "gzip",
162
- "Vary" => "Accept-Encoding",
163
- "Last-Modified" => last_modified,
164
- "Content-Type" => "text/plain"
165
- })
250
+ should 'deflate when Content-Encoding is identity' do
251
+ options = {
252
+ 'response_headers' => {
253
+ 'Content-Type' => 'text/plain',
254
+ 'Content-Encoding' => 'identity'
255
+ }
256
+ }
257
+ verify(200, 'Hello World!', 'deflate', options)
258
+ end
166
259
 
167
- buf = ''
168
- response[2].each { |part| buf << part }
169
- io = StringIO.new(buf)
170
- gz = Zlib::GzipReader.new(io)
171
- gz.read.should.equal("Hello World!")
172
- gz.close
260
+ should "deflate if content-type matches :include" do
261
+ options = {
262
+ 'response_headers' => {
263
+ 'Content-Type' => 'text/plain'
264
+ },
265
+ 'deflater_options' => {
266
+ :include => %w(text/plain)
267
+ }
268
+ }
269
+ verify(200, 'Hello World!', 'gzip', options)
173
270
  end
174
271
 
175
- should "do nothing when no-transform Cache-Control directive present" do
176
- app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Cache-Control' => 'no-transform'}, ['Hello World!']] }
177
- request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
178
- response = deflater(app).call(request)
272
+ should "deflate if content-type is included it :include" do
273
+ options = {
274
+ 'response_headers' => {
275
+ 'Content-Type' => 'text/plain; charset=us-ascii'
276
+ },
277
+ 'deflater_options' => {
278
+ :include => %w(text/plain)
279
+ }
280
+ }
281
+ verify(200, 'Hello World!', 'gzip', options)
282
+ end
179
283
 
180
- response[0].should.equal(200)
181
- response[1].should.not.include "Content-Encoding"
182
- response[2].to_enum.to_a.join.should.equal("Hello World!")
284
+ should "not deflate if content-type is not set but given in :include" do
285
+ options = {
286
+ 'deflater_options' => {
287
+ :include => %w(text/plain)
288
+ }
289
+ }
290
+ verify(304, 'Hello World!', { 'gzip' => nil }, options)
183
291
  end
184
292
 
185
- should "do nothing when Content-Encoding already present" do
186
- app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Encoding' => 'gzip'}, ['Hello World!']] }
187
- request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
188
- response = deflater(app).call(request)
293
+ should "not deflate if content-type do not match :include" do
294
+ options = {
295
+ 'response_headers' => {
296
+ 'Content-Type' => 'text/plain'
297
+ },
298
+ 'deflater_options' => {
299
+ :include => %w(text/json)
300
+ }
301
+ }
302
+ verify(200, 'Hello World!', { 'gzip' => nil }, options)
303
+ end
189
304
 
190
- response[0].should.equal(200)
191
- response[2].to_enum.to_a.join.should.equal("Hello World!")
305
+ should "deflate response if :if lambda evaluates to true" do
306
+ options = {
307
+ 'deflater_options' => {
308
+ :if => lambda { |env, status, headers, body| true }
309
+ }
310
+ }
311
+ verify(200, 'Hello World!', 'deflate', options)
192
312
  end
193
313
 
194
- should "deflate when Content-Encoding is identity" do
195
- app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Encoding' => 'identity'}, ['Hello World!']] }
196
- request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "deflate")
197
- response = deflater(app).call(request)
314
+ should "not deflate if :if lambda evaluates to false" do
315
+ options = {
316
+ 'deflater_options' => {
317
+ :if => lambda { |env, status, headers, body| false }
318
+ }
319
+ }
320
+ verify(200, 'Hello World!', { 'gzip' => nil }, options)
321
+ end
198
322
 
199
- response[0].should.equal(200)
200
- buf = ''
201
- response[2].each { |part| buf << part }
202
- inflate(buf).should.equal("Hello World!")
323
+ should "check for Content-Length via :if" do
324
+ body = 'Hello World!'
325
+ body_len = body.length
326
+ options = {
327
+ 'response_headers' => {
328
+ 'Content-Length' => body_len.to_s
329
+ },
330
+ 'deflater_options' => {
331
+ :if => lambda { |env, status, headers, body|
332
+ headers['Content-Length'].to_i >= body_len
333
+ }
334
+ }
335
+ }
336
+
337
+ verify(200, body, 'gzip', options)
203
338
  end
204
339
  end