diff options
author | José Valim <jose.valim@dashbit.co> | 2021-12-06 14:56:27 +0100 |
---|---|---|
committer | José Valim <jose.valim@dashbit.co> | 2021-12-06 14:58:01 +0100 |
commit | aa41c880863f1852149626c9103b901e3a6db5e4 (patch) | |
tree | 9f95a9aa43bd87a2baefc742ba226478385d017f | |
parent | 88bad1a3b0f16ede148242ffbab707f0eaddb3ae (diff) | |
download | elixir-aa41c880863f1852149626c9103b901e3a6db5e4.tar.gz |
Do not deprecate URI.parse/1
Closes #11450.
-rw-r--r-- | lib/elixir/lib/uri.ex | 129 | ||||
-rw-r--r-- | lib/elixir/test/elixir/uri_test.exs | 8 |
2 files changed, 108 insertions, 29 deletions
diff --git a/lib/elixir/lib/uri.ex b/lib/elixir/lib/uri.ex index 382fa3793..1535e0086 100644 --- a/lib/elixir/lib/uri.ex +++ b/lib/elixir/lib/uri.ex @@ -12,7 +12,7 @@ defmodule URI do """ defstruct scheme: nil, - path: "", + path: nil, query: nil, fragment: nil, authority: nil, @@ -20,7 +20,6 @@ defmodule URI do host: nil, port: nil - # TODO: Remove nil from path when we fully deprecate URI.parse on Elixir v1.17 @type t :: %__MODULE__{ authority: authority, fragment: nil | binary, @@ -480,12 +479,13 @@ defmodule URI do Creates a new URI struct from a URI or a string. If a `%URI{}` struct is given, it returns `{:ok, uri}`. If a string is - given, it will parse it and returns `{:ok, uri}`. If the string is - invalid, it returns `{:error, part}` instead, with the invalid part of the URI. + given, it will parse and validate it. If the string is valid, it returns + `{:ok, uri}`, otherwise it returns `{:error, part}` with the invalid part + of the URI. For parsing URIs without further validation, see `parse/1`. This function can parse both absolute and relative URLs. You can check if a URI is absolute or relative by checking if the `scheme` field is - `nil` or not. All fields may be `nil`, except for the `path`. + `nil` or not. When a URI is given without a port, the value returned by `URI.default_port/1` for the URI's scheme is used for the `:port` field. The scheme is also @@ -552,7 +552,7 @@ defmodule URI do {:ok, %URI{ fragment: nil, host: nil, - path: "", + path: nil, port: 443, query: "query", scheme: "https", @@ -634,6 +634,8 @@ defmodule URI do end end + defp uri_from_map(%{path: ""} = map), do: uri_from_map(%{map | path: nil}) + defp uri_from_map(map) do uri = Map.merge(%URI{}, map) @@ -658,29 +660,106 @@ defmodule URI do end @doc """ - Parses a well-formed URI into its components. + Parses a URI into its components, without further validation. - This function is deprecated as it fails to raise in case of invalid URIs. - Use `URI.new!/1` or `URI.new/1` instead. In case you want to mimic the - behaviour of this function, you can do: + This function can parse both absolute and relative URLs. You can check + if a URI is absolute or relative by checking if the `scheme` field is + nil or not. Furthermore, this function expects both absolute and + relative URIs to be well-formed and does not perform any validation. + See the "Examples" section below. Use `new/1` if you want more strict + validation. - case URI.new(path) do - {:ok, uri} -> uri - {:error, _} -> %URI{path: path} - end + When a URI is given without a port, the value returned by `URI.default_port/1` + for the URI's scheme is used for the `:port` field. The scheme is also + normalized to lowercase. + + If a `%URI{}` struct is given to this function, this function returns it + unmodified. + + > Note: this function sets the field :authority for backwards + > compatibility reasons but it is deprecated. + + ## Examples + + iex> URI.parse("https://elixir-lang.org/") + %URI{ + authority: "elixir-lang.org", + fragment: nil, + host: "elixir-lang.org", + path: "/", + port: 443, + query: nil, + scheme: "https", + userinfo: nil + } + + iex> URI.parse("//elixir-lang.org/") + %URI{ + authority: "elixir-lang.org", + fragment: nil, + host: "elixir-lang.org", + path: "/", + port: nil, + query: nil, + scheme: nil, + userinfo: nil + } + + iex> URI.parse("/foo/bar") + %URI{ + authority: nil, + fragment: nil, + host: nil, + path: "/foo/bar", + port: nil, + query: nil, + scheme: nil, + userinfo: nil + } + + iex> URI.parse("foo/bar") + %URI{ + authority: nil, + fragment: nil, + host: nil, + path: "foo/bar", + port: nil, + query: nil, + scheme: nil, + userinfo: nil + } - There are two differences in the behaviour of this function compared to - `URI.new/1`: + In contrast to `URI.new/1`, this function will parse poorly-formed + URIs, for example: - * This function sets the deprecated authority field + iex> URI.parse("/invalid_greater_than_in_path/>") + %URI{ + authority: nil, + fragment: nil, + host: nil, + path: "/invalid_greater_than_in_path/>", + port: nil, + query: nil, + scheme: nil, + userinfo: nil + } + + Another example is a URI with brackets in query strings. It is accepted + by `parse/1` but it will be refused by `new/1`: - * This function sets the path to `nil` when it is empty, - while `new/1` consider the path always exists and sets it - to an empty string + iex> URI.parse("/?foo[bar]=baz") + %URI{ + authority: nil, + fragment: nil, + host: nil, + path: "/", + port: nil, + query: "foo[bar]=baz", + scheme: nil, + userinfo: nil + } """ - # TODO: Deprecate me on Elixir v1.17 - @doc deprecated: "Use URI.new/1 or URI.new!/1 instead" @spec parse(t | binary) :: t def parse(%URI{} = uri), do: uri @@ -813,6 +892,7 @@ defmodule URI do %{rel | scheme: base.scheme, path: remove_dot_segments_from_path(rel.path)} end + # TODO: Check only for nils in future versions def merge(%URI{} = base, %URI{path: rel_path} = rel) when rel_path in ["", nil] do %{base | query: rel.query || base.query, fragment: rel.fragment} end @@ -826,9 +906,7 @@ defmodule URI do merge(parse(base), parse(rel)) end - # TODO: Deprecate me on Elixir v1.19 defp merge_paths(nil, rel_path), do: merge_paths("/", rel_path) - defp merge_paths("", rel_path), do: merge_paths("/", rel_path) defp merge_paths(_, "/" <> _ = rel_path), do: remove_dot_segments_from_path(rel_path) defp merge_paths(base_path, rel_path) do @@ -868,7 +946,8 @@ end defimpl String.Chars, for: URI do def to_string(%{host: host, path: path} = uri) - when host != nil and path != "" and binary_part(path, 0, 1) != "/" do + when host != nil and is_binary(path) and + path != "" and binary_part(path, 0, 1) != "/" do raise ArgumentError, ":path in URI must be empty or an absolute path if URL has a :host, got: #{inspect(uri)}" end diff --git a/lib/elixir/test/elixir/uri_test.exs b/lib/elixir/test/elixir/uri_test.exs index 1d9a1f524..9b4f3eabe 100644 --- a/lib/elixir/test/elixir/uri_test.exs +++ b/lib/elixir/test/elixir/uri_test.exs @@ -94,7 +94,7 @@ defmodule URITest do describe "new/1" do test "empty" do - assert URI.new("") == {:ok, %URI{path: ""}} + assert URI.new("") == {:ok, %URI{}} end test "errors on bad URIs" do @@ -131,7 +131,7 @@ defmodule URITest do query: nil, fragment: nil, port: 443, - path: "", + path: nil, userinfo: nil } @@ -349,9 +349,9 @@ defmodule URITest do assert URI.merge(base, "") |> to_string == "http://example.com/foo/bar" assert URI.merge(base, "#fragment") |> to_string == "http://example.com/foo/bar#fragment" assert URI.merge(base, "?query") |> to_string == "http://example.com/foo/bar?query" - assert URI.merge(base, %URI{path: ""}) |> to_string == "http://example.com/foo/bar" + assert URI.merge(base, %URI{}) |> to_string == "http://example.com/foo/bar" - assert URI.merge(base, %URI{path: "", fragment: "fragment"}) + assert URI.merge(base, %URI{fragment: "fragment"}) |> to_string == "http://example.com/foo/bar#fragment" base = URI.new!("http://example.com") |