您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

1938 行
78 KiB

13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
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 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
13 年前
  1. %%%-------------------------------------------------------------------
  2. %%% File : ibrowse_http_client.erl
  3. %%% Author : Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  4. %%% Description : The name says it all
  5. %%%
  6. %%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi <chandrashekhar.mullaparthi@t-mobile.co.uk>
  7. %%%-------------------------------------------------------------------
  8. -module(ibrowse_http_client).
  9. -behaviour(gen_server).
  10. %%--------------------------------------------------------------------
  11. %% Include files
  12. %%--------------------------------------------------------------------
  13. %%--------------------------------------------------------------------
  14. %% External exports
  15. -export([
  16. start_link/1,
  17. start_link/2,
  18. start/1,
  19. start/2,
  20. stop/1,
  21. send_req/7
  22. ]).
  23. -ifdef(debug).
  24. -compile(export_all).
  25. -endif.
  26. %% gen_server callbacks
  27. -export([
  28. init/1,
  29. handle_call/3,
  30. handle_cast/2,
  31. handle_info/2,
  32. terminate/2,
  33. code_change/3
  34. ]).
  35. -include("ibrowse.hrl").
  36. -include_lib("kernel/include/inet.hrl").
  37. -record(state, {host, port, connect_timeout,
  38. inactivity_timer_ref,
  39. use_proxy = false, proxy_auth_digest,
  40. ssl_options = [], is_ssl = false, socket,
  41. proxy_tunnel_setup = false,
  42. tunnel_setup_queue = [],
  43. reqs=queue:new(), cur_req, status=idle, http_status_code,
  44. reply_buffer = <<>>, rep_buf_size=0, streamed_size = 0,
  45. recvd_headers=[],
  46. status_line, raw_headers,
  47. is_closing, content_length,
  48. deleted_crlf = false, transfer_encoding,
  49. chunk_size, chunk_size_buffer = <<>>,
  50. recvd_chunk_size, interim_reply_sent = false,
  51. lb_ets_tid, cur_pipeline_size = 0, prev_req_id
  52. }).
  53. -record(request, {url, method, options, from,
  54. stream_to, caller_controls_socket = false,
  55. caller_socket_options = [],
  56. req_id,
  57. stream_chunk_size,
  58. save_response_to_file = false,
  59. tmp_file_name, tmp_file_fd, preserve_chunked_encoding,
  60. response_format, timer_ref}).
  61. -import(ibrowse_lib, [
  62. get_value/2,
  63. get_value/3,
  64. do_trace/2
  65. ]).
  66. -define(DEFAULT_STREAM_CHUNK_SIZE, 1024*1024).
  67. -define(dec2hex(X), erlang:integer_to_list(X, 16)).
  68. %%====================================================================
  69. %% External functions
  70. %%====================================================================
  71. %%--------------------------------------------------------------------
  72. %% Function: start_link/0
  73. %% Description: Starts the server
  74. %%--------------------------------------------------------------------
  75. start(Args) ->
  76. start(Args, []).
  77. start(Args, Options) ->
  78. gen_server:start(?MODULE, Args, Options).
  79. start_link(Args) ->
  80. start_link(Args, []).
  81. start_link(Args, Options) ->
  82. gen_server:start_link(?MODULE, Args, Options).
  83. stop(Conn_pid) ->
  84. case catch gen_server:call(Conn_pid, stop) of
  85. {'EXIT', {timeout, _}} ->
  86. exit(Conn_pid, kill),
  87. ok;
  88. _ ->
  89. ok
  90. end.
  91. send_req(Conn_Pid, Url, Headers, Method, Body, Options, Timeout) ->
  92. gen_server:call(
  93. Conn_Pid,
  94. {send_req, {Url, Headers, Method, Body, Options, Timeout}}, Timeout).
  95. %%====================================================================
  96. %% Server functions
  97. %%====================================================================
  98. %%--------------------------------------------------------------------
  99. %% Function: init/1
  100. %% Description: Initiates the server
  101. %% Returns: {ok, State} |
  102. %% {ok, State, Timeout} |
  103. %% ignore |
  104. %% {stop, Reason}
  105. %%--------------------------------------------------------------------
  106. init({Lb_Tid, #url{host = Host, port = Port}, {SSLOptions, Is_ssl}}) ->
  107. State = #state{host = Host,
  108. port = Port,
  109. ssl_options = SSLOptions,
  110. is_ssl = Is_ssl,
  111. lb_ets_tid = Lb_Tid},
  112. put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]),
  113. put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)),
  114. {ok, set_inac_timer(State)};
  115. init(Url) when is_list(Url) ->
  116. case catch ibrowse_lib:parse_url(Url) of
  117. #url{protocol = Protocol} = Url_rec ->
  118. init({undefined, Url_rec, {[], Protocol == https}});
  119. {'EXIT', _} ->
  120. {error, invalid_url}
  121. end;
  122. init({Host, Port}) ->
  123. State = #state{host = Host,
  124. port = Port},
  125. put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]),
  126. put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)),
  127. {ok, set_inac_timer(State)}.
  128. %%--------------------------------------------------------------------
  129. %% Function: handle_call/3
  130. %% Description: Handling call messages
  131. %% Returns: {reply, Reply, State} |
  132. %% {reply, Reply, State, Timeout} |
  133. %% {noreply, State} |
  134. %% {noreply, State, Timeout} |
  135. %% {stop, Reason, Reply, State} | (terminate/2 is called)
  136. %% {stop, Reason, State} (terminate/2 is called)
  137. %%--------------------------------------------------------------------
  138. %% Received a request when the remote server has already sent us a
  139. %% Connection: Close header
  140. handle_call({send_req, _}, _From, #state{is_closing = true} = State) ->
  141. {reply, {error, connection_closing}, State};
  142. handle_call({send_req, {Url, Headers, Method, Body, Options, Timeout}},
  143. From, State) ->
  144. send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State);
  145. handle_call(stop, _From, State) ->
  146. do_close(State),
  147. do_error_reply(State, closing_on_request),
  148. {stop, normal, ok, State};
  149. handle_call(Request, _From, State) ->
  150. Reply = {unknown_request, Request},
  151. {reply, Reply, State}.
  152. %%--------------------------------------------------------------------
  153. %% Function: handle_cast/2
  154. %% Description: Handling cast messages
  155. %% Returns: {noreply, State} |
  156. %% {noreply, State, Timeout} |
  157. %% {stop, Reason, State} (terminate/2 is called)
  158. %%--------------------------------------------------------------------
  159. handle_cast(_Msg, State) ->
  160. {noreply, State}.
  161. %%--------------------------------------------------------------------
  162. %% Function: handle_info/2
  163. %% Description: Handling all non call/cast messages
  164. %% Returns: {noreply, State} |
  165. %% {noreply, State, Timeout} |
  166. %% {stop, Reason, State} (terminate/2 is called)
  167. %%--------------------------------------------------------------------
  168. handle_info({tcp, _Sock, Data}, #state{status = Status} = State) ->
  169. do_trace("Data recvd in state: ~p. Size: ~p. ~p~n~n", [Status, size(Data), Data]),
  170. handle_sock_data(Data, State);
  171. handle_info({ssl, _Sock, Data}, State) ->
  172. handle_sock_data(Data, State);
  173. handle_info({stream_next, Req_id}, #state{socket = Socket,
  174. cur_req = #request{req_id = Req_id}} = State) ->
  175. do_setopts(Socket, [{active, once}], State),
  176. {noreply, set_inac_timer(State)};
  177. handle_info({stream_next, _Req_id}, State) ->
  178. _Cur_req_id = case State#state.cur_req of
  179. #request{req_id = Cur} ->
  180. Cur;
  181. _ ->
  182. undefined
  183. end,
  184. {noreply, State};
  185. handle_info({stream_close, _Req_id}, State) ->
  186. shutting_down(State),
  187. do_close(State),
  188. do_error_reply(State, closing_on_request),
  189. {stop, normal, State};
  190. handle_info({tcp_closed, _Sock}, State) ->
  191. do_trace("TCP connection closed by peer!~n", []),
  192. handle_sock_closed(State),
  193. {stop, normal, State};
  194. handle_info({ssl_closed, _Sock}, State) ->
  195. do_trace("SSL connection closed by peer!~n", []),
  196. handle_sock_closed(State),
  197. {stop, normal, State};
  198. handle_info({tcp_error, _Sock, Reason}, State) ->
  199. do_trace("Error on connection to ~1000.p:~1000.p -> ~1000.p~n",
  200. [State#state.host, State#state.port, Reason]),
  201. handle_sock_closed(State),
  202. {stop, normal, State};
  203. handle_info({ssl_error, _Sock, Reason}, State) ->
  204. do_trace("Error on SSL connection to ~1000.p:~1000.p -> ~1000.p~n",
  205. [State#state.host, State#state.port, Reason]),
  206. handle_sock_closed(State),
  207. {stop, normal, State};
  208. handle_info({req_timedout, From}, State) ->
  209. case lists:keysearch(From, #request.from, queue:to_list(State#state.reqs)) of
  210. false ->
  211. {noreply, State};
  212. {value, #request{stream_to = StreamTo, req_id = ReqId}} ->
  213. catch StreamTo ! {ibrowse_async_response_timeout, ReqId},
  214. shutting_down(State),
  215. {stop, normal, State}
  216. end;
  217. handle_info(timeout, State) ->
  218. do_trace("Inactivity timeout triggered. Shutting down connection~n", []),
  219. shutting_down(State),
  220. do_error_reply(State, req_timedout),
  221. {stop, normal, State};
  222. handle_info({trace, Bool}, State) ->
  223. put(my_trace_flag, Bool),
  224. {noreply, State};
  225. handle_info(Info, State) ->
  226. io:format("Unknown message recvd for ~1000.p:~1000.p -> ~p~n",
  227. [State#state.host, State#state.port, Info]),
  228. io:format("Recvd unknown message ~p when in state: ~p~n", [Info, State]),
  229. {noreply, State}.
  230. %%--------------------------------------------------------------------
  231. %% Function: terminate/2
  232. %% Description: Shutdown the server
  233. %% Returns: any (ignored by gen_server)
  234. %%--------------------------------------------------------------------
  235. terminate(_Reason, State) ->
  236. do_close(State),
  237. ok.
  238. %%--------------------------------------------------------------------
  239. %% Func: code_change/3
  240. %% Purpose: Convert process state when code is changed
  241. %% Returns: {ok, NewState}
  242. %%--------------------------------------------------------------------
  243. code_change(_OldVsn, State, _Extra) ->
  244. {ok, State}.
  245. %%--------------------------------------------------------------------
  246. %%% Internal functions
  247. %%--------------------------------------------------------------------
  248. %%--------------------------------------------------------------------
  249. %% Handles data recvd on the socket
  250. %%--------------------------------------------------------------------
  251. handle_sock_data(Data, #state{status=idle}=State) ->
  252. do_trace("Data recvd on socket in state idle!. ~1000.p~n", [Data]),
  253. shutting_down(State),
  254. do_error_reply(State, data_in_status_idle),
  255. do_close(State),
  256. {stop, normal, State};
  257. handle_sock_data(Data, #state{status = get_header}=State) ->
  258. case parse_response(Data, State) of
  259. {error, _Reason} ->
  260. shutting_down(State),
  261. {stop, normal, State};
  262. #state{socket = Socket, status = Status, cur_req = CurReq} = State_1 ->
  263. case {Status, CurReq} of
  264. {get_header, #request{caller_controls_socket = true}} ->
  265. do_setopts(Socket, [{active, once}], State_1);
  266. _ ->
  267. active_once(State_1)
  268. end,
  269. {noreply, set_inac_timer(State_1)}
  270. end;
  271. handle_sock_data(Data, #state{status = get_body,
  272. socket = Socket,
  273. content_length = CL,
  274. http_status_code = StatCode,
  275. recvd_headers = Headers,
  276. chunk_size = CSz} = State) ->
  277. case (CL == undefined) and (CSz == undefined) of
  278. true ->
  279. case accumulate_response(Data, State) of
  280. {error, Reason} ->
  281. shutting_down(State),
  282. fail_pipelined_requests(State,
  283. {error, {Reason, {stat_code, StatCode}, Headers}}),
  284. {stop, normal, State};
  285. State_1 ->
  286. active_once(State_1),
  287. State_2 = set_inac_timer(State_1),
  288. {noreply, State_2}
  289. end;
  290. _ ->
  291. case parse_11_response(Data, State) of
  292. {error, Reason} ->
  293. shutting_down(State),
  294. fail_pipelined_requests(State,
  295. {error, {Reason, {stat_code, StatCode}, Headers}}),
  296. {stop, normal, State};
  297. #state{cur_req = #request{caller_controls_socket = Ccs},
  298. interim_reply_sent = Irs} = State_1 ->
  299. case Irs of
  300. true ->
  301. active_once(State_1);
  302. false when Ccs == true ->
  303. do_setopts(Socket, [{active, once}], State);
  304. false ->
  305. active_once(State_1)
  306. end,
  307. State_2 = State_1#state{interim_reply_sent = false},
  308. case Ccs of
  309. true ->
  310. cancel_timer(State_2#state.inactivity_timer_ref, {eat_message, timeout}),
  311. {noreply, State_2#state{inactivity_timer_ref = undefined}};
  312. _ ->
  313. {noreply, set_inac_timer(State_2)}
  314. end;
  315. State_1 ->
  316. active_once(State_1),
  317. State_2 = set_inac_timer(State_1),
  318. {noreply, State_2}
  319. end
  320. end.
  321. accumulate_response(Data,
  322. #state{
  323. cur_req = #request{save_response_to_file = Srtf,
  324. tmp_file_fd = undefined} = CurReq,
  325. http_status_code=[$2 | _]}=State) when Srtf /= false ->
  326. TmpFilename = make_tmp_filename(Srtf),
  327. Mode = file_mode(Srtf),
  328. case file:open(TmpFilename, [Mode, delayed_write, raw]) of
  329. {ok, Fd} ->
  330. accumulate_response(Data, State#state{
  331. cur_req = CurReq#request{
  332. tmp_file_fd = Fd,
  333. tmp_file_name = TmpFilename}});
  334. {error, Reason} ->
  335. {error, {file_open_error, Reason}}
  336. end;
  337. accumulate_response(Data, #state{cur_req = #request{save_response_to_file = Srtf,
  338. tmp_file_fd = Fd},
  339. transfer_encoding=chunked,
  340. reply_buffer = Reply_buf,
  341. http_status_code=[$2 | _]
  342. } = State) when Srtf /= false ->
  343. case file:write(Fd, [Reply_buf, Data]) of
  344. ok ->
  345. State#state{reply_buffer = <<>>};
  346. {error, Reason} ->
  347. {error, {file_write_error, Reason}}
  348. end;
  349. accumulate_response(Data, #state{cur_req = #request{save_response_to_file = Srtf,
  350. tmp_file_fd = Fd},
  351. reply_buffer = RepBuf,
  352. http_status_code=[$2 | _]
  353. } = State) when Srtf /= false ->
  354. case file:write(Fd, [RepBuf, Data]) of
  355. ok ->
  356. State#state{reply_buffer = <<>>};
  357. {error, Reason} ->
  358. {error, {file_write_error, Reason}}
  359. end;
  360. accumulate_response(Data, #state{reply_buffer = RepBuf,
  361. rep_buf_size = RepBufSize,
  362. streamed_size = Streamed_size,
  363. cur_req = CurReq}=State) ->
  364. #request{stream_to = StreamTo,
  365. req_id = ReqId,
  366. stream_chunk_size = Stream_chunk_size,
  367. response_format = Response_format,
  368. caller_controls_socket = Caller_controls_socket} = CurReq,
  369. RepBuf_1 = <<RepBuf/binary, Data/binary>>,
  370. New_data_size = RepBufSize - Streamed_size,
  371. case StreamTo of
  372. undefined ->
  373. State#state{reply_buffer = RepBuf_1};
  374. _ when Caller_controls_socket == true ->
  375. do_interim_reply(StreamTo, Response_format, ReqId, RepBuf_1),
  376. State#state{reply_buffer = <<>>,
  377. interim_reply_sent = true,
  378. streamed_size = Streamed_size + size(RepBuf_1)};
  379. _ when New_data_size >= Stream_chunk_size ->
  380. {Stream_chunk, Rem_data} = split_binary(RepBuf_1, Stream_chunk_size),
  381. do_interim_reply(StreamTo, Response_format, ReqId, Stream_chunk),
  382. State_1 = State#state{
  383. reply_buffer = <<>>,
  384. interim_reply_sent = true,
  385. streamed_size = Streamed_size + Stream_chunk_size},
  386. case Rem_data of
  387. <<>> ->
  388. State_1;
  389. _ ->
  390. accumulate_response(Rem_data, State_1)
  391. end;
  392. _ ->
  393. State#state{reply_buffer = RepBuf_1}
  394. end.
  395. make_tmp_filename(true) ->
  396. DownloadDir = ibrowse:get_config_value(download_dir, filename:absname("./")),
  397. {A,B,C} = now(),
  398. filename:join([DownloadDir,
  399. "ibrowse_tmp_file_"++
  400. integer_to_list(A) ++
  401. integer_to_list(B) ++
  402. integer_to_list(C)]);
  403. make_tmp_filename(File) when is_list(File) ->
  404. File;
  405. make_tmp_filename({append, File}) when is_list(File) ->
  406. File.
  407. file_mode({append, _File}) -> append;
  408. file_mode(_Srtf) -> write.
  409. %%--------------------------------------------------------------------
  410. %% Handles the case when the server closes the socket
  411. %%--------------------------------------------------------------------
  412. handle_sock_closed(#state{status=get_header} = State) ->
  413. shutting_down(State),
  414. do_error_reply(State, connection_closed);
  415. handle_sock_closed(#state{cur_req=undefined} = State) ->
  416. shutting_down(State);
  417. %% We check for IsClosing because this the server could have sent a
  418. %% Connection-Close header and has closed the socket to indicate end
  419. %% of response. There maybe requests pipelined which need a response.
  420. handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code = SC,
  421. is_closing = IsClosing,
  422. cur_req = #request{tmp_file_name=TmpFilename,
  423. tmp_file_fd=Fd} = CurReq,
  424. status = get_body,
  425. recvd_headers = Headers,
  426. status_line = Status_line,
  427. raw_headers = Raw_headers
  428. }=State) ->
  429. #request{from=From, stream_to=StreamTo, req_id=ReqId,
  430. response_format = Resp_format,
  431. options = Options} = CurReq,
  432. case IsClosing of
  433. true ->
  434. {_, Reqs_1} = queue:out(Reqs),
  435. Body = case TmpFilename of
  436. undefined ->
  437. Buf;
  438. _ ->
  439. ok = file:close(Fd),
  440. {file, TmpFilename}
  441. end,
  442. Reply = case get_value(give_raw_headers, Options, false) of
  443. true ->
  444. {ok, Status_line, Raw_headers, Body};
  445. false ->
  446. {ok, SC, Headers, Buf}
  447. end,
  448. State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
  449. ok = do_error_reply(State_1#state{reqs = Reqs_1}, connection_closed),
  450. State_1;
  451. _ ->
  452. ok = do_error_reply(State, connection_closed),
  453. State
  454. end.
  455. do_connect(Host, Port, Options, #state{is_ssl = true,
  456. use_proxy = false,
  457. ssl_options = SSLOptions},
  458. Timeout) ->
  459. ssl:connect(Host, Port, get_sock_options(Host, Options, SSLOptions), Timeout);
  460. do_connect(Host, Port, Options, _State, Timeout) ->
  461. Socks5Host = get_value(socks5_host, Options, undefined),
  462. case Socks5Host of
  463. undefined ->
  464. gen_tcp:connect(Host, Port, get_sock_options(Host, Options, []), Timeout);
  465. _ ->
  466. catch ibrowse_socks5:connect(Host, Port, Options)
  467. end.
  468. get_sock_options(Host, Options, SSLOptions) ->
  469. Caller_socket_options = get_value(socket_options, Options, []),
  470. Ipv6Options = case is_ipv6_host(Host) of
  471. true ->
  472. [inet6];
  473. false ->
  474. []
  475. end,
  476. Other_sock_options = filter_sock_options(SSLOptions ++ Caller_socket_options ++ Ipv6Options),
  477. case lists:keysearch(nodelay, 1, Other_sock_options) of
  478. false ->
  479. [{nodelay, true}, binary, {active, false} | Other_sock_options];
  480. {value, _} ->
  481. [binary, {active, false} | Other_sock_options]
  482. end.
  483. is_ipv6_host(Host) ->
  484. case inet_parse:address(Host) of
  485. {ok, {_, _, _, _, _, _, _, _}} ->
  486. true;
  487. {ok, {_, _, _, _}} ->
  488. false;
  489. _ ->
  490. case inet:gethostbyname(Host) of
  491. {ok, #hostent{h_addrtype = inet6}} ->
  492. true;
  493. _ ->
  494. false
  495. end
  496. end.
  497. %% We don't want the caller to specify certain options
  498. filter_sock_options(Opts) ->
  499. lists:filter(fun({active, _}) ->
  500. false;
  501. ({packet, _}) ->
  502. false;
  503. (list) ->
  504. false;
  505. (_) ->
  506. true
  507. end, Opts).
  508. do_send(Req, #state{socket = Sock,
  509. is_ssl = true,
  510. use_proxy = true,
  511. proxy_tunnel_setup = Pts}) when Pts /= done -> gen_tcp:send(Sock, Req);
  512. do_send(Req, #state{socket = Sock, is_ssl = true}) -> ssl:send(Sock, Req);
  513. do_send(Req, #state{socket = Sock, is_ssl = false}) -> gen_tcp:send(Sock, Req).
  514. %% @spec do_send_body(Sock::socket_descriptor(), Source::source_descriptor(), IsSSL::boolean()) -> ok | error()
  515. %% source_descriptor() = fun_arity_0 |
  516. %% {fun_arity_0} |
  517. %% {fun_arity_1, term()}
  518. %% error() = term()
  519. do_send_body(Source, State, TE) when is_function(Source) ->
  520. do_send_body({Source}, State, TE);
  521. do_send_body({Source}, State, TE) when is_function(Source) ->
  522. do_send_body1(Source, Source(), State, TE);
  523. do_send_body({Source, Source_state}, State, TE) when is_function(Source) ->
  524. do_send_body1(Source, Source(Source_state), State, TE);
  525. do_send_body(Body, State, _TE) ->
  526. do_send(Body, State).
  527. do_send_body1(Source, Resp, State, TE) ->
  528. case Resp of
  529. {ok, Data} when Data == []; Data == <<>> ->
  530. do_send_body({Source}, State, TE);
  531. {ok, Data} ->
  532. do_send(maybe_chunked_encode(Data, TE), State),
  533. do_send_body({Source}, State, TE);
  534. {ok, Data, New_source_state} when Data == []; Data == <<>> ->
  535. do_send_body({Source, New_source_state}, State, TE);
  536. {ok, Data, New_source_state} ->
  537. do_send(maybe_chunked_encode(Data, TE), State),
  538. do_send_body({Source, New_source_state}, State, TE);
  539. eof when TE == true ->
  540. do_send(<<"0\r\n\r\n">>, State),
  541. ok;
  542. eof ->
  543. ok;
  544. Err ->
  545. Err
  546. end.
  547. maybe_chunked_encode(Data, false) ->
  548. Data;
  549. maybe_chunked_encode(Data, true) ->
  550. [?dec2hex(iolist_size(Data)), "\r\n", Data, "\r\n"].
  551. do_close(#state{socket = undefined}) -> ok;
  552. do_close(#state{socket = Sock,
  553. is_ssl = true,
  554. use_proxy = true,
  555. proxy_tunnel_setup = Pts
  556. }) when Pts /= done -> catch gen_tcp:close(Sock);
  557. do_close(#state{socket = Sock, is_ssl = true}) -> catch ssl:close(Sock);
  558. do_close(#state{socket = Sock, is_ssl = false}) -> catch gen_tcp:close(Sock).
  559. active_once(#state{cur_req = #request{caller_controls_socket = true}}) ->
  560. ok;
  561. active_once(#state{socket = Socket} = State) ->
  562. do_setopts(Socket, [{active, once}], State).
  563. do_setopts(_Sock, [], _) -> ok;
  564. do_setopts(Sock, Opts, #state{is_ssl = true,
  565. use_proxy = true,
  566. proxy_tunnel_setup = Pts}
  567. ) when Pts /= done -> inet:setopts(Sock, Opts);
  568. do_setopts(Sock, Opts, #state{is_ssl = true}) -> ssl:setopts(Sock, Opts);
  569. do_setopts(Sock, Opts, _) -> inet:setopts(Sock, Opts).
  570. check_ssl_options(Options, State) ->
  571. case get_value(is_ssl, Options, false) of
  572. false ->
  573. State;
  574. true ->
  575. State#state{is_ssl=true, ssl_options=get_value(ssl_options, Options)}
  576. end.
  577. send_req_1(From,
  578. #url{host = Host,
  579. port = Port} = Url,
  580. Headers, Method, Body, Options, Timeout,
  581. #state{socket = undefined} = State) ->
  582. {Host_1, Port_1, State_1} =
  583. case get_value(proxy_host, Options, false) of
  584. false ->
  585. {Host, Port, State};
  586. PHost ->
  587. ProxyUser = get_value(proxy_user, Options, []),
  588. ProxyPassword = get_value(proxy_password, Options, []),
  589. Digest = http_auth_digest(ProxyUser, ProxyPassword),
  590. {PHost, get_value(proxy_port, Options, 80),
  591. State#state{use_proxy = true,
  592. proxy_auth_digest = Digest}}
  593. end,
  594. State_2 = check_ssl_options(Options, State_1),
  595. do_trace("Connecting...~n", []),
  596. Conn_timeout = get_value(connect_timeout, Options, Timeout),
  597. case do_connect(Host_1, Port_1, Options, State_2, Conn_timeout) of
  598. {ok, Sock} ->
  599. do_trace("Connected! Socket: ~1000.p~n", [Sock]),
  600. State_3 = State_2#state{socket = Sock,
  601. connect_timeout = Conn_timeout},
  602. send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State_3);
  603. Err ->
  604. shutting_down(State_2),
  605. do_trace("Error connecting. Reason: ~1000.p~n", [Err]),
  606. gen_server:reply(From, {error, {conn_failed, Err}}),
  607. {stop, normal, State_2}
  608. end;
  609. %% Send a CONNECT request.
  610. %% Wait for 200 OK
  611. %% Upgrade to SSL connection
  612. %% Then send request
  613. send_req_1(From,
  614. #url{
  615. host = Server_host,
  616. port = Server_port
  617. } = Url,
  618. Headers, Method, Body, Options, Timeout,
  619. #state{
  620. proxy_tunnel_setup = false,
  621. use_proxy = true,
  622. is_ssl = true} = State) ->
  623. Ref = case Timeout of
  624. infinity ->
  625. undefined;
  626. _ ->
  627. erlang:send_after(Timeout, self(), {req_timedout, From})
  628. end,
  629. NewReq = #request{
  630. method = connect,
  631. preserve_chunked_encoding = get_value(preserve_chunked_encoding, Options, false),
  632. options = Options,
  633. timer_ref = Ref
  634. },
  635. State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)},
  636. Pxy_auth_headers = maybe_modify_headers(Url, Method, Options, [], State_1),
  637. Path = [Server_host, $:, integer_to_list(Server_port)],
  638. {Req, Body_1} = make_request(connect, Pxy_auth_headers,
  639. Path, Path,
  640. [], Options, State_1, undefined),
  641. TE = is_chunked_encoding_specified(Options),
  642. trace_request(Req),
  643. case do_send(Req, State) of
  644. ok ->
  645. case do_send_body(Body_1, State_1, TE) of
  646. ok ->
  647. trace_request_body(Body_1),
  648. active_once(State_1),
  649. State_1_1 = inc_pipeline_counter(State_1),
  650. State_2 = State_1_1#state{status = get_header,
  651. cur_req = NewReq,
  652. proxy_tunnel_setup = in_progress,
  653. tunnel_setup_queue = [{From, Url, Headers, Method, Body, Options, Timeout}]},
  654. State_3 = set_inac_timer(State_2),
  655. {noreply, State_3};
  656. Err ->
  657. shutting_down(State_1),
  658. do_trace("Send failed... Reason: ~p~n", [Err]),
  659. gen_server:reply(From, {error, {send_failed, Err}}),
  660. {stop, normal, State_1}
  661. end;
  662. Err ->
  663. shutting_down(State_1),
  664. do_trace("Send failed... Reason: ~p~n", [Err]),
  665. gen_server:reply(From, {error, {send_failed, Err}}),
  666. {stop, normal, State_1}
  667. end;
  668. send_req_1(From, Url, Headers, Method, Body, Options, Timeout,
  669. #state{proxy_tunnel_setup = in_progress,
  670. tunnel_setup_queue = Q} = State) ->
  671. do_trace("Queued SSL request awaiting tunnel setup: ~n"
  672. "URL : ~s~n"
  673. "Method : ~p~n"
  674. "Headers : ~p~n", [Url, Method, Headers]),
  675. {noreply, State#state{tunnel_setup_queue = [{From, Url, Headers, Method, Body, Options, Timeout} | Q]}};
  676. send_req_1(From,
  677. #url{abspath = AbsPath,
  678. path = RelPath} = Url,
  679. Headers, Method, Body, Options, Timeout,
  680. #state{status = Status,
  681. socket = Socket} = State) ->
  682. cancel_timer(State#state.inactivity_timer_ref, {eat_message, timeout}),
  683. ReqId = make_req_id(),
  684. Resp_format = get_value(response_format, Options, list),
  685. Caller_socket_options = get_value(socket_options, Options, []),
  686. {StreamTo, Caller_controls_socket} =
  687. case get_value(stream_to, Options, undefined) of
  688. {Caller, once} when is_pid(Caller) or
  689. is_atom(Caller) ->
  690. Async_pid_rec = {{req_id_pid, ReqId}, self()},
  691. true = ets:insert(ibrowse_stream, Async_pid_rec),
  692. {Caller, true};
  693. undefined ->
  694. {undefined, false};
  695. Caller when is_pid(Caller) or
  696. is_atom(Caller) ->
  697. {Caller, false};
  698. Stream_to_inv ->
  699. exit({invalid_option, {stream_to, Stream_to_inv}})
  700. end,
  701. SaveResponseToFile = get_value(save_response_to_file, Options, false),
  702. Ref = case Timeout of
  703. infinity ->
  704. undefined;
  705. _ ->
  706. erlang:send_after(Timeout, self(), {req_timedout, From})
  707. end,
  708. NewReq = #request{url = Url,
  709. method = Method,
  710. stream_to = StreamTo,
  711. caller_controls_socket = Caller_controls_socket,
  712. caller_socket_options = Caller_socket_options,
  713. options = Options,
  714. req_id = ReqId,
  715. save_response_to_file = SaveResponseToFile,
  716. stream_chunk_size = get_stream_chunk_size(Options),
  717. response_format = Resp_format,
  718. from = From,
  719. preserve_chunked_encoding = get_value(preserve_chunked_encoding, Options, false),
  720. timer_ref = Ref
  721. },
  722. State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)},
  723. Headers_1 = maybe_modify_headers(Url, Method, Options, Headers, State_1),
  724. {Req, Body_1} = make_request(Method,
  725. Headers_1,
  726. AbsPath, RelPath, Body, Options, State_1,
  727. ReqId),
  728. trace_request(Req),
  729. do_setopts(Socket, Caller_socket_options, State_1),
  730. TE = is_chunked_encoding_specified(Options),
  731. case do_send(Req, State_1) of
  732. ok ->
  733. case do_send_body(Body_1, State_1, TE) of
  734. ok ->
  735. trace_request_body(Body_1),
  736. State_2 = inc_pipeline_counter(State_1),
  737. active_once(State_2),
  738. State_3 = case Status of
  739. idle ->
  740. State_2#state{status = get_header,
  741. cur_req = NewReq};
  742. _ ->
  743. State_2
  744. end,
  745. case StreamTo of
  746. undefined ->
  747. ok;
  748. _ ->
  749. gen_server:reply(From, {ibrowse_req_id, ReqId})
  750. end,
  751. State_4 = set_inac_timer(State_3),
  752. {noreply, State_4};
  753. Err ->
  754. shutting_down(State_1),
  755. do_trace("Send failed... Reason: ~p~n", [Err]),
  756. gen_server:reply(From, {error, {send_failed, Err}}),
  757. {stop, normal, State_1}
  758. end;
  759. Err ->
  760. shutting_down(State_1),
  761. do_trace("Send failed... Reason: ~p~n", [Err]),
  762. gen_server:reply(From, {error, {send_failed, Err}}),
  763. {stop, normal, State_1}
  764. end.
  765. maybe_modify_headers(#url{}, connect, _, Headers, State) ->
  766. add_proxy_auth_headers(State, Headers);
  767. maybe_modify_headers(#url{host = Host, port = Port} = Url,
  768. _Method,
  769. Options, Headers, State) ->
  770. case get_value(headers_as_is, Options, false) of
  771. false ->
  772. Headers_1 = add_auth_headers(Url, Options, Headers, State),
  773. HostHeaderValue = case lists:keysearch(host_header, 1, Options) of
  774. false ->
  775. case Port of
  776. 80 -> Host;
  777. 443 -> Host;
  778. _ -> [Host, ":", integer_to_list(Port)]
  779. end;
  780. {value, {_, Host_h_val}} ->
  781. Host_h_val
  782. end,
  783. [{"Host", HostHeaderValue} | Headers_1];
  784. true ->
  785. Headers
  786. end.
  787. add_auth_headers(#url{username = User,
  788. password = UPw},
  789. Options,
  790. Headers,
  791. State) ->
  792. Headers_1 = case User of
  793. undefined ->
  794. case get_value(basic_auth, Options, undefined) of
  795. undefined ->
  796. Headers;
  797. {U,P} ->
  798. [{"Authorization", ["Basic ", http_auth_digest(U, P)]} | Headers]
  799. end;
  800. _ ->
  801. [{"Authorization", ["Basic ", http_auth_digest(User, UPw)]} | Headers]
  802. end,
  803. add_proxy_auth_headers(State, Headers_1).
  804. add_proxy_auth_headers(#state{use_proxy = false}, Headers) ->
  805. Headers;
  806. add_proxy_auth_headers(#state{proxy_auth_digest = []}, Headers) ->
  807. Headers;
  808. add_proxy_auth_headers(#state{proxy_auth_digest = Auth_digest}, Headers) ->
  809. [{"Proxy-Authorization", ["Basic ", Auth_digest]} | Headers].
  810. http_auth_digest([], []) ->
  811. [];
  812. http_auth_digest(Username, Password) ->
  813. ibrowse_lib:encode_base64(Username ++ [$: | Password]).
  814. make_request(Method, Headers, AbsPath, RelPath, Body, Options,
  815. #state{use_proxy = UseProxy, is_ssl = Is_ssl}, ReqId) ->
  816. HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})),
  817. Fun1 = fun({X, Y}) when is_atom(X) ->
  818. {to_lower(atom_to_list(X)), X, Y};
  819. ({X, Y}) when is_list(X) ->
  820. {to_lower(X), X, Y}
  821. end,
  822. Headers_0 = [Fun1(X) || X <- Headers],
  823. Headers_1 =
  824. case lists:keysearch("content-length", 1, Headers_0) of
  825. false when (Body =:= [] orelse Body =:= <<>>) andalso
  826. (Method =:= post orelse Method =:= put) ->
  827. [{"content-length", "Content-Length", "0"} | Headers_0];
  828. false when is_binary(Body) orelse is_list(Body) ->
  829. [{"content-length", "Content-Length", integer_to_list(iolist_size(Body))} | Headers_0];
  830. _ ->
  831. %% Content-Length is already specified or Body is a
  832. %% function or function/state pair
  833. Headers_0
  834. end,
  835. {Headers_2, Body_1} =
  836. case is_chunked_encoding_specified(Options) of
  837. false ->
  838. {[{Y, Z} || {_, Y, Z} <- Headers_1], Body};
  839. true ->
  840. Chunk_size_1 = case get_value(transfer_encoding, Options) of
  841. chunked ->
  842. 5120;
  843. {chunked, Chunk_size} ->
  844. Chunk_size
  845. end,
  846. {[{Y, Z} || {X, Y, Z} <- Headers_1,
  847. X /= "content-length"] ++
  848. [{"Transfer-Encoding", "chunked"}],
  849. chunk_request_body(Body, Chunk_size_1)}
  850. end,
  851. Headers_3 = case lists:member({include_ibrowse_req_id, true}, Options) of
  852. true ->
  853. [{"x-ibrowse-request-id", io_lib:format("~1000.p",[ReqId])} | Headers_2];
  854. false ->
  855. Headers_2
  856. end,
  857. Headers_4 = cons_headers(Headers_3),
  858. Uri = case get_value(use_absolute_uri, Options, false) or UseProxy of
  859. true ->
  860. case Is_ssl of
  861. true ->
  862. RelPath;
  863. false ->
  864. AbsPath
  865. end;
  866. false ->
  867. RelPath
  868. end,
  869. {[method(Method), " ", Uri, " ", HttpVsn, crnl(), Headers_4, crnl()], Body_1}.
  870. is_chunked_encoding_specified(Options) ->
  871. case get_value(transfer_encoding, Options, false) of
  872. false ->
  873. false;
  874. {chunked, _} ->
  875. true;
  876. chunked ->
  877. true
  878. end.
  879. http_vsn_string({0,9}) -> "HTTP/0.9";
  880. http_vsn_string({1,0}) -> "HTTP/1.0";
  881. http_vsn_string({1,1}) -> "HTTP/1.1".
  882. cons_headers(Headers) ->
  883. cons_headers(Headers, []).
  884. cons_headers([], Acc) ->
  885. encode_headers(Acc);
  886. cons_headers([{basic_auth, {U,P}} | T], Acc) ->
  887. cons_headers(T, [{"Authorization",
  888. ["Basic ", ibrowse_lib:encode_base64(U++":"++P)]} | Acc]);
  889. cons_headers([{cookie, Cookie} | T], Acc) ->
  890. cons_headers(T, [{"Cookie", Cookie} | Acc]);
  891. cons_headers([{content_length, L} | T], Acc) ->
  892. cons_headers(T, [{"Content-Length", L} | Acc]);
  893. cons_headers([{content_type, L} | T], Acc) ->
  894. cons_headers(T, [{"Content-Type", L} | Acc]);
  895. cons_headers([H | T], Acc) ->
  896. cons_headers(T, [H | Acc]).
  897. encode_headers(L) ->
  898. encode_headers(L, []).
  899. encode_headers([{http_vsn, _Val} | T], Acc) ->
  900. encode_headers(T, Acc);
  901. encode_headers([{Name,Val} | T], Acc) when is_list(Name) ->
  902. encode_headers(T, [[Name, ": ", fmt_val(Val), crnl()] | Acc]);
  903. encode_headers([{Name,Val} | T], Acc) when is_atom(Name) ->
  904. encode_headers(T, [[atom_to_list(Name), ": ", fmt_val(Val), crnl()] | Acc]);
  905. encode_headers([], Acc) ->
  906. lists:reverse(Acc).
  907. chunk_request_body(Body, _ChunkSize) when is_tuple(Body) orelse
  908. is_function(Body) ->
  909. Body;
  910. chunk_request_body(Body, ChunkSize) ->
  911. chunk_request_body(Body, ChunkSize, []).
  912. chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
  913. LastChunk = "0\r\n",
  914. lists:reverse(["\r\n", LastChunk | Acc]);
  915. chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
  916. size(Body) >= ChunkSize ->
  917. <<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
  918. Chunk = [?dec2hex(ChunkSize),"\r\n",
  919. ChunkBody, "\r\n"],
  920. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  921. chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
  922. BodySize = size(Body),
  923. Chunk = [?dec2hex(BodySize),"\r\n",
  924. Body, "\r\n"],
  925. LastChunk = "0\r\n",
  926. lists:reverse(["\r\n", LastChunk, Chunk | Acc]);
  927. chunk_request_body(Body, ChunkSize, Acc) when length(Body) >= ChunkSize ->
  928. {ChunkBody, Rest} = split_list_at(Body, ChunkSize),
  929. Chunk = [?dec2hex(ChunkSize),"\r\n",
  930. ChunkBody, "\r\n"],
  931. chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
  932. chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) ->
  933. BodySize = length(Body),
  934. Chunk = [?dec2hex(BodySize),"\r\n",
  935. Body, "\r\n"],
  936. LastChunk = "0\r\n",
  937. lists:reverse(["\r\n", LastChunk, Chunk | Acc]).
  938. parse_response(<<>>, #state{cur_req = undefined}=State) ->
  939. State#state{status = idle};
  940. parse_response(Data, #state{cur_req = undefined}) ->
  941. do_trace("Data left to process when no pending request. ~1000.p~n", [Data]),
  942. {error, data_in_status_idle};
  943. parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
  944. cur_req = CurReq} = State) ->
  945. #request{from=From, stream_to=StreamTo, req_id=ReqId,
  946. method=Method, response_format = Resp_format,
  947. options = Options, timer_ref = T_ref
  948. } = CurReq,
  949. MaxHeaderSize = ibrowse:get_config_value(max_headers_size, infinity),
  950. case scan_header(Acc, Data) of
  951. {yes, Headers, Data_1} ->
  952. do_trace("Recvd Header Data -> ~s~n----~n", [Headers]),
  953. do_trace("Recvd headers~n--- Headers Begin ---~n~s~n--- Headers End ---~n~n", [Headers]),
  954. {HttpVsn, StatCode, Headers_1, Status_line, Raw_headers} = parse_headers(Headers),
  955. do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Headers_1]),
  956. LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1],
  957. ConnClose = to_lower(get_value("connection", LCHeaders, "false")),
  958. IsClosing = is_connection_closing(HttpVsn, ConnClose),
  959. State_0 = case IsClosing of
  960. true ->
  961. shutting_down(State),
  962. State#state{is_closing = IsClosing};
  963. false ->
  964. State
  965. end,
  966. Give_raw_headers = get_value(give_raw_headers, Options, false),
  967. State_1 = case Give_raw_headers of
  968. true ->
  969. State_0#state{recvd_headers=Headers_1, status=get_body,
  970. reply_buffer = <<>>,
  971. status_line = Status_line,
  972. raw_headers = Raw_headers,
  973. http_status_code=StatCode};
  974. false ->
  975. State_0#state{recvd_headers=Headers_1, status=get_body,
  976. reply_buffer = <<>>,
  977. http_status_code=StatCode}
  978. end,
  979. put(conn_close, ConnClose),
  980. TransferEncodings = to_lower(get_value("transfer-encoding", LCHeaders, "false")),
  981. IsChunked = lists:any(fun(Enc) -> string:strip(Enc) =:= "chunked" end,
  982. string:tokens(TransferEncodings, ",")),
  983. Head_response_with_body = lists:member({workaround, head_response_with_body}, Options),
  984. case get_value("content-length", LCHeaders, undefined) of
  985. _ when Method == connect,
  986. hd(StatCode) == $2 ->
  987. {_, Reqs_1} = queue:out(Reqs),
  988. cancel_timer(T_ref),
  989. upgrade_to_ssl(set_cur_request(State_0#state{reqs = Reqs_1,
  990. recvd_headers = [],
  991. status = idle
  992. }));
  993. _ when Method == connect ->
  994. {_, Reqs_1} = queue:out(Reqs),
  995. do_error_reply(State#state{reqs = Reqs_1},
  996. {error, proxy_tunnel_failed}),
  997. {error, proxy_tunnel_failed};
  998. _ when Method =:= head,
  999. Head_response_with_body =:= false ->
  1000. %% This (HEAD response with body) is not supposed
  1001. %% to happen, but it does. An Apache server was
  1002. %% observed to send an "empty" body, but in a
  1003. %% Chunked-Transfer-Encoding way, which meant
  1004. %% there was still a body. Issue #67 on Github
  1005. {_, Reqs_1} = queue:out(Reqs),
  1006. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1007. State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format,
  1008. {ok, StatCode, Headers_1, []}),
  1009. cancel_timer(T_ref, {eat_message, {req_timedout, From}}),
  1010. State_2 = reset_state(State_1_1),
  1011. State_3 = set_cur_request(State_2#state{reqs = Reqs_1}),
  1012. parse_response(Data_1, State_3);
  1013. _ when hd(StatCode) =:= $1 ->
  1014. %% No message body is expected. Server may send
  1015. %% one or more 1XX responses before a proper
  1016. %% response.
  1017. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1018. do_trace("Recvd a status code of ~p. Ignoring and waiting for a proper response~n", [StatCode]),
  1019. parse_response(Data_1, State_1#state{recvd_headers = [],
  1020. status = get_header});
  1021. _ when StatCode =:= "204";
  1022. StatCode =:= "304" ->
  1023. %% No message body is expected for these Status Codes.
  1024. %% RFC2616 - Sec 4.4
  1025. {_, Reqs_1} = queue:out(Reqs),
  1026. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1027. State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format,
  1028. {ok, StatCode, Headers_1, []}),
  1029. cancel_timer(T_ref, {eat_message, {req_timedout, From}}),
  1030. State_2 = reset_state(State_1_1),
  1031. State_3 = set_cur_request(State_2#state{reqs = Reqs_1}),
  1032. parse_response(Data_1, State_3);
  1033. _ when IsChunked ->
  1034. do_trace("Chunked encoding detected...~n",[]),
  1035. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1036. case parse_11_response(Data_1, State_1#state{transfer_encoding=chunked,
  1037. chunk_size=chunk_start,
  1038. reply_buffer = <<>>}) of
  1039. {error, Reason} ->
  1040. fail_pipelined_requests(State_1,
  1041. {error, {Reason,
  1042. {stat_code, StatCode}, Headers_1}}),
  1043. {error, Reason};
  1044. State_2 ->
  1045. State_2
  1046. end;
  1047. undefined when HttpVsn =:= "HTTP/1.0";
  1048. ConnClose =:= "close" ->
  1049. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1050. State_1#state{reply_buffer = Data_1};
  1051. undefined ->
  1052. fail_pipelined_requests(State_1,
  1053. {error, {content_length_undefined,
  1054. {stat_code, StatCode}, Headers}}),
  1055. {error, content_length_undefined};
  1056. V ->
  1057. case catch list_to_integer(V) of
  1058. V_1 when is_integer(V_1), V_1 >= 0 ->
  1059. send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
  1060. do_trace("Recvd Content-Length of ~p~n", [V_1]),
  1061. State_2 = State_1#state{rep_buf_size=0,
  1062. reply_buffer = <<>>,
  1063. content_length=V_1},
  1064. case parse_11_response(Data_1, State_2) of
  1065. {error, Reason} ->
  1066. fail_pipelined_requests(State_1,
  1067. {error, {Reason,
  1068. {stat_code, StatCode}, Headers_1}}),
  1069. {error, Reason};
  1070. State_3 ->
  1071. State_3
  1072. end;
  1073. _ ->
  1074. fail_pipelined_requests(State_1,
  1075. {error, {content_length_undefined,
  1076. {stat_code, StatCode}, Headers}}),
  1077. {error, content_length_undefined}
  1078. end
  1079. end;
  1080. {no, Acc_1} when MaxHeaderSize == infinity ->
  1081. State#state{reply_buffer = Acc_1};
  1082. {no, Acc_1} when size(Acc_1) < MaxHeaderSize ->
  1083. State#state{reply_buffer = Acc_1};
  1084. {no, _Acc_1} ->
  1085. fail_pipelined_requests(State, {error, max_headers_size_exceeded}),
  1086. {error, max_headers_size_exceeded}
  1087. end.
  1088. upgrade_to_ssl(#state{socket = Socket,
  1089. connect_timeout = Conn_timeout,
  1090. ssl_options = Ssl_options,
  1091. tunnel_setup_queue = Q} = State) ->
  1092. case ssl:connect(Socket, Ssl_options, Conn_timeout) of
  1093. {ok, Ssl_socket} ->
  1094. do_trace("Upgraded to SSL socket!!~n", []),
  1095. State_1 = State#state{socket = Ssl_socket,
  1096. proxy_tunnel_setup = done},
  1097. send_queued_requests(lists:reverse(Q), State_1);
  1098. Err ->
  1099. do_trace("Upgrade to SSL socket failed. Reson: ~p~n", [Err]),
  1100. do_error_reply(State, {error, {send_failed, Err}}),
  1101. {error, send_failed}
  1102. end.
  1103. send_queued_requests([], State) ->
  1104. do_trace("Sent all queued requests via SSL connection~n", []),
  1105. State#state{tunnel_setup_queue = []};
  1106. send_queued_requests([{From, Url, Headers, Method, Body, Options, Timeout} | Q],
  1107. State) ->
  1108. case send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State) of
  1109. {noreply, State_1} ->
  1110. send_queued_requests(Q, State_1);
  1111. Err ->
  1112. do_trace("Error sending queued SSL request: ~n"
  1113. "URL : ~s~n"
  1114. "Method : ~p~n"
  1115. "Headers : ~p~n", [Url, Method, Headers]),
  1116. do_error_reply(State, {error, {send_failed, Err}}),
  1117. {error, send_failed}
  1118. end.
  1119. is_connection_closing("HTTP/0.9", _) -> true;
  1120. is_connection_closing(_, "close") -> true;
  1121. is_connection_closing("HTTP/1.0", "false") -> true;
  1122. is_connection_closing(_, _) -> false.
  1123. %% This clause determines the chunk size when given data from the beginning of the chunk
  1124. parse_11_response(DataRecvd,
  1125. #state{transfer_encoding = chunked,
  1126. chunk_size = chunk_start,
  1127. chunk_size_buffer = Chunk_sz_buf
  1128. } = State) ->
  1129. case scan_crlf(Chunk_sz_buf, DataRecvd) of
  1130. {yes, ChunkHeader, Data_1} ->
  1131. State_1 = maybe_accumulate_ce_data(State, <<ChunkHeader/binary, $\r, $\n>>),
  1132. ChunkSize = parse_chunk_header(ChunkHeader),
  1133. %%
  1134. %% Do we have to preserve the chunk encoding when
  1135. %% streaming? NO. This should be transparent to the client
  1136. %% process. Chunked encoding was only introduced to make
  1137. %% it efficient for the server.
  1138. %%
  1139. RemLen = size(Data_1),
  1140. do_trace("Determined chunk size: ~p. Already recvd: ~p~n",
  1141. [ChunkSize, RemLen]),
  1142. parse_11_response(Data_1, State_1#state{chunk_size_buffer = <<>>,
  1143. deleted_crlf = true,
  1144. recvd_chunk_size = 0,
  1145. chunk_size = ChunkSize});
  1146. {no, Data_1} ->
  1147. State#state{chunk_size_buffer = Data_1}
  1148. end;
  1149. %% This clause is to remove the CRLF between two chunks
  1150. %%
  1151. parse_11_response(DataRecvd,
  1152. #state{transfer_encoding = chunked,
  1153. chunk_size = tbd,
  1154. chunk_size_buffer = Buf
  1155. } = State) ->
  1156. case scan_crlf(Buf, DataRecvd) of
  1157. {yes, _, NextChunk} ->
  1158. State_1 = maybe_accumulate_ce_data(State, <<$\r, $\n>>),
  1159. State_2 = State_1#state{chunk_size = chunk_start,
  1160. chunk_size_buffer = <<>>,
  1161. deleted_crlf = true},
  1162. parse_11_response(NextChunk, State_2);
  1163. {no, Data_1} ->
  1164. State#state{chunk_size_buffer = Data_1}
  1165. end;
  1166. %% This clause deals with the end of a chunked transfer. ibrowse does
  1167. %% not support Trailers in the Chunked Transfer encoding. Any trailer
  1168. %% received is silently discarded.
  1169. parse_11_response(DataRecvd,
  1170. #state{transfer_encoding = chunked, chunk_size = 0,
  1171. cur_req = CurReq,
  1172. deleted_crlf = DelCrlf,
  1173. chunk_size_buffer = Trailer,
  1174. reqs = Reqs} = State) ->
  1175. do_trace("Detected end of chunked transfer...~n", []),
  1176. DataRecvd_1 = case DelCrlf of
  1177. false ->
  1178. DataRecvd;
  1179. true ->
  1180. <<$\r, $\n, DataRecvd/binary>>
  1181. end,
  1182. case scan_header(Trailer, DataRecvd_1) of
  1183. {yes, TEHeaders, Rem} ->
  1184. {_, Reqs_1} = queue:out(Reqs),
  1185. State_1 = maybe_accumulate_ce_data(State, <<TEHeaders/binary, $\r, $\n>>),
  1186. State_2 = handle_response(CurReq,
  1187. State_1#state{reqs = Reqs_1}),
  1188. parse_response(Rem, reset_state(State_2));
  1189. {no, Rem} ->
  1190. accumulate_response(<<>>, State#state{chunk_size_buffer = Rem, deleted_crlf = false})
  1191. end;
  1192. %% This clause extracts a chunk, given the size.
  1193. parse_11_response(DataRecvd,
  1194. #state{transfer_encoding = chunked,
  1195. chunk_size = CSz,
  1196. recvd_chunk_size = Recvd_csz,
  1197. rep_buf_size = RepBufSz} = State) ->
  1198. NeedBytes = CSz - Recvd_csz,
  1199. DataLen = size(DataRecvd),
  1200. do_trace("Recvd more data: size: ~p. NeedBytes: ~p~n", [DataLen, NeedBytes]),
  1201. case DataLen >= NeedBytes of
  1202. true ->
  1203. {RemChunk, RemData} = split_binary(DataRecvd, NeedBytes),
  1204. do_trace("Recvd another chunk...~p~n", [RemChunk]),
  1205. do_trace("RemData -> ~p~n", [RemData]),
  1206. case accumulate_response(RemChunk, State) of
  1207. {error, Reason} ->
  1208. do_trace("Error accumulating response --> ~p~n", [Reason]),
  1209. {error, Reason};
  1210. #state{} = State_1 ->
  1211. State_2 = State_1#state{chunk_size=tbd},
  1212. parse_11_response(RemData, State_2)
  1213. end;
  1214. false ->
  1215. accumulate_response(DataRecvd,
  1216. State#state{rep_buf_size = RepBufSz + DataLen,
  1217. recvd_chunk_size = Recvd_csz + DataLen})
  1218. end;
  1219. %% This clause to extract the body when Content-Length is specified
  1220. parse_11_response(DataRecvd,
  1221. #state{content_length=CL, rep_buf_size=RepBufSz,
  1222. reqs=Reqs}=State) ->
  1223. NeedBytes = CL - RepBufSz,
  1224. DataLen = size(DataRecvd),
  1225. case DataLen >= NeedBytes of
  1226. true ->
  1227. {RemBody, Rem} = split_binary(DataRecvd, NeedBytes),
  1228. {_, Reqs_1} = queue:out(Reqs),
  1229. State_1 = accumulate_response(RemBody, State),
  1230. State_2 = handle_response(State_1#state.cur_req, State_1#state{reqs=Reqs_1}),
  1231. State_3 = reset_state(State_2),
  1232. parse_response(Rem, State_3);
  1233. false ->
  1234. accumulate_response(DataRecvd, State#state{rep_buf_size = (RepBufSz+DataLen)})
  1235. end.
  1236. maybe_accumulate_ce_data(#state{cur_req = #request{preserve_chunked_encoding = false}} = State, _) ->
  1237. State;
  1238. maybe_accumulate_ce_data(State, Data) ->
  1239. accumulate_response(Data, State).
  1240. handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
  1241. response_format = Resp_format,
  1242. save_response_to_file = SaveResponseToFile,
  1243. tmp_file_name = TmpFilename,
  1244. tmp_file_fd = Fd,
  1245. options = Options,
  1246. timer_ref = ReqTimer
  1247. },
  1248. #state{http_status_code = SCode,
  1249. status_line = Status_line,
  1250. raw_headers = Raw_headers,
  1251. reply_buffer = RepBuf,
  1252. recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false ->
  1253. Body = RepBuf,
  1254. case Fd of
  1255. undefined ->
  1256. ok;
  1257. _ ->
  1258. ok = file:close(Fd)
  1259. end,
  1260. ResponseBody = case TmpFilename of
  1261. undefined ->
  1262. Body;
  1263. _ ->
  1264. {file, TmpFilename}
  1265. end,
  1266. {Resp_headers_1, Raw_headers_1} = maybe_add_custom_headers(RespHeaders, Raw_headers, Options),
  1267. Reply = case get_value(give_raw_headers, Options, false) of
  1268. true ->
  1269. {ok, Status_line, Raw_headers_1, ResponseBody};
  1270. false ->
  1271. {ok, SCode, Resp_headers_1, ResponseBody}
  1272. end,
  1273. State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
  1274. cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}),
  1275. set_cur_request(State_1);
  1276. handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
  1277. response_format = Resp_format,
  1278. options = Options, timer_ref = ReqTimer},
  1279. #state{http_status_code = SCode,
  1280. status_line = Status_line,
  1281. raw_headers = Raw_headers,
  1282. recvd_headers = Resp_headers,
  1283. reply_buffer = RepBuf
  1284. } = State) ->
  1285. Body = RepBuf,
  1286. {Resp_headers_1, Raw_headers_1} = maybe_add_custom_headers(Resp_headers, Raw_headers, Options),
  1287. Reply = case get_value(give_raw_headers, Options, false) of
  1288. true ->
  1289. {ok, Status_line, Raw_headers_1, Body};
  1290. false ->
  1291. {ok, SCode, Resp_headers_1, Body}
  1292. end,
  1293. State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
  1294. cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}),
  1295. set_cur_request(State_1).
  1296. reset_state(State) ->
  1297. State#state{status = get_header,
  1298. rep_buf_size = 0,
  1299. streamed_size = 0,
  1300. content_length = undefined,
  1301. reply_buffer = <<>>,
  1302. chunk_size_buffer = <<>>,
  1303. recvd_headers = [],
  1304. status_line = undefined,
  1305. raw_headers = undefined,
  1306. deleted_crlf = false,
  1307. http_status_code = undefined,
  1308. chunk_size = undefined,
  1309. transfer_encoding = undefined
  1310. }.
  1311. set_cur_request(#state{reqs = Reqs, socket = Socket} = State) ->
  1312. case queue:peek(Reqs) of
  1313. empty ->
  1314. State#state{cur_req = undefined};
  1315. {value, #request{caller_controls_socket = Ccs} = NextReq} ->
  1316. case Ccs of
  1317. true ->
  1318. do_setopts(Socket, [{active, once}], State);
  1319. _ ->
  1320. ok
  1321. end,
  1322. State#state{cur_req = NextReq}
  1323. end.
  1324. parse_headers(Headers) ->
  1325. case scan_crlf(Headers) of
  1326. {yes, StatusLine, T} ->
  1327. parse_headers(StatusLine, T);
  1328. {no, StatusLine} ->
  1329. parse_headers(StatusLine, <<>>)
  1330. end.
  1331. parse_headers(StatusLine, Headers) ->
  1332. Headers_1 = parse_headers_1(Headers),
  1333. case parse_status_line(StatusLine) of
  1334. {ok, HttpVsn, StatCode, _Msg} ->
  1335. put(http_prot_vsn, HttpVsn),
  1336. {HttpVsn, StatCode, Headers_1, StatusLine, Headers};
  1337. _ -> %% A HTTP 0.9 response?
  1338. put(http_prot_vsn, "HTTP/0.9"),
  1339. {"HTTP/0.9", undefined, Headers, StatusLine, Headers}
  1340. end.
  1341. % From RFC 2616
  1342. %
  1343. % HTTP/1.1 header field values can be folded onto multiple lines if
  1344. % the continuation line begins with a space or horizontal tab. All
  1345. % linear white space, including folding, has the same semantics as
  1346. % SP. A recipient MAY replace any linear white space with a single
  1347. % SP before interpreting the field value or forwarding the message
  1348. % downstream.
  1349. parse_headers_1(B) when is_binary(B) ->
  1350. parse_headers_1(binary_to_list(B));
  1351. parse_headers_1(String) ->
  1352. parse_headers_1(String, [], []).
  1353. parse_headers_1([$\n, H |T], [$\r | L], Acc) when H =:= 32;
  1354. H =:= $\t ->
  1355. parse_headers_1(lists:dropwhile(fun(X) ->
  1356. is_whitespace(X)
  1357. end, T), [32 | L], Acc);
  1358. parse_headers_1([$\n, H |T], L, Acc) when H =:= 32;
  1359. H =:= $\t ->
  1360. parse_headers_1(lists:dropwhile(fun(X) ->
  1361. is_whitespace(X)
  1362. end, T), [32 | L], Acc);
  1363. parse_headers_1([$\n|T], [$\r | L], Acc) ->
  1364. case parse_header(lists:reverse(L)) of
  1365. invalid ->
  1366. parse_headers_1(T, [], Acc);
  1367. NewHeader ->
  1368. parse_headers_1(T, [], [NewHeader | Acc])
  1369. end;
  1370. parse_headers_1([$\n|T], L, Acc) ->
  1371. case parse_header(lists:reverse(L)) of
  1372. invalid ->
  1373. parse_headers_1(T, [], Acc);
  1374. NewHeader ->
  1375. parse_headers_1(T, [], [NewHeader | Acc])
  1376. end;
  1377. parse_headers_1([H|T], L, Acc) ->
  1378. parse_headers_1(T, [H|L], Acc);
  1379. parse_headers_1([], [], Acc) ->
  1380. lists:reverse(Acc);
  1381. parse_headers_1([], L, Acc) ->
  1382. Acc_1 = case parse_header(lists:reverse(L)) of
  1383. invalid ->
  1384. Acc;
  1385. NewHeader ->
  1386. [NewHeader | Acc]
  1387. end,
  1388. lists:reverse(Acc_1).
  1389. parse_status_line(Line) when is_binary(Line) ->
  1390. parse_status_line(binary_to_list(Line));
  1391. parse_status_line(Line) ->
  1392. parse_status_line(Line, get_prot_vsn, [], []).
  1393. parse_status_line([32 | T], get_prot_vsn, ProtVsn, StatCode) ->
  1394. parse_status_line(T, get_status_code, ProtVsn, StatCode);
  1395. parse_status_line([32 | T], get_status_code, ProtVsn, StatCode) ->
  1396. {ok, lists:reverse(ProtVsn), lists:reverse(StatCode), T};
  1397. parse_status_line([], get_status_code, ProtVsn, StatCode) ->
  1398. {ok, lists:reverse(ProtVsn), lists:reverse(StatCode), []};
  1399. parse_status_line([H | T], get_prot_vsn, ProtVsn, StatCode) ->
  1400. parse_status_line(T, get_prot_vsn, [H|ProtVsn], StatCode);
  1401. parse_status_line([H | T], get_status_code, ProtVsn, StatCode) ->
  1402. parse_status_line(T, get_status_code, ProtVsn, [H | StatCode]);
  1403. parse_status_line([], _, _, _) ->
  1404. http_09.
  1405. parse_header(L) ->
  1406. parse_header(L, []).
  1407. parse_header([$: | V], Acc) ->
  1408. {lists:reverse(Acc), string:strip(V)};
  1409. parse_header([H | T], Acc) ->
  1410. parse_header(T, [H | Acc]);
  1411. parse_header([], _) ->
  1412. invalid.
  1413. scan_header(Bin) ->
  1414. case get_crlf_crlf_pos(Bin, 0) of
  1415. {yes, Pos} ->
  1416. {Headers, <<_:4/binary, Body/binary>>} = split_binary(Bin, Pos),
  1417. {yes, Headers, Body};
  1418. {yes_dodgy, Pos} ->
  1419. {Headers, <<_:2/binary, Body/binary>>} = split_binary(Bin, Pos),
  1420. {yes, Headers, Body};
  1421. no ->
  1422. {no, Bin}
  1423. end.
  1424. scan_header(Bin1, Bin2) when size(Bin1) < 4 ->
  1425. scan_header(<<Bin1/binary, Bin2/binary>>);
  1426. scan_header(Bin1, <<>>) ->
  1427. scan_header(Bin1);
  1428. scan_header(Bin1, Bin2) ->
  1429. Bin1_already_scanned_size = size(Bin1) - 4,
  1430. <<Headers_prefix:Bin1_already_scanned_size/binary, Rest/binary>> = Bin1,
  1431. Bin_to_scan = <<Rest/binary, Bin2/binary>>,
  1432. case get_crlf_crlf_pos(Bin_to_scan, 0) of
  1433. {yes, Pos} ->
  1434. {Headers_suffix, <<_:4/binary, Body/binary>>} = split_binary(Bin_to_scan, Pos),
  1435. {yes, <<Headers_prefix/binary, Headers_suffix/binary>>, Body};
  1436. {yes_dodgy, Pos} ->
  1437. {Headers_suffix, <<_:2/binary, Body/binary>>} = split_binary(Bin_to_scan, Pos),
  1438. {yes, <<Headers_prefix/binary, Headers_suffix/binary>>, Body};
  1439. no ->
  1440. {no, <<Bin1/binary, Bin2/binary>>}
  1441. end.
  1442. get_crlf_crlf_pos(<<$\r, $\n, $\r, $\n, _/binary>>, Pos) -> {yes, Pos};
  1443. get_crlf_crlf_pos(<<$\n, $\n, _/binary>>, Pos) -> {yes_dodgy, Pos};
  1444. get_crlf_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_crlf_pos(Rest, Pos + 1);
  1445. get_crlf_crlf_pos(<<>>, _) -> no.
  1446. scan_crlf(Bin) ->
  1447. case get_crlf_pos(Bin) of
  1448. {yes, Offset, Pos} ->
  1449. {Prefix, <<_:Offset/binary, Suffix/binary>>} = split_binary(Bin, Pos),
  1450. {yes, Prefix, Suffix};
  1451. no ->
  1452. {no, Bin}
  1453. end.
  1454. scan_crlf(<<>>, Bin2) ->
  1455. scan_crlf(Bin2);
  1456. scan_crlf(Bin1, Bin2) when size(Bin1) < 2 ->
  1457. scan_crlf(<<Bin1/binary, Bin2/binary>>);
  1458. scan_crlf(Bin1, Bin2) ->
  1459. scan_crlf_1(size(Bin1) - 2, Bin1, Bin2).
  1460. scan_crlf_1(Bin1_head_size, Bin1, Bin2) ->
  1461. <<Bin1_head:Bin1_head_size/binary, Bin1_tail/binary>> = Bin1,
  1462. Bin3 = <<Bin1_tail/binary, Bin2/binary>>,
  1463. case get_crlf_pos(Bin3) of
  1464. {yes, Offset, Pos} ->
  1465. {Prefix, <<_:Offset/binary, Suffix/binary>>} = split_binary(Bin3, Pos),
  1466. {yes, list_to_binary([Bin1_head, Prefix]), Suffix};
  1467. no ->
  1468. {no, list_to_binary([Bin1, Bin2])}
  1469. end.
  1470. get_crlf_pos(Bin) ->
  1471. get_crlf_pos(Bin, 0).
  1472. get_crlf_pos(<<$\r, $\n, _/binary>>, Pos) -> {yes, 2, Pos};
  1473. get_crlf_pos(<<$\n, _/binary>>, Pos) -> {yes, 1, Pos};
  1474. get_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_pos(Rest, Pos + 1);
  1475. get_crlf_pos(<<>>, _) -> no.
  1476. fmt_val(L) when is_list(L) -> L;
  1477. fmt_val(I) when is_integer(I) -> integer_to_list(I);
  1478. fmt_val(A) when is_atom(A) -> atom_to_list(A);
  1479. fmt_val(B) when is_binary(B) -> B;
  1480. fmt_val(Term) -> io_lib:format("~p", [Term]).
  1481. crnl() -> "\r\n".
  1482. method(connect) -> "CONNECT";
  1483. method(delete) -> "DELETE";
  1484. method(get) -> "GET";
  1485. method(head) -> "HEAD";
  1486. method(options) -> "OPTIONS";
  1487. method(post) -> "POST";
  1488. method(put) -> "PUT";
  1489. method(trace) -> "TRACE";
  1490. %% webdav
  1491. method(copy) -> "COPY";
  1492. method(lock) -> "LOCK";
  1493. method(mkcol) -> "MKCOL";
  1494. method(move) -> "MOVE";
  1495. method(propfind) -> "PROPFIND";
  1496. method(proppatch) -> "PROPPATCH";
  1497. method(search) -> "SEARCH";
  1498. method(unlock) -> "UNLOCK";
  1499. %% subversion %%
  1500. method(report) -> "REPORT";
  1501. method(mkactivity) -> "MKACTIVITY";
  1502. method(checkout) -> "CHECKOUT";
  1503. method(merge) -> "MERGE";
  1504. %% upnp
  1505. method(msearch) -> "MSEARCH";
  1506. method(notify) -> "NOTIFY";
  1507. method(subscribe) -> "SUBSCRIBE";
  1508. method(unsubscribe) -> "UNSUBSCRIBE";
  1509. %% rfc-5789
  1510. method(patch) -> "PATCH";
  1511. method(purge) -> "PURGE".
  1512. %% From RFC 2616
  1513. %%
  1514. % The chunked encoding modifies the body of a message in order to
  1515. % transfer it as a series of chunks, each with its own size indicator,
  1516. % followed by an OPTIONAL trailer containing entity-header
  1517. % fields. This allows dynamically produced content to be transferred
  1518. % along with the information necessary for the recipient to verify
  1519. % that it has received the full message.
  1520. % Chunked-Body = *chunk
  1521. % last-chunk
  1522. % trailer
  1523. % CRLF
  1524. % chunk = chunk-size [ chunk-extension ] CRLF
  1525. % chunk-data CRLF
  1526. % chunk-size = 1*HEX
  1527. % last-chunk = 1*("0") [ chunk-extension ] CRLF
  1528. % chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  1529. % chunk-ext-name = token
  1530. % chunk-ext-val = token | quoted-string
  1531. % chunk-data = chunk-size(OCTET)
  1532. % trailer = *(entity-header CRLF)
  1533. % The chunk-size field is a string of hex digits indicating the size
  1534. % of the chunk. The chunked encoding is ended by any chunk whose size
  1535. % is zero, followed by the trailer, which is terminated by an empty
  1536. % line.
  1537. %%
  1538. %% The parsing implemented here discards all chunk extensions. It also
  1539. %% strips trailing spaces from the chunk size fields as Apache 1.3.27 was
  1540. %% sending them.
  1541. parse_chunk_header(ChunkHeader) ->
  1542. parse_chunk_header(ChunkHeader, []).
  1543. parse_chunk_header(<<$;, _/binary>>, Acc) ->
  1544. hexlist_to_integer(lists:reverse(Acc));
  1545. parse_chunk_header(<<H, T/binary>>, Acc) ->
  1546. case is_whitespace(H) of
  1547. true ->
  1548. parse_chunk_header(T, Acc);
  1549. false ->
  1550. parse_chunk_header(T, [H | Acc])
  1551. end;
  1552. parse_chunk_header(<<>>, Acc) ->
  1553. hexlist_to_integer(lists:reverse(Acc)).
  1554. is_whitespace($\s) -> true;
  1555. is_whitespace($\r) -> true;
  1556. is_whitespace($\n) -> true;
  1557. is_whitespace($\t) -> true;
  1558. is_whitespace(_) -> false.
  1559. send_async_headers(_ReqId, undefined, _, _State) ->
  1560. ok;
  1561. send_async_headers(ReqId, StreamTo, Give_raw_headers,
  1562. #state{status_line = Status_line, raw_headers = Raw_headers,
  1563. recvd_headers = Headers, http_status_code = StatCode,
  1564. cur_req = #request{options = Opts}
  1565. }) ->
  1566. {Headers_1, Raw_headers_1} = maybe_add_custom_headers(Headers, Raw_headers, Opts),
  1567. case Give_raw_headers of
  1568. false ->
  1569. catch StreamTo ! {ibrowse_async_headers, ReqId, StatCode, Headers_1};
  1570. true ->
  1571. catch StreamTo ! {ibrowse_async_headers, ReqId, Status_line, Raw_headers_1}
  1572. end.
  1573. maybe_add_custom_headers(Headers, Raw_headers, Opts) ->
  1574. Custom_headers = get_value(add_custom_headers, Opts, []),
  1575. Headers_1 = Headers ++ Custom_headers,
  1576. Raw_headers_1 = case Custom_headers of
  1577. [_ | _] when is_binary(Raw_headers) ->
  1578. Custom_headers_bin = list_to_binary(string:join([[X, $:, Y] || {X, Y} <- Custom_headers], "\r\n")),
  1579. <<Raw_headers/binary, "\r\n", Custom_headers_bin/binary>>;
  1580. _ ->
  1581. Raw_headers
  1582. end,
  1583. {Headers_1, Raw_headers_1}.
  1584. format_response_data(Resp_format, Body) ->
  1585. case Resp_format of
  1586. list when is_list(Body) ->
  1587. flatten(Body);
  1588. list when is_binary(Body) ->
  1589. binary_to_list(Body);
  1590. binary when is_list(Body) ->
  1591. list_to_binary(Body);
  1592. _ ->
  1593. %% This is to cater for sending messages such as
  1594. %% {chunk_start, _}, chunk_end etc
  1595. Body
  1596. end.
  1597. do_reply(State, From, undefined, _, Resp_format, {ok, St_code, Headers, Body}) ->
  1598. Msg_1 = {ok, St_code, Headers, format_response_data(Resp_format, Body)},
  1599. gen_server:reply(From, Msg_1),
  1600. dec_pipeline_counter(State);
  1601. do_reply(State, From, undefined, _, _, Msg) ->
  1602. gen_server:reply(From, Msg),
  1603. dec_pipeline_counter(State);
  1604. do_reply(#state{prev_req_id = Prev_req_id} = State,
  1605. _From, StreamTo, ReqId, Resp_format, {ok, _, _, Body}) ->
  1606. State_1 = dec_pipeline_counter(State),
  1607. case Body of
  1608. [] ->
  1609. ok;
  1610. _ ->
  1611. Body_1 = format_response_data(Resp_format, Body),
  1612. catch StreamTo ! {ibrowse_async_response, ReqId, Body_1}
  1613. end,
  1614. catch StreamTo ! {ibrowse_async_response_end, ReqId},
  1615. %% We don't want to delete the Req-id to Pid mapping straightaway
  1616. %% as the client may send a stream_next message just while we are
  1617. %% sending back this ibrowse_async_response_end message. If we
  1618. %% deleted this mapping straightaway, the caller will see a
  1619. %% {error, unknown_req_id} when it calls ibrowse:stream_next/1. To
  1620. %% get around this, we store the req id, and clear it after the
  1621. %% next request. If there are wierd combinations of stream,
  1622. %% stream_once and sync requests on the same connection, it will
  1623. %% take a while for the req_id-pid mapping to get cleared, but it
  1624. %% should do no harm.
  1625. ets:delete(ibrowse_stream, {req_id_pid, Prev_req_id}),
  1626. State_1#state{prev_req_id = ReqId};
  1627. do_reply(State, _From, StreamTo, ReqId, Resp_format, Msg) ->
  1628. State_1 = dec_pipeline_counter(State),
  1629. Msg_1 = format_response_data(Resp_format, Msg),
  1630. catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1},
  1631. State_1.
  1632. do_interim_reply(undefined, _, _ReqId, _Msg) ->
  1633. ok;
  1634. do_interim_reply(StreamTo, Response_format, ReqId, Msg) ->
  1635. Msg_1 = format_response_data(Response_format, Msg),
  1636. catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1}.
  1637. do_error_reply(#state{reqs = Reqs, tunnel_setup_queue = Tun_q} = State, Err) ->
  1638. ReqList = queue:to_list(Reqs),
  1639. lists:foreach(fun(#request{from=From, stream_to=StreamTo, req_id=ReqId,
  1640. response_format = Resp_format}) ->
  1641. ets:delete(ibrowse_stream, {req_id_pid, ReqId}),
  1642. do_reply(State, From, StreamTo, ReqId, Resp_format, {error, Err})
  1643. end, ReqList),
  1644. lists:foreach(
  1645. fun({From, _Url, _Headers, _Method, _Body, _Options, _Timeout}) ->
  1646. do_reply(State, From, undefined, undefined, undefined, Err)
  1647. end, Tun_q).
  1648. fail_pipelined_requests(#state{reqs = Reqs, cur_req = CurReq} = State, Reply) ->
  1649. {_, Reqs_1} = queue:out(Reqs),
  1650. #request{from=From, stream_to=StreamTo, req_id=ReqId,
  1651. response_format = Resp_format} = CurReq,
  1652. State_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
  1653. do_error_reply(State_1#state{reqs = Reqs_1}, previous_request_failed).
  1654. split_list_at(List, N) ->
  1655. split_list_at(List, N, []).
  1656. split_list_at([], _, Acc) ->
  1657. {lists:reverse(Acc), []};
  1658. split_list_at(List2, 0, List1) ->
  1659. {lists:reverse(List1), List2};
  1660. split_list_at([H | List2], N, List1) ->
  1661. split_list_at(List2, N-1, [H | List1]).
  1662. hexlist_to_integer(List) ->
  1663. hexlist_to_integer(lists:reverse(List), 1, 0).
  1664. hexlist_to_integer([H | T], Multiplier, Acc) ->
  1665. hexlist_to_integer(T, Multiplier*16, Multiplier*to_ascii(H) + Acc);
  1666. hexlist_to_integer([], _, Acc) ->
  1667. Acc.
  1668. to_ascii($A) -> 10;
  1669. to_ascii($a) -> 10;
  1670. to_ascii($B) -> 11;
  1671. to_ascii($b) -> 11;
  1672. to_ascii($C) -> 12;
  1673. to_ascii($c) -> 12;
  1674. to_ascii($D) -> 13;
  1675. to_ascii($d) -> 13;
  1676. to_ascii($E) -> 14;
  1677. to_ascii($e) -> 14;
  1678. to_ascii($F) -> 15;
  1679. to_ascii($f) -> 15;
  1680. to_ascii($1) -> 1;
  1681. to_ascii($2) -> 2;
  1682. to_ascii($3) -> 3;
  1683. to_ascii($4) -> 4;
  1684. to_ascii($5) -> 5;
  1685. to_ascii($6) -> 6;
  1686. to_ascii($7) -> 7;
  1687. to_ascii($8) -> 8;
  1688. to_ascii($9) -> 9;
  1689. to_ascii($0) -> 0.
  1690. cancel_timer(undefined) -> ok;
  1691. cancel_timer(Ref) -> _ = erlang:cancel_timer(Ref),
  1692. ok.
  1693. cancel_timer(Ref, {eat_message, Msg}) ->
  1694. cancel_timer(Ref),
  1695. receive
  1696. Msg ->
  1697. ok
  1698. after 0 ->
  1699. ok
  1700. end.
  1701. make_req_id() ->
  1702. now().
  1703. to_lower(Str) ->
  1704. to_lower(Str, []).
  1705. to_lower([H|T], Acc) when H >= $A, H =< $Z ->
  1706. to_lower(T, [H+32|Acc]);
  1707. to_lower([H|T], Acc) ->
  1708. to_lower(T, [H|Acc]);
  1709. to_lower([], Acc) ->
  1710. lists:reverse(Acc).
  1711. shutting_down(#state{lb_ets_tid = undefined}) ->
  1712. ok;
  1713. shutting_down(#state{lb_ets_tid = Tid,
  1714. cur_pipeline_size = _Sz}) ->
  1715. catch ets:delete(Tid, self()).
  1716. inc_pipeline_counter(#state{is_closing = true} = State) ->
  1717. State;
  1718. inc_pipeline_counter(#state{lb_ets_tid = undefined} = State) ->
  1719. State;
  1720. inc_pipeline_counter(#state{cur_pipeline_size = Pipe_sz,
  1721. lb_ets_tid = Tid} = State) ->
  1722. update_counter(Tid, self(), {2,1,99999,9999}),
  1723. State#state{cur_pipeline_size = Pipe_sz + 1}.
  1724. update_counter(Tid, Key, Args) ->
  1725. ets:update_counter(Tid, Key, Args).
  1726. dec_pipeline_counter(#state{is_closing = true} = State) ->
  1727. State;
  1728. dec_pipeline_counter(#state{lb_ets_tid = undefined} = State) ->
  1729. State;
  1730. dec_pipeline_counter(#state{cur_pipeline_size = Pipe_sz,
  1731. lb_ets_tid = Tid} = State) ->
  1732. try
  1733. update_counter(Tid, self(), {2,-1,0,0}),
  1734. update_counter(Tid, self(), {3,-1,0,0})
  1735. catch
  1736. _:_ ->
  1737. ok
  1738. end,
  1739. State#state{cur_pipeline_size = Pipe_sz - 1}.
  1740. flatten([H | _] = L) when is_integer(H) ->
  1741. L;
  1742. flatten([H | _] = L) when is_list(H) ->
  1743. lists:flatten(L);
  1744. flatten([]) ->
  1745. [].
  1746. get_stream_chunk_size(Options) ->
  1747. case lists:keysearch(stream_chunk_size, 1, Options) of
  1748. {value, {_, V}} when V > 0 ->
  1749. V;
  1750. _ ->
  1751. ?DEFAULT_STREAM_CHUNK_SIZE
  1752. end.
  1753. set_inac_timer(State) ->
  1754. cancel_timer(State#state.inactivity_timer_ref),
  1755. set_inac_timer(State#state{inactivity_timer_ref = undefined},
  1756. get_inac_timeout(State)).
  1757. set_inac_timer(State, Timeout) when is_integer(Timeout) ->
  1758. Ref = erlang:send_after(Timeout, self(), timeout),
  1759. State#state{inactivity_timer_ref = Ref};
  1760. set_inac_timer(State, _) ->
  1761. State.
  1762. get_inac_timeout(#state{cur_req = #request{options = Opts}}) ->
  1763. get_value(inactivity_timeout, Opts, infinity);
  1764. get_inac_timeout(#state{cur_req = undefined}) ->
  1765. case ibrowse:get_config_value(inactivity_timeout, undefined) of
  1766. Val when is_integer(Val) ->
  1767. Val;
  1768. _ ->
  1769. case application:get_env(ibrowse, inactivity_timeout) of
  1770. {ok, Val} when is_integer(Val), Val > 0 ->
  1771. Val;
  1772. _ ->
  1773. 10000
  1774. end
  1775. end.
  1776. trace_request(Req) ->
  1777. case get(my_trace_flag) of
  1778. true ->
  1779. %%Avoid the binary operations if trace is not on...
  1780. NReq = to_binary(Req),
  1781. do_trace("Sending request: ~n"
  1782. "--- Request Begin ---~n~s~n"
  1783. "--- Request End ---~n", [NReq]);
  1784. _ -> ok
  1785. end.
  1786. trace_request_body(Body) ->
  1787. case get(my_trace_flag) of
  1788. true ->
  1789. %%Avoid the binary operations if trace is not on...
  1790. NBody = to_binary(Body),
  1791. case size(NBody) > 1024 of
  1792. true ->
  1793. ok;
  1794. false ->
  1795. do_trace("Sending request body: ~n"
  1796. "--- Request Body Begin ---~n~s~n"
  1797. "--- Request Body End ---~n", [NBody])
  1798. end;
  1799. false ->
  1800. ok
  1801. end.
  1802. to_binary(X) when is_list(X) -> list_to_binary(X);
  1803. to_binary(X) when is_binary(X) -> X.