aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src/http_client
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets/src/http_client')
-rw-r--r--lib/inets/src/http_client/Makefile19
-rw-r--r--lib/inets/src/http_client/httpc.erl32
-rw-r--r--lib/inets/src/http_client/httpc_cookie.erl43
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl181
-rw-r--r--lib/inets/src/http_client/httpc_handler_sup.erl19
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl19
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl21
-rw-r--r--lib/inets/src/http_client/httpc_profile_sup.erl19
-rw-r--r--lib/inets/src/http_client/httpc_request.erl22
-rw-r--r--lib/inets/src/http_client/httpc_response.erl21
-rw-r--r--lib/inets/src/http_client/httpc_sup.erl21
11 files changed, 252 insertions, 165 deletions
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index f0d4ce139e..cb97b525f6 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -3,16 +3,17 @@
#
# Copyright Ericsson AB 2005-2012. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
#
# %CopyrightEnd%
#
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index da9bbdd1ec..4554881d79 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -100,7 +101,8 @@ request(Url, Profile) ->
%% {ok, {StatusLine, Headers, Body}} | {ok, {Status, Body}} |
%% {ok, RequestId} | {error,Reason} | {ok, {saved_as, FilePath}
%%
-%% Method - atom() = head | get | put | post | trace | options| delete
+%% Method - atom() = head | get | put | patch | post | trace |
+%% options | delete
%% Request - {Url, Headers} | {Url, Headers, ContentType, Body}
%% Url - string()
%% HTTPOptions - [HttpOption]
@@ -175,8 +177,8 @@ request(Method,
request(Method,
{Url, Headers, ContentType, Body},
HTTPOptions, Options, Profile)
- when ((Method =:= post) orelse (Method =:= put) orelse (Method =:= delete)) andalso
- (is_atom(Profile) orelse is_pid(Profile)) ->
+ when ((Method =:= post) orelse (Method =:= patch) orelse (Method =:= put) orelse
+ (Method =:= delete)) andalso (is_atom(Profile) orelse is_pid(Profile)) ->
?hcrt("request", [{method, Method},
{url, Url},
{headers, Headers},
@@ -554,7 +556,7 @@ handle_request(Method, Url,
Request = #request{from = Receiver,
scheme = Scheme,
- address = {Host, Port},
+ address = {host_address(Host, BracketedHost), Port},
path = MaybeEscPath,
pquery = MaybeEscQuery,
method = Method,
@@ -1266,3 +1268,7 @@ child_name(Pid, [_ | Children]) ->
%% d(_, _, _) ->
%% ok.
+host_address(Host, false) ->
+ Host;
+host_address(Host, true) ->
+ string:strip(string:strip(Host, right, $]), left, $[).
diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl
index 134115bdfa..0c4f44a575 100644
--- a/lib/inets/src/http_client/httpc_cookie.erl
+++ b/lib/inets/src/http_client/httpc_cookie.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -115,8 +116,8 @@ maybe_dets_close(Db) ->
%%--------------------------------------------------------------------
-%% Func: insert(CookieDb) -> ok
-%% Purpose: Close the cookie db
+%% Func: insert(CookieDb, Cookie) -> ok
+%% Purpose: insert cookies into the cookie db
%%--------------------------------------------------------------------
%% If no persistent cookie database is defined we
@@ -334,9 +335,23 @@ add_domain(Str, #http_cookie{domain_default = true}) ->
add_domain(Str, #http_cookie{domain = Domain}) ->
Str ++ "; $Domain=" ++ Domain.
+is_set_cookie_valid("") ->
+ %% an empty Set-Cookie header is not valid
+ false;
+is_set_cookie_valid([$=|_]) ->
+ %% a Set-Cookie header without name is not valid
+ false;
+is_set_cookie_valid(SetCookieHeader) ->
+ %% a Set-Cookie header without name/value is not valid
+ case string:chr(SetCookieHeader, $=) of
+ 0 -> false;
+ _ -> true
+ end.
+
parse_set_cookies(CookieHeaders, DefaultPathDomain) ->
- %% empty Set-Cookie header is invalid according to RFC but some sites violate it
- SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders, Value /= ""],
+ %% filter invalid Set-Cookie headers
+ SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders,
+ is_set_cookie_valid(Value)],
Cookies = [parse_set_cookie(SetCookieHeader, DefaultPathDomain) ||
SetCookieHeader <- SetCookieHeaders],
%% print_cookies("Parsed Cookies", Cookies),
@@ -348,6 +363,8 @@ parse_set_cookie(CookieHeader, {DefaultPath, DefaultDomain}) ->
Name = string:substr(CookieHeader, 1, Pos - 1),
{Value, Attrs} =
case string:substr(CookieHeader, Pos + 1) of
+ [] ->
+ {"", ""};
[$;|ValueAndAttrs] ->
{"", string:tokens(ValueAndAttrs, ";")};
ValueAndAttrs ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 88e08be789..d1c52dcc78 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -25,6 +26,7 @@
-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
+-define(IS_STREAMED(Code), ((Code =:= 200) orelse (Code =:= 206))).
%%--------------------------------------------------------------------
%% Internal Application API
@@ -87,7 +89,7 @@
%% block the httpc manager process in odd cases such as trying to call
%% a server that does not exist. (See OTP-6735) The only API function
%% sending messages to the handler process that can be called before
-%% init has compleated is cancel and that is not a problem! (Send and
+%% init has completed is cancel and that is not a problem! (Send and
%% stream will not be called before the first request has been sent and
%% the reply or part of it has arrived.)
%%--------------------------------------------------------------------
@@ -162,22 +164,22 @@ info(Pid) ->
%% Request should not be streamed
stream(BodyPart, #request{stream = none} = Request, _) ->
?hcrt("stream - none", []),
- {BodyPart, Request};
+ {false, BodyPart, Request};
%% Stream to caller
stream(BodyPart, #request{stream = Self} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso
+ when ?IS_STREAMED(Code) andalso
((Self =:= self) orelse (Self =:= {self, once})) ->
?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
- {<<>>, Request};
+ {true, <<>>, Request};
%% Stream to file
%% This has been moved to start_stream/3
%% We keep this for backward compatibillity...
stream(BodyPart, #request{stream = Filename} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -189,18 +191,18 @@ stream(BodyPart, #request{stream = Filename} = Request, Code)
%% Stream to file
stream(BodyPart, #request{stream = Fd} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
ok ->
- {<<>>, Request};
+ {true, <<>>, Request};
{error, Reason} ->
exit({stream_to_file_failed, Reason})
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
?hcrt("stream - ignore", [{request, Request}]),
- {BodyPart, Request}.
+ {false, BodyPart, Request}.
%%====================================================================
@@ -316,8 +318,9 @@ handle_call(#request{address = Addr} = Request, _,
{reply, ok, State}
end;
{error, Reason} ->
- ?hcri("failed sending request", [{reason, Reason}]),
- {reply, {pipeline_failed, Reason}, State0}
+ ?hcri("failed sending request", [{reason, Reason}]),
+ NewPipeline = queue:in(Request, State0#state.pipeline),
+ {stop, shutdown, {pipeline_failed, Reason}, State0#state{pipeline = NewPipeline}}
end;
handle_call(#request{address = Addr} = Request, _,
@@ -350,30 +353,30 @@ handle_call(#request{address = Addr} = Request, _,
{reply, ok, State0#state{keep_alive = NewKeepAlive,
session = NewSession}};
undefined ->
- %% Note: tcp-message reciving has already been
+ %% Note: tcp-message receiving has already been
%% activated by handle_pipeline/2.
?hcrd("no current request", []),
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
+ NewTimers = Timers#timers{queue_timer = undefined},
+ State1 = State0#state{timers = NewTimers},
Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, Request) of
ok ->
?hcrd("request sent", []),
%% Activate the request time out for the new request
- State1 =
- activate_request_timeout(State0#state{request = Request}),
- NewTimers = State1#state.timers,
+ State2 =
+ activate_request_timeout(State1#state{request = Request}),
NewSession =
Session#session{queue_length = 1,
client_close = ClientClose},
insert_session(NewSession, ProfileName),
- State = init_wait_for_response_state(Request, State1#state{session = NewSession,
- timers = NewTimers}),
+ State = init_wait_for_response_state(Request, State2#state{session = NewSession}),
{reply, ok, State};
{error, Reason} ->
?hcri("failed sending request", [{reason, Reason}]),
- {reply, {request_failed, Reason}, State0}
+ {stop, shutdown, {keepalive_failed, Reason}, State1}
end
end;
@@ -391,7 +394,7 @@ handle_call(info, _, State) ->
%% When the request in process has been canceled the handler process is
%% stopped and the pipelined requests will be reissued or remaining
%% requests will be sent on a new connection. This is is
-%% based on the assumption that it is proably cheaper to reissue the
+%% based on the assumption that it is probably cheaper to reissue the
%% requests than to wait for a potentiall large response that we then
%% only throw away. This of course is not always true maybe we could
%% do something smarter here?! If the request canceled is not
@@ -419,6 +422,16 @@ handle_cast({cancel, RequestId},
{profile, ProfileName},
{canceled, Canceled}]),
{noreply, State#state{canceled = [RequestId | Canceled]}};
+handle_cast({cancel, RequestId},
+ #state{profile_name = ProfileName,
+ request = undefined,
+ canceled = Canceled} = State) ->
+ ?hcrv("cancel", [{request_id, RequestId},
+ {curr_req_id, undefined},
+ {profile, ProfileName},
+ {canceled, Canceled}]),
+ {noreply, State};
+
handle_cast(stream_next, #state{session = Session} = State) ->
activate_once(Session),
@@ -462,18 +475,18 @@ handle_info({Proto, _Socket, Data},
{Module, whole_body, [Body, Length]} ->
?hcrd("data processed - whole body", [{length, Length}]),
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(Body, Request, Code),
+ {Streamed, NewBody, NewRequest} = stream(Body, Request, Code),
%% When we stream we will not keep the already
%% streamed data, that would be a waste of memory.
NewLength =
- case Stream of
- none ->
+ case Streamed of
+ false ->
Length;
- _ ->
+ true ->
Length - size(Body)
end,
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, whole_body, [NewBody, NewLength]},
{noreply, NewState#state{mfa = NewMFA,
request = NewRequest}};
@@ -485,8 +498,8 @@ handle_info({Proto, _Socket, Data},
%% The response body is chunk-encoded. Steal decoded
%% chunks as much as possible to stream.
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(BodySoFar, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(BodySoFar, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_size,
[TotalChunk, HexList,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -505,8 +518,8 @@ handle_info({Proto, _Socket, Data},
NewChunkSize = ChunkSize - ChunkSizeToSteal,
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(StolenBody, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(StolenBody, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_data,
[NewChunkSize, NewTotalChunk,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -632,7 +645,7 @@ handle_info({timeout, RequestId},
handle_info(timeout_queue, State = #state{request = undefined}) ->
{stop, normal, State};
-%% Timing was such as the pipeline_timout was not canceled!
+%% Timing was such as the queue_timeout was not canceled!
handle_info(timeout_queue, #state{timers = Timers} = State) ->
{noreply, State#state{timers =
Timers#timers{queue_timer = undefined}}};
@@ -1059,13 +1072,13 @@ handle_http_msg({ChunkedHeaders, Body},
?hcrt("handle_http_msg",
[{chunked_headers, ChunkedHeaders}, {headers, Headers}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{headers = NewHeaders,
body = NewBody,
request = NewRequest});
handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
?hcrt("handle_http_msg", [{code, Code}]),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
handle_http_body(_, #state{status = {ssl_tunnel, _},
@@ -1100,14 +1113,14 @@ handle_http_body(Body, #state{headers = Headers,
case case_insensitive_header(TransferEnc) of
"chunked" ->
?hcrt("handle_http_body - chunked", []),
- case http_chunk:decode(Body, State#state.max_body_size,
- State#state.max_header_size) of
+ try http_chunk:decode(Body, State#state.max_body_size,
+ State#state.max_header_size) of
{Module, Function, Args} ->
?hcrt("handle_http_body - new mfa",
[{module, Module},
{function, Function},
{args, Args}]),
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa =
{Module, Function, Args}}};
{ok, {ChunkedHeaders, NewBody}} ->
@@ -1116,8 +1129,23 @@ handle_http_body(Body, #state{headers = Headers,
{new_body, NewBody}]),
NewHeaders = http_chunk:handle_headers(Headers,
ChunkedHeaders),
- handle_response(State#state{headers = NewHeaders,
- body = NewBody})
+ case Body of
+ <<>> ->
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody});
+ _ ->
+ {_, NewBody2, _} =
+ stream(NewBody, Request, Code),
+ handle_response(State#state{headers = NewHeaders,
+ body = NewBody2})
+ end
+ catch throw:{error, Reason} ->
+ NewState =
+ answer_request(Request,
+ httpc_response:error(Request,
+ Reason),
+ State),
+ {stop, normal, NewState}
end;
Enc when Enc =:= "identity"; Enc =:= undefined ->
?hcrt("handle_http_body - identity", []),
@@ -1127,12 +1155,12 @@ handle_http_body(Body, #state{headers = Headers,
true ->
case httpc_response:whole_body(Body, Length) of
{ok, Body} ->
- {NewBody, NewRequest} =
+ {_, NewBody, NewRequest} =
stream(Body, Request, Code),
handle_response(State#state{body = NewBody,
request = NewRequest});
MFA ->
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa = MFA}}
end;
false ->
@@ -1218,6 +1246,7 @@ handle_response(#state{request = Request,
handle_queue(State#state{request = undefined}, Data);
{ok, Msg, Data} ->
?hcrd("handle response - ok", []),
+ stream_remaining_body(Body, Request, StatusLine),
end_stream(StatusLine, Request),
NewState = maybe_send_answer(Request, Msg, State),
handle_queue(NewState, Data);
@@ -1292,7 +1321,8 @@ handle_pipeline(#state{status = pipeline,
handle_keep_alive_queue(#state{status = keep_alive,
session = Session,
profile_name = ProfileName,
- options = #options{keep_alive_timeout = TimeOut}} = State,
+ options = #options{keep_alive_timeout = TimeOut,
+ proxy = Proxy}} = State,
Data) ->
?hcrd("handle keep_alive", [{profile, ProfileName},
@@ -1313,14 +1343,15 @@ handle_keep_alive_queue(#state{status = keep_alive,
State#state{keep_alive = KeepAlive}, Data);
false ->
?hcrv("next request", [{request, NextRequest}]),
- #request{address = Address} = NextRequest,
+ #request{address = Addr} = NextRequest,
+ Address = handle_proxy(Addr, Proxy),
case httpc_request:send(Address, Session, NextRequest) of
ok ->
receive_response(NextRequest,
Session, <<>>,
State#state{keep_alive = KeepAlive});
{error, Reason} ->
- {reply, {keep_alive_failed, Reason}, State}
+ {stop, {shutdown, {keepalive_failed, Reason}}, State}
end
end
end.
@@ -1335,7 +1366,7 @@ handle_empty_queue(Session, ProfileName, TimeOut, State) ->
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
update_session(ProfileName, Session, #session.queue_length, 0),
- %% Note mfa will be initilized when a new request
+ %% Note mfa will be initialized when a new request
%% arrives.
{noreply,
NewState#state{request = undefined,
@@ -1378,6 +1409,8 @@ case_insensitive_header(Str) ->
activate_once(#session{socket = Socket, socket_type = SocketType}) ->
http_transport:setopts(SocketType, Socket, [{active, once}]).
+close_socket(#session{socket = {remote_close,_}}) ->
+ ok;
close_socket(#session{socket = Socket, socket_type = SocketType}) ->
http_transport:close(SocketType, Socket).
@@ -1621,21 +1654,21 @@ start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = self} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, ignore),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = {self, once}} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self:once", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, self()),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, _Headers,
#request{stream = Filename} = Request)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("start stream", [{code, Code}, {filename, Filename}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -1648,6 +1681,10 @@ start_stream(_StatusLine, _Headers, Request) ->
?hcrt("start stream - no op", []),
{ok, Request}.
+stream_remaining_body(<<>>, _, _) ->
+ ok;
+stream_remaining_body(Body, Request, {_, Code, _}) ->
+ stream(Body, Request, Code).
%% Note the end stream message is handled by httpc_response and will
%% be sent by answer_request
@@ -1683,13 +1720,15 @@ end_stream(SL, R) ->
next_body_chunk(#state{request = #request{stream = {self, once}},
once = once,
- session = Session} = State) ->
+ session = Session} = State,
+ Code) when ?IS_STREAMED(Code) ->
activate_once(Session),
State#state{once = inactive};
next_body_chunk(#state{request = #request{stream = {self, once}},
- once = inactive} = State) ->
+ once = inactive} = State,
+ Code) when ?IS_STREAMED(Code) ->
State; %% Wait for user to call stream_next
-next_body_chunk(#state{session = Session} = State) ->
+next_body_chunk(#state{session = Session} = State, _) ->
activate_once(Session),
State.
@@ -1780,7 +1819,7 @@ tls_tunnel_request(#request{headers = Headers,
host_header(#http_request_h{host = Host}, _) ->
Host;
-%% Handles header_as_is
+%% Handles headers_as_is
host_header(_, URI) ->
{ok, {_, _, Host, _, _, _}} = http_uri:parse(URI),
Host.
@@ -1788,13 +1827,15 @@ host_header(_, URI) ->
tls_upgrade(#state{status =
{ssl_tunnel,
#request{settings =
- #http_options{ssl = {_, TLSOptions} = SocketType}} = Request},
+ #http_options{ssl = {_, TLSOptions0} = SocketType},
+ address = {Host, _} = Address} = Request},
session = #session{socket = TCPSocket} = Session0,
options = Options} = State) ->
+ TLSOptions = maybe_add_sni(Host, TLSOptions0),
+
case ssl:connect(TCPSocket, TLSOptions) of
{ok, TLSSocket} ->
- Address = Request#request.address,
ClientClose = httpc_request:is_client_closing(Request#request.headers),
SessionType = httpc_manager:session_type(Options),
Session = Session0#session{
@@ -1815,10 +1856,23 @@ tls_upgrade(#state{status =
status = new
},
{noreply, activate_request_timeout(NewState)};
- {error, _Reason} ->
+ {error, Reason} ->
+ Error = httpc_response:error(Request, {failed_connect,
+ [{to_address, Address},
+ {tls, TLSOptions, Reason}]}),
+ maybe_send_answer(Request, Error, State),
{stop, normal, State#state{request = Request}}
end.
+maybe_add_sni(Host, Options) ->
+ case http_util:is_hostname(Host) andalso
+ not lists:keymember(server_name_indication, 1, Options) of
+ true ->
+ [{server_name_indication, Host} | Options];
+ false ->
+ Options
+ end.
+
%% ---------------------------------------------------------------------
%% Session wrappers
%% ---------------------------------------------------------------------
@@ -1837,6 +1891,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
Session2 = erlang:setelement(Pos, Session, Value),
insert_session(Session2, ProfileName);
T:E ->
+ Stacktrace = erlang:get_stacktrace(),
error_logger:error_msg("Failed updating session: "
"~n ProfileName: ~p"
"~n SessionId: ~p"
@@ -1860,7 +1915,7 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) ->
{value, Value},
{etype, T},
{error, E},
- {stacktrace, erlang:get_stacktrace()}]})
+ {stacktrace, Stacktrace}]})
end.
diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl
index f7a0b014b3..403512fc25 100644
--- a/lib/inets/src/http_client/httpc_handler_sup.erl
+++ b/lib/inets/src/http_client/httpc_handler_sup.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index add5d11dfa..bb8c0e20fa 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 48a9c32454..c7974836c2 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2002-2014. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_profile_sup.erl b/lib/inets/src/http_client/httpc_profile_sup.erl
index 29f86aa373..b83aeaa4e0 100644
--- a/lib/inets/src/http_client/httpc_profile_sup.erl
+++ b/lib/inets/src/http_client/httpc_profile_sup.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl
index 879053f0f2..af4c3f75f2 100644
--- a/lib/inets/src/http_client/httpc_request.erl
+++ b/lib/inets/src/http_client/httpc_request.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -186,7 +187,8 @@ is_client_closing(Headers) ->
%%% Internal functions
%%%========================================================================
post_data(Method, Headers, {ContentType, Body}, HeadersAsIs)
- when (Method =:= post) orelse (Method =:= put) ->
+ when (Method =:= post) orelse (Method =:= put)
+ orelse (Method =:= patch) ->
NewBody = case Headers#http_request_h.expect of
"100-continue" ->
"";
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index 9107dfbf05..4bf2ba2b9b 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%
@@ -327,7 +328,7 @@ status_service_unavailable(Response = {_, Headers, _}, Request) ->
undefined ->
status_server_error_50x(Response, Request);
Time when (length(Time) < 3) -> % Wait only 99 s or less
- NewTime = list_to_integer(Time) * 100, % time in ms
+ NewTime = list_to_integer(Time) * 1000, % time in ms
{_, Data} = format_response(Response),
{retry, {NewTime, Request}, Data};
_ ->
diff --git a/lib/inets/src/http_client/httpc_sup.erl b/lib/inets/src/http_client/httpc_sup.erl
index 152a57d32d..2b2ee0f34a 100644
--- a/lib/inets/src/http_client/httpc_sup.erl
+++ b/lib/inets/src/http_client/httpc_sup.erl
@@ -3,16 +3,17 @@
%%
%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
%%
%% %CopyrightEnd%
%%