request_test.rb 31.7 KB
Newer Older
1
require 'abstract_unit'
2

3
class RequestTest < ActiveSupport::TestCase
4 5

  def url_for(options = {})
6
    options = { host: 'www.example.com' }.merge!(options)
7 8 9 10 11 12 13 14 15 16 17
    ActionDispatch::Http::URL.url_for(options)
  end

  test "url_for class method" do
    e = assert_raise(ArgumentError) { url_for(:host => nil) }
    assert_match(/Please provide the :host parameter/, e.message)

    assert_equal '/books', url_for(:only_path => true, :path => '/books')

    assert_equal 'http://www.example.com',  url_for
    assert_equal 'http://api.example.com',  url_for(:subdomain => 'api')
18
    assert_equal 'http://example.com',      url_for(:subdomain => false)
19 20 21 22 23 24 25 26 27
    assert_equal 'http://www.ror.com',      url_for(:domain => 'ror.com')
    assert_equal 'http://api.ror.co.uk',    url_for(:host => 'www.ror.co.uk', :subdomain => 'api', :tld_length => 2)
    assert_equal 'http://www.example.com:8080',   url_for(:port => 8080)
    assert_equal 'https://www.example.com',       url_for(:protocol => 'https')
    assert_equal 'http://www.example.com/docs',   url_for(:path => '/docs')
    assert_equal 'http://www.example.com#signup', url_for(:anchor => 'signup')
    assert_equal 'http://www.example.com/',       url_for(:trailing_slash => true)
    assert_equal 'http://dhh:supersecret@www.example.com', url_for(:user => 'dhh', :password => 'supersecret')
    assert_equal 'http://www.example.com?search=books',    url_for(:params => { :search => 'books' })
28 29
    assert_equal 'http://www.example.com?params=',  url_for(:params => '')
    assert_equal 'http://www.example.com?params=1', url_for(:params => 1)
30 31
  end

32
  test "remote ip" do
33 34
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4'
    assert_equal '1.2.3.4', request.remote_ip
35

36 37
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
    assert_equal '1.2.3.4', request.remote_ip
38

39
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4',
40
                           'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
41
    assert_equal '3.4.5.6', request.remote_ip
42

43
    request = stub_request 'REMOTE_ADDR' => '127.0.0.1',
44
                           'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
45
    assert_equal '3.4.5.6', request.remote_ip
J
Jeremy Kemper 已提交
46

47
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6,unknown'
48
    assert_equal '3.4.5.6', request.remote_ip
49

50
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.16.0.1,3.4.5.6'
51
    assert_equal nil, request.remote_ip
J
Jeremy Kemper 已提交
52

53
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
54
    assert_equal nil, request.remote_ip
55

56
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
57
    assert_equal nil, request.remote_ip
58

59
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
60
    assert_equal nil, request.remote_ip
61

62
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '127.0.0.1,3.4.5.6'
63
    assert_equal nil, request.remote_ip
64

65
    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
66
    assert_equal nil, request.remote_ip
67

68
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 172.31.4.4'
69
    assert_equal '3.4.5.6', request.remote_ip
70

71 72 73
    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address'
    assert_equal nil, request.remote_ip

74 75
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
                           'HTTP_CLIENT_IP'       => '2.2.2.2'
C
Carlhuda 已提交
76
    e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
77
      request.remote_ip
J
Jeremy Kemper 已提交
78
    }
79 80 81
    assert_match(/IP spoofing attack/, e.message)
    assert_match(/HTTP_X_FORWARDED_FOR="1.1.1.1"/, e.message)
    assert_match(/HTTP_CLIENT_IP="2.2.2.2"/, e.message)
J
Jeremy Kemper 已提交
82

83 84 85 86 87
    # turn IP Spoofing detection off.
    # This is useful for sites that are aimed at non-IP clients.  The typical
    # example is WAP.  Since the cellular network is not IP based, it's a
    # leap of faith to assume that their proxies are ever going to set the
    # HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly.
88
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
C
Carlhuda 已提交
89 90
                           'HTTP_CLIENT_IP'       => '2.2.2.2',
                           :ip_spoofing_check => false
