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

2124 regels
87 KiB

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