rack 2.0.1 → 2.2.17

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 (189) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +795 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +188 -145
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +46 -17
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +1 -1
  14. data/lib/rack/auth/basic.rb +6 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  16. data/lib/rack/auth/digest/nonce.rb +5 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +5 -3
  19. data/lib/rack/body_proxy.rb +15 -14
  20. data/lib/rack/builder.rb +116 -23
  21. data/lib/rack/cascade.rb +28 -12
  22. data/lib/rack/chunked.rb +68 -20
  23. data/lib/rack/common_logger.rb +37 -25
  24. data/lib/rack/conditional_get.rb +20 -16
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +8 -7
  27. data/lib/rack/content_type.rb +5 -4
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +60 -70
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +8 -5
  32. data/lib/rack/events.rb +19 -20
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +218 -0
  35. data/lib/rack/handler/cgi.rb +2 -3
  36. data/lib/rack/handler/fastcgi.rb +4 -4
  37. data/lib/rack/handler/lsws.rb +3 -3
  38. data/lib/rack/handler/scgi.rb +9 -8
  39. data/lib/rack/handler/thin.rb +3 -3
  40. data/lib/rack/handler/webrick.rb +19 -10
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +221 -186
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +14 -4
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +23 -8
  48. data/lib/rack/method_override.rb +13 -4
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +135 -29
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +85 -68
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +6 -5
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +108 -36
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +232 -60
  60. data/lib/rack/response.rb +127 -44
  61. data/lib/rack/rewindable_input.rb +4 -3
  62. data/lib/rack/runtime.rb +6 -4
  63. data/lib/rack/sendfile.rb +14 -10
  64. data/lib/rack/server.rb +97 -25
  65. data/lib/rack/session/abstract/id.rb +113 -25
  66. data/lib/rack/session/cookie.rb +22 -14
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -10
  69. data/lib/rack/show_exceptions.rb +22 -18
  70. data/lib/rack/show_status.rb +9 -9
  71. data/lib/rack/static.rb +25 -12
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +13 -7
  74. data/lib/rack/utils.rb +135 -123
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -29
  78. metadata +25 -184
  79. data/HISTORY.md +0 -505
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  114. data/test/multipart/filename_with_single_quote +0 -7
  115. data/test/multipart/filename_with_unescaped_percentages +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  118. data/test/multipart/filename_with_unescaped_quotes +0 -6
  119. data/test/multipart/ie +0 -6
  120. data/test/multipart/invalid_character +0 -6
  121. data/test/multipart/mixed_files +0 -21
  122. data/test/multipart/nested +0 -10
  123. data/test/multipart/none +0 -9
  124. data/test/multipart/quoted +0 -15
  125. data/test/multipart/rack-logo.png +0 -0
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/unity3d_wwwform +0 -11
  130. data/test/multipart/webkit +0 -32
  131. data/test/rackup/config.ru +0 -31
  132. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  133. data/test/spec_auth_basic.rb +0 -89
  134. data/test/spec_auth_digest.rb +0 -260
  135. data/test/spec_body_proxy.rb +0 -85
  136. data/test/spec_builder.rb +0 -233
  137. data/test/spec_cascade.rb +0 -63
  138. data/test/spec_cgi.rb +0 -84
  139. data/test/spec_chunked.rb +0 -103
  140. data/test/spec_common_logger.rb +0 -95
  141. data/test/spec_conditional_get.rb +0 -103
  142. data/test/spec_config.rb +0 -23
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -46
  145. data/test/spec_deflater.rb +0 -365
  146. data/test/spec_directory.rb +0 -148
  147. data/test/spec_etag.rb +0 -108
  148. data/test/spec_events.rb +0 -133
  149. data/test/spec_fastcgi.rb +0 -85
  150. data/test/spec_file.rb +0 -251
  151. data/test/spec_handler.rb +0 -57
  152. data/test/spec_head.rb +0 -46
  153. data/test/spec_lint.rb +0 -515
  154. data/test/spec_lobster.rb +0 -59
  155. data/test/spec_lock.rb +0 -194
  156. data/test/spec_logger.rb +0 -24
  157. data/test/spec_media_type.rb +0 -42
  158. data/test/spec_method_override.rb +0 -83
  159. data/test/spec_mime.rb +0 -51
  160. data/test/spec_mock.rb +0 -342
  161. data/test/spec_multipart.rb +0 -716
  162. data/test/spec_null_logger.rb +0 -21
  163. data/test/spec_recursive.rb +0 -75
  164. data/test/spec_request.rb +0 -1393
  165. data/test/spec_response.rb +0 -510
  166. data/test/spec_rewindable_input.rb +0 -128
  167. data/test/spec_runtime.rb +0 -50
  168. data/test/spec_sendfile.rb +0 -125
  169. data/test/spec_server.rb +0 -193
  170. data/test/spec_session_abstract_id.rb +0 -31
  171. data/test/spec_session_abstract_session_hash.rb +0 -28
  172. data/test/spec_session_cookie.rb +0 -442
  173. data/test/spec_session_memcache.rb +0 -320
  174. data/test/spec_session_pool.rb +0 -210
  175. data/test/spec_show_exceptions.rb +0 -80
  176. data/test/spec_show_status.rb +0 -104
  177. data/test/spec_static.rb +0 -184
  178. data/test/spec_tempfile_reaper.rb +0 -64
  179. data/test/spec_thin.rb +0 -96
  180. data/test/spec_urlmap.rb +0 -237
  181. data/test/spec_utils.rb +0 -742
  182. data/test/spec_version.rb +0 -11
  183. data/test/spec_webrick.rb +0 -208
  184. data/test/static/another/index.html +0 -1
  185. data/test/static/foo.html +0 -1
  186. data/test/static/index.html +0 -1
  187. data/test/testrequest.rb +0 -78
  188. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  189. 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_equal 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_equal 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_equal 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_equal 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_equal nil