91
    assert_equal '2.2.2.2', request.remote_ip
92

93
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 8.8.8.8'
94
    assert_equal '9.9.9.9', request.remote_ip
95 96
  end

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  test "remote ip v6" do
    request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => '::1',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '::1, ::1, fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,::1'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
                           'HTTP_CLIENT_IP'       => '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
      request.remote_ip
    }
    assert_match(/IP spoofing attack/, e.message)
    assert_match(/HTTP_X_FORWARDED_FOR="fe80:0000:0000:0000:0202:b3ff:fe1e:8329"/, e.message)
    assert_match(/HTTP_CLIENT_IP="2001:0db8:85a3:0000:0000:8a2e:0370:7334"/, e.message)

    # Turn IP Spoofing detection off.
    # This is useful for sites that are aimed at non-IP clients.  The typical
    # example is WAP.  Since the cellular network is not IP based, it's a
    # leap of faith to assume that their proxies are ever going to set the
    # HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly.
    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329',
                           'HTTP_CLIENT_IP'       => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
                           :ip_spoofing_check     => false
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329, 2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip
  end

A
Andre Arko 已提交
159 160 161 162 163
  test "remote ip when the remote ip middleware returns nil" do
    request = stub_request 'REMOTE_ADDR' => '127.0.0.1'
    assert_equal '127.0.0.1', request.remote_ip
  end

164 165
  test "remote ip with user specified trusted proxies String" do
    @trusted_proxies = "67.205.106.73"
166

167 168
    request = stub_request 'REMOTE_ADDR' => '3.4.5.6',
                           'HTTP_X_FORWARDED_FOR' => '67.205.106.73'
169 170 171
    assert_equal '3.4.5.6', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => '172.16.0.1,67.205.106.73',
172 173
                           'HTTP_X_FORWARDED_FOR' => '67.205.106.73'
    assert_equal '172.16.0.1', request.remote_ip
174

175 176
    request = stub_request 'REMOTE_ADDR' => '67.205.106.73,3.4.5.6',
                           'HTTP_X_FORWARDED_FOR' => '67.205.106.73'
177
    assert_equal '3.4.5.6', request.remote_ip
178 179

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73'
180
    assert_equal nil, request.remote_ip
181

182
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '3.4.5.6, 9.9.9.9, 10.0.0.1, 67.205.106.73'
183 184 185
    assert_equal '3.4.5.6', request.remote_ip
  end

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
  test "remote ip v6 with user specified trusted proxies String" do
    @trusted_proxies = 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'

    request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'REMOTE_ADDR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,::1',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal nil, request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329,2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    assert_equal nil, request.remote_ip
  end

208 209 210 211 212 213 214
  test "remote ip with user specified trusted proxies Regexp" do
    @trusted_proxies = /^67\.205\.106\.73$/i

    request = stub_request 'REMOTE_ADDR' => '67.205.106.73',
                           'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip

215
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '67.205.106.73, 10.0.0.1, 9.9.9.9, 3.4.5.6'
216 217 218 219 220 221 222 223 224 225 226 227
    assert_equal nil, request.remote_ip
  end

  test "remote ip v6 with user specified trusted proxies Regexp" do
    @trusted_proxies = /^fe80:0000:0000:0000:0202:b3ff:fe1e:8329$/i

    request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
                           'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329'
    assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip

    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329, 2001:0db8:85a3:0000:0000:8a2e:0370:7334'
    assert_equal nil, request.remote_ip
228 229
  end

230
  test "domains" do
231 232
    request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org'
    assert_equal "rubyonrails.org", request.domain
233

234 235
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk"
    assert_equal "rubyonrails.co.uk", request.domain(2)
236

237 238 239
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk", :tld_length => 2
    assert_equal "rubyonrails.co.uk", request.domain

240 241
    request = stub_request 'HTTP_HOST' => "192.168.1.200"
    assert_nil request.domain
242

243 244
    request = stub_request 'HTTP_HOST' => "foo.192.168.1.200"
    assert_nil request.domain
245

