aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2010-01-19 17:04:23 +0000
committerErlang/OTP <[email protected]>2010-01-19 18:21:11 +0100
commit10f031e7e6b497430918a29db97d12ffe37a5b2d (patch)
tree5a673bf18de904738221d20b504f0e65986ec101 /lib/inets
parent7aeea26018246415dbfa829f134040b3eefa1ee2 (diff)
downloadotp-10f031e7e6b497430918a29db97d12ffe37a5b2d.tar.gz
otp-10f031e7e6b497430918a29db97d12ffe37a5b2d.tar.bz2
otp-10f031e7e6b497430918a29db97d12ffe37a5b2d.zip
OTP-8016, OTP-8056, OTP-8103, OTP-8106, OTP-8312, OTP-8315, OTP-8327,
OTP-8349, OTP-8351, OTP-8352, OTP-8359 & OTP-8371.
Diffstat (limited to 'lib/inets')
-rw-r--r--lib/inets/doc/src/httpc.xml60
-rw-r--r--lib/inets/doc/src/notes.xml10
-rw-r--r--lib/inets/src/http_client/httpc.erl115
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl271
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl34
-rw-r--r--lib/inets/vsn.mk3
6 files changed, 344 insertions, 149 deletions
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 680473cc38..e143ba2c1a 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -21,7 +21,7 @@
</legalnotice>
- <title>http</title>
+ <title>httpc</title>
<prepared>Ingela Anderton Andin</prepared>
<responsible></responsible>
<docno></docno>
@@ -64,6 +64,8 @@ request_id() = ref()
profile() = atom()
path() = string() representing a file path or directory path
ip_address() = See inet(3)
+socket_opt() = See the Options used by gen_tcp(3) and
+ ssl(3) connect(s)
]]></code>
</section>
@@ -162,13 +164,13 @@ ssl_options() = {verify, code()} |
<v>Request = request()</v>
<v>HTTPOptions = http_options()</v>
<v>http_options() = [http_option()]</v>
- <v>http_option() = {timeout, timeout()} |
+ <v>http_option() = {timeout, timeout()} |
{connect_timeout, timeout()} |
- {ssl, ssl_options()} |
- {autoredirect, boolean()} |
+ {ssl, ssl_options()} |
+ {autoredirect, boolean()} |
{proxy_auth, {userstring(), passwordstring()}} |
- {version, http_version()} |
- {relaxed, boolean()}</v>
+ {version, http_version()} |
+ {relaxed, boolean()}</v>
<v>timeout() = integer() >= 0 | infinity</v>
<v>Options = options()</v>
<v>options() = [option()]</v>
@@ -177,8 +179,10 @@ ssl_options() = {verify, code()} |
{body_format, body_format()} |
{full_result, boolean()} |
{headers_as_is, boolean() |
+ {socket_opts, socket_opts()} |
{receiver, receiver()}}</v>
<v>stream_to() = none | self | {self, once} | filename() </v>
+ <v>socket_opts() = [socket_opt()]</v>
<v>receiver() = pid() | function()/1 | {Module, Function, Args} </v>
<v>Module = atom() </v>
<v>Function = atom() </v>
@@ -315,6 +319,24 @@ ssl_options() = {verify, code()} |
<p>Defaults to <c>false</c>. </p>
</item>
+ <tag><c><![CDATA[socket_opts]]></c></tag>
+ <item>
+ <p>Socket options to be used for this and subsequent
+ request(s). </p>
+ <p>Overrides any value set by the
+ <seealso marker="set_options">set_options</seealso>
+ function. </p>
+ <p>Note that the validity of the options are <em>not</em>
+ checked in any way. </p>
+ <p>Note that this may change the socket behaviour
+ (see <seealso marker="inet#setopts">inet:setopts/2</seealso>)
+ for an already existing, and therefor already connected
+ request handler. </p>
+ <p>By defaults the socket options set by the
+ <seealso marker="#set_options">set_options/1,2</seealso>
+ function is used when establishing connection. </p>
+ </item>
+
<tag><c><![CDATA[receiver]]></c></tag>
<item>
<p>Defines how the client will deliver the result for a
@@ -393,17 +415,30 @@ apply(Module, Function, [ReplyInfo | Args])
<fsummary>Sets options to be used for subsequent requests.</fsummary>
<type>
<v>Options = [Option]</v>
- <v>Option = {proxy, {Proxy, NoProxy}} | {max_sessions, MaxSessions} |
- {max_keep_alive_length, MaxKeepAlive} | {keep_alive_timeout, KeepAliveTimeout} |
- {max_pipeline_length, MaxPipeline} | {pipeline_timeout, PipelineTimeout} |
- {cookies | CookieMode} |
- {ipfamily, IpFamily} | {ip, IpAddress} | {port, Port} |
- {verbose, VerboseMode} </v>
+ <v>Option = {proxy, {Proxy, NoProxy}} |
+ {max_sessions, MaxSessions} |
+ {max_keep_alive_length, MaxKeepAlive} |
+ {keep_alive_timeout, KeepAliveTimeout} |
+ {max_pipeline_length, MaxPipeline} |
+ {pipeline_timeout, PipelineTimeout} |
+ {cookies, CookieMode} |
+ {ipfamily, IpFamily} |
+ {ip, IpAddress} |
+ {port, Port} |
+ {socket_opts, socket_opts()} |
+ {verbose, VerboseMode} </v>
<v>Proxy = {Hostname, Port}</v>
<v>Hostname = string() </v>
<d>ex: "localhost" or "foo.bar.se"</d>
<v>Port = integer()</v>
<d>ex: 8080 </d>
+ <v>socket_opts() = [socket_opt()]</v>
+ <d>The options are appended to the socket options used by the
+ client. </d>
+ <d>These are the default values when a new request handler
+ is started (for the initial connect). They are passed directly
+ to the underlying transport (gen_tcp or ssl) <em>without</em>
+ verification! </d>
<v>NoProxy = [NoProxyDesc]</v>
<v>NoProxyDesc = DomainDesc | HostName | IPDesc</v>
<v>DomainDesc = "*.Domain"</v>
@@ -573,6 +608,7 @@ apply(Module, Function, [ReplyInfo | Args])
<section>
<title>SEE ALSO</title>
<p>RFC 2616, <seealso marker="inets">inets(3)</seealso>,
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>,
<seealso marker="ssl:ssl">ssl(3)</seealso>
</p>
</section>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index ed83708940..e95c8d6e97 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -41,6 +41,16 @@
<list>
<item>
+ <p>[httpc] - Allow users to pass socket options to the transport
+ module when making requests. </p>
+ <p>See the <c>socket_opts</c> option in the
+ <seealso marker="httpc#request2">request/4</seealso> or
+ <seealso marker="httpc#set_options">set_options/1,2</seealso>
+ for more info, </p>
+ <p>Own Id: OTP-8352</p>
+ </item>
+
+ <item>
<p>[httpc] Fix bug crafting Host header when port is not 80. </p>
<p>The host header should include the port number as well as the
host name when making a request to a server listening on a port
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index c4ee4f1fda..5205605e0a 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -28,7 +28,8 @@
-behaviour(inets_service).
%% API
--export([request/1, request/2, request/4, request/5,
+-export([
+ request/1, request/2, request/4, request/5,
cancel_request/1, cancel_request/2,
set_option/2, set_option/3,
set_options/1, set_options/2,
@@ -38,7 +39,9 @@
reset_cookies/0, reset_cookies/1,
stream_next/1,
default_profile/0,
- profile_name/1, profile_name/2]).
+ profile_name/1, profile_name/2,
+ info/0, info/1
+ ]).
%% Behavior callbacks
-export([start_standalone/1, start_service/1,
@@ -314,6 +317,27 @@ which_cookies(Profile) ->
%%--------------------------------------------------------------------------
+%% info() -> list()
+%% info(Profile) -> list()
+%%
+%% Description: Debug function, retreive info about the profile
+%%-------------------------------------------------------------------------
+info() ->
+ info(default_profile()).
+
+info(Profile) ->
+ ?hcrt("info", [{profile, Profile}]),
+ try
+ begin
+ httpc_manager:info(profile_name(Profile))
+ end
+ catch
+ exit:{noproc, _} ->
+ {error, {not_started, Profile}}
+ end.
+
+
+%%--------------------------------------------------------------------------
%% reset_cookies() -> void()
%% reset_cookies(Profile) -> void()
%%
@@ -399,35 +423,34 @@ handle_request(Method, Url,
Headers, ContentType, Body,
HTTPOptions0, Options0, Profile) ->
- Started = http_util:timestamp(),
- NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers],
+ Started = http_util:timestamp(),
+ NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers],
try
begin
- HTTPOptions = http_options(HTTPOptions0),
- Options = request_options(Options0),
- Sync = proplists:get_value(sync, Options),
- Stream = proplists:get_value(stream, Options),
- HeadersRecord =
- header_record(NewHeaders,
- #http_request_h{},
- header_host(Host, Port),
- HTTPOptions#http_options.version),
- Receiver = proplists:get_value(receiver, Options),
- Request = #request{from = Receiver,
- scheme = Scheme,
- address = {Host,Port},
- path = Path,
- pquery = Query,
- method = Method,
- headers = HeadersRecord,
- content = {ContentType,Body},
- settings = HTTPOptions,
- abs_uri = Url,
- userinfo = UserInfo,
- stream = Stream,
+ HTTPOptions = http_options(HTTPOptions0),
+ Options = request_options(Options0),
+ Sync = proplists:get_value(sync, Options),
+ Stream = proplists:get_value(stream, Options),
+ Host2 = header_host(Host, Port),
+ HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions),
+ Receiver = proplists:get_value(receiver, Options),
+ SocketOpts = proplists:get_value(socket_opts, Options),
+ Request = #request{from = Receiver,
+ scheme = Scheme,
+ address = {Host, Port},
+ path = Path,
+ pquery = Query,
+ method = Method,
+ headers = HeadersRecord,
+ content = {ContentType, Body},
+ settings = HTTPOptions,
+ abs_uri = Url,
+ userinfo = UserInfo,
+ stream = Stream,
headers_as_is = headers_as_is(Headers, Options),
- started = Started},
+ socket_opts = SocketOpts,
+ started = Started},
case httpc_manager:request(Request, profile_name(Profile)) of
{ok, RequestId} ->
handle_answer(RequestId, Sync, Options);
@@ -591,6 +614,7 @@ http_options_default() ->
{connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}
].
+
request_options_defaults() ->
VerifyBoolean =
fun(Value) when ((Value =:= true) orelse (Value =:= false)) ->
@@ -640,13 +664,23 @@ request_options_defaults() ->
error
end,
+ VerifySocketOpts =
+ fun([]) ->
+ {ok, undefined};
+ (Value) when is_list(Value) ->
+ ok;
+ (_) ->
+ error
+ end,
+
[
- {sync, true, VerifySync},
- {stream, none, VerifyStream},
- {body_format, string, VerifyBodyFormat},
- {full_result, true, VerifyFullResult},
- {headers_as_is, false, VerifyHeaderAsIs},
- {receiver, self(), VerifyReceiver}
+ {sync, true, VerifySync},
+ {stream, none, VerifyStream},
+ {body_format, string, VerifyBodyFormat},
+ {full_result, true, VerifyFullResult},
+ {headers_as_is, false, VerifyHeaderAsIs},
+ {receiver, self(), VerifyReceiver},
+ {socket_opts, undefined, VerifySocketOpts}
].
request_options(Options) ->
@@ -671,6 +705,9 @@ request_options([{Key, DefaultVal, Verify} | Defaults], Options, Acc) ->
ok ->
Options2 = lists:keydelete(Key, 1, Options),
request_options(Defaults, Options2, [{Key, Value} | Acc]);
+ {ok, Value2} ->
+ Options2 = lists:keydelete(Key, 1, Options),
+ request_options(Defaults, Options2, [{Key, Value2} | Acc]);
error ->
Report = io_lib:format("Invalid option ~p:~p ignored ~n",
[Key, Value]),
@@ -756,6 +793,10 @@ validate_options([{port, Value} = Opt| Tail], Acc) ->
validate_port(Value),
validate_options(Tail, [Opt | Acc]);
+validate_options([{socket_opts, Value} = Opt| Tail], Acc) ->
+ validate_socket_opts(Value),
+ validate_options(Tail, [Opt | Acc]);
+
validate_options([{verbose, Value} = Opt| Tail], Acc) ->
validate_verbose(Value),
validate_options(Tail, [Opt | Acc]);
@@ -836,6 +877,11 @@ validate_port(Value) when is_integer(Value) ->
validate_port(BadValue) ->
bad_option(port, BadValue).
+validate_socket_opts(Value) when is_list(Value) ->
+ Value;
+validate_socket_opts(BadValue) ->
+ bad_option(socket_opts, BadValue).
+
validate_verbose(Value)
when ((Value =:= false) orelse
(Value =:= verbose) orelse
@@ -855,6 +901,9 @@ header_host(Host, Port) ->
Host ++ ":" ++ integer_to_list(Port).
+header_record(NewHeaders, Host, #http_options{version = Version}) ->
+ header_record(NewHeaders, #http_request_h{}, Host, Version).
+
header_record([], RequestHeaders, Host, Version) ->
validate_headers(RequestHeaders, Host, Version);
header_record([{"cache-control", Val} | Rest], RequestHeaders, Host, Version) ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 25f9b0777f..fec74932a2 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -28,8 +28,15 @@
%%--------------------------------------------------------------------
%% Internal Application API
--export([start_link/2, connect_and_send/2,
- send/2, cancel/2, stream/3, stream_next/1]).
+-export([
+ start_link/2,
+ connect_and_send/2,
+ send/2,
+ cancel/2,
+ stream/3,
+ stream_next/1,
+ info/1
+ ]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -131,6 +138,18 @@ stream_next(Pid) ->
%%--------------------------------------------------------------------
+%% Function: info(Pid) -> [{Key, Val}]
+%% Pid = pid() - the pid of the http-request handler process.
+%%
+%% Description:
+%% Returns various information related to this handler
+%% Used for debugging and testing
+%%--------------------------------------------------------------------
+info(Pid) ->
+ call(info, Pid).
+
+
+%%--------------------------------------------------------------------
%% Function: stream(BodyPart, Request, Code) -> _
%% BodyPart = binary()
%% Request = #request{}
@@ -143,21 +162,21 @@ stream_next(Pid) ->
%%--------------------------------------------------------------------
%% Request should not be streamed
stream(BodyPart, Request = #request{stream = none}, _) ->
- ?hcrt("stream - none", [{body_part, BodyPart}]),
+ ?hcrt("stream - none", []),
{BodyPart, Request};
%% Stream to caller
stream(BodyPart, Request = #request{stream = Self}, Code)
when ((Code =:= 200) orelse (Code =:= 206)) andalso
((Self =:= self) orelse (Self =:= {self, once})) ->
- ?hcrt("stream - self", [{stream, Self}, {code, Code}, {body_part, BodyPart}]),
+ ?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
{<<>>, Request};
stream(BodyPart, Request = #request{stream = Self}, 404)
when (Self =:= self) orelse (Self =:= {self, once}) ->
- ?hcrt("stream - self with 404", [{stream, Self}, {body_part, BodyPart}]),
+ ?hcrt("stream - self with 404", [{stream, Self}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
{<<>>, Request};
@@ -167,7 +186,7 @@ stream(BodyPart, Request = #request{stream = Self}, 404)
%% We keep this for backward compatibillity...
stream(BodyPart, Request = #request{stream = Filename}, Code)
when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
- ?hcrt("stream - filename", [{stream, Filename}, {code, Code}, {body_part, BodyPart}]),
+ ?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
?hcrt("stream - file open ok", [{fd, Fd}]),
@@ -179,7 +198,7 @@ stream(BodyPart, Request = #request{stream = Filename}, Code)
%% Stream to file
stream(BodyPart, Request = #request{stream = Fd}, Code)
when ((Code =:= 200) orelse (Code =:= 206)) ->
- ?hcrt("stream to file", [{stream, Fd}, {code, Code}, {body_part, BodyPart}]),
+ ?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
ok ->
{<<>>, Request};
@@ -188,7 +207,7 @@ stream(BodyPart, Request = #request{stream = Fd}, Code)
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
- ?hcrt("stream - ignore", [{request, Request}, {body_part, BodyPart}]),
+ ?hcrt("stream - ignore", [{request, Request}]),
{BodyPart, Request}.
@@ -260,22 +279,22 @@ handle_call({connect_and_send, #request{address = Address0,
end
end;
-handle_call(Request, _,
+handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
session = #tcp_session{socket = Socket,
type = pipeline} = Session,
timers = Timers,
- options = Options,
+ options = #options{proxy = Proxy} = _Options,
profile_name = ProfileName} = State)
when Status =/= undefined ->
- ?hcrv("new request", [{request, Request},
- {profile, ProfileName},
- {status, Status},
- {session_type, pipeline},
- {timers, Timers}]),
+ ?hcrv("new request on a pipeline session",
+ [{request, Request},
+ {profile, ProfileName},
+ {status, Status},
+ {timers, Timers}]),
- Address = handle_proxy(Request#request.address, Options#options.proxy),
+ Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Request, Socket) of
ok ->
@@ -331,21 +350,21 @@ handle_call(Request, _,
{reply, {pipeline_failed, Reason}, State}
end;
-handle_call(Request, _,
+handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
session = #tcp_session{socket = Socket,
type = keep_alive} = Session,
timers = Timers,
- options = Options,
+ options = #options{proxy = Proxy} = _Options,
profile_name = ProfileName} = State)
when Status =/= undefined ->
- ?hcrv("new request", [{request, Request},
- {profile, ProfileName},
- {status, Status},
- {session_type, keep_alive}]),
+ ?hcrv("new request on a keep-alive session",
+ [{request, Request},
+ {profile, ProfileName},
+ {status, Status}]),
- Address = handle_proxy(Request#request.address, Options#options.proxy),
+ Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Request, Socket) of
ok ->
@@ -396,7 +415,12 @@ handle_call(Request, _,
{error, Reason} ->
?hcri("failed sending request", [{reason, Reason}]),
{reply, {request_failed, Reason}, State}
- end.
+ end;
+
+
+handle_call(info, _, State) ->
+ Info = handler_info(State),
+ {reply, Info, State}.
%%--------------------------------------------------------------------
@@ -441,8 +465,7 @@ handle_cast({cancel, RequestId},
{noreply, State#state{canceled = [RequestId | Canceled]}};
handle_cast(stream_next, #state{session = Session} = State) ->
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket, [{active, once}]),
+ activate_once(Session),
{noreply, State#state{once = once}}.
@@ -453,7 +476,7 @@ handle_cast(stream_next, #state{session = Session} = State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({Proto, _Socket, Data},
- #state{mfa = {Module, Function, Args} = MFA,
+ #state{mfa = {Module, Function, Args},
request = #request{method = Method,
stream = Stream} = Request,
session = Session,
@@ -463,8 +486,8 @@ handle_info({Proto, _Socket, Data},
(Proto =:= httpc_handler) ->
?hcri("received data", [{proto, Proto},
- {data, Data},
- {mfa, MFA},
+ {module, Module},
+ {function, Function},
{method, Method},
{stream, Stream},
{session, Session},
@@ -473,14 +496,13 @@ handle_info({Proto, _Socket, Data},
FinalResult =
try Module:Function([Data | Args]) of
{ok, Result} ->
- ?hcrd("data processed - ok", [{result, Result}]),
+ ?hcrd("data processed - ok", []),
handle_http_msg(Result, State);
{_, whole_body, _} when Method =:= head ->
?hcrd("data processed - whole body", []),
handle_response(State#state{body = <<>>});
{Module, whole_body, [Body, Length]} ->
- ?hcrd("data processed - whole body",
- [{module, Module}, {body, Body}, {length, Length}]),
+ ?hcrd("data processed - whole body", [{length, Length}]),
{_, Code, _} = StatusLine,
{NewBody, NewRequest} = stream(Body, Request, Code),
%% When we stream we will not keep the already
@@ -498,25 +520,25 @@ handle_info({Proto, _Socket, Data},
{noreply, NewState#state{mfa = NewMFA,
request = NewRequest}};
NewMFA ->
- ?hcrd("data processed", [{new_mfa, NewMFA}]),
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ ?hcrd("data processed - new mfa", []),
+ activate_once(Session),
{noreply, State#state{mfa = NewMFA}}
catch
- exit:_ ->
+ exit:_Exit ->
+ ?hcrd("data processing exit", [{exit, _Exit}]),
ClientReason = {could_not_parse_as_http, Data},
ClientErrMsg = httpc_response:error(Request, ClientReason),
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState};
- error:_ ->
+ error:_Error ->
+ ?hcrd("data processing error", [{error, _Error}]),
ClientReason = {could_not_parse_as_http, Data},
ClientErrMsg = httpc_response:error(Request, ClientReason),
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState}
end,
- ?hcri("data processed", [{result, FinalResult}]),
+ ?hcri("data processed", []),
FinalResult;
@@ -667,6 +689,9 @@ terminate(normal,
request = Request,
timers = Timers,
pipeline = Pipeline}) ->
+ ?hcrt("terminate(normal) - remote close",
+ [{id, Id}, {profile, ProfileName}]),
+
%% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
@@ -776,19 +801,22 @@ new_queue(Queue, Fun) ->
end, List),
queue:from_list(NewList).
-%%--------------------------------------------------------------------
+
+%%%--------------------------------------------------------------------
%%% Internal functions
-%%--------------------------------------------------------------------
+%%%--------------------------------------------------------------------
-connect(SocketType, ToAddress, #options{ipfamily = IpFamily,
- ip = FromAddress,
- port = FromPort}, Timeout) ->
+connect(SocketType, ToAddress,
+ #options{ipfamily = IpFamily,
+ ip = FromAddress,
+ port = FromPort,
+ socket_opts = Opts0}, Timeout) ->
Opts1 =
case FromPort of
default ->
- [];
+ Opts0;
_ ->
- [{port, FromPort}]
+ [{port, FromPort} | Opts0]
end,
Opts2 =
case FromAddress of
@@ -814,10 +842,10 @@ connect(SocketType, ToAddress, #options{ipfamily = IpFamily,
end.
connect_and_send_first_request(Address,
- #request{settings = Settings,
- headers = Headers,
- address = OrigAddress,
- scheme = Scheme} = Request,
+ #request{settings = Settings,
+ headers = Headers,
+ address = OrigAddress,
+ scheme = Scheme} = Request,
#state{options = Options} = State) ->
?hcrd("connect",
@@ -841,13 +869,13 @@ connect_and_send_first_request(Address,
client_close = ClientClose,
type = SessionType},
TmpState =
- State#state{request = Request,
- session = Session,
- mfa = init_mfa(Request, State),
+ State#state{request = Request,
+ session = Session,
+ mfa = init_mfa(Request, State),
status_line = init_status_line(Request),
- headers = undefined,
- body = undefined,
- status = new},
+ headers = undefined,
+ body = undefined,
+ status = new},
?hcrt("activate socket", []),
activate_once(Session),
NewState = activate_request_timeout(TmpState),
@@ -867,12 +895,87 @@ connect_and_send_first_request(Address,
{stop, Error, State#state{request = Request}}
end.
+
+handler_info(#state{request = Request,
+ session = Session,
+ status_line = _StatusLine,
+ pipeline = Pipeline,
+ keep_alive = KeepAlive,
+ status = Status,
+ canceled = _Canceled,
+ options = _Options,
+ timers = _Timers} = _State) ->
+
+ ?hcrt("handler info", [{request, Request},
+ {session, Session},
+ {pipeline, Pipeline},
+ {keep_alive, KeepAlive},
+ {status, Status}]),
+
+ %% Info about the current request
+ RequestInfo =
+ case Request of
+ undefined ->
+ [];
+ #request{id = Id,
+ started = ReqStarted} ->
+ [{id, Id}, {started, ReqStarted}]
+ end,
+
+ ?hcrt("handler info", [{request_info, RequestInfo}]),
+
+ %% Info about the current session/socket
+ SessionType = Session#tcp_session.type,
+ QueueLen = case Session#tcp_session.type of
+ pipeline ->
+ queue:len(Pipeline);
+ keep_alive ->
+ queue:len(KeepAlive)
+ end,
+ Socket = Session#tcp_session.socket,
+ Scheme = Session#tcp_session.scheme,
+ SocketType = socket_type(Scheme),
+
+ ?hcrt("handler info", [{session_type, SessionType},
+ {queue_length, QueueLen},
+ {scheme, Scheme},
+ {socket_type, SocketType},
+ {socket, Socket}]),
+
+ SocketOpts = http_transport:getopts(SocketType, Socket),
+ SocketStats = http_transport:getstat(SocketType, Socket),
+
+ Remote = http_transport:peername(SocketType, Socket),
+ Local = http_transport:sockname(SocketType, Socket),
+
+ ?hcrt("handler info", [{remote, Remote},
+ {local, Local},
+ {socket_opts, SocketOpts},
+ {socket_stats, SocketStats}]),
+
+ SocketInfo = [{remote, Remote},
+ {local, Local},
+ {socket_opts, SocketOpts},
+ {socket_stats, SocketStats}],
+
+ SessionInfo =
+ [{type, SessionType},
+ {queue_length, QueueLen},
+ {scheme, Scheme},
+ {socket_info, SocketInfo}],
+
+ [{status, Status},
+ {current_request, RequestInfo},
+ {session, SessionInfo}].
+
+
+
handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
State = #state{request = Request}) ->
- ?hcrt("handle_http_msg", [{body, Body}]),
+ ?hcrt("handle_http_msg", [{headers, Headers}]),
case Headers#http_response_h.'content-type' of
"multipart/byteranges" ++ _Param ->
- exit({not_yet_implemented, multypart_nyteranges});
+ exit({not_yet_implemented, multypart_byteranges});
_ ->
StatusLine = {Version, StatusCode, ReasonPharse},
{ok, NewRequest} = start_stream(StatusLine, Headers, Request),
@@ -883,11 +986,11 @@ handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body},
end;
handle_http_msg({ChunkedHeaders, Body}, #state{headers = Headers} = State) ->
?hcrt("handle_http_msg",
- [{chunked_headers, ChunkedHeaders}, {body, Body}]),
+ [{chunked_headers, ChunkedHeaders}, {headers, Headers}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
handle_response(State#state{headers = NewHeaders, body = Body});
handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
- ?hcrt("handle_http_msg", [{body, Body}, {code, Code}]),
+ ?hcrt("handle_http_msg", [{code, Code}]),
{NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
@@ -903,15 +1006,12 @@ handle_http_body(<<>>, State = #state{request = #request{method = head}}) ->
?hcrt("handle_http_body - head", []),
handle_response(State#state{body = <<>>});
-handle_http_body(Body, State = #state{headers = Headers,
- max_body_size = MaxBodySize,
- status_line = {_,Code, _},
- request = Request}) ->
+handle_http_body(Body, #state{headers = Headers,
+ max_body_size = MaxBodySize,
+ status_line = {_,Code, _},
+ request = Request} = State) ->
?hcrt("handle_http_body",
- [{headers, Headers},
- {body, Body},
- {max_body_size, MaxBodySize},
- {code, Code}]),
+ [{max_body_size, MaxBodySize}, {headers, Headers}, {code, Code}]),
TransferEnc = Headers#http_response_h.'transfer-encoding',
case case_insensitive_header(TransferEnc) of
"chunked" ->
@@ -1000,9 +1100,7 @@ handle_response(#state{request = Request,
Session#tcp_session.socket,
RequestBody),
%% Wait for next response
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ activate_once(Session),
Relaxed = (Request#request.settings)#http_options.relaxed,
MFA = {httpc_response, parse,
[State#state.max_header_size, Relaxed]},
@@ -1038,7 +1136,7 @@ handle_response(#state{request = Request,
ok = httpc_manager:retry_request(TimeNewRequest, ProfileName),
handle_queue(State#state{request = undefined}, Data);
{ok, Msg, Data} ->
- ?hcrd("handle response - ok", [{msg, Msg}, {data, Data}]),
+ ?hcrd("handle response - ok", []),
end_stream(StatusLine, Request),
NewState = answer_request(Request, Msg, State),
handle_queue(NewState, Data);
@@ -1137,10 +1235,7 @@ handle_pipeline(#state{status = pipeline,
body = undefined},
case Data of
<<>> ->
- http_transport:setopts(
- socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ activate_once(Session),
{noreply, NewState};
_ ->
%% If we already received some bytes of
@@ -1164,14 +1259,12 @@ handle_keep_alive_queue(
case queue:out(State#state.keep_alive) of
{empty, _} ->
- ?hcrd("epmty keep_alive queue", []),
+ ?hcrd("empty keep_alive queue", []),
%% The server may choose too terminate an idle keep_alive session
%% in this case we want to receive the close message
%% at once and not when trying to send the next
%% request.
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ activate_once(Session),
%% If a keep_alive session has been idle for some time is not
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
@@ -1276,9 +1369,8 @@ is_keep_alive_enabled_server("HTTP/1.0",
is_keep_alive_enabled_server(_,_) ->
false.
-is_keep_alive_connection(Headers, Session) ->
- (not ((Session#tcp_session.client_close) or
- httpc_response:is_server_closing(Headers))).
+is_keep_alive_connection(Headers, #tcp_session{client_close = ClientClose}) ->
+ (not ((ClientClose) orelse httpc_response:is_server_closing(Headers))).
try_to_enable_pipeline_or_keep_alive(
#state{session = Session,
@@ -1286,8 +1378,12 @@ try_to_enable_pipeline_or_keep_alive(
status_line = {Version, _, _},
headers = Headers,
profile_name = ProfileName} = State) ->
- case (is_keep_alive_enabled_server(Version, Headers) andalso
- is_keep_alive_connection(Headers, Session)) of
+ ?hcrd("try to enable pipeline or keep-alive",
+ [{version, Version},
+ {headers, Headers},
+ {session, Session}]),
+ case is_keep_alive_enabled_server(Version, Headers) andalso
+ is_keep_alive_connection(Headers, Session) of
true ->
case (is_pipeline_enabled_client(Session) andalso
httpc_request:is_idempotent(Method)) of
@@ -1307,7 +1403,7 @@ try_to_enable_pipeline_or_keep_alive(
end.
answer_request(Request, Msg, #state{timers = Timers} = State) ->
- ?hcrt("answer request", [{request, Request}, {msg, Msg}]),
+ ?hcrt("answer request", [{request, Request}]),
httpc_response:send(Request#request.from, Msg),
RequestTimers = Timers#timers.request_timers,
TimerRef =
@@ -1435,7 +1531,7 @@ socket_type(#request{scheme = https, settings = Settings}) ->
socket_type(http) ->
ip_comm;
socket_type(https) ->
- {ssl, []}. %% Dummy value ok for ex setops that does not use this value
+ {ssl, []}. %% Dummy value ok for ex setopts that does not use this value
start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
#request{stream = none} = Request) ->
@@ -1529,6 +1625,7 @@ handle_verbose(trace) ->
handle_verbose(_) ->
ok.
+
%%% Normaly I do not comment out code, I throw it away. But this might
%%% actually be used one day if ssl is improved.
%% send_ssl_tunnel_request(Address, Request = #request{address = {Host, Port}},
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 4c5e6ed5d8..4d76c4beb3 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -46,7 +46,7 @@
%% bool() - true if auto redirect on 30x response
autoredirect = true,
- %% Ssl socket options
+ %% ssl socket options
ssl = [],
%% {User, Password} = {string(), string()}
@@ -63,19 +63,20 @@
%%% HTTP Client per profile setting.
-record(options,
{
- proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]},
- %% 0 means persistent connections are used without pipelining
- pipeline_timeout = ?HTTP_PIPELINE_TIMEOUT,
- max_pipeline_length = ?HTTP_PIPELINE_LENGTH,
- max_keep_alive_length = ?HTTP_KEEP_ALIVE_LENGTH,
- keep_alive_timeout = ?HTTP_KEEP_ALIVE_TIMEOUT, % Used when pipeline_timeout = 0
- max_sessions = ?HTTP_MAX_TCP_SESSIONS,
- cookies = disabled, % enabled | disabled | verify
- verbose = false,
- ipfamily = inet, % inet | inet6 | inet6fb4
- ip = default, % specify local interface
- port = default % specify local port
- }
+ proxy = {undefined, []}, % {{ProxyHost, ProxyPort}, [NoProxy]},
+ %% 0 means persistent connections are used without pipelining
+ pipeline_timeout = ?HTTP_PIPELINE_TIMEOUT,
+ max_pipeline_length = ?HTTP_PIPELINE_LENGTH,
+ max_keep_alive_length = ?HTTP_KEEP_ALIVE_LENGTH,
+ keep_alive_timeout = ?HTTP_KEEP_ALIVE_TIMEOUT, % Used when pipeline_timeout = 0
+ max_sessions = ?HTTP_MAX_TCP_SESSIONS,
+ cookies = disabled, % enabled | disabled | verify
+ verbose = false,
+ ipfamily = inet, % inet | inet6 | inet6fb4
+ ip = default, % specify local interface
+ port = default, % specify local port
+ socket_opts = [] % other socket options
+ }
).
%%% All data associated to a specific HTTP request
@@ -98,7 +99,8 @@
headers_as_is, % Boolean() - workaround for servers that does
% not honor the http standard, can also be used for testing purposes.
started, % integer() > 0 - When we started processing the request
- timer % undefined | ref()
+ timer, % undefined | ref()
+ socket_opts % undefined | [socket_option()]
}
).
@@ -109,7 +111,7 @@
scheme, % http (HTTP/TCP) | https (HTTP/SSL/TCP)
socket, % Open socket, used by connection
queue_length = 1, % Current length of pipeline or keep alive queue
- type % pipeline | keep_alive (wait for response before sending new request)
+ type % pipeline | keep_alive (wait for response before sending new request)
}).
-record(http_cookie,
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 433724f190..746517eed5 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,7 +19,7 @@
APPLICATION = inets
INETS_VSN = 5.3
-PRE_VSN =-p12
+PRE_VSN =-p13
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
TICKETS = \
@@ -32,6 +32,7 @@ TICKETS = \
OTP-8327 \
OTP-8349 \
OTP-8351 \
+ OTP-8352 \
OTP-8359 \
OTP-8371