Created
May 11, 2010 07:31
-
-
Save cmullaparthi/397015 to your computer and use it in GitHub Desktop.
ibrowse CONNECT patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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