246 247
    request = stub_request 'HTTP_HOST' => "192.168.1.200.com"
    assert_equal "200.com", request.domain
248 249
  end

250
  test "subdomains" do
251 252
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.org"
    assert_equal %w( www ), request.subdomains
J
José Valim 已提交
253
    assert_equal "www", request.subdomain
254

255 256
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk"
    assert_equal %w( www ), request.subdomains(2)
J
José Valim 已提交
257
    assert_equal "www", request.subdomain(2)
258

259 260
    request = stub_request 'HTTP_HOST' => "dev.www.rubyonrails.co.uk"
    assert_equal %w( dev www ), request.subdomains(2)
J
José Valim 已提交
261
    assert_equal "dev.www", request.subdomain(2)
262

263 264
    request = stub_request 'HTTP_HOST' => "dev.www.rubyonrails.co.uk", :tld_length => 2
    assert_equal %w( dev www ), request.subdomains
J
José Valim 已提交
265
    assert_equal "dev.www", request.subdomain
266

267 268
    request = stub_request 'HTTP_HOST' => "foobar.foobar.com"
    assert_equal %w( foobar ), request.subdomains
J
José Valim 已提交
269
    assert_equal "foobar", request.subdomain
270

271 272
    request = stub_request 'HTTP_HOST' => "192.168.1.200"
    assert_equal [], request.subdomains
J
José Valim 已提交
273
    assert_equal "", request.subdomain
274

275 276
    request = stub_request 'HTTP_HOST' => "foo.192.168.1.200"
    assert_equal [], request.subdomains
J
José Valim 已提交
277
    assert_equal "", request.subdomain
278

279 280
    request = stub_request 'HTTP_HOST' => "192.168.1.200.com"
    assert_equal %w( 192 168 1 ), request.subdomains
J
José Valim 已提交
281
    assert_equal "192.168.1", request.subdomain
282

283 284
    request = stub_request 'HTTP_HOST' => nil
    assert_equal [], request.subdomains
J
José Valim 已提交
285
    assert_equal "", request.subdomain
286
  end
287

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
  test "standard_port" do
    request = stub_request
    assert_equal 80, request.standard_port

    request = stub_request 'HTTPS' => 'on'
    assert_equal 443, request.standard_port
  end

  test "standard_port?" do
    request = stub_request
    assert !request.ssl?
    assert request.standard_port?

    request = stub_request 'HTTPS' => 'on'
    assert request.ssl?
    assert request.standard_port?

    request = stub_request 'HTTP_HOST' => 'www.example.org:8080'
    assert !request.ssl?
    assert !request.standard_port?

    request = stub_request 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'
    assert request.ssl?
    assert !request.standard_port?
  end

314
  test "optional port" do
315
    request = stub_request 'HTTP_HOST' => 'www.example.org:80'
316
    assert_equal nil, request.optional_port
317

318
    request = stub_request 'HTTP_HOST' => 'www.example.org:8080'
319
    assert_equal 8080, request.optional_port
320
  end
321

322 323 324 325 326 327 328 329
  test "port string" do
    request = stub_request 'HTTP_HOST' => 'www.example.org:80'
    assert_equal '', request.port_string

    request = stub_request 'HTTP_HOST' => 'www.example.org:8080'
    assert_equal ':8080', request.port_string
  end

330
  test "full path" do
331
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1'
332 333
    assert_equal "/path/of/some/uri?mapped=1", request.fullpath
    assert_equal "/path/of/some/uri",          request.path_info
334

335
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri'
336 337
    assert_equal "/path/of/some/uri", request.fullpath
    assert_equal "/path/of/some/uri", request.path_info
338

339
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/'
340 341
    assert_equal "/", request.fullpath
    assert_equal "/", request.path_info
342

343
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/', 'QUERY_STRING' => 'm=b'
344 345
    assert_equal "/?m=b", request.fullpath
    assert_equal "/",     request.path_info
346

347
    request = stub_request 'SCRIPT_NAME' => '/hieraki', 'PATH_INFO' => '/'
348 349
    assert_equal "/hieraki/", request.fullpath
    assert_equal "/",         request.path_info
