rack 2.0.6 → 2.1.2

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/CHANGELOG.md +69 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -117
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  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.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +39 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +32 -17
  30. data/lib/rack/directory.rb +17 -14
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +4 -2
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +55 -52
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +80 -27
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -18
  65. data/lib/rack/session/abstract/id.rb +96 -21
  66. data/lib/rack/session/cookie.rb +21 -11
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +17 -8
  69. data/lib/rack/show_exceptions.rb +15 -9
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +55 -70
  75. data/rack.gemspec +17 -7
  76. metadata +30 -171
  77. data/HISTORY.md +0 -505
  78. data/test/builder/an_underscore_app.rb +0 -5
  79. data/test/builder/anything.rb +0 -5
  80. data/test/builder/comment.ru +0 -4
  81. data/test/builder/end.ru +0 -5
  82. data/test/builder/line.ru +0 -1
  83. data/test/builder/options.ru +0 -2
  84. data/test/cgi/assets/folder/test.js +0 -1
  85. data/test/cgi/assets/fonts/font.eot +0 -1
  86. data/test/cgi/assets/images/image.png +0 -1
  87. data/test/cgi/assets/index.html +0 -1
  88. data/test/cgi/assets/javascripts/app.js +0 -1
  89. data/test/cgi/assets/stylesheets/app.css +0 -1
  90. data/test/cgi/lighttpd.conf +0 -26
  91. data/test/cgi/rackup_stub.rb +0 -6
  92. data/test/cgi/sample_rackup.ru +0 -5
  93. data/test/cgi/test +0 -9
  94. data/test/cgi/test+directory/test+file +0 -1
  95. data/test/cgi/test.fcgi +0 -9
  96. data/test/cgi/test.gz +0 -0
  97. data/test/cgi/test.ru +0 -5
  98. data/test/gemloader.rb +0 -10
  99. data/test/helper.rb +0 -34
  100. data/test/multipart/bad_robots +0 -259
  101. data/test/multipart/binary +0 -0
  102. data/test/multipart/content_type_and_no_filename +0 -6
  103. data/test/multipart/empty +0 -10
  104. data/test/multipart/fail_16384_nofile +0 -814
  105. data/test/multipart/file1.txt +0 -1
  106. data/test/multipart/filename_and_modification_param +0 -7
  107. data/test/multipart/filename_and_no_name +0 -6
  108. data/test/multipart/filename_with_encoded_words +0 -7
  109. data/test/multipart/filename_with_escaped_quotes +0 -6
  110. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  111. data/test/multipart/filename_with_null_byte +0 -7
  112. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  113. data/test/multipart/filename_with_single_quote +0 -7
  114. data/test/multipart/filename_with_unescaped_percentages +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  117. data/test/multipart/filename_with_unescaped_quotes +0 -6
  118. data/test/multipart/ie +0 -6
  119. data/test/multipart/invalid_character +0 -6
  120. data/test/multipart/mixed_files +0 -21
  121. data/test/multipart/nested +0 -10
  122. data/test/multipart/none +0 -9
  123. data/test/multipart/quoted +0 -15
  124. data/test/multipart/rack-logo.png +0 -0
  125. data/test/multipart/semicolon +0 -6
  126. data/test/multipart/text +0 -15
  127. data/test/multipart/three_files_three_fields +0 -31
  128. data/test/multipart/unity3d_wwwform +0 -11
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth_basic.rb +0 -89
  133. data/test/spec_auth_digest.rb +0 -260
  134. data/test/spec_body_proxy.rb +0 -85
  135. data/test/spec_builder.rb +0 -233
  136. data/test/spec_cascade.rb +0 -63
  137. data/test/spec_cgi.rb +0 -84
  138. data/test/spec_chunked.rb +0 -103
  139. data/test/spec_common_logger.rb +0 -95
  140. data/test/spec_conditional_get.rb +0 -103
  141. data/test/spec_config.rb +0 -23
  142. data/test/spec_content_length.rb +0 -86
  143. data/test/spec_content_type.rb +0 -46
  144. data/test/spec_deflater.rb +0 -375
  145. data/test/spec_directory.rb +0 -148
  146. data/test/spec_etag.rb +0 -108
  147. data/test/spec_events.rb +0 -133
  148. data/test/spec_fastcgi.rb +0 -85
  149. data/test/spec_file.rb +0 -264
  150. data/test/spec_handler.rb +0 -57
  151. data/test/spec_head.rb +0 -46
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -59
  154. data/test/spec_lock.rb +0 -204
  155. data/test/spec_logger.rb +0 -24
  156. data/test/spec_media_type.rb +0 -42
  157. data/test/spec_method_override.rb +0 -110
  158. data/test/spec_mime.rb +0 -51
  159. data/test/spec_mock.rb +0 -359
  160. data/test/spec_multipart.rb +0 -722
  161. data/test/spec_null_logger.rb +0 -21
  162. data/test/spec_recursive.rb +0 -75
  163. data/test/spec_request.rb +0 -1398
  164. data/test/spec_response.rb +0 -510
  165. data/test/spec_rewindable_input.rb +0 -128
  166. data/test/spec_runtime.rb +0 -50
  167. data/test/spec_sendfile.rb +0 -125
  168. data/test/spec_server.rb +0 -193
  169. data/test/spec_session_abstract_id.rb +0 -31
  170. data/test/spec_session_abstract_session_hash.rb +0 -45
  171. data/test/spec_session_cookie.rb +0 -442
  172. data/test/spec_session_memcache.rb +0 -320
  173. data/test/spec_session_pool.rb +0 -210
  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,57 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/handler'
