diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb index 634da49..18ca589 100644 --- a/lib/uri/generic.rb +++ b/lib/uri/generic.rb @@ -1108,10 +1108,13 @@ def merge!(oth) # # +oth+:: # URI or String + # +allow_relative+:: + # Boolean (false by default) # # == Description # - # Merges two URIs. + # Merges two URIs. If +allow_relative+ is true, allows merging + # a relative base URI with a relative URI. # # == Usage # @@ -1121,7 +1124,7 @@ def merge!(oth) # uri.merge("/main.rbx?page=1") # # => "http://my.example.com/main.rbx?page=1" # - def merge(oth) + def merge(oth, allow_relative = false) rel = parser.__send__(:convert_to_uri, oth) if rel.absolute? @@ -1130,7 +1133,7 @@ def merge(oth) return rel end - unless self.absolute? + unless self.absolute? || allow_relative raise BadURIError, "both URI are relative" end diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb index 94eea71..5fd7c5b 100644 --- a/test/uri/test_generic.rb +++ b/test/uri/test_generic.rb @@ -288,6 +288,26 @@ def test_merge_authority assert_equal(u0, u1) end + def test_merge_allow_relative + # When both URIs are relative, merge should raise unless allow_relative is true: + base = URI.parse('a/b/c') + assert_raise(URI::BadURIError) do + base.merge('d/e') + end + + # With allow_relative=true, relative base + relative ref should merge paths: + merged = base.merge('d/e', true) + assert_equal('a/b/d/e', merged.to_s) + + # Parent directory traversals should be handled as with absolute bases: + merged_up = base.merge('../x', true) + assert_equal('a/x', merged_up.to_s) + + # Leading slash should reset to root-like behavior within relative context: + merged_abs = base.merge('/z', true) + assert_equal('/z', merged_abs.to_s) + end + def test_route url = URI.parse('http://hoge/a.html').route_to('http://hoge/b.html') assert_equal('b.html', url.to_s)