350

351
    request = stub_request 'SCRIPT_NAME' => '/collaboration/hieraki', 'PATH_INFO' => '/books/edit/2'
352 353
    assert_equal "/collaboration/hieraki/books/edit/2", request.fullpath
    assert_equal "/books/edit/2",                       request.path_info
354

355
    request = stub_request 'SCRIPT_NAME' => '/path', 'PATH_INFO' => '/of/some/uri', 'QUERY_STRING' => 'mapped=1'
356 357
    assert_equal "/path/of/some/uri?mapped=1", request.fullpath
    assert_equal "/of/some/uri",               request.path_info
358
  end
359

360
  test "host with default port" do
361 362
    request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80'
    assert_equal "rubyonrails.org", request.host_with_port
363
  end
364

365
  test "host with non default port" do
366 367
    request = stub_request 'HTTP_HOST' => 'rubyonrails.org:81'
    assert_equal "rubyonrails.org:81", request.host_with_port
368
  end
369

370
  test "server software" do
371 372
    request = stub_request
    assert_equal nil, request.server_software
373

374 375
    request = stub_request 'SERVER_SOFTWARE' => 'Apache3.422'
    assert_equal 'apache', request.server_software
376

377 378
    request = stub_request 'SERVER_SOFTWARE' => 'lighttpd(1.1.4)'
    assert_equal 'lighttpd', request.server_software
379
  end
380

381
  test "xml http request" do
382 383 384 385
    request = stub_request

    assert !request.xml_http_request?
    assert !request.xhr?
386

387 388 389
    request = stub_request 'HTTP_X_REQUESTED_WITH' => 'DefinitelyNotAjax1.0'
    assert !request.xml_http_request?
    assert !request.xhr?
390

391 392 393
    request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
    assert request.xml_http_request?
    assert request.xhr?
394
  end
395

396
  test "reports ssl" do
397 398 399 400 401
    request = stub_request
    assert !request.ssl?

    request = stub_request 'HTTPS' => 'on'
    assert request.ssl?
402 403
  end

404
  test "reports ssl when proxied via lighttpd" do
405 406 407 408 409
    request = stub_request
    assert !request.ssl?

    request = stub_request 'HTTP_X_FORWARDED_PROTO' => 'https'
    assert request.ssl?
410
  end
411

412 413 414 415 416 417 418 419 420 421
  test "scheme returns https when proxied" do
    request = stub_request 'rack.url_scheme' => 'http'
    assert !request.ssl?
    assert_equal 'http', request.scheme

    request = stub_request 'rack.url_scheme' => 'http', 'HTTP_X_FORWARDED_PROTO' => 'https'
    assert request.ssl?
    assert_equal 'https', request.scheme
  end

422
  test "String request methods" do
423
    [:get, :post, :patch, :put, :delete].each do |method|
424
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase
425 426 427 428 429
      assert_equal method.to_s.upcase, request.method
    end
  end

  test "Symbol forms of request methods via method_symbol" do
430
    [:get, :post, :patch, :put, :delete].each do |method|
431 432
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase
      assert_equal method, request.method_symbol
433 434 435
    end
  end

436
  test "invalid http method raises exception" do
437
    assert_raise(ActionController::UnknownHttpMethod) do
438 439
      request = stub_request 'REQUEST_METHOD' => 'RANDOM_METHOD'
      request.request_method
440 441 442
    end
  end

443
  test "allow method hacking on post" do
444
    %w(GET OPTIONS PATCH PUT POST DELETE).each do |method|
445
      request = stub_request "REQUEST_METHOD" => method.to_s.upcase
446
      assert_equal(method == "HEAD" ? "GET" : method, request.method)
447 448 449
    end
  end

450
  test "invalid method hacking on post raises exception" do
451 452 453
    assert_raise(ActionController::UnknownHttpMethod) do
      request = stub_request "REQUEST_METHOD" => "_RANDOM_METHOD"
      request.request_method
454 455 456
    end
  end

457
  test "restrict method hacking" do
458
    [:get, :patch, :put, :delete].each do |method|
