Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

465 рядки
16 KiB

20 роки тому
17 роки тому
17 роки тому
11 роки тому
20 роки тому
17 роки тому
20 роки тому
20 роки тому
20 роки тому
17 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
20 роки тому
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 роки тому
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 роки тому
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 роки тому
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 роки тому
17 роки тому
17 роки тому
13 роки тому
13 роки тому
17 роки тому
14 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
17 роки тому
11 роки тому
17 роки тому
17 роки тому
17 роки тому
  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.