You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

441 lines
15 KiB

Use Erlang's OTP base64 module (available since R12B02) and avoid duplicated base64 encoding/decoding code in ibrowse_lib.erl and ibrowse_http_client.erl. OTP's base64 module is also more efficient (C implementation): 1> Data = crypto:rand_bytes(4096). <<205,174,13,169,97,159,110,161,71,43,226,153,42,101,243, 83,11,96,23,161,253,251,129,240,163,216,58,175,190,...>> 2> 2> timer:tc(ibrowse_lib, encode_base64, [Data]). {2920, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 3> timer:tc(ibrowse_lib, encode_base64, [Data]). {1221, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 4> timer:tc(ibrowse_lib, encode_base64, [Data]). {1436, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 5> timer:tc(ibrowse_lib, encode_base64, [Data]). {1195, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 6> 6> timer:tc(base64, encode, [Data]). {1846, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 7> timer:tc(base64, encode, [Data]). {743, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 8> timer:tc(base64, encode, [Data]). {737, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 9> timer:tc(base64, encode, [Data]). {656, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>}
15 years ago
Use Erlang's OTP base64 module (available since R12B02) and avoid duplicated base64 encoding/decoding code in ibrowse_lib.erl and ibrowse_http_client.erl. OTP's base64 module is also more efficient (C implementation): 1> Data = crypto:rand_bytes(4096). <<205,174,13,169,97,159,110,161,71,43,226,153,42,101,243, 83,11,96,23,161,253,251,129,240,163,216,58,175,190,...>> 2> 2> timer:tc(ibrowse_lib, encode_base64, [Data]). {2920, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 3> timer:tc(ibrowse_lib, encode_base64, [Data]). {1221, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 4> timer:tc(ibrowse_lib, encode_base64, [Data]). {1436, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 5> timer:tc(ibrowse_lib, encode_base64, [Data]). {1195, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 6> 6> timer:tc(base64, encode, [Data]). {1846, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 7> timer:tc(base64, encode, [Data]). {743, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 8> timer:tc(base64, encode, [Data]). {737, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 9> timer:tc(base64, encode, [Data]). {656, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>}
15 years ago
Use Erlang's OTP base64 module (available since R12B02) and avoid duplicated base64 encoding/decoding code in ibrowse_lib.erl and ibrowse_http_client.erl. OTP's base64 module is also more efficient (C implementation): 1> Data = crypto:rand_bytes(4096). <<205,174,13,169,97,159,110,161,71,43,226,153,42,101,243, 83,11,96,23,161,253,251,129,240,163,216,58,175,190,...>> 2> 2> timer:tc(ibrowse_lib, encode_base64, [Data]). {2920, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 3> timer:tc(ibrowse_lib, encode_base64, [Data]). {1221, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 4> timer:tc(ibrowse_lib, encode_base64, [Data]). {1436, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 5> timer:tc(ibrowse_lib, encode_base64, [Data]). {1195, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 6> 6> timer:tc(base64, encode, [Data]). {1846, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 7> timer:tc(base64, encode, [Data]). {743, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 8> timer:tc(base64, encode, [Data]). {737, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 9> timer:tc(base64, encode, [Data]). {656, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>}
15 years ago
Use Erlang's OTP base64 module (available since R12B02) and avoid duplicated base64 encoding/decoding code in ibrowse_lib.erl and ibrowse_http_client.erl. OTP's base64 module is also more efficient (C implementation): 1> Data = crypto:rand_bytes(4096). <<205,174,13,169,97,159,110,161,71,43,226,153,42,101,243, 83,11,96,23,161,253,251,129,240,163,216,58,175,190,...>> 2> 2> timer:tc(ibrowse_lib, encode_base64, [Data]). {2920, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 3> timer:tc(ibrowse_lib, encode_base64, [Data]). {1221, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 4> timer:tc(ibrowse_lib, encode_base64, [Data]). {1436, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 5> timer:tc(ibrowse_lib, encode_base64, [Data]). {1195, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 6> 6> timer:tc(base64, encode, [Data]). {1846, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 7> timer:tc(base64, encode, [Data]). {743, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 8> timer:tc(base64, encode, [Data]). {737, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>} 9> timer:tc(base64, encode, [Data]). {656, <<"za4NqWGfbqFHK+KZKmXzUwtgF6H9+4Hwo9g6r77h2EF1/Xk1oKOIOmnAkgtv41LPXg37fp2dlr45C8qCA9/8zrcc9F5zr2JT0eVPTrh5aahl"...>>}
15 years ago
14 years ago
  1. %%% File : ibrowse_lib.erl
  2. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  3. %%% Description :
  4. %%% Created : 27 Feb 2004 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  5. %% @doc Module with a few useful functions
  6. -module(ibrowse_lib).
  7. -author('chandru').
  8. -ifdef(debug).
  9. -compile(export_all).
  10. -endif.
  11. -include("ibrowse.hrl").
  12. -ifdef(EUNIT).
  13. -include_lib("eunit/include/eunit.hrl").
  14. -endif.
  15. -export([
  16. get_trace_status/2,
  17. do_trace/2,
  18. do_trace/3,
  19. url_encode/1,
  20. decode_rfc822_date/1,
  21. status_code/1,
  22. encode_base64/1,
  23. decode_base64/1,
  24. get_value/2,
  25. get_value/3,
  26. parse_url/1,
  27. printable_date/0
  28. ]).
  29. get_trace_status(Host, Port) ->
  30. ibrowse:get_config_value({trace, Host, Port}, false).
  31. %% @doc URL-encodes a string based on RFC 1738. Returns a flat list.
  32. %% @spec url_encode(Str) -> UrlEncodedStr
  33. %% Str = string()
  34. %% UrlEncodedStr = string()
  35. url_encode(Str) when is_list(Str) ->
  36. url_encode_char(lists:reverse(Str), []).
  37. url_encode_char([X | T], Acc) when X >= $0, X =< $9 ->
  38. url_encode_char(T, [X | Acc]);
  39. url_encode_char([X | T], Acc) when X >= $a, X =< $z ->
  40. url_encode_char(T, [X | Acc]);
  41. url_encode_char([X | T], Acc) when X >= $A, X =< $Z ->
  42. url_encode_char(T, [X | Acc]);
  43. url_encode_char([X | T], Acc) when X == $-; X == $_; X == $. ->
  44. url_encode_char(T, [X | Acc]);
  45. url_encode_char([32 | T], Acc) ->
  46. url_encode_char(T, [$+ | Acc]);
  47. url_encode_char([X | T], Acc) ->
  48. url_encode_char(T, [$%, d2h(X bsr 4), d2h(X band 16#0f) | Acc]);
  49. url_encode_char([], Acc) ->
  50. Acc.
  51. d2h(N) when N<10 -> N+$0;
  52. d2h(N) -> N+$a-10.
  53. decode_rfc822_date(String) when is_list(String) ->
  54. case catch decode_rfc822_date_1(string:tokens(String, ", \t\r\n")) of
  55. {'EXIT', _} ->
  56. {error, invalid_date};
  57. Res ->
  58. Res
  59. end.
  60. % TODO: Have to handle the Zone
  61. decode_rfc822_date_1([_,DayInt,Month,Year, Time,Zone]) ->
  62. decode_rfc822_date_1([DayInt,Month,Year, Time,Zone]);
  63. decode_rfc822_date_1([Day,Month,Year, Time,_Zone]) ->
  64. DayI = list_to_integer(Day),
  65. MonthI = month_int(Month),
  66. YearI = list_to_integer(Year),
  67. TimeTup = case string:tokens(Time, ":") of
  68. [H,M] ->
  69. {list_to_integer(H),
  70. list_to_integer(M),
  71. 0};
  72. [H,M,S] ->
  73. {list_to_integer(H),
  74. list_to_integer(M),
  75. list_to_integer(S)}
  76. end,
  77. {{YearI,MonthI,DayI}, TimeTup}.
  78. month_int("Jan") -> 1;
  79. month_int("Feb") -> 2;
  80. month_int("Mar") -> 3;
  81. month_int("Apr") -> 4;
  82. month_int("May") -> 5;
  83. month_int("Jun") -> 6;
  84. month_int("Jul") -> 7;
  85. month_int("Aug") -> 8;
  86. month_int("Sep") -> 9;
  87. month_int("Oct") -> 10;
  88. month_int("Nov") -> 11;
  89. month_int("Dec") -> 12.
  90. %% @doc Given a status code, returns an atom describing the status code.
  91. %% @spec status_code(StatusCode::status_code()) -> StatusDescription
  92. %% status_code() = string() | integer()
  93. %% StatusDescription = atom()
  94. status_code(100) -> continue;
  95. status_code(101) -> switching_protocols;
  96. status_code(102) -> processing;
  97. status_code(200) -> ok;
  98. status_code(201) -> created;
  99. status_code(202) -> accepted;
  100. status_code(203) -> non_authoritative_information;
  101. status_code(204) -> no_content;
  102. status_code(205) -> reset_content;
  103. status_code(206) -> partial_content;
  104. status_code(207) -> multi_status;
  105. status_code(300) -> multiple_choices;
  106. status_code(301) -> moved_permanently;
  107. status_code(302) -> found;
  108. status_code(303) -> see_other;
  109. status_code(304) -> not_modified;
  110. status_code(305) -> use_proxy;
  111. status_code(306) -> unused;
  112. status_code(307) -> temporary_redirect;
  113. status_code(400) -> bad_request;
  114. status_code(401) -> unauthorized;
  115. status_code(402) -> payment_required;
  116. status_code(403) -> forbidden;
  117. status_code(404) -> not_found;
  118. status_code(405) -> method_not_allowed;
  119. status_code(406) -> not_acceptable;
  120. status_code(407) -> proxy_authentication_required;
  121. status_code(408) -> request_timeout;
  122. status_code(409) -> conflict;
  123. status_code(410) -> gone;
  124. status_code(411) -> length_required;
  125. status_code(412) -> precondition_failed;
  126. status_code(413) -> request_entity_too_large;
  127. status_code(414) -> request_uri_too_long;
  128. status_code(415) -> unsupported_media_type;
  129. status_code(416) -> requested_range_not_satisfiable;
  130. status_code(417) -> expectation_failed;
  131. status_code(422) -> unprocessable_entity;
  132. status_code(423) -> locked;
  133. status_code(424) -> failed_dependency;
  134. status_code(500) -> internal_server_error;
  135. status_code(501) -> not_implemented;
  136. status_code(502) -> bad_gateway;
  137. status_code(503) -> service_unavailable;
  138. status_code(504) -> gateway_timeout;
  139. status_code(505) -> http_version_not_supported;
  140. status_code(507) -> insufficient_storage;
  141. status_code(X) when is_list(X) -> status_code(list_to_integer(X));
  142. status_code(_) -> unknown_status_code.
  143. %% @doc Implements the base64 encoding algorithm. The output data type matches in the input data type.
  144. %% @spec encode_base64(In) -> Out
  145. %% In = string() | binary()
  146. %% Out = string() | binary()
  147. encode_base64(List) when is_list(List) ->
  148. binary_to_list(base64:encode(List));
  149. encode_base64(Bin) when is_binary(Bin) ->
  150. base64:encode(Bin).
  151. %% @doc Implements the base64 decoding algorithm. The output data type matches in the input data type.
  152. %% @spec decode_base64(In) -> Out | exit({error, invalid_input})
  153. %% In = string() | binary()
  154. %% Out = string() | binary()
  155. decode_base64(List) when is_list(List) ->
  156. binary_to_list(base64:decode(List));
  157. decode_base64(Bin) when is_binary(Bin) ->
  158. base64:decode(Bin).
  159. get_value(Tag, TVL, DefVal) ->
  160. case lists:keysearch(Tag, 1, TVL) of
  161. false ->
  162. DefVal;
  163. {value, {_, Val}} ->
  164. Val
  165. end.
  166. get_value(Tag, TVL) ->
  167. {value, {_, V}} = lists:keysearch(Tag,1,TVL),
  168. V.
  169. parse_url(Url) ->
  170. try
  171. case parse_url(Url, get_protocol, #url{abspath=Url}, []) of
  172. #url{host_type = undefined, host = Host} = UrlRec ->
  173. case inet_parse:address(Host) of
  174. {ok, {_, _, _, _, _, _, _, _}} ->
  175. UrlRec#url{host_type = ipv6_address};
  176. {ok, {_, _, _, _}} ->
  177. UrlRec#url{host_type = ipv4_address};
  178. _ ->
  179. UrlRec#url{host_type = hostname}
  180. end;
  181. #url{} = UrlRec ->
  182. UrlRec;
  183. _ ->
  184. {error, invalid_uri}
  185. end
  186. catch _:_ ->
  187. {error, invalid_uri}
  188. end.
  189. parse_url([$:, $/, $/ | _], get_protocol, Url, []) ->
  190. {invalid_uri_1, Url};
  191. parse_url([$:, $/, $/ | T], get_protocol, Url, TmpAcc) ->
  192. Prot = list_to_existing_atom(lists:reverse(TmpAcc)),
  193. parse_url(T, get_username,
  194. Url#url{protocol = Prot},
  195. []);
  196. parse_url([H | T], get_username, Url, TmpAcc) when H == $/;
  197. H == $? ->
  198. Path = case H of
  199. $/ ->
  200. [$/ | T];
  201. $? ->
  202. [$/, $? | T]
  203. end,
  204. %% No username/password. No port number
  205. Url#url{host = lists:reverse(TmpAcc),
  206. port = default_port(Url#url.protocol),
  207. path = Path};
  208. parse_url([$: | T], get_username, Url, TmpAcc) ->
  209. %% It is possible that no username/password has been
  210. %% specified. But we'll continue with the assumption that there is
  211. %% a username/password. If we encounter a '@' later on, there is a
  212. %% username/password indeed. If we encounter a '/', it was
  213. %% actually the hostname
  214. parse_url(T, get_password,
  215. Url#url{username = lists:reverse(TmpAcc)},
  216. []);
  217. parse_url([$@ | T], get_username, Url, TmpAcc) ->
  218. parse_url(T, get_host,
  219. Url#url{username = lists:reverse(TmpAcc),
  220. password = ""},
  221. []);
  222. parse_url([$[ | T], get_username, Url, []) ->
  223. % IPv6 address literals are enclosed by square brackets:
  224. % http://www.ietf.org/rfc/rfc2732.txt
  225. parse_url(T, get_ipv6_address, Url#url{host_type = ipv6_address}, []);
  226. parse_url([$[ | T], get_username, _Url, TmpAcc) ->
  227. {error, {invalid_username_or_host, lists:reverse(TmpAcc) ++ "[" ++ T}};
  228. parse_url([$[ | _], get_password, _Url, []) ->
  229. {error, missing_password};
  230. parse_url([$[ | T], get_password, Url, TmpAcc) ->
  231. % IPv6 address literals are enclosed by square brackets:
  232. % http://www.ietf.org/rfc/rfc2732.txt
  233. parse_url(T, get_ipv6_address,
  234. Url#url{host_type = ipv6_address,
  235. password = lists:reverse(TmpAcc)},
  236. []);
  237. parse_url([$@ | T], get_password, Url, TmpAcc) ->
  238. parse_url(T, get_host,
  239. Url#url{password = lists:reverse(TmpAcc)},
  240. []);
  241. parse_url([H | T], get_password, Url, TmpAcc) when H == $/;
  242. H == $? ->
  243. %% Ok, what we thought was the username/password was the hostname
  244. %% and portnumber
  245. #url{username=User} = Url,
  246. Port = list_to_integer(lists:reverse(TmpAcc)),
  247. Path = case H of
  248. $/ ->
  249. [$/ | T];
  250. $? ->
  251. [$/, $? | T]
  252. end,
  253. Url#url{host = User,
  254. port = Port,
  255. username = undefined,
  256. password = undefined,
  257. path = Path};
  258. parse_url([$] | T], get_ipv6_address, #url{protocol = Prot} = Url, TmpAcc) ->
  259. Addr = lists:reverse(TmpAcc),
  260. case inet_parse:address(Addr) of
  261. {ok, {_, _, _, _, _, _, _, _}} ->
  262. Url2 = Url#url{host = Addr, port = default_port(Prot)},
  263. case T of
  264. [$: | T2] ->
  265. parse_url(T2, get_port, Url2, []);
  266. [$/ | T2] ->
  267. Url2#url{path = [$/ | T2]};
  268. [$? | T2] ->
  269. Url2#url{path = [$/, $? | T2]};
  270. [] ->
  271. Url2#url{path = "/"};
  272. _ ->
  273. {error, {invalid_host, "[" ++ Addr ++ "]" ++ T}}
  274. end;
  275. _ ->
  276. {error, {invalid_ipv6_address, Addr}}
  277. end;
  278. parse_url([$[ | T], get_host, #url{} = Url, []) ->
  279. parse_url(T, get_ipv6_address, Url#url{host_type = ipv6_address}, []);
  280. parse_url([$: | T], get_host, #url{} = Url, TmpAcc) ->
  281. parse_url(T, get_port,
  282. Url#url{host = lists:reverse(TmpAcc)},
  283. []);
  284. parse_url([H | T], get_host, #url{protocol=Prot} = Url, TmpAcc) when H == $/;
  285. H == $? ->
  286. Path = case H of
  287. $/ ->
  288. [$/ | T];
  289. $? ->
  290. [$/, $? | T]
  291. end,
  292. Url#url{host = lists:reverse(TmpAcc),
  293. port = default_port(Prot),
  294. path = Path};
  295. parse_url([H | T], get_port, #url{protocol=Prot} = Url, TmpAcc) when H == $/;
  296. H == $? ->
  297. Path = case H of
  298. $/ ->
  299. [$/ | T];
  300. $? ->
  301. [$/, $? | T]
  302. end,
  303. Port = case TmpAcc of
  304. [] ->
  305. default_port(Prot);
  306. _ ->
  307. list_to_integer(lists:reverse(TmpAcc))
  308. end,
  309. Url#url{port = Port, path = Path};
  310. parse_url([H | T], State, Url, TmpAcc) ->
  311. parse_url(T, State, Url, [H | TmpAcc]);
  312. parse_url([], get_host, Url, TmpAcc) when TmpAcc /= [] ->
  313. Url#url{host = lists:reverse(TmpAcc),
  314. port = default_port(Url#url.protocol),
  315. path = "/"};
  316. parse_url([], get_username, Url, TmpAcc) when TmpAcc /= [] ->
  317. Url#url{host = lists:reverse(TmpAcc),
  318. port = default_port(Url#url.protocol),
  319. path = "/"};
  320. parse_url([], get_port, #url{protocol=Prot} = Url, TmpAcc) ->
  321. Port = case TmpAcc of
  322. [] ->
  323. default_port(Prot);
  324. _ ->
  325. list_to_integer(lists:reverse(TmpAcc))
  326. end,
  327. Url#url{port = Port,
  328. path = "/"};
  329. parse_url([], get_password, Url, TmpAcc) ->
  330. %% Ok, what we thought was the username/password was the hostname
  331. %% and portnumber
  332. #url{username=User} = Url,
  333. Port = case TmpAcc of
  334. [] ->
  335. default_port(Url#url.protocol);
  336. _ ->
  337. list_to_integer(lists:reverse(TmpAcc))
  338. end,
  339. Url#url{host = User,
  340. port = Port,
  341. username = undefined,
  342. password = undefined,
  343. path = "/"};
  344. parse_url([], State, Url, TmpAcc) ->
  345. {invalid_uri_2, State, Url, TmpAcc}.
  346. default_port(http) -> 80;
  347. default_port(https) -> 443;
  348. default_port(ftp) -> 21.
  349. printable_date() ->
  350. {{Y,Mo,D},{H, M, S}} = calendar:local_time(),
  351. {_,_,MicroSecs} = now(),
  352. [integer_to_list(Y),
  353. $-,
  354. integer_to_list(Mo),
  355. $-,
  356. integer_to_list(D),
  357. $_,
  358. integer_to_list(H),
  359. $:,
  360. integer_to_list(M),
  361. $:,
  362. integer_to_list(S),
  363. $:,
  364. integer_to_list(MicroSecs div 1000)].
  365. do_trace(Fmt, Args) ->
  366. do_trace(get(my_trace_flag), Fmt, Args).
  367. -ifdef(DEBUG).
  368. do_trace(_, Fmt, Args) ->
  369. io:format("~s -- (~s) - "++Fmt,
  370. [printable_date(),
  371. get(ibrowse_trace_token) | Args]).
  372. -else.
  373. do_trace(true, Fmt, Args) ->
  374. io:format("~s -- (~s) - "++Fmt,
  375. [printable_date(),
  376. get(ibrowse_trace_token) | Args]);
  377. do_trace(_, _, _) ->
  378. ok.
  379. -endif.
  380. -ifdef(EUNIT).
  381. parse_url_test() ->
  382. Urls = [{"http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html",
  383. #url{abspath = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html",
  384. host = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210",
  385. port = 80, protocol = http, path = "/index.html",
  386. host_type = ipv6_address}},
  387. {"http://[1080:0:0:0:8:800:200C:417A]/index.html",
  388. #url{abspath = "http://[1080:0:0:0:8:800:200C:417A]/index.html",
  389. host_type = ipv6_address, port = 80, protocol = http,
  390. host = "1080:0:0:0:8:800:200C:417A", path = "/index.html"}},
  391. {"http://[3ffe:2a00:100:7031::1]",
  392. #url{abspath = "http://[3ffe:2a00:100:7031::1]",
  393. host_type = ipv6_address, port = 80, protocol = http,
  394. host = "3ffe:2a00:100:7031::1", path = "/"}},
  395. {"http://[1080::8:800:200C:417A]/foo",
  396. #url{abspath = "http://[1080::8:800:200C:417A]/foo",
  397. host_type = ipv6_address, port = 80, protocol = http,
  398. host = "1080::8:800:200C:417A", path = "/foo"}},
  399. {"http://[::192.9.5.5]/ipng",
  400. #url{abspath = "http://[::192.9.5.5]/ipng",
  401. host_type = ipv6_address, port = 80, protocol = http,
  402. host = "::192.9.5.5", path = "/ipng"}},
  403. {"http://[::FFFF:129.144.52.38]:80/index.html",
  404. #url{abspath = "http://[::FFFF:129.144.52.38]:80/index.html",
  405. host_type = ipv6_address, port = 80, protocol = http,
  406. host = "::FFFF:129.144.52.38", path = "/index.html"}},
  407. {"http://[2010:836B:4179::836B:4179]",
  408. #url{abspath = "http://[2010:836B:4179::836B:4179]",
  409. host_type = ipv6_address, port = 80, protocol = http,
  410. host = "2010:836B:4179::836B:4179", path = "/"}}
  411. ],
  412. lists:foreach(
  413. fun({Url, Expected_result}) ->
  414. ?assertMatch(Expected_result, parse_url(Url))
  415. end, Urls).
  416. -endif.