459
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase,
460
        'action_dispatch.request.request_parameters' => { :_method => 'put' }
461
      assert_equal method.to_s.upcase, request.method
462 463
    end
  end
464

465 466 467 468 469 470 471
  test "post masquerading as patch" do
    request = stub_request 'REQUEST_METHOD' => 'PATCH', "rack.methodoverride.original_method" => "POST"
    assert_equal "POST", request.method
    assert_equal "PATCH",  request.request_method
    assert request.patch?
  end

472 473 474 475 476 477 478
  test "post masquerading as put" do
    request = stub_request 'REQUEST_METHOD' => 'PUT', "rack.methodoverride.original_method" => "POST"
    assert_equal "POST", request.method
    assert_equal "PUT",  request.request_method
    assert request.put?
  end

479
  test "xml format" do
480 481 482
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'xml' })
    assert_equal Mime::XML, request.format
483
  end
484

485
  test "xhtml format" do
486 487 488
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'xhtml' })
    assert_equal Mime::HTML, request.format
489
  end
490

491
  test "txt format" do
492 493 494
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'txt' })
    assert_equal Mime::TEXT, request.format
495
  end
496

497
  test "XMLHttpRequest" do
498 499 500 501 502 503
    request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest',
                           'HTTP_ACCEPT' =>
                             [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(",")
    request.expects(:parameters).at_least_once.returns({})
    assert request.xhr?
    assert_equal Mime::JS, request.format
504
  end
505

506
  test "content type" do
507
    request = stub_request 'CONTENT_TYPE' => 'text/html'
508
    assert_equal Mime::HTML, request.content_mime_type
509 510
  end

511
  test "can override format with parameter" do
512 513 514 515 516 517 518
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :txt })
    assert !request.format.xml?

    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :xml })
    assert request.format.xml?
519 520
  end

521
  test "no content type" do
522
    request = stub_request
523
    assert_equal nil, request.content_mime_type
524
  end
525

526
  test "content type is XML" do
527
    request = stub_request 'CONTENT_TYPE' => 'application/xml'
528
    assert_equal Mime::XML, request.content_mime_type
529 530
  end

531
  test "content type with charset" do
532
    request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
533
    assert_equal Mime::XML, request.content_mime_type
534
  end
535

536
  test "user agent" do
537 538
    request = stub_request 'HTTP_USER_AGENT' => 'TestAgent'
    assert_equal 'TestAgent', request.user_agent
539
  end
540

541
  test "parameters" do
542 543 544
    request = stub_request
    request.stubs(:request_parameters).returns({ "foo" => 1 })
    request.stubs(:query_parameters).returns({ "bar" => 2 })
545

546 547 548 549 550
    assert_equal({"foo" => 1, "bar" => 2}, request.parameters)
    assert_equal({"foo" => 1}, request.request_parameters)
    assert_equal({"bar" => 2}, request.query_parameters)
  end

551 552 553
  test "parameters still accessible after rack parse error" do
    mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" }
    request = nil
554 555 556
    request = stub_request(mock_rack_env)

    assert_raises(ActionController::BadRequest) do
557
      # rack will raise a TypeError when parsing this query string
558
      request.parameters
559
    end
560

561 562 563
    assert_equal({}, request.parameters)
  end

564 565 566 567 568 569 570 571 572 573 574 575 576 577
  test "we have access to the original exception" do
    mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" }
    request = nil
    request = stub_request(mock_rack_env)

    e = assert_raises(ActionController::BadRequest) do
      # rack will raise a TypeError when parsing this query string
      request.parameters
    end

    assert e.original_exception
    assert_equal e.original_exception.backtrace, e.backtrace
  end

578
  test "formats with accept header" do
579 580
    request = stub_request 'HTTP_ACCEPT' => 'text/html'
    request.expects(:parameters).at_least_once.returns({})
581
    assert_equal [Mime::HTML], request.formats
582

583 584 585
    request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
                           'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
    request.expects(:parameters).at_least_once.returns({})
586
    assert_equal [Mime::XML], request.formats
587 588 589

    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :txt })
590
    assert_equal [Mime::TEXT], request.formats
