aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2011-06-15 18:30:40 +0200
committerMicael Karlberg <[email protected]>2011-06-15 18:30:40 +0200
commit90a1be0b3540af99c7bc6af6ad7bb3ebb03760d3 (patch)
tree3be88bfc46afbc7e7bc0ac75b6b7403fd61f1b10
parent7c27ae290554d9577ac7c93c5d0c6106b647dc52 (diff)
parenta6e707004f9fcb370f05bb897a6ec975f1128572 (diff)
downloadotp-90a1be0b3540af99c7bc6af6ad7bb3ebb03760d3.tar.gz
otp-90a1be0b3540af99c7bc6af6ad7bb3ebb03760d3.tar.bz2
otp-90a1be0b3540af99c7bc6af6ad7bb3ebb03760d3.zip
[httpc|httpd] Added support for IPv6 with ssl.
OTP-5566 Merge branch 'bmk/inets/handle_ipv6_with_ssl2/OTP-5566' into bmk/inets/inets57_integration2 Conflicts: lib/inets/doc/src/notes.xml lib/inets/src/inets_app/inets.appup.src lib/inets/test/inets_test_lib.erl
-rw-r--r--lib/inets/doc/src/httpc.xml180
-rw-r--r--lib/inets/doc/src/notes.xml21
-rw-r--r--lib/inets/src/http_client/httpc.erl23
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl13
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl6
-rw-r--r--lib/inets/src/http_lib/http_transport.erl193
-rw-r--r--lib/inets/src/inets_app/inets.appup.src12
-rw-r--r--lib/inets/test/httpc_SUITE.erl551
-rw-r--r--lib/inets/test/httpd_SUITE.erl304
-rw-r--r--lib/inets/test/httpd_test_lib.erl91
-rw-r--r--lib/inets/test/inets_test_lib.erl131
11 files changed, 1012 insertions, 513 deletions
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index f6b6827e93..d1671ac9bd 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -144,7 +144,7 @@ filename() = string()
<v>Result = {status_line(), headers(), Body} |
{status_code(), Body} | request_id() </v>
<v>Body = string() | binary()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
<v>Reason = term() </v>
</type>
<desc>
@@ -194,16 +194,16 @@ filename() = string()
<v>Result = {status_line(), headers(), Body} |
{status_code(), Body} | request_id() </v>
<v>Body = string() | binary()</v>
- <v>Profile = profile() </v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
<v>Reason = {connect_failed, term()} |
{send_failed, term()} | term() </v>
</type>
<desc>
<p>Sends a HTTP-request. The function can be both synchronous
- and asynchronous. In the later case the function will return
- <c>{ok, RequestId}</c> and later on the information will be delivered
- to the <c>receiver</c> depending on that value. </p>
+ and asynchronous. In the later case the function will return
+ <c>{ok, RequestId}</c> and later on the information will be delivered
+ to the <c>receiver</c> depending on that value. </p>
<p>Http option (<c>http_option()</c>) details: </p>
<taglist>
@@ -211,7 +211,7 @@ filename() = string()
<item>
<p>Timeout time for the request. </p>
<p>The clock starts ticking as soon as the request has been
- sent. </p>
+ sent. </p>
<p>Time is in milliseconds. </p>
<p>Defaults to <c>infinity</c>. </p>
</item>
@@ -219,7 +219,7 @@ filename() = string()
<tag><c><![CDATA[connect_timeout]]></c></tag>
<item>
<p>Connection timeout time, used during the initial request,
- when the client is <em>connecting</em> to the server. </p>
+ when the client is <em>connecting</em> to the server. </p>
<p>Time is in milliseconds. </p>
<p>Defaults to the value of the <c>timeout</c> option. </p>
</item>
@@ -227,60 +227,61 @@ filename() = string()
<tag><c><![CDATA[ssl]]></c></tag>
<item>
<p>This is the default ssl config option, currently defaults to
- <c>essl</c>, see below. </p>
+ <c>essl</c>, see below. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[ossl]]></c></tag>
<item>
<p>If using the OpenSSL based (old) implementation of SSL,
- these SSL-specific options are used. </p>
+ these SSL-specific options are used. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[essl]]></c></tag>
<item>
<p>If using the Erlang based (new) implementation of SSL,
- these SSL-specific options are used. </p>
+ these SSL-specific options are used. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[autoredirect]]></c></tag>
<item>
- <p>Should the client automatically retrieve the information
- from the new URI and return that as the result instead
- of a 30X-result code. </p>
- <p>Note that for some 30X-result codes automatic redirect
- is not allowed. In these cases the 30X-result will always
- be returned. </p>
- <p>Defaults to <c>true</c>. </p>
+ <p>Should the client automatically retrieve the information
+ from the new URI and return that as the result instead
+ of a 30X-result code. </p>
+ <p>Note that for some 30X-result codes automatic redirect
+ is not allowed. In these cases the 30X-result will always
+ be returned. </p>
+ <p>Defaults to <c>true</c>. </p>
</item>
<tag><c><![CDATA[proxy_auth]]></c></tag>
<item>
<p>A proxy-authorization header using the provided user name and
- password will be added to the request. </p>
+ password will be added to the request. </p>
</item>
<tag><c><![CDATA[version]]></c></tag>
<item>
<p>Can be used to make the client act as an <c>HTTP/1.0</c> or
- <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
- client. When using <c>HTTP/1.0</c> persistent connections will
- not be used. </p>
- <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
+ <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
+ client. When using <c>HTTP/1.0</c> persistent connections will
+ not be used. </p>
+ <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
</item>
<tag><c><![CDATA[relaxed]]></c></tag>
<item>
- <p>If set to <c>true</c> workarounds for known server deviations from
- the HTTP-standard are enabled. </p>
+ <p>If set to <c>true</c> workarounds for known server deviations
+ from the HTTP-standard are enabled. </p>
<p>Defaults to <c>false</c>. </p>
</item>
<tag><c><![CDATA[url_encode]]></c></tag>
<item>
- <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p>
+ <p>Will apply Percent-encoding, also known as URL encoding on the
+ URL.</p>
<p>Defaults to <c>false</c>. </p>
</item>
</taglist>
@@ -296,77 +297,77 @@ filename() = string()
<tag><c><![CDATA[stream]]></c></tag>
<item>
<p>Streams the body of a 200 or 206 response to the calling
- process or to a file. When streaming to the calling process
- using the option <c>self</c> the following stream messages
- will be sent to that process: <c>{http, {RequestId,
- stream_start, Headers}, {http, {RequestId, stream,
- BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
- streaming to to the calling processes using the option
- <c>{self, once}</c> the first message will have an additional
- element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
- this is the process id that should be used as an argument to
- <c>http:stream_next/1</c> to trigger the next message to be sent to
- the calling process. </p>
+ process or to a file. When streaming to the calling process
+ using the option <c>self</c> the following stream messages
+ will be sent to that process: <c>{http, {RequestId,
+ stream_start, Headers}, {http, {RequestId, stream,
+ BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
+ streaming to to the calling processes using the option
+ <c>{self, once}</c> the first message will have an additional
+ element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
+ this is the process id that should be used as an argument to
+ <c>http:stream_next/1</c> to trigger the next message to be sent to
+ the calling process. </p>
<p>Note that it is possible that chunked encoding will add
- headers so that there are more headers in the <c>stream_end</c>
- message than in the <c>stream_start</c>.
- When streaming to a file and the request is asynchronous the
- message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
+ headers so that there are more headers in the <c>stream_end</c>
+ message than in the <c>stream_start</c>.
+ When streaming to a file and the request is asynchronous the
+ message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
<p>Defaults to <c>none</c>. </p>
</item>
<tag><c><![CDATA[body_format]]></c></tag>
<item>
<p>Defines if the body shall be delivered as a string or as a
- binary. This option is only valid for the synchronous
- request. </p>
+ binary. This option is only valid for the synchronous
+ request. </p>
<p>Defaults to <c>string</c>. </p>
</item>
<tag><c><![CDATA[full_result]]></c></tag>
<item>
<p>Should a "full result" be returned to the caller (that is,
- the body, the headers and the entire status-line) or not
- (the body and the status code). </p>
+ the body, the headers and the entire status-line) or not
+ (the body and the status code). </p>
<p>Defaults to <c>true</c>. </p>
</item>
<tag><c><![CDATA[header_as_is]]></c></tag>
<item>
<p>Shall the headers provided by the user be made
- lower case or be regarded as case sensitive. </p>
+ lower case or be regarded as case sensitive. </p>
<p>Note that the http standard requires them to be
- case insenstive. This feature should only be used if there is
- no other way to communicate with the server or for testing
- purpose. Also note that when this option is used no headers
- will be automatically added, all necessary headers have to be
- provided by the user. </p>
- <p>Defaults to <c>false</c>. </p>
+ case insenstive. This feature should only be used if there is
+ no other way to communicate with the server or for testing
+ purpose. Also note that when this option is used no headers
+ will be automatically added, all necessary headers have to be
+ provided by the user. </p>
+ <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>
+ 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>
+ checked in any way. </p>
<p>Note that this may change the socket behaviour
- (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
- for an already existing one, and therefore an already connected
- request handler. </p>
+ (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
+ for an already existing one, and therefore an already connected
+ request handler. </p>
<p>By default the socket options set by the
- <seealso marker="#set_options">set_options/1,2</seealso>
- function are used when establishing a connection. </p>
+ <seealso marker="#set_options">set_options/1,2</seealso>
+ function are used when establishing a connection. </p>
</item>
<tag><c><![CDATA[receiver]]></c></tag>
<item>
<p>Defines how the client will deliver the result of an
- asynchroneous request (<c>sync</c> has the value
- <c>false</c>). </p>
+ asynchroneous request (<c>sync</c> has the value
+ <c>false</c>). </p>
<taglist>
<tag><c><![CDATA[pid()]]></c></tag>
@@ -380,7 +381,7 @@ filename() = string()
<tag><c><![CDATA[function/1]]></c></tag>
<item>
<p>Information will be delivered to the receiver via calls
- to the provided fun: </p>
+ to the provided fun: </p>
<pre>
Receiver(ReplyInfo)
</pre>
@@ -389,7 +390,7 @@ Receiver(ReplyInfo)
<tag><c><![CDATA[{Module, Funcion, Args}]]></c></tag>
<item>
<p>Information will be delivered to the receiver via calls
- to the callback function: </p>
+ to the callback function: </p>
<pre>
apply(Module, Function, [ReplyInfo | Args])
</pre>
@@ -410,7 +411,7 @@ apply(Module, Function, [ReplyInfo | Args])
</pre>
<p>Defaults to the <c>pid()</c> of the process calling the request
- function (<c>self()</c>). </p>
+ function (<c>self()</c>). </p>
</item>
</taglist>
@@ -425,7 +426,7 @@ apply(Module, Function, [ReplyInfo | Args])
<type>
<v>RequestId = request_id() - A unique identifier as returned
by request/4</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Cancels an asynchronous HTTP-request. </p>
@@ -514,11 +515,10 @@ apply(Module, Function, [ReplyInfo | Args])
This option is used to switch on (or off)
different levels of erlang trace on the client.
It is a debug feature.</d>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Sets options to be used for subsequent
- requests.</p>
+ <p>Sets options to be used for subsequent requests.</p>
<note>
<p>If possible the client will keep its connections
alive and use persistent connections
@@ -548,7 +548,7 @@ apply(Module, Function, [ReplyInfo | Args])
</type>
<desc>
<p>Triggers the next message to be streamed, e.i.
- same behavior as active once for sockets.</p>
+ same behavior as active once for sockets. </p>
<marker id="verify_cookies"></marker>
<marker id="store_cookies"></marker>
@@ -562,14 +562,14 @@ apply(Module, Function, [ReplyInfo | Args])
<type>
<v>SetCookieHeaders = headers() - where field = "set-cookie"</v>
<v>Url = url()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Saves the cookies defined in SetCookieHeaders
- in the client profile's cookie database. You need to
- call this function if you have set the option <c>cookies</c> to <c>verify</c>.
- If no profile is specified the default profile will be used.
- </p>
+ in the client profile's cookie database. You need to
+ call this function if you have set the option <c>cookies</c>
+ to <c>verify</c>.
+ If no profile is specified the default profile will be used. </p>
<marker id="cookie_header"></marker>
</desc>
@@ -582,13 +582,12 @@ apply(Module, Function, [ReplyInfo | Args])
making a request to Url using the profile <c>Profile</c>.</fsummary>
<type>
<v>Url = url()</v>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
<p>Returns the cookie header that would be sent
- when making a request to <c>Url</c> using the profile <c>Profile</c>.
- If no profile is specified the default profile will be used.
- </p>
+ when making a request to <c>Url</c> using the profile <c>Profile</c>.
+ If no profile is specified the default profile will be used. </p>
<marker id="reset_cookies"></marker>
</desc>
@@ -600,12 +599,12 @@ apply(Module, Function, [ReplyInfo | Args])
<name>reset_cookies(Profile) -> void()</name>
<fsummary>Reset the cookie database.</fsummary>
<type>
- <v>Profile = profile()</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
</type>
<desc>
- <p>Resets (clears) the cookie database for the specified <c>Profile</c>.
- If no profile is specified the default profile will be used.
- </p>
+ <p>Resets (clears) the cookie database for the specified
+ <c>Profile</c>. If no profile is specified the default profile
+ will be used. </p>
</desc>
</func>
@@ -615,17 +614,16 @@ apply(Module, Function, [ReplyInfo | Args])
<name>which_cookies(Profile) -> cookies()</name>
<fsummary>Dumps out the entire cookie database.</fsummary>
<type>
- <v>Profile = profile()</v>
- <v>cookies() = [cooie_stores()]</v>
- <v>cookie_stores() = {cookies, icookies()} | {session_cookies, icookies()}</v>
- <v>icookies() = [icookie()]</v>
+ <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v>
+ <v>cookies() = [cookie_stores()]</v>
+ <v>cookie_stores() = {cookies, cookies()} | {session_cookies, cookies()}</v>
+ <v>cookies() = [cookie()]</v>
<v>cookie() = term()</v>
</type>
<desc>
<p>This function produces a list of the entire cookie database.
- It is intended for debugging/testing purposes.
- If no profile is specified the default profile will be used.
- </p>
+ It is intended for debugging/testing purposes.
+ If no profile is specified the default profile will be used. </p>
</desc>
</func>
</funcs>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 47198b1b91..34f26bf45b 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -35,22 +35,17 @@
<section><title>Inets 5.7</title>
<section><title>Improvements and New Features</title>
+<!--
<p>-</p>
+-->
-<!--
<list>
<item>
- <p>[httpc] Add support for upload body streaming (PUT and POST).</p>
- <p>For more info,
- see the definition of the <c>Body</c> argument of the
- <seealso marker="httpc#request2">request/4,5</seealso>
- function. </p>
- <p>Filipe David Manana</p>
- <p>Own Id: OTP-9094</p>
+ <p>[httpc|httpd] Added support for IPv6 with ssl. </p>
+ <p>Own Id: OTP-5566</p>
</item>
</list>
--->
</section>
@@ -80,6 +75,14 @@
<p>Own Id: OTP-9343</p>
</item>
+ <item>
+ <p>[httpc] Clients started stand-alone not properly handled.
+ Also it was not documented how to use them, that is that
+ once started, they are represented by a <c>pid()</c> and not by
+ their <c>profile()</c>. </p>
+ <p>Own Id: OTP-9365</p>
+ </item>
+
</list>
</section>
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 8aaa9ec402..fe8e93af1f 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -64,17 +64,16 @@ default_profile() ->
profile_name(?DEFAULT_PROFILE) ->
httpc_manager;
+profile_name(Profile) when is_pid(Profile) ->
+ Profile;
profile_name(Profile) ->
- profile_name("httpc_manager_", Profile).
+ Prefix = lists:flatten(io_lib:format("~w_", [?MODULE])),
+ profile_name(Prefix, Profile).
profile_name(Prefix, Profile) when is_atom(Profile) ->
list_to_atom(Prefix ++ atom_to_list(Profile));
-profile_name(Prefix, Profile) when is_pid(Profile) ->
- ProfileStr0 =
- string:strip(string:strip(erlang:pid_to_list(Profile), left, $<), right, $>),
- F = fun($.) -> $_; (X) -> X end,
- ProfileStr = [F(C) || C <- ProfileStr0],
- list_to_atom(Prefix ++ "pid_" ++ ProfileStr).
+profile_name(_Prefix, Profile) when is_pid(Profile) ->
+ Profile.
%%--------------------------------------------------------------------------
@@ -115,9 +114,11 @@ request(Url, Profile) ->
%% {keyfile, path()} | {password, string()} | {cacertfile, path()} |
%% {ciphers, string()}
%% Options - [Option]
-%% Option - {sync, Boolean} | {body_format, BodyFormat} |
-%% {full_result, Boolean} | {stream, To} |
-%% {headers_as_is, Boolean}
+%% Option - {sync, Boolean} |
+%% {body_format, BodyFormat} |
+%% {full_result, Boolean} |
+%% {stream, To} |
+%% {headers_as_is, Boolean}
%% StatusLine = {HTTPVersion, StatusCode, ReasonPhrase}</v>
%% HTTPVersion = string()
%% StatusCode = integer()
@@ -550,9 +551,7 @@ return_answer(Options, {{"HTTP/0.9",_,_}, _, BinBody}) ->
{ok, Body};
return_answer(Options, {StatusLine, Headers, BinBody}) ->
-
Body = maybe_format_body(BinBody, Options),
-
case proplists:get_value(full_result, Options, true) of
true ->
{ok, {StatusLine, Headers, Body}};
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 1f0e012e7e..9ac9ee6f7b 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -515,7 +515,7 @@ handle_info({Proto, _Socket, Data},
{stop, normal, NewState}
end,
- ?hcri("data processed", []),
+ ?hcri("data processed", [{final_result, FinalResult}]),
FinalResult;
@@ -629,8 +629,9 @@ handle_info(timeout_queue, #state{timers = Timers} = State) ->
Timers#timers{queue_timer = undefined}}};
%% Setting up the connection to the server somehow failed.
-handle_info({init_error, _, ClientErrMsg},
+handle_info({init_error, Tag, ClientErrMsg},
State = #state{request = Request}) ->
+ ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]),
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState};
@@ -707,9 +708,9 @@ terminate(normal,
%% And, just in case, close our side (**really** overkill)
http_transport:close(SocketType, Socket);
-terminate(Reason, #state{session = #session{id = Id,
- socket = Socket,
- socket_type = SocketType},
+terminate(Reason, #state{session = #session{id = Id,
+ socket = Socket,
+ socket_type = SocketType},
request = undefined,
profile_name = ProfileName,
timers = Timers,
@@ -1403,7 +1404,7 @@ try_to_enable_pipeline_or_keep_alive(
answer_request(#request{id = RequestId, from = From} = Request, Msg,
#state{timers = Timers, profile_name = ProfileName} = State) ->
- ?hcrt("answer request", [{request, Request}]),
+ ?hcrt("answer request", [{request, Request}, {msg, Msg}]),
httpc_response:send(From, Msg),
RequestTimers = Timers#timers.request_timers,
TimerRef =
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index 7f66b477eb..9015bf1ce2 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -52,7 +52,7 @@
cancel = [], % [{RequestId, HandlerPid, ClientPid}]
handler_db, % ets() - Entry: #handler_info{}
cookie_db, % cookie_db()
- session_db, % ets() - Entry: #tcp_session{}
+ session_db, % ets() - Entry: #session{}
profile_name, % atom()
options = #options{}
}).
@@ -178,7 +178,7 @@ request_done(RequestId, ProfileName) ->
%%--------------------------------------------------------------------
%% Function: insert_session(Session, ProfileName) -> _
-%% Session - #tcp_session{}
+%% Session - #session{}
%% ProfileName - atom()
%%
%% Description: Inserts session information into the httpc manager
@@ -669,7 +669,7 @@ select_session(Method, HostPort, Scheme, SessionType,
(SessionType =:= keep_alive) of
true ->
%% Look for handlers connecting to this host (HostPort)
- %% tcp_session with record name field (tcp_session) and
+ %% session with record name field (session) and
%% socket fields ignored. The fields id (part of: HostPort),
%% client_close, scheme and type specified.
%% The fields id (part of: HandlerPid) and queue_length
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 6c2ffc143d..9b8190ebed 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -33,8 +33,8 @@
peername/2, sockname/2,
resolve/0
]).
-
-export([negotiate/3]).
+-export([ipv4_name/1, ipv6_name/1]).
-include_lib("inets/src/inets_app/inets_internal.hrl").
-include("http_internal.hrl").
@@ -142,8 +142,8 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) ->
ERROR
end;
-connect({essl, SslConfig}, {Host, Port}, _, Timeout) ->
- Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig,
+connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
+ Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig,
?hlrt("connect using essl",
[{host, Host},
{port, Port},
@@ -176,8 +176,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) ->
listen(SocketType, Port) ->
listen(SocketType, undefined, Port).
-listen(ip_comm = SocketType, Addr, Port) ->
- listen(SocketType, Addr, Port, undefined);
+listen(ip_comm = _SocketType, Addr, Port) ->
+ listen_ip_comm(Addr, Port, undefined);
%% Wrapper for backaward compatibillity
listen({ssl, SSLConfig}, Addr, Port) ->
@@ -187,35 +187,33 @@ listen({ssl, SSLConfig}, Addr, Port) ->
{ssl_config, SSLConfig}]),
listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port);
-listen({ossl, SSLConfig} = Ssl, Addr, Port) ->
+listen({ossl, SSLConfig}, Addr, Port) ->
?hlrt("listen (ossl)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- Opt = sock_opt(Ssl, Addr, SSLConfig),
- ?hlrt("listen options", [{opt, Opt}]),
- ssl:listen(Port, [{ssl_imp, old} | Opt]);
+ listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]);
-listen({essl, SSLConfig} = Ssl, Addr, Port) ->
+listen({essl, SSLConfig}, Addr, Port) ->
?hlrt("listen (essl)",
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- Opt = sock_opt(Ssl, Addr, SSLConfig),
- ?hlrt("listen options", [{opt, Opt}]),
- Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt],
- ssl:listen(Port, Opt2).
+ listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]).
+
listen(ip_comm, Addr, Port, Fd) ->
- case (catch listen_ip_comm(Addr, Port, Fd)) of
+ listen_ip_comm(Addr, Port, Fd).
+
+listen_ip_comm(Addr, Port, Fd) ->
+ case (catch do_listen_ip_comm(Addr, Port, Fd)) of
{'EXIT', Reason} ->
{error, {exit, Reason}};
Else ->
Else
end.
-
-listen_ip_comm(Addr, Port, Fd) ->
+do_listen_ip_comm(Addr, Port, Fd) ->
{NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd),
case IpFamily of
inet6fb4 ->
@@ -248,6 +246,41 @@ listen_ip_comm(Addr, Port, Fd) ->
gen_tcp:listen(NewPort, Opts2)
end.
+
+listen_ssl(Addr, Port, Opts0) ->
+ IpFamily = ipfamily_default(Addr, Port),
+ BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0],
+ Opts = sock_opts(Addr, BaseOpts),
+ case IpFamily of
+ inet6fb4 ->
+ Opts2 = [inet6 | Opts],
+ ?hlrt("try ipv6 listen", [{opts, Opts2}]),
+ case (catch ssl:listen(Port, Opts2)) of
+ {error, Reason} when ((Reason =:= nxdomain) orelse
+ (Reason =:= eafnosupport)) ->
+ Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen failed - try ipv4 instead",
+ [{reason, Reason}, {opts, Opts3}]),
+ ssl:listen(Port, Opts3);
+
+ {'EXIT', Reason} ->
+ Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen exit - try ipv4 instead",
+ [{reason, Reason}, {opts, Opts3}]),
+ ssl:listen(Port, Opts3);
+
+ Other ->
+ ?hlrt("ipv6 listen done", [{other, Other}]),
+ Other
+ end;
+
+ _ ->
+ Opts2 = [IpFamily | Opts],
+ ?hlrt("listen", [{opts, Opts2}]),
+ ssl:listen(Port, Opts2)
+ end.
+
+
ipfamily_default(Addr, Port) ->
httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4).
@@ -257,9 +290,9 @@ get_socket_info(Addr, Port, Fd0) ->
%% The presence of a file descriptor takes precedence
case get_fd(Port, Fd0, IpFamilyDefault) of
{Fd, IpFamily} ->
- {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily};
+ {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily};
undefined ->
- {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault}
+ {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault}
end.
get_fd(Port, undefined = _Fd, IpFamilyDefault) ->
@@ -499,38 +532,28 @@ close({essl, _}, Socket) ->
%% connection, usning either gen_tcp or ssl.
%%-------------------------------------------------------------------------
peername(ip_comm, Socket) ->
- case inet:peername(Socket) of
- {ok, {Addr, Port}} when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
- PeerName = ipv4_name(Addr),
- {Port, PeerName};
- {ok, {Addr, Port}} when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
- PeerName = ipv6_name(Addr),
- {Port, PeerName};
- {error, _} ->
- {-1, "unknown"}
- end;
+ do_peername(inet:peername(Socket));
%% Wrapper for backaward compatibillity
peername({ssl, SSLConfig}, Socket) ->
peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
peername({ossl, _}, Socket) ->
- peername_ssl(Socket);
+ do_peername(ssl:peername(Socket));
peername({essl, _}, Socket) ->
- peername_ssl(Socket).
-
-peername_ssl(Socket) ->
- case ssl:peername(Socket) of
- {ok, {Addr, Port}} when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
- PeerName = ipv4_name(Addr),
- {Port, PeerName};
- {ok, {Addr, Port}} when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
- PeerName = ipv6_name(Addr),
- {Port, PeerName};
- {error, _} ->
- {-1, "unknown"}
- end.
+ do_peername(ssl:peername(Socket)).
+
+do_peername({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
+ PeerName = ipv4_name(Addr),
+ {Port, PeerName};
+do_peername({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
+ PeerName = ipv6_name(Addr),
+ {Port, PeerName};
+do_peername({error, _}) ->
+ {-1, "unknown"}.
%%-------------------------------------------------------------------------
@@ -544,38 +567,28 @@ peername_ssl(Socket) ->
%% other end of connection, using either gen_tcp or ssl.
%%-------------------------------------------------------------------------
sockname(ip_comm, Socket) ->
- case inet:sockname(Socket) of
- {ok, {Addr, Port}} ->
- SockName = ipv4_name(Addr),
- {Port, SockName};
- {ok, {Addr, Port}} ->
- SockName = ipv6_name(Addr),
- {Port, SockName};
- {error, _} ->
- {-1, "unknown"}
- end;
+ do_sockname(inet:sockname(Socket));
%% Wrapper for backaward compatibillity
sockname({ssl, SSLConfig}, Socket) ->
sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
sockname({ossl, _}, Socket) ->
- sockname_ssl(Socket);
+ do_sockname(ssl:sockname(Socket));
sockname({essl, _}, Socket) ->
- sockname_ssl(Socket).
-
-sockname_ssl(Socket) ->
- case ssl:sockname(Socket) of
- {ok, {Addr, Port}} ->
- SockName = ipv4_name(Addr),
- {Port, SockName};
- {ok, {Addr, Port}} ->
- SockName = ipv6_name(Addr),
- {Port, SockName};
- {error, _} ->
- {-1, "unknown"}
- end.
+ do_sockname(ssl:sockname(Socket)).
+
+do_sockname({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
+ SockName = ipv4_name(Addr),
+ {Port, SockName};
+do_sockname({ok, {Addr, Port}})
+ when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
+ SockName = ipv6_name(Addr),
+ {Port, SockName};
+do_sockname({error, _}) ->
+ {-1, "unknown"}.
%%-------------------------------------------------------------------------
@@ -589,10 +602,14 @@ resolve() ->
Name.
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-
+%%-------------------------------------------------------------------------
+%% ipv4_name(Ipv4Addr) -> string()
+%% ipv6_name(Ipv6Addr) -> string()
+%% Ipv4Addr = ip4_address()
+%% Ipv6Addr = ip6_address()
+%%
+%% Description: Returns the local hostname.
+%%-------------------------------------------------------------------------
ipv4_name({A, B, C, D}) ->
integer_to_list(A) ++ "." ++
integer_to_list(B) ++ "." ++
@@ -600,7 +617,8 @@ ipv4_name({A, B, C, D}) ->
integer_to_list(D).
ipv6_name({A, B, C, D, E, F, G, H}) ->
- http_util:integer_to_hexlist(B) ++ ":" ++
+ http_util:integer_to_hexlist(A) ++ ":"++
+ http_util:integer_to_hexlist(B) ++ ":" ++
http_util:integer_to_hexlist(C) ++ ":" ++
http_util:integer_to_hexlist(D) ++ ":" ++
http_util:integer_to_hexlist(E) ++ ":" ++
@@ -609,25 +627,24 @@ ipv6_name({A, B, C, D, E, F, G, H}) ->
http_util:integer_to_hexlist(H).
+%%%========================================================================
+%%% Internal functions
+%%%========================================================================
+
+%% -- sock_opts --
%% Address any comes from directive: BindAddress "*"
-sock_opt(ip_comm, any = Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(ip_comm, undefined, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, any = _Addr, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, undefined = _Addr, Opts) ->
- sock_opt2(Opts);
-sock_opt(_, {_,_,_,_} = Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(ip_comm, Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]);
-sock_opt(_, Addr, Opts) ->
- sock_opt2([{ip, Addr} | Opts]).
-
-sock_opt2(Opts) ->
+sock_opts(undefined, Opts) ->
+ sock_opts(Opts);
+sock_opts(any = Addr, Opts) ->
+ sock_opts([{ip, Addr} | Opts]);
+sock_opts(Addr, Opts) ->
+ sock_opts([{ip, Addr} | Opts]).
+
+sock_opts(Opts) ->
[{packet, 0}, {active, false} | Opts].
+
+%% -- negotiate --
negotiate(ip_comm,_,_) ->
?hlrt("negotiate(ip_comm)", []),
ok;
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index b64e266175..0c24c67fc3 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -20,8 +20,10 @@
[
{"5.6",
[
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, http_transport, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}
+ {load_module, http_transport, soft_purge, soft_purge, [http_transport]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]},
{update, ftp, soft, soft_purge, soft_purge, []}
]
},
@@ -49,8 +51,10 @@
[
{"5.6",
[
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, http_transport, soft_purge, soft_purge, []},
+ {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}
+ {load_module, http_transport, soft_purge, soft_purge, [http_transport]},
+ {update, httpc_handler, soft, soft_purge, soft_purge, []},
+ {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]},
{update, ftp, soft, soft_purge, soft_purge, []}
]
},
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 1998bd3950..828fa54523 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -64,16 +64,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[
- proxy_options,
- proxy_head,
- proxy_get,
- proxy_trace,
- proxy_post,
- proxy_put,
- proxy_delete,
- proxy_auth,
- proxy_headers,
- proxy_emulate_lower_versions,
http_options,
http_head,
http_get,
@@ -88,15 +78,6 @@ all() ->
http_headers,
http_headers_dummy,
http_bad_response,
- ssl_head,
- ossl_head,
- essl_head,
- ssl_get,
- ossl_get,
- essl_get,
- ssl_trace,
- ossl_trace,
- essl_trace,
http_redirect,
http_redirect_loop,
http_internal_server_error,
@@ -106,21 +87,44 @@ all() ->
http_emulate_lower_versions,
http_relaxed,
page_does_not_exist,
- proxy_page_does_not_exist,
- proxy_https_not_supported,
- http_stream,
- http_stream_once,
- proxy_stream,
parse_url,
options,
- ipv6,
headers_as_is,
+ {group, proxy},
+ {group, ssl},
+ {group, stream},
+ {group, ipv6},
{group, tickets},
initial_server_connect
].
groups() ->
- [{tickets, [], [hexed_query_otp_6191,
+ [
+ {proxy, [], [proxy_options,
+ proxy_head,
+ proxy_get,
+ proxy_trace,
+ proxy_post,
+ proxy_put,
+ proxy_delete,
+ proxy_auth,
+ proxy_headers,
+ proxy_emulate_lower_versions,
+ proxy_page_does_not_exist,
+ proxy_https_not_supported]},
+ {ssl, [], [ssl_head,
+ ossl_head,
+ essl_head,
+ ssl_get,
+ ossl_get,
+ essl_get,
+ ssl_trace,
+ ossl_trace,
+ essl_trace]},
+ {stream, [], [http_stream,
+ http_stream_once,
+ proxy_stream]},
+ {tickets, [], [hexed_query_otp_6191,
empty_body_otp_6243,
empty_response_header_otp_6830,
transfer_encoding_otp_6807,
@@ -139,7 +143,10 @@ groups() ->
{otp_8154, [], [otp_8154_1]},
{otp_8106, [], [otp_8106_pid,
otp_8106_fun,
- otp_8106_mfa]}].
+ otp_8106_mfa]},
+ {ipv6, [], [ipv6_ipcomm, ipv6_essl]}
+ ].
+
init_per_group(_GroupName, Config) ->
@@ -213,36 +220,38 @@ end_per_suite(Config) ->
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
+
init_per_testcase(otp_8154_1 = Case, Config) ->
init_per_testcase(Case, 5, Config);
-init_per_testcase(initial_server_connect, Config) ->
+init_per_testcase(initial_server_connect = Case, Config) ->
%% Try to check if crypto actually exist or not,
%% this test case does not work unless it does
- case (catch crypto:start()) of
- ok ->
- application:start(public_key),
- application:start(ssl),
+ try
+ begin
+ ensure_started(crypto),
+ ensure_started(public_key),
+ ensure_started(ssl),
inets:start(),
- Config;
- _ ->
- {skip,"Could not start crypto"}
+ Config
+ end
+ catch
+ throw:{error, {failed_starting, App, ActualError}} ->
+ tsp("init_per_testcase(~w) -> failed starting ~w: "
+ "~n ~p", [Case, App, ActualError]),
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
end;
init_per_testcase(Case, Config) ->
init_per_testcase(Case, 2, Config).
-init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
- tsp("init_per_testcase_ssl -> stop ssl"),
- application:stop(ssl),
- Config2 = lists:keydelete(local_ssl_server, 1, Config),
- %% Will start inets
- tsp("init_per_testcase_ssl -> try start http server (including inets)"),
- Server = inets_test_lib:start_http_server(
- filename:join(PrivDir, SslConfFile), Tag),
- tsp("init_per_testcase -> Server: ~p", [Server]),
- [{local_ssl_server, Server} | Config2].
-
init_per_testcase(Case, Timeout, Config) ->
io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n",
[?MODULE, Case, Timeout]),
@@ -261,13 +270,16 @@ init_per_testcase(Case, Timeout, Config) ->
NewConfig =
case atom_to_list(Case) of
[$s, $s, $l | _] ->
- init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(ssl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
[$o, $s, $s, $l | _] ->
- init_per_testcase_ssl(ossl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(ossl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
[$e, $s, $s, $l | _] ->
- init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+ init_per_testcase_ssl(essl, PrivDir, SslConfFile,
+ [{watchdog, Dog} | TmpConfig]);
"proxy_" ++ Rest ->
io:format("init_per_testcase -> Rest: ~p~n", [Rest]),
@@ -275,16 +287,23 @@ init_per_testcase(Case, Timeout, Config) ->
"https_not_supported" ->
tsp("init_per_testcase -> [proxy case] start inets"),
inets:start(),
- tsp("init_per_testcase -> [proxy case] start ssl"),
- application:start(crypto),
- application:start(public_key),
- case (catch application:start(ssl)) of
+ tsp("init_per_testcase -> "
+ "[proxy case] start crypto, public_key and ssl"),
+ try ensure_started([crypto, public_key, ssl]) of
ok ->
- [{watchdog, Dog} | TmpConfig];
- _ ->
- [{skip, "SSL does not seem to be supported"}
- | TmpConfig]
+ [{watchdog, Dog} | TmpConfig]
+ catch
+ throw:{error, {failed_starting, App, _}} ->
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
end;
+
_ ->
%% We use erlang.org for the proxy tests
%% and after the switch to erlang-web, many
@@ -321,6 +340,33 @@ init_per_testcase(Case, Timeout, Config) ->
[{skip, "proxy not responding"} | TmpConfig]
end
end;
+
+ "ipv6_" ++ _Rest ->
+ %% Ensure needed apps (crypto, public_key and ssl) started
+ try ensure_started([crypto, public_key, ssl]) of
+ ok ->
+ Profile = ipv6,
+ %% A stand-alone profile is represented by a pid()
+ {ok, ProfilePid} =
+ inets:start(httpc,
+ [{profile, Profile},
+ {data_dir, PrivDir}], stand_alone),
+ httpc:set_options([{ipfamily, inet6}], ProfilePid),
+ tsp("httpc profile pid: ~p", [ProfilePid]),
+ [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig]
+ catch
+ throw:{error, {failed_starting, App, ActualError}} ->
+ tsp("init_per_testcase(~w) -> failed starting ~w: "
+ "~n ~p", [Case, App, ActualError]),
+ SkipString =
+ "Could not start " ++ atom_to_list(App),
+ {skip, SkipString};
+ _:X ->
+ SkipString =
+ lists:flatten(
+ io_lib:format("Failed starting apps: ~p", [X])),
+ {skip, SkipString}
+ end;
_ ->
TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig),
Server =
@@ -330,9 +376,7 @@ init_per_testcase(Case, Timeout, Config) ->
[{watchdog, Dog}, {local_server, Server} | TmpConfig2]
end,
- %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
- %% ["localhost", ?IPV6_LOCAL_HOST]}}]),
-
+ %% This will fail for the ipv6_ - cases (but that is ok)
httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
["localhost", ?IPV6_LOCAL_HOST]}},
{ipfamily, inet6fb4}]),
@@ -341,6 +385,18 @@ init_per_testcase(Case, Timeout, Config) ->
NewConfig.
+init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
+ tsp("init_per_testcase_ssl -> stop ssl"),
+ application:stop(ssl),
+ Config2 = lists:keydelete(local_ssl_server, 1, Config),
+ %% Will start inets
+ tsp("init_per_testcase_ssl -> try start http server (including inets)"),
+ Server = inets_test_lib:start_http_server(
+ filename:join(PrivDir, SslConfFile), Tag),
+ tsp("init_per_testcase -> Server: ~p", [Server]),
+ [{local_ssl_server, Server} | Config2].
+
+
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
%% Case - atom()
@@ -349,13 +405,36 @@ init_per_testcase(Case, Timeout, Config) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(http_save_to_file, Config) ->
- PrivDir = ?config(priv_dir, Config),
+end_per_testcase(http_save_to_file = Case, Config) ->
+ io:format(user, "~n~n*** END ~w:~w ***~n~n",
+ [?MODULE, Case]),
+ PrivDir = ?config(priv_dir, Config),
FullPath = filename:join(PrivDir, "dummy.html"),
file:delete(FullPath),
finish(Config);
-end_per_testcase(_, Config) ->
+end_per_testcase(Case, Config) ->
+ io:format(user, "~n~n*** END ~w:~w ***~n~n",
+ [?MODULE, Case]),
+ case atom_to_list(Case) of
+ "ipv6_" ++ _Rest ->
+ tsp("end_per_testcase(~w) -> stop ssl", [Case]),
+ application:stop(ssl),
+ tsp("end_per_testcase(~w) -> stop public_key", [Case]),
+ application:stop(public_key),
+ tsp("end_per_testcase(~w) -> stop crypto", [Case]),
+ application:stop(crypto),
+ ProfilePid = ?config(profile, Config),
+ tsp("end_per_testcase(~w) -> stop httpc profile (~p)",
+ [Case, ProfilePid]),
+ unlink(ProfilePid),
+ inets:stop(stand_alone, ProfilePid),
+ tsp("end_per_testcase(~w) -> httpc profile (~p) stopped",
+ [Case, ProfilePid]),
+ ok;
+ _ ->
+ ok
+ end,
finish(Config).
finish(Config) ->
@@ -364,6 +443,7 @@ finish(Config) ->
undefined ->
ok;
_ ->
+ tsp("finish -> stop watchdog (~p)", [Dog]),
test_server:timetrap_cancel(Dog)
end.
@@ -565,7 +645,7 @@ http_relaxed(suite) ->
http_relaxed(Config) when is_list(Config) ->
ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
%% ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/missing_reason_phrase.html",
@@ -591,7 +671,7 @@ http_dummy_pipe(suite) ->
[];
http_dummy_pipe(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html",
@@ -905,7 +985,7 @@ http_headers_dummy(suite) ->
[];
http_headers_dummy(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html",
@@ -970,7 +1050,7 @@ http_bad_response(suite) ->
[];
http_bad_response(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html",
@@ -1170,7 +1250,7 @@ http_redirect(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
tsp("http_redirect -> start dummy server inet"),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
tsp("http_redirect -> server port = ~p", [Port]),
URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html",
@@ -1282,7 +1362,7 @@ http_redirect_loop(suite) ->
[];
http_redirect_loop(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html",
@@ -1299,7 +1379,7 @@ http_internal_server_error(suite) ->
[];
http_internal_server_error(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html",
@@ -1335,7 +1415,7 @@ http_userinfo(suite) ->
http_userinfo(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URLAuth = "http://alladin:sesame@localhost:"
++ integer_to_list(Port) ++ "/userinfo.html",
@@ -1361,7 +1441,7 @@ http_cookie(suite) ->
[];
http_cookie(Config) when is_list(Config) ->
ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URLStart = ?URL_START
++ integer_to_list(Port),
@@ -1735,7 +1815,7 @@ http_stream_once(Config) when is_list(Config) ->
p("http_stream_once -> set ipfamily to inet", []),
ok = httpc:set_options([{ipfamily, inet}]),
p("http_stream_once -> start dummy server", []),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
PortStr = integer_to_list(Port),
p("http_stream_once -> once", []),
@@ -1871,28 +1951,79 @@ parse_url(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-ipv6() ->
- [{require,ipv6_hosts}].
-ipv6(doc) ->
- ["Test ipv6."];
-ipv6(suite) ->
- [];
-ipv6(Config) when is_list(Config) ->
- {ok, Hostname} = inet:gethostname(),
-
- case lists:member(list_to_atom(Hostname),
- ct:get_config(ipv6_hosts)) of
- true ->
- {DummyServerPid, Port} = dummy_server(self(), ipv6),
-
- URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++
+
+ipv6_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_ipcomm(doc) ->
+ ["Test ip_comm ipv6."];
+ipv6_ipcomm(suite) ->
+ [];
+ipv6_ipcomm(Config) when is_list(Config) ->
+ HTTPOptions = [],
+ SocketType = ip_comm,
+ Scheme = "http",
+ Extra = [],
+ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
+
+
+%%-------------------------------------------------------------------------
+
+ipv6_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_essl(doc) ->
+ ["Test essl ipv6."];
+ipv6_essl(suite) ->
+ [];
+ipv6_essl(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
+ SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
+ SSLConfig = {essl, SSLOptions},
+ tsp("ssl_ipv6 -> make request using: "
+ "~n SSLOptions: ~p", [SSLOptions]),
+ HTTPOptions = [{ssl, SSLConfig}],
+ SocketType = essl,
+ Scheme = "https",
+ Extra = SSLOptions,
+ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config).
+
+
+%%-------------------------------------------------------------------------
+
+ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) ->
+ %% Check if we are a IPv6 host
+ tsp("ipv6 -> verify ipv6 support", []),
+ case inets_test_lib:has_ipv6_support(Config) of
+ {ok, Addr} ->
+ tsp("ipv6 -> ipv6 supported: ~p", [Addr]),
+ {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra),
+ Profile = ?config(profile, Config),
+ URL =
+ Scheme ++
+ "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++
integer_to_list(Port) ++ "/foobar.html",
- {ok, {{_,200,_}, [_ | _], [_|_]}} =
- httpc:request(get, {URL, []}, [], []),
-
- DummyServerPid ! stop,
+ tsp("ipv6 -> issue request with: "
+ "~n URL: ~p"
+ "~n HTTPOptions: ~p", [URL, HTTPOptions]),
+ case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of
+ {ok, {{_,200,_}, [_ | _], [_|_]}} ->
+ tsp("ipv6 -> expected result"),
+ DummyServerPid ! stop,
+ ok;
+ {ok, Unexpected} ->
+ tsp("ipv6 -> unexpected result: "
+ "~n ~p", [Unexpected]),
+ DummyServerPid ! stop,
+ tsf({unexpected_result, Unexpected});
+ {error, Reason} ->
+ tsp("ipv6 -> error: "
+ "~n Reason: ~p", [Reason]),
+ DummyServerPid ! stop,
+ tsf(Reason)
+ end,
ok;
- false ->
+ _ ->
+ tsp("ipv6 -> ipv6 not supported", []),
{skip, "Host does not support IPv6"}
end.
@@ -1945,7 +2076,7 @@ http_invalid_http(suite) ->
[];
http_invalid_http(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html",
@@ -2002,7 +2133,7 @@ transfer_encoding_otp_6807(suite) ->
[];
transfer_encoding_otp_6807(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/capital_transfer_encoding.html",
@@ -2035,7 +2166,7 @@ empty_response_header_otp_6830(suite) ->
[];
empty_response_header_otp_6830(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html",
{ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL),
@@ -2052,7 +2183,7 @@ no_content_204_otp_6982(suite) ->
[];
no_content_204_otp_6982(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html",
{ok, {{_,204,_}, [], []}} = httpc:request(URL),
@@ -2070,7 +2201,7 @@ missing_CR_otp_7304(suite) ->
[];
missing_CR_otp_7304(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html",
{ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL),
@@ -2089,7 +2220,7 @@ otp_7883_1(suite) ->
otp_7883_1(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
{error, socket_closed_remotely} = httpc:request(URL),
@@ -2105,7 +2236,7 @@ otp_7883_2(suite) ->
otp_7883_2(Config) when is_list(Config) ->
ok = httpc:set_options([{ipfamily, inet}]),
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
Method = get,
@@ -2626,7 +2757,7 @@ otp_8371(suite) ->
[];
otp_8371(Config) when is_list(Config) ->
ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
- {DummyServerPid, Port} = dummy_server(self(), ipv4),
+ {DummyServerPid, Port} = dummy_server(ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/ensure_host_header_with_port.html",
@@ -2864,75 +2995,179 @@ receive_streamed_body(RequestId, Body, Pid) ->
test_server:fail(Msg)
end.
+%% Perform a synchronous stop
+dummy_server_stop(Pid) ->
+ Pid ! {stop, self()},
+ receive
+ {stopped, Pid} ->
+ ok
+ end.
+
+dummy_server(IpV) ->
+ dummy_server(self(), ip_comm, IpV, []).
+dummy_server(SocketType, IpV, Extra) ->
+ dummy_server(self(), SocketType, IpV, Extra).
-dummy_server(Caller, IpV) ->
- Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]),
+dummy_server(Caller, SocketType, IpV, Extra) ->
+ Args = [Caller, SocketType, IpV, Extra],
+ Pid = spawn(httpc_SUITE, dummy_server_init, Args),
receive
{port, Port} ->
{Pid, Port}
end.
-dummy_server_init(Caller, IpV) ->
+dummy_server_init(Caller, ip_comm, IpV, _) ->
+ BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}],
{ok, ListenSocket} =
case IpV of
ipv4 ->
- gen_tcp:listen(0, [binary, inet, {packet, 0},
- {reuseaddr,true},
- {active, false}]);
+ tsp("ip_comm ipv4 listen", []),
+ gen_tcp:listen(0, [inet | BaseOpts]);
ipv6 ->
- gen_tcp:listen(0, [binary, inet6, {packet, 0},
- {reuseaddr,true},
- {active, false}])
+ tsp("ip_comm ipv6 listen", []),
+ gen_tcp:listen(0, [inet6 | BaseOpts])
end,
{ok, Port} = inet:port(ListenSocket),
- tsp("dummy_server_init -> Port: ~p", [Port]),
+ tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]),
+ Caller ! {port, Port},
+ dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ [], ListenSocket);
+dummy_server_init(Caller, essl, IpV, SSLOptions) ->
+ BaseOpts = [{ssl_imp, new},
+ {backlog, 128}, binary, {reuseaddr,true}, {active, false} |
+ SSLOptions],
+ dummy_ssl_server_init(Caller, BaseOpts, IpV);
+dummy_server_init(Caller, ossl, IpV, SSLOptions) ->
+ BaseOpts = [{ssl_imp, old},
+ {backlog, 128}, binary, {active, false} | SSLOptions],
+ dummy_ssl_server_init(Caller, BaseOpts, IpV).
+
+dummy_ssl_server_init(Caller, BaseOpts, IpV) ->
+ {ok, ListenSocket} =
+ case IpV of
+ ipv4 ->
+ tsp("dummy_ssl_server_init -> ssl ipv4 listen", []),
+ ssl:listen(0, [inet | BaseOpts]);
+ ipv6 ->
+ tsp("dummy_ssl_server_init -> ssl ipv6 listen", []),
+ ssl:listen(0, [inet6 | BaseOpts])
+ end,
+ tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]),
+ {ok, {_, Port}} = ssl:sockname(ListenSocket),
+ tsp("dummy_ssl_server_init -> Port: ~p", [Port]),
Caller ! {port, Port},
- dummy_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
- [], ListenSocket).
+ dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]},
+ [], ListenSocket).
-dummy_server_loop(MFA, Handlers, ListenSocket) ->
+dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) ->
receive
stop ->
- lists:foreach(fun(Handler) -> Handler ! stop end, Handlers)
+ tsp("dummy_ipcomm_server_loop -> stop handlers", []),
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ tsp("dummy_ipcomm_server_loop -> "
+ "stop command from ~p for handlers (~p)", [From, Handlers]),
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
after 0 ->
+ tsp("dummy_ipcomm_server_loop -> await accept", []),
{ok, Socket} = gen_tcp:accept(ListenSocket),
+ tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]),
HandlerPid = dummy_request_handler(MFA, Socket),
+ tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]),
gen_tcp:controlling_process(Socket, HandlerPid),
- HandlerPid ! controller,
- dummy_server_loop(MFA, [HandlerPid | Handlers],
+ tsp("dummy_ipcomm_server_loop -> "
+ "control transfered to handler", []),
+ HandlerPid ! ipcomm_controller,
+ tsp("dummy_ipcomm_server_loop -> "
+ "handler informed about control transfer", []),
+ dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers],
ListenSocket)
end.
+dummy_ssl_server_loop(MFA, Handlers, ListenSocket) ->
+ receive
+ stop ->
+ tsp("dummy_ssl_server_loop -> stop handlers", []),
+ lists:foreach(fun(Handler) -> Handler ! stop end, Handlers);
+ {stop, From} ->
+ tsp("dummy_ssl_server_loop -> "
+ "stop command from ~p for handlers (~p)", [From, Handlers]),
+ Stopper = fun(Handler) -> Handler ! stop end,
+ lists:foreach(Stopper, Handlers),
+ From ! {stopped, self()}
+ after 0 ->
+ tsp("dummy_ssl_server_loop -> await accept", []),
+ {ok, Socket} = ssl:transport_accept(ListenSocket),
+ tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]),
+ HandlerPid = dummy_request_handler(MFA, Socket),
+ tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]),
+ ssl:controlling_process(Socket, HandlerPid),
+ tsp("dummy_ssl_server_loop -> control transfered to handler", []),
+ HandlerPid ! ssl_controller,
+ tsp("dummy_ssl_server_loop -> "
+ "handler informed about control transfer", []),
+ dummy_ssl_server_loop(MFA, [HandlerPid | Handlers],
+ ListenSocket)
+ end.
+
dummy_request_handler(MFA, Socket) ->
+ tsp("spawn request handler", []),
spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]).
dummy_request_handler_init(MFA, Socket) ->
- receive
- controller ->
- inet:setopts(Socket, [{active, true}])
- end,
- dummy_request_handler_loop(MFA, Socket).
+ SockType =
+ receive
+ ipcomm_controller ->
+ tsp("dummy_request_handler_init -> "
+ "received ip_comm controller - activate", []),
+ inet:setopts(Socket, [{active, true}]),
+ ip_comm;
+ ssl_controller ->
+ tsp("dummy_request_handler_init -> "
+ "received ssl controller - activate", []),
+ ssl:setopts(Socket, [{active, true}]),
+ ssl
+ end,
+ dummy_request_handler_loop(MFA, SockType, Socket).
-dummy_request_handler_loop({Module, Function, Args}, Socket) ->
+dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) ->
tsp("dummy_request_handler_loop -> entry with"
"~n Module: ~p"
"~n Function: ~p"
"~n Args: ~p", [Module, Function, Args]),
receive
- {tcp, _, Data} ->
- tsp("dummy_request_handler_loop -> Data ~p", [Data]),
- case handle_request(Module, Function, [Data | Args], Socket) of
- stop ->
+ {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) ->
+ tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]),
+ case handle_request(Module, Function, [Data | Args], Socket, Proto) of
+ stop when Proto =:= tcp ->
gen_tcp:close(Socket);
+ stop when Proto =:= ssl ->
+ ssl:close(Socket);
NewMFA ->
- dummy_request_handler_loop(NewMFA, Socket)
+ dummy_request_handler_loop(NewMFA, SockType, Socket)
end;
- stop ->
- gen_tcp:close(Socket)
+ stop when SockType =:= ip_comm ->
+ gen_tcp:close(Socket);
+ stop when SockType =:= ssl ->
+ ssl:close(Socket)
end.
-handle_request(Module, Function, Args, Socket) ->
+
+mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end;
+mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end.
+
+mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end;
+mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end.
+
+handle_request(Module, Function, Args, Socket, Proto) ->
+ Close = mk_close(Proto),
+ Send = mk_send(Proto),
+ handle_request(Module, Function, Args, Socket, Close, Send).
+
+handle_request(Module, Function, Args, Socket, Close, Send) ->
tsp("handle_request -> entry with"
"~n Module: ~p"
"~n Function: ~p"
@@ -2941,7 +3176,7 @@ handle_request(Module, Function, Args, Socket) ->
{ok, Result} ->
tsp("handle_request -> ok"
"~n Result: ~p", [Result]),
- case (catch handle_http_msg(Result, Socket)) of
+ case (catch handle_http_msg(Result, Socket, Close, Send)) of
stop ->
stop;
<<>> ->
@@ -2949,7 +3184,8 @@ handle_request(Module, Function, Args, Socket) ->
{httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]};
Data ->
handle_request(httpd_request, parse,
- [Data |[?HTTP_MAX_HEADER_SIZE]], Socket)
+ [Data |[?HTTP_MAX_HEADER_SIZE]], Socket,
+ Close, Send)
end;
NewMFA ->
tsp("handle_request -> "
@@ -2957,7 +3193,7 @@ handle_request(Module, Function, Args, Socket) ->
NewMFA
end.
-handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
+handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) ->
tsp("handle_http_msg -> entry with: "
"~n RelUri: ~p"
"~n Headers: ~p"
@@ -3114,16 +3350,16 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
"Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++
"Proxy-Authenticate:#1Basic" ++
"\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/capital_transfer_encoding.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")),
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket, http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/cookie.html" ->
"HTTP/1.1 200 ok\r\n" ++
@@ -3142,20 +3378,20 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
"/once_chunked.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")),
- gen_tcp:send(Socket,
+ Send(Socket, Head),
+ Send(Socket, http_chunk:encode("<HTML><BODY>fo")),
+ Send(Socket,
http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
"/once.html" ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n",
- gen_tcp:send(Socket, Head),
- gen_tcp:send(Socket, "<HTML><BODY>fo"),
+ Send(Socket, Head),
+ Send(Socket, "<HTML><BODY>fo"),
test_server:sleep(1000),
- gen_tcp:send(Socket, "ob"),
+ Send(Socket, "ob"),
test_server:sleep(1000),
- gen_tcp:send(Socket, "ar</BODY></HTML>");
+ Send(Socket, "ar</BODY></HTML>");
"/invalid_http.html" ->
"HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++
"Transfer-Encoding:chunked\r\n\r\n";
@@ -3178,9 +3414,9 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) ->
ok;
close ->
%% Nothing to send, just close
- gen_tcp:close(Socket);
+ Close(Socket);
_ when is_list(Msg) orelse is_binary(Msg) ->
- gen_tcp:send(Socket, Msg)
+ Send(Socket, Msg)
end,
tsp("handle_http_msg -> done"),
NextRequest.
@@ -3316,3 +3552,20 @@ dummy_ssl_server_hang_loop(_) ->
stop ->
ok
end.
+
+
+ensure_started([]) ->
+ ok;
+ensure_started([App|Apps]) ->
+ ensure_started(App),
+ ensure_started(Apps);
+ensure_started(App) when is_atom(App) ->
+ case (catch application:start(App)) of
+ ok ->
+ ok;
+ {error, {already_started, _}} ->
+ ok;
+ Error ->
+ throw({error, {failed_starting, App, Error}})
+ end.
+
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index fde5178879..c4d4bf969b 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -207,8 +207,11 @@
-export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1,
ticket_7304/1]).
-%%% Misc
--export([ipv6_hostname/1, ipv6_address/1]).
+%%% IPv6 tests
+-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1,
+ ipv6_address_ipcomm/0, ipv6_address_ipcomm/1,
+ ipv6_hostname_essl/0, ipv6_hostname_essl/1,
+ ipv6_address_essl/0, ipv6_address_essl/1]).
%% Help functions
-export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]).
@@ -241,9 +244,15 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, ip}, {group, ssl}, {group, http_1_1_ip},
- {group, http_1_0_ip}, {group, http_0_9_ip},
- {group, tickets}].
+ [
+ {group, ip},
+ {group, ssl},
+ {group, http_1_1_ip},
+ {group, http_1_0_ip},
+ {group, http_0_9_ip},
+ {group, ipv6},
+ {group, tickets}
+ ].
groups() ->
[{ip, [],
@@ -329,7 +338,8 @@ groups() ->
{http_1_0_ip, [],
[ip_head_1_0, ip_get_1_0, ip_post_1_0]},
{http_0_9_ip, [], [ip_get_0_9]},
- {ipv6, [], [ipv6_hostname, ipv6_address]},
+ {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm,
+ ipv6_hostname_essl, ipv6_address_essl]},
{tickets, [],
[ticket_5775, ticket_5865, ticket_5913, ticket_6003,
ticket_7304]}].
@@ -408,10 +418,10 @@ init_per_testcase2(Case, Config) ->
"~n Config: ~p"
"~n", [?MODULE, Case, Config]),
- IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
- IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf",
- SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
- SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf",
+ IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
+ IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf",
+ SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
+ SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
DataDir = ?config(data_dir, Config),
SuiteTopDir = ?config(suite_top_dir, Config),
@@ -471,9 +481,9 @@ init_per_testcase2(Case, Config) ->
io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n",
[?MODULE, Case]),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- normal_acess, IpNormal),
+ normal_access, IpNormal),
create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig],
- mod_htaccess, IpHtacess),
+ mod_htaccess, IpHtaccess),
%% To be used by SSL test cases
io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n",
@@ -491,9 +501,9 @@ init_per_testcase2(Case, Config) ->
end,
create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- normal_acess, SslNormal),
+ normal_access, SslNormal),
create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
- mod_htaccess, SslHtacess),
+ mod_htaccess, SslHtaccess),
%% To be used by IPv6 test cases. Case-clause is so that
%% you can do ts:run(inets, httpd_SUITE, <test case>)
@@ -501,22 +511,52 @@ init_per_testcase2(Case, Config) ->
%% on 'test_host_ipv6_only' that will only be present
%% when you run the whole test suite due to shortcomings
%% of the test server.
- %% case (catch ?config(test_host_ipv6_only, Config)) of
- %% {_,IPv6Host,IPv6Adress,_,_} ->
- %% create_ipv6_config([{port, ?IP_PORT},
- %% {sock_type, ip_comm} | NewConfig],
- %% "ipv6_hostname.conf", IPv6Host),
- %% create_ipv6_config([{port, ?IP_PORT},
- %% {sock_type, ip_comm} | NewConfig],
- %% "ipv6_address.conf", IPv6Adress);
- %% _ ->
- %% ok
- %% end,
-
+
+ io:format(user, "~w:init_per_testcase2(~w) -> "
+ "maybe generate IPv6 config file(s)", [?MODULE, Case]),
+ NewConfig2 =
+ case atom_to_list(Case) of
+ "ipv6_" ++ _ ->
+ case (catch inets_test_lib:has_ipv6_support(NewConfig)) of
+ {ok, IPv6Address0} ->
+ {ok, Hostname} = inet:gethostname(),
+ IPv6Address = http_transport:ipv6_name(IPv6Address0),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_ipcomm.conf",
+ Hostname),
+ create_ipv6_config([{port, ?IP_PORT},
+ {sock_type, ip_comm},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_ipcomm.conf",
+ IPv6Address),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_hostname_essl.conf",
+ Hostname),
+ create_ipv6_config([{port, ?SSL_PORT},
+ {sock_type, essl},
+ {ipv6_host, IPv6Address} |
+ NewConfig],
+ "ipv6_address_essl.conf",
+ IPv6Address),
+ [{ipv6_host, IPv6Address} | NewConfig];
+ _ ->
+ NewConfig
+ end;
+ _ ->
+ NewConfig
+ end,
+
io:format(user, "~w:init_per_testcase2(~w) -> done~n",
[?MODULE, Case]),
- NewConfig.
+ NewConfig2.
init_per_testcase3(Case, Config) ->
@@ -547,10 +587,10 @@ init_per_testcase3(Case, Config) ->
[?MODULE, Case]),
inets:disable_trace();
_ ->
- %% TraceLevel = max,
io:format(user, "~w:init_per_testcase3(~w) -> enabling trace",
[?MODULE, Case]),
- TraceLevel = 70,
+ %% TraceLevel = 70,
+ TraceLevel = max,
TraceDest = io,
inets:enable_trace(TraceLevel, TraceDest, httpd)
end,
@@ -569,7 +609,7 @@ init_per_testcase3(Case, Config) ->
inets_test_lib:start_http_server(
filename:join(TcTopDir,
integer_to_list(?IP_PORT) ++
- "htacess.conf")),
+ "htaccess.conf")),
"mod_htaccess";
"ip_" ++ Rest ->
inets_test_lib:start_http_server(
@@ -602,7 +642,7 @@ init_per_testcase3(Case, Config) ->
case inets_test_lib:start_http_server_ssl(
filename:join(TcTopDir,
integer_to_list(?SSL_PORT) ++
- "htacess.conf"), SslTag) of
+ "htaccess.conf"), SslTag) of
ok ->
"mod_htaccess";
Other ->
@@ -627,16 +667,13 @@ init_per_testcase3(Case, Config) ->
{skip, "SSL does not seem to be supported"}
end;
"ipv6_" ++ _ = TestCaseStr ->
- {ok, Hostname} = inet:gethostname(),
-
- case lists:member(list_to_atom(Hostname),
- ?config(ipv6_hosts, Config)) of
- true ->
+ case inets_test_lib:has_ipv6_support() of
+ {ok, _} ->
inets_test_lib:start_http_server(
filename:join(TcTopDir,
TestCaseStr ++ ".conf"));
- false ->
+ _ ->
{skip, "Host does not support IPv6"}
end
end,
@@ -650,8 +687,8 @@ init_per_testcase3(Case, Config) ->
"mod_htaccess" ->
ServerRoot = ?config(server_root, Config),
Path = filename:join([ServerRoot, "htdocs"]),
- catch remove_htacess(Path),
- create_htacess_data(Path, ?config(address, Config)),
+ catch remove_htaccess(Path),
+ create_htaccess_data(Path, ?config(address, Config)),
[{watchdog, Dog} | NewConfig];
"range" ->
ServerRoot = ?config(server_root, Config),
@@ -2409,30 +2446,76 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-ipv6_hostname(doc) ->
+
+ipv6_hostname_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_hostname_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_hostname(SocketType, Port, X).
+
+ipv6_hostname(_SocketType, _Port, doc) ->
["Test standard ipv6 address"];
-ipv6_hostname(suite)->
+ipv6_hostname(_SocketType, _Port, suite)->
[];
-ipv6_hostname(Config) when is_list(Config) ->
+ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_hostname -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
Host = ?config(host, Config),
- httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, node(),
- "GET / HTTP/1.1\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.1"}]),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ tsp("ipv6_hostname -> Host: ~p", [Host]),
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
ok.
%%-------------------------------------------------------------------------
-ipv6_address(doc) ->
+
+ipv6_address_ipcomm() ->
+ [{require, ipv6_hosts}].
+ipv6_address_ipcomm(X) ->
+ SocketType = ip_comm,
+ Port = ?IP_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address_essl() ->
+ [{require, ipv6_hosts}].
+ipv6_address_essl(X) ->
+ SocketType = essl,
+ Port = ?SSL_PORT,
+ ipv6_address(SocketType, Port, X).
+
+ipv6_address(_SocketType, _Port, doc) ->
["Test standard ipv6 address"];
-ipv6_address(suite)->
+ipv6_address(_SocketType, _Port, suite)->
[];
-ipv6_address(Config) when is_list(Config) ->
- httpd_test_lib:verify_request(ip_comm, ?IPV6_LOCAL_HOST, ?IP_PORT,
- node(), "GET / HTTP/1.1\r\n\r\n",
- [{statuscode, 200},
- {version, "HTTP/1.1"}]),
+ipv6_address(SocketType, Port, Config) when is_list(Config) ->
+ tsp("ipv6_address -> entry with"
+ "~n SocketType: ~p"
+ "~n Port: ~p"
+ "~n Config: ~p", [SocketType, Port, Config]),
+ Host = ?config(host, Config),
+ tsp("ipv6_address -> Host: ~p", [Host]),
+ URI = "GET HTTP://" ++
+ Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
+ httpd_test_lib:verify_request(SocketType, Host, Port, [inet6],
+ node(),
+ URI,
+ [{statuscode, 200}, {version, "HTTP/1.1"}]),
ok.
+
%%--------------------------------------------------------------------
ticket_5775(doc) ->
["Tests that content-length is correct"];
@@ -2805,22 +2888,22 @@ cleanup_mnesia() ->
mnesia:delete_schema([node()]),
ok.
-create_htacess_data(Path, IpAddress)->
- create_htacess_dirs(Path),
+create_htaccess_data(Path, IpAddress)->
+ create_htaccess_dirs(Path),
create_html_file(filename:join([Path,"ht/open/dummy.html"])),
create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])),
create_html_file(filename:join([Path,"ht/secret/dummy.html"])),
create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
- create_htacess_file(filename:join([Path,"ht/open/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]),
Path, "user one Aladdin"),
- create_htacess_file(filename:join([Path,"ht/secret/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]),
Path, "group group1 group2"),
- create_htacess_file(filename:join([Path,
+ create_htaccess_file(filename:join([Path,
"ht/secret/top_secret/.htaccess"]),
Path, "user four"),
- create_htacess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
+ create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]),
Path, nouser, IpAddress),
create_user_group_file(filename:join([Path,"ht","users.file"]),
@@ -2835,7 +2918,7 @@ create_html_file(PathAndFileName)->
"<html><head><title>test</title></head>
<body>testar</body></html>")).
-create_htacess_file(PathAndFileName, BaseDir, RequireData)->
+create_htaccess_file(PathAndFileName, BaseDir, RequireData)->
file:write_file(PathAndFileName,
list_to_binary(
"AuthUserFile "++ BaseDir ++
@@ -2844,7 +2927,7 @@ create_htacess_file(PathAndFileName, BaseDir, RequireData)->
" Basic\n<Limit>\nrequire " ++ RequireData ++
"\n</Limit>")).
-create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
+create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
file:write_file(PathAndFileName,list_to_binary(
"AuthUserFile "++ BaseDir ++
"/ht/users.file\nAuthGroupFile " ++
@@ -2858,14 +2941,14 @@ create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)->
create_user_group_file(PathAndFileName, Data)->
file:write_file(PathAndFileName, list_to_binary(Data)).
-create_htacess_dirs(Path)->
+create_htaccess_dirs(Path)->
ok = file:make_dir(filename:join([Path,"ht"])),
ok = file:make_dir(filename:join([Path,"ht/open"])),
ok = file:make_dir(filename:join([Path,"ht/blocknet"])),
ok = file:make_dir(filename:join([Path,"ht/secret"])),
ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])).
-remove_htacess_dirs(Path)->
+remove_htaccess_dirs(Path)->
file:del_dir(filename:join([Path,"ht/secret/top_secret"])),
file:del_dir(filename:join([Path,"ht/secret"])),
file:del_dir(filename:join([Path,"ht/blocknet"])),
@@ -2888,7 +2971,7 @@ format_ip(IpAddress,Pos)when Pos > 0->
format_ip(IpAddress, _Pos)->
"1" ++ IpAddress.
-remove_htacess(Path)->
+remove_htaccess(Path)->
file:delete(filename:join([Path,"ht/open/dummy.html"])),
file:delete(filename:join([Path,"ht/secret/dummy.html"])),
file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])),
@@ -2899,7 +2982,7 @@ remove_htacess(Path)->
file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])),
file:delete(filename:join([Path,"ht","users.file"])),
file:delete(filename:join([Path,"ht","groups.file"])),
- remove_htacess_dirs(Path).
+ remove_htaccess_dirs(Path).
dos_hostname_poll(Type, Host, Port, Node, Hosts) ->
@@ -2939,35 +3022,66 @@ create_range_data(Path) ->
"12345678901234567890",
"12345678901234567890"])).
-%% create_ipv6_config(Config, FileName, Ipv6Address) ->
-%% ServerRoot = ?config(server_root, Config),
-%% TcTopDir = ?config(tc_top_dir, Config),
-%% Port = ?config(port, Config),
-%% SockType = ?config(sock_type, Config),
-%%
-%% MaxHdrSz = io_lib:format("~p", [256]),
-%% MaxHdrAct = io_lib:format("~p", [close]),
-%%
-%% Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
-%% " mod_include mod_dir mod_get mod_head"
-%% " mod_log mod_disk_log mod_trace",
-%%
-%% HttpConfig = [cline(["BindAddress ", "[" ++ Ipv6Address ++"]|inet6"]),
-%% cline(["Port ", integer_to_list(Port)]),
-%% cline(["ServerName ", "httpc_test"]),
-%% cline(["SocketType ", atom_to_list(SockType)]),
-%% cline([Mod_order]),
-%% cline(["ServerRoot ", ServerRoot]),
-%% cline(["DocumentRoot ",
-%% filename:join(ServerRoot, "htdocs")]),
-%% cline(["MaxHeaderSize ",MaxHdrSz]),
-%% cline(["MaxHeaderAction ",MaxHdrAct]),
-%% cline(["DirectoryIndex ", "index.html "]),
-%% cline(["DefaultType ", "text/plain"])],
-%% ConfigFile = filename:join([TcTopDir,FileName]),
-%% {ok, Fd} = file:open(ConfigFile, [write]),
-%% ok = file:write(Fd, lists:flatten(HttpConfig)),
-%% ok = file:close(Fd).
+create_ipv6_config(Config, FileName, Ipv6Address) ->
+ ServerRoot = ?config(server_root, Config),
+ TcTopDir = ?config(tc_top_dir, Config),
+ Port = ?config(port, Config),
+ SockType = ?config(sock_type, Config),
+ Mods = io_lib:format("~p", [httpd_mod]),
+ Funcs = io_lib:format("~p", [ssl_password_cb]),
+ Host = ?config(ipv6_host, Config),
+
+ MaxHdrSz = io_lib:format("~p", [256]),
+ MaxHdrAct = io_lib:format("~p", [close]),
+
+ Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi"
+ " mod_include mod_dir mod_get mod_head"
+ " mod_log mod_disk_log mod_trace",
+
+ SSL =
+ if
+ (SockType =:= ssl) orelse
+ (SockType =:= ossl) orelse
+ (SockType =:= essl) ->
+ [cline(["SSLCertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCertificateKeyFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLCACertificateFile ",
+ filename:join(ServerRoot, "ssl/ssl_server.pem")]),
+ cline(["SSLPasswordCallbackModule ", Mods]),
+ cline(["SSLPasswordCallbackFunction ", Funcs]),
+ cline(["SSLVerifyClient 0"]),
+ cline(["SSLVerifyDepth 1"])];
+ true ->
+ []
+ end,
+
+ BindAddress = "[" ++ Ipv6Address ++"]|inet6",
+
+ HttpConfig =
+ [cline(["BindAddress ", BindAddress]),
+ cline(["Port ", integer_to_list(Port)]),
+ cline(["ServerName ", Host]),
+ cline(["SocketType ", atom_to_list(SockType)]),
+ cline([Mod_order]),
+ cline(["ServerRoot ", ServerRoot]),
+ cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]),
+ cline(["MaxHeaderSize ",MaxHdrSz]),
+ cline(["MaxHeaderAction ",MaxHdrAct]),
+ cline(["DirectoryIndex ", "index.html "]),
+ cline(["DefaultType ", "text/plain"]),
+ SSL],
+ ConfigFile = filename:join([TcTopDir,FileName]),
+ {ok, Fd} = file:open(ConfigFile, [write]),
+ ok = file:write(Fd, lists:flatten(HttpConfig)),
+ ok = file:close(Fd).
+
+
+%% tsp(F) ->
+%% inets_test_lib:tsp(F).
+tsp(F, A) ->
+ inets_test_lib:tsp(F, A).
tsf(Reason) ->
- test_server:fail(Reason).
+ inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 3189a758a5..2903aaafa5 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -22,7 +22,7 @@
-include("inets_test_lib.hrl").
%% Poll functions
--export([verify_request/6, verify_request/7, is_expect/1]).
+-export([verify_request/6, verify_request/7, verify_request/8, is_expect/1]).
-record(state, {request, % string()
socket, % socket()
@@ -81,33 +81,57 @@
%%------------------------------------------------------------------
verify_request(SocketType, Host, Port, Node, RequestStr, Options) ->
verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000).
-verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) ->
- {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port),
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options)
+ when is_list(TranspOpts) ->
+ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000);
+verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
+ when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) ->
+ verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) ->
+ tsp("verify_request -> entry with"
+ "~n SocketType: ~p"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n TranspOpts: ~p"
+ "~n Node: ~p"
+ "~n Options: ~p"
+ "~n TimeOut: ~p",
+ [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]),
+ case (catch inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts)) of
+ {ok, Socket} ->
+ tsp("verify_request -> connected - now send message"),
+ SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
+ tsp("verify_request -> send result: "
+ "~n ~p", [SendRes]),
+ State = case inets_regexp:match(RequestStr, "printenv") of
+ nomatch ->
+ #state{};
+ _ ->
+ #state{print = true}
+ end,
+
+ case request(State#state{request = RequestStr,
+ socket = Socket}, TimeOut) of
+ {error, Reason} ->
+ tsp("request failed: "
+ "~n Reason: ~p", [Reason]),
+ {error, Reason};
+ NewState ->
+ tsp("validate reply: "
+ "~n NewState: ~p", [NewState]),
+ ValidateResult =
+ validate(RequestStr, NewState, Options, Node, Port),
+ tsp("validation result: "
+ "~n ~p", [ValidateResult]),
+ inets_test_lib:close(SocketType, Socket),
+ ValidateResult
+ end;
- _SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
-
- State = case inets_regexp:match(RequestStr, "printenv") of
- nomatch ->
- #state{};
- _ ->
- #state{print = true}
- end,
-
- case request(State#state{request = RequestStr,
- socket = Socket}, TimeOut) of
- {error, Reason} ->
- tsp("request failed: "
- "~n Reason: ~p", [Reason]),
- {error, Reason};
- NewState ->
- tsp("validate reply: "
- "~n NewState: ~p", [NewState]),
- ValidateResult = validate(RequestStr, NewState, Options,
- Node, Port),
- tsp("validation result: "
- "~n ~p", [ValidateResult]),
- inets_test_lib:close(SocketType, Socket),
- ValidateResult
+ ConnectError ->
+ tsp("verify_request -> connect failed: "
+ "~n ~p"
+ "~n", [ConnectError]),
+ tsf({connect_failure, ConnectError})
end.
request(#state{mfa = {Module, Function, Args},
@@ -214,7 +238,10 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
headers = Headers,
body = Body}, Options, N, P) ->
- %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]),
+ %% tsp("validate -> entry with"
+ %% "~n StatusCode: ~p"
+ %% "~n Headers: ~p"
+ %% "~n Body: ~p", [StatusCode, Headers, Body]),
check_version(Version, Options),
case lists:keysearch(statuscode, 1, Options) of
{value, _} ->
@@ -342,8 +369,10 @@ print(_, _, #state{print = false}) ->
ok.
-%% tsp(F) ->
-%% tsp(F, []).
+tsp(F) ->
+ inets_test_lib:tsp(F).
tsp(F, A) ->
- test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+ inets_test_lib:tsp(F, A).
+tsf(Reason) ->
+ inets_test_lib:tsf(Reason).
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index 57e5d482c9..eaeb244203 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -26,18 +26,64 @@
-export([start_http_server/1, start_http_server/2]).
-export([start_http_server_ssl/1, start_http_server_ssl/2]).
-export([hostname/0]).
--export([connect_bin/3, connect_byte/3, send/3, close/2]).
+-export([connect_bin/3, connect_bin/4,
+ connect_byte/3, connect_byte/4,
+ send/3, close/2]).
-export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]).
-export([info/4, log/4, debug/4, print/4]).
+-export([tsp/1, tsp/2, tsf/1]).
-export([check_body/1]).
-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]).
--export([oscmd/1]).
+-export([oscmd/1, has_ipv6_support/1]).
-export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]).
-export([flush/0]).
-export([start_node/1, stop_node/1]).
%% -- Misc os command and stuff
+has_ipv6_support(Config) ->
+ case lists:keysearch(ipv6_hosts, Config) of
+ false ->
+ %% Do a basic check to se if
+ %% our own host has a working IPv6 address...
+ tsp("has_ipv6_support -> no ipv6_hosts config"),
+ {ok, Hostname} = inet:gethostname(),
+ case inet:getaddrs(Hostname, inet6) of
+ {ok, [Addr|_]} when is_tuple(Addr) andalso
+ (element(1, Addr) =/= 0) ->
+ %% We actually need to test that the addr can be used,
+ %% this is done by attempting to create a (tcp)
+ %% listen socket
+ tsp("has_ipv6_support -> check Addr: ~p", [Addr]),
+ case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of
+ {ok, LSock} ->
+ tsp("has_ipv6_support -> we are ipv6 host"),
+ gen_tcp:close(LSock),
+ {ok, Addr};
+ _ ->
+ undefined
+ end;
+ _ ->
+ undefined
+ end;
+ {value, {_, Hosts}} when is_list(Hosts) ->
+ %% Check if our host is in the list of *known* IPv6 hosts
+ tsp("has_ipv6_support -> Hosts: ~p", [Hosts]),
+ {ok, Hostname} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname), Hosts) of
+ true ->
+ tsp("has_ipv6_support -> we are known ipv6 host"),
+ {ok, [Addr|_]} = inet:getaddrs(Hostname, inet6),
+ {ok, Addr};
+ false ->
+ undefined
+ end;
+
+ _ ->
+ undefined
+
+ end.
+
oscmd(Cmd) ->
string:strip(os:cmd(Cmd), right, $\n).
@@ -285,29 +331,45 @@ os_based_skip(_) ->
%% Host -> atom() | string() | {A, B, C, D}
%% Port -> integer()
-connect_bin(ssl, Host, Port) ->
- connect(ssl, Host, Port, [binary, {packet,0}]);
-connect_bin(ossl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, old}, binary, {packet,0}]);
-connect_bin(essl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true}]);
-connect_bin(ip_comm, Host, Port) ->
- Opts = [inet6, binary, {packet,0}],
+connect_bin(SockType, Host, Port) ->
+ connect_bin(SockType, Host, Port, []).
+
+connect_bin(ssl, Host, Port, Opts0) ->
+ Opts = [binary, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(ossl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, old}, binary, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(essl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_bin(ip_comm, Host, Port, Opts0) ->
+ Opts = [binary, {packet, 0} | Opts0],
connect(ip_comm, Host, Port, Opts).
+
+connect_byte(SockType, Host, Port) ->
+ connect_byte(SockType, Host, Port, []).
-connect_byte(ssl, Host, Port) ->
- connect(ssl, Host, Port, [{packet,0}]);
-connect_byte(ossl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, old}, {packet,0}]);
-connect_byte(essl, Host, Port) ->
- connect(ssl, Host, Port, [{ssl_imp, new}, {packet,0}]);
-connect_byte(ip_comm, Host, Port) ->
- Opts = [inet6, {packet, 0}],
+connect_byte(ssl, Host, Port, Opts0) ->
+ Opts = [{packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(ossl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, old}, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(essl, Host, Port, Opts0) ->
+ Opts = [{ssl_imp, new}, {packet,0} | Opts0],
+ connect(ssl, Host, Port, Opts);
+connect_byte(ip_comm, Host, Port, Opts0) ->
+ Opts = [{packet,0} | Opts0],
connect(ip_comm, Host, Port, Opts).
connect(ssl, Host, Port, Opts) ->
+ tsp("connect(ssl) -> entry with"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n Opts: ~p", [Host, Port, Opts]),
ssl:start(),
%% Does not support ipv6 in old ssl
case ssl:connect(Host, Port, Opts) of
@@ -319,24 +381,28 @@ connect(ssl, Host, Port, Opts) ->
Error
end;
connect(ip_comm, Host, Port, Opts) ->
+ tsp("connect(ip_comm) -> entry with"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n Opts: ~p", [Host, Port, Opts]),
case gen_tcp:connect(Host,Port, Opts) of
{ok, Socket} ->
- %% tsp("connect success"),
+ tsp("connect success"),
{ok, Socket};
{error, nxdomain} ->
- tsp("nxdomain opts: ~p", [Opts]),
+ tsp("connect error nxdomain when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, eafnosupport} ->
- tsp("eafnosupport when opts: ~p", [Opts]),
+ tsp("connect error eafnosupport when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, econnreset} ->
- tsp("econnreset when opts: ~p", [Opts]),
+ tsp("connect error econnreset when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, enetunreach} ->
- tsp("eafnosupport when opts: ~p", [Opts]),
+ tsp("connect error eafnosupport when opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, {enfile,_}} ->
- tsp("Error enfile"),
+ tsp("connect error enfile when opts: ~p", [Opts]),
{error, enfile};
Error ->
tsp("Unexpected error: "
@@ -417,7 +483,22 @@ flush() ->
tsp(F) ->
tsp(F, []).
tsp(F, A) ->
- test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]).
+ Timestamp = formated_timestamp(),
+ test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n",
+ [Timestamp, node(), self(), ?MODULE | A]).
tsf(Reason) ->
test_server:fail(Reason).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
+