Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

131 linhas
4.0 KiB

  1. %%% @doc multi-OTP version compatibility shim for working with URIs
  2. -module(rebar_uri).
  3. -export([
  4. parse/1, parse/2, scheme_defaults/0,
  5. append_path/2
  6. ]).
  7. -ifdef(OTP_RELEASE).
  8. -spec parse(URIString) -> URIMap when
  9. URIString :: uri_string:uri_string(),
  10. URIMap :: uri_string:uri_map() | uri_string:error().
  11. parse(URIString) ->
  12. parse(URIString, []).
  13. parse(URIString, URIOpts) ->
  14. case uri_string:parse(URIString) of
  15. #{path := ""} = Map -> apply_opts(Map#{path => "/"}, URIOpts);
  16. Map when is_map(Map) -> apply_opts(Map, URIOpts);
  17. {error, _, _} = E -> E
  18. end.
  19. -else.
  20. -spec parse(URIString) -> URIMap when
  21. URIString :: iodata(),
  22. URIMap :: map() | {error, term(), term()}.
  23. parse(URIString) ->
  24. parse(URIString, []).
  25. parse(URIString, URIOpts) ->
  26. case http_uri:parse(URIString, URIOpts) of
  27. {error, Reason} ->
  28. %% no additional parser/term info available to us,
  29. %% e.g. see what uri_string returns in
  30. %% uri_string:parse(<<"h$ttp:::://////lolz">>).
  31. {error, "", Reason};
  32. {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
  33. #{
  34. scheme => rebar_utils:to_list(Scheme),
  35. host => Host,
  36. port => Port,
  37. path => Path,
  38. %% http_uri:parse/1 includes the leading question mark
  39. %% in query string but uri_string:parse/1 leaves it out.
  40. %% string:slice/2 isn't available in OTP <= 19.
  41. query => case Query of
  42. [] -> "";
  43. _ -> string:substr(Query, 2)
  44. end,
  45. userinfo => UserInfo
  46. }
  47. end.
  48. -endif.
  49. %% OTP 21+
  50. -ifdef(OTP_RELEASE).
  51. append_path(Url, ExtraPath) ->
  52. case parse(Url) of
  53. #{path := Path} = Map ->
  54. FullPath = join(Path, ExtraPath),
  55. {ok, uri_string:recompose(maps:update(path, FullPath, Map))};
  56. _ ->
  57. error
  58. end.
  59. -else.
  60. append_path(Url, ExtraPath) ->
  61. case parse(Url) of
  62. #{scheme := Scheme, userinfo := UserInfo, host := Host,
  63. port := Port, path := Path, query := Query} ->
  64. ListScheme = rebar_utils:to_list(Scheme),
  65. PrefixedQuery = case Query of
  66. [] -> [];
  67. Other -> lists:append(["?", Other])
  68. end,
  69. NormPath = case Path of
  70. "" -> "/";
  71. _ -> Path
  72. end,
  73. {ok, maybe_port(
  74. Url, lists:append([ListScheme, "://", UserInfo, Host]),
  75. [$: | rebar_utils:to_list(Port)],
  76. lists:append([join(NormPath, ExtraPath), PrefixedQuery])
  77. )};
  78. _ ->
  79. error
  80. end.
  81. -endif.
  82. %% OTP 21+
  83. -ifdef(OTP_RELEASE).
  84. scheme_defaults() ->
  85. %% no scheme defaults here; just custom ones
  86. [].
  87. -else.
  88. scheme_defaults() ->
  89. http_uri:scheme_defaults().
  90. -endif.
  91. join(URI, "") -> URI;
  92. join(URI, "/") -> URI;
  93. join("/", [$/|_] = Path) -> Path;
  94. join("/", Path) -> [$/ | Path];
  95. join("", [$/|_] = Path) -> Path;
  96. join("", Path) -> [$/ | Path];
  97. join([H|T], Path) -> [H | join(T, Path)].
  98. -ifdef(OTP_RELEASE).
  99. apply_opts(Map = #{port := _}, _) ->
  100. Map;
  101. apply_opts(Map = #{scheme := Scheme}, URIOpts) ->
  102. SchemeDefaults = proplists:get_value(scheme_defaults, URIOpts, []),
  103. %% Here is the funky bit: don't add the port number if it's in a default
  104. %% to maintain proper default behaviour.
  105. try lists:keyfind(list_to_existing_atom(Scheme), 1, SchemeDefaults) of
  106. {_, Port} ->
  107. Map#{port => Port};
  108. false ->
  109. Map
  110. catch
  111. error:badarg -> % not an existing atom, not in the list
  112. Map
  113. end.
  114. -else.
  115. maybe_port(Url, Host, Port, PathQ) ->
  116. case lists:prefix(Host ++ Port, Url) of
  117. true -> Host ++ Port ++ PathQ; % port was explicit
  118. false -> Host ++ PathQ % port was implicit
  119. end.
  120. -endif.