591 592 593

    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :unknown })
594
    assert request.formats.empty?
595 596
  end

597 598 599 600 601 602
  test "formats with xhr request" do
    request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
    request.expects(:parameters).at_least_once.returns({})
    assert_equal [Mime::JS], request.formats
  end

603 604 605 606 607 608 609 610
  test "ignore_accept_header" do
    ActionDispatch::Request.ignore_accept_header = true

    begin
      request = stub_request 'HTTP_ACCEPT' => 'application/xml'
      request.expects(:parameters).at_least_once.returns({})
      assert_equal [ Mime::HTML ], request.formats

J
José Valim 已提交
611 612 613 614 615 616 617 618 619 620 621 622
      request = stub_request 'HTTP_ACCEPT' => 'koz-asked/something-crazy'
      request.expects(:parameters).at_least_once.returns({})
      assert_equal [ Mime::HTML ], request.formats

      request = stub_request 'HTTP_ACCEPT' => '*/*;q=0.1'
      request.expects(:parameters).at_least_once.returns({})
      assert_equal [ Mime::HTML ], request.formats

      request = stub_request 'HTTP_ACCEPT' => 'application/jxw'
      request.expects(:parameters).at_least_once.returns({})
      assert_equal [ Mime::HTML ], request.formats

623 624 625 626 627 628 629 630 631 632 633 634 635 636
      request = stub_request 'HTTP_ACCEPT' => 'application/xml',
                             'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
      request.expects(:parameters).at_least_once.returns({})
      assert_equal [ Mime::JS ], request.formats

      request = stub_request 'HTTP_ACCEPT' => 'application/xml',
                             'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
      request.expects(:parameters).at_least_once.returns({:format => :json})
      assert_equal [ Mime::JSON ], request.formats
    ensure
      ActionDispatch::Request.ignore_accept_header = false
    end
  end

637
  test "negotiate_mime" do
638 639 640 641 642 643 644 645 646 647 648 649 650
    request = stub_request 'HTTP_ACCEPT' => 'text/html',
                           'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"

    request.expects(:parameters).at_least_once.returns({})

    assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON])
    assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML])
    assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL])

    request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
                           'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
    request.expects(:parameters).at_least_once.returns({})
    assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV])
651
  end
652

653 654 655 656 657 658 659
  test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do
    request = stub_request('rack.input' => StringIO.new("foo"),
                           'CONTENT_LENGTH' => 3)
    assert_equal "foo", request.raw_post
    assert_equal "foo", request.env['rack.input'].read
  end

660
  test "process parameter filter" do
