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

3
class RequestTest < ActiveSupport::TestCase
4
  test "remote ip" do
5 6
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4'
    assert_equal '1.2.3.4', request.remote_ip
7

8 9
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
    assert_equal '1.2.3.4', request.remote_ip
10

11 12 13
    request = stub_request 'REMOTE_ADDR' => '1.2.3.4',
      'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
    assert_equal '1.2.3.4', request.remote_ip
14

15 16 17
    request = stub_request 'REMOTE_ADDR' => '127.0.0.1',
      'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
J
Jeremy Kemper 已提交
18

19 20
    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
21

22 23
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.16.0.1,3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
J
Jeremy Kemper 已提交
24

25 26
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
27

28 29
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
30

31 32
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
33

34 35
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '127.0.0.1,3.4.5.6'
    assert_equal '3.4.5.6', request.remote_ip
36

37 38
    request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
    assert_equal 'unknown', request.remote_ip
39

40 41
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
    assert_equal '3.4.5.6', request.remote_ip
42

43 44
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
                           'HTTP_CLIENT_IP'       => '2.2.2.2'
C
Carlhuda 已提交
45
    e = assert_raise(ActionDispatch::RemoteIp::IpSpoofAttackError) {
46
      request.remote_ip
J
Jeremy Kemper 已提交
47
    }
48 49 50
    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 已提交
51

52 53 54 55 56
    # 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.
57
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1',
C
Carlhuda 已提交
58 59
                           'HTTP_CLIENT_IP'       => '2.2.2.2',
                           :ip_spoofing_check => false
60
    assert_equal '2.2.2.2', request.remote_ip
61

62 63
    request = stub_request 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
    assert_equal '9.9.9.9', request.remote_ip
64 65
  end

66
  test "remote ip with user specified trusted proxies" do
C
Carlhuda 已提交
67
    @trusted_proxies = /^67\.205\.106\.73$/i
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

    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

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

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

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

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

    request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 67.205.106.73'
    assert_equal '3.4.5.6', request.remote_ip
  end

92
  test "domains" do
93 94
    request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org'
    assert_equal "rubyonrails.org", request.domain
95

96 97
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk"
    assert_equal "rubyonrails.co.uk", request.domain(2)
98

99 100
    request = stub_request 'HTTP_HOST' => "192.168.1.200"
    assert_nil request.domain
101

102 103
    request = stub_request 'HTTP_HOST' => "foo.192.168.1.200"
    assert_nil request.domain
104

105 106
    request = stub_request 'HTTP_HOST' => "192.168.1.200.com"
    assert_equal "200.com", request.domain
107 108
  end

109
  test "subdomains" do
110 111
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.org"
    assert_equal %w( www ), request.subdomains
112

113 114
    request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk"
    assert_equal %w( www ), request.subdomains(2)
115

116 117
    request = stub_request 'HTTP_HOST' => "dev.www.rubyonrails.co.uk"
    assert_equal %w( dev www ), request.subdomains(2)
118

119 120 121
    request = stub_request 'HTTP_HOST' => "dev.www.rubyonrails.co.uk", :tld_length => 2
    assert_equal %w( dev www ), request.subdomains

122 123
    request = stub_request 'HTTP_HOST' => "foobar.foobar.com"
    assert_equal %w( foobar ), request.subdomains
124

125 126
    request = stub_request 'HTTP_HOST' => "192.168.1.200"
    assert_equal [], request.subdomains
127

128 129
    request = stub_request 'HTTP_HOST' => "foo.192.168.1.200"
    assert_equal [], request.subdomains
130

131 132
    request = stub_request 'HTTP_HOST' => "192.168.1.200.com"
    assert_equal %w( 192 168 1 ), request.subdomains
133

134 135
    request = stub_request 'HTTP_HOST' => nil
    assert_equal [], request.subdomains
136
  end
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  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

164
  test "port string" do
165 166
    request = stub_request 'HTTP_HOST' => 'www.example.org:80'
    assert_equal "", request.port_string
167

168 169
    request = stub_request 'HTTP_HOST' => 'www.example.org:8080'
    assert_equal ":8080", request.port_string
170
  end
171

172
  test "full path" do
173
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1'
174 175
    assert_equal "/path/of/some/uri?mapped=1", request.fullpath
    assert_equal "/path/of/some/uri",          request.path_info
176

177
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri'
178 179
    assert_equal "/path/of/some/uri", request.fullpath
    assert_equal "/path/of/some/uri", request.path_info
180

181
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/'
182 183
    assert_equal "/", request.fullpath
    assert_equal "/", request.path_info
184

185
    request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/', 'QUERY_STRING' => 'm=b'
