Created
May 11, 2010 07:31
-
-
Save cmullaparthi/397015 to your computer and use it in GitHub Desktop.
ibrowse CONNECT patch
This file contains hidden or 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