661 662 663 664 665 666 667
    test_hashes = [
    [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
    [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'],
    [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'],
    [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
    [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
    [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'],
668
    [{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]]
669 670

    test_hashes.each do |before_filter, after_filter, filter_words|
671 672
      parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
      assert_equal after_filter, parameter_filter.filter(before_filter)
673

674 675
      filter_words << 'blah'
      filter_words << lambda { |key, value|
676
        value.reverse! if key =~ /bargain/
677
      }
678

679
      parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
680
      before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
681
      after_filter['barg']  = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
682

683
      assert_equal after_filter, parameter_filter.filter(before_filter)
684 685
    end
  end
686

687
  test "filtered_parameters returns params filtered" do
688 689 690
    request = stub_request('action_dispatch.request.parameters' =>
      { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' },
      'action_dispatch.parameter_filter' => [:lifo, :amount])
691 692 693 694 695 696 697 698

    params = request.filtered_parameters
    assert_equal "[FILTERED]", params["lifo"]
    assert_equal "[FILTERED]", params["amount"]
    assert_equal "1", params["step"]
  end

  test "filtered_env filters env as a whole" do
699 700 701
    request = stub_request('action_dispatch.request.parameters' =>
      { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada",
      'action_dispatch.parameter_filter' => [:lifo, :amount])
702

703
    request = stub_request(request.filtered_env)
704 705 706

    assert_equal "[FILTERED]", request.raw_post
    assert_equal "[FILTERED]", request.params["amount"]
707
    assert_equal "1", request.params["step"]
708 709
  end

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
  test "filtered_path returns path with filtered query string" do
    %w(; &).each do |sep|
      request = stub_request('QUERY_STRING' => %w(username=sikachu secret=bd4f21f api_key=b1bc3b3cd352f68d79d7).join(sep),
        'PATH_INFO' => '/authenticate',
        'action_dispatch.parameter_filter' => [:secret, :api_key])

      path = request.filtered_path
      assert_equal %w(/authenticate?username=sikachu secret=[FILTERED] api_key=[FILTERED]).join(sep), path
    end
  end

  test "filtered_path should not unescape a genuine '[FILTERED]' value" do
    request = stub_request('QUERY_STRING' => "secret=bd4f21f&genuine=%5BFILTERED%5D",
      'PATH_INFO' => '/authenticate',
      'action_dispatch.parameter_filter' => [:secret])

    path = request.filtered_path
    assert_equal "/authenticate?secret=[FILTERED]&genuine=%5BFILTERED%5D", path
  end

  test "filtered_path should preserve duplication of keys in query string" do
    request = stub_request('QUERY_STRING' => "username=sikachu&secret=bd4f21f&username=fxn",
      'PATH_INFO' => '/authenticate',
      'action_dispatch.parameter_filter' => [:secret])

    path = request.filtered_path
    assert_equal "/authenticate?username=sikachu&secret=[FILTERED]&username=fxn", path
  end

  test "filtered_path should ignore searchparts" do
    request = stub_request('QUERY_STRING' => "secret",
      'PATH_INFO' => '/authenticate',
      'action_dispatch.parameter_filter' => [:secret])

    path = request.filtered_path
    assert_equal "/authenticate?secret", path
  end

748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
  test "original_fullpath returns ORIGINAL_FULLPATH" do
    request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar")

    path = request.original_fullpath
    assert_equal "/foo?bar", path
  end

  test "original_url returns url built using ORIGINAL_FULLPATH" do
    request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar",
                           'HTTP_HOST'         => "example.org",
                           'rack.url_scheme'   => "http")

    url = request.original_url
    assert_equal "http://example.org/foo?bar", url
  end

  test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do
    request = stub_request('PATH_INFO'    => "/foo",
                           'QUERY_STRING' => "bar")

    path = request.original_fullpath
    assert_equal "/foo?bar", path
  end

772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
  test "if_none_match_etags none" do
    request = stub_request

    assert_equal nil, request.if_none_match
    assert_equal [], request.if_none_match_etags
    assert !request.etag_matches?("foo")
    assert !request.etag_matches?(nil)
  end

  test "if_none_match_etags single" do
    header = 'the-etag'
    request = stub_request('HTTP_IF_NONE_MATCH' => header)

    assert_equal header, request.if_none_match
    assert_equal [header], request.if_none_match_etags
    assert request.etag_matches?("the-etag")
  end

  test "if_none_match_etags quoted single" do
    header = '"the-etag"'
    request = stub_request('HTTP_IF_NONE_MATCH' => header)

    assert_equal header, request.if_none_match
    assert_equal ['the-etag'], request.if_none_match_etags
    assert request.etag_matches?("the-etag")
  end

  test "if_none_match_etags multiple" do
    header = 'etag1, etag2, "third etag", "etag4"'
    expected = ['etag1', 'etag2', 'third etag', 'etag4']
    request = stub_request('HTTP_IF_NONE_MATCH' => header)

    assert_equal header, request.if_none_match
    assert_equal expected, request.if_none_match_etags
    expected.each do |etag|
      assert request.etag_matches?(etag), etag
    end
  end

811 812
protected

813
  def stub_request(env = {})
C
Carlhuda 已提交
814
    ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true
E
Emilio Tagua 已提交
815
    @trusted_proxies ||= nil
C
Carlhuda 已提交
816
    ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies)
817
    tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1
C
Carlhuda 已提交
818
    ip_app.call(env)
819
    ActionDispatch::Http::URL.tld_length = tld_length
820
    ActionDispatch::Request.new(env)
821
  end
822
end