45
- end
46
- end
@@ -1,365 +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
- it 'be able to deflate bodies that respond to each' do
85
- app_body = Object.new
86
- class << app_body; def each; yield('foo'); yield('bar'); end; end
87
-
88
- verify(200, 'foobar', 'deflate', { 'app_body' => app_body }) do |status, headers, body|
89
- headers.must_equal({
90
- 'Content-Encoding' => 'deflate',
91
- 'Vary' => 'Accept-Encoding',
92
- 'Content-Type' => 'text/plain'
93
- })
94
- end
95
- end
96
-
97
- it 'flush deflated chunks to the client as they become ready' do
98
- app_body = Object.new
99
- class << app_body; def each; yield('foo'); yield('bar'); end; end
100
-
101
- verify(200, app_body, 'deflate', { 'skip_body_verify' => true }) do |status, headers, body|
102
- headers.must_equal({
103
- 'Content-Encoding' => 'deflate',
104
- 'Vary' => 'Accept-Encoding',
105
- 'Content-Type' => 'text/plain'
106
- })
107
-
108
- buf = []
109
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
110
- body.each { |part| buf << inflater.inflate(part) }
111
- buf << inflater.finish
112
-
113
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
114
- end
115
- end
116
-
117
- it 'does not raise when a client aborts reading' do
118
- app_body = Object.new
119
- class << app_body; def each; yield('foo'); yield('bar'); end; end
120
- opts = { 'skip_body_verify' => true }
121
- verify(200, app_body, 'deflate', opts) do |status, headers, body|
122
- headers.must_equal({
123
- 'Content-Encoding' => 'deflate',
124
- 'Vary' => 'Accept-Encoding',
125
- 'Content-Type' => 'text/plain'
126
- })
127
-
128
- buf = []
129
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
130
- FakeDisconnect = Class.new(RuntimeError)
131
- assert_raises(FakeDisconnect, "not Zlib::DataError not raised") do
132
- body.each do |part|
133
- buf << inflater.inflate(part)
134
- raise FakeDisconnect
135
- end
136
- end
137
- assert_raises(Zlib::BufError) { inflater.finish }
138
- buf.must_equal(%w(foo))
139
- end
140
- end
141
-
142
- # TODO: This is really just a special case of the above...
143
- it 'be able to deflate String bodies' do
144
- verify(200, 'Hello world!', 'deflate') do |status, headers, body|
145
- headers.must_equal({
146
- 'Content-Encoding' => 'deflate',
147
- 'Vary' => 'Accept-Encoding',
148
- 'Content-Type' => 'text/plain'
149
- })
150
- end
151
- end
152
-
153
- it 'be able to gzip bodies that respond to each' do
154
- app_body = Object.new
155
- class << app_body; def each; yield('foo'); yield('bar'); end; end
156
-
157
- verify(200, 'foobar', 'gzip', { 'app_body' => app_body }) do |status, headers, body|
158
- headers.must_equal({
159
- 'Content-Encoding' => 'gzip',
160
- 'Vary' => 'Accept-Encoding',
161
- 'Content-Type' => 'text/plain'
162
- })
163
- end
164
- end
165
-
166
- it 'flush gzipped chunks to the client as they become ready' do
167
- app_body = Object.new
168
- class << app_body; def each; yield('foo'); yield('bar'); end; end
169
-
170
- verify(200, app_body, 'gzip', { 'skip_body_verify' => true }) do |status, headers, body|
171
- headers.must_equal({
172
- 'Content-Encoding' => 'gzip',
173
- 'Vary' => 'Accept-Encoding',
174
- 'Content-Type' => 'text/plain'
175
- })
176
-
177
- buf = []
178
- inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
179
- body.each { |part| buf << inflater.inflate(part) }
180
- buf << inflater.finish
181
-
182
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
183
- end
184
- end
185
-
186
- it 'be able to fallback to no deflation' do
187
- verify(200, 'Hello world!', 'superzip') do |status, headers, body|
188
- headers.must_equal({
189
- 'Vary' => 'Accept-Encoding',
190
- 'Content-Type' => 'text/plain'
191
- })
192
- end
193
- end
194
-
195
- it 'be able to skip when there is no response entity body' do
196
- verify(304, '', { 'gzip' => nil }, { 'app_body' => [] }) do |status, headers, body|
197
- headers.must_equal({})
198
- end
199
- end
200
-
201
- it 'handle the lack of an acceptable encoding' do
202
- app_body = 'Hello world!'
203
- not_found_body1 = 'An acceptable encoding for the requested resource / could not be found.'
204
- not_found_body2 = 'An acceptable encoding for the requested resource /foo/bar could not be found.'
205
- options1 = {
206
- 'app_status' => 200,
207
- 'app_body' => app_body,
208
- 'request_headers' => {
209
- 'PATH_INFO' => '/'
210
- }
211
- }
212
- options2 = {
213
- 'app_status' => 200,
214
- 'app_body' => app_body,
215
- 'request_headers' => {
216
- 'PATH_INFO' => '/foo/bar'
217
- }
218
- }
219
-
220
- verify(406, not_found_body1, 'identity;q=0', options1) do |status, headers, body|
221
- headers.must_equal({
222
- 'Content-Type' => 'text/plain',
223
- 'Content-Length' => not_found_body1.length.to_s
224
- })
225
- end
226
-
227
- verify(406, not_found_body2, 'identity;q=0', options2) do |status, headers, body|
228
- headers.must_equal({
229
- 'Content-Type' => 'text/plain',
230
- 'Content-Length' => not_found_body2.length.to_s
231
- })
232
- end
233
- end
234
-
235
- it 'handle gzip response with Last-Modified header' do
236
- last_modified = Time.now.httpdate
237
- options = {
238
- 'response_headers' => {
239
- 'Content-Type' => 'text/plain',
240
- 'Last-Modified' => last_modified
241
- }
242
- }
243
-
244
- verify(200, 'Hello World!', 'gzip', options) do |status, headers, body|
245
- headers.must_equal({
246
- 'Content-Encoding' => 'gzip',
247
- 'Vary' => 'Accept-Encoding',
248
- 'Last-Modified' => last_modified,
249
- 'Content-Type' => 'text/plain'
250
- })
251
- end
252
- end
253
-
254
- it 'do nothing when no-transform Cache-Control directive present' do
255
- options = {
256
- 'response_headers' => {
257
- 'Content-Type' => 'text/plain',
258
- 'Cache-Control' => 'no-transform'
259
- }
260
- }
261
- verify(200, 'Hello World!', { 'gzip' => nil }, options) do |status, headers, body|
262
- headers.wont_include 'Content-Encoding'
263
- end
264
- end
265
-
266
- it 'do nothing when Content-Encoding already present' do
267
- options = {
268
- 'response_headers' => {
269
- 'Content-Type' => 'text/plain',
270
- 'Content-Encoding' => 'gzip'
271
- }
272
- }
273
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
274
- end
275
-
276
- it 'deflate when Content-Encoding is identity' do
277
- options = {
278
- 'response_headers' => {
279
- 'Content-Type' => 'text/plain',
280
- 'Content-Encoding' => 'identity'
281
- }
282
- }
283
- verify(200, 'Hello World!', 'deflate', options)
284
- end
285
-
286
- it "deflate if content-type matches :include" do
287
- options = {
288
- 'response_headers' => {
289
- 'Content-Type' => 'text/plain'
290
- },
291
- 'deflater_options' => {
292
- :include => %w(text/plain)
293
- }
294
- }
295
- verify(200, 'Hello World!', 'gzip', options)
296
- end
297
-
298
- it "deflate if content-type is included it :include" do
299
- options = {
300
- 'response_headers' => {
301
- 'Content-Type' => 'text/plain; charset=us-ascii'
302
- },
303
- 'deflater_options' => {
304
- :include => %w(text/plain)
305
- }
306
- }
307
- verify(200, 'Hello World!', 'gzip', options)
308
- end
309
-
310
- it "not deflate if content-type is not set but given in :include" do
311
- options = {
312
- 'deflater_options' => {
313
- :include => %w(text/plain)
314
- }
315
- }
316
- verify(304, 'Hello World!', { 'gzip' => nil }, options)
317
- end
318
-
319
- it "not deflate if content-type do not match :include" do
320
- options = {
321
- 'response_headers' => {
322
- 'Content-Type' => 'text/plain'
323
- },
324
- 'deflater_options' => {
325
- :include => %w(text/json)
326
- }
327
- }
328
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
329
- end
330
-
331
- it "deflate response if :if lambda evaluates to true" do
332
- options = {
333
- 'deflater_options' => {
334
- :if => lambda { |env, status, headers, body| true }
335
- }
336
- }
337
- verify(200, 'Hello World!', 'deflate', options)
338
- end
339
-
340
- it "not deflate if :if lambda evaluates to false" do
341
- options = {
342
- 'deflater_options' => {
343
- :if => lambda { |env, status, headers, body| false }
344
- }
345
- }
346
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
347
- end
348
-
349
- it "check for Content-Length via :if" do
350
- response = 'Hello World!'
351
- response_len = response.length
352
- options = {
353
- 'response_headers' => {
354
- 'Content-Length' => response_len.to_s
355
- },
356
- 'deflater_options' => {
357
- :if => lambda { |env, status, headers, body|
358
- headers['Content-Length'].to_i >= response_len
359
- }
360
- }
361
- }
362
-
363
- verify(200, response, 'gzip', options)
364
- end
365
- end