diff options
Diffstat (limited to 'deps/amqp_client/src/uri_parser.erl')
-rw-r--r-- | deps/amqp_client/src/uri_parser.erl | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/deps/amqp_client/src/uri_parser.erl b/deps/amqp_client/src/uri_parser.erl new file mode 100644 index 0000000000..a2bb98dad7 --- /dev/null +++ b/deps/amqp_client/src/uri_parser.erl @@ -0,0 +1,125 @@ +%% This file is a copy of http_uri.erl from the R13B-1 Erlang/OTP +%% distribution with several modifications. + +%% All modifications are Copyright (c) 2009-2020 VMware, Inc. or its affiliates. + +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at https://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' + +%% See https://tools.ietf.org/html/rfc3986 + +-module(uri_parser). + +-export([parse/2]). + +%%%========================================================================= +%%% API +%%%========================================================================= + +%% Returns a key list of elements extracted from the URI. Note that +%% only 'scheme' is guaranteed to exist. Key-Value pairs from the +%% Defaults list will be used absence of a non-empty value extracted +%% from the URI. The values extracted are strings, except for 'port' +%% which is an integer, 'userinfo' which is a list of strings (split +%% on $:), and 'query' which is a list of strings where no $= char +%% found, or a {key,value} pair where a $= char is found (initial +%% split on $& and subsequent optional split on $=). Possible keys +%% are: 'scheme', 'userinfo', 'host', 'port', 'path', 'query', +%% 'fragment'. + +-spec parse(AbsURI, Defaults :: list()) + -> [{atom(), string()}] | {error, no_scheme | {malformed_uri, AbsURI, any()}} + when AbsURI :: string() | binary(). +parse(AbsURI, Defaults) -> + AbsUriString = rabbit_data_coercion:to_list(AbsURI), + case parse_scheme(AbsUriString) of + {error, Reason} -> + {error, Reason}; + {Scheme, Rest} -> + case (catch parse_uri_rest(Rest, true)) of + [_|_] = List -> + merge_keylists([{scheme, Scheme} | List], Defaults); + E -> + {error, {malformed_uri, AbsURI, E}} + end + end. + +%%%======================================================================== +%%% Internal functions +%%%======================================================================== +parse_scheme(AbsURI) -> + split_uri(AbsURI, ":", {error, no_scheme}). + +parse_uri_rest("//" ++ URIPart, true) -> + %% we have an authority + {Authority, PathQueryFrag} = + split_uri(URIPart, "/|\\?|#", {URIPart, ""}, 1, 0), + AuthorityParts = parse_authority(Authority), + parse_uri_rest(PathQueryFrag, false) ++ AuthorityParts; +parse_uri_rest(PathQueryFrag, _Bool) -> + %% no authority, just a path and maybe query + {PathQuery, Frag} = split_uri(PathQueryFrag, "#", {PathQueryFrag, ""}), + {Path, QueryString} = split_uri(PathQuery, "\\?", {PathQuery, ""}), + QueryPropList = split_query(QueryString), + [{path, Path}, {'query', QueryPropList}, {fragment, Frag}]. + +parse_authority(Authority) -> + {UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}), + UserInfoSplit = case re:split(UserInfo, ":", [{return, list}]) of + [""] -> []; + UIS -> UIS + end, + [{userinfo, UserInfoSplit} | parse_host_port(HostPort)]. + +parse_host_port("[" ++ HostPort) -> %ipv6 + {Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, ""}), + [{host, Host} | case split_uri(ColonPort, ":", not_found, 0, 1) of + not_found -> case ColonPort of + [] -> []; + _ -> throw({invalid_port, ColonPort}) + end; + {_, Port} -> [{port, list_to_integer(Port)}] + end]; + +parse_host_port(HostPort) -> + {Host, Port} = split_uri(HostPort, ":", {HostPort, not_found}), + [{host, Host} | case Port of + not_found -> []; + _ -> [{port, list_to_integer(Port)}] + end]. + +split_query(Query) -> + case re:split(Query, "&", [{return, list}]) of + [""] -> []; + QParams -> [split_uri(Param, "=", Param) || Param <- QParams] + end. + +split_uri(UriPart, SplitChar, NoMatchResult) -> + split_uri(UriPart, SplitChar, NoMatchResult, 1, 1). + +split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) -> + case re:run(UriPart, SplitChar) of + {match, [{Match, _}]} -> + {string:substr(UriPart, 1, Match + 1 - SkipLeft), + string:substr(UriPart, Match + 1 + SkipRight, length(UriPart))}; + nomatch -> + NoMatchResult + end. + +merge_keylists(A, B) -> + {AEmpty, ANonEmpty} = lists:partition(fun ({_Key, V}) -> V =:= [] end, A), + [AEmptyS, ANonEmptyS, BS] = + [lists:ukeysort(1, X) || X <- [AEmpty, ANonEmpty, B]], + lists:ukeymerge(1, lists:ukeymerge(1, ANonEmptyS, BS), AEmptyS). |