WIP commit

This commit is contained in:
Gavin M. Roy 2016-01-08 23:21:49 -05:00
parent f16b0be421
commit 7b05c0c2b5
4 changed files with 192 additions and 13 deletions

View file

@ -5,7 +5,7 @@ all: get-deps compile
build-plt:
@dialyzer --build_plt --output_plt ~/.$(PROJECT).plt \
--apps kernel stdlib erts
--apps kernel stdlib erts inets edoc
clean:
@( $(REBAR) clean )

View file

@ -1,10 +1,11 @@
{application, urilib, [
{description, "RFC-3986 URI Library"},
{vsn, "0.1.0"},
{registered, []},
{registered, [urilib]},
{applications, [
kernel,
stdlib,
inets,
edoc
]},
{mod, {urilib, []}},

View file

@ -7,7 +7,6 @@
-module(urilib).
-export([build/1,
parse/1,
parse_uri/1,
parse_url/1,
encode/1,
@ -30,22 +29,60 @@
%% @doc Returns a URI from the record passed in.
%%
%% @end
build(_URL) ->
ok.
-spec parse(string()) -> #uri{}.
parse(URL) -> parse_uri(URL).
build(#uri{scheme=Scheme, userinfo=UserInfo, authority=Authority,
path=Path, query=QArgs, fragment=Fragment}) ->
U1 = url_add_scheme(Scheme),
U2 = url_maybe_add_user(UserInfo, U1),
U3 = url_add_host_and_port(Scheme,
Authority#authority.host,
Authority#authority.port, U2),
U4 = url_add_path(Path, U3),
U5 = url_maybe_add_qargs(QArgs, U4),
url_maybe_add_fragment(Fragment, U5).
-spec parse_uri(string()) -> #uri{}.
parse_uri(_URL) ->
ok.
%% @spec parse_uri(URI) -> ParsedURI.
%% where
%% URI = string()
%% ParsedURI = #uri{}
%% @doc Parse a URI string returning the parsed data as a record
%% @end
parse_uri(URI) ->
case http_uri:parse(URI, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
#uri{scheme=Scheme,
userinfo=parse_userinfo(UserInfo),
authority=#authority{host=Host,
port=Port},
path=Path,
query=parse_query(Query),
fragment=Fragment};
{error, Reason} -> {error, Reason}
end.
-spec parse_url(string()) -> #url{}.
parse_url(_URL) ->
ok.
%% @spec parse_url(URL) -> ParsedURL.
%% where
%% URI = string()
%% ParsedURL = #url{}
%% @doc Parse a URL string returning the parsed data as a record
%% @end
parse_url(URL) ->
case http_uri:parse(URL, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
User = parse_userinfo(UserInfo),
#url{scheme=Scheme,
username=User#userinfo.username,
password=User#userinfo.password,
host=Host,
port=Port,
path=Path,
query=parse_query(Query),
fragment=Fragment};
{error, Reason} -> {error, Reason}
end.
-spec encode(string()) -> string().
@ -97,3 +134,76 @@ decode(Value) ->
%% @end
decode_plus(Value) ->
string:join([http_uri:decode(V) || V <- string:tokens(Value, "+")], " ").
-spec parse_query(string()) -> [].
%% @private
parse_query([]) -> [];
parse_query(Query) ->
case re:split(Query, "[&|?]", [{return, list}]) of
[""] -> [];
QArgs -> [split_query_arg(Arg) || Arg <- QArgs, Arg =/= []]
end.
-spec parse_userinfo(string()) -> #userinfo{}.
%% @private
parse_userinfo([]) -> #userinfo{};
parse_userinfo(Value) ->
case string:tokens(Value, ":") of
[User, Password] -> #userinfo{username=User, password=Password};
[User] -> #userinfo{username=User}
end.
-spec split_query_arg(string()) -> {string(), string()}.
%% @private
split_query_arg(Argument) ->
[K, V] = string:tokens(Argument, "="),
{K, V}.
%% @private
url_add_scheme(Scheme) ->
string:concat(atom_to_list(Scheme), "://").
%% @private
url_maybe_add_user([], URL) -> URL;
url_maybe_add_user(User, URL) ->
string:concat(URL, string:concat(User, "@")).
%% @private
url_add_host_and_port(http, Host, 80, URL) ->
string:concat(URL, Host);
%% @private
url_add_host_and_port(https, Host, 443, URL) ->
string:concat(URL, Host);
url_add_host_and_port(_, Host, Port, URL) ->
string:concat(URL, string:join([Host, integer_to_list(Port)], ":")).
%% @private
url_add_path(Path, URL) ->
Escaped = string:join([edoc_lib:escape_uri(P) || P <- string:tokens(Path, "/")], "/"),
string:join([URL, Escaped], "/").
%% @private
url_maybe_add_qargs([], URL) -> URL;
url_maybe_add_qargs(QArgs, URL) ->
QStr = string:join([string:join([encode_plus(K), encode_plus(V)], "=") || {K,V} <- QArgs], "&"),
string:join([URL, QStr], "?").
%% @private
url_maybe_add_fragment([], URL) -> URL;
url_maybe_add_fragment(Value, URL) ->
Fragment = case string:left(Value, 1) of
"#" -> edoc_lib:escape_uri(string:sub_string(Value, 2));
_ -> edoc_lib:escape_uri(Value)
end,
string:join([URL, Fragment], "#").

View file

@ -2,6 +2,7 @@
-include_lib("eunit/include/eunit.hrl").
-include("urilib.hrl").
decode_test() ->
Value = "foo%2fbar%20baz",
@ -27,3 +28,70 @@ encode_plus1_test() ->
Value = "foo/bar baz",
Expect = "foo%2fbar+baz",
?assertEqual(Expect, urilib:encode_plus(Value)).
parse_uri_variation1_test() ->
URI = "amqp://guest:rabbitmq@rabbitmq:5672/%2f?heartbeat=5",
Expect = #uri{scheme=amqp,
userinfo=#userinfo{username="guest",
password="rabbitmq"},
authority=#authority{host="rabbitmq", port=5672},
path="/%2f",
query=[{"heartbeat", "5"}],
fragment=[]},
?assertEqual(Expect, urilib:parse_uri(URI)).
parse_uri_variation2_test() ->
URI = "http://www.google.com/search?foo=bar#baz",
Expect = #uri{scheme=http,
userinfo=#userinfo{},
authority=#authority{host="www.google.com", port=80},
path="/search",
query=[{"foo", "bar"}],
fragment="#baz"},
?assertEqual(Expect, urilib:parse_uri(URI)).
parse_uri_variation3_test() ->
URI = "https://www.google.com/search",
Expect = #uri{scheme=https,
userinfo=#userinfo{},
authority=#authority{host="www.google.com", port=443},
path="/search",
query=[],
fragment=[]},
?assertEqual(Expect, urilib:parse_uri(URI)).
parse_url_variation1_test() ->
URL = "amqp://guest:rabbitmq@rabbitmq:5672/%2f?heartbeat=5",
Expect = #url{scheme=amqp,
username="guest",
password="rabbitmq",
host="rabbitmq",
port=5672,
path="/%2f",
query=[{"heartbeat", "5"}],
fragment=[]},
?assertEqual(Expect, urilib:parse_url(URL)).
parse_url_variation2_test() ->
URL = "http://www.google.com/search?foo=bar#baz",
Expect = #url{scheme=http,
username=undefined,
password=undefined,
host="www.google.com",
port=80,
path="/search",
query=[{"foo", "bar"}],
fragment="#baz"},
?assertEqual(Expect, urilib:parse_url(URL)).
parse_url_variation3_test() ->
URL = "https://www.google.com/search",
Expect = #url{scheme=https,
username=undefined,
password=undefined,
host="www.google.com",
port=443,
path="/search",
query=[],
fragment=[]},
?assertEqual(Expect, urilib:parse_url(URL)).