From fbc2d41b098869a704f561095899adff66d21c2e Mon Sep 17 00:00:00 2001 From: Benoit Chesneau Date: Fri, 21 Aug 2020 15:10:07 +0200 Subject: [PATCH] add new url format for unix socket Current way to pass the socket requires the user to url encode the path. For example "/var/run/mysocket' should be encoded as "%2Fvar%2Frun%2Fmyscoket" which is not really convenient. Changes: We introduce thiss tuple as a ne format {<<"unix:/var/run/mysock'>>, <<"http://path/to/resource"> } This allows the user to pass a path withut encoding it while supporting extensions in the future. note: for now only http is suppored. --- src/hackney.erl | 2 +- src/hackney_url.erl | 39 +++++++++++++++++++++++++++++++++++++- test/hackney_url_tests.erl | 24 +++++++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/hackney.erl b/src/hackney.erl index 48966858..71b2ab99 100644 --- a/src/hackney.erl +++ b/src/hackney.erl @@ -345,7 +345,7 @@ request(Method, #hackney_url{}=URL0, Headers0, Body, Options0) -> Error end; request(Method, URL, Headers, Body, Options) - when is_binary(URL) orelse is_list(URL) -> + when is_binary(URL) orelse is_list(URL) orelse is_tuple(URL) -> request(Method, hackney_url:parse_url(URL), Headers, Body, Options). diff --git a/src/hackney_url.erl b/src/hackney_url.erl index 0baf86ce..cba7a4c6 100644 --- a/src/hackney_url.erl +++ b/src/hackney_url.erl @@ -8,6 +8,8 @@ %%% %% @doc module to manage URLs. +%% +%% TODO: add support of ssl on unix sockets -module(hackney_url). @@ -32,7 +34,7 @@ -type qs_opt() :: noplus | upper. %% @doc Parse an URL and return a #hackney_url record. --spec parse_url(URL::binary()|list()) -> hackney_url(). +-spec parse_url(URL::binary()|list()|tuple()) -> hackney_url(). parse_url(URL) when is_list(URL) -> case unicode:characters_to_binary(URL) of URL1 when is_binary(URL1) -> @@ -48,9 +50,15 @@ parse_url(<<"https://", Rest/binary>>) -> scheme=https}); parse_url(<<"http+unix://", Rest/binary>>) -> parse_url(Rest, #hackney_url{transport=hackney_local_tcp, scheme=http_unix}); +parse_url({<<"unix:", SocketPath/binary>>, <<"http:/", Rest/binary >>}) -> + parse_unix_url(SocketPath, Rest, #hackney_url{transport=hackney_local_tcp, scheme=http_unix}); + +parse_url(URL) when is_tuple(URL) -> + erlang:error({bad_url, URL}); parse_url(URL) -> parse_url(URL, #hackney_url{transport=hackney_tcp, scheme=http}). + parse_url(URL, S) -> {URL1, Fragment} = parse_fragment(URL), case binary:split(URL1, <<"/">>) of @@ -73,6 +81,35 @@ parse_url(URL, S) -> end. +parse_unix_url(SocketPath, URL, S) -> + {URL1, Fragment} = parse_fragment(URL), + {Path, Query} = parse_path(URL1), + + case binary:split(Path, <<"@">>) of + [Path1] -> + S#hackney_url{host=unicode:characters_to_list(urldecode(SocketPath)), + port=0, + raw_path = URL, + path = Path1, + qs = Query, + fragment = Fragment}; + [Credentials, Path1] -> + {User, Password}  = case binary:split(Credentials, <<":">>) of + [User1, Password1] -> {User1, Password1}; + [User1] -> {User1, <<>>} + end, + S#hackney_url{host=unicode:characters_to_list(urldecode(SocketPath)), + port=0, + raw_path = URL, + path = Path1, + qs = Query, + fragment = Fragment, + user=urldecode(User), + password=urldecode(Password)} + + end. + + raw_fragment(<<"">>) -> <<"">>; raw_fragment(Fragment) -> <<"#", Fragment/binary>>. diff --git a/test/hackney_url_tests.erl b/test/hackney_url_tests.erl index d396a598..ad55d74e 100644 --- a/test/hackney_url_tests.erl +++ b/test/hackney_url_tests.erl @@ -175,10 +175,34 @@ parse_and_unparse_url_test_() -> user = <<"">>, password = <<"">>} } + ], [{V, fun() -> R = hackney_url:parse_url(V) end} || {V, R} <- Tests] ++ [{V, fun() -> V = hackney_url:unparse_url(R) end} || {V, R} <- Tests]. + + +parse_unix_socket_test_() -> + Tests = [ + {{<<"unix:/var/run/test.sock">>, <<"http:user@/path?key=value#Section%205">>}, + #hackney_url{transport =hackney_local_tcp, + scheme = http_unix, + netloc = <<"%2Fvar%2Frun%2Ftest.sock">>, + raw_path = <<"/path?key=value#Section%205">>, + path = <<"/path">>, + qs = <<"key=value">>, + fragment = <<"Section%205">>, + host = "/var/run/test.sock", + port = 0, + user = <<"user">>, + password = <<"">>} + } + ], + + [{V, fun() -> R = hackney_url:parse_url(V) end} || {_, {V, R}} <- Tests]. + + + parse_url_test_() -> %% {Value, Result}. Tests = [