186 187
    assert_equal "/?m=b", request.fullpath
    assert_equal "/",     request.path_info
188

189
    request = stub_request 'SCRIPT_NAME' => '/hieraki', 'PATH_INFO' => '/'
190 191
    assert_equal "/hieraki/", request.fullpath
    assert_equal "/",         request.path_info
192

193
    request = stub_request 'SCRIPT_NAME' => '/collaboration/hieraki', 'PATH_INFO' => '/books/edit/2'
194 195
    assert_equal "/collaboration/hieraki/books/edit/2", request.fullpath
    assert_equal "/books/edit/2",                       request.path_info
196

197
    request = stub_request 'SCRIPT_NAME' => '/path', 'PATH_INFO' => '/of/some/uri', 'QUERY_STRING' => 'mapped=1'
198 199
    assert_equal "/path/of/some/uri?mapped=1", request.fullpath
    assert_equal "/of/some/uri",               request.path_info
200
  end
201

202

203
  test "host with default port" do
204 205
    request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80'
    assert_equal "rubyonrails.org", request.host_with_port
206
  end
207

208
  test "host with non default port" do
209 210
    request = stub_request 'HTTP_HOST' => 'rubyonrails.org:81'
    assert_equal "rubyonrails.org:81", request.host_with_port
211
  end
212

213
  test "server software" do
214 215
    request = stub_request
    assert_equal nil, request.server_software
216

217 218
    request = stub_request 'SERVER_SOFTWARE' => 'Apache3.422'
    assert_equal 'apache', request.server_software
219

220 221
    request = stub_request 'SERVER_SOFTWARE' => 'lighttpd(1.1.4)'
    assert_equal 'lighttpd', request.server_software
222
  end
223

224
  test "xml http request" do
225 226 227 228
    request = stub_request

    assert !request.xml_http_request?
    assert !request.xhr?
229

230 231 232
    request = stub_request 'HTTP_X_REQUESTED_WITH' => 'DefinitelyNotAjax1.0'
    assert !request.xml_http_request?
    assert !request.xhr?
233

234 235 236
    request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
    assert request.xml_http_request?
    assert request.xhr?
237
  end
238

239
  test "reports ssl" do
240 241 242 243 244
    request = stub_request
    assert !request.ssl?

    request = stub_request 'HTTPS' => 'on'
    assert request.ssl?
245 246
  end

247
  test "reports ssl when proxied via lighttpd" do
248 249 250 251 252
    request = stub_request
    assert !request.ssl?

    request = stub_request 'HTTP_X_FORWARDED_PROTO' => 'https'
    assert request.ssl?
253
  end
254

255 256 257 258 259 260 261 262 263 264
  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

265
  test "String request methods" do
266
    [:get, :post, :put, :delete].each do |method|
267
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase
268 269 270 271 272 273 274 275
      assert_equal method.to_s.upcase, request.method
    end
  end

  test "Symbol forms of request methods via method_symbol" do
    [:get, :post, :put, :delete].each do |method|
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase
      assert_equal method, request.method_symbol
276 277 278
    end
  end

279
  test "invalid http method raises exception" do
280
    assert_raise(ActionController::UnknownHttpMethod) do
281 282
      request = stub_request 'REQUEST_METHOD' => 'RANDOM_METHOD'
      request.request_method
283 284 285
    end
  end

286
  test "allow method hacking on post" do
287
    %w(GET OPTIONS PUT POST DELETE).each do |method|
288
      request = stub_request "REQUEST_METHOD" => method.to_s.upcase
289
      assert_equal(method == "HEAD" ? "GET" : method, request.method)
290 291 292
    end
  end

293
  test "invalid method hacking on post raises exception" do
294 295 296
    assert_raise(ActionController::UnknownHttpMethod) do
      request = stub_request "REQUEST_METHOD" => "_RANDOM_METHOD"
      request.request_method
297 298 299
    end
  end

300
  test "restrict method hacking" do
301
    [:get, :put, :delete].each do |method|
302
      request = stub_request 'REQUEST_METHOD' => method.to_s.upcase,
303
        'action_dispatch.request.request_parameters' => { :_method => 'put' }
304
      assert_equal method.to_s.upcase, request.method
305 306
    end
  end
307

308
  test "head masquerading as get" do
309
    request = stub_request 'REQUEST_METHOD' => 'GET', "rack.methodoverride.original_method" => "HEAD"
310 311
    assert_equal "HEAD", request.method
    assert_equal "GET",  request.request_method
312 313
    assert request.get?
    assert request.head?
314
  end
315

316
  test "xml format" do
