rack 2.0.6 → 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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +220 -155
  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/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 -8
  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 +14 -11
  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 -52
  52. data/lib/rack/multipart/uploaded_file.rb +2 -0
  53. data/lib/rack/multipart.rb +5 -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 +80 -27
  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 +104 -21
  65. data/lib/rack/session/cookie.rb +21 -11
  66. data/lib/rack/session/memcache.rb +4 -87
  67. data/lib/rack/session/pool.rb +17 -8
  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 +55 -70
  74. data/lib/rack.rb +63 -60
  75. data/rack.gemspec +17 -7
  76. metadata +30 -171
  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 -95
  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 -515
  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 -722
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1398
  163. data/test/spec_response.rb +0 -510
  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 -320
  172. data/test/spec_session_pool.rb +0 -210
  173. data/test/spec_show_exceptions.rb +0 -93
  174. data/test/spec_show_status.rb +0 -104
  175. data/test/spec_static.rb +0 -184
  176. data/test/spec_tempfile_reaper.rb +0 -64
  177. data/test/spec_thin.rb +0 -96
  178. data/test/spec_urlmap.rb +0 -237
  179. data/test/spec_utils.rb +0 -742
  180. data/test/spec_version.rb +0 -11
  181. data/test/spec_webrick.rb +0 -206
  182. data/test/static/another/index.html +0 -1
  183. data/test/static/foo.html +0 -1
  184. data/test/static/index.html +0 -1
  185. data/test/testrequest.rb +0 -78
  186. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  187. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/spec_request.rb DELETED
