rack 2.0.9.4 → 2.1.0

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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +214 -164
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +79 -133
  5. data/Rakefile +25 -18
  6. data/SPEC +9 -9
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack/auth/abstract/handler.rb +3 -1
  12. data/lib/rack/auth/abstract/request.rb +2 -0
  13. data/lib/rack/auth/basic.rb +4 -1
  14. data/lib/rack/auth/digest/md5.rb +9 -7
  15. data/lib/rack/auth/digest/nonce.rb +6 -3
  16. data/lib/rack/auth/digest/params.rb +4 -2
  17. data/lib/rack/auth/digest/request.rb +2 -0
  18. data/lib/rack/body_proxy.rb +3 -6
  19. data/lib/rack/builder.rb +38 -15
  20. data/lib/rack/cascade.rb +6 -5
  21. data/lib/rack/chunked.rb +29 -6
  22. data/lib/rack/common_logger.rb +9 -11
  23. data/lib/rack/conditional_get.rb +3 -1
  24. data/lib/rack/config.rb +2 -0
  25. data/lib/rack/content_length.rb +3 -1
  26. data/lib/rack/content_type.rb +3 -1
  27. data/lib/rack/core_ext/regexp.rb +14 -0
  28. data/lib/rack/deflater.rb +28 -17
  29. data/lib/rack/directory.rb +17 -14
  30. data/lib/rack/etag.rb +3 -1
  31. data/lib/rack/events.rb +5 -3
  32. data/lib/rack/file.rb +5 -173
  33. data/lib/rack/files.rb +178 -0
  34. data/lib/rack/handler/cgi.rb +3 -1
  35. data/lib/rack/handler/fastcgi.rb +4 -2
  36. data/lib/rack/handler/lsws.rb +3 -1
  37. data/lib/rack/handler/scgi.rb +9 -6
  38. data/lib/rack/handler/thin.rb +3 -1
  39. data/lib/rack/handler/webrick.rb +4 -2
  40. data/lib/rack/handler.rb +7 -2
  41. data/lib/rack/head.rb +2 -0
  42. data/lib/rack/lint.rb +15 -12
  43. data/lib/rack/lobster.rb +7 -5
  44. data/lib/rack/lock.rb +2 -0
  45. data/lib/rack/logger.rb +2 -0
  46. data/lib/rack/media_type.rb +10 -5
  47. data/lib/rack/method_override.rb +4 -2
  48. data/lib/rack/mime.rb +9 -1
  49. data/lib/rack/mock.rb +74 -15
  50. data/lib/rack/multipart/generator.rb +6 -7
  51. data/lib/rack/multipart/parser.rb +55 -62
  52. data/lib/rack/multipart/uploaded_file.rb +2 -0
  53. data/lib/rack/multipart.rb +6 -3
  54. data/lib/rack/null_logger.rb +2 -0
  55. data/lib/rack/query_parser.rb +51 -25
  56. data/lib/rack/recursive.rb +7 -5
  57. data/lib/rack/reloader.rb +10 -4
  58. data/lib/rack/request.rb +79 -26
  59. data/lib/rack/response.rb +71 -31
  60. data/lib/rack/rewindable_input.rb +4 -2
  61. data/lib/rack/runtime.rb +4 -2
  62. data/lib/rack/sendfile.rb +15 -8
  63. data/lib/rack/server.rb +88 -16
  64. data/lib/rack/session/abstract/id.rb +40 -22
  65. data/lib/rack/session/cookie.rb +10 -9
  66. data/lib/rack/session/memcache.rb +4 -93
  67. data/lib/rack/session/pool.rb +4 -2
  68. data/lib/rack/show_exceptions.rb +15 -9
  69. data/lib/rack/show_status.rb +4 -2
  70. data/lib/rack/static.rb +15 -10
  71. data/lib/rack/tempfile_reaper.rb +2 -0
  72. data/lib/rack/urlmap.rb +11 -2
  73. data/lib/rack/utils.rb +64 -93
  74. data/lib/rack.rb +63 -60
  75. data/rack.gemspec +17 -7
  76. metadata +33 -175
  77. data/test/builder/an_underscore_app.rb +0 -5
  78. data/test/builder/anything.rb +0 -5
  79. data/test/builder/comment.ru +0 -4
  80. data/test/builder/end.ru +0 -5
  81. data/test/builder/line.ru +0 -1
  82. data/test/builder/options.ru +0 -2
  83. data/test/cgi/assets/folder/test.js +0 -1
  84. data/test/cgi/assets/fonts/font.eot +0 -1
  85. data/test/cgi/assets/images/image.png +0 -1
  86. data/test/cgi/assets/index.html +0 -1
  87. data/test/cgi/assets/javascripts/app.js +0 -1
  88. data/test/cgi/assets/stylesheets/app.css +0 -1
  89. data/test/cgi/lighttpd.conf +0 -26
  90. data/test/cgi/rackup_stub.rb +0 -6
  91. data/test/cgi/sample_rackup.ru +0 -5
  92. data/test/cgi/test +0 -9
  93. data/test/cgi/test+directory/test+file +0 -1
  94. data/test/cgi/test.fcgi +0 -9
  95. data/test/cgi/test.gz +0 -0
  96. data/test/cgi/test.ru +0 -5
  97. data/test/gemloader.rb +0 -10
  98. data/test/helper.rb +0 -34
  99. data/test/multipart/bad_robots +0 -259
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/content_type_and_no_filename +0 -6
  102. data/test/multipart/empty +0 -10
  103. data/test/multipart/fail_16384_nofile +0 -814
  104. data/test/multipart/file1.txt +0 -1
  105. data/test/multipart/filename_and_modification_param +0 -7
  106. data/test/multipart/filename_and_no_name +0 -6
  107. data/test/multipart/filename_with_encoded_words +0 -7
  108. data/test/multipart/filename_with_escaped_quotes +0 -6
  109. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  110. data/test/multipart/filename_with_null_byte +0 -7
  111. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_single_quote +0 -7
  113. data/test/multipart/filename_with_unescaped_percentages +0 -6
  114. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  116. data/test/multipart/filename_with_unescaped_quotes +0 -6
  117. data/test/multipart/ie +0 -6
  118. data/test/multipart/invalid_character +0 -6
  119. data/test/multipart/mixed_files +0 -21
  120. data/test/multipart/nested +0 -10
  121. data/test/multipart/none +0 -9
  122. data/test/multipart/quoted +0 -15
  123. data/test/multipart/rack-logo.png +0 -0
  124. data/test/multipart/semicolon +0 -6
  125. data/test/multipart/text +0 -15
  126. data/test/multipart/three_files_three_fields +0 -31
  127. data/test/multipart/unity3d_wwwform +0 -11
  128. data/test/multipart/webkit +0 -32
  129. data/test/rackup/config.ru +0 -31
  130. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  131. data/test/spec_auth_basic.rb +0 -89
  132. data/test/spec_auth_digest.rb +0 -260
  133. data/test/spec_body_proxy.rb +0 -85
  134. data/test/spec_builder.rb +0 -233
  135. data/test/spec_cascade.rb +0 -63
  136. data/test/spec_cgi.rb +0 -84
  137. data/test/spec_chunked.rb +0 -103
  138. data/test/spec_common_logger.rb +0 -107
  139. data/test/spec_conditional_get.rb +0 -103
  140. data/test/spec_config.rb +0 -23
  141. data/test/spec_content_length.rb +0 -86
  142. data/test/spec_content_type.rb +0 -46
  143. data/test/spec_deflater.rb +0 -375
  144. data/test/spec_directory.rb +0 -148
  145. data/test/spec_etag.rb +0 -108
  146. data/test/spec_events.rb +0 -133
  147. data/test/spec_fastcgi.rb +0 -85
  148. data/test/spec_file.rb +0 -264
  149. data/test/spec_handler.rb +0 -57
  150. data/test/spec_head.rb +0 -46
  151. data/test/spec_lint.rb +0 -520
  152. data/test/spec_lobster.rb +0 -59
  153. data/test/spec_lock.rb +0 -204
  154. data/test/spec_logger.rb +0 -24
  155. data/test/spec_media_type.rb +0 -42
  156. data/test/spec_method_override.rb +0 -110
  157. data/test/spec_mime.rb +0 -51
  158. data/test/spec_mock.rb +0 -359
  159. data/test/spec_multipart.rb +0 -721
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1423
  163. data/test/spec_response.rb +0 -528
  164. data/test/spec_rewindable_input.rb +0 -128
  165. data/test/spec_runtime.rb +0 -50
  166. data/test/spec_sendfile.rb +0 -125
  167. data/test/spec_server.rb +0 -193
  168. data/test/spec_session_abstract_id.rb +0 -31
  169. data/test/spec_session_abstract_session_hash.rb +0 -45
  170. data/test/spec_session_cookie.rb +0 -442
  171. data/test/spec_session_memcache.rb +0 -357
  172. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  173. data/test/spec_session_pool.rb +0 -247
  174. data/test/spec_show_exceptions.rb +0 -93
  175. data/test/spec_show_status.rb +0 -104
  176. data/test/spec_static.rb +0 -184
  177. data/test/spec_tempfile_reaper.rb +0 -64
  178. data/test/spec_thin.rb +0 -96
  179. data/test/spec_urlmap.rb +0 -237
  180. data/test/spec_utils.rb +0 -742
  181. data/test/spec_version.rb +0 -11
  182. data/test/spec_webrick.rb +0 -206
  183. data/test/static/another/index.html +0 -1
  184. data/test/static/foo.html +0 -1
  185. data/test/static/index.html +0 -1
  186. data/test/testrequest.rb +0 -78
  187. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  188. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,103 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'time'