3
-
4
- class Rack::Handler::Lobster; end
5
- class RockLobster; end
6
-
7
- describe Rack::Handler do
8
- it "has registered default handlers" do
9
- Rack::Handler.get('cgi').must_equal Rack::Handler::CGI
10
- Rack::Handler.get('webrick').must_equal Rack::Handler::WEBrick
11
-
12
- begin
13
- Rack::Handler.get('fastcgi').must_equal Rack::Handler::FastCGI
14
- rescue LoadError
15
- end
16
- end
17
-
18
- it "raise LoadError if handler doesn't exist" do
19
- lambda {
20
- Rack::Handler.get('boom')
21
- }.must_raise(LoadError)
22
-
23
- lambda {
24
- Rack::Handler.get('Object')
25
- }.must_raise(LoadError)
26
- end
27
-
28
- it "get unregistered, but already required, handler by name" do
29
- Rack::Handler.get('Lobster').must_equal Rack::Handler::Lobster
30
- end
31
-
32
- it "register custom handler" do
33
- Rack::Handler.register('rock_lobster', 'RockLobster')
34
- Rack::Handler.get('rock_lobster').must_equal RockLobster
35
- end
36
-
37
- it "not need registration for properly coded handlers even if not already required" do
38
- begin
39
- $LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
40
- Rack::Handler.get('Unregistered').must_equal Rack::Handler::Unregistered
41
- lambda { Rack::Handler.get('UnRegistered') }.must_raise LoadError
42
- Rack::Handler.get('UnregisteredLongOne').must_equal Rack::Handler::UnregisteredLongOne
43
- ensure
44
- $LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
45
- end
46
- end
47
-
48
- it "allow autoloaded handlers to be registered properly while being loaded" do
49
- path = File.expand_path('../registering_handler', __FILE__)
50
- begin
51
- $LOAD_PATH.push path
52
- Rack::Handler.get('registering_myself').must_equal Rack::Handler::RegisteringMyself
53
- ensure
54
- $LOAD_PATH.delete path
55
- end
56
- end
57
- end
@@ -1,46 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/head'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
-
6
- describe Rack::Head do
7
-
8
- def test_response(headers = {})
9
- body = StringIO.new "foo"
10
- app = lambda do |env|
11
- [200, {"Content-type" => "test/plain", "Content-length" => "3"}, body]
12
- end
13
- request = Rack::MockRequest.env_for("/", headers)
14
- response = Rack::Lint.new(Rack::Head.new(app)).call(request)
15
-
16
- return response, body
17
- end
18
-
19
- it "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
20
- %w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
21
- resp, _ = test_response("REQUEST_METHOD" => type)
22
-
23
- resp[0].must_equal 200
24
- resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
25
- resp[2].to_enum.to_a.must_equal ["foo"]
26
- end
27
- end
28
-
29
- it "remove body from HEAD requests" do
30
- resp, _ = test_response("REQUEST_METHOD" => "HEAD")
31
-
32
- resp[0].must_equal 200
33
- resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
34
- resp[2].to_enum.to_a.must_equal []
35
- end
36
-
37
- it "close the body when it is removed" do
38
- resp, body = test_response("REQUEST_METHOD" => "HEAD")
39
- resp[0].must_equal 200
40
- resp[1].must_equal "Content-type" => "test/plain", "Content-length" => "3"
41
- resp[2].to_enum.to_a.must_equal []
42
- body.wont_be :closed?
43
- resp[2].close
44
- body.must_be :closed?
45
- end
46
- end
@@ -1,515 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'stringio'
3
- require 'tempfile'
4
- require 'rack/lint'
5
- require 'rack/mock'
6
-
7
- describe Rack::Lint do
8
- def env(*args)
9
- Rack::MockRequest.env_for("/", *args)
10
- end
11
-
12
- it "pass valid request" do
13
- Rack::Lint.new(lambda { |env|
14
- [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
15
- }).call(env({})).first.must_equal 200
16
- end
17
-
18
- it "notice fatal errors" do
19
- lambda { Rack::Lint.new(nil).call }.must_raise(Rack::Lint::LintError).
20
- message.must_match(/No env given/)
21
- end
22
-
23
- it "notice environment errors" do
24
- lambda { Rack::Lint.new(nil).call 5 }.must_raise(Rack::Lint::LintError).
25
- message.must_match(/not a Hash/)
26
-
27
- lambda {
28
- e = env
29
- e.delete("REQUEST_METHOD")
30
- Rack::Lint.new(nil).call(e)
31
- }.must_raise(Rack::Lint::LintError).
32
- message.must_match(/missing required key REQUEST_METHOD/)
33
-
34
- lambda {
35
- e = env
36
- e.delete("SERVER_NAME")
37
- Rack::Lint.new(nil).call(e)
38
- }.must_raise(Rack::Lint::LintError).
39
- message.must_match(/missing required key SERVER_NAME/)
40
-
41
-
42
- lambda {
43
- Rack::Lint.new(nil).call(env("HTTP_CONTENT_TYPE" => "text/plain"))
44
- }.must_raise(Rack::Lint::LintError).
45
- message.must_match(/contains HTTP_CONTENT_TYPE/)
46
-
47
- lambda {
48
- Rack::Lint.new(nil).call(env("HTTP_CONTENT_LENGTH" => "42"))
49
- }.must_raise(Rack::Lint::LintError).
50
- message.must_match(/contains HTTP_CONTENT_LENGTH/)
51
-
52
- lambda {
53
- Rack::Lint.new(nil).call(env("FOO" => Object.new))
54
- }.must_raise(Rack::Lint::LintError).
55
- message.must_match(/non-string value/)
56
-
57
- lambda {
58
- Rack::Lint.new(nil).call(env("rack.version" => "0.2"))
59
- }.must_raise(Rack::Lint::LintError).
60
- message.must_match(/must be an Array/)
61
-
62
- lambda {
63
- Rack::Lint.new(nil).call(env("rack.url_scheme" => "gopher"))
64
- }.must_raise(Rack::Lint::LintError).
65
- message.must_match(/url_scheme unknown/)
66
-
67
- lambda {
68
- Rack::Lint.new(nil).call(env("rack.session" => []))
69
- }.must_raise(Rack::Lint::LintError).
70
- message.must_equal "session [] must respond to store and []="
71
-
72
- lambda {
73
- Rack::Lint.new(nil).call(env("rack.logger" => []))
74
- }.must_raise(Rack::Lint::LintError).
75
- message.must_equal "logger [] must respond to info"
76
-
77
- lambda {
78
- Rack::Lint.new(nil).call(env("rack.multipart.buffer_size" => 0))
79
- }.must_raise(Rack::Lint::LintError).
80
- message.must_equal "rack.multipart.buffer_size must be an Integer > 0 if specified"
81
-
82
- lambda {
83
- Rack::Lint.new(nil).call(env("rack.multipart.tempfile_factory" => Tempfile))
84
- }.must_raise(Rack::Lint::LintError).
85
- message.must_equal "rack.multipart.tempfile_factory must respond to #call"
86
-
87
- lambda {
88
- Rack::Lint.new(lambda { |env|
89
- env['rack.multipart.tempfile_factory'].call("testfile", "text/plain")
90
- }).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| Object.new }))
91
- }.must_raise(Rack::Lint::LintError).
92
- message.must_equal "rack.multipart.tempfile_factory return value must respond to #<<"
93
-
94
- lambda {
95
- Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
96
- }.must_raise(Rack::Lint::LintError).
97
- message.must_match(/REQUEST_METHOD/)
98
-
99
- lambda {
100
- Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
101
- }.must_raise(Rack::Lint::LintError).
102
- message.must_match(/must start with/)
103
-
104
- lambda {
105
- Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo"))
106
- }.must_raise(Rack::Lint::LintError).
107
- message.must_match(/must start with/)
108
-
109
- lambda {
110
- Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii"))
111
- }.must_raise(Rack::Lint::LintError).
112
- message.must_match(/Invalid CONTENT_LENGTH/)
113
-
114
- lambda {
115
- e = env
116
- e.delete("PATH_INFO")
117
- e.delete("SCRIPT_NAME")
118
- Rack::Lint.new(nil).call(e)
119
- }.must_raise(Rack::Lint::LintError).
120
- message.must_match(/One of .* must be set/)
121
-
122
- lambda {
123
- Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/"))
124
- }.must_raise(Rack::Lint::LintError).
125
- message.must_match(/cannot be .* make it ''/)
126
- end
127
-
128
- it "notice input errors" do
129
- lambda {
130
- Rack::Lint.new(nil).call(env("rack.input" => ""))
131
- }.must_raise(Rack::Lint::LintError).
132
- message.must_match(/does not respond to #gets/)
133
-
134
- lambda {
135
- input = Object.new
136
- def input.binmode?
137
- false
138
- end
139
- Rack::Lint.new(nil).call(env("rack.input" => input))
140
- }.must_raise(Rack::Lint::LintError).
141
- message.must_match(/is not opened in binary mode/)
142
-
143
- lambda {
144
- input = Object.new
145
- def input.external_encoding
146
- result = Object.new
147
- def result.name
148
- "US-ASCII"
149
- end
150
- result
151
- end
152
- Rack::Lint.new(nil).call(env("rack.input" => input))
153
- }.must_raise(Rack::Lint::LintError).
154
- message.must_match(/does not have ASCII-8BIT as its external encoding/)
155
- end
156
-
157
- it "notice error errors" do
158
- lambda {
159
- Rack::Lint.new(nil).call(env("rack.errors" => ""))
160
- }.must_raise(Rack::Lint::LintError).
161
- message.must_match(/does not respond to #puts/)
162
- end
163
-
164
- it "notice status errors" do
165
- lambda {
166
- Rack::Lint.new(lambda { |env|
167
- ["cc", {}, ""]
168
- }).call(env({}))
169
- }.must_raise(Rack::Lint::LintError).
170
- message.must_match(/must be >=100 seen as integer/)
171
-
172
- lambda {
173
- Rack::Lint.new(lambda { |env|
174
- [42, {}, ""]
175
- }).call(env({}))
176
- }.must_raise(Rack::Lint::LintError).
177
- message.must_match(/must be >=100 seen as integer/)
178
- end
179
-
180
- it "notice header errors" do
181
- lambda {
182
- Rack::Lint.new(lambda { |env|
183
- [200, Object.new, []]
184
- }).call(env({}))
185
- }.must_raise(Rack::Lint::LintError).
186
- message.must_equal "headers object should respond to #each, but doesn't (got Object as headers)"
187
-
188
- lambda {
189
- Rack::Lint.new(lambda { |env|
190
- [200, {true=>false}, []]
191
- }).call(env({}))
192
- }.must_raise(Rack::Lint::LintError).
193
- message.must_equal "header key must be a string, was TrueClass"
194
-
195
- lambda {
196
- Rack::Lint.new(lambda { |env|
197
- [200, {"Status" => "404"}, []]
198
- }).call(env({}))
199
- }.must_raise(Rack::Lint::LintError).
200
- message.must_match(/must not contain Status/)
201
-
202
- # From RFC 7230:<F24><F25>
203
- # Most HTTP header field values are defined using common syntax
204
- # components (token, quoted-string, and comment) separated by
205
- # whitespace or specific delimiting characters. Delimiters are chosen
206
- # from the set of US-ASCII visual characters not allowed in a token
207
- # (DQUOTE and "(),/:;<=>?@[\]{}").
208
- #
209
- # token = 1*tchar
210
- #
211
- # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
212
- # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
213
- # / DIGIT / ALPHA
214
- # ; any VCHAR, except delimiters
215
- invalid_headers = 0.upto(31).map(&:chr) + %W<( ) , / : ; < = > ? @ [ \\ ] { } \x7F>
216
- invalid_headers.each do |invalid_header|
217
- lambda {
218
- Rack::Lint.new(lambda { |env|
219
- [200, {invalid_header => "text/plain"}, []]
220
- }).call(env({}))
221
- }.must_raise(Rack::Lint::LintError, "on invalid header: #{invalid_header}").
222
- message.must_equal("invalid header name: #{invalid_header}")
223
- end
224
- valid_headers = 0.upto(127).map(&:chr) - invalid_headers
225
- valid_headers.each do |valid_header|
226
- Rack::Lint.new(lambda { |env|
227
- [200, {valid_header => "text/plain"}, []]
228
- }).call(env({})).first.must_equal 200
229
- end
230
-
231
- lambda {
232
- Rack::Lint.new(lambda { |env|
233
- [200, {"Foo" => Object.new}, []]
234
- }).call(env({}))
235
- }.must_raise(Rack::Lint::LintError).
236
- message.must_equal "a header value must be a String, but the value of 'Foo' is a Object"
237
-
238
- lambda {
239
- Rack::Lint.new(lambda { |env|
240
- [200, {"Foo" => [1, 2, 3]}, []]
241
- }).call(env({}))
242
- }.must_raise(Rack::Lint::LintError).
243
- message.must_equal "a header value must be a String, but the value of 'Foo' is a Array"
244
-
245
-
246
- lambda {
247
- Rack::Lint.new(lambda { |env|
248
- [200, {"Foo-Bar" => "text\000plain"}, []]
249
- }).call(env({}))
250
- }.must_raise(Rack::Lint::LintError).
251
- message.must_match(/invalid header/)
252
-
253
- # line ends (010).must_be :allowed in header values.?
254
- Rack::Lint.new(lambda { |env|
255
- [200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
256
- }).call(env({})).first.must_equal 200
257
-
258
- # non-Hash header responses.must_be :allowed?
259
- Rack::Lint.new(lambda { |env|
260
- [200, [%w(Content-Type text/plain), %w(Content-Length 0)], []]
261
- }).call(env({})).first.must_equal 200
262
- end
263
-
264
- it "notice content-type errors" do
265
- # lambda {
266
- # Rack::Lint.new(lambda { |env|
267
- # [200, {"Content-length" => "0"}, []]
268
- # }).call(env({}))
269
- # }.must_raise(Rack::Lint::LintError).
270
- # message.must_match(/No Content-Type/)
271
-
272
- [100, 101, 204, 304].each do |status|
273
- lambda {
274
- Rack::Lint.new(lambda { |env|
275
- [status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
276
- }).call(env({}))
277
- }.must_raise(Rack::Lint::LintError).
278
- message.must_match(/Content-Type header found/)
279
- end
280
- end
281
-
282
- it "notice content-length errors" do
283
- [100, 101, 204, 304].each do |status|
284
- lambda {
285
- Rack::Lint.new(lambda { |env|
286
- [status, {"Content-length" => "0"}, []]
287
- }).call(env({}))
288
- }.must_raise(Rack::Lint::LintError).
289
- message.must_match(/Content-Length header found/)
290
- end
291
-
292
- lambda {
293
- Rack::Lint.new(lambda { |env|
294
- [200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
295
- }).call(env({}))[2].each { }
296
- }.must_raise(Rack::Lint::LintError).
297
- message.must_match(/Content-Length header was 1, but should be 0/)
298
- end
299
-
300
- it "notice body errors" do
301
- lambda {
302
- body = Rack::Lint.new(lambda { |env|
303
- [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
304
- }).call(env({}))[2]
305
- body.each { |part| }
306
- }.must_raise(Rack::Lint::LintError).
307
- message.must_match(/yielded non-string/)
308
- end
309
-
310
- it "notice input handling errors" do
311
- lambda {
312
- Rack::Lint.new(lambda { |env|
313
- env["rack.input"].gets("\r\n")
314
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
315
- }).call(env({}))
316
- }.must_raise(Rack::Lint::LintError).
317
- message.must_match(/gets called with arguments/)
318
-
319
- lambda {
320
- Rack::Lint.new(lambda { |env|
321
- env["rack.input"].read(1, 2, 3)
322
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
323
- }).call(env({}))
324
- }.must_raise(Rack::Lint::LintError).
325
- message.must_match(/read called with too many arguments/)
326
-
327
- lambda {
328
- Rack::Lint.new(lambda { |env|
329
- env["rack.input"].read("foo")
330
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
331
- }).call(env({}))
332
- }.must_raise(Rack::Lint::LintError).
333
- message.must_match(/read called with non-integer and non-nil length/)
334
-
335
- lambda {
336
- Rack::Lint.new(lambda { |env|
337
- env["rack.input"].read(-1)
338
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
339
- }).call(env({}))
340
- }.must_raise(Rack::Lint::LintError).
341
- message.must_match(/read called with a negative length/)
342
-
343
- lambda {
344
- Rack::Lint.new(lambda { |env|
345
- env["rack.input"].read(nil, nil)
346
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
347
- }).call(env({}))
348
- }.must_raise(Rack::Lint::LintError).
349
- message.must_match(/read called with non-String buffer/)
350
-
351
- lambda {
352
- Rack::Lint.new(lambda { |env|
353
- env["rack.input"].read(nil, 1)
354
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
355
- }).call(env({}))
356
- }.must_raise(Rack::Lint::LintError).
357
- message.must_match(/read called with non-String buffer/)
358
-
359
- lambda {
360
- Rack::Lint.new(lambda { |env|
361
- env["rack.input"].rewind(0)
362
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
363
- }).call(env({}))
364
- }.must_raise(Rack::Lint::LintError).
365
- message.must_match(/rewind called with arguments/)
366
-
367
- weirdio = Object.new
368
- class << weirdio
369
- def gets
370
- 42
371
- end
372
-
373
- def read
374
- 23
375
- end
376
-
377
- def each
378
- yield 23
379
- yield 42
380
- end
381
-
382
- def rewind
383
- raise Errno::ESPIPE, "Errno::ESPIPE"
384
- end
385
- end
386
-
387
- eof_weirdio = Object.new
388
- class << eof_weirdio
389
- def gets
390
- nil
391
- end
392
-
393
- def read(*args)
394
- nil
395
- end
396
-
397
- def each
398
- end
399
-
400
- def rewind
401
- end
402
- end
403
-
404
- lambda {
405
- Rack::Lint.new(lambda { |env|
406
- env["rack.input"].gets
407
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
408
- }).call(env("rack.input" => weirdio))
409
- }.must_raise(Rack::Lint::LintError).
410
- message.must_match(/gets didn't return a String/)
411
-
412
- lambda {
413
- Rack::Lint.new(lambda { |env|
414
- env["rack.input"].each { |x| }
415
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
416
- }).call(env("rack.input" => weirdio))
417
- }.must_raise(Rack::Lint::LintError).
418
- message.must_match(/each didn't yield a String/)
419
-
420
- lambda {
421
- Rack::Lint.new(lambda { |env|
422
- env["rack.input"].read
423
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
424
- }).call(env("rack.input" => weirdio))
425
- }.must_raise(Rack::Lint::LintError).
426
- message.must_match(/read didn't return nil or a String/)
427
-
428
- lambda {
429
- Rack::Lint.new(lambda { |env|
430
- env["rack.input"].read
431
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
432
- }).call(env("rack.input" => eof_weirdio))
433
- }.must_raise(Rack::Lint::LintError).
434
- message.must_match(/read\(nil\) returned nil on EOF/)
435
-
436
- lambda {
437
- Rack::Lint.new(lambda { |env|
438
- env["rack.input"].rewind
439
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
440
- }).call(env("rack.input" => weirdio))
441
- }.must_raise(Rack::Lint::LintError).
442
- message.must_match(/rewind raised Errno::ESPIPE/)
443
-
444
-
445
- lambda {
446
- Rack::Lint.new(lambda { |env|
447
- env["rack.input"].close
448
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
449
- }).call(env({}))
450
- }.must_raise(Rack::Lint::LintError).
451
- message.must_match(/close must not be called/)
452
- end
453
-
454
- it "notice error handling errors" do
455
- lambda {
456
- Rack::Lint.new(lambda { |env|
457
- env["rack.errors"].write(42)
458
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
459
- }).call(env({}))
460
- }.must_raise(Rack::Lint::LintError).
461
- message.must_match(/write not called with a String/)
462
-
463
- lambda {
464
- Rack::Lint.new(lambda { |env|
465
- env["rack.errors"].close
466
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
467
- }).call(env({}))
468
- }.must_raise(Rack::Lint::LintError).
469
- message.must_match(/close must not be called/)
470
- end
471
-
472
- it "notice HEAD errors" do
473
- Rack::Lint.new(lambda { |env|
474
- [200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
475
- }).call(env({"REQUEST_METHOD" => "HEAD"})).first.must_equal 200
476
-
477
- lambda {
478
- Rack::Lint.new(lambda { |env|
479
- [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
480
- }).call(env({"REQUEST_METHOD" => "HEAD"}))[2].each { }
481
- }.must_raise(Rack::Lint::LintError).
482
- message.must_match(/body was given for HEAD/)
483
- end
484
-
485
- def assert_lint(*args)
486
- hello_str = "hello world"
487
- hello_str.force_encoding(Encoding::ASCII_8BIT)
488
-
489
- Rack::Lint.new(lambda { |env|
490
- env["rack.input"].send(:read, *args)
491
- [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
492
- }).call(env({"rack.input" => StringIO.new(hello_str)})).
493
- first.must_equal 201
494
- end
495
-
496
- it "pass valid read calls" do
497
- assert_lint
498
- assert_lint 0
499
- assert_lint 1
500
- assert_lint nil
501
- assert_lint nil, ''
502
- assert_lint 1, ''
503
- end
504
- end
505
-
506
- describe "Rack::Lint::InputWrapper" do
507
- it "delegate :rewind to underlying IO object" do
508
- io = StringIO.new("123")
509
- wrapper = Rack::Lint::InputWrapper.new(io)
510
- wrapper.read.must_equal "123"
511
- wrapper.read.must_equal ""
512
- wrapper.rewind
513
- wrapper.read.must_equal "123"
514
- end
515
- end