@@ -1,1398 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'stringio'
3
- require 'cgi'
4
- require 'rack/request'
5
- require 'rack/mock'
6
- require 'rack/multipart'
7
- require 'securerandom'
8
-
9
- class RackRequestTest < Minitest::Spec
10
- it "copies the env when duping" do
11
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
12
- refute_same req.env, req.dup.env
13
- end
14
-
15
- it 'can check if something has been set' do
16
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
17
- refute req.has_header?("FOO")
18
- end
19
-
20
- it "can get a key from the env" do
21
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
22
- assert_equal "example.com", req.get_header("SERVER_NAME")
23
- end
24
-
25
- it 'can calculate the authority' do
26
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
27
- assert_equal "example.com:8080", req.authority
28
- end
29
-
30
- it 'can calculate the authority without a port' do
31
- req = make_request(Rack::MockRequest.env_for("http://example.com/"))
32
- assert_equal "example.com:80", req.authority
33
- end
34
-
35
- it 'can calculate the authority without a port on ssl' do
36
- req = make_request(Rack::MockRequest.env_for("https://example.com/"))
37
- assert_equal "example.com:443", req.authority
38
- end
39
-
40
- it 'yields to the block if no value has been set' do
41
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
42
- yielded = false
43
- req.fetch_header("FOO") do
44
- yielded = true
45
- req.set_header "FOO", 'bar'
46
- end
47
-
48
- assert yielded
49
- assert_equal "bar", req.get_header("FOO")
50
- end
51
-
52
- it 'can iterate over values' do
53
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
54
- req.set_header 'foo', 'bar'
55
- hash = {}
56
- req.each_header do |k,v|
57
- hash[k] = v
58
- end
59
- assert_equal 'bar', hash['foo']
60
- end
61
-
62
- it 'can set values in the env' do
63
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
64
- req.set_header("FOO", "BAR")
65
- assert_equal "BAR", req.get_header("FOO")
66
- end
67
-
68
- it 'can add to multivalued headers in the env' do
69
- req = make_request(Rack::MockRequest.env_for('http://example.com:8080/'))
70
-
71
- assert_equal '1', req.add_header('FOO', '1')
72
- assert_equal '1', req.get_header('FOO')
73
-
74
- assert_equal '1,2', req.add_header('FOO', '2')
75
- assert_equal '1,2', req.get_header('FOO')
76
-
77
- assert_equal '1,2', req.add_header('FOO', nil)
78
- assert_equal '1,2', req.get_header('FOO')
79
- end
80
-
81
- it 'can delete env values' do
82
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
83
- req.set_header 'foo', 'bar'
84
- assert req.has_header? 'foo'
85
- req.delete_header 'foo'
86
- refute req.has_header? 'foo'
87
- end
88
-
89
- it "wrap the rack variables" do
90
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
91
-
92
- req.body.must_respond_to :gets
93
- req.scheme.must_equal "http"
94
- req.request_method.must_equal "GET"
95
-
96
- req.must_be :get?
97
- req.wont_be :post?
98
- req.wont_be :put?
99
- req.wont_be :delete?
100
- req.wont_be :head?
101
- req.wont_be :patch?
102
-
103
- req.script_name.must_equal ""
104
- req.path_info.must_equal "/"
105
- req.query_string.must_equal ""
106
-
107
- req.host.must_equal "example.com"
108
- req.port.must_equal 8080
109
-
110
- req.content_length.must_equal "0"
111
- req.content_type.must_be_nil
112
- end
113
-
114
- it "figure out the correct host" do
115
- req = make_request \
116
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
117
- req.host.must_equal "www2.example.org"
118
-
119
- req = make_request \
120
- Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
121
- req.host.must_equal "example.org"
122
-
123
- req = make_request \
124
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
125
- req.host.must_equal "example.org"
126
-
127
- env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
128
- env.delete("SERVER_NAME")
129
- req = make_request(env)
130
- req.host.must_equal "192.168.1.1"
131
-
132
- env = Rack::MockRequest.env_for("/")
133
- env.delete("SERVER_NAME")
134
- req = make_request(env)
135
- req.host.must_equal ""
136
- end
137
-
138
- it "figure out the correct port" do
139
- req = make_request \
140
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
141
- req.port.must_equal 80
142
-
143
- req = make_request \
144
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
145
- req.port.must_equal 81
146
-
147
- req = make_request \
148
- Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
149
- req.port.must_equal 9292
150
-
151
- req = make_request \
152
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
153
- req.port.must_equal 9292
154
-
155
- req = make_request \
156
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
157
- req.port.must_equal 80
158
-
159
- req = make_request \
160
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
161
- req.port.must_equal 443
162
-
163
- req = make_request \
164
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
165
- req.port.must_equal 443
166
-
167
- req = make_request \
168
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
169
- req.port.must_equal 9393
170
-
171
- req = make_request \
172
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
173
- req.port.must_equal 9393
174
-
175
- req = make_request \
176
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
177
- req.port.must_equal 80
178
-
179
- req = make_request \
180
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80")
181
- req.port.must_equal 443
182
-
183
- req = make_request \
184
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https,https", "SERVER_PORT" => "80")
185
- req.port.must_equal 443
186
- end
187
-
188
- it "figure out the correct host with port" do
189
- req = make_request \
190
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
191
- req.host_with_port.must_equal "www2.example.org"
192
-
193
- req = make_request \
194
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
195
- req.host_with_port.must_equal "localhost:81"
196
-
197
- req = make_request \
198
- Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
199
- req.host_with_port.must_equal "example.org:9292"
200
-
201
- req = make_request \
202
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
203
- req.host_with_port.must_equal "example.org:9292"
204
-
205
- req = make_request \
206
- Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
207
- req.host_with_port.must_equal "example.org"
208
- end
209
-
210
- it "parse the query string" do
211
- req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
212
- req.query_string.must_equal "foo=bar&quux=bla"
213
- req.GET.must_equal "foo" => "bar", "quux" => "bla"
214
- req.POST.must_be :empty?
215
- req.params.must_equal "foo" => "bar", "quux" => "bla"
216
- end
217
-
218
- it "not truncate query strings containing semi-colons #543 only in POST" do
219
- mr = Rack::MockRequest.env_for("/",
220
- "REQUEST_METHOD" => 'POST',
221
- :input => "foo=bar&quux=b;la")
222
- req = make_request mr
223
- req.query_string.must_equal ""
224
- req.GET.must_be :empty?
225
- req.POST.must_equal "foo" => "bar", "quux" => "b;la"
226
- req.params.must_equal req.GET.merge(req.POST)
227
- end
228
-
229
- it "should use the query_parser for query parsing" do
230
- c = Class.new(Rack::QueryParser::Params) do
231
- def initialize(*)
232
- super
233
- @params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
234
- end
235
- end
236
- parser = Rack::QueryParser.new(c, 65536, 100)
237
- c = Class.new(Rack::Request) do
238
- define_method(:query_parser) do
239
- parser
240
- end
241
- end
242
- req = c.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
243
- req.GET[:foo].must_equal "bar"
244
- req.GET[:quux].must_equal "bla"
245
- req.params[:foo].must_equal "bar"
246
- req.params[:quux].must_equal "bla"
247
- end
248
-
249
- it "use semi-colons as separators for query strings in GET" do
250
- req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=b;la;wun=duh"))
251
- req.query_string.must_equal "foo=bar&quux=b;la;wun=duh"
252
- req.GET.must_equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh"
253
- req.POST.must_be :empty?
254
- req.params.must_equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh"
255
- end
256
-
257
- it "limit the keys from the GET query string" do
258
- env = Rack::MockRequest.env_for("/?foo=bar")
259
-
260
- old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
261
- begin
262
- req = make_request(env)
263
- lambda { req.GET }.must_raise RangeError
264
- ensure
265
- Rack::Utils.key_space_limit = old
266
- end
267
- end
268
-
269
- it "limit the key size per nested params hash" do
270
- nested_query = Rack::MockRequest.env_for("/?foo%5Bbar%5D%5Bbaz%5D%5Bqux%5D=1")
271
- plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
272
-
273
- old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
274
- begin
275
- exp = {"foo"=>{"bar"=>{"baz"=>{"qux"=>"1"}}}}
276
- make_request(nested_query).GET.must_equal exp
277
- lambda { make_request(plain_query).GET }.must_raise RangeError
278
- ensure
279
- Rack::Utils.key_space_limit = old
280
- end
281
- end
282
-
283
- it "not unify GET and POST when calling params" do
284
- mr = Rack::MockRequest.env_for("/?foo=quux",
285
- "REQUEST_METHOD" => 'POST',
286
- :input => "foo=bar&quux=bla"
287
- )
288
- req = make_request mr
289
-
290
- req.params
291
-
292
- req.GET.must_equal "foo" => "quux"
293
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
294
- req.params.must_equal req.GET.merge(req.POST)
295
- end
296
-
297
- it "use the query_parser's params_class for multipart params" do
298
- c = Class.new(Rack::QueryParser::Params) do
299
- def initialize(*)
300
- super
301
- @params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
302
- end
303
- end
304
- parser = Rack::QueryParser.new(c, 65536, 100)
305
- c = Class.new(Rack::Request) do
306
- define_method(:query_parser) do
307
- parser
308
- end
309
- end
310
- mr = Rack::MockRequest.env_for("/?foo=quux",
311
- "REQUEST_METHOD" => 'POST',
312
- :input => "foo=bar&quux=bla"
313
- )
314
- req = c.new mr
315
-
316
- req.params
317
-
318
- req.GET[:foo].must_equal "quux"
319
- req.POST[:foo].must_equal "bar"
320
- req.POST[:quux].must_equal "bla"
321
- req.params[:foo].must_equal "bar"
322
- req.params[:quux].must_equal "bla"
323
- end
324
-
325
- it "raise if input params has invalid %-encoding" do
326
- mr = Rack::MockRequest.env_for("/?foo=quux",
327
- "REQUEST_METHOD" => 'POST',
328
- :input => "a%=1"
329
- )
330
- req = make_request mr
331
-
332
- lambda { req.POST }.must_raise(Rack::Utils::InvalidParameterError).
333
- message.must_equal "invalid %-encoding (a%)"
334
- end
335
-
336
- it "raise if rack.input is missing" do
337
- req = make_request({})
338
- lambda { req.POST }.must_raise RuntimeError
339
- end
340
-
341
- it "parse POST data when method is POST and no Content-Type given" do
342
- req = make_request \
343
- Rack::MockRequest.env_for("/?foo=quux",
344
- "REQUEST_METHOD" => 'POST',
345
- :input => "foo=bar&quux=bla")
346
- req.content_type.must_be_nil
347
- req.media_type.must_be_nil
348
- req.query_string.must_equal "foo=quux"
349
- req.GET.must_equal "foo" => "quux"
350
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
351
- req.params.must_equal "foo" => "bar", "quux" => "bla"
352
- end
353
-
354
- it "limit the keys from the POST form data" do
355
- env = Rack::MockRequest.env_for("",
356
- "REQUEST_METHOD" => 'POST',
357
- :input => "foo=bar&quux=bla")
358
-
359
- old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
360
- begin
361
- req = make_request(env)
362
- lambda { req.POST }.must_raise RangeError
363
- ensure
364
- Rack::Utils.key_space_limit = old
365
- end
366
- end
367
-
368
- it "parse POST data with explicit content type regardless of method" do
369
- req = make_request \
370
- Rack::MockRequest.env_for("/",
371
- "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
372
- :input => "foo=bar&quux=bla")
373
- req.content_type.must_equal 'application/x-www-form-urlencoded;foo=bar'
374
- req.media_type.must_equal 'application/x-www-form-urlencoded'
375
- req.media_type_params['foo'].must_equal 'bar'
376
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
377
- req.params.must_equal "foo" => "bar", "quux" => "bla"
378
- end
379
-
380
- it "not parse POST data when media type is not form-data" do
381
- req = make_request \
382
- Rack::MockRequest.env_for("/?foo=quux",
383
- "REQUEST_METHOD" => 'POST',
384
- "CONTENT_TYPE" => 'text/plain;charset=utf-8',
385
- :input => "foo=bar&quux=bla")
386
- req.content_type.must_equal 'text/plain;charset=utf-8'
387
- req.media_type.must_equal 'text/plain'
388
- req.media_type_params['charset'].must_equal 'utf-8'
389
- req.POST.must_be :empty?
390
- req.params.must_equal "foo" => "quux"
391
- req.body.read.must_equal "foo=bar&quux=bla"
392
- end
393
-
394
- it "parse POST data on PUT when media type is form-data" do
395
- req = make_request \
396
- Rack::MockRequest.env_for("/?foo=quux",
397
- "REQUEST_METHOD" => 'PUT',
398
- "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
399
- :input => "foo=bar&quux=bla")
400
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
401
- req.body.read.must_equal "foo=bar&quux=bla"
402
- end
403
-
404
- it "rewind input after parsing POST data" do
405
- input = StringIO.new("foo=bar&quux=bla")
406
- req = make_request \
407
- Rack::MockRequest.env_for("/",
408
- "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
409
- :input => input)
410
- req.params.must_equal "foo" => "bar", "quux" => "bla"
411
- input.read.must_equal "foo=bar&quux=bla"
412
- end
413
-
414
- it "safely accepts POST requests with empty body" do
415
- mr = Rack::MockRequest.env_for("/",
416
- "REQUEST_METHOD" => "POST",
417
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
418
- "CONTENT_LENGTH" => '0',
419
- :input => nil)
420
-
421
- req = make_request mr
422
- req.query_string.must_equal ""
423
- req.GET.must_be :empty?
424
- req.POST.must_be :empty?
425
- req.params.must_equal({})
426
- end
427
-
428
- it "clean up Safari's ajax POST body" do
429
- req = make_request \
430
- Rack::MockRequest.env_for("/",
431
- 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
432
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
433
- end
434
-
435
- it "get value by key from params with #[]" do
436
- req = make_request \
437
- Rack::MockRequest.env_for("?foo=quux")
438
- req['foo'].must_equal 'quux'
439
- req[:foo].must_equal 'quux'
440
- end
441
-
442
- it "set value to key on params with #[]=" do
443
- req = make_request \
444
- Rack::MockRequest.env_for("?foo=duh")
445
- req['foo'].must_equal 'duh'
446
- req[:foo].must_equal 'duh'
447
- req.params.must_equal 'foo' => 'duh'
448
-
449
- if req.delegate?
450
- skip "delegate requests don't cache params, so mutations have no impact"
451
- end
452
-
453
- req['foo'] = 'bar'
454
- req.params.must_equal 'foo' => 'bar'
455
- req['foo'].must_equal 'bar'
456
- req[:foo].must_equal 'bar'
457
-
458
- req[:foo] = 'jaz'
459
- req.params.must_equal 'foo' => 'jaz'
460
- req['foo'].must_equal 'jaz'
461
- req[:foo].must_equal 'jaz'
462
- end
463
-
464
- it "return values for the keys in the order given from values_at" do
465
- req = make_request \
466
- Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
467
- req.values_at('foo').must_equal ['baz']
468
- req.values_at('foo', 'wun').must_equal ['baz', 'der']
469
- req.values_at('bar', 'foo', 'wun').must_equal ['ful', 'baz', 'der']
470
- end
471
-
472
- it "extract referrer correctly" do
473
- req = make_request \
474
- Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
475
- req.referer.must_equal "/some/path"
476
-
477
- req = make_request \
478
- Rack::MockRequest.env_for("/")
479
- req.referer.must_be_nil
480
- end
481
-
482
- it "extract user agent correctly" do
483
- req = make_request \
484
- Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
485
- req.user_agent.must_equal "Mozilla/4.0 (compatible)"
486
-
487
- req = make_request \
488
- Rack::MockRequest.env_for("/")
489
- req.user_agent.must_be_nil
490
- end
491
-
492
- it "treat missing content type as nil" do
493
- req = make_request \
494
- Rack::MockRequest.env_for("/")
495
- req.content_type.must_be_nil
496
- end
497
-
498
- it "treat empty content type as nil" do
499
- req = make_request \
500
- Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
501
- req.content_type.must_be_nil
502
- end
503
-
504
- it "return nil media type for empty content type" do
505
- req = make_request \
506
- Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
507
- req.media_type.must_be_nil
508
- end
509
-
510
- it "cache, but invalidates the cache" do
511
- req = make_request \
512
- Rack::MockRequest.env_for("/?foo=quux",
513
- "CONTENT_TYPE" => "application/x-www-form-urlencoded",
514
- :input => "foo=bar&quux=bla")
515
- req.GET.must_equal "foo" => "quux"
516
- req.GET.must_equal "foo" => "quux"
517
- req.set_header("QUERY_STRING", "bla=foo")
518
- req.GET.must_equal "bla" => "foo"
519
- req.GET.must_equal "bla" => "foo"
520
-
521
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
522
- req.POST.must_equal "foo" => "bar", "quux" => "bla"
523
- req.set_header("rack.input", StringIO.new("foo=bla&quux=bar"))
524
- req.POST.must_equal "foo" => "bla", "quux" => "bar"
525
- req.POST.must_equal "foo" => "bla", "quux" => "bar"
526
- end
527
-
528
- it "figure out if called via XHR" do
529
- req = make_request(Rack::MockRequest.env_for(""))
530
- req.wont_be :xhr?
531
-
532
- req = make_request \
533
- Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
534
- req.must_be :xhr?
535
- end
536
-
537
- it "ssl detection" do
538
- request = make_request(Rack::MockRequest.env_for("/"))
539
- request.scheme.must_equal "http"
540
- request.wont_be :ssl?
541
-
542
- request = make_request(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
543
- request.scheme.must_equal "https"
544
- request.must_be :ssl?
545
-
546
- request = make_request(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
547
- request.scheme.must_equal "https"
548
- request.must_be :ssl?
549
-
550
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
551
- request.scheme.must_equal "http"
552
- request.wont_be :ssl?
553
-
554
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
555
- request.scheme.must_equal "https"
556
- request.must_be :ssl?
557
-
558
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
559
- request.scheme.must_equal "https"
560
- request.must_be :ssl?
561
-
562
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
563
- request.scheme.must_equal "https"
564
- request.must_be :ssl?
565
-
566
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
567
- request.scheme.must_equal "https"
568
- request.must_be :ssl?
569
-
570
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
571
- request.scheme.must_equal "https"
572
- request.must_be :ssl?
573
- end
574
-
575
- it "prevents scheme abuse" do
576
- request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a."><script>alert(1)</script>'))
577
- request.scheme.must_equal 'http'
578
- end
579
-
580
- it "parse cookies" do
581
- req = make_request \
582
- Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
583
- req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
584
- req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
585
- req.delete_header("HTTP_COOKIE")
586
- req.cookies.must_equal({})
587
- end
588
-
589
- it "always return the same hash object" do
590
- req = make_request \
591
- Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
592
- hash = req.cookies
593
- req.env.delete("HTTP_COOKIE")
594
- req.cookies.must_equal hash
595
- req.env["HTTP_COOKIE"] = "zoo=m"
596
- req.cookies.must_equal hash
597
- end
598
-
599
- it "modify the cookies hash in place" do
600
- req = make_request(Rack::MockRequest.env_for(""))
601
- req.cookies.must_equal({})
602
- req.cookies['foo'] = 'bar'
603
- req.cookies.must_equal 'foo' => 'bar'
604
- end
605
-
606
- it "not modify the params hash in place" do
607
- e = Rack::MockRequest.env_for("")
608
- req1 = make_request(e)
609
- if req1.delegate?
610
- skip "delegate requests don't cache params, so mutations have no impact"
611
- end
612
- req1.params.must_equal({})
613
- req1.params['foo'] = 'bar'
614
- req1.params.must_equal 'foo' => 'bar'
615
- req2 = make_request(e)
616
- req2.params.must_equal({})
617
- end
618
-
619
- it "modify params hash if param is in GET" do
620
- e = Rack::MockRequest.env_for("?foo=duh")
621
- req1 = make_request(e)
622
- req1.params.must_equal 'foo' => 'duh'
623
- req1.update_param 'foo', 'bar'
624
- req1.params.must_equal 'foo' => 'bar'
625
- req2 = make_request(e)
626
- req2.params.must_equal 'foo' => 'bar'
627
- end
628
-
629
- it "modify params hash if param is in POST" do
630
- e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=duh')
631
- req1 = make_request(e)
632
- req1.params.must_equal 'foo' => 'duh'
633
- req1.update_param 'foo', 'bar'
634
- req1.params.must_equal 'foo' => 'bar'
635
- req2 = make_request(e)
636
- req2.params.must_equal 'foo' => 'bar'
637
- end
638
-
639
- it "modify params hash, even if param didn't exist before" do
640
- e = Rack::MockRequest.env_for("")
641
- req1 = make_request(e)
642
- req1.params.must_equal({})
643
- req1.update_param 'foo', 'bar'
644
- req1.params.must_equal 'foo' => 'bar'
645
- req2 = make_request(e)
646
- req2.params.must_equal 'foo' => 'bar'
647
- end
648
-
649
- it "modify params hash by changing only GET" do
650
- e = Rack::MockRequest.env_for("?foo=duhget")
651
- req = make_request(e)
652
- req.GET.must_equal 'foo' => 'duhget'
653
- req.POST.must_equal({})
654
- req.update_param 'foo', 'bar'
655
- req.GET.must_equal 'foo' => 'bar'
656
- req.POST.must_equal({})
657
- end
658
-
659
- it "modify params hash by changing only POST" do
660
- e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
661
- req = make_request(e)
662
- req.GET.must_equal({})
663
- req.POST.must_equal 'foo' => 'duhpost'
664
- req.update_param 'foo', 'bar'
665
- req.GET.must_equal({})
666
- req.POST.must_equal 'foo' => 'bar'
667
- end
668
-
669
- it "modify params hash, even if param is defined in both POST and GET" do
670
- e = Rack::MockRequest.env_for("?foo=duhget", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
671
- req1 = make_request(e)
672
- req1.GET.must_equal 'foo' => 'duhget'
673
- req1.POST.must_equal 'foo' => 'duhpost'
674
- req1.params.must_equal 'foo' => 'duhpost'
675
- req1.update_param 'foo', 'bar'
676
- req1.GET.must_equal 'foo' => 'bar'
677
- req1.POST.must_equal 'foo' => 'bar'
678
- req1.params.must_equal 'foo' => 'bar'
679
- req2 = make_request(e)
680
- req2.GET.must_equal 'foo' => 'bar'
681
- req2.POST.must_equal 'foo' => 'bar'
682
- req2.params.must_equal 'foo' => 'bar'
683
- req2.params.must_equal 'foo' => 'bar'
684
- end
685
-
686
- it "allow deleting from params hash if param is in GET" do
687
- e = Rack::MockRequest.env_for("?foo=bar")
688
- req1 = make_request(e)
689
- req1.params.must_equal 'foo' => 'bar'
690
- req1.delete_param('foo').must_equal 'bar'
691
- req1.params.must_equal({})
692
- req2 = make_request(e)
693
- req2.params.must_equal({})
694
- end
695
-
696
- it "allow deleting from params hash if param is in POST" do
697
- e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=bar')
698
- req1 = make_request(e)
699
- req1.params.must_equal 'foo' => 'bar'
700
- req1.delete_param('foo').must_equal 'bar'
701
- req1.params.must_equal({})
702
- req2 = make_request(e)
703
- req2.params.must_equal({})
704
- end
705
-
706
- it "pass through non-uri escaped cookies as-is" do
707
- req = make_request Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
708
- req.cookies["foo"].must_equal "%"
709
- end
710
-
711
- it "parse cookies according to RFC 2109" do
712
- req = make_request \
713
- Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
714
- req.cookies.must_equal 'foo' => 'bar'
715
- end
716
-
717
- it 'parse cookies with quotes' do
718
- req = make_request Rack::MockRequest.env_for('', {
719
- 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
720
- })
721
- req.cookies.must_equal({
722
- '$Version' => '"1"',
723
- 'Customer' => '"WILE_E_COYOTE"',
724
- '$Path' => '"/acme"',
725
- 'Part_Number' => '"Rocket_Launcher_0001"',
726
- })
727
- end
728
-
729
- it "provide setters" do
730
- req = make_request(e=Rack::MockRequest.env_for(""))
731
- req.script_name.must_equal ""
732
- req.script_name = "/foo"
733
- req.script_name.must_equal "/foo"
734
- e["SCRIPT_NAME"].must_equal "/foo"
735
-
736
- req.path_info.must_equal "/"
737
- req.path_info = "/foo"
738
- req.path_info.must_equal "/foo"
739
- e["PATH_INFO"].must_equal "/foo"
740
- end
741
-
742
- it "provide the original env" do
743
- req = make_request(e = Rack::MockRequest.env_for(""))
744
- req.env.must_equal e
745
- end
746
-
747
- it "restore the base URL" do
748
- make_request(Rack::MockRequest.env_for("")).base_url.
749
- must_equal "http://example.org"
750
- make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
751
- must_equal "http://example.org"
752
- end
753
-
754
- it "restore the URL" do
755
- make_request(Rack::MockRequest.env_for("")).url.
756
- must_equal "http://example.org/"
757
- make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
758
- must_equal "http://example.org/foo/"
759
- make_request(Rack::MockRequest.env_for("/foo")).url.
760
- must_equal "http://example.org/foo"
761
- make_request(Rack::MockRequest.env_for("?foo")).url.
762
- must_equal "http://example.org/?foo"
763
- make_request(Rack::MockRequest.env_for("http://example.org:8080/")).url.
764
- must_equal "http://example.org:8080/"
765
- make_request(Rack::MockRequest.env_for("https://example.org/")).url.
766
- must_equal "https://example.org/"
767
- make_request(Rack::MockRequest.env_for("coffee://example.org/")).url.
768
- must_equal "coffee://example.org/"
769
- make_request(Rack::MockRequest.env_for("coffee://example.org:443/")).url.
770
- must_equal "coffee://example.org:443/"
771
- make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
772
- must_equal "https://example.com:8080/foo?foo"
773
- end
774
-
775
- it "restore the full path" do
776
- make_request(Rack::MockRequest.env_for("")).fullpath.
777
- must_equal "/"
778
- make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
779
- must_equal "/foo/"
780
- make_request(Rack::MockRequest.env_for("/foo")).fullpath.
781
- must_equal "/foo"
782
- make_request(Rack::MockRequest.env_for("?foo")).fullpath.
783
- must_equal "/?foo"
784
- make_request(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
785
- must_equal "/"
786
- make_request(Rack::MockRequest.env_for("https://example.org/")).fullpath.
787
- must_equal "/"
788
-
789
- make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
790
- must_equal "/foo?foo"
791
- end
792
-
793
- it "handle multiple media type parameters" do
794
- req = make_request \
795
- Rack::MockRequest.env_for("/",
796
- "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"')
797
- req.wont_be :form_data?
798
- req.media_type_params.must_include 'foo'
799
- req.media_type_params['foo'].must_equal 'BAR'
800
- req.media_type_params.must_include 'baz'
801
- req.media_type_params['baz'].must_equal 'bizzle dizzle'
802
- req.media_type_params.wont_include 'BLING'
803
- req.media_type_params.must_include 'bling'
804
- req.media_type_params['bling'].must_equal 'bam'
805
- req.media_type_params['blong'].must_equal 'boo'
806
- req.media_type_params['zump'].must_equal 'zoo\"o'
807
- req.media_type_params['weird'].must_equal 'lol"'
808
- end
809
-
810
- it "parse with junk before boundary" do
811
- # Adapted from RFC 1867.
812
- input = <<EOF
813
- blah blah\r
814
- \r
815
- --AaB03x\r
816
- content-disposition: form-data; name="reply"\r
817
- \r
818
- yes\r
819
- --AaB03x\r
820
- content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
821
- Content-Type: image/jpeg\r
822
- Content-Transfer-Encoding: base64\r
823
- \r
824
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
825
- --AaB03x--\r
826
- EOF
827
- req = make_request Rack::MockRequest.env_for("/",
828
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
829
- "CONTENT_LENGTH" => input.size,
830
- :input => input)
831
-
832
- req.POST.must_include "fileupload"
833
- req.POST.must_include "reply"
834
-
835
- req.must_be :form_data?
836
- req.content_length.must_equal input.size
837
- req.media_type.must_equal 'multipart/form-data'
838
- req.media_type_params.must_include 'boundary'
839
- req.media_type_params['boundary'].must_equal 'AaB03x'
840
-
841
- req.POST["reply"].must_equal "yes"
842
-
843
- f = req.POST["fileupload"]
844
- f.must_be_kind_of Hash
845
- f[:type].must_equal "image/jpeg"
846
- f[:filename].must_equal "dj.jpg"
847
- f.must_include :tempfile
848
- f[:tempfile].size.must_equal 76
849
- end
850
-
851
- it "not infinite loop with a malformed HTTP request" do
852
- # Adapted from RFC 1867.
853
- input = <<EOF
854
- --AaB03x
855
- content-disposition: form-data; name="reply"
856
-
857
- yes
858
- --AaB03x
859
- content-disposition: form-data; name="fileupload"; filename="dj.jpg"
860
- Content-Type: image/jpeg
861
- Content-Transfer-Encoding: base64
862
-
863
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
864
- --AaB03x--
865
- EOF
866
- req = make_request Rack::MockRequest.env_for("/",
867
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
868
- "CONTENT_LENGTH" => input.size,
869
- :input => input)
870
-
871
- lambda{req.POST}.must_raise EOFError
872
- end
873
-
874
-
875
- it "parse multipart form data" do
876
- # Adapted from RFC 1867.
877
- input = <<EOF
878
- --AaB03x\r
879
- content-disposition: form-data; name="reply"\r
880
- \r
881
- yes\r
882
- --AaB03x\r
883
- content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
884
- Content-Type: image/jpeg\r
885
- Content-Transfer-Encoding: base64\r
886
- \r
887
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
888
- --AaB03x--\r
889
- EOF
890
- req = make_request Rack::MockRequest.env_for("/",
891
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
892
- "CONTENT_LENGTH" => input.size,
893
- :input => input)
894
-
895
- req.POST.must_include "fileupload"
896
- req.POST.must_include "reply"
897
-
898
- req.must_be :form_data?
899
- req.content_length.must_equal input.size
900
- req.media_type.must_equal 'multipart/form-data'
901
- req.media_type_params.must_include 'boundary'
902
- req.media_type_params['boundary'].must_equal 'AaB03x'
903
-
904
- req.POST["reply"].must_equal "yes"
905
-
906
- f = req.POST["fileupload"]
907
- f.must_be_kind_of Hash
908
- f[:type].must_equal "image/jpeg"
909
- f[:filename].must_equal "dj.jpg"
910
- f.must_include :tempfile
911
- f[:tempfile].size.must_equal 76
912
- end
913
-
914
- it "MultipartPartLimitError when request has too many multipart parts if limit set" do
915
- begin
916
- data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
917
- data += "--AaB03x--\r"
918
-
919
- options = {
920
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
921
- "CONTENT_LENGTH" => data.length.to_s,
922
- :input => StringIO.new(data)
923
- }
924
-
925
- request = make_request Rack::MockRequest.env_for("/", options)
926
- lambda { request.POST }.must_raise Rack::Multipart::MultipartPartLimitError
927
- end
928
- end
929
-
930
- it 'closes tempfiles it created in the case of too many created' do
931
- begin
932
- data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
933
- data += "--AaB03x--\r"
934
-
935
- files = []
936
- options = {
937
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
938
- "CONTENT_LENGTH" => data.length.to_s,
939
- Rack::RACK_MULTIPART_TEMPFILE_FACTORY => lambda { |filename, content_type|
940
- file = Tempfile.new(["RackMultipart", ::File.extname(filename)])
941
- files << file
942
- file
943
- },
944
- :input => StringIO.new(data)
945
- }
946
-
947
- request = make_request Rack::MockRequest.env_for("/", options)
948
- assert_raises(Rack::Multipart::MultipartPartLimitError) do
949
- request.POST
950
- end
951
- refute_predicate files, :empty?
952
- files.each { |f| assert_predicate f, :closed? }
953
- end
954
- end
955
-
956
- it "parse big multipart form data" do
957
- input = <<EOF
958
- --AaB03x\r
959
- content-disposition: form-data; name="huge"; filename="huge"\r
960
- \r
961
- #{"x"*32768}\r
962
- --AaB03x\r
963
- content-disposition: form-data; name="mean"; filename="mean"\r
964
- \r
965
- --AaB03xha\r
966
- --AaB03x--\r
967
- EOF
968
- req = make_request Rack::MockRequest.env_for("/",
969
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
970
- "CONTENT_LENGTH" => input.size,
971
- :input => input)
972
-
973
- req.POST["huge"][:tempfile].size.must_equal 32768
974
- req.POST["mean"][:tempfile].size.must_equal 10
975
- req.POST["mean"][:tempfile].read.must_equal "--AaB03xha"
976
- end
977
-
978
- it "record tempfiles from multipart form data in env[rack.tempfiles]" do
979
- input = <<EOF
980
- --AaB03x\r
981
- content-disposition: form-data; name="fileupload"; filename="foo.jpg"\r
982
- Content-Type: image/jpeg\r
983
- Content-Transfer-Encoding: base64\r
984
- \r
985
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
986
- --AaB03x\r
987
- content-disposition: form-data; name="fileupload"; filename="bar.jpg"\r
988
- Content-Type: image/jpeg\r
989
- Content-Transfer-Encoding: base64\r
990
- \r
991
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
992
- --AaB03x--\r
993
- EOF
994
- env = Rack::MockRequest.env_for("/",
995
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
996
- "CONTENT_LENGTH" => input.size,
997
- :input => input)
998
- req = make_request(env)
999
- req.params
1000
- env['rack.tempfiles'].size.must_equal 2
1001
- end
1002
-
1003
- it "detect invalid multipart form data" do
1004
- input = <<EOF
1005
- --AaB03x\r
1006
- content-disposition: form-data; name="huge"; filename="huge"\r
1007
- EOF
1008
- req = make_request Rack::MockRequest.env_for("/",
1009
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1010
- "CONTENT_LENGTH" => input.size,
1011
- :input => input)
1012
-
1013
- lambda { req.POST }.must_raise EOFError
1014
-
1015
- input = <<EOF
1016
- --AaB03x\r
1017
- content-disposition: form-data; name="huge"; filename="huge"\r
1018
- \r
1019
- foo\r
1020
- EOF
1021
- req = make_request Rack::MockRequest.env_for("/",
1022
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1023
- "CONTENT_LENGTH" => input.size,
1024
- :input => input)
1025
-
1026
- lambda { req.POST }.must_raise EOFError
1027
-
1028
- input = <<EOF
1029
- --AaB03x\r
1030
- content-disposition: form-data; name="huge"; filename="huge"\r
1031
- \r
1032
- foo\r
1033
- EOF
1034
- req = make_request Rack::MockRequest.env_for("/",
1035
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1036
- "CONTENT_LENGTH" => input.size,
1037
- :input => input)
1038
-
1039
- lambda { req.POST }.must_raise EOFError
1040
- end
1041
-
1042
- it "consistently raise EOFError on bad multipart form data" do
1043
- input = <<EOF
1044
- --AaB03x\r
1045
- content-disposition: form-data; name="huge"; filename="huge"\r
1046
- EOF
1047
- req = make_request Rack::MockRequest.env_for("/",
1048
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1049
- "CONTENT_LENGTH" => input.size,
1050
- :input => input)
1051
-
1052
- lambda { req.POST }.must_raise EOFError
1053
- lambda { req.POST }.must_raise EOFError
1054
- end
1055
-
1056
- it "correctly parse the part name from Content-Id header" do
1057
- input = <<EOF
1058
- --AaB03x\r
1059
- Content-Type: text/xml; charset=utf-8\r
1060
- Content-Id: <soap-start>\r
1061
- Content-Transfer-Encoding: 7bit\r
1062
- \r
1063
- foo\r
1064
- --AaB03x--\r
1065
- EOF
1066
- req = make_request Rack::MockRequest.env_for("/",
1067
- "CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
1068
- "CONTENT_LENGTH" => input.size,
1069
- :input => input)
1070
-
1071
- req.params.keys.must_equal ["<soap-start>"]
1072
- end
1073
-
1074
- it "not try to interpret binary as utf8" do
1075
- input = <<EOF
1076
- --AaB03x\r
1077
- content-disposition: form-data; name="fileupload"; filename="junk.a"\r
1078
- content-type: application/octet-stream\r
1079
- \r
1080
- #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
1081
- --AaB03x--\r
1082
- EOF
1083
-
1084
- req = make_request Rack::MockRequest.env_for("/",
1085
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1086
- "CONTENT_LENGTH" => input.size,
1087
- :input => input)
1088
-
1089
- req.POST["fileupload"][:tempfile].size.must_equal 4
1090
- end
1091
-
1092
- it "use form_hash when form_input is a Tempfile" do
1093
- input = "{foo: 'bar'}"
1094
-
1095
- rack_input = Tempfile.new("rackspec")
1096
- rack_input.write(input)
1097
- rack_input.rewind
1098
-
1099
- req = make_request Rack::MockRequest.env_for("/",
1100
- "rack.request.form_hash" => {'foo' => 'bar'},
1101
- "rack.request.form_input" => rack_input,
1102
- :input => rack_input)
1103
-
1104
- req.POST.must_equal req.env['rack.request.form_hash']
1105
- end
1106
-
1107
- it "conform to the Rack spec" do
1108
- app = lambda { |env|
1109
- content = make_request(env).POST["file"].inspect
1110
- size = content.bytesize
1111
- [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
1112
- }
1113
-
1114
- input = <<EOF
1115
- --AaB03x\r
1116
- content-disposition: form-data; name="reply"\r
1117
- \r
1118
- yes\r
1119
- --AaB03x\r
1120
- content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
1121
- Content-Type: image/jpeg\r
1122
- Content-Transfer-Encoding: base64\r
1123
- \r
1124
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
1125
- --AaB03x--\r
1126
- EOF
1127
- input.force_encoding(Encoding::ASCII_8BIT)
1128
- res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
1129
- "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
1130
- "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
1131
-
1132
- res.must_be :ok?
1133
- end
1134
-
1135
- it "parse Accept-Encoding correctly" do
1136
- parser = lambda do |x|
1137
- make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
1138
- end
1139
-
1140
- parser.call(nil).must_equal []
1141
-
1142
- parser.call("compress, gzip").must_equal [["compress", 1.0], ["gzip", 1.0]]
1143
- parser.call("").must_equal []
1144
- parser.call("*").must_equal [["*", 1.0]]
1145
- parser.call("compress;q=0.5, gzip;q=1.0").must_equal [["compress", 0.5], ["gzip", 1.0]]
1146
- parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").must_equal [["gzip", 1.0], ["identity", 0.5], ["*", 0] ]
1147
-
1148
- parser.call("gzip ; q=0.9").must_equal [["gzip", 0.9]]
1149
- parser.call("gzip ; deflate").must_equal [["gzip", 1.0]]
1150
- end
1151
-
1152
- it "parse Accept-Language correctly" do
1153
- parser = lambda do |x|
1154
- make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
1155
- end
1156
-
1157
- parser.call(nil).must_equal []
1158
-
1159
- parser.call("fr, en").must_equal [["fr", 1.0], ["en", 1.0]]
1160
- parser.call("").must_equal []
1161
- parser.call("*").must_equal [["*", 1.0]]
1162
- parser.call("fr;q=0.5, en;q=1.0").must_equal [["fr", 0.5], ["en", 1.0]]
1163
- parser.call("fr;q=1.0, en; q=0.5, *;q=0").must_equal [["fr", 1.0], ["en", 0.5], ["*", 0] ]
1164
-
1165
- parser.call("fr ; q=0.9").must_equal [["fr", 0.9]]
1166
- parser.call("fr").must_equal [["fr", 1.0]]
1167
- end
1168
-
1169
- def ip_app
1170
- lambda { |env|
1171
- request = make_request(env)
1172
- response = Rack::Response.new
1173
- response.write request.ip
1174
- response.finish
1175
- }
1176
- end
1177
-
1178
- it 'provide ip information' do
1179
- mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1180
-
1181
- res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4'
1182
- res.body.must_equal '1.2.3.4'
1183
-
1184
- res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329'
1185
- res.body.must_equal 'fe80::202:b3ff:fe1e:8329'
1186
-
1187
- res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
1188
- res.body.must_equal '1.2.3.4'
1189
- end
1190
-
1191
- it 'deals with proxies' do
1192
- mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1193
-
1194
- res = mock.get '/',
1195
- 'REMOTE_ADDR' => '1.2.3.4',
1196
- 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1197
- res.body.must_equal '1.2.3.4'
1198
-
1199
- res = mock.get '/',
1200
- 'REMOTE_ADDR' => '1.2.3.4',
1201
- 'HTTP_X_FORWARDED_FOR' => 'unknown'
1202
- res.body.must_equal '1.2.3.4'
1203
-
1204
- res = mock.get '/',
1205
- 'REMOTE_ADDR' => '127.0.0.1',
1206
- 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1207
- res.body.must_equal '3.4.5.6'
1208
-
1209
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
1210
- res.body.must_equal '3.4.5.6'
1211
-
1212
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
1213
- res.body.must_equal '3.4.5.6'
1214
-
1215
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
1216
- res.body.must_equal '3.4.5.6'
1217
-
1218
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
1219
- res.body.must_equal '3.4.5.6'
1220
-
1221
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6'
1222
- res.body.must_equal '3.4.5.6'
1223
-
1224
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
1225
- res.body.must_equal 'unknown'
1226
-
1227
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1'
1228
- res.body.must_equal 'unknown'
1229
-
1230
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1'
1231
- res.body.must_equal 'unknown'
1232
-
1233
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
1234
- res.body.must_equal '3.4.5.6'
1235
-
1236
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11'
1237
- res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
1238
-
1239
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1'
1240
- res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
1241
-
1242
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11'
1243
- res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
1244
-
1245
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000'
1246
- res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
1247
-
1248
- res = mock.get '/',
1249
- 'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1',
1250
- 'HTTP_CLIENT_IP' => '1.1.1.1'
1251
- res.body.must_equal '1.1.1.1'
1252
-
1253
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
1254
- res.body.must_equal '9.9.9.9'
1255
-
1256
- res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329'
1257
- res.body.must_equal 'fe80::202:b3ff:fe1e:8329'
1258
-
1259
- # Unix Sockets
1260
- res = mock.get '/',
1261
- 'REMOTE_ADDR' => 'unix',
1262
- 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1263
- res.body.must_equal '3.4.5.6'
1264
-
1265
- res = mock.get '/',
1266
- 'REMOTE_ADDR' => 'unix:/tmp/foo',
1267
- 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1268
- res.body.must_equal '3.4.5.6'
1269
- end
1270
-
1271
- it "not allow IP spoofing via Client-IP and X-Forwarded-For headers" do
1272
- mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1273
-
1274
- # IP Spoofing attempt:
1275
- # Client sends X-Forwarded-For: 6.6.6.6
1276
- # Client-IP: 6.6.6.6
1277
- # Load balancer adds X-Forwarded-For: 2.2.2.3, 192.168.0.7
1278
- # App receives: X-Forwarded-For: 6.6.6.6
1279
- # X-Forwarded-For: 2.2.2.3, 192.168.0.7
1280
- # Client-IP: 6.6.6.6
1281
- # Rack env: HTTP_X_FORWARDED_FOR: '6.6.6.6, 2.2.2.3, 192.168.0.7'
1282
- # HTTP_CLIENT_IP: '6.6.6.6'
1283
- res = mock.get '/',
1284
- 'HTTP_X_FORWARDED_FOR' => '6.6.6.6, 2.2.2.3, 192.168.0.7',
1285
- 'HTTP_CLIENT_IP' => '6.6.6.6'
1286
- res.body.must_equal '2.2.2.3'
1287
- end
1288
-
1289
- it "regard local addresses as proxies" do
1290
- req = make_request(Rack::MockRequest.env_for("/"))
1291
- req.trusted_proxy?('127.0.0.1').must_equal 0
1292
- req.trusted_proxy?('10.0.0.1').must_equal 0
1293
- req.trusted_proxy?('172.16.0.1').must_equal 0
1294
- req.trusted_proxy?('172.20.0.1').must_equal 0
1295
- req.trusted_proxy?('172.30.0.1').must_equal 0
1296
- req.trusted_proxy?('172.31.0.1').must_equal 0
1297
- req.trusted_proxy?('192.168.0.1').must_equal 0
1298
- req.trusted_proxy?('::1').must_equal 0
1299
- req.trusted_proxy?('fd00::').must_equal 0
1300
- req.trusted_proxy?('localhost').must_equal 0
1301
- req.trusted_proxy?('unix').must_equal 0
1302
- req.trusted_proxy?('unix:/tmp/sock').must_equal 0
1303
-
1304
- req.trusted_proxy?("unix.example.org").must_be_nil
1305
- req.trusted_proxy?("example.org\n127.0.0.1").must_be_nil
1306
- req.trusted_proxy?("127.0.0.1\nexample.org").must_be_nil
1307
- req.trusted_proxy?("11.0.0.1").must_be_nil
1308
- req.trusted_proxy?("172.15.0.1").must_be_nil
1309
- req.trusted_proxy?("172.32.0.1").must_be_nil
1310
- req.trusted_proxy?("2001:470:1f0b:18f8::1").must_be_nil
1311
- end
1312
-
1313
- it "sets the default session to an empty hash" do
1314
- req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
1315
- assert_equal Hash.new, req.session
1316
- end
1317
-
1318
- class MyRequest < Rack::Request
1319
- def params
1320
- {:foo => "bar"}
1321
- end
1322
- end
1323
-
1324
- it "allow subclass request to be instantiated after parent request" do
1325
- env = Rack::MockRequest.env_for("/?foo=bar")
1326
-
1327
- req1 = make_request(env)
1328
- req1.GET.must_equal "foo" => "bar"
1329
- req1.params.must_equal "foo" => "bar"
1330
-
1331
- req2 = MyRequest.new(env)
1332
- req2.GET.must_equal "foo" => "bar"
1333
- req2.params.must_equal :foo => "bar"
1334
- end
1335
-
1336
- it "allow parent request to be instantiated after subclass request" do
1337
- env = Rack::MockRequest.env_for("/?foo=bar")
1338
-
1339
- req1 = MyRequest.new(env)
1340
- req1.GET.must_equal "foo" => "bar"
1341
- req1.params.must_equal :foo => "bar"
1342
-
1343
- req2 = make_request(env)
1344
- req2.GET.must_equal "foo" => "bar"
1345
- req2.params.must_equal "foo" => "bar"
1346
- end
1347
-
1348
- it "raise TypeError every time if request parameters are broken" do
1349
- broken_query = Rack::MockRequest.env_for("/?foo%5B%5D=0&foo%5Bbar%5D=1")
1350
- req = make_request(broken_query)
1351
- lambda{req.GET}.must_raise TypeError
1352
- lambda{req.params}.must_raise TypeError
1353
- end
1354
-
1355
- (0x20...0x7E).collect { |a|
1356
- b = a.chr
1357
- c = CGI.escape(b)
1358
- it "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
1359
- url = "/?foo=#{c}bar#{c}"
1360
- env = Rack::MockRequest.env_for(url)
1361
- req2 = make_request(env)
1362
- req2.GET.must_equal "foo" => "#{b}bar#{b}"
1363
- req2.params.must_equal "foo" => "#{b}bar#{b}"
1364
- end
1365
- }
1366
-
1367
- class NonDelegate < Rack::Request
1368
- def delegate?; false; end
1369
- end
1370
-
1371
- def make_request(env)
1372
- NonDelegate.new env
1373
- end
1374
-
1375
- class TestProxyRequest < RackRequestTest
1376
- class DelegateRequest
1377
- include Rack::Request::Helpers
1378
- extend Forwardable
1379
-
1380
- def_delegators :@req, :has_header?, :get_header, :fetch_header,
1381
- :each_header, :set_header, :add_header, :delete_header
1382
-
1383
- def_delegators :@req, :[], :[]=, :values_at
1384
-
1385
- def initialize(req)
1386
- @req = req
1387
- end
1388
-
1389
- def delegate?; true; end
1390
-
1391
- def env; @req.env.dup; end
1392
- end
1393
-
1394
- def make_request(env)
1395
- DelegateRequest.new super(env)
1396
- end
1397
- end
1398
- end