@@ -12,15 +12,14 @@ defmodule URI do
1212 """
1313
1414 defstruct scheme: nil ,
15- path: "" ,
15+ path: nil ,
1616 query: nil ,
1717 fragment: nil ,
1818 authority: nil ,
1919 userinfo: nil ,
2020 host: nil ,
2121 port: nil
2222
23- # TODO: Remove nil from path when we fully deprecate URI.parse on Elixir v1.17
2423 @ type t :: % __MODULE__ {
2524 authority: authority ,
2625 fragment: nil | binary ,
@@ -480,12 +479,13 @@ defmodule URI do
480479 Creates a new URI struct from a URI or a string.
481480
482481 If a `%URI{}` struct is given, it returns `{:ok, uri}`. If a string is
483- given, it will parse it and returns `{:ok, uri}`. If the string is
484- invalid, it returns `{:error, part}` instead, with the invalid part of the URI.
482+ given, it will parse and validate it. If the string is valid, it returns
483+ `{:ok, uri}`, otherwise it returns `{:error, part}` with the invalid part
484+ of the URI. For parsing URIs without further validation, see `parse/1`.
485485
486486 This function can parse both absolute and relative URLs. You can check
487487 if a URI is absolute or relative by checking if the `scheme` field is
488- `nil` or not. All fields may be `nil`, except for the `path`.
488+ `nil` or not.
489489
490490 When a URI is given without a port, the value returned by `URI.default_port/1`
491491 for the URI's scheme is used for the `:port` field. The scheme is also
@@ -552,7 +552,7 @@ defmodule URI do
552552 {:ok, %URI{
553553 fragment: nil,
554554 host: nil,
555- path: "" ,
555+ path: nil ,
556556 port: 443,
557557 query: "query",
558558 scheme: "https",
@@ -634,6 +634,8 @@ defmodule URI do
634634 end
635635 end
636636
637+ defp uri_from_map ( % { path: "" } = map ) , do: uri_from_map ( % { map | path: nil } )
638+
637639 defp uri_from_map ( map ) do
638640 uri = Map . merge ( % URI { } , map )
639641
@@ -658,29 +660,106 @@ defmodule URI do
658660 end
659661
660662 @ doc """
661- Parses a well-formed URI into its components.
663+ Parses a URI into its components, without further validation .
662664
663- This function is deprecated as it fails to raise in case of invalid URIs.
664- Use `URI.new!/1` or `URI.new/1` instead. In case you want to mimic the
665- behaviour of this function, you can do:
665+ This function can parse both absolute and relative URLs. You can check
666+ if a URI is absolute or relative by checking if the `scheme` field is
667+ nil or not. Furthermore, this function expects both absolute and
668+ relative URIs to be well-formed and does not perform any validation.
669+ See the "Examples" section below. Use `new/1` if you want more strict
670+ validation.
666671
667- case URI.new(path) do
668- {:ok, uri} -> uri
669- {:error, _} -> %URI{path: path}
670- end
672+ When a URI is given without a port, the value returned by `URI.default_port/1`
673+ for the URI's scheme is used for the `:port` field. The scheme is also
674+ normalized to lowercase.
675+
676+ If a `%URI{}` struct is given to this function, this function returns it
677+ unmodified.
678+
679+ > Note: this function sets the field :authority for backwards
680+ > compatibility reasons but it is deprecated.
681+
682+ ## Examples
683+
684+ iex> URI.parse("https://elixir-lang.org/")
685+ %URI{
686+ authority: "elixir-lang.org",
687+ fragment: nil,
688+ host: "elixir-lang.org",
689+ path: "/",
690+ port: 443,
691+ query: nil,
692+ scheme: "https",
693+ userinfo: nil
694+ }
695+
696+ iex> URI.parse("//elixir-lang.org/")
697+ %URI{
698+ authority: "elixir-lang.org",
699+ fragment: nil,
700+ host: "elixir-lang.org",
701+ path: "/",
702+ port: nil,
703+ query: nil,
704+ scheme: nil,
705+ userinfo: nil
706+ }
707+
708+ iex> URI.parse("/foo/bar")
709+ %URI{
710+ authority: nil,
711+ fragment: nil,
712+ host: nil,
713+ path: "/foo/bar",
714+ port: nil,
715+ query: nil,
716+ scheme: nil,
717+ userinfo: nil
718+ }
719+
720+ iex> URI.parse("foo/bar")
721+ %URI{
722+ authority: nil,
723+ fragment: nil,
724+ host: nil,
725+ path: "foo/bar",
726+ port: nil,
727+ query: nil,
728+ scheme: nil,
729+ userinfo: nil
730+ }
671731
672- There are two differencws in the behaviour of this function compared to
673- `URI.new/1` :
732+ In contrast to `URI.new/1`, this function will parse poorly-formed
733+ URIs, for example :
674734
675- * This function sets the deprecated authority field
735+ iex> URI.parse("/invalid_greater_than_in_path/>")
736+ %URI{
737+ authority: nil,
738+ fragment: nil,
739+ host: nil,
740+ path: "/invalid_greater_than_in_path/>",
741+ port: nil,
742+ query: nil,
743+ scheme: nil,
744+ userinfo: nil
745+ }
746+
747+ Another example is a URI with brackets in query strings. It is accepted
748+ by `parse/1` but it will be refused by `new/1`:
676749
677- * This function sets the path to `nil` when it is empty,
678- while `new/1` consider the path always exists and sets it
679- to an empty string
750+ iex> URI.parse("/?foo[bar]=baz")
751+ %URI{
752+ authority: nil,
753+ fragment: nil,
754+ host: nil,
755+ path: "/",
756+ port: nil,
757+ query: "foo[bar]=baz",
758+ scheme: nil,
759+ userinfo: nil
760+ }
680761
681762 """
682- # TODO: Deprecate me at least on v1.17
683- @ doc deprecated: "Use URI.new/1 or URI.new!/1 instead"
684763 @ spec parse ( t | binary ) :: t
685764 def parse ( % URI { } = uri ) , do: uri
686765
@@ -813,6 +892,7 @@ defmodule URI do
813892 % { rel | scheme: base . scheme , path: remove_dot_segments_from_path ( rel . path ) }
814893 end
815894
895+ # TODO: Check only for nils in future versions
816896 def merge ( % URI { } = base , % URI { path: rel_path } = rel ) when rel_path in [ "" , nil ] do
817897 % { base | query: rel . query || base . query , fragment: rel . fragment }
818898 end
@@ -826,9 +906,7 @@ defmodule URI do
826906 merge ( parse ( base ) , parse ( rel ) )
827907 end
828908
829- # TODO: Deprecate me on Elixir v1.19
830909 defp merge_paths ( nil , rel_path ) , do: merge_paths ( "/" , rel_path )
831- defp merge_paths ( "" , rel_path ) , do: merge_paths ( "/" , rel_path )
832910 defp merge_paths ( _ , "/" <> _ = rel_path ) , do: remove_dot_segments_from_path ( rel_path )
833911
834912 defp merge_paths ( base_path , rel_path ) do
868946
869947defimpl String.Chars , for: URI do
870948 def to_string ( % { host: host , path: path } = uri )
871- when host != nil and path != "" and binary_part ( path , 0 , 1 ) != "/" do
949+ when host != nil and is_binary ( path ) and
950+ path != "" and binary_part ( path , 0 , 1 ) != "/" do
872951 raise ArgumentError ,
873952 ":path in URI must be empty or an absolute path if URL has a :host, got: #{ inspect ( uri ) } "
874953 end
0 commit comments