317 318 319
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'xml' })
    assert_equal Mime::XML, request.format
320
  end
321

322
  test "xhtml format" do
323 324 325
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'xhtml' })
    assert_equal Mime::HTML, request.format
326
  end
327

328
  test "txt format" do
329 330 331
    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => 'txt' })
    assert_equal Mime::TEXT, request.format
332
  end
333

334
  test "XMLHttpRequest" do
335 336 337 338 339 340
    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
341
  end
342

343
  test "content type" do
344
    request = stub_request 'CONTENT_TYPE' => 'text/html'
345
    assert_equal Mime::HTML, request.content_mime_type
346 347
  end

348
  test "can override format with parameter" do
349 350 351 352 353 354 355
    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?
356 357
  end

358
  test "no content type" do
359
    request = stub_request
360
    assert_equal nil, request.content_mime_type
361
  end
362

363
  test "content type is XML" do
364
    request = stub_request 'CONTENT_TYPE' => 'application/xml'
365
    assert_equal Mime::XML, request.content_mime_type
366 367
  end

368
  test "content type with charset" do
369
    request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8'
370
    assert_equal Mime::XML, request.content_mime_type
371
  end
372

373
  test "user agent" do
374 375
    request = stub_request 'HTTP_USER_AGENT' => 'TestAgent'
    assert_equal 'TestAgent', request.user_agent
376
  end
377

378
  test "parameters" do
379 380 381
    request = stub_request
    request.stubs(:request_parameters).returns({ "foo" => 1 })
    request.stubs(:query_parameters).returns({ "bar" => 2 })
382

383 384 385 386 387
    assert_equal({"foo" => 1, "bar" => 2}, request.parameters)
    assert_equal({"foo" => 1}, request.request_parameters)
    assert_equal({"bar" => 2}, request.query_parameters)
  end

388 389 390 391 392 393 394 395 396 397 398 399
  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
    begin
      request = stub_request(mock_rack_env) 
      request.parameters
    rescue TypeError => e
      # rack will raise a TypeError when parsing this query string
    end
    assert_equal({}, request.parameters)
  end

400
  test "formats with accept header" do
401 402 403
    request = stub_request 'HTTP_ACCEPT' => 'text/html'
    request.expects(:parameters).at_least_once.returns({})
    assert_equal [ Mime::HTML ], request.formats
404

405 406 407 408 409 410 411 412
    request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
                           'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
    request.expects(:parameters).at_least_once.returns({})
    assert_equal with_set(Mime::XML), request.formats

    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :txt })
    assert_equal with_set(Mime::TEXT), request.formats
413 414 415 416

    request = stub_request
    request.expects(:parameters).at_least_once.returns({ :format => :unknown })
    assert request.formats.empty?
417 418 419
  end

  test "negotiate_mime" do
420 421 422 423 424 425 426 427 428 429 430 431 432
    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])
433
  end
434 435

  test "process parameter filter" do
436 437 438 439 440 441 442
    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'],
443
    [{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]]
444 445

    test_hashes.each do |before_filter, after_filter, filter_words|
446 447
      parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
      assert_equal after_filter, parameter_filter.filter(before_filter)
448

449 450
      filter_words << 'blah'
      filter_words << lambda { |key, value|
451
        value.reverse! if key =~ /bargain/
452
      }
453

454
      parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
455
      before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
456
      after_filter['barg']  = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
457

458
      assert_equal after_filter, parameter_filter.filter(before_filter)
459 460
    end
  end
461

462
  test "filtered_parameters returns params filtered" do
463 464 465
    request = stub_request('action_dispatch.request.parameters' =>
      { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' },
      'action_dispatch.parameter_filter' => [:lifo, :amount])
466 467 468 469 470 471 472 473

    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
474 475 476
    request = stub_request('action_dispatch.request.parameters' =>
      { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada",
      'action_dispatch.parameter_filter' => [:lifo, :amount])
477

478
    request = stub_request(request.filtered_env)
479 480 481

    assert_equal "[FILTERED]", request.raw_post
    assert_equal "[FILTERED]", request.params["amount"]
482
    assert_equal "1", request.params["step"]
483 484
  end

485 486
protected

487
  def stub_request(env = {})
C
Carlhuda 已提交
488
    ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true
E
Emilio Tagua 已提交
489
    @trusted_proxies ||= nil
C
Carlhuda 已提交
490
    ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies)
491
    tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1
C
Carlhuda 已提交
492
    ip_app.call(env)
493
    ActionDispatch::Http::URL.tld_length = tld_length
494
    ActionDispatch::Request.new(env)
495
  end
496

497
  def with_set(*args)
498
    args
499
  end
500
end