Skip to content

Commit

Permalink
Improve URI#normalize (#7635)
Browse files Browse the repository at this point in the history
Adds normalization for scheme, host and path.
  • Loading branch information
straight-shoota authored Apr 20, 2019
1 parent 87c554b commit 0a3070b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 33 deletions.
66 changes: 41 additions & 25 deletions spec/std/uri_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -159,31 +159,19 @@ describe "URI" do
it { URI.parse("/foo").relative?.should be_true }
end

describe "normalize" do
it "removes dot notation from path" do
cases = {
"../bar" => "bar",
"./bar" => "bar",
".././bar" => "bar",
"/foo/./bar" => "/foo/bar",
"/bar/./" => "/bar/",
"/." => "/",
"/bar/." => "/bar/",
"/foo/../bar" => "/bar",
"/bar/../" => "/",
"/.." => "/",
"/bar/.." => "/",
"/foo/bar/.." => "/foo/",
"." => "",
".." => "",
}

cases.each do |input, expected|
uri = URI.parse(input)
uri = uri.normalize

uri.path.should eq(expected), "failed to remove dot notation from #{input}"
end
describe "#normalize" do
it "doesn't modify instance" do
uri = URI.parse("HTTP://example.COM:80/./foo/../bar/")
uri.normalize.should eq URI.parse("http://example.com/bar/")
uri.should eq URI.parse("HTTP://example.COM:80/./foo/../bar/")
end

it "normalizes scheme" do
URI.parse("HtTp://").normalize.should eq URI.parse("http://")
end

it "normalizes host" do
URI.parse("http://FoO.cOm/").normalize.should eq URI.parse("http://foo.com/")
end

it "removes default port" do
Expand All @@ -194,6 +182,34 @@ describe "URI" do
URI.new("ldap", "www.example.com", 389).normalize.to_s.should eq("ldap://www.example.com")
URI.new("ldaps", "www.example.com", 636).normalize.to_s.should eq("ldaps://www.example.com")
end

it "removes dot notation from path" do
URI.new(path: "../bar").normalize.path.should eq "bar"
URI.new(path: "./bar").normalize.path.should eq "bar"
URI.new(path: ".././bar").normalize.path.should eq "bar"
URI.new(path: "/foo/./bar").normalize.path.should eq "/foo/bar"
URI.new(path: "/bar/./").normalize.path.should eq "/bar/"
URI.new(path: "/.").normalize.path.should eq "/"
URI.new(path: "/bar/.").normalize.path.should eq "/bar/"
URI.new(path: "/foo/../bar").normalize.path.should eq "/bar"
URI.new(path: "/bar/../").normalize.path.should eq "/"
URI.new(path: "/..").normalize.path.should eq "/"
URI.new(path: "/bar/..").normalize.path.should eq "/"
URI.new(path: "/foo/bar/..").normalize.path.should eq "/foo/"
URI.new(path: ".").normalize.path.should eq ""
URI.new(path: "..").normalize.path.should eq ""
end

it "prefixes relative path with colon with `./`" do
URI.parse("./a:b").normalize.should eq URI.parse("./a:b")
URI.parse("http:a:b").normalize.should eq URI.parse("http:./a:b")
end
end

it "#normalize!" do
uri = URI.parse("HTTP://example.COM:80/./foo/../bar/")
uri.normalize!
uri.should eq URI.parse("http://example.com/bar/")
end

it "implements ==" do
Expand Down
38 changes: 30 additions & 8 deletions src/uri.cr
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,35 @@ class URI
end
end

# Returns normalized URI.
def normalize : self
uri = dup
uri.normalize!
uri
# Returns a normalized copy of this URI.
#
# See `#normalize!` for details.
def normalize : URI
dup.normalize!
end

# Destructive normalize.
def normalize! : Nil
@path = remove_dot_segments(path)
# Normalizes this URI instance.
#
# The following normalizations are applied to the individual components (if available):
#
# * `scheme` is lowercased.
# * `host` is lowercased.
# * `port` is removed if it is the `.default_port?` of the scheme.
# * `path` is resolved to a minimal, semantical equivalent representation removing
# dot segments `/.` and `/..`.
#
# ```
# uri = URI.parse("HTTP://example.COM:80/./foo/../bar/")
# uri.normalize!
# uri # => http://example.com/bar/
# ```
def normalize! : URI
@scheme = @scheme.try &.downcase
@host = @host.try &.downcase
@port = nil if default_port?
@path = remove_dot_segments(path)

self
end

# Parses `raw_url` into an URI. The `raw_url` may be relative or absolute.
Expand Down Expand Up @@ -468,6 +486,10 @@ class URI
path = path[segment_end_idx..-1]
end
end
first = result.first?
if first && !first.starts_with?('/') && first.includes?(':')
result.unshift "./"
end

result.join
end
Expand Down

0 comments on commit 0a3070b

Please sign in to comment.