diff --git a/lib/open-uri.rb b/lib/open-uri.rb index 00e4c2d9b8..56dac75b29 100644 --- a/lib/open-uri.rb +++ b/lib/open-uri.rb @@ -108,6 +108,7 @@ module OpenURI :ftp_active_mode => false, :redirect => true, :encoding => nil, + :max_redirects => nil, } def OpenURI.check_options(options) # :nodoc: @@ -211,6 +212,7 @@ module OpenURI end uri_set = {} + max_redirects = options[:max_redirects] buf = nil while true redirect = catch(:open_uri_redirect) { @@ -238,6 +240,7 @@ module OpenURI uri = redirect raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s uri_set[uri.to_s] = true + raise "Too many redirects" if max_redirects && uri_set.size > max_redirects else break end diff --git a/test/open-uri/test_open-uri.rb b/test/open-uri/test_open-uri.rb index 86aefc52c8..6bad7f4bdf 100644 --- a/test/open-uri/test_open-uri.rb +++ b/test/open-uri/test_open-uri.rb @@ -558,6 +558,25 @@ class TestOpenURI < Test::Unit::TestCase } end + def test_max_redirects_success + with_http {|srv, dr, url| + srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } + srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } + srv.mount_proc("/r3/") {|req, res| res.body = "r3" } + URI.open("#{url}/r1/", max_redirects: 2) { |f| assert_equal("r3", f.read) } + } + end + + def test_max_redirects_too_many + with_http {|srv, dr, url| + srv.mount_proc("/r1/") {|req, res| res.status = 301; res["location"] = "#{url}/r2"; res.body = "r1" } + srv.mount_proc("/r2/") {|req, res| res.status = 301; res["location"] = "#{url}/r3"; res.body = "r2" } + srv.mount_proc("/r3/") {|req, res| res.body = "r3" } + exc = assert_raise(RuntimeError) { URI.open("#{url}/r1/", max_redirects: 1) {} } + assert_equal("Too many redirects", exc.message) + } + end + def test_userinfo assert_raise(ArgumentError) { URI.open("http://user:pass@127.0.0.1/") {} } end