Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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