From db842388b4957891c9cec8279636da965a75411e Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 26 Dec 2019 15:11:32 +0300 Subject: [PATCH] Introduce rebar_uri Attempts to bridge http_uri (deprecated in OTP 23) and uri_string (available as of OTP 21+). (cherry picked from commit 7057cbe281296707771ee3a017406f993f24c77d) --- src/rebar_git_resource.erl | 4 ++-- src/rebar_uri.erl | 44 ++++++++++++++++++++++++++++++++++++++ src/rebar_utils.erl | 33 +++++++++++++++++++++------- test/rebar_utils_SUITE.erl | 16 +++++++++++--- 4 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/rebar_uri.erl diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 45bc0432..ab06836b 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -117,8 +117,8 @@ parse_git_url(Url) -> end. parse_git_url(not_scp, Url) -> UriOpts = [{scheme_defaults, [{git, 9418} | http_uri:scheme_defaults()]}], - case http_uri:parse(Url, UriOpts) of - {ok, {_Scheme, _User, Host, _Port, Path, _Query}} -> + case rebar_uri:parse(Url, UriOpts) of + #{path := Path, host := Host} -> {ok, {Host, filename:rootname(Path, ".git")}}; {error, Reason} -> {error, Reason} diff --git a/src/rebar_uri.erl b/src/rebar_uri.erl new file mode 100644 index 00000000..6e28a90d --- /dev/null +++ b/src/rebar_uri.erl @@ -0,0 +1,44 @@ +%%% @doc multi-OTP version compatibility shim for working with URIs +-module(rebar_uri). + +-ifdef (OTP_RELEASE). +-export([parse/1]). + +-spec parse(URIString) -> URIMap when + URIString :: uri_string:uri_string(), + URIMap :: uri_string:uri_map() | uri_string:error(). + +parse(URIString) -> + uri_string:parse(URIString). +-else. +-export([parse/1]). + +-spec parse(URIString) -> URIMap when + URIString :: iodata(), + URIMap :: map() | {error, atom(), term()}. + +parse(URIString) -> + case http_uri:parse(URIString) of + {error, Reason} -> + %% no additional parser/term info available to us, + %% e.g. see what uri_string returns in + %% uri_string:parse(<<"h$ttp:::://////lolz">>). + {error, Reason, ""}; + {ok, {Scheme, UserInfo, Host, Port, Path, Query}} -> + #{ + scheme => Scheme, + host => Host, + port => Port, + path => Path, + %% http_uri:parse/1 includes the leading question mark + %% in query string but uri_string:parse/1 leaves it out. + %% string:slice/2 isn't available in OTP <= 19. + query => case Query of + [] -> ""; + _ -> string:substr(Query, 2) + end, + userinfo => UserInfo + } + end. +-endif. + diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index c587363d..7bfa5ca8 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -910,8 +910,7 @@ get_http_vars(Scheme) -> -ifdef (OTP_RELEASE). -if(?OTP_RELEASE >= 23). - -compile({nowarn_deprecated_function, [{http_uri, parse, 1}, - {http_uri, decode, 1}]}). + -compile({nowarn_deprecated_function, [{http_uri, decode, 1}]}). -endif. -endif. @@ -924,7 +923,10 @@ set_httpc_options(_, []) -> set_httpc_options(Scheme, Proxy) -> URI = normalise_proxy(Scheme, Proxy), - {ok, {_, UserInfo, Host, Port, _, _}} = http_uri:parse(URI), + Parts = rebar_uri:parse(URI), + Host = maps:get(host, Parts, []), + Port = maps:get(port, Parts, []), + UserInfo = maps:get(userinfo, Parts, []), httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar), set_proxy_auth(UserInfo). @@ -935,14 +937,30 @@ normalise_proxy(Scheme, URI) -> _ -> URI end. +%% OTP 21+ +-ifdef (OTP_RELEASE). url_append_path(Url, ExtraPath) -> - case http_uri:parse(Url) of - {ok, {Scheme, UserInfo, Host, Port, Path, Query}} -> + case rebar_uri:parse(Url) of + #{path := Path} = Map -> + FullPath = filename:join(Path, ExtraPath), + {ok, uri_string:recompose(maps:update(path, FullPath, Map))}; + _ -> + error + end. +-else. +url_append_path(Url, ExtraPath) -> + case rebar_uri:parse(Url) of + #{scheme := Scheme, userinfo := UserInfo, host := Host, port := Port, path := Path, query := Query} -> + PrefixedQuery = case Query of + [] -> []; + Other -> lists:append(["?", Other]) + end, {ok, lists:append([atom_to_list(Scheme), "://", UserInfo, Host, ":", integer_to_list(Port), - filename:join(Path, ExtraPath), Query])}; + filename:join(Path, ExtraPath), PrefixedQuery])}; _ -> error end. +-endif. %% escape\ as\ a\ shell\? escape_chars(Str) when is_atom(Str) -> @@ -1028,8 +1046,7 @@ ssl_opts(Url) -> ssl_opts(ssl_verify_enabled, Url) -> case check_ssl_version() of true -> - {ok, {_, _, Hostname, _, _, _}} = - http_uri:parse(rebar_utils:to_list(Url)), + #{host := Hostname} = rebar_uri:parse(rebar_utils:to_list(Url)), VerifyFun = {fun ssl_verify_hostname:verify_fun/3, [{check_hostname, Hostname}]}, CACerts = certifi:cacerts(), diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index 4901f40c..3b111f15 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -322,6 +322,16 @@ is_list_of_strings(_Config) -> ?assert(rebar_utils:is_list_of_strings("foo") == false). url_append_path(_Config) -> - ?assertEqual({ok, "https://repo.hex.pm:443/repos/org"}, rebar_utils:url_append_path("https://repo.hex.pm", "/repos/org")), - ?assertEqual({ok, "https://repo.hex.pm:443/repos/org?foo=bar"}, rebar_utils:url_append_path("https://repo.hex.pm", - "/repos/org?foo=bar")). + %% OTP version differences + {ok, Val1} = rebar_utils:url_append_path("https://repo.hex.pm", "/repos/org"), + ?assert(lists:member(Val1, [ + "https://repo.hex.pm/repos/org", + "https://repo.hex.pm:443/repos/org" + ])), + {ok, Val2} = rebar_utils:url_append_path("https://repo.hex.pm?foo=bar", "/repos/org"), + ?assert(lists:member(Val2, [ + "https://repo.hex.pm/repos/org?foo=bar", + "https://repo.hex.pm:443/repos/org?foo=bar" + ])), + ?assertEqual({ok, "https://repo.hex.pm:443/repos/org?foo=bar"}, + rebar_utils:url_append_path("https://repo.hex.pm:443?foo=bar", "/repos/org")).