3
- require 'rack/conditional_get'
4
- require 'rack/mock'
5
-
6
- describe Rack::ConditionalGet do
7
- def conditional_get(app)
8
- Rack::Lint.new Rack::ConditionalGet.new(app)
9
- end
10
-
11
- it "set a 304 status and truncate body when If-Modified-Since hits" do
12
- timestamp = Time.now.httpdate
13
- app = conditional_get(lambda { |env|
14
- [200, {'Last-Modified'=>timestamp}, ['TEST']] })
15
-
16
- response = Rack::MockRequest.new(app).
17
- get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp)
18
-
19
- response.status.must_equal 304
20
- response.body.must_be :empty?
21
- end
22
-
23
- it "set a 304 status and truncate body when If-Modified-Since hits and is higher than current time" do
24
- app = conditional_get(lambda { |env|
25
- [200, {'Last-Modified'=>(Time.now - 3600).httpdate}, ['TEST']] })
26
-
27
- response = Rack::MockRequest.new(app).
28
- get("/", 'HTTP_IF_MODIFIED_SINCE' => Time.now.httpdate)
29
-
30
- response.status.must_equal 304
31
- response.body.must_be :empty?
32
- end
33
-
34
- it "set a 304 status and truncate body when If-None-Match hits" do
35
- app = conditional_get(lambda { |env|
36
- [200, {'ETag'=>'1234'}, ['TEST']] })
37
-
38
- response = Rack::MockRequest.new(app).
39
- get("/", 'HTTP_IF_NONE_MATCH' => '1234')
40
-
41
- response.status.must_equal 304
42
- response.body.must_be :empty?
43
- end
44
-
45
- it "not set a 304 status if If-Modified-Since hits but Etag does not" do
46
- timestamp = Time.now.httpdate
47
- app = conditional_get(lambda { |env|
48
- [200, {'Last-Modified'=>timestamp, 'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
49
-
50
- response = Rack::MockRequest.new(app).
51
- get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '4321')
52
-
53
- response.status.must_equal 200
54
- response.body.must_equal 'TEST'
55
- end
56
-
57
- it "set a 304 status and truncate body when both If-None-Match and If-Modified-Since hits" do
58
- timestamp = Time.now.httpdate
59
- app = conditional_get(lambda { |env|
60
- [200, {'Last-Modified'=>timestamp, 'ETag'=>'1234'}, ['TEST']] })
61
-
62
- response = Rack::MockRequest.new(app).
63
- get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp, 'HTTP_IF_NONE_MATCH' => '1234')
64
-
65
- response.status.must_equal 304
66
- response.body.must_be :empty?
67
- end
68
-
69
- it "not affect non-GET/HEAD requests" do
70
- app = conditional_get(lambda { |env|
71
- [200, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
72
-
73
- response = Rack::MockRequest.new(app).
74
- post("/", 'HTTP_IF_NONE_MATCH' => '1234')
75
-
76
- response.status.must_equal 200
77
- response.body.must_equal 'TEST'
78
- end
79
-
80
- it "not affect non-200 requests" do
81
- app = conditional_get(lambda { |env|
82
- [302, {'Etag'=>'1234', 'Content-Type' => 'text/plain'}, ['TEST']] })
83
-
84
- response = Rack::MockRequest.new(app).
85
- get("/", 'HTTP_IF_NONE_MATCH' => '1234')
86
-
87
- response.status.must_equal 302
88
- response.body.must_equal 'TEST'
89
- end
90
-
91
- it "not affect requests with malformed HTTP_IF_NONE_MATCH" do
92
- bad_timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
93
- app = conditional_get(lambda { |env|
94
- [200,{'Last-Modified'=>(Time.now - 3600).httpdate, 'Content-Type' => 'text/plain'}, ['TEST']] })
95
-
96
- response = Rack::MockRequest.new(app).
97
- get("/", 'HTTP_IF_MODIFIED_SINCE' => bad_timestamp)
98
-
99
- response.status.must_equal 200
100
- response.body.must_equal 'TEST'
101
- end
102
-
103
- end
data/test/spec_config.rb DELETED
@@ -1,23 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/builder'
3
- require 'rack/config'
4
- require 'rack/content_length'
5
- require 'rack/lint'
6
- require 'rack/mock'
7
-
8
- describe Rack::Config do
9
- it "accept a block that modifies the environment" do
10
- app = Rack::Builder.new do
11
- use Rack::Lint
12
- use Rack::Config do |env|
13
- env['greeting'] = 'hello'
14
- end
15
- run lambda { |env|
16
- [200, {'Content-Type' => 'text/plain'}, [env['greeting'] || '']]
17
- }
18
- end
19
-
20
- response = Rack::MockRequest.new(app).get('/')
21
- response.body.must_equal 'hello'
22
- end
23
- end
@@ -1,86 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/content_length'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
-
6
- describe Rack::ContentLength do
7
- def content_length(app)
8
- Rack::Lint.new Rack::ContentLength.new(app)
9
- end
10
-
11
- def request
12
- Rack::MockRequest.env_for
13
- end
14
-
15
- it "set Content-Length on Array bodies if none is set" do
16
- app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
17
- response = content_length(app).call(request)
18
- response[1]['Content-Length'].must_equal '13'
19
- end
20
-
21
- it "not set Content-Length on variable length bodies" do
22
- body = lambda { "Hello World!" }
23
- def body.each ; yield call ; end
24
-
25
- app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
26
- response = content_length(app).call(request)
27
- response[1]['Content-Length'].must_be_nil
28
- end
29
-
30
- it "not change Content-Length if it is already set" do
31
- app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] }
32
- response = content_length(app).call(request)
33
- response[1]['Content-Length'].must_equal '1'
34
- end
35
-
36
- it "not set Content-Length on 304 responses" do
37
- app = lambda { |env| [304, {}, []] }
38
- response = content_length(app).call(request)
39
- response[1]['Content-Length'].must_be_nil
40
- end
41
-
42
- it "not set Content-Length when Transfer-Encoding is chunked" do
43
- app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Transfer-Encoding' => 'chunked'}, []] }
44
- response = content_length(app).call(request)
45
- response[1]['Content-Length'].must_be_nil
46
- end
47
-
48
- # Using "Connection: close" for this is fairly contended. It might be useful
49
- # to have some other way to signal this.
50
- #
51
- # should "not force a Content-Length when Connection:close" do
52
- # app = lambda { |env| [200, {'Connection' => 'close'}, []] }
53
- # response = content_length(app).call({})
54
- # response[1]['Content-Length'].must_be_nil
55
- # end
56
-
57
- it "close bodies that need to be closed" do
58
- body = Struct.new(:body) do
59
- attr_reader :closed
60
- def each; body.join; end
61
- def close; @closed = true; end
62
- def to_ary; end
63
- end.new(%w[one two three])
64
-
65
- app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
66
- response = content_length(app).call(request)
67
- body.closed.must_be_nil
68
- response[2].close
69
- body.closed.must_equal true
70
- end
71
-
72
- it "support single-execute bodies" do
73
- body = Struct.new(:body) do
74
- def each
75
- yield body.shift until body.empty?
76
- end
77
- def to_ary; end
78
- end.new(%w[one two three])
79
-
80
- app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
81
- response = content_length(app).call(request)
82
- expected = %w[one two three]
83
- response[1]['Content-Length'].must_equal expected.join.size.to_s
84
- response[2].to_enum.to_a.must_equal expected
85
- end
86
- end
@@ -1,46 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/content_type'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
-
6
- describe Rack::ContentType do
7
- def content_type(app, *args)
8
- Rack::Lint.new Rack::ContentType.new(app, *args)
9
- end
10
-
11
- def request
12
- Rack::MockRequest.env_for
13
- end
14
-
15
- it "set Content-Type to default text/html if none is set" do
16
- app = lambda { |env| [200, {}, "Hello, World!"] }
17
- headers = content_type(app).call(request)[1]
18
- headers['Content-Type'].must_equal 'text/html'
19
- end
20
-
21
- it "set Content-Type to chosen default if none is set" do
22
- app = lambda { |env| [200, {}, "Hello, World!"] }
23
- headers =
24
- content_type(app, 'application/octet-stream').call(request)[1]
25
- headers['Content-Type'].must_equal 'application/octet-stream'
26
- end
27
-
28
- it "not change Content-Type if it is already set" do
29
- app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] }
30
- headers = content_type(app).call(request)[1]
31
- headers['Content-Type'].must_equal 'foo/bar'
32
- end
33
-
34
- it "detect Content-Type case insensitive" do
35
- app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] }
36
- headers = content_type(app).call(request)[1]
37
- headers.to_a.select { |k,v| k.downcase == "content-type" }.
38
- must_equal [["CONTENT-Type","foo/bar"]]
39
- end
40
-
41
- it "not set Content-Type on 304 responses" do
42
- app = lambda { |env| [304, {}, []] }
43
- response = content_type(app, "text/html").call(request)
44
- response[1]['Content-Type'].must_be_nil
45
- end
46
- end
@@ -1,375 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'stringio'
3
- require 'time' # for Time#httpdate
4
- require 'rack/deflater'
5
- require 'rack/lint'
6
- require 'rack/mock'
7
- require 'zlib'
8
-
9
- describe Rack::Deflater do
10
-
11
- def build_response(status, body, accept_encoding, options = {})
12
- body = [body] if body.respond_to? :to_str
13
- app = lambda do |env|
14
- res = [status, options['response_headers'] || {}, body]
15
- res[1]['Content-Type'] = 'text/plain' unless res[0] == 304
16
- res
17
- end
18
-
19
- request = Rack::MockRequest.env_for('', (options['request_headers'] || {}).merge('HTTP_ACCEPT_ENCODING' => accept_encoding))
20
- deflater = Rack::Lint.new Rack::Deflater.new(app, options['deflater_options'] || {})
21
-
22
- deflater.call(request)
23
- end
24
-
25
- ##
26
- # Constructs response object and verifies if it yields right results
27
- #
28
- # [expected_status] expected response status, e.g. 200, 304
29
- # [expected_body] expected response body
30
- # [accept_encoing] what Accept-Encoding header to send and expect, e.g.
31
- # 'deflate' - accepts and expects deflate encoding in response
32
- # { 'gzip' => nil } - accepts gzip but expects no encoding in response
33
- # [options] hash of request options, i.e.
34
- # 'app_status' - what status dummy app should return (may be changed by deflater at some point)
35
- # 'app_body' - what body dummy app should return (may be changed by deflater at some point)
36
- # 'request_headers' - extra request headers to be sent
37
- # 'response_headers' - extra response headers to be returned
38
- # 'deflater_options' - options passed to deflater middleware
39
- # [block] useful for doing some extra verification
40
- def verify(expected_status, expected_body, accept_encoding, options = {}, &block)
41
- accept_encoding, expected_encoding = if accept_encoding.kind_of?(Hash)
42
- [accept_encoding.keys.first, accept_encoding.values.first]
43
- else
44
- [accept_encoding, accept_encoding.dup]
45
- end
46
-
47
- # build response
48
- status, headers, body = build_response(
49
- options['app_status'] || expected_status,
50
- options['app_body'] || expected_body,
51
- accept_encoding,
52
- options
53
- )
54
-
55
- # verify status
56
- status.must_equal expected_status
57
-
58
- # verify body
59
- unless options['skip_body_verify']
60
- body_text = ''
61
- body.each { |part| body_text << part }
62
-
63
- deflated_body = case expected_encoding
64
- when 'deflate'
65
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
66
- inflater.inflate(body_text) << inflater.finish
67
- when 'gzip'
68
- io = StringIO.new(body_text)
69
- gz = Zlib::GzipReader.new(io)
70
- tmp = gz.read
71
- gz.close
72
- tmp
73
- else
74
- body_text
75
- end
76
-
77
- deflated_body.must_equal expected_body
78
- end
79
-
80
- # yield full response verification
81
- yield(status, headers, body) if block_given?
82
- end
83
-
84
- # automatic gzip detection (streamable)
85
- def auto_inflater
86
- Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
87
- end
88
-
89
- def deflate_or_gzip
90
- {'deflate, gzip' => 'gzip'}
91
- end
92
-
93
- it 'be able to deflate bodies that respond to each' do
94
- app_body = Object.new
95
- class << app_body; def each; yield('foo'); yield('bar'); end; end
96
-
97
- verify(200, 'foobar', deflate_or_gzip, { 'app_body' => app_body }) do |status, headers, body|
98
- headers.must_equal({
99
- 'Content-Encoding' => 'gzip',
100
- 'Vary' => 'Accept-Encoding',
101
- 'Content-Type' => 'text/plain'
102
- })
103
- end
104
- end
105
-
106
- it 'flush deflated chunks to the client as they become ready' do
107
- app_body = Object.new
108
- class << app_body; def each; yield('foo'); yield('bar'); end; end
109
-
110
- verify(200, app_body, deflate_or_gzip, { 'skip_body_verify' => true }) do |status, headers, body|
111
- headers.must_equal({
112
- 'Content-Encoding' => 'gzip',
113
- 'Vary' => 'Accept-Encoding',
114
- 'Content-Type' => 'text/plain'
115
- })
116
-
117
- buf = []
118
- inflater = auto_inflater
119
- body.each { |part| buf << inflater.inflate(part) }
120
- buf << inflater.finish
121
-
122
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
123
- end
124
- end
125
-
126
- it 'does not raise when a client aborts reading' do
127
- app_body = Object.new
128
- class << app_body; def each; yield('foo'); yield('bar'); end; end
129
- opts = { 'skip_body_verify' => true }
130
- verify(200, app_body, 'gzip', opts) do |status, headers, body|
131
- headers.must_equal({
132
- 'Content-Encoding' => 'gzip',
133
- 'Vary' => 'Accept-Encoding',
134
- 'Content-Type' => 'text/plain'
135
- })
136
-
137
- buf = []
138
- inflater = auto_inflater
139
- FakeDisconnect = Class.new(RuntimeError)
140
- assert_raises(FakeDisconnect, "not Zlib::DataError not raised") do
141
- body.each do |part|
142
- tmp = inflater.inflate(part)
143
- buf << tmp if tmp.bytesize > 0
144
- raise FakeDisconnect
145
- end
146
- end
147
- inflater.finish
148
- buf.must_equal(%w(foo))
149
- end
150
- end
151
-
152
- # TODO: This is really just a special case of the above...
153
- it 'be able to deflate String bodies' do
154
- verify(200, 'Hello world!', deflate_or_gzip) do |status, headers, body|
155
- headers.must_equal({
156
- 'Content-Encoding' => 'gzip',
157
- 'Vary' => 'Accept-Encoding',
158
- 'Content-Type' => 'text/plain'
159
- })
160
- end
161
- end
162
-
163
- it 'be able to gzip bodies that respond to each' do
164
- app_body = Object.new
165
- class << app_body; def each; yield('foo'); yield('bar'); end; end
166
-
167
- verify(200, 'foobar', 'gzip', { 'app_body' => app_body }) do |status, headers, body|
168
- headers.must_equal({
169
- 'Content-Encoding' => 'gzip',
170
- 'Vary' => 'Accept-Encoding',
171
- 'Content-Type' => 'text/plain'
172
- })
173
- end
174
- end
175
-
176
- it 'flush gzipped chunks to the client as they become ready' do
177
- app_body = Object.new
178
- class << app_body; def each; yield('foo'); yield('bar'); end; end
179
-
180
- verify(200, app_body, 'gzip', { 'skip_body_verify' => true }) do |status, headers, body|
181
- headers.must_equal({
182
- 'Content-Encoding' => 'gzip',
183
- 'Vary' => 'Accept-Encoding',
184
- 'Content-Type' => 'text/plain'
185
- })
186
-
187
- buf = []
188
- inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
189
- body.each { |part| buf << inflater.inflate(part) }
190
- buf << inflater.finish
191
-
192
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
193
- end
194
- end
195
-
196
- it 'be able to fallback to no deflation' do
197
- verify(200, 'Hello world!', 'superzip') do |status, headers, body|
198
- headers.must_equal({
199
- 'Vary' => 'Accept-Encoding',
200
- 'Content-Type' => 'text/plain'
201
- })
202
- end
203
- end
204
-
205
- it 'be able to skip when there is no response entity body' do
206
- verify(304, '', { 'gzip' => nil }, { 'app_body' => [] }) do |status, headers, body|
207
- headers.must_equal({})
208
- end
209
- end
210
-
211
- it 'handle the lack of an acceptable encoding' do
212
- app_body = 'Hello world!'
213
- not_found_body1 = 'An acceptable encoding for the requested resource / could not be found.'
214
- not_found_body2 = 'An acceptable encoding for the requested resource /foo/bar could not be found.'
215
- options1 = {
216
- 'app_status' => 200,
217
- 'app_body' => app_body,
218
- 'request_headers' => {
219
- 'PATH_INFO' => '/'
220
- }
221
- }
222
- options2 = {
223
- 'app_status' => 200,
224
- 'app_body' => app_body,
225
- 'request_headers' => {
226
- 'PATH_INFO' => '/foo/bar'
227
- }
228
- }
229
-
230
- verify(406, not_found_body1, 'identity;q=0', options1) do |status, headers, body|
231
- headers.must_equal({
232
- 'Content-Type' => 'text/plain',
233
- 'Content-Length' => not_found_body1.length.to_s
234
- })
235
- end
236
-
237
- verify(406, not_found_body2, 'identity;q=0', options2) do |status, headers, body|
238
- headers.must_equal({
239
- 'Content-Type' => 'text/plain',
240
- 'Content-Length' => not_found_body2.length.to_s
241
- })
242
- end
243
- end
244
-
245
- it 'handle gzip response with Last-Modified header' do
246
- last_modified = Time.now.httpdate
247
- options = {
248
- 'response_headers' => {
249
- 'Content-Type' => 'text/plain',
250
- 'Last-Modified' => last_modified
251
- }
252
- }
253
-
254
- verify(200, 'Hello World!', 'gzip', options) do |status, headers, body|
255
- headers.must_equal({
256
- 'Content-Encoding' => 'gzip',
257
- 'Vary' => 'Accept-Encoding',
258
- 'Last-Modified' => last_modified,
259
- 'Content-Type' => 'text/plain'
260
- })
261
- end
262
- end
263
-
264
- it 'do nothing when no-transform Cache-Control directive present' do
265
- options = {
266
- 'response_headers' => {
267
- 'Content-Type' => 'text/plain',
268
- 'Cache-Control' => 'no-transform'
269
- }
270
- }
271
- verify(200, 'Hello World!', { 'gzip' => nil }, options) do |status, headers, body|
272
- headers.wont_include 'Content-Encoding'
273
- end
274
- end
275
-
276
- it 'do nothing when Content-Encoding already present' do
277
- options = {
278
- 'response_headers' => {
279
- 'Content-Type' => 'text/plain',
280
- 'Content-Encoding' => 'gzip'
281
- }
282
- }
283
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
284
- end
285
-
286
- it 'deflate when Content-Encoding is identity' do
287
- options = {
288
- 'response_headers' => {
289
- 'Content-Type' => 'text/plain',
290
- 'Content-Encoding' => 'identity'
291
- }
292
- }
293
- verify(200, 'Hello World!', deflate_or_gzip, options)
294
- end
295
-
296
- it "deflate if content-type matches :include" do
297
- options = {
298
- 'response_headers' => {
299
- 'Content-Type' => 'text/plain'
300
- },
301
- 'deflater_options' => {
302
- :include => %w(text/plain)
303
- }
304
- }
305
- verify(200, 'Hello World!', 'gzip', options)
306
- end
307
-
308
- it "deflate if content-type is included it :include" do
309
- options = {
310
- 'response_headers' => {
311
- 'Content-Type' => 'text/plain; charset=us-ascii'
312
- },
313
- 'deflater_options' => {
314
- :include => %w(text/plain)
315
- }
316
- }
317
- verify(200, 'Hello World!', 'gzip', options)
318
- end
319
-
320
- it "not deflate if content-type is not set but given in :include" do
321
- options = {
322
- 'deflater_options' => {
323
- :include => %w(text/plain)
324
- }
325
- }
326
- verify(304, 'Hello World!', { 'gzip' => nil }, options)
327
- end
328
-
329
- it "not deflate if content-type do not match :include" do
330
- options = {
331
- 'response_headers' => {
332
- 'Content-Type' => 'text/plain'
333
- },
334
- 'deflater_options' => {
335
- :include => %w(text/json)
336
- }
337
- }
338
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
339
- end
340
-
341
- it "deflate response if :if lambda evaluates to true" do
342
- options = {
343
- 'deflater_options' => {
344
- :if => lambda { |env, status, headers, body| true }
345
- }
346
- }
347
- verify(200, 'Hello World!', deflate_or_gzip, options)
348
- end
349
-
350
- it "not deflate if :if lambda evaluates to false" do
351
- options = {
352
- 'deflater_options' => {
353
- :if => lambda { |env, status, headers, body| false }
354
- }
355
- }
356
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
357
- end
358
-
359
- it "check for Content-Length via :if" do
360
- response = 'Hello World!'
361
- response_len = response.length
362
- options = {
363
- 'response_headers' => {
364
- 'Content-Length' => response_len.to_s
365
- },
366
- 'deflater_options' => {
367
- :if => lambda { |env, status, headers, body|
368
- headers['Content-Length'].to_i >= response_len
369
- }
370
- }
371
- }
372
-
373
- verify(200, response, 'gzip', options)
374
- end
375
- end