Skip to content

Instantly share code, notes, and snippets.

@cmullaparthi
Created May 11, 2010 07:31
Show Gist options
  • Save cmullaparthi/397015 to your computer and use it in GitHub Desktop.
Save cmullaparthi/397015 to your computer and use it in GitHub Desktop.
ibrowse CONNECT patch
diff --git a/src/ibrowse_http_client.erl b/src/ibrowse_http_client.erl
index 4fdb334..19a7b47 100644
--- a/src/ibrowse_http_client.erl
+++ b/src/ibrowse_http_client.erl
@@ -38,12 +38,15 @@
-include("ibrowse.hrl").
--record(state, {host, port,
+-record(state, {host, port, connect_timeout,
use_proxy = false, proxy_auth_digest,
ssl_options = [], is_ssl = false, socket,
+ proxy_tunnel_setup = false,
+ tunnel_setup_queue = [],
reqs=queue:new(), cur_req, status=idle, http_status_code,
reply_buffer = <<>>, rep_buf_size=0, streamed_size = 0,
recvd_headers=[],
+ status_line, raw_headers,
is_closing, send_timer, content_length,
deleted_crlf = false, transfer_encoding,
chunk_size, chunk_size_buffer = <<>>, recvd_chunk_size,
@@ -169,9 +172,8 @@ handle_info({ssl, _Sock, Data}, State) ->
handle_sock_data(Data, State);
handle_info({stream_next, Req_id}, #state{socket = Socket,
- is_ssl = Is_ssl,
cur_req = #request{req_id = Req_id}} = State) ->
- do_setopts(Socket, [{active, once}], Is_ssl),
+ do_setopts(Socket, [{active, once}], State),
{noreply, State};
handle_info({stream_next, _Req_id}, State) ->
@@ -257,7 +259,8 @@ handle_sock_data(Data, #state{status = get_header}=State) ->
{stop, normal, State};
State_1 ->
active_once(State_1),
- {noreply, State_1, get_inac_timeout(State_1)}
+ set_inac_timer(State_1),
+ {noreply, State_1}
end;
handle_sock_data(Data, #state{status = get_body,
@@ -275,7 +278,8 @@ handle_sock_data(Data, #state{status = get_body,
{stop, normal, State};
State_1 ->
active_once(State_1),
- {noreply, State_1, get_inac_timeout(State_1)}
+ set_inac_timer(State_1),
+ {noreply, State_1}
end;
_ ->
case parse_11_response(Data, State) of
@@ -286,7 +290,8 @@ handle_sock_data(Data, #state{status = get_body,
{stop, normal, State};
State_1 ->
active_once(State_1),
- {noreply, State_1, get_inac_timeout(State_1)}
+ set_inac_timer(State_1),
+ {noreply, State_1}
end
end.
@@ -388,21 +393,31 @@ handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code =
SC
is_closing = IsClosing,
cur_req = #request{tmp_file_name=TmpFilename,
tmp_file_fd=Fd} = CurReq,
- status = get_body, recvd_headers = Headers}=State) ->
+ status = get_body,
+ recvd_headers = Headers,
+ status_line = Status_line,
+ raw_headers = Raw_headers
+ }=State) ->
#request{from=From, stream_to=StreamTo, req_id=ReqId,
- response_format = Resp_format} = CurReq,
+ response_format = Resp_format,
+ options = Options} = CurReq,
case IsClosing of
true ->
{_, Reqs_1} = queue:out(Reqs),
- case TmpFilename of
- undefined ->
- do_reply(State, From, StreamTo, ReqId, Resp_format,
- {ok, SC, Headers, Buf});
- _ ->
- file:close(Fd),
- do_reply(State, From, StreamTo, ReqId, Resp_format,
- {ok, SC, Headers, {file, TmpFilename}})
- end,
+ Body = case TmpFilename of
+ undefined ->
+ Buf;
+ _ ->
+ file:close(Fd),
+ {file, TmpFilename}
+ end,
+ Reply = case get_value(give_raw_headers, Options, false) of
+ true ->
+ {ok, Status_line, Raw_headers, Body};
+ false ->
+ {ok, SC, Headers, Buf}
+ end,
+ do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
do_error_reply(State#state{reqs = Reqs_1}, connection_closed),
State;
_ ->
@@ -410,7 +425,10 @@ handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code = S
C
State
end.
-do_connect(Host, Port, Options, #state{is_ssl=true, ssl_options=SSLOptions}, Timeout) ->
+do_connect(Host, Port, Options, #state{is_ssl = true,
+ use_proxy = false,
+ ssl_options = SSLOptions},
+ Timeout) ->
Caller_socket_options = get_value(socket_options, Options, []),
Other_sock_options = filter_sock_options(SSLOptions ++ Caller_socket_options),
ssl:connect(Host, Port,
@@ -419,7 +437,7 @@ do_connect(Host, Port, Options, #state{is_ssl=true, ssl_options=SSLOptions}, Tim
do_connect(Host, Port, Options, _State, Timeout) ->
Caller_socket_options = get_value(socket_options, Options, []),
Other_sock_options = filter_sock_options(Caller_socket_options),
- gen_tcp:connect(Host, Port,
+ gen_tcp:connect(Host, list_to_integer(Port),
[binary, {nodelay, true}, {active, false} | Other_sock_options],
Timeout).
@@ -435,6 +453,9 @@ filter_sock_options(Opts) ->
true
end, Opts).
+do_send(Req, #state{socket = Sock,
+ is_ssl = true,
+ proxy_tunnel_setup = Pts}) when Pts /= done -> gen_tcp:send(Sock, Req);
do_send(Req, #state{socket = Sock, is_ssl = true}) -> ssl:send(Sock, Req);
do_send(Req, #state{socket = Sock, is_ssl = false}) -> gen_tcp:send(Sock, Req).
@@ -467,17 +488,26 @@ do_send_body1(Source, Resp, State) ->
end.
do_close(#state{socket = undefined}) -> ok;
+do_close(#state{socket = Sock,
+ is_ssl = true,
+ use_proxy = true,
+ proxy_tunnel_setup = Pts
+ }) when Pts /= done -> gen_tcp:close(Sock);
do_close(#state{socket = Sock, is_ssl = true}) -> ssl:close(Sock);
do_close(#state{socket = Sock, is_ssl = false}) -> gen_tcp:close(Sock).
active_once(#state{cur_req = #request{caller_controls_socket = true}}) ->
ok;
-active_once(#state{socket = Socket, is_ssl = Is_ssl}) ->
- do_setopts(Socket, [{active, once}], Is_ssl).
+active_once(#state{socket = Socket} = State) ->
+ do_setopts(Socket, [{active, once}], State).
do_setopts(_Sock, [], _) -> ok;
-do_setopts(Sock, Opts, true) -> ssl:setopts(Sock, Opts);
-do_setopts(Sock, Opts, false) -> inet:setopts(Sock, Opts).
+do_setopts(Sock, Opts, #state{is_ssl = true,
+ use_proxy = true,
+ proxy_tunnel_setup = Pts}
+ ) when Pts /= done -> inet:setopts(Sock, Opts);
+do_setopts(Sock, Opts, #state{is_ssl = true}) -> ssl:setopts(Sock, Opts);
+do_setopts(Sock, Opts, _) -> inet:setopts(Sock, Opts).
check_ssl_options(Options, State) ->
case get_value(is_ssl, Options, false) of
@@ -518,7 +548,8 @@ send_req_1(From,
_ ->
Timeout - trunc(round(timer:now_diff(End_ts, Start_ts) / 1000))
end,
- State_3 = State_2#state{socket = Sock},
+ State_3 = State_2#state{socket = Sock,
+ connect_timeout = Conn_timeout},
send_req_1(From, Url, Headers, Method, Body, Options, Timeout_1, State_3);
Err ->
shutting_down(State_2),
@@ -526,13 +557,80 @@ send_req_1(From,
gen_server:reply(From, {error, conn_failed}),
{stop, normal, State_2}
end;
+
+%% Send a CONNECT request.
+%% Wait for 200 OK
+%% Upgrade to SSL connection
+%% Then send request
+
+send_req_1(From,
+ #url{
+ host = Server_host,
+ port = Server_port
+ } = Url,
+ Headers, Method, Body, Options, Timeout,
+ #state{
+ proxy_tunnel_setup = false,
+ use_proxy = true,
+ is_ssl = true} = State) ->
+ NewReq = #request{
+ method = connect,
+ options = Options
+ },
+ State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)},
+ Pxy_auth_headers = maybe_modify_headers(Url, Method, Options, [], State_1),
+ Path = [Server_host, $:, integer_to_list(Server_port)],
+ {Req, Body_1} = make_request(connect, Pxy_auth_headers,
+ Path, Path,
+ [], Options, State_1),
+ trace_request(Req),
+ case do_send(Req, State) of
+ ok ->
+ case do_send_body(Body_1, State_1) of
+ ok ->
+ active_once(State_1),
+ Ref = case Timeout of
+ infinity ->
+ undefined;
+ _ ->
+ erlang:send_after(Timeout, self(), {req_timedout, From})
+ end,
+ State_2 = State_1#state{status = get_header,
+ cur_req = NewReq,
+ send_timer = Ref,
+ proxy_tunnel_setup = in_progress,
+ tunnel_setup_queue = [{From, Url, Headers, Method, Body
, Options, Timeout}]},
+ set_inac_timer(State_1),
+ {noreply, State_2};
+ Err ->
+ shutting_down(State_1),
+ do_trace("Send failed... Reason: ~p~n", [Err]),
+ gen_server:reply(From, {error, send_failed}),
+ {stop, normal, State_1}
+ end;
+ Err ->
+ shutting_down(State_1),
+ do_trace("Send failed... Reason: ~p~n", [Err]),
+ gen_server:reply(From, {error, send_failed}),
+ {stop, normal, State_1}
+ end;
+
+send_req_1(From, Url, Headers, Method, Body, Options, Timeout,
+ #state{proxy_tunnel_setup = in_progress,
+ tunnel_setup_queue = Q} = State) ->
+ do_trace("Queued SSL request awaiting tunnel setup: ~n"
+ "URL : ~s~n"
+ "Method : ~p~n"
+ "Headers : ~p~n", [Url, Method, Headers]),
+ {noreply, State#state{tunnel_setup_queue = [{From, Url, Headers, Method, Body, Options, Timeout
} | Q]}};
+
send_req_1(From,
#url{abspath = AbsPath,
path = RelPath} = Url,
Headers, Method, Body, Options, Timeout,
- #state{status = Status,
- socket = Socket,
- is_ssl = Is_ssl} = State) ->
+ #state{status = Status,
+ socket = Socket,
+ is_ssl = Is_ssl} = State) ->
ReqId = make_req_id(),
Resp_format = get_value(response_format, Options, list),
Caller_socket_options = get_value(socket_options, Options, []),
@@ -564,19 +662,11 @@ send_req_1(From,
response_format = Resp_format,
from = From},
State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)},
- Headers_1 = maybe_modify_headers(Url, Options, Headers, State_1),
+ Headers_1 = maybe_modify_headers(Url, Method, Options, Headers, State_1),
{Req, Body_1} = make_request(Method,
Headers_1,
- AbsPath, RelPath, Body, Options, State_1#state.use_proxy),
- case get(my_trace_flag) of
- true ->
- %%Avoid the binary operations if trace is not on...
- NReq = binary_to_list(list_to_binary(Req)),
- do_trace("Sending request: ~n"
- "--- Request Begin ---~n~s~n"
- "--- Request End ---~n", [NReq]);
- _ -> ok
- end,
+ AbsPath, RelPath, Body, Options, State_1),
+ trace_request(Req),
do_setopts(Socket, Caller_socket_options, Is_ssl),
case do_send(Req, State_1) of
ok ->
@@ -604,7 +694,8 @@ send_req_1(From,
_ ->
gen_server:reply(From, {ibrowse_req_id, ReqId})
end,
- {noreply, State_3, get_inac_timeout(State_3)};
+ set_inac_timer(State_1),
+ {noreply, State_3};
Err ->
shutting_down(State_1),
do_trace("Send failed... Reason: ~p~n", [Err]),
@@ -618,7 +709,10 @@ send_req_1(From,
{stop, normal, State_1}
end.
+maybe_modify_headers(#url{}, connect, _, Headers, State) ->
+ add_proxy_auth_headers(State, Headers);
maybe_modify_headers(#url{host = Host, port = Port} = Url,
+ _Method,
Options, Headers, State) ->
case get_value(headers_as_is, Options, false) of
false ->
@@ -641,8 +735,7 @@ add_auth_headers(#url{username = User,
password = UPw},
Options,
Headers,
- #state{use_proxy = UseProxy,
- proxy_auth_digest = ProxyAuthDigest}) ->
+ State) ->
Headers_1 = case User of
undefined ->
case get_value(basic_auth, Options, undefined) of
@@ -654,14 +747,14 @@ add_auth_headers(#url{username = User,
_ ->
[{"Authorization", ["Basic ", http_auth_digest(User, UPw)]} | Headers]
end,
- case UseProxy of
- false ->
- Headers_1;
- true when ProxyAuthDigest == [] ->
- Headers_1;
- true ->
- [{"Proxy-Authorization", ["Basic ", ProxyAuthDigest]} | Headers_1]
- end.
+ add_proxy_auth_headers(State, Headers_1).
+
+add_proxy_auth_headers(#state{use_proxy = false}, Headers) ->
+ Headers;
+add_proxy_auth_headers(#state{proxy_auth_digest = []}, Headers) ->
+ Headers;
+add_proxy_auth_headers(#state{proxy_auth_digest = Auth_digest}, Headers) ->
+ [{"Proxy-Authorization", ["Basic ", Auth_digest]} | Headers].
http_auth_digest([], []) ->
[];
@@ -688,7 +781,8 @@ e(62) -> $+;
e(63) -> $/;
e(X) -> exit({bad_encode_base64_token, X}).
-make_request(Method, Headers, AbsPath, RelPath, Body, Options, UseProxy) ->
+make_request(Method, Headers, AbsPath, RelPath, Body, Options,
+ #state{use_proxy = UseProxy}) ->
HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})),
Headers_1 =
case get_value(content_length, Headers, false) of
@@ -698,7 +792,7 @@ make_request(Method, Headers, AbsPath, RelPath, Body, Options, UseProxy) ->
is_function(Body) ->
Headers;
false when is_binary(Body) ->
- [{"content-length", integer_to_list(byte_size(Body))} | Headers];
+ [{"content-length", integer_to_list(size(Body))} | Headers];
false ->
[{"content-length", integer_to_list(length(Body))} | Headers];
_ ->
@@ -762,13 +856,14 @@ chunk_request_body(Body, ChunkSize) ->
chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] ->
LastChunk = "0\r\n",
lists:reverse(["\r\n", LastChunk | Acc]);
-chunk_request_body(Body, ChunkSize, Acc) when byte_size(Body) >= ChunkSize ->
+chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body),
+ size(Body) >= ChunkSize ->
<<ChunkBody:ChunkSize/binary, Rest/binary>> = Body,
Chunk = [ibrowse_lib:dec2hex(4, ChunkSize),"\r\n",
ChunkBody, "\r\n"],
chunk_request_body(Rest, ChunkSize, [Chunk | Acc]);
chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) ->
- BodySize = byte_size(Body),
+ BodySize = size(Body),
Chunk = [ibrowse_lib:dec2hex(4, BodySize),"\r\n",
Body, "\r\n"],
LastChunk = "0\r\n",
@@ -791,13 +886,15 @@ parse_response(_Data, #state{cur_req = undefined}=State) ->
parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
cur_req = CurReq} = State) ->
#request{from=From, stream_to=StreamTo, req_id=ReqId,
- method=Method, response_format = Resp_format} = CurReq,
+ method=Method, response_format = Resp_format,
+ options = Options
+ } = CurReq,
MaxHeaderSize = ibrowse:get_config_value(max_headers_size, infinity),
case scan_header(Acc, Data) of
{yes, Headers, Data_1} ->
do_trace("Recvd Header Data -> ~s~n----~n", [Headers]),
do_trace("Recvd headers~n--- Headers Begin ---~n~s~n--- Headers End ---~n~n", [Headers])
,
- {HttpVsn, StatCode, Headers_1} = parse_headers(Headers),
+ {HttpVsn, StatCode, Headers_1, Status_line, Raw_headers} = parse_headers(Headers),
do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Header
s_1]),
LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1],
ConnClose = to_lower(get_value("connection", LCHeaders, "false")),
@@ -808,15 +905,33 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
false ->
ok
end,
- State_1 = State#state{recvd_headers=Headers_1, status=get_body,
- reply_buffer = <<>>,
- http_status_code=StatCode, is_closing=IsClosing},
+ Give_raw_headers = get_value(give_raw_headers, Options, false),
+ State_1 = case Give_raw_headers of
+ true ->
+ State#state{recvd_headers=Headers_1, status=get_body,
+ reply_buffer = <<>>,
+ status_line = Status_line,
+ raw_headers = Raw_headers,
+ http_status_code=StatCode, is_closing=IsClosing};
+ false ->
+ State#state{recvd_headers=Headers_1, status=get_body,
+ reply_buffer = <<>>,
+ http_status_code=StatCode, is_closing=IsClosing}
+ end,
put(conn_close, ConnClose),
TransferEncoding = to_lower(get_value("transfer-encoding", LCHeaders, "false")),
case get_value("content-length", LCHeaders, undefined) of
+ _ when Method == connect,
+ hd(StatCode) == $2 ->
+ cancel_timer(State#state.send_timer),
+ {_, Reqs_1} = queue:out(Reqs),
+ upgrade_to_ssl(set_cur_request(State#state{reqs = Reqs_1,
+ recvd_headers = [],
+ status = idle
+ }));
_ when Method == head ->
{_, Reqs_1} = queue:out(Reqs),
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format,
{ok, StatCode, Headers_1, []}),
cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}),
@@ -827,7 +942,7 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
%% No message body is expected. Server may send
%% one or more 1XX responses before a proper
%% response.
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
do_trace("Recvd a status code of ~p. Ignoring and waiting for a proper response~
n", [StatCode]),
parse_response(Data_1, State_1#state{recvd_headers = [],
status = get_header});
@@ -836,7 +951,7 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
%% No message body is expected for these Status Codes.
%% RFC2616 - Sec 4.4
{_, Reqs_1} = queue:out(Reqs),
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format,
{ok, StatCode, Headers_1, []}),
cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}),
@@ -845,7 +960,7 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
parse_response(Data_1, State_3);
_ when TransferEncoding =:= "chunked" ->
do_trace("Chunked encoding detected...~n",[]),
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
case parse_11_response(Data_1, State_1#state{transfer_encoding=chunked,
chunk_size=chunk_start,
reply_buffer = <<>>}) of
@@ -858,8 +973,8 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
State_2
end;
undefined when HttpVsn =:= "HTTP/1.0";
- ConnClose =:= "close" ->
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ ConnClose =:= "close" ->
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
State_1#state{reply_buffer = Data_1};
undefined ->
fail_pipelined_requests(State_1,
@@ -869,7 +984,7 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
V ->
case catch list_to_integer(V) of
V_1 when is_integer(V_1), V_1 >= 0 ->
- send_async_headers(ReqId, StreamTo, StatCode, Headers_1),
+ send_async_headers(ReqId, StreamTo, Give_raw_headers, State_1),
do_trace("Recvd Content-Length of ~p~n", [V_1]),
State_2 = State_1#state{rep_buf_size=0,
reply_buffer = <<>>,
@@ -885,8 +1000,8 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
end;
_ ->
fail_pipelined_requests(State_1,
- {error, {content_length_undefined,
- {stat_code, StatCode}, Headers}}),
+ {error, {content_length_undefined,
+ {stat_code, StatCode}, Headers}}),
{error, content_length_undefined}
end
end;
@@ -899,6 +1014,39 @@ parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs,
{error, max_headers_size_exceeded}
end.
+upgrade_to_ssl(#state{socket = Socket,
+ connect_timeout = Conn_timeout,
+ ssl_options = Ssl_options,
+ tunnel_setup_queue = Q} = State) ->
+ case ssl:connect(Socket, Ssl_options, Conn_timeout) of
+ {ok, Ssl_socket} ->
+ do_trace("Upgraded to SSL socket!!~n", []),
+ State_1 = State#state{socket = Ssl_socket,
+ proxy_tunnel_setup = done},
+ send_queued_requests(Q, State_1);
+ Err ->
+ do_trace("Upgrade to SSL socket failed. Reson: ~p~n", [Err]),
+ do_error_reply(State, {error, send_failed}),
+ {error, send_failed}
+ end.
+
+send_queued_requests([], State) ->
+ do_trace("Sent all queued requests via SSL connection~n", []),
+ State#state{tunnel_setup_queue = done};
+send_queued_requests([{From, Url, Headers, Method, Body, Options, Timeout} | Q],
+ State) ->
+ case send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State) of
+ {noreply, State_1} ->
+ send_queued_requests(Q, State_1);
+ _ ->
+ do_trace("Error sending queued SSL request: ~n"
+ "URL : ~s~n"
+ "Method : ~p~n"
+ "Headers : ~p~n", [Url, Method, Headers]),
+ do_error_reply(State, {error, send_failed}),
+ {error, send_failed}
+ end.
+
is_connection_closing("HTTP/0.9", _) -> true;
is_connection_closing(_, "close") -> true;
is_connection_closing("HTTP/1.0", "false") -> true;
@@ -1020,11 +1168,14 @@ handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
response_format = Resp_format,
save_response_to_file = SaveResponseToFile,
tmp_file_name = TmpFilename,
- tmp_file_fd = Fd
+ tmp_file_fd = Fd,
+ options = Options
},
#state{http_status_code = SCode,
- send_timer = ReqTimer,
- reply_buffer = RepBuf,
+ status_line = Status_line,
+ raw_headers = Raw_headers,
+ send_timer = ReqTimer,
+ reply_buffer = RepBuf,
recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false ->
Body = RepBuf,
State_1 = set_cur_request(State),
@@ -1035,25 +1186,38 @@ handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
_ ->
{file, TmpFilename}
end,
- State_2 = do_reply(State_1, From, StreamTo, ReqId, Resp_format,
- {ok, SCode, RespHeaders, ResponseBody}),
+ Reply = case get_value(give_raw_headers, Options, false) of
+ true ->
+ {ok, Status_line, Raw_headers, ResponseBody};
+ false ->
+ {ok, SCode, RespHeaders, ResponseBody}
+ end,
+ State_2 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, Reply),
cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}),
State_2;
handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId,
- response_format = Resp_format},
- #state{http_status_code=SCode, recvd_headers=RespHeaders,
- reply_buffer = RepBuf,
- send_timer=ReqTimer}=State) ->
+ response_format = Resp_format,
+ options = Options},
+ #state{http_status_code = SCode,
+ status_line = Status_line,
+ raw_headers = Raw_headers,
+ recvd_headers = RespHeaders,
+ reply_buffer = RepBuf,
+ send_timer = ReqTimer} = State) ->
Body = RepBuf,
%% State_1 = set_cur_request(State),
+ Reply = case get_value(give_raw_headers, Options, false) of
+ true ->
+ {ok, Status_line, Raw_headers, Body};
+ false ->
+ {ok, SCode, RespHeaders, Body}
+ end,
State_1 = case get(conn_close) of
"close" ->
- do_reply(State, From, StreamTo, ReqId, Resp_format,
- {ok, SCode, RespHeaders, Body}),
+ do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
exit(normal);
_ ->
- State_1_1 = do_reply(State, From, StreamTo, ReqId, Resp_format,
- {ok, SCode, RespHeaders, Body}),
+ State_1_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, Reply),
cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}),
State_1_1
end,
@@ -1067,6 +1231,8 @@ reset_state(State) ->
reply_buffer = <<>>,
chunk_size_buffer = <<>>,
recvd_headers = [],
+ status_line = undefined,
+ raw_headers = undefined,
deleted_crlf = false,
http_status_code = undefined,
chunk_size = undefined,
@@ -1093,10 +1259,10 @@ parse_headers(StatusLine, Headers) ->
case parse_status_line(StatusLine) of
{ok, HttpVsn, StatCode, _Msg} ->
put(http_prot_vsn, HttpVsn),
- {HttpVsn, StatCode, Headers_1};
+ {HttpVsn, StatCode, Headers_1, StatusLine, Headers};
_ -> %% A HTTP 0.9 response?
put(http_prot_vsn, "HTTP/0.9"),
- {"HTTP/0.9", undefined, Headers}
+ {"HTTP/0.9", undefined, Headers, StatusLine, Headers}
end.
% From RFC 2616
@@ -1171,12 +1337,12 @@ scan_header(Bin) ->
{no, Bin}
end.
-scan_header(Bin1, Bin2) when byte_size(Bin1) < 4 ->
+scan_header(Bin1, Bin2) when size(Bin1) < 4 ->
scan_header(<<Bin1/binary, Bin2/binary>>);
scan_header(Bin1, <<>>) ->
scan_header(Bin1);
scan_header(Bin1, Bin2) ->
- Bin1_already_scanned_size = byte_size(Bin1) - 4,
+ Bin1_already_scanned_size = size(Bin1) - 4,
<<Headers_prefix:Bin1_already_scanned_size/binary, Rest/binary>> = Bin1,
Bin_to_scan = <<Rest/binary, Bin2/binary>>,
case get_crlf_crlf_pos(Bin_to_scan, 0) of
@@ -1202,10 +1368,10 @@ scan_crlf(Bin) ->
scan_crlf(<<>>, Bin2) ->
scan_crlf(Bin2);
-scan_crlf(Bin1, Bin2) when byte_size(Bin1) < 2 ->
+scan_crlf(Bin1, Bin2) when size(Bin1) < 2 ->
scan_crlf(<<Bin1/binary, Bin2/binary>>);
scan_crlf(Bin1, Bin2) ->
- scan_crlf_1(byte_size(Bin1) - 2, Bin1, Bin2).
+ scan_crlf_1(size(Bin1) - 2, Bin1, Bin2).
scan_crlf_1(Bin1_head_size, Bin1, Bin2) ->
<<Bin1_head:Bin1_head_size/binary, Bin1_tail/binary>> = Bin1,
@@ -1245,7 +1411,8 @@ method(proppatch) -> "PROPPATCH";
method(lock) -> "LOCK";
method(unlock) -> "UNLOCK";
method(move) -> "MOVE";
-method(copy) -> "COPY".
+method(copy) -> "COPY";
+method(connect) -> "CONNECT".
%% From RFC 2616
%%
@@ -1297,10 +1464,18 @@ is_whitespace($\n) -> true;
is_whitespace($\t) -> true;
is_whitespace(_) -> false.
-send_async_headers(_ReqId, undefined, _StatCode, _Headers) ->
+send_async_headers(_ReqId, undefined, _, _State) ->
ok;
-send_async_headers(ReqId, StreamTo, StatCode, Headers) ->
- catch StreamTo ! {ibrowse_async_headers, ReqId, StatCode, Headers}.
+send_async_headers(ReqId, StreamTo, Give_raw_headers,
+ #state{status_line = Status_line, raw_headers = Raw_headers,
+ recvd_headers = Headers, http_status_code = StatCode
+ }) ->
+ case Give_raw_headers of
+ false ->
+ catch StreamTo ! {ibrowse_async_headers, ReqId, StatCode, Headers};
+ true ->
+ catch StreamTo ! {ibrowse_async_headers, ReqId, Status_line, Raw_headers}
+ end.
format_response_data(Resp_format, Body) ->
case Resp_format of
@@ -1474,7 +1649,26 @@ get_stream_chunk_size(Options) ->
?DEFAULT_STREAM_CHUNK_SIZE
end.
+set_inac_timer(State) ->
+ set_inac_timer(State, get_inac_timeout(State)).
+
+set_inac_timer(_State, Timeout) when is_integer(Timeout) ->
+ erlang:send_after(Timeout, self(), timeout);
+set_inac_timer(_, _) ->
+ undefined.
+
get_inac_timeout(#state{cur_req = #request{options = Opts}}) ->
get_value(inactivity_timeout, Opts, infinity);
get_inac_timeout(#state{cur_req = undefined}) ->
infinity.
+
+trace_request(Req) ->
+ case get(my_trace_flag) of
+ true ->
+ %%Avoid the binary operations if trace is not on...
+ NReq = binary_to_list(list_to_binary(Req)),
+ do_trace("Sending request: ~n"
+ "--- Request Begin ---~n~s~n"
+ "--- Request End ---~n", [NReq]);
+ _ -> ok
+ end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment