diff options
Diffstat (limited to 'lib/inets')
139 files changed, 10482 insertions, 4196 deletions
diff --git a/lib/inets/Makefile b/lib/inets/Makefile index ec05efa461..4765a2ca3c 100644 --- a/lib/inets/Makefile +++ b/lib/inets/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2010. All Rights Reserved. +# Copyright Ericsson AB 1996-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 @@ -36,6 +36,8 @@ SPECIAL_TARGETS = # ---------------------------------------------------- include $(ERL_TOP)/make/otp_subdir.mk +.PHONY: info gclean + info: @echo "OS: $(OS)" @echo "DOCB: $(DOCB)" @@ -44,3 +46,5 @@ info: @echo "APP_VSN: $(APP_VSN)" @echo "" +gclean: + git clean -fXd diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml index 25dfe716fc..ca902d8d9d 100644 --- a/lib/inets/doc/src/ftp.xml +++ b/lib/inets/doc/src/ftp.xml @@ -107,7 +107,7 @@ <tag>{mode, Mode}</tag> <item> <marker id="mode"></marker> - <p>Mode = <c>active | passive</c> </p>> + <p>Mode = <c>active | passive</c> </p> <p>Default is <c>passive</c>. </p> </item> diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml index 49327ca80f..4542211d71 100644 --- a/lib/inets/doc/src/http_client.xml +++ b/lib/inets/doc/src/http_client.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -42,10 +42,10 @@ dynamically in runtime. Each client profile will spawn a new process to handle each request unless there is a possibility to use a persistent connection with or without pipelining. - The client will add a host header and an empty - te header if there are no such headers present in the request.</p> + The client will add a <c>host</c> header and an empty + <c>te</c> header if there are no such headers present in the request.</p> - <p>The clients supports ipv6 as long as the underlying mechanisms also do + <p>The client supports ipv6 as long as the underlying mechanisms also do so.</p> </section> @@ -87,7 +87,7 @@ httpc:request("http://www.erlang.org"). </code> <p>An ordinary asynchronous request. The result will be sent - to the calling process on the form {http, {ReqestId, Result}}</p> + to the calling process in the form <c>{http, {ReqestId, Result}}</c></p> <code type="erl"> 5 > {ok, RequestId} = httpc:request(get, {"http://www.erlang.org", []}, [], [{sync, false}]). diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml index 68dfd1add0..f29b505bc7 100644 --- a/lib/inets/doc/src/http_server.xml +++ b/lib/inets/doc/src/http_server.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -63,9 +63,9 @@ technologies such as SOAP.</p> <p>Allmost all server functionality has been implemented using an - especially crafted server API, it is described in the Erlang Web - Server API. This API can be used to advantage by all who wants - to enhance the server core functionality, for example custom + especially crafted server API which is described in the Erlang Web + Server API. This API can be used to advantage by all who wish + to enhance the core server functionality, for example with custom logging and authentication.</p> <marker id="config"></marker> @@ -406,7 +406,7 @@ http://your.server.org/***/Module[:/]Function(?QueryString|/PathInfo) phase instead of first generating the whole web page and then sending it to the client. The option to implement a function with arity two is only kept for - backwardcompatibilty reasons. + backwards compatibility reasons. See <seealso marker="mod_esi">mod_esi(3)</seealso> for implementation details of the esi callback function.</p> </section> @@ -472,7 +472,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <tag><em>bytes</em></tag> <item>The content-length of the document transferred. </item> </taglist> - <p>Internal server errors are recorde in the error log file. The + <p>Internal server errors are recorded in the error log file. The format of this file is a more ad hoc format than the logs using Common Logfile Format, but conforms to the following syntax: </p> @@ -766,7 +766,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <code> -module(mnesia_test). -export([start/0,load_data/0]). --include("mod_auth.hrl"). +-include_lib("mod_auth.hrl"). first_start() -> mnesia:create_schema([node()]), diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 7430a62b1b..d1671ac9bd 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,7 +36,7 @@ <note> <p>When starting the Inets application a manager process for the default profile will be started. The functions in this API - that does not explicitly use a profile will accesses the + that do not explicitly use a profile will access the default profile. A profile keeps track of proxy options, cookies and other options that can be applied to more than one request. </p> @@ -76,38 +76,37 @@ socket_opt() = See the Options used by gen_tcp(3) and <p>For more information about HTTP see rfc 2616</p> <code type="none"><![CDATA[ -method() = head | get | put | post | trace | options | delete -request() = {url(), headers()} | - {url(), headers(), content_type(), body()} -url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" -status_line() = {http_version(), status_code(), reason_phrase()} -http_version() = string() ex: "HTTP/1.1" -status_code() = integer() -reason_phrase() = string() -content_type() = string() -headers() = [header()] -header() = {field(), value()} -field() = string() -value() = string() -body() = string() | binary() -filename() = string() +method() = head | get | put | post | trace | options | delete +request() = {url(), headers()} | + {url(), headers(), content_type(), body()} +url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" +status_line() = {http_version(), status_code(), reason_phrase()} +http_version() = string() ex: "HTTP/1.1" +status_code() = integer() +reason_phrase() = string() +content_type() = string() +headers() = [header()] +header() = {field(), value()} +field() = string() +value() = string() +body() = string() | + binary() | + {fun(accumulator()) -> body_processing_result(), + accumulator()} | + {chunkify, + fun(accumulator()) -> body_processing_result(), + accumulator()} +body_processing_result() = eof | {ok, iolist(), accumulator()} +accumulator() = term() +filename() = string() ]]></code> </section> <section> <title>SSL DATA TYPES </title> - <p>Some type definitions relevant when using https, - for details <seealso marker="ssl:ssl">ssl(3)</seealso>: </p> - <code type="none"><![CDATA[ -ssl_options() = {verify, code()} | - {depth, depth()} | - {certfile, path()} | - {keyfile, path()} | - {password, string()} | - {cacertfile, path()} | - {ciphers, string()} - ]]></code> + <p>See <seealso marker="ssl:ssl">ssl(3)</seealso> for information + about ssl options (<c>ssloptions()</c>). </p> </section> <section> @@ -117,7 +116,7 @@ ssl_options() = {verify, code()} | application or started dynamically in runtime by calling the inets application API <c>inets:start(httpc, ServiceConfig)</c>, or <c>inets:start(httpc, ServiceConfig, How)</c> - see <seealso marker="inets">inets(3)</seealso> Below follows a + see <seealso marker="inets">inets(3)</seealso>. Below follows a description of the available configuration options.</p> <taglist> <tag>{profile, profile()}</tag> @@ -129,8 +128,8 @@ ssl_options() = {verify, code()} | as session cookies.</item> </taglist> - <p>The client can be stopped using inets:stop(httpc, Pid) or - inets:stop(httpc, Profile).</p> + <p>The client can be stopped using <c>inets:stop(httpc, Pid)</c> or + <c>inets:stop(httpc, Profile)</c>.</p> <marker id="request1"></marker> </section> @@ -142,13 +141,14 @@ ssl_options() = {verify, code()} | <fsummary>Sends a get HTTP-request</fsummary> <type> <v>Url = url() </v> - <v>Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() </v> - <v>Profile = profile()</v> + <v>Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() </v> + <v>Body = string() | binary()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> <v>Reason = term() </v> </type> <desc> - <p>Equivalent to httpc:request(get, {Url, []}, [], []).</p> + <p>Equivalent to <c>httpc:request(get, {Url, []}, [], [])</c>.</p> <marker id="request2"></marker> </desc> @@ -166,11 +166,14 @@ ssl_options() = {verify, code()} | <v>http_options() = [http_option()]</v> <v>http_option() = {timeout, timeout()} | {connect_timeout, timeout()} | - {ssl, ssl_options()} | + {ssl, ssloptions()} | + {ossl, ssloptions()} | + {essl, ssloptions()} | {autoredirect, boolean()} | {proxy_auth, {userstring(), passwordstring()}} | {version, http_version()} | - {relaxed, boolean()}</v> + {relaxed, boolean()} | + {url_encode, boolean()}</v> <v>timeout() = integer() >= 0 | infinity</v> <v>Options = options()</v> <v>options() = [option()]</v> @@ -188,26 +191,27 @@ ssl_options() = {verify, code()} | <v>Function = atom() </v> <v>Args = list() </v> <v>body_format() = string | binary </v> - <v>Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() </v> - <v>Profile = profile() </v> + <v>Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() </v> + <v>Body = string() | binary()</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 - {ok, RequestId} 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> <tag><c><![CDATA[timeout]]></c></tag> <item> <p>Timeout time for the request. </p> - <p>The clock start ticking as soon as the request has been - sent. </p> + <p>The clock starts ticking as soon as the request has been + sent. </p> <p>Time is in milliseconds. </p> <p>Defaults to <c>infinity</c>. </p> </item> @@ -215,50 +219,71 @@ ssl_options() = {verify, code()} | <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> <tag><c><![CDATA[ssl]]></c></tag> <item> - <p>If using SSL, these SSL-specific options are used. </p> + <p>This is the default ssl config option, currently defaults to + <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> + <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> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[autoredirect]]></c></tag> <item> - <p>Should the client automatically retreive 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 trsing <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 true 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>Defaults to <c>false</c>. </p> + </item> </taglist> <p>Option (<c>option()</c>) details: </p> @@ -272,77 +297,77 @@ ssl_options() = {verify, code()} | <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 the following stream messages - will be sent to that process: {http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}. When - streaming to to the calling processes using the option - <c>{self, once}</c> the first message will have an additional - element e.i. {http, {RequestId, stream_start, Headers, Pid}, - this is the process id that should be used as an argument to - http:stream_next/1 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 stream_end - message than in the stream_start. - When streaming to a file and the request is asynchronous the - message {http, {RequestId, saved_to_file}} 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 has 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">inet:setopts/2</seealso>) - for an already existing, and therefor already connected - request handler. </p> - <p>By defaults the socket options set by the - <seealso marker="#set_options">set_options/1,2</seealso> - function is used when establishing connection. </p> + (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> </item> <tag><c><![CDATA[receiver]]></c></tag> <item> - <p>Defines how the client will deliver the result for a - asynchroneous request (<c>sync</c> has the value - <c>false</c>). </p> + <p>Defines how the client will deliver the result of an + asynchroneous request (<c>sync</c> has the value + <c>false</c>). </p> <taglist> <tag><c><![CDATA[pid()]]></c></tag> @@ -356,7 +381,7 @@ ssl_options() = {verify, code()} | <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> @@ -365,14 +390,14 @@ 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> </item> </taglist> - <p>In all cases above, <c>ReplyInfo</c> has the following + <p>In all of the above cases, <c>ReplyInfo</c> has the following structure: </p> <pre> @@ -386,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> @@ -401,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> @@ -447,67 +472,66 @@ apply(Module, Function, [ReplyInfo | Args]) <v>IpDesc = string()</v> <d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d> <v>MaxSessions = integer() </v> - <d>Default is <em>2</em>. + <d>Default is <c>2</c>. Maximum number of persistent connections to a host.</d> <v>MaxKeepAlive = integer() </v> - <d>Default is <em>5</em>. + <d>Default is <c>5</c>. Maximum number of outstanding requests on the same connection to a host.</d> <v>KeepAliveTimeout = integer() </v> - <d>Default is <em>120000</em> (= 2 min). + <d>Default is <c>120000</c> (= 2 min). If a persistent connection is idle longer than the - keep_alive_timeout the client will close the connection. - The server may also have a such a time out but you should + <c>keep_alive_timeout</c> the client will close the connection. + The server may also have such a time out but you should not count on it!</d> <v>MaxPipeline = integer() </v> - <d>Default is <em>2</em>. + <d>Default is <c>2</c>. Maximum number of outstanding requests on a pipelined connection to a host.</d> <v>PipelineTimeout = integer() </v> - <d>Default is <em>0</em>, + <d>Default is <c>0</c>, which will result in pipelining not being used. If a persistent connection is idle longer than the - pipeline_timeout the client will close the connection. </d> + <c>pipeline_timeout</c> the client will close the connection. </d> <v>CookieMode = enabled | disabled | verify </v> - <d>Default is <em>disabled</em>. + <d>Default is <c>disabled</c>. If Cookies are enabled all valid cookies will automatically be saved in the client manager's cookie database. - If the option verify is used the function http:verify_cookie/2 - has to be called for the cookie to be saved.</d> + If the option <c>verify</c> is used the function <c>store_cookies/2</c> + has to be called for the cookies to be saved.</d> <v>IpFamily = inet | inet6 | inet6fb4 </v> - <d>By default <em>inet</em>. + <d>By default <c>inet</c>. When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. The option is here to provide a workaround for buggy ipv6 stacks to ensure that ipv4 will always work.</d> <v>IpAddress = ip_address() </v> <d>If the host has several network interfaces, this option specifies which one to use. - See gen_tcp:connect/3,4 for more info. </d> + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> <v>Port = integer() </v> <d>Specify which local port number to use. - See gen_tcp:connect/3,4 for more info. </d> + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> <v>VerboseMode = false | verbose | debug | trace </v> - <d>Default is <em>false</em>. + <d>Default is <c>false</c>. 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 with or without pipeline depending on configuration and current circumstances. The HTTP/1.1 specification does not - provide a guideline for how many requests that would be + provide a guideline for how many requests would be ideal to be sent on a persistent connection, this very much depends on the application. Note that a very long queue of requests may cause a - user perceived delays as earlier request may take a long time + user perceived delay as earlier requests may take a long time to complete. The HTTP/1.1 specification does suggest a limit of 2 persistent connections per server, which is the - default value of the max_sessions option. </p> + default value of the <c>max_sessions</c> option. </p> </note> <marker id="stream_next"></marker> @@ -524,28 +548,28 @@ 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_cookie"></marker> - <marker id="store_cookie"></marker> + <marker id="verify_cookies"></marker> + <marker id="store_cookies"></marker> </desc> </func> <func> - <name>store_cookie(SetCookieHeaders, Url) -> </name> - <name>store_cookie(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name> + <name>store_cookies(SetCookieHeaders, Url) -> </name> + <name>store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name> <fsummary>Saves the cookies defined in SetCookieHeaders in the client profile's cookie database.</fsummary> <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 set the option cookies 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> @@ -553,18 +577,17 @@ apply(Module, Function, [ReplyInfo | Args]) <func> <name>cookie_header(Url) -> </name> - <name>cookie_header(Url, Profile) -> header() | {error, Rason}</name> + <name>cookie_header(Url, Profile) -> header() | {error, Reason}</name> <fsummary>Returns the cookie header that would be sent when - making a request to Url using the profile Profile.</fsummary> + 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 Url using the profile Profile. - 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> @@ -576,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 Profile. - 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> @@ -591,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> @@ -615,4 +637,3 @@ apply(Module, Function, [ReplyInfo | Args]) </section> </erlref> - diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index f061488ac3..edacb73b65 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -148,8 +148,13 @@ in the apache like configuration file. </item> - <tag>{socket_type, ip_comm | ssl}</tag> + <tag>{socket_type, ip_comm | ssl | ossl | essl}</tag> <item> + <p>When using ssl, there are several alternatives. + <c>ossl</c> specifically uses the OpenSSL based (old) SSL. + <c>essl</c> specifically uses the Erlang based (new) SSL. + When using <c>ssl</c> it <em>currently</em> defaults to + <c>essl</c>. </p> <p>Defaults to <c>ip_comm</c>. </p> </item> @@ -267,18 +272,22 @@ text/plain asc txt The <c>common</c> format is one line that looks like this: <c>remotehost rfc931 authuser [date] "request" status bytes</c></p> - <pre>remotehost + <pre> +remotehost Remote rfc931 The client's remote username (RFC 931). authuser - The username with which the user authenticated himself. + The username with which the user authenticated + himself. [date] Date and time of the request (RFC 1123). "request" - The request line exactly as it came from the client(RFC 1945). + The request line exactly as it came from the client + (RFC 1945). status - The HTTP status code returned to the client (RFC 1945). + The HTTP status code returned to the client + (RFC 1945). bytes The content-length of the document transferred. </pre> @@ -286,10 +295,11 @@ bytes <p>The <c>combined</c> format is on line that look like this: <c>remotehost rfc931 authuser [date] "request" status bytes "referer" "user_agent" </c></p> - <pre>"referer" + <pre> +"referer" The url the client was on before - requesting your url. (If it could not be determined a minus - sign will be placed in this field) + requesting your url. (If it could not be determined + a minus sign will be placed in this field) "user_agent" The software the client claims to be using. (If it could not be determined a minus sign will be placed in @@ -389,6 +399,31 @@ bytes and an access to http://your.server.org/image/foo.gif would refer to the file /ftp/pub/image/foo.gif.</item> + <tag>{re_write, {Re, Replacement}}</tag> + + <item> Where Re = string() and Replacement = string(). + The ReWrite property allows documents to be stored in the local file + system instead of the document_root location. URLs are rewritten + by re:replace/3 to produce a path in the local filesystem. + For example: + + <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}</code> + + and an access to http://your.server.org/~bob/foo.gif would refer to + the file /home/bob/public/foo.gif. + + In an Apache like configuration file the Re is separated + from Replacement with one single space, and as expected + backslashes do not need to be backslash escaped so the + same example would become: + + <code>ReWrite ^/[~]([^/]+)(.*)$ /home/\1/public\2</code> + + Beware of trailing space in Replacement that will be used. + If you must have a space in Re use e.g the character encoding + <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. + </item> + <tag>{directory_index, [string()]}</tag> <item> @@ -408,7 +443,7 @@ bytes </taglist> <marker id="cgi_prop"></marker> - <p><em>CGI properties - requires mod_cgi</em></p> + <p><em>CGI properties - requires mod_cgi</em></p> <taglist> <tag>{script_alias, {Alias, RealName}}</tag> <item> Where Alias = string() and RealName = string(). @@ -423,6 +458,19 @@ bytes the server to run the script /web/cgi-bin/foo. </item> + <tag>{script_re_write, {Re, Replacement}}</tag> + <item> Where Re = string() and Replacement = string(). + Has the same behavior as the ReWrite property, except that + it also marks the target directory as containing CGI + scripts. URLs with a path beginning with url-path are mapped to + scripts beginning with directory-filename, for example: + + <code> {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> + + and an access to http://your.server.org/cgi-bin/17/foo would cause + the server to run the script /web/17/cgi-bin/foo. + </item> + <tag>{script_nocache, boolean()}</tag> <item> @@ -477,12 +525,13 @@ bytes scheme scripts. A matching URL is mapped into a specific module and function. For example: - <code>{erl_script_alias, {"/cgi-bin/example" [httpd_example]} + <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]} </code> and a request to http://your.server.org/cgi-bin/example/httpd_example:yahoo - would refer to httpd_example:yahoo/2 and + would refer to httpd_example:yahoo/3 or, if that did not exist, + httpd_example:yahoo/2 and http://your.server.org/cgi-bin/example/other:yahoo would not be allowed to execute. </item> @@ -745,14 +794,14 @@ bytes <marker id="sdir_prop"></marker> <p> Here follows the valid properties for security directories</p> <taglist> - <tag>{security_data_file, path()}</tag> + <tag>{data_file, path()}</tag> <item> Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to store persistent data for the mod_security module. </item> - <tag>{security_max_retries, integer()}</tag> + <tag>{max_retries, integer()}</tag> <item> Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user @@ -762,13 +811,13 @@ bytes server will return 401 (Unauthorized), for security reasons. Defaults to 3 may also be set to infinity.</item> - <tag>{security_block_time, integer()}</tag> + <tag>{block_time, integer()}</tag> <item> Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. Defaults to 60</item> - <tag>{security_fail_expire_time, integer()}</tag> + <tag>{fail_expire_time, integer()}</tag> <item> Specifies the number of minutes a failed user authentication @@ -776,7 +825,7 @@ bytes time, his previous failed authentications are forgotten. Defaults to 30</item> - <tag>{security_auth_timeout, integer()}</tag> + <tag>{auth_timeout, integer()}</tag> <item> Specifies the number of seconds a successful user @@ -883,6 +932,10 @@ bytes connection }). </code> + + <p>To acess the record in your callback-module use </p> + <code> -include_lib("inets/include/httpd.hrl"). </code> + <p>The fields of the <c>mod</c> record has the following meaning: </p> <taglist> @@ -930,10 +983,10 @@ bytes <c>parsed_header</c> contains all HTTP header fields from the HTTP-request stored in a list as key-value tuples. See RFC 2616 for a listing of all header fields. For example the date field - would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"}. + would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"} </c>. RFC 2616 defines that HTTP is a case insensitive protocol and the header fields may be in lower case or upper case. Httpd will - ensure that all header field names are in lower case. </c>. + ensure that all header field names are in lower case. </item> <tag><c>entity_body</c></tag> <item>The <c>Entity-Body</c> as defined diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml index c367d7fa77..079f60779d 100644 --- a/lib/inets/doc/src/inets.xml +++ b/lib/inets/doc/src/inets.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2007</year><year>2010</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,8 +32,10 @@ <modulesummary>The inets services API</modulesummary> <description> <p>This module provides the most basic API to the - clients and servers, that are part of the Inets application, - such as start and stop. </p> + clients and servers, that are part of the Inets application, + such as start and stop. </p> + + <marker id="common_data_types"></marker> </description> <section> @@ -42,7 +44,10 @@ this module: </p> <p><c> service() = ftpc | tftp | httpc | httpd</c></p> <p><c> property() = atom() </c></p> + <marker id="functions"></marker> + <marker id="services"></marker> </section> + <funcs> <func> <name>services() -> [{Service, Pid}]</name> @@ -54,11 +59,13 @@ <desc> <p>Returns a list of currently running services.</p> <note> - <p>Services started as <c>stand_alone</c> will not - be listed.</p> + <p>Services started as <c>stand_alone</c> will not be listed.</p> </note> + + <marker id="services_info"></marker> </desc> </func> + <func> <name>services_info() -> [{Service, Pid, Info}]</name> <fsummary>Returns a list of currently running services where @@ -73,11 +80,13 @@ </type> <desc> <p>Returns a list of currently running services where each - service is described by a [{Option, Value}] list. The - information given in the list is specific for each service - and it is probable that each service will have its own info - function that gives you even more details about the - service.</p> + service is described by a [{Option, Value}] list. The + information given in the list is specific for each service + and it is probable that each service will have its own info + function that gives you even more details about the + service.</p> + + <marker id="service_names"></marker> </desc> </func> @@ -89,6 +98,8 @@ </type> <desc> <p>Returns a list of available service names.</p> + + <marker id="start"></marker> </desc> </func> @@ -101,18 +112,24 @@ </type> <desc> <p>Starts the Inets application. Default type - is temporary. See also - <seealso marker="kernel:application">application(3)</seealso></p> + is temporary. See also + <seealso marker="kernel:application">application(3)</seealso>. </p> + + <marker id="stop"></marker> </desc> </func> + <func> <name>stop() -> ok </name> <fsummary>Stops the inets application.</fsummary> <desc> <p>Stops the inets application. See also - <seealso marker="kernel:application">application(3)</seealso></p> + <seealso marker="kernel:application">application(3)</seealso>. </p> + + <marker id="start2"></marker> </desc> </func> + <func> <name>start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name> <name>start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name> @@ -144,8 +161,11 @@ some sense the calling process has now become the top supervisor.</p> </note> + + <marker id="stop2"></marker> </desc> </func> + <func> <name>stop(Service, Reference) -> ok | {error, Reason} </name> <fsummary>Stops a started service of the inets application or takes @@ -157,9 +177,11 @@ </type> <desc> <p>Stops a started service of the inets application or takes - down a "stand_alone-service" gracefully. When the - <c>stand_alone</c> option is used in start, - only the pid is a valid argument to stop.</p> + down a "stand_alone-service" gracefully. When the + <c>stand_alone</c> option is used in start, + only the pid is a valid argument to stop.</p> + + <marker id="see_also"></marker> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml index f3628c8297..2134ebeeae 100644 --- a/lib/inets/doc/src/mod_auth.xml +++ b/lib/inets/doc/src/mod_auth.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -80,7 +80,7 @@ <marker id="delete_user"></marker> <p><c>delete_user/2, delete_user/3</c> and <c>delete_user/4</c> deletes a user - from the user database. If the operation is succesfull, this + from the user database. If the operation is successful, this function returns <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned. When <c>delete_user/2</c> is called the Port and Dir options are mandatory.</p> @@ -111,7 +111,8 @@ </desc> </func> <func> - <name>list_users(Options) -> {ok, Users} | {error, Reason} <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> + <name>list_users(Options) -> {ok, Users} | {error, Reason}</name> + <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> <name>list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name> <fsummary>List users in the user database.</fsummary> <type> diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index 6bad77dc0a..9906ae0895 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,17 +31,20 @@ <module>mod_esi</module> <modulesummary>Erlang Server Interface </modulesummary> <description> - <p>This module defines the API - Erlang Server Interface (ESI). - Which is a more efficient way of writing erlang scripts - for your Inets web server than writing them as common CGI scripts.</p> + <p>This module defines the Erlang Server Interface (ESI) API. + It is a more efficient way of writing erlang scripts + for your Inets web server than writing them as common CGI scripts.</p> + + <marker id="deliver"></marker> </description> + <funcs> <func> <name>deliver(SessionID, Data) -> ok | {error, Reason}</name> <fsummary>Sends Data back to client.</fsummary> <type> <v>SessionID = term()</v> - <v>Data = string() | io_list()</v> + <v>Data = string() | io_list() | binary()</v> <v>Reason = term()</v> </type> <desc> @@ -51,13 +54,15 @@ parts of the content to the user.</p> <p>Sends data from a Erl Scheme script back to the client.</p> - <note><p>Note - that if any HTTP-header fields should be added by the - script they must be in the first call to deliver/2 and the - data in the call must be a string. Do not - assume anything about the data type of SessionID, the - SessionID must be the value given as input to the esi - call back function that you implemented.</p></note> + <note> + <p>Note that if any HTTP-header fields should be added by the + script they must be in the first call to deliver/2 and the + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of + SessionID, the SessionID must be the value given as input to + the esi call back function that you implemented.</p> + </note> </desc> </func> </funcs> @@ -73,28 +78,29 @@ <v>SessionID = term()</v> <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v> <v>EnvironmentDirectives = {Key,Value}</v> - <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name. <v>Input = string()</v> + <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name</v> + <v>Input = string()</v> </type> <desc> <p>The <c>Module</c> must be found in the code path and export - <c>Function</c> with an arity of two. An erlScriptAlias must + <c>Function</c> with an arity of three. An erlScriptAlias must also be set up in the configuration file for the Web server.</p> - <p>If the HTTP request is a post request and a body is sent + <p>If the HTTP request is a 'post' request and a body is sent then content_length will be the length of the posted - data. If get is used query_string will be the data after + data. If 'get' is used query_string will be the data after <em>?</em> in the url.</p> <p>ParsedHeader is the HTTP request as a key value tuple list. The keys in parsed header will be the in lower case.</p> <p>SessionID is a identifier - the server use when <c>deliver/2</c> is called, do not - assume any-thing about the datatype.</p> + the server uses when <c>deliver/2</c> is called; do not + assume anything about the datatype.</p> <p>Use this callback function to dynamically generate dynamic web - content. when a part of the page is generated send the + content. When a part of the page is generated send the data back to the client through <c>deliver/2</c>. Note that the first chunk of data sent to the client must at least contain all HTTP header fields that the response - will generate. If the first chunk not contains - <em>End of HTTP header</em> that is <c>"\r\n\r\n"</c> + will generate. If the first chunk does not contain the + <em>End of HTTP header</em>, that is <c>"\r\n\r\n",</c> the server will assume that no HTTP header fields will be generated.</p> </desc> @@ -105,14 +111,15 @@ <type> <v>Env = [EnvironmentDirectives] ++ ParsedHeader</v> <v>EnvironmentDirectives = {Key,Value}</v> - <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name. <v>Input = string()</v> + <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name.</v> + <v>Input = string()</v> <v>Response = string()</v> </type> <desc> - <p>This callback format consumes quite much memory since the + <p>This callback format consumes a lot of memory since the whole response must be generated before it is sent to the - user. This functions is deprecated and only keept for backwards - compatibility. + user. This function is deprecated and only kept for backwards + compatibility. For new development Module:Function/3 should be used.</p> </desc> </func> diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml index 2a871d29d8..a3c91dca5b 100644 --- a/lib/inets/doc/src/mod_security.xml +++ b/lib/inets/doc/src/mod_security.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1998</year><year>2010</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index a113652868..44f25e8492 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>Inets Release Notes</title> @@ -32,8 +32,23 @@ <file>notes.xml</file> </header> - <section><title>Inets 5.3.6</title> + <section><title>Inets 5.7.2</title> + <section><title>Improvements and New Features</title> + <p>-</p> + +<!-- + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> +--> + + </section> + <section><title>Fixed Bugs and Malfunctions</title> <list> <item> @@ -47,93 +62,430 @@ </list> </section> - </section> <!-- 5.3.6 --> + </section> <!-- 5.7.2 --> - <section><title>Inets 5.3.5</title> - + <section><title>Inets 5.7.1</title> + + <section><title>Improvements and New Features</title> + <p>-</p> + +<!-- + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> +--> + + </section> + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + <list> <item> - <p>Updated http-server to make sure URLs in error-messages - are URL-encoded. Added support in http-client to use - URL-encoding. Also added the missing include directory - for the inets application.</p> - <p>Own Id: OTP-8940</p> - <p>Aux Id: seq11735</p> + <p>[httpc] Parsing of a cookie expire date should be more forgiving. + That is, if the parsing fails, the date should be ignored. + Also added support for (yet another) date format: + "Tue Jan 01 08:00:01 2036 GMT". </p> + <p>Own Id: OTP-9433</p> + </item> + + <item> + <p>[httpc] Rewrote cookie parsing. Among other things solving + cookie processing from www.expedia.com. </p> + <p>Own Id: OTP-9434</p> + </item> + + <item> + <p>[httpd] Fix httpd directory traversal on Windows. + Directory traversal was possible on Windows where + backward slash is used as directory separator. </p> + <p>Andr�s Veres-Szentkir�lyi.</p> + <p>Own Id: OTP-9561</p> + </item> + + </list> + </section> + + </section> <!-- 5.7.1 --> + + + <section><title>Inets 5.7</title> + + <section><title>Improvements and New Features</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc] Remove unnecessary usage of iolist_to_binary when + processing body (for PUT and POST). </p> + <p>Filipe David Manana</p> + <p>Own Id: OTP-9317</p> + </item> + + <item> + <p>[ftp] FTP client doesn't work with IPv6 host.</p> + <p>Attila Rajmund Nohl</p> + <p>Own Id: OTP-9342 Aux Id: seq11853</p> + </item> + + <item> + <p>[httpd] Peer/sockname resolv doesn't work with IPv6 addrs + in HTTP. </p> + <p>Attila Rajmund Nohl.</p> + <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> + + </section> <!-- 5.7 --> + + + <section><title>Inets 5.6</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> + </item> + + <item> + <p>[ftp] Added (type) spec for all exported functions.</p> + <p>Own Id: OTP-9114 Aux Id: seq11799</p> + </item> + + <item> + <p>[httpd] + <seealso marker="mod_esi#deliver">mod_esi:deliver/2</seealso> + made to accept binary data. </p> + <p>Bernard Duggan</p> + <p>Own Id: OTP-9123</p> </item> <item> <p>[httpd] Prevent XSS in error pages. - Prevent user controlled input from being interpreted - as HTML in error pages by encoding the reserved HTML - characters. </p> - <p>Michael Santos</p> + Prevent user controlled input from being interpreted + as HTML in error pages by encoding the reserved HTML + characters. </p> + <p>Michael Santos</p> <p>Own Id: OTP-9124</p> </item> + + <item> + <p>[httpd] Improved error messages. </p> + <p>Ricardo Catalinas Jim�nez</p> + <p>Own Id: OTP-9157</p> + </item> + + <item> + <p>[httpd] Extended support for file descriptors. + In order to be able to bind to a privileged port + without running the erlang VM as root, the support + for using file descriptors has been improved. + It is now possible to add the file descriptor to the config + (option fd) when calling the + <seealso marker="inets#start2">inets:start(httpd, ...)</seealso> + function. </p> + <p>Attila Rajmund Nohl</p> + <p>Own Id: OTP-9202</p> + <p>Aux Id: seq11819</p> + </item> + + <item> + <p>The default ssl kind has now been changed to <c>essl</c>. </p> + <p><c>ossl</c> will work for as long as the ssl application + supports it. </p> + <p>See the httpd + <seealso marker="httpd#comm_prop">socket_type</seealso> + communication property or the httpc + <seealso marker="httpc#request2">request/4,5</seealso> function + for more info. </p> + <p>Own Id: OTP-9230</p> + <p>*** POTENTIAL INCOMPATIBILITY ***</p> + </item> + </list> </section> - - </section> <!-- 5.3.5 --> + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> - <section><title>Inets 5.3.4</title> + <list> + <item> + <p>[httpd] Wrong + <seealso marker="httpd#sec_prop">security property</seealso> + names used in documentation. </p> + <p><c>security_data_file</c> used instead of <c>data_file</c>. </p> + <p><c>security_max_retries</c> used instead of <c>max_retries</c>. </p> + <p><c>security_block_time</c> used instead of <c>block_time</c>. </p> + <p><c>security_fail_expire_time</c> used instead of <c>fail_expire_time</c>. </p> + <p><c>security_auth_timeout</c> used instead of <c>auth_timeout</c>. </p> + <p>Garrett Smith</p> + <p>Own Id: OTP-9131</p> + </item> + + <item> + <p>[httpd] Fix timeout message generated by mod_esi. + When a mod_esi request times out, the code to send a + timeout response was incorrect and generated an + internal server error as well as an invalid response + line. </p> + <p>Bernard Duggan</p> + <p>Own Id: OTP-9158</p> + </item> + + <item> + <p>[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash. </p> + <p>This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions. </p> + <p>Own Id: OTP-9246</p> + </item> + + <item> + <p>[httpc] Issuing a request (<c>httpc:request</c>) to an + host with the ssl option + <c>{ip, {127,0,0,1}}</c> results in an handler crash. + The reason was that the connect call resulted in an exit with + reason <c>badarg</c> + (this was the same for both <c>ssl</c> and <c>gen_tcp</c>). </p> + <p>Exits was not catched. This has now been improved. </p> + <p>Own Id: OTP-9289</p> + <p>Aux Id: seq11845</p> + </item> + + </list> + </section> + + </section> <!-- 5.6 --> + + + <section><title>Inets 5.5.2</title> <section><title>Improvements and New Features</title> - <p>-</p> + <p>-</p> <!-- <list> <item> - <p>[httpc] - Allow users to pass socket options to the transport - module when making requests. </p> - <p>See the <c>socket_opts</c> option in the - <seealso marker="httpc#request2">request/4</seealso> or - <seealso marker="httpc#set_options">set_options/1,2</seealso> - for more info, </p> - <p>Own Id: OTP-8352</p> + <p> + Miscellaneous inet6 related problems.</p> + <p>Own Id: OTP-8927</p> </item> - </list> --> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>[httpd] httpd_response:send_chunk handles empty list and + empty binary - i.e. no chunk is sent, but it does + not handle a list with an empty binary [<<>>]. + This will be sent as an empty chunk - which in turn + will be encoded by http_chunk to the same as a final + chunk, which will make the http client believe that + the end of the page is reached.</p> + <p>Own Id: OTP-8906</p> + </item> + </list> + </section> + + </section> <!-- 5.5.2 --> + + + <section><title>Inets 5.5.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Miscellaneous inet6 related problems.</p> + <p>Own Id: OTP-8927</p> + </item> + <item> + <p>Updated http-server to make sure URLs in error-messages + are URL-encoded. Added support in http-client to use + URL-encoding. Also added the missing include directory + for the inets application.</p> + <p>Own Id: OTP-8940</p> + <p>Aux Id: seq11735</p> + </item> + </list> + </section> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages. </p> + <p>Own Id: OTP-8600</p> + </item> + <item> + <p>[httpc] Pipelined and queued requests not processed when + connection closed remotelly.</p> + <p>Own Id: OTP-8906</p> + </item> + </list> </section> + </section> <!-- 5.5.1 --> + + + <section><title>Inets 5.5</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + [httpc] If a request times out (not connect timeout), the + handler process exited (normal) but neglected to inform + the manager process. For this reason, the manager did not + clean up the request table., resulting in a memory leak. + Also the manager did not create a monitor for the + handler, so in an unforseen handler crash, this could + also create a memory leak.</p> + <p> + Own Id: OTP-8739</p> + </item> + <item> + <p> + The service tftp was spelled wrong in documentation and + in some parts of the code. It should be tftp.</p> + <p> + Own Id: OTP-8741 Aux Id: seq11635 </p> + </item> + <item> + <p> + [httpc] Replaced the old http client api module (http) + with the new, httpc in the users guide.</p> + <p> + Own Id: OTP-8742</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Eliminated warnings for auto-imported BIF clashes.</p> + <p> + Own Id: OTP-8840</p> + </item> + </list> + </section> + + </section> <!-- 5.5 --> + + <section><title>Inets 5.4</title> + + <section><title>Improvements and New Features</title> <!-- <p>-</p> --> <list> <item> - <p>[httpc] - If a request times out (not during connect), the - handler process exited (normal) but neglected to inform - the manager process. For this reason, the manager - did not clean up the request table, resulting in a - memory leak. Also the manager did not create a monitor - for the handler, so in an unforseen handler crash, this - could also create a memory leak. </p> - <p>Own Id: OTP-8739</p> + <p>[httpc|httpd] - Now allow the use of the "new" ssl, by using + the <c>essl</c> tag instead. </p> + <p>See the <c>http_option</c> option in the + <seealso marker="httpc#request2">request/4,5</seealso> or + the <seealso marker="httpd#comm_prop">socket-type</seealso> + section of the Communication properties chapter for more info, </p> + <p>Own Id: OTP-7907</p> </item> <item> - <p>[tftp] - Was spelled wrong in documentation and in some - parts of the code. It should be tftp. </p> - <p>Own Id: OTP-8741</p> + <p>Deprecated functions designated to be removed in R14 has been + removed. Also, some new functions has been marked as deprecated + (the old http client api module). </p> + <p>Own Id: OTP-8564</p> + <p>*** POTENTIAL INCOMPATIBILITY ***</p> </item> <item> - <p>[htpc] - Replaced the old http client api module (http) - with the new, http client in the - <seealso marker="http_client">Users Guide</seealso>. </p> - <p>Own Id: OTP-8742</p> - <p>Ryan Zezeski</p> + <p>[httpd] - Improved mod_alias. + Now able to do better URL rewrites. </p> + <p>See + <seealso marker="httpd#alias_prop">URL aliasing properties</seealso> + and the + <seealso marker="httpd#cgi_prop">CGI properties</seealso> + section(s) for more info, </p> + <p>Own Id: OTP-8573</p> + </item> + + </list> + </section> + + <section><title>Fixed Bugs and Malfunctions</title> + + <p>-</p> + +<!-- + <list> + <item> + <p>[httpd] The server did not fully support the documented module + callback api. Specifically, the load function should be able to + return the atom <c>ok</c>, but this was not accepted. </p> + <p>Own Id: OTP-8359</p> </item> </list> +--> + </section> - </section> <!-- 5.3.4 --> + </section> <!-- 5.4 --> <section><title>Inets 5.3.3</title> @@ -408,6 +760,7 @@ <p>Own Id: OTP-8016</p> <p>*** POTENTIAL INCOMPATIBILITY ***</p> </item> + </list> </section> diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index 6480bad758..151bec375e 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -123,7 +123,7 @@ re-receive of acknowledgments. If multiple copies of the same acknowledgments is received the spurious ones are silently ignored. This fix was intended for inets-4.7.14 - but accidentaly it was not included in that release.</p> + but accidentally it was not included in that release.</p> <p>Own Id: OTP-6706 Aux Id: OTP-6691 </p> </item> </list> diff --git a/lib/inets/examples/Makefile b/lib/inets/examples/Makefile index a42b0e38b6..775c449062 100644 --- a/lib/inets/examples/Makefile +++ b/lib/inets/examples/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1997-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 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/. -# +# # 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. -# +# # %CopyrightEnd% # # @@ -21,189 +21,15 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- -# Application version -# ---------------------------------------------------- -include ../vsn.mk -VSN=$(INETS_VSN) - -# ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) - -# ---------------------------------------------------- -# Target Specs +# Common Macros # ---------------------------------------------------- -MODULE= -AUTH_FILES = server_root/auth/group \ - server_root/auth/passwd -CGI_FILES = server_root/cgi-bin/printenv.sh -CONF_FILES = server_root/conf/8080.conf \ - server_root/conf/8888.conf \ - server_root/conf/httpd.conf \ - server_root/conf/ssl.conf \ - server_root/conf/mime.types -OPEN_FILES = server_root/htdocs/open/dummy.html -MNESIA_OPEN_FILES = server_root/htdocs/mnesia_open/dummy.html -MISC_FILES = server_root/htdocs/misc/friedrich.html \ - server_root/htdocs/misc/oech.html -SECRET_FILES = server_root/htdocs/secret/dummy.html -MNESIA_SECRET_FILES = server_root/htdocs/mnesia_secret/dummy.html -HTDOCS_FILES = server_root/htdocs/index.html \ - server_root/htdocs/config.shtml \ - server_root/htdocs/echo.shtml \ - server_root/htdocs/exec.shtml \ - server_root/htdocs/flastmod.shtml \ - server_root/htdocs/fsize.shtml \ - server_root/htdocs/include.shtml -ICON_FILES = server_root/icons/README \ - server_root/icons/a.gif \ - server_root/icons/alert.black.gif \ - server_root/icons/alert.red.gif \ - server_root/icons/apache_pb.gif \ - server_root/icons/back.gif \ - server_root/icons/ball.gray.gif \ - server_root/icons/ball.red.gif \ - server_root/icons/binary.gif \ - server_root/icons/binhex.gif \ - server_root/icons/blank.gif \ - server_root/icons/bomb.gif \ - server_root/icons/box1.gif \ - server_root/icons/box2.gif \ - server_root/icons/broken.gif \ - server_root/icons/burst.gif \ - server_root/icons/button1.gif \ - server_root/icons/button10.gif \ - server_root/icons/button2.gif \ - server_root/icons/button3.gif \ - server_root/icons/button4.gif \ - server_root/icons/button5.gif \ - server_root/icons/button6.gif \ - server_root/icons/button7.gif \ - server_root/icons/button8.gif \ - server_root/icons/button9.gif \ - server_root/icons/buttonl.gif \ - server_root/icons/buttonr.gif \ - server_root/icons/c.gif \ - server_root/icons/comp.blue.gif \ - server_root/icons/comp.gray.gif \ - server_root/icons/compressed.gif \ - server_root/icons/continued.gif \ - server_root/icons/dir.gif \ - server_root/icons/down.gif \ - server_root/icons/dvi.gif \ - server_root/icons/f.gif \ - server_root/icons/folder.gif \ - server_root/icons/folder.open.gif \ - server_root/icons/folder.sec.gif \ - server_root/icons/forward.gif \ - server_root/icons/generic.gif \ - server_root/icons/generic.red.gif \ - server_root/icons/generic.sec.gif \ - server_root/icons/hand.right.gif \ - server_root/icons/hand.up.gif \ - server_root/icons/htdig.gif \ - server_root/icons/icon.sheet.gif \ - server_root/icons/image1.gif \ - server_root/icons/image2.gif \ - server_root/icons/image3.gif \ - server_root/icons/index.gif \ - server_root/icons/layout.gif \ - server_root/icons/left.gif \ - server_root/icons/link.gif \ - server_root/icons/movie.gif \ - server_root/icons/p.gif \ - server_root/icons/patch.gif \ - server_root/icons/pdf.gif \ - server_root/icons/pie0.gif \ - server_root/icons/pie1.gif \ - server_root/icons/pie2.gif \ - server_root/icons/pie3.gif \ - server_root/icons/pie4.gif \ - server_root/icons/pie5.gif \ - server_root/icons/pie6.gif \ - server_root/icons/pie7.gif \ - server_root/icons/pie8.gif \ - server_root/icons/portal.gif \ - server_root/icons/poweredby.gif \ - server_root/icons/ps.gif \ - server_root/icons/quill.gif \ - server_root/icons/right.gif \ - server_root/icons/screw1.gif \ - server_root/icons/screw2.gif \ - server_root/icons/script.gif \ - server_root/icons/sound1.gif \ - server_root/icons/sound2.gif \ - server_root/icons/sphere1.gif \ - server_root/icons/sphere2.gif \ - server_root/icons/star.gif \ - server_root/icons/star_blank.gif \ - server_root/icons/tar.gif \ - server_root/icons/tex.gif \ - server_root/icons/text.gif \ - server_root/icons/transfer.gif \ - server_root/icons/unknown.gif \ - server_root/icons/up.gif \ - server_root/icons/uu.gif \ - server_root/icons/uuencoded.gif \ - server_root/icons/world1.gif \ - server_root/icons/world2.gif +include subdirs.mk -SSL_FILES = server_root/ssl/ssl_client.pem \ - server_root/ssl/ssl_server.pem +SPECIAL_TARGETS = # ---------------------------------------------------- -# FLAGS +# Default Subdir Targets # ---------------------------------------------------- -ERL_COMPILE_FLAGS += - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- - -debug opt: - -clean: - -docs: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DATA) $(OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc - $(INSTALL_DATA) $(MISC_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/misc - $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret - $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret - $(INSTALL_DATA) $(SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret - $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs - -release_docs_spec: +include $(ERL_TOP)/make/otp_subdir.mk diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile new file mode 100644 index 0000000000..1cc61ad8ae --- /dev/null +++ b/lib/inets/examples/httpd_load_test/Makefile @@ -0,0 +1,123 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 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/. +# +# 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. +# +# %CopyrightEnd% + +include $(ERL_TOP)/make/target.mk + +EBIN = . + +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk + +VSN=$(INETS_VSN) + + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) +EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +HDLT_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/httpd_load_test + + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +include modules.mk + +ERL_FILES = $(MODULES:%=%.erl) + +SOURCE = $(ERL_FILES) $(INTERNAL_HRL_FILES) + +TARGET_FILES = \ + $(ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR)) + +ifeq ($(TYPE),debug) +ERL_COMPILE_FLAGS += -Ddebug -W +endif + + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +include ../../src/inets_app/inets.mk + +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) \ + -I../../include + + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +debug: + @${MAKE} TYPE=debug opt + +opt: $(TARGET_FILES) + +clean: + rm -f $(TARGET_FILES) + rm -f errs core *~ + +docs: + + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + + +release_spec: opt + $(INSTALL_DIR) $(EXAMPLE_RELSYSDIR) + $(INSTALL_DIR) $(HDLT_RELSYSDIR) + $(INSTALL_DATA) $(SCRIPT_SKELETONS) $(HDLT_RELSYSDIR) + $(INSTALL_DATA) $(CONF_SKELETONS) $(HDLT_RELSYSDIR) + $(INSTALL_DATA) $(CERT_FILES) $(HDLT_RELSYSDIR) + $(INSTALL_DATA) $(TARGET_FILES) $(HDLT_RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(HDLT_RELSYSDIR) + + +release_docs_spec: + + +# ---------------------------------------------------- +# Include dependencies +# ---------------------------------------------------- + +megaco_codec_transform.$(EMULATOR): megaco_codec_transform.erl + +megaco_codec_meas.$(EMULATOR): megaco_codec_meas.erl + +megaco_codec_mstone1.$(EMULATOR): megaco_codec_mstone1.erl + +megaco_codec_mstone2.$(EMULATOR): megaco_codec_mstone2.erl + +megaco_codec_mstone_lib.$(EMULATOR): megaco_codec_mstone_lib.erl + diff --git a/lib/inets/examples/httpd_load_test/hdlt.config.skel b/lib/inets/examples/httpd_load_test/hdlt.config.skel new file mode 100644 index 0000000000..640867ebac --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt.config.skel @@ -0,0 +1,20 @@ +%% Debug: silence | info | log | debug +{debug, [{ctrl, info}, {proxy, silence}, {slave, silence}, {client, silence}]}. +{server, {"/usr/local/bin", "fooserver"}}. +%% {port, 8888}. % integer() > 0 +{server_dir, "/tmp/hdlt"}. % Absolute path +{work_dir, "/tmp/hdlt"}. % Absolute path +{clients, + [ + {"/opt/local/bin", "foo"}, + {"/usr/local/bin", "bar"} + ] +}. +%% {send_rate, 80}. % Max number of outstanding requests, integer() > 0 +%% {test_time, 120}. % Number of seconds, +%% {max_nof_schedulers, 8}. % integer() >= 0 +%% {work_simulator, 10000}. % integer() > 0 +%% {data_size, {100, 500, 2}}. % {integer() > 0, integer() > 0, integer() > 0} +%% {socket_type, ip_comm}. % ip_comm | ssl | essl | ossl +%% {server_cert_file, "hdlt_ssl_server_cert.pem"}. +%% {client_cert_file, "hdlt_ssl_client_cert.pem"}.
\ No newline at end of file diff --git a/lib/inets/examples/httpd_load_test/hdlt.erl b/lib/inets/examples/httpd_load_test/hdlt.erl new file mode 100644 index 0000000000..18d8c34ccf --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt.erl @@ -0,0 +1,74 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: Main API module for the httpd load test utility +%%---------------------------------------------------------------------- + +-module(hdlt). + + +%%----------------------------------------------------------------- +%% Public interface +%%----------------------------------------------------------------- + +-export([start/0, start/1, stop/0, help/0]). + + +%%----------------------------------------------------------------- +%% Start the HDLT utility +%%----------------------------------------------------------------- + +start() -> + ConfigFile = "hdlt.config", + case file:consult(ConfigFile) of + {ok, Config} when is_list(Config) -> + start(Config); + Error -> + Error + end. + +start(Config) -> + Flag = process_flag(trap_exit, true), + Result = + case hdlt_ctrl:start(Config) of + {ok, Pid} -> + receive + {'EXIT', Pid, normal} -> + ok; + {'EXIT', Pid, Reason} -> + io:format("HDLT failed: " + "~n ~p" + "~n", [Reason]), + {error, Reason} + end; + Error -> + Error + end, + process_flag(trap_exit, Flag), + Result. + + + +stop() -> + hdlt_ctrl:stop(). + + +help() -> + hdlt_ctrl:help(). diff --git a/lib/inets/examples/httpd_load_test/hdlt.sh.skel b/lib/inets/examples/httpd_load_test/hdlt.sh.skel new file mode 100644 index 0000000000..a250bad9c5 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt.sh.skel @@ -0,0 +1,44 @@ +#!/bin/sh + +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 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/. +# +# 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. +# +# %CopyrightEnd% + +# Skeleton for a script intended to run the hdlt(N) +# performance test. +# +# This test can be used for several things depending on the +# configuration: SMP or SocketType performance tests +# + +ERL_HOME=<path to otp top dir> +INETS_HOME=$ERL_HOME/lib/erlang/lib/<inets dir> +HDLT_HOME=$INETS_HOME/examples/httpd_load_test +PATH=$ERL_HOME/bin:$PATH + +HDLT="-s hdlt start" +STOP="-s init stop" + +ERL="erl \ + -noshell \ + -pa $HDLT_HOME \ + $HDLT \ + $STOP" + +echo $ERL +$ERL | tee hdlt.log + diff --git a/lib/inets/examples/httpd_load_test/hdlt_client.erl b/lib/inets/examples/httpd_load_test/hdlt_client.erl new file mode 100644 index 0000000000..d65ac5a885 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_client.erl @@ -0,0 +1,370 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: The HDLT client module. +%% This is the traffic generator +%%---------------------------------------------------------------------- + +-module(hdlt_client). + +-export([ + start/1, + stop/0, + start_inets/0, + start_service/1, + release/0, + node_info/0 + ]). + +-export([ + proxy/1 + ]). + +-include("hdlt_logger.hrl"). + +-define(CTRL, hdlt_ctrl). +-define(PROXY, hdlt_proxy). + +-record(state, + { + mode = initial, + send_rate, + time, + stop_time, + url, + nof_reqs = 0, + nof_reps = 0, + last_req, + sizes, + socket_type, + cert_file + }). + + + +start(Debug) -> + proc_lib:start_link(?MODULE, proxy, [Debug]). + +stop() -> + (catch erlang:send(?PROXY, stop)), + ok. + +start_inets() -> + ?PROXY ! start_inets. + +start_service(Args) -> + ?PROXY ! {start_client, Args, self()}, + receive + client_started -> + %% ?LOG("client service started"), + ok + end. + +release() -> + ?PROXY ! release. + +node_info() -> + ?PROXY ! {node_info, self()}, + receive + {node_info, NodeInfo} -> + NodeInfo + end. + + +%% --------------------------------------------------------------------- +%% +%% The proxy process +%% + +proxy(Debug) -> + process_flag(trap_exit, true), + erlang:register(?PROXY, self()), + SName = lists:flatten( + io_lib:format("HDLT PROXY[~p,~p]", [self(), node()])), + ?SET_NAME(SName), + ?SET_LEVEL(Debug), + ?LOG("starting", []), + Ref = await_for_controller(10), + CtrlNode = node(Ref), + erlang:monitor_node(CtrlNode, true), + proc_lib:init_ack({ok, self()}), + ?DEBUG("started", []), + proxy_loop(Ref, CtrlNode, undefined). + +await_for_controller(N) when N > 0 -> + case global:whereis_name(hdlt_ctrl) of + Pid when is_pid(Pid) -> + erlang:monitor(process, Pid); + _ -> + timer:sleep(1000), + await_for_controller(N-1) + end; +await_for_controller(_) -> + proc_lib:init_ack({error, controller_not_found, nodes()}), + timer:sleep(500), + init:stop(). + + +proxy_loop(Ref, CtrlNode, Client) -> + ?DEBUG("await command", []), + receive + stop -> + ?LOG("stop", []), + timer:sleep(1000), + halt(); + + start_inets -> + ?LOG("start the inets service framework", []), + %% inets:enable_trace(max, "/tmp/inets-httpc-trace.log", all), + case (catch inets:start()) of + ok -> + ?LOG("framework started", []), + proxy_loop(Ref, CtrlNode, Client); + Error -> + ?LOG("failed starting inets service framework: " + "~n Error: ~p", [Error]), + timer:sleep(1000), + halt() + end; + + {start_client, Args, From} -> + ?LOG("start client with" + "~n Args: ~p", [Args]), + Client2 = spawn_link(fun() -> client(Args) end), + From ! client_started, + proxy_loop(Ref, CtrlNode, Client2); + + release -> + ?LOG("release", []), + Client ! go, + proxy_loop(Ref, CtrlNode, Client); + + {node_info, Pid} -> + ?LOG("received requets for node info", []), + NodeInfo = get_node_info(), + Pid ! {node_info, NodeInfo}, + proxy_loop(Ref, CtrlNode, Client); + + {'EXIT', Client, normal} -> + ?LOG("received normal exit message from client (~p)", + [Client]), + exit(normal); + + {'EXIT', Client, Reason} -> + ?INFO("received exit message from client (~p)" + "~n Reason: ~p", [Client, Reason]), + %% Unexpected client termination, inform the controller and die + global:send(hdlt_ctrl, {client_exit, Client, node(), Reason}), + exit({client_exit, Reason}); + + {nodedown, CtrlNode} -> + ?LOG("received nodedown for controller node - terminate", []), + halt(); + + {'DOWN', Ref, process, _, _} -> + ?INFO("received DOWN message for controller - terminate", []), + %% The controller has terminated, dont care why, time to die + halt() + + end. + + + +%% --------------------------------------------------------------------- +%% +%% The client process +%% + +client([SocketType, CertFile, URLBase, Sizes, Time, SendRate, Debug]) -> + SName = lists:flatten( + io_lib:format("HDLT CLIENT[~p,~p]", [self(), node()])), + ?SET_NAME(SName), + ?SET_LEVEL(Debug), + ?LOG("starting with" + "~n SocketType: ~p" + "~n Time: ~p" + "~n SendRate: ~p", [SocketType, Time, SendRate]), + httpc:set_options([{max_pipeline_length, 0}]), + if + (SocketType =:= ssl) orelse + (SocketType =:= ossl) orelse + (SocketType =:= essl) -> + %% Ensure crypto and ssl started: + crypto:start(), + ssl:start(); + true -> + ok + end, + State = #state{mode = idle, + url = URLBase, + time = Time, + send_rate = SendRate, + sizes = Sizes, + socket_type = SocketType, + cert_file = CertFile}, + ?DEBUG("started", []), + client_loop(State). + +%% The point is to first start all client nodes and then this +%% process. Then, when they are all started, the go-ahead, go, +%% message is sent to let them lose at the same time. +client_loop(#state{mode = idle, + time = Time, + send_rate = SendRate} = State) -> + ?DEBUG("[idle] awaiting the go command", []), + receive + go -> + ?LOG("[idle] received go", []), + erlang:send_after(Time, self(), stop), + NewState = send_requests(State, SendRate), + client_loop(NewState#state{mode = generating, + nof_reqs = SendRate}) + end; + +%% In this mode the client is generating traffic. +%% It will continue to do so until the stop message +%% is received. +client_loop(#state{mode = generating} = State) -> + receive + stop -> + ?LOG("[generating] received stop", []), + StopTime = timestamp(), + req_reply(State), + client_loop(State#state{mode = stopping, stop_time = StopTime}); + + {http, {_, {{_, 200, _}, _, _}}} -> + %% ?DEBUG("[generating] received reply - send another request", []), + NewState = send_requests(State, 1), + client_loop(NewState#state{nof_reps = NewState#state.nof_reps + 1, + nof_reqs = NewState#state.nof_reqs + 1}); + + {http, {ReqId, {error, Reason}}} -> + ?INFO("[generating] request ~p failed: " + "~n Reason: ~p" + "~n NofReqs: ~p" + "~n NofReps: ~p", + [ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]), + exit({Reason, generating, State#state.nof_reqs, State#state.nof_reps}); + + Else -> + ?LOG("[generating] received unexpected message: " + "~n~p", [Else]), + unexpected_data(Else), + client_loop(State) + end; + +%% The client no longer issues any new requests, instead it +%% waits for replies for all the oustanding requests to +%% arrive. +client_loop(#state{mode = stopping, + time = Time, + last_req = LastReqId} = State) -> + receive + {http, {LastReqId, {{_, 200, _}, _, _}}} -> + ?DEBUG("[stopping] received reply for last request (~p)", [LastReqId]), + time_to_complete(State), + ok; + + {http, {ReqId, {{_, 200, _}, _, _}}} -> + ?DEBUG("[stopping] received reply ~p", [ReqId]), + client_loop(State); + + {http, {ReqId, {error, Reason}}} -> + ?INFO("[stopping] request ~p failed: " + "~n Reason: ~p" + "~n NofReqs: ~p" + "~n NofReps: ~p", + [ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]), + exit({Reason, stopping, State#state.nof_reqs, State#state.nof_reps}); + + Else -> + ?LOG("[stopping] received unexpected message: " + "~n~p", [Else]), + unexpected_data(Else), + client_loop(State) + + after Time -> + ?INFO("timeout when" + "~n Number of requests: ~p" + "~n Number of replies: ~p", + [State#state.nof_reqs, State#state.nof_reps]), + exit({timeout, State#state.nof_reqs, State#state.nof_reps}) + end. + +req_reply(#state{nof_reqs = NofReqs, nof_reps = NofReps}) -> + load_data({req_reply, node(), NofReqs, NofReps}). + +time_to_complete(#state{stop_time = StopTime}) -> + StoppedTime = os:timestamp(), + load_data({time_to_complete, node(), StopTime, StoppedTime}). + +load_data(Data) -> + global:send(?CTRL, {load_data, Data}). + +unexpected_data(Else) -> + global:send(?CTRL, {unexpected_data, Else}). + + +send_requests(#state{sizes = Sizes} = State, N) -> + send_requests(State, N, Sizes). + +send_requests(State, 0, Sizes) -> + State#state{sizes = Sizes}; +send_requests(#state{socket_type = SocketType, + cert_file = CertFile} = State, N, [Sz | Sizes]) -> + URL = lists:flatten(io_lib:format("~s~w", [State#state.url, Sz])), + Method = get, + Request = {URL, []}, + HTTPOptions = + case SocketType of + ip_comm -> + []; + _ -> + SslOpts = [{verify, 0}, + {certfile, CertFile}, + {keyfile, CertFile}], + case SocketType of + ssl -> + [{ssl, SslOpts}]; + ossl -> + [{ssl, {ossl, SslOpts}}]; + essl -> + [{ssl, {essl, SslOpts}}] + end + end, + Options = [{sync, false}], + {ok, Ref} = httpc:request(Method, Request, HTTPOptions, Options), + send_requests(State#state{last_req = Ref}, N-1, lists:append(Sizes, [Sz])). + + +timestamp() -> + os:timestamp(). + + +get_node_info() -> + [{cpu_topology, erlang:system_info(cpu_topology)}, + {heap_type, erlang:system_info(heap_type)}, + {nof_schedulers, erlang:system_info(schedulers)}, + {otp_release, erlang:system_info(otp_release)}, + {version, erlang:system_info(version)}, + {system_version, erlang:system_info(system_version)}, + {system_architecture, erlang:system_info(system_architecture)}]. + + diff --git a/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl new file mode 100644 index 0000000000..950d2632f7 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl @@ -0,0 +1,1530 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: The httpd load test (hdlt) controller/collector module, +%% This module contains all the code of the httpd load test +%% controller/collector. It sets up the test, starts all +%% server and client nodes and applications and finally +%% collects test data. +%%---------------------------------------------------------------------- + +-module(hdlt_ctrl). + +-export([start/1, stop/0, help/0]). + +-export([init/1, proxy/7]). + +-include_lib("kernel/include/file.hrl"). +-include("hdlt_logger.hrl"). + +-define(DEFAULT_SENDRATE, 89). +-define(DEFAULT_TEST_TIME, 120). % 2 minutes +-define(DEFAULT_PORT, 8889). +-define(TIMEOUT, 60000). +-define(DEFAULT_MAX_NOF_SCHEDULERS, 8). +-define(DEFAULT_SERVER_DIR, "/tmp/hdlt"). +-define(DEFAULT_WORK_DIR, "/tmp/hdlt"). +-define(SSH_PORT, 22). +-define(DEFAULT_SOCKET_TYPE, ip_comm). +-define(DEFAULT_SERVER_CERT, "hdlt_ssl_server_cert.pem"). +-define(DEFAULT_CLIENT_CERT, "hdlt_ssl_client_cert.pem"). +-define(SSH_CONNECT_TIMEOUT, 5000). +-define(NODE_START_TIMEOUT, 5000). +-define(LOCAL_PROXY_START_TIMEOUT, ?NODE_START_TIMEOUT * 4). +-define(DEFAULT_DEBUGS, + [{ctrl, info}, {slave, silence}, {proxy, silence}, {client, silence}]). +-define(DEFAULT_WORK_SIM, 10000). +-define(DEFAULT_DATA_SIZE_START, 500). +-define(DEFAULT_DATA_SIZE_END, 1500). +-define(DEFAULT_DATA_SIZE_INCR, 1). +-define(DEFAULT_DATA_SIZE, {?DEFAULT_DATA_SIZE_START, + ?DEFAULT_DATA_SIZE_END, + ?DEFAULT_DATA_SIZE_INCR}). + + +%% hdlt = httpd load test + +-define(COLLECTOR, hdlt_ctrl). +-define(RESULTS_TAB, hdlt_results). + +-define(CLIENT_MOD, hdlt_client). +-define(CLIENT_NODE_NAME, ?CLIENT_MOD). + +-define(SERVER_MOD, hdlt_server). +-define(SERVER_NODE_NAME, ?SERVER_MOD). + +-define(LOGGER, hdlt_logger). + + +-record(state, + { + url, + test_time, + send_rate, + http_server, + http_port, + results = ?RESULTS_TAB, + nodes, + server_root, + doc_root, + server_dir, + work_dir, + server_conn, + client_conns = [], + client_mod = ?CLIENT_MOD, + clients, + nof_schedulers = 0, + max_nof_schedulers, + socket_type, + server_cert_file, + client_cert_file, + debugs, + client_sz_from, + client_sz_to, + client_sz_incr + } + ). + +-record(proxy, + { + mode, + mod, + connection, + channel, + host, + cmd, + node_name, + node, + ref, + erl_path, + paths, + args + }). + +-record(connection, + { + proxy, + node, + node_name, + host + }). + + +-record(client, {host, path, version}). +-record(server, {host, path, version}). + + +start(Config) when is_list(Config) -> + proc_lib:start_link(?MODULE, init, [Config]). + +stop() -> + global:send(?COLLECTOR, stop). + +init(Config) -> + %% io:format("Config: ~n~p~n", [Config]), + case (catch do_init(Config)) of + {ok, State} -> + proc_lib:init_ack({ok, self()}), + loop(State); + {error, _Reason} = Error -> + proc_lib:init_ack(Error), + ok; + {'EXIT', Reason} -> + proc_lib:init_ack({error, Reason}), + ok + end. + +do_init(Config) -> + %% Do not trap exit, but register ourself + global:register_name(?COLLECTOR, self()), + + State = #state{}, + ets:new(State#state.results, [bag, named_table]), + + hdlt_logger:start(), + global:sync(), + + %% Maybe enable debug + Debugs = get_debugs(Config), + ?SET_NAME("HDLT CTRL"), + set_debug_level(Debugs), + + ?DEBUG("network info: " + "~n Global names: ~p" + "~n Nodes: ~p", [global:registered_names(), nodes()]), + + %% Read config + ?LOG("read config", []), + SendRate = get_send_rate(Config), + Clients = get_clients(Config), + TestTime = get_test_time(Config), + Server = get_server(Config), + Port = get_port(Config), + ServerDir = get_server_dir(Config), + WorkingDir = get_work_dir(Config), + MaxNofSchedulers = get_max_nof_schedulers(Config), + SocketType = get_socket_type(Config), + ServerCertFile = get_server_cert_file(Config), + ClientCertFile = get_client_cert_file(Config), + WorkSim = get_work_sim(Config), + {From, To, Incr} = get_data_size(Config), + + URL = url(Server, Port, SocketType, WorkSim), + ServerRoot = filename:join(ServerDir, "server_root"), + DocRoot = ServerRoot, %% Not really used in this test + + ?DEBUG("randomize setup", []), + randomized_sizes_init(), + + %% Start used applications + ?DEBUG("ensure crypto started", []), + crypto:start(), + ?DEBUG("ensure ssh started", []), + ssh:start(), + + State2 = State#state{server_root = ServerRoot, + doc_root = DocRoot, + server_dir = ServerDir, + work_dir = WorkingDir, + max_nof_schedulers = MaxNofSchedulers, + socket_type = SocketType, + server_cert_file = ServerCertFile, + client_cert_file = ClientCertFile, + http_server = Server, + http_port = Port, + url = URL, + test_time = TestTime, + send_rate = SendRate, + clients = Clients, + debugs = Debugs, + client_sz_from = From, + client_sz_to = To, + client_sz_incr = Incr}, + + ?LOG("prepare server host", []), + prepare_server_host(State2), + + ?LOG("prepare client hosts", []), + State3 = prepare_client_hosts(State2), + + ?LOG("basic init done", []), + {ok, State3}. + + +loop(#state{nof_schedulers = N, max_nof_schedulers = M} = State) when N > M -> + + ?INFO("Starting to analyse data", []), + + AnalysedTab = analyse_data(State), + + Files = save_results_to_file(AnalysedTab, State), + io:format("~n******************************************************" + "~n~nResult(s) saved to: ~n~p~n", [Files]), + clean_up(State); + +loop(#state{url = URL, + test_time = TestTime, + send_rate = SendRate, + nof_schedulers = NofSchedulers} = State) -> + + {StartH, StartM, StartS} = erlang:time(), + + ?INFO("Performing test with ~p smp-scheduler(s): ~n" + " It will take a minimum of: ~p seconds. ~n" + " Start time: ~.2.0w:~.2.0w:~.2.0w", + [NofSchedulers, round(TestTime/1000), StartH, StartM, StartS]), + + %% Start the server node + %% (The local proxy, the node, the remote proxy, and the inets framework) + State1 = start_server_node(State), + ?DEBUG("nodes after server start: ~p", [nodes() -- [node()]]), + + %% Start the client node(s) + %% (The local proxy, the node, the remote proxy, and the inets framework) + ?LOG("start client node(s)", []), + State2 = start_client_nodes(State1), + ?DEBUG("nodes after client(s) start: ~p", [nodes() -- [node()]]), + + ?LOG("start server", []), + start_server(State2), + + ?LOG("start clients", []), + start_clients(State2, URL, TestTime, SendRate), + + ?LOG("release clients", []), + release_clients(State2), + + ?LOG("collect data", []), + collect_data(State2), + + ?LOG("stop all nodes", []), + State3 = stop_nodes(State2), + + ?INFO("Test with ~p smp-scheduler(s) complete" + "~n~n" + "****************************************************************" + "~n", + [NofSchedulers]), + loop(State3#state{nof_schedulers = NofSchedulers + 1}). + + +prepare_server_host(#state{server_root = ServerRoot, + http_server = #server{host = Host}, + socket_type = SocketType, + server_cert_file = CertFile}) -> + ?INFO("prepare server host ~s", [Host]), + Opts = [{user_interaction, false}, + {silently_accept_hosts, true}, + {timeout, 2*?SSH_CONNECT_TIMEOUT}, + {connect_timeout, ?SSH_CONNECT_TIMEOUT}], + case ssh_sftp:start_channel(Host, Opts) of + {ok, Sftp, ConnectionRef} -> + ?DEBUG("sftp connection established - now transer server content", + []), + create_server_content(Sftp, ServerRoot, SocketType, CertFile), + ?DEBUG("server content transfered - now close ssh connection ", + []), + ssh:close(ConnectionRef), + ?DEBUG("server preparation complete ", []), + ok; + Error -> + ?INFO("FAILED creating sftp channel to server host ~s: " + "~n ~p", [Host, Error]), + exit({failed_establishing_sftp_connection, Error}) + end. + +create_server_content(Sftp, ServerRoot, SocketType, CertFile) -> + %% Create server root + ?DEBUG("ensure existence of ~p", [ServerRoot]), + ensure_remote_dir_exist(Sftp, ServerRoot), + + %% Create the server ebin dir (for the starter module) + EBIN = filename:join(ServerRoot, "ebin"), + ?DEBUG("make ebin dir: ~p", [EBIN]), + maybe_create_remote_dir(Sftp, EBIN), + + %% Create the server ebin dir (for the starter module) + LOG = filename:join(ServerRoot, "log"), + ?DEBUG("make log dir: ~p", [LOG]), + maybe_create_remote_dir(Sftp, LOG), + + LocalServerMod = local_server_module(), + ?DEBUG("copy server stub/proxy module ~s", [LocalServerMod]), + RemoteServerMod = remote_server_module(EBIN), + {ok, ServerModBin} = file:read_file(LocalServerMod), + ok = ssh_sftp:write_file(Sftp, RemoteServerMod, ServerModBin), + + LocalSlaveMod = local_slave_module(), + ?DEBUG("copy slave module ~s", [LocalSlaveMod]), + RemoteSlaveMod = remote_slave_module(EBIN), + {ok, SlaveModBin} = file:read_file(LocalSlaveMod), + ok = ssh_sftp:write_file(Sftp, RemoteSlaveMod, SlaveModBin), + + LocalLoggerMod = local_logger_module(), + ?DEBUG("copy logger module ~s", [LocalLoggerMod]), + RemoteLoggerMod = remote_logger_module(EBIN), + {ok, LoggerModBin} = file:read_file(LocalLoggerMod), + ok = ssh_sftp:write_file(Sftp, RemoteLoggerMod, LoggerModBin), + + %% Create the inets server data dir + CGI = filename:join(ServerRoot, "cgi-bin"), + ?DEBUG("make cgi dir: ~p", [CGI]), + maybe_create_remote_dir(Sftp, CGI), + + LocalRandomMod = local_random_html_module(), + ?DEBUG("copy random-html module ~s", [LocalRandomMod]), + RemoteRandomMod = remote_random_html_module(EBIN), + {ok, RandomModBin} = file:read_file(LocalRandomMod), + ok = ssh_sftp:write_file(Sftp, RemoteRandomMod, RandomModBin), + + case SocketType of + ip_comm -> + ok; + _ -> + SSLDir = filename:join(ServerRoot, "ssl"), + ?DEBUG("make conf dir: ~p", [SSLDir]), + maybe_create_remote_dir(Sftp, SSLDir), + ?DEBUG("copy ssl cert file ~s", [CertFile]), + {ok, CertBin} = file:read_file(CertFile), + RemoteCertFile = filename:join(SSLDir, + filename:basename(CertFile)), + ok = ssh_sftp:write_file(Sftp, RemoteCertFile, CertBin), + ok + end, + + ?DEBUG("done", []), + ok. + +remote_server_module(Path) -> + Mod = server_module(), + filename:join(Path, Mod). + +local_server_module() -> + Mod = server_module(), + case code:where_is_file(Mod) of + Path when is_list(Path) -> + Path; + _ -> + exit({server_module_not_found, Mod}) + end. + +server_module() -> + module(?SERVER_MOD). + + +prepare_client_hosts(#state{work_dir = WorkDir, + clients = Clients, + socket_type = SocketType, + client_cert_file = CertFile} = State) -> + Clients2 = + prepare_client_hosts(WorkDir, SocketType, CertFile, Clients, []), + State#state{clients = Clients2}. + +prepare_client_hosts(_WorkDir, _SocketType, _CertFile, [], Acc) -> + lists:reverse(Acc); +prepare_client_hosts(WorkDir, SocketType, CertFile, [Client|Clients], Acc) -> + case prepare_client_host(WorkDir, SocketType, CertFile, Client) of + ok -> + prepare_client_hosts(WorkDir, SocketType, CertFile, Clients, + [Client|Acc]); + _ -> + prepare_client_hosts(WorkDir, SocketType, CertFile, Clients, Acc) + end. + +prepare_client_host(WorkDir, SocketType, CertFile, #client{host = Host}) -> + ?INFO("prepare client host ~s", [Host]), + Opts = [{user_interaction, false}, + {silently_accept_hosts, true}, + {timeout, 2*?SSH_CONNECT_TIMEOUT}, + {connect_timeout, ?SSH_CONNECT_TIMEOUT}], + case ssh_sftp:start_channel(Host, Opts) of + {ok, Sftp, ConnectionRef} -> + ?DEBUG("sftp connection established - now transer client content", + []), + create_client_content(Sftp, WorkDir, SocketType, CertFile), + ?DEBUG("client content transered - now close ssh connection ", []), + ssh:close(ConnectionRef), + ?DEBUG("client preparation complete ", []), + ok; + Error -> + ?INFO("FAILED creating sftp channel to client host ~s: skipping" + "~n ~p", [Host, Error]), + Error + end. + +create_client_content(Sftp, WorkDir, SocketType, CertFile) -> + %% Create work dir + ?DEBUG("ensure existence of ~p", [WorkDir]), + ensure_remote_dir_exist(Sftp, WorkDir), + + %% Create the client ebin dir + EBIN = filename:join(WorkDir, "ebin"), + RemoteClientMod = remote_client_module(EBIN), + ?DEBUG("make ebin dir: ~p", [EBIN]), + maybe_create_remote_dir(Sftp, EBIN), + + LocalClientMod = local_client_module(), + ?DEBUG("copy client stub/proxy module ~s", [LocalClientMod]), + {ok, ClientModBin} = file:read_file(LocalClientMod), + ok = ssh_sftp:write_file(Sftp, RemoteClientMod, ClientModBin), + + LocalSlaveMod = local_slave_module(), + ?DEBUG("copy slave module ~s", [LocalSlaveMod]), + RemoteSlaveMod = remote_slave_module(EBIN), + {ok, SlaveModBin} = file:read_file(LocalSlaveMod), + ok = ssh_sftp:write_file(Sftp, RemoteSlaveMod, SlaveModBin), + + LocalLoggerMod = local_logger_module(), + ?DEBUG("copy logger module ~s", [LocalLoggerMod]), + RemoteLoggerMod = remote_logger_module(EBIN), + {ok, LoggerModBin} = file:read_file(LocalLoggerMod), + ok = ssh_sftp:write_file(Sftp, RemoteLoggerMod, LoggerModBin), + + case SocketType of + ip_comm -> + ok; + _ -> + %% We should really store the remote path somewhere as + %% we use it when starting the client service... + SSLDir = filename:join(WorkDir, "ssl"), + ?DEBUG("make ssl dir: ~p", [SSLDir]), + maybe_create_remote_dir(Sftp, SSLDir), + ?DEBUG("copy ssl cert file ~s", [CertFile]), + {ok, CertBin} = file:read_file(CertFile), + RemoteCertFile = filename:join(SSLDir, + filename:basename(CertFile)), + ok = ssh_sftp:write_file(Sftp, RemoteCertFile, CertBin), + ok + end, + + ?DEBUG("done", []), + ok. + +remote_client_module(Path) -> + Mod = client_module(), + filename:join(Path, Mod). + +local_client_module() -> + Mod = client_module(), + case code:where_is_file(Mod) of + Path when is_list(Path) -> + Path; + _ -> + exit({client_module_not_found, Mod}) + end. + +client_module() -> + module(?CLIENT_MOD). + + +remote_slave_module(Path) -> + Mod = slave_module(), + filename:join(Path, Mod). + +local_slave_module() -> + Mod = slave_module(), + case code:where_is_file(Mod) of + Path when is_list(Path) -> + Path; + _ -> + exit({slave_module_not_found, Mod}) + end. + +slave_module() -> + module(hdlt_slave). + + +remote_logger_module(Path) -> + Mod = logger_module(), + filename:join(Path, Mod). + +local_logger_module() -> + Mod = logger_module(), + case code:where_is_file(Mod) of + Path when is_list(Path) -> + Path; + _ -> + exit({logger_module_not_found, Mod}) + end. + +logger_module() -> + module(hdlt_logger). + + +remote_random_html_module(Path) -> + Mod = random_html_module(), + filename:join(Path, Mod). + +local_random_html_module() -> + Mod = random_html_module(), + case code:where_is_file(Mod) of + Path when is_list(Path) -> + Path; + _ -> + exit({random_module_not_found, Mod}) + end. + +random_html_module() -> + module(hdlt_random_html). + + +module(Mod) -> + Ext = string:to_lower(erlang:system_info(machine)), + lists:flatten(io_lib:format("~w.~s", [Mod, Ext])). + + +%% ----------------------------------------------------------------------- +%% - For every node created (server and client both) there is both +%% a local and remote proxy. +%% - The local proxy is running on the local (controller/collector) node. +%% - The remote proxy is running on the client or server node(s). +%% - The local (ctrl) proxy monitor the remote (server/client) proxy. +%% - The remote (server/client) proxy monitor the local (ctrl) proxy. +%% + +start_client_nodes(#state{clients = Clients, + work_dir = WorkDir, + debugs = Debugs} = State) -> + Connections = + [start_client_node(Client, WorkDir, Debugs) || Client <- Clients], + State#state{client_conns = Connections}. + +start_client_node(#client{path = ErlPath, host = Host}, WorkDir, Debugs) -> + ?INFO("start client on host ~p", [Host]), + EbinDir = filename:join(WorkDir, "ebin"), + start_client_node(Host, ErlPath, [EbinDir], Debugs). + +start_client_node(Host, ErlPath, Paths, Debugs) -> + start_node(Host, ?CLIENT_NODE_NAME, + ErlPath, Paths, [], ?CLIENT_MOD, Debugs). + + +start_server_node(#state{http_server = #server{path = ErlPath, host = Host}, + server_root = ServerRoot, + nof_schedulers = NofScheds, + debugs = Debugs} = State) -> + ?INFO("start server on host ~p", [Host]), + CgiBinDir = filename:join(ServerRoot, "cgi-bin"), + EbinDir = filename:join(ServerRoot, "ebin"), + Connection = + start_server_node(Host, ErlPath, [CgiBinDir, EbinDir], + Debugs, NofScheds), + State#state{server_conn = Connection}. + +start_server_node(Host, ErlPath, Paths, Debugs, NofScheds) -> + Args = + if + NofScheds =:= 0 -> + "-smp disable"; + true -> + lists:flatten(io_lib:format("-smp +S ~w", [NofScheds])) + end, + start_node(Host, ?SERVER_NODE_NAME, + ErlPath, Paths, Args, ?SERVER_MOD, Debugs). + + +%% ----------------------------------------------------------------------- +%% - For every node created (server and client both) there is both +%% a local and remote proxy. +%% - The local proxy is running on the local (controller/collector) node. +%% - The remote proxy is running on the client or server node(s). +%% - The local (ctrl) proxy monitor the remote (server/client) proxy. +%% - The remote (server/client) proxy monitor the local (ctrl) proxy. +%% + +start_node(Host, NodeName, ErlPath, Paths, Args, Module, Debugs) -> + %% Start the (local) proxy + ?DEBUG("start_node -> start local proxy and remote node", []), + ProxyDebug = proplists:get_value(proxy, Debugs, silence), + Proxy = proxy_start(Host, NodeName, ErlPath, Paths, Args, Module, + ProxyDebug), + + ?DEBUG("start_node -> local proxy started - now start node", []), + SlaveDebug = proplists:get_value(slave, Debugs, silence), + Node = proxy_start_node(Proxy, SlaveDebug), + + ?DEBUG("start_node -> sync global", []), + global:sync(), + + ?DEBUG("start_node -> start remote proxy", []), + proxy_start_remote(Proxy), + + ?DEBUG("start_node -> start (remote) inets framework", []), + proxy_start_inets(Proxy), + + ?DEBUG("start_node -> done", []), + #connection{proxy = Proxy, node = Node, node_name = NodeName, host = Host}. + + +proxy_start(Host, NodeName, ErlPath, Paths, Args, Module, Debug) -> + ?LOG("try starting local proxy for ~p@~s", [NodeName, Host]), + ProxyArgs = [Host, NodeName, ErlPath, Paths, Args, Module, Debug], + case proc_lib:start_link(?MODULE, proxy, + ProxyArgs, ?LOCAL_PROXY_START_TIMEOUT) of + {ok, Proxy} -> + Proxy; + Error -> + exit({failed_starting_proxy, Error}) + end. + +proxy_start_node(Proxy, Debug) -> + {ok, Node} = proxy_request(Proxy, {start_node, Debug}), + Node. + +proxy_start_remote(Proxy) -> + proxy_request(Proxy, start_remote_proxy). + +proxy_start_inets(Proxy) -> + proxy_request(Proxy, start_inets). + +proxy_start_service(Proxy, Args) -> + proxy_request(Proxy, {start_service, Args}). + +proxy_release(Proxy) -> + proxy_request(Proxy, release). + +proxy_stop(Proxy) -> + StopResult = proxy_request(Proxy, stop), + ?DEBUG("proxy stop result: ~p", [StopResult]), + StopResult. + +proxy_request(Proxy, Req) -> + Ref = make_ref(), + Proxy ! {proxy_request, Ref, self(), Req}, + receive + {proxy_reply, Ref, Proxy, Rep} -> + Rep + end. + +proxy_reply(From, Ref, Rep) -> + From ! {proxy_reply, Ref, self(), Rep}. + +proxy(Host, NodeName, ErlPath, Paths, Args, Module, Debug) -> + process_flag(trap_exit, true), + SName = lists:flatten( + io_lib:format("HDLT CTRL PROXY[~p,~s,~w]", + [self(), Host, NodeName])), + ?SET_NAME(SName), + ?SET_LEVEL(Debug), + ?LOG("starting with" + "~n Host: ~p" + "~n NodeName: ~p" + "~n ErlPath: ~p" + "~n Paths: ~p" + "~n Args: ~p" + "~n Module: ~p", [Host, NodeName, ErlPath, Paths, Args, Module]), + State = #proxy{mode = started, + mod = Module, + host = Host, + node_name = NodeName, + erl_path = ErlPath, + paths = Paths, + args = Args}, + proc_lib:init_ack({ok, self()}), + ?DEBUG("started", []), + proxy_loop(State). + + +proxy_loop(#proxy{mode = stopping}) -> + receive + {proxy_request, Ref, From, stop} -> + ?LOG("[stopping] received stop order", []), + proxy_reply(From, Ref, ok), + exit(normal); + + {'EXIT', Pid, Reason} -> + ?INFO("[stopping] received exit message from ~p: " + "~n Reason: ~p", [Pid, Reason]), + exit(Reason) + + end; + +proxy_loop(#proxy{mode = started, + host = Host, + node_name = NodeName, + erl_path = ErlPath, + paths = Paths, + args = Args} = State) -> + receive + {proxy_request, Ref, From, {start_node, Debug}} -> + ?LOG("[starting] received start_node order", []), + case hdlt_slave:start_link(Host, NodeName, + ErlPath, Paths, Args, + Debug) of + {ok, Node} -> + ?DEBUG("[starting] node ~p started - now monitor", [Node]), + erlang:monitor_node(Node, true), + State2 = State#proxy{mode = operational, + node = Node}, + proxy_reply(From, Ref, {ok, Node}), + proxy_loop(State2); + {error, Reason} -> + ?INFO("[starting] failed starting node: " + "~n Reason: ~p", [Reason]), + exit({failed_starting_node, {Host, NodeName, Reason}}) + end; + + {'EXIT', Pid, Reason} -> + ?INFO("[stopping] received exit message from ~p: " + "~n Reason: ~p", [Pid, Reason]), + exit(Reason) + + end; + +proxy_loop(#proxy{mode = operational, + mod = Mod, + node = Node} = State) -> + ?DEBUG("[operational] await command", []), + receive + {proxy_request, Ref, From, start_remote_proxy} -> + ?LOG("[operational] start remote proxy", []), + case rpc:call(Node, Mod, start, [?GET_LEVEL()]) of + {ok, Pid} -> + ?DEBUG("[operational] remote proxy started (~p) - " + "create monitor", [Pid]), + ProxyRef = erlang:monitor(process, Pid), + ?DEBUG("[operational] monitor: ~p", [Ref]), + proxy_reply(From, Ref, ok), + proxy_loop(State#proxy{ref = ProxyRef}); + Error -> + ?INFO("[operational] failed starting remote proxy" + "~n Error: ~p", [Error]), + ReplyReason = {failed_starting_remote_proxy, + {Node, Error}}, + Reply = {error, ReplyReason}, + proxy_reply(From, Ref, Reply), + exit({failed_starting_remote_proxy, {Node, Error}}) + end; + + {proxy_request, Ref, From, start_inets} -> + ?INFO("[operational] start inets framework", []), + rpc:cast(Node, Mod, start_inets, []), + proxy_reply(From, Ref, ok), + proxy_loop(State); + + {proxy_request, Ref, From, {start_service, Args}} -> + ?INFO("[operational] start service with" + "~n ~p", [Args]), + case rpc:call(Node, Mod, start_service, Args) of + ok -> + ?DEBUG("[operational] service started", []), + proxy_reply(From, Ref, ok), + proxy_loop(State); + Error -> + ?INFO("[operational] failed starting service: " + "~n Args. ~p" + "~n Error: ~p", [Args, Error]), + erlang:demonitor(State#proxy.ref, [flush]), + Reply = {error, {failed_starting_service, Node, Error}}, + proxy_reply(From, Ref, Reply), + exit({failed_starting_service, Node, Error}) + end; + + {proxy_request, Ref, From, release} -> + ?INFO("[operational] release", []), + rpc:call(Node, Mod, release, []), + proxy_reply(From, Ref, ok), + proxy_loop(State); + + {proxy_request, Ref, From, stop} -> + ?INFO("[operational] received stop order", []), + erlang:demonitor(State#proxy.ref, [flush]), + ?DEBUG("[operational] rpc cast stop order", []), + rpc:cast(Node, Mod, stop, []), + %% And wait for the node death to be reported + Reason = + receive + {nodedown, Node} when State#proxy.node =:= Node -> + ok + after 10000 -> + ?INFO("Node did not die within expected time frame", + []), + {node_death_timeout, Node} + end, + ?DEBUG("[operational] ack stop", []), + proxy_reply(From, Ref, Reason), + exit(normal); + + {nodedown, Node} when State#proxy.node =:= Node -> + ?INFO("[operational] received unexpected nodedoen message", []), + exit({node_died, Node}); + + {'DOWN', Ref, process, _, normal} when State#proxy.ref =:= Ref -> + ?INFO("[operational] remote proxy terminated normally", []), + proxy_loop(State#proxy{ref = undefined, + connection = undefined, + mode = stopping}); + + {'DOWN', Ref, process, _, noconnection} when State#proxy.ref =:= Ref -> + ?INFO("[operational] remote proxy terminated - no node", []), + proxy_loop(State#proxy{ref = undefined, + connection = undefined, + mode = stopping}); + + {'DOWN', Ref, process, _, Reason} when State#proxy.ref =:= Ref -> + ?INFO("[operational] remote proxy terminated: " + "~n Reason: ~p", [Reason]), + exit({remote_proxy_crash, Reason}); + + {'EXIT', Pid, Reason} -> + ?INFO("[operational] received unexpected exit message from ~p: " + "~n Reason: ~p", [Pid, Reason]), + proxy_loop(State) + + end. + + +stop_nodes(#state{server_conn = ServerConn, + client_conns = ClientConns} = State) -> + lists:foreach( + fun(#connection{proxy = Proxy, node_name = NodeName, host = Host}) -> + ?DEBUG("stop_erlang_nodes -> send stop order to local proxy ~p" + "~n for node ~p on ~s", [Proxy, NodeName, Host]), + proxy_stop(Proxy) + end, + ClientConns ++ [ServerConn]), + ?DEBUG("stop_erlang_nodes -> sleep some to give the nodes time to die", + []), + timer:sleep(1000), + ?DEBUG("stop_erlang_nodes -> and a final cleanup round", []), + lists:foreach(fun(Node) -> + ?INFO("try brutal stop node ~p", [Node]), + rpc:cast(Node, erlang, halt, []) + end, + nodes() -- [node()]), + ?DEBUG("stop_erlang_nodes -> done", []), + State#state{server_conn = undefined, client_conns = []}. + + +%% The nodes on which the HDLT clients run have been started previously +start_clients(#state{client_conns = Connections, + debugs = Debugs, + work_dir = WorkDir, + socket_type = SocketType, + client_cert_file = CertFile, + client_sz_from = From, + client_sz_to = To, + client_sz_incr = Incr}, + URL, TestTime, SendRate) -> + Debug = proplists:get_value(client, Debugs, silence), + StartClient = + fun(#connection{host = Host} = Connection) -> + ?DEBUG("start client on ~p", [Host]), + start_client(Connection, + WorkDir, SocketType, CertFile, + URL, From, To, Incr, + TestTime, SendRate, Debug); + (_) -> + ok + end, + lists:foreach(StartClient, Connections). + +start_client(#connection{proxy = Proxy}, + WorkDir, SocketType, LocalCertFile, + URL, From, To, Incr, + TestTime, SendRate, Debug) -> + SSLDir = filename:join(WorkDir, "ssl"), + CertFile = filename:join(SSLDir, filename:basename(LocalCertFile)), + Sizes = randomized_sizes(From, To, Incr), + Args = [SocketType, CertFile, URL, Sizes, TestTime, SendRate, Debug], + proxy_start_service(Proxy, [Args]). + +release_clients(#state{client_conns = Connections}) -> + ReleaseClient = + fun(#connection{proxy = Proxy, + host = Host}) -> + ?DEBUG("release client on ~p", [Host]), + proxy_release(Proxy); + (_) -> + ok + end, + lists:foreach(ReleaseClient, Connections). + + +start_server(#state{server_conn = #connection{proxy = Proxy}, + http_port = Port, + server_root = ServerRoot, + doc_root = DocRoot, + socket_type = SocketType, + server_cert_file = CertFile}) -> + + HttpdConfig = + httpd_config(Port, "hdlt", ServerRoot, DocRoot, SocketType, CertFile), + ?LOG("start the httpd inets service with config: " + "~n ~p", [HttpdConfig]), + proxy_start_service(Proxy, [HttpdConfig]), + ?DEBUG("start_server -> done", []), + ok. + + +httpd_config(Port, ServerName, ServerRoot, DocRoot, + SocketType, LocalCertFile) -> + LogDir = filename:join(ServerRoot, "log"), + ErrorLog = filename:join(LogDir, "error_log"), + TransferLog = filename:join(LogDir, "access_log"), + + SSL = + case SocketType of + ip_comm -> + []; + _ -> % ssl + SSLDir = filename:join(ServerRoot, "ssl"), + CertFile = + filename:join(SSLDir, filename:basename(LocalCertFile)), + [ + {ssl_certificate_file, CertFile}, + {ssl_certificate_key_file, CertFile}, + {ssl_verify_client, 0} + ] + end, + [{port, Port}, + {server_name, ServerName}, + {server_root, ServerRoot}, + {document_root, DocRoot}, + {error_log, ErrorLog}, + {error_log_format, pretty}, + {transfer_log, TransferLog}, + {socket_type, SocketType}, + {max_clients, 10000}, + {modules, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, + mod_dir, mod_get, mod_head, mod_log, mod_disk_log]}, + {script_alias, {"/cgi-bin", filename:join(ServerRoot, "cgi-bin")}}, + {erl_script_alias, {"/cgi-bin", [hdlt_random_html]}}, + {erl_script_timeout, 120000} | SSL]. + + +clean_up(#state{server_root = ServerRoot, + work_dir = WorkDir, + http_server = #server{host = Host}, + clients = Clients}) -> + ?DEBUG("begin server cleanup", []), + server_clean_up(ServerRoot, WorkDir, Host), + ?DEBUG("begin lient cleanup", []), + clients_clean_up(WorkDir, Clients), + ?DEBUG("cleanup done", []), + ok. + +server_clean_up(ServerRoot, WorkDir, Host) -> + ?DEBUG("server cleanup - create sftp channel", []), + {ok, Sftp, ConnectionRef} = + ssh_sftp:start_channel(Host, [{user_interaction, false}, + {silently_accept_hosts, true}]), + ?DEBUG("server cleanup - delete ~p dirs", [ServerRoot]), + del_dirs(Sftp, ServerRoot), + ?DEBUG("server cleanup - delete ~p dirs", [WorkDir]), + del_dirs(Sftp, WorkDir), + ?DEBUG("server cleanup - close sftp channel", []), + ssh:close(ConnectionRef). + +clients_clean_up(_WorkDir, []) -> + ok; +clients_clean_up(WorkDir, [Client|Clients]) -> + client_clean_up(WorkDir, Client), + clients_clean_up(WorkDir, Clients). + +client_clean_up(WorkDir, #client{host = Host}) -> + ?DEBUG("client cleanup - create sftp channel to ~p", [Host]), + {ok, Sftp, ConnectionRef} = + ssh_sftp:start_channel(Host, [{user_interaction, false}, + {silently_accept_hosts, true}]), + ?DEBUG("client cleanup - delete ~p dirs", [WorkDir]), + del_dirs(Sftp, WorkDir), + ?DEBUG("client cleanup - close sftp channel", []), + ssh:close(ConnectionRef). + + +del_dirs(Sftp, Dir) -> + case ssh_sftp:list_dir(Sftp, Dir) of + {ok, []} -> + ssh_sftp:del_dir(Sftp, Dir); + {ok, Files} -> + Files2 = [F || F <- Files, (F =/= "..") andalso (F =/= ".")], + lists:foreach(fun(File) when ((File =/= "..") andalso + (File =/= ".")) -> + FullPath = filename:join(Dir, File), + case ssh_sftp:read_file_info(Sftp, + FullPath) of + {ok, #file_info{type = directory}} -> + del_dirs(Sftp, FullPath), + ssh_sftp:del_dir(Sftp, FullPath); + {ok, _} -> + ssh_sftp:delete(Sftp, FullPath) + end + end, Files2); + _ -> + ok + end. + +collect_data(#state{clients = Clients} = State) -> + N = length(Clients), + collect_req_reply(N, State), + collect_time(N, State). + +collect_req_reply(0, _State) -> + ?DEBUG("all reply data collected", []), + ok; +collect_req_reply(N, #state{nof_schedulers = NofScheduler, + results = Db, + client_conns = Conns} = State) -> + ?DEBUG("await reply data from ~p client(s)", [N]), + receive + {load_data, + {req_reply, Client, NoRequests, NoReplys}} -> + ?DEBUG("received req_reply load-data from client ~p: " + "~n Number of requests: ~p" + "~n Number of replies: ~p", + [Client, NoRequests, NoReplys]), + ets:insert(Db, {{NofScheduler, Client}, + {req_reply, NoRequests, NoReplys}}); + stop -> + ?INFO("received stop", []), + exit(self(), stop); + + {client_exit, Client, Node, Reason} -> + ?INFO("Received unexpected client exit from ~p on node ~p " + "while collecting replies: " + "~n ~p", [Client, Node, Reason]), + case lists:keysearch(Node, #connection.node, Conns) of + {value, Conn} -> + ?LOG("Found problem connection: " + "~n ~p", [Conn]), + exit({unexpected_client_exit, Reason}); + false -> + collect_req_reply(N, State) + end + end, + collect_req_reply(N-1, State). + +collect_time(0, _State) -> + ?DEBUG("all time data collected", []), + ok; +collect_time(N, #state{nof_schedulers = NofScheduler, + results = Db, + client_conns = Conns} = State) -> + ?DEBUG("await time data from ~p clients", [N]), + receive + {load_data, + {time_to_complete, Client, StopTime, LastResponseTime}} -> + ?LOG("received time load-data from client ~p: " + "~n Time of stop: ~p" + "~n Time of last response: ~p", + [Client, StopTime, LastResponseTime]), + ets:insert(Db, {{NofScheduler, Client}, + {time, StopTime, LastResponseTime}}); + stop -> + ?INFO("received stop while collecting data, when N = ~p", [N]), + exit(self(), stop); + + {client_exit, Client, Node, Reason} -> + ?INFO("Received unexpected exit from client ~p on node ~p " + "while collecting time data: " + "~n ~p", [Client, Node, Reason]), + case lists:keysearch(Node, #connection.node, Conns) of + {value, Conn} -> + ?LOG("Found problem connection: " + "~n ~p", [Conn]), + exit({unexpected_client_exit, Reason}); + false -> + collect_req_reply(N, State) + end; + + Else -> %%% Something is wrong! + ?INFO("RECEIVED UNEXPECTED MESSAGE WHILE COLLECTING TIME DATA: " + "~n ~p", [Else]), + collect_time(N, State) + end, + collect_time(N-1, State). + +analyse_data(#state{results = Db, + max_nof_schedulers = MaxNofSchedulers, + test_time = MicroSec}) -> + Tab = ets:new(analysed_results, [set]), + lists:foreach(fun(NofSchedulers) -> + Result = analyse(NofSchedulers, Db, MicroSec), + ets:insert(Tab, Result) + end, [N || N <- lists:seq(0, MaxNofSchedulers)]), + Tab. + + +no_requests_replys(NoSchedulers, Tab) -> + NoRequests = + ets:select(Tab, [{{{NoSchedulers,'_'},{req_reply, '$1', '_'}}, + [],['$$']}]), + NoReplys = + ets:select(Tab, [{{{NoSchedulers, '_'}, {req_reply, '_', '$1'}}, + [], ['$$']}]), + + {lists:sum(lists:append(NoRequests)), + lists:sum(lists:append(NoReplys))}. + +max_time_to_final_response(NofSchedulers, Tab) -> + Candidates = + ets:select(Tab, [{{{NofSchedulers, '_'}, {time, '$1', '$2'}}, + [], ['$$']}]), + + NewCandidates = lists:map( + fun([StopTime, LastTime]) -> + round( + timer:now_diff(LastTime, StopTime) / 100000)/10 + end, Candidates), + + lists:max(NewCandidates). + + +analyse(NofSchedulers, Db, TestTime) -> + Sec = TestTime / 1000, + {NoRequests, NoReplys} = no_requests_replys(NofSchedulers, Db), + {NofSchedulers, round(NoReplys / Sec), NoRequests, + max_time_to_final_response(NofSchedulers, Db)}. + + +save_results_to_file(AnalysedTab, + #state{socket_type = SocketType, + http_server = #server{host = Server}, + max_nof_schedulers = MaxNofSchedulers}) -> + FileName = fun(Post) -> + File = + lists:flatten( + io_lib:format("~s_~w_~s", + [Server, SocketType, Post])), + filename:join("./", File) + end, + Reps = FileName("replys_per_sec.txt"), + Reqs = FileName("total_requests.txt"), + Decay = FileName("decay_time.txt"), + + [FdReps, FdReqs, FdDecay] = + lists:map(fun(File) -> + {ok, Fd} = file:open(File, [write]), + Fd + end, [Reps, Reqs, Decay]), + lists:foreach(fun(NofSchedulers) -> + save_result_to_file(NofSchedulers, + FdReps, FdReqs, + FdDecay, AnalysedTab) + end, [N || N <- lists:seq(0, MaxNofSchedulers)]), + [Reps, Reqs, Decay]. + +save_result_to_file(NofSchedulers, + FdReps, FdReqs, FdDecay, AnalysedTab) -> + + [{NofSchedulers, NofRepsPerSec, NofReqs, MaxFinalResponseTime}] = + ets:lookup(AnalysedTab, NofSchedulers), + + file:write(FdReps, io_lib:format("~p,~p~n", + [NofRepsPerSec, NofSchedulers])), + file:write(FdReqs, io_lib:format("~p,~p~n", + [NofReqs, NofSchedulers])), + file:write(FdDecay, io_lib:format("~p,~p~n", [MaxFinalResponseTime, + NofSchedulers])). + + +help() -> + io:format("hdlt:start(Options). Where options:~n " + " ~n~p~n~n hdlt:start([]). -> hdlt:start(~p)~n~n", + [[{send_rate, "integer()", + "Numer of outstanding requests that a client " + "should have during the test to create a load situation."}, + {clients, "[{path(), host()}]", "Paths to erlang and names of hosts to run clients on."}, + {test_time, "{hours(), mins(), sec()}", + "How long the test should be run."}, + {server, "{path(), host()}", "Path to erl and name of host to run the HTTP-server on."}, + {port, "port()", "The port that the HTTP-server should use."}, + {server_dir, "dir()", "The directory where the HTTP server " + " stores its contents and configuration."}, + {work_dir, "dir()", "Path on the computer, where the test " + "is run, to a directory where the results can be saved."}, + {max_no_schedulers, "integer()", + "Max number of schedulers to run."}, + {socket_type, "Httpd configuration option socket_type"}], + defaults()]). + + +defaults() -> + [{send_rate, ?DEFAULT_SENDRATE}, + %% {clients, []}, + {test_time, ?DEFAULT_TEST_TIME}, + %% {server, ?DEFAULT_SERVER}, + {port, ?DEFAULT_PORT}, + {server_dir, ?DEFAULT_SERVER_DIR}, + {work_dir, ?DEFAULT_WORK_DIR}, + {max_nof_schedulers, ?DEFAULT_MAX_NOF_SCHEDULERS}, + {socket_type, ?DEFAULT_SOCKET_TYPE}]. + + +get_debugs(Config) -> + ?DEBUG("get debugs", []), + Debugs = proplists:get_value(debug, Config, ?DEFAULT_DEBUGS), + verify_debugs(Debugs), + Debugs. + +verify_debugs([]) -> + ok; +verify_debugs([{Tag, Debug}|Debugs]) -> + verify_debug(Tag, Debug), + verify_debugs(Debugs). + +verify_debug(Tag, Debug) -> + case lists:member(Tag, [ctrl, proxy, slave, client]) of + true -> + ok; + false -> + exit({bad_debug_tag, Tag}) + end, + case lists:member(Debug, [silence, info, log, debug]) of + true -> + ok; + false -> + exit({bad_debug_level, Debug}) + end. + +get_send_rate(Config) -> + ?DEBUG("get send_rate", []), + case proplists:get_value(send_rate, Config, ?DEFAULT_SENDRATE) of + SendRate when is_integer(SendRate) andalso (SendRate > 0) -> + SendRate; + BadSendRate -> + exit({bad_sendrate, BadSendRate}) + end. + + +get_clients(Config) -> + ?DEBUG("get clients", []), + case proplists:get_value(clients, Config, undefined) of + undefined -> + missing_mandatory_config(clients); + Clients when is_list(Clients) andalso (length(Clients) > 0) -> + case [#client{path = Path, host = Host} || + {Path, Host} <- Clients] of + Clients2 when (length(Clients2) > 0) -> + Clients2; + _ -> + exit({bad_clients, Clients}) + end; + + BadClients -> + exit({bad_clients, BadClients}) + + end. + +get_server(Config) -> + ?DEBUG("get server", []), + case proplists:get_value(server, Config) of + {Path, Host} when is_list(Path) andalso is_list(Host) -> + #server{path = Path, host = Host}; + undefined -> + missing_mandatory_config(server) + end. + +get_server_dir(Config) -> + ?DEBUG("get server_dir", []), + get_dir(server_dir, Config, ?DEFAULT_SERVER_DIR). + +get_work_dir(Config) -> + ?DEBUG("get work_dir", []), + get_dir(work_dir, Config, ?DEFAULT_WORK_DIR). + +get_dir(Key, Config, Default) -> + Dir = proplists:get_value(Key, Config, Default), + ensure_absolute(Dir), + Dir. + +ensure_absolute(Path) -> + case filename:pathtype(Path) of + absolute -> + ok; + PathType -> + exit({bad_pathtype, Path, PathType}) + end. + +get_port(Config) -> + ?DEBUG("get port", []), + case proplists:get_value(port, Config, ?DEFAULT_PORT) of + Port when is_integer(Port) andalso (Port > 0) -> + Port; + BadPort -> + exit({bad_port, BadPort}) + end. + +get_socket_type(Config) -> + ?DEBUG("get socket_type", []), + case proplists:get_value(socket_type, Config, ?DEFAULT_SOCKET_TYPE) of + SocketType when ((SocketType =:= ip_comm) orelse + (SocketType =:= ssl) orelse + (SocketType =:= essl) orelse + (SocketType =:= ossl)) -> + SocketType; + BadSocketType -> + exit({bad_socket_type, BadSocketType}) + end. + +get_test_time(Config) -> + ?DEBUG("get test_time", []), + case proplists:get_value(test_time, Config, ?DEFAULT_TEST_TIME) of + Seconds when is_integer(Seconds) andalso (Seconds > 0) -> + timer:seconds(Seconds); + BadTestTime -> + exit({bad_test_time, BadTestTime}) + end. + +get_max_nof_schedulers(Config) -> + ?DEBUG("get max_nof_schedulers", []), + case proplists:get_value(max_nof_schedulers, + Config, + ?DEFAULT_MAX_NOF_SCHEDULERS) of + MaxNofScheds when (is_integer(MaxNofScheds) andalso + (MaxNofScheds >= 0)) -> + MaxNofScheds; + BadMaxNofScheds -> + exit({bad_max_nof_schedulers, BadMaxNofScheds}) + end. + + +get_server_cert_file(Config) -> + ?DEBUG("get server cert file", []), + get_cert_file(server_cert_file, ?DEFAULT_SERVER_CERT, Config). + +get_client_cert_file(Config) -> + ?DEBUG("get client cert file", []), + get_cert_file(client_cert_file, ?DEFAULT_CLIENT_CERT, Config). + +get_cert_file(Tag, DefaultCertFileName, Config) -> + LibDir = code:lib_dir(inets), + HdltDir = filename:join(LibDir, "examples/httpd_load_test"), + DefaultCertFile = filename:join(HdltDir, DefaultCertFileName), + case proplists:get_value(Tag, Config, DefaultCertFile) of + F when is_list(F) -> + case file:read_file_info(F) of + {ok, #file_info{type = regular}} -> + F; + {ok, #file_info{type = Type}} -> + exit({wrong_file_type, Tag, F, Type}); + {error, Reason} -> + exit({failed_readin_file_info, Tag, F, Reason}) + end; + BadFile -> + exit({bad_cert_file, Tag, BadFile}) + end. + + +get_work_sim(Config) -> + ?DEBUG("get work_sim", []), + case proplists:get_value(work_simulator, Config, ?DEFAULT_WORK_SIM) of + WS when is_integer(WS) andalso (WS > 0) -> + WS; + BadWS -> + exit({bad_work_simulator, BadWS}) + end. + + +get_data_size(Config) -> + ?DEBUG("get data_size", []), + case proplists:get_value(data_size, Config, ?DEFAULT_DATA_SIZE) of + {From, To, Incr} = DS when (is_integer(From) andalso + is_integer(To) andalso + is_integer(Incr) andalso + (To > From) andalso + (From > 0) andalso + (Incr > 0)) -> + DS; + {From, To} when (is_integer(From) andalso + is_integer(To) andalso + (To > From) andalso + (From > 0)) -> + {From, To, ?DEFAULT_DATA_SIZE_INCR}; + BadDS -> + exit({bad_data_size, BadDS}) + end. + + +url(#server{host = Host}, Port, SocketType, WorkSim) -> + Scheme = + case SocketType of + ip_comm -> + "http"; + _ -> %% SSL + "https" + end, + lists:flatten( + io_lib:format("~s://~s:~w/cgi-bin/hdlt_random_html:page?~w:", + [Scheme, Host, Port, WorkSim])). + + +missing_mandatory_config(Missing) -> + exit({missing_mandatory_config, Missing}). + + +ensure_remote_dir_exist(Sftp, Path0) -> + case filename:split(Path0) of + [Root, Dir | Rest] -> + %% We never accept creating the root directory, + %% or the next level, so these *must* exist: + Path = filename:join(Root, Dir), + case ssh_sftp:read_file_info(Sftp, Path) of + {ok, #file_info{type = directory}} -> + ensure_remote_dir_exist(Sftp, Path, Rest); + {ok, #file_info{type = Type}} -> + ?INFO("Not a dir: ~p (~p)", [Path, Type]), + exit({not_a_dir, Path, Type}); + {error, Reason} -> + ?INFO("Failed reading file info for ~p: ~p", + [Path, Reason]), + exit({failed_reading_file_info, Path, Reason}) + end; + BadSplit -> + ?INFO("Bad remote dir path: ~p -> ~p", [Path0, BadSplit]), + exit({bad_dir, Path0}) + end. + +ensure_remote_dir_exist(_Sftp, _Dir, []) -> + ok; +ensure_remote_dir_exist(Sftp, Path, [Dir|Rest]) -> + NewPath = filename:join(Path, Dir), + case ssh_sftp:read_file_info(Sftp, NewPath) of + {ok, #file_info{type = directory}} -> + ensure_remote_dir_exist(Sftp, NewPath, Rest); + {ok, #file_info{type = Type}} -> + %% Exist, but is not a dir + ?INFO("Not a dir: ~p (~p)", [NewPath, Type]), + exit({not_a_dir, NewPath, Type}); + {error, Reason} -> + %% This *could* be because the dir does not exist, + %% but it could also be some other error. + %% As usual, the error reason of the sftp is + %% a pease of crap, so we cannot use the + %% error reason. + %% The simplest way to test this is to simply + %% try to create the directory, since we should + %% ensure its existence anyway.. + case ssh_sftp:make_dir(Sftp, NewPath) of + ok -> + ensure_remote_dir_exist(Sftp, NewPath, Rest); + _ -> + ?INFO("Failed reading file info for ~p: ~p", + [Dir, Reason]), + exit({failed_reading_file_info, NewPath, Reason}) + end + end. + +maybe_create_remote_dir(Sftp, Dir) -> + case ssh_sftp:read_file_info(Sftp, Dir) of + {ok, #file_info{type = directory}} -> + ok; + {ok, #file_info{type = Type}} -> + %% Exist, but is not a dir + ?INFO("Not a dir: ~p (~p)", [Dir, Type]), + exit({not_a_dir, Dir, Type}); + {error, Reason} -> + %% Assume dir noes not exist... + case ssh_sftp:make_dir(Sftp, Dir) of + ok -> + ok; + _ -> + ?INFO("Failed reading file info for ~p: ~p", + [Dir, Reason]), + exit({failed_reading_file_info, Dir, Reason}) + end + end. + + +set_debug_level(Debugs) -> + Debug = proplists:get_value(ctrl, Debugs, silence), + ?SET_LEVEL(Debug). + + +%% Generates a list of numbers between A and B, such that +%% there is exact one number between A and B and then +%% randomizes that list. + +randomized_sizes_init() -> + {A, B, C} = os:timestamp(), + random:seed(A, B, C). + +randomized_sizes(From, To, Incr) -> + L = lists:seq(From, To, Incr), + Len = length(L), + randomized_sizes2(L, 0, Len-1). + +randomized_sizes2(L, N, Len) when N >= Len -> + L; +randomized_sizes2(L, N, Len) -> + SplitWhere = random:uniform(Len), + {A, B} = lists:split(SplitWhere, L), + randomized_sizes2(B ++ A, N+1, Len). diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.erl b/lib/inets/examples/httpd_load_test/hdlt_logger.erl new file mode 100644 index 0000000000..b0c7eab2d1 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_logger.erl @@ -0,0 +1,138 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% Purpose: This is a simple logger utility for the HDLT toolkit. +%% It assumesd that the debug level and the "name" of the +%% logging entity has been put in process environment +%% (using the set_level and set_name functions respectively). +%%---------------------------------------------------------------------- + +%% + +-module(hdlt_logger). + +-export([ + start/0, + set_level/1, get_level/0, set_name/1, + info/2, log/2, debug/2 + ]). + +-export([logger/1]). + +-define(LOGGER, ?MODULE). +-define(MSG, hdlt_logger_msg). +-define(LEVEL, hdlt_logger_level). +-define(NAME, hdlt_logger_name). +-define(INFO_STR, "INFO"). +-define(LOG_STR, "LOG "). +-define(DEBUG_STR, "DBG "). + + +start() -> + Self = self(), + proc_lib:start(?MODULE, logger, [Self]). + +set_name(Name) when is_list(Name) -> + put(?NAME, Name), + ok. + +get_level() -> + get(?LEVEL). + +set_level(Level) -> + case lists:member(Level, [silence, info, log, debug]) of + true -> + put(?LEVEL, Level), + ok; + false -> + erlang:error({bad_debug_level, Level}) + end. + + +info(F, A) -> +%% io:format("info -> " ++ F ++ "~n", A), + do_log(info, get(?LEVEL), F, A). + +log(F, A) -> +%% io:format("log -> " ++ F ++ "~n", A), + do_log(log, get(?LEVEL), F, A). + +debug(F, A) -> +%% io:format("debug -> " ++ F ++ "~n", A), + do_log(debug, get(?LEVEL), F, A). + + +logger(Parent) -> + global:register_name(?LOGGER, self()), + Ref = erlang:monitor(process, Parent), + proc_lib:init_ack(self()), + logger_loop(Ref). + +logger_loop(Ref) -> + receive + {?MSG, F, A} -> + io:format(F, A), + logger_loop(Ref); + {'DOWN', Ref, process, _Object, _Info} -> + %% start the stop timer + erlang:send_after(timer:seconds(5), self(), stop), + logger_loop(undefined); + stop -> + global:unregister_name(?LOGGER), + ok + end. + + +formated_timestamp() -> + {Date, Time} = erlang:localtime(), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", + [YYYY,MM,DD,Hour,Min,Sec]), + lists:flatten(FormatDate). + +do_log(_, silence, _, _) -> + ok; +do_log(info, info, F, A) -> + do_log(?INFO_STR, F, A); +do_log(info, log, F, A) -> + do_log(?INFO_STR, F, A); +do_log(log, log, F, A) -> + do_log(?LOG_STR, F, A); +do_log(info, debug, F, A) -> + do_log(?INFO_STR, F, A); +do_log(log, debug, F, A) -> + do_log(?LOG_STR, F, A); +do_log(debug, debug, F, A) -> + do_log(?DEBUG_STR, F, A); +do_log(_, _, _F, _A) -> + ok. + +do_log(SEV, F, A) -> + Name = + case get(?NAME) of + L when is_list(L) -> + L; + _ -> + "UNDEFINED" + end, + Msg = {?MSG, "~s ~s [~s] " ++ F ++ "~n", + [SEV, Name, formated_timestamp() | A]}, + (catch global:send(?LOGGER, Msg)). diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.hrl b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl new file mode 100644 index 0000000000..aa94babc48 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(hdlt_logger_hrl). +-define(hdlt_logger_hrl, true). + +%% Various log macros +-define(SET_LEVEL(N), hdlt_logger:set_level(N)). +-define(GET_LEVEL(), hdlt_logger:get_level()). +-define(SET_NAME(N), hdlt_logger:set_name(N)). + +-define(INFO(F, A), hdlt_logger:info(F, A)). +-define(LOG(F, A), hdlt_logger:log(F, A)). +-define(DEBUG(F, A), hdlt_logger:debug(F, A)). + +-endif. % -ifdef(hdlt_logger_hrl). diff --git a/lib/inets/examples/httpd_load_test/hdlt_random_html.erl b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl new file mode 100644 index 0000000000..e3a572c61f --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl @@ -0,0 +1,59 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% + +-module(hdlt_random_html). +-export([page/3]). + +page(SessionID, _Env, Input) -> +%% log("page(~p) -> deliver content-type when" +%% "~n SessionID: ~p" +%% "~n Env: ~p" +%% "~n Input: ~p", [self(), SessionID, Env, Input]), + [WorkSimStr, SzSimStr] = string:tokens(Input, [$:]), + WorkSim = list_to_integer(WorkSimStr), + SzSim = list_to_integer(SzSimStr), + mod_esi:deliver(SessionID, "Content-Type:text/html\r\n\r\n"), + mod_esi:deliver(SessionID, start("Random test page")), + mod_esi:deliver(SessionID, content(WorkSim, SzSim)), + mod_esi:deliver(SessionID, stop()), + ok. + +start(Title) -> + "<HTML> +<HEAD> +<TITLE>" ++ Title ++ "</TITLE> + </HEAD> +<BODY>\n". + +stop() -> + "</BODY> +</HTML> +". + +content(WorkSim, SzSim) -> + {A, B, C} = now(), + random:seed(A, B, C), + lists:sort([random:uniform(X) || X <- lists:seq(1, WorkSim)]), + lists:flatten(lists:duplicate(SzSim, "Dummy data ")). + +%% log(F, A) -> +%% hdlt_logger:set_name("HDLT RANDOM-HTML"), +%% hdlt_logger:set_level(debug), +%% hdlt_logger:log(F, A). diff --git a/lib/inets/examples/httpd_load_test/hdlt_server.erl b/lib/inets/examples/httpd_load_test/hdlt_server.erl new file mode 100644 index 0000000000..3e5a849d5b --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_server.erl @@ -0,0 +1,163 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: The HDLT server module. +%% This is just a stub, making future expansion easy. +%% All code in this module is executed in the local node! +%%---------------------------------------------------------------------- + +-module(hdlt_server). + +-export([start/1, stop/0, start_inets/0, start_service/1]). + +-export([proxy/1]). + +-include_lib("kernel/include/file.hrl"). +-include("hdlt_logger.hrl"). + + +-define(PROXY, hdlt_proxy). + + +%% This function is used to start the proxy process +%% This function is called *after* the nodes has been +%% "connected" with the controller/collector node. + +start(Debug) -> + proc_lib:start(?MODULE, proxy, [Debug]). + +stop() -> + ?PROXY ! stop. + +start_inets() -> + ?PROXY ! start_inets. + +start_service(Config) -> + ?PROXY ! {server_start, Config, self()}, + receive + {server_start_result, Result} -> + Result + after 15000 -> + {error, timeout} + end. + + +proxy(Debug) -> + process_flag(trap_exit, true), + erlang:register(?PROXY, self()), + ?SET_NAME("HDLT PROXY"), + ?SET_LEVEL(Debug), + ?LOG("starting", []), + Ref = await_for_controller(10), + CtrlNode = node(Ref), + erlang:monitor_node(CtrlNode, true), + proc_lib:init_ack({ok, self()}), + ?DEBUG("started", []), + proxy_loop(Ref, CtrlNode). + +await_for_controller(N) when N > 0 -> + case global:whereis_name(hdlt_ctrl) of + Pid when is_pid(Pid) -> + erlang:monitor(process, Pid); + _ -> + timer:sleep(1000), + await_for_controller(N-1) + end; +await_for_controller(_) -> + proc_lib:init_ack({error, controller_not_found, nodes()}), + timer:sleep(500), + halt(). + + +proxy_loop(Ref, CtrlNode) -> + ?DEBUG("await command", []), + receive + stop -> + ?LOG("received stop", []), + halt(); + + start_inets -> + ?LOG("start the inets service framework", []), + case (catch inets:start()) of + ok -> + ?LOG("framework started", []), + proxy_loop(Ref, CtrlNode); + Error -> + ?LOG("failed starting inets service framework: " + "~n Error: ~p", [Error]), + halt() + end; + + {server_start, Config, From} -> + ?LOG("start-server", []), + maybe_start_crypto_and_ssl(Config), + %% inets:enable_trace(max, "/tmp/inets-httpd-trace.log", httpd), + %% inets:enable_trace(max, "/tmp/inets-httpd-trace.log", all), + case (catch inets:start(httpd, Config)) of + {ok, _} -> + ?LOG("server started when" + "~n which(inets): ~p" + "~n RootDir: ~p" + "~n System info: ~p", [code:which(inets), + code:root_dir(), + get_node_info()]), + From ! {server_start_result, ok}, + proxy_loop(Ref, CtrlNode); + Error -> + ?INFO("server start failed" + "~n Error: ~p", [Error]), + From ! {server_start_result, Error}, + halt() + end; + + {nodedown, CtrlNode} -> + ?LOG("received nodedown for controller node - terminate", []), + halt(); + + {'DOWN', Ref, process, _, _} -> + ?LOG("received DOWN message for controller - terminate", []), + %% The controller has terminated, time to die + halt() + + end. + + +maybe_start_crypto_and_ssl(Config) -> + case lists:keysearch(socket_type, 1, Config) of + {value, {socket_type, SocketType}} when ((SocketType =:= ssl) orelse + (SocketType =:= ossl) orelse + (SocketType =:= essl)) -> + ?LOG("maybe start crypto and ssl", []), + (catch crypto:start()), + ssl:start(); + _ -> + ok + end. + + +get_node_info() -> + [{cpu_topology, erlang:system_info(cpu_topology)}, + {heap_type, erlang:system_info(heap_type)}, + {nof_schedulers, erlang:system_info(schedulers)}, + {otp_release, erlang:system_info(otp_release)}, + {version, erlang:system_info(version)}, + {system_version, erlang:system_info(system_version)}, + {system_architecture, erlang:system_info(system_architecture)}]. + diff --git a/lib/inets/examples/httpd_load_test/hdlt_slave.erl b/lib/inets/examples/httpd_load_test/hdlt_slave.erl new file mode 100644 index 0000000000..52af9b5b90 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_slave.erl @@ -0,0 +1,291 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +-module(hdlt_slave). + + +-export([start_link/4, start_link/5, start_link/6, stop/1]). + +%% Internal exports +-export([wait_for_slave/9, slave_start/1, wait_for_master_to_die/3]). + +-include("hdlt_logger.hrl"). + +-define(SSH_PORT, 22). +-define(TIMEOUT, 60000). +-define(LOGGER, hdlt_logger). + + +%% *********************************************************************** +%% start_link/4,5 -- +%% +%% The start/4,5 functions are used to start a slave Erlang node. +%% The node on which the start/N functions are used is called the +%% master in the description below. +%% +%% If hostname is the same for the master and the slave, +%% the Erlang node will simply be spawned. The only requirment for +%% this to work is that the 'erl' program can be found in PATH. +%% +%% If the master and slave are on different hosts, start/N uses +%% the 'rsh' program to spawn an Erlang node on the other host. +%% Alternative, if the master was started as +%% 'erl -sname xxx -rsh my_rsh...', then 'my_rsh' will be used instead +%% of 'rsh' (this is useful for systems where the rsh program is named +%% 'remsh'). +%% +%% For this to work, the following conditions must be fulfilled: +%% +%% 1. There must be an Rsh program on computer; if not an error +%% is returned. +%% +%% 2. The hosts must be configured to allowed 'rsh' access without +%% prompts for password. +%% +%% The slave node will have its filer and user server redirected +%% to the master. When the master node dies, the slave node will +%% terminate. For the start_link functions, the slave node will +%% terminate also if the process which called start_link terminates. +%% +%% Returns: {ok, Name@Host} | +%% {error, timeout} | +%% {error, no_rsh} | +%% {error, {already_running, Name@Host}} + +start_link(Host, Name, ErlPath, Paths) -> + start_link(Host, Name, ErlPath, Paths, [], silence). + +start_link(Host, Name, ErlPath, Paths, DebugLevel) when is_atom(DebugLevel) -> + start_link(Host, Name, ErlPath, Paths, [], DebugLevel); +start_link(Host, Name, ErlPath, Paths, Args) when is_list(Args) -> + start_link(Host, Name, ErlPath, Paths, Args, silence). + +start_link(Host, Name, ErlPath, Paths, Args, DebugLevel) -> + Node = list_to_atom(lists:concat([Name, "@", Host])), + case net_adm:ping(Node) of + pang -> + start_it(Host, Name, Node, ErlPath, Paths, Args, DebugLevel); + pong -> + {error, {already_running, Node}} + end. + +%% Stops a running node. + +stop(Node) -> + rpc:call(Node, erlang, halt, []), + ok. + + +%% Starts a new slave node. + +start_it(Host, Name, Node, ErlPath, Paths, Args, DebugLevel) -> + Prog = filename:join([ErlPath, "erl"]), + spawn(?MODULE, wait_for_slave, [self(), Host, Name, Node, Paths, Args, self(), Prog, DebugLevel]), + receive + {result, Result} -> Result + end. + +%% Waits for the slave to start. + +wait_for_slave(Parent, Host, Name, Node, Paths, Args, + LinkTo, Prog, DebugLevel) -> + ?SET_NAME("HDLT SLAVE STARTER"), + ?SET_LEVEL(DebugLevel), + ?DEBUG("begin", []), + Waiter = register_unique_name(0), + case mk_cmd(Host, Name, Paths, Args, Waiter, Prog) of + {ok, Cmd} -> + ?DEBUG("command generated: ~n~s", [Cmd]), + case (catch ssh_slave_start(Host, Cmd)) of + {ok, Conn, _Chan} -> + ?DEBUG("ssh channel created", []), + receive + {SlavePid, slave_started} -> + ?DEBUG("slave started: ~p", [SlavePid]), + unregister(Waiter), + slave_started(Parent, LinkTo, SlavePid, Conn, + DebugLevel) + after 32000 -> + ?INFO("slave node failed to report in on time", + []), + %% If it seems that the node was partially started, + %% try to kill it. + case net_adm:ping(Node) of + pong -> + spawn(Node, erlang, halt, []), + ok; + _ -> + ok + end, + Parent ! {result, {error, timeout}} + end; + {error, Reason} = Error -> + ?INFO("FAILED starting node: " + "~n ~p" + "~n ~p", [Reason, Cmd]), + Parent ! {result, Error} + end; + Other -> + ?INFO("FAILED creating node command string: " + "~n ~p", [Other]), + Parent ! {result, Other} + end. + + +ssh_slave_start(Host, ErlCmd) -> + ?DEBUG("ssh_slave_start -> try connect to ~p", [Host]), + Connection = + case (catch ssh:connect(Host, ?SSH_PORT, + [{silently_accept_hosts, true}])) of + {ok, Conn} -> + ?DEBUG("ssh_exec_erl -> connected: ~p", [Conn]), + Conn; + Error1 -> + ?LOG("failed connecting to ~p: ~p", [Host, Error1]), + throw({error, {ssh_connect_failed, Error1}}) + end, + + ?DEBUG("ssh_exec_erl -> connected - now create channel", []), + Channel = + case (catch ssh_connection:session_channel(Connection, ?TIMEOUT)) of + {ok, Chan} -> + ?DEBUG("ssh_exec_erl -> channel ~p created", [Chan]), + Chan; + Error2 -> + ?LOG("failed creating channel: ~p", [Error2]), + throw({error, {ssh_channel_create_failed, Error2}}) + end, + + ?DEBUG("ssh_exec_erl -> channel created - now exec command: " + "~n ~p", [ErlCmd]), + case (catch ssh_connection:exec(Connection, Channel, ErlCmd, infinity)) of + success -> + ?DEBUG("ssh_exec_erl -> command exec'ed - clean ssh msg", []), + clean_ssh_msg(), + ?DEBUG("ssh_exec_erl -> done", []), + {ok, Connection, Channel}; + Error3 -> + ?LOG("failed exec comand: ~p", [Error3]), + throw({error, {ssh_exec_failed, Error3}}) + end. + +clean_ssh_msg() -> + receive + {ssh_cm, _X, _Y} -> + clean_ssh_msg() + after 1000 -> + ok + end. + + +slave_started(ReplyTo, Master, Slave, Conn, Level) + when is_pid(Master) andalso is_pid(Slave) -> + process_flag(trap_exit, true), + SName = lists:flatten( + io_lib:format("HDLT SLAVE CTRL[~p,~p]", + [self(), node(Slave)])), + ?SET_NAME(SName), + ?SET_LEVEL(Level), + ?LOG("initiating", []), + MasterRef = erlang:monitor(process, Master), + SlaveRef = erlang:monitor(process, Slave), + ReplyTo ! {result, {ok, node(Slave)}}, + slave_running(Master, MasterRef, Slave, SlaveRef, Conn). + + +%% The slave node will be killed if the master process terminates, +%% The master process will not be killed if the slave node terminates. + +slave_running(Master, MasterRef, Slave, SlaveRef, Conn) -> + ?DEBUG("await message", []), + receive + {'DOWN', MasterRef, process, _Object, _Info} -> + ?LOG("received DOWN from master", []), + erlang:demonitor(SlaveRef, [flush]), + Slave ! {nodedown, node()}, + ssh:close(Conn); + + {'DOWN', SlaveRef, process, Object, _Info} -> + ?LOG("received DOWN from slave (~p)", [Object]), + erlang:demonitor(MasterRef, [flush]), + ssh:close(Conn); + + Other -> + ?DEBUG("received unknown: ~n~p", [Other]), + slave_running(Master, MasterRef, Slave, SlaveRef, Conn) + + end. + +register_unique_name(Number) -> + Name = list_to_atom(lists:concat([?MODULE, "_waiter_", Number])), + case catch register(Name, self()) of + true -> + Name; + {'EXIT', {badarg, _}} -> + register_unique_name(Number+1) + end. + + +%% Makes up the command to start the nodes. +%% If the node should run on the local host, there is +%% no need to use rsh. + +mk_cmd(Host, Name, Paths, Args, Waiter, Prog) -> + PaPaths = [[" -pa ", Path] || Path <- Paths], + {ok, lists:flatten( + lists:concat([Prog, + " -detached -nopinput ", + Args, " ", + " -sname ", Name, "@", Host, + " -s ", ?MODULE, " slave_start ", node(), + " ", Waiter, + " ", PaPaths]))}. + + +%% This function will be invoked on the slave, using the -s option of erl. +%% It will wait for the master node to terminate. + +slave_start([Master, Waiter]) -> + spawn(?MODULE, wait_for_master_to_die, [Master, Waiter, silence]); +slave_start([Master, Waiter, Level]) -> + spawn(?MODULE, wait_for_master_to_die, [Master, Waiter, Level]). + + +wait_for_master_to_die(Master, Waiter, Level) -> + process_flag(trap_exit, true), + SName = lists:flatten( + io_lib:format("HDLT-SLAVE MASTER MONITOR[~p,~p,~p]", + [self(), node(), Master])), + ?SET_NAME(SName), + ?SET_LEVEL(Level), + erlang:monitor_node(Master, true), + {Waiter, Master} ! {self(), slave_started}, + wloop(Master). + +wloop(Master) -> + ?DEBUG("await message", []), + receive + {nodedown, Master} -> + ?INFO("received master nodedown", []), + halt(); + _Other -> + wloop(Master) + end. + + + diff --git a/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem b/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem new file mode 120000 index 0000000000..41644a1098 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem @@ -0,0 +1 @@ +../../test/httpc_SUITE_data/ssl_client_cert.pem
\ No newline at end of file diff --git a/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem b/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem new file mode 120000 index 0000000000..41644a1098 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem @@ -0,0 +1 @@ +../../test/httpc_SUITE_data/ssl_client_cert.pem
\ No newline at end of file diff --git a/lib/inets/examples/httpd_load_test/modules.mk b/lib/inets/examples/httpd_load_test/modules.mk new file mode 100644 index 0000000000..9d0d7103d5 --- /dev/null +++ b/lib/inets/examples/httpd_load_test/modules.mk @@ -0,0 +1,44 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode + +# %CopyrightBegin% +# +# Copyright Ericsson AB 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/. +# +# 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. +# +# %CopyrightEnd% + +SCRIPT_SKELETONS = \ + hdlt.sh.skel + +CONF_SKELETONS = \ + hdlt.config.skel + +CERT_FILES = \ + hdlt_ssl_client_cert.pem \ + hdlt_ssl_server_cert.pem + +README = HDLT_README + +MODULES = \ + hdlt \ + hdlt_ctrl \ + hdlt_client \ + hdlt_logger \ + hdlt_random_html \ + hdlt_server \ + hdlt_slave + +INTERNAL_HRL_FILES = \ + hdlt_logger.hrl + + diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile new file mode 100644 index 0000000000..d7a3231068 --- /dev/null +++ b/lib/inets/examples/server_root/Makefile @@ -0,0 +1,209 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-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/. +# +# 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. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(INETS_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +MODULE= + +AUTH_FILES = auth/group \ + auth/passwd +CGI_FILES = cgi-bin/printenv.sh +CONF_FILES = conf/8080.conf \ + conf/8888.conf \ + conf/httpd.conf \ + conf/ssl.conf \ + conf/mime.types +OPEN_FILES = htdocs/open/dummy.html +MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html +MISC_FILES = htdocs/misc/friedrich.html \ + htdocs/misc/oech.html +SECRET_FILES = htdocs/secret/dummy.html +MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html +HTDOCS_FILES = htdocs/index.html \ + htdocs/config.shtml \ + htdocs/echo.shtml \ + htdocs/exec.shtml \ + htdocs/flastmod.shtml \ + htdocs/fsize.shtml \ + htdocs/include.shtml +ICON_FILES = icons/README \ + icons/a.gif \ + icons/alert.black.gif \ + icons/alert.red.gif \ + icons/apache_pb.gif \ + icons/back.gif \ + icons/ball.gray.gif \ + icons/ball.red.gif \ + icons/binary.gif \ + icons/binhex.gif \ + icons/blank.gif \ + icons/bomb.gif \ + icons/box1.gif \ + icons/box2.gif \ + icons/broken.gif \ + icons/burst.gif \ + icons/button1.gif \ + icons/button10.gif \ + icons/button2.gif \ + icons/button3.gif \ + icons/button4.gif \ + icons/button5.gif \ + icons/button6.gif \ + icons/button7.gif \ + icons/button8.gif \ + icons/button9.gif \ + icons/buttonl.gif \ + icons/buttonr.gif \ + icons/c.gif \ + icons/comp.blue.gif \ + icons/comp.gray.gif \ + icons/compressed.gif \ + icons/continued.gif \ + icons/dir.gif \ + icons/down.gif \ + icons/dvi.gif \ + icons/f.gif \ + icons/folder.gif \ + icons/folder.open.gif \ + icons/folder.sec.gif \ + icons/forward.gif \ + icons/generic.gif \ + icons/generic.red.gif \ + icons/generic.sec.gif \ + icons/hand.right.gif \ + icons/hand.up.gif \ + icons/htdig.gif \ + icons/icon.sheet.gif \ + icons/image1.gif \ + icons/image2.gif \ + icons/image3.gif \ + icons/index.gif \ + icons/layout.gif \ + icons/left.gif \ + icons/link.gif \ + icons/movie.gif \ + icons/p.gif \ + icons/patch.gif \ + icons/pdf.gif \ + icons/pie0.gif \ + icons/pie1.gif \ + icons/pie2.gif \ + icons/pie3.gif \ + icons/pie4.gif \ + icons/pie5.gif \ + icons/pie6.gif \ + icons/pie7.gif \ + icons/pie8.gif \ + icons/portal.gif \ + icons/poweredby.gif \ + icons/ps.gif \ + icons/quill.gif \ + icons/right.gif \ + icons/screw1.gif \ + icons/screw2.gif \ + icons/script.gif \ + icons/sound1.gif \ + icons/sound2.gif \ + icons/sphere1.gif \ + icons/sphere2.gif \ + icons/star.gif \ + icons/star_blank.gif \ + icons/tar.gif \ + icons/tex.gif \ + icons/text.gif \ + icons/transfer.gif \ + icons/unknown.gif \ + icons/up.gif \ + icons/uu.gif \ + icons/uuencoded.gif \ + icons/world1.gif \ + icons/world2.gif + +SSL_FILES = ssl/ssl_client.pem \ + ssl/ssl_server.pem + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: + +clean: + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DATA) $(OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DATA) $(MISC_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + $(INSTALL_DATA) $(SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret + $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + +release_docs_spec: + diff --git a/lib/inets/examples/subdirs.mk b/lib/inets/examples/subdirs.mk new file mode 100644 index 0000000000..10a331fc26 --- /dev/null +++ b/lib/inets/examples/subdirs.mk @@ -0,0 +1,3 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode + +SUB_DIRECTORIES = server_root httpd_load_test
\ No newline at end of file diff --git a/lib/inets/include/httpd.hrl b/lib/inets/include/httpd.hrl new file mode 100644 index 0000000000..a7e63ca670 --- /dev/null +++ b/lib/inets/include/httpd.hrl @@ -0,0 +1,41 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(httpd_hrl). +-define(httpd_hrl, true). + +-include_lib("kernel/include/file.hrl"). + +-record(init_data,{peername,resolve}). + +-record(mod,{init_data, + data=[], + socket_type=ip_comm, + socket, + config_db, + method, + absolute_uri=[], + request_uri, + http_version, + request_line, + parsed_header=[], + entity_body, + connection}). +-endif. % -ifdef(httpd_hrl). diff --git a/lib/inets/include/mod_auth.hrl b/lib/inets/include/mod_auth.hrl new file mode 100644 index 0000000000..cf931e681a --- /dev/null +++ b/lib/inets/include/mod_auth.hrl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-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/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% + +-ifndef(mod_auth_hrl). +-define(mod_auth_hrl, true). + +-record(httpd_user, + {username, + password, + user_data}). + +-record(httpd_group, + {name, + userlist}). + +-endif. % -ifdef(mod_auth_hrl). diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile index 0c15277a18..19b93870df 100644 --- a/lib/inets/src/ftp/Makefile +++ b/lib/inets/src/ftp/Makefile @@ -22,6 +22,7 @@ include $(ERL_TOP)/make/target.mk EBIN = ../../ebin include $(ERL_TOP)/make/$(TARGET)/otp.mk + # ---------------------------------------------------- # Application version # ---------------------------------------------------- @@ -29,6 +30,7 @@ include ../../vsn.mk VSN = $(INETS_VSN) + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -52,24 +54,21 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- -# INETS FLAGS +# FLAGS # ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' + +include ../inets_app/inets.mk ifeq ($(FTP_DEBUG),true) INETS_FLAGS += -Dftp_debug endif +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) \ + -I../../include \ + -I../inets_app -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -INETS_ERL_FLAGS += -I ../inets_app -pa ../../ebin - -ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \ - $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' # ---------------------------------------------------- # Targets @@ -89,9 +88,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/ftp + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/ftp + $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 534fcae675..ac72963347 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -25,14 +25,12 @@ -behaviour(gen_server). -behaviour(inets_service). --deprecated({open, 3, next_major_release}). --deprecated({force_active, 1, next_major_release}). %% API - Client interface -export([cd/2, close/1, delete/2, formaterror/1, lcd/2, lpwd/1, ls/1, ls/2, mkdir/2, nlist/1, nlist/2, - open/1, open/2, open/3, force_active/1, + open/1, open/2, pwd/1, quote/2, recv/2, recv/3, recv_bin/2, recv_chunk_start/2, recv_chunk/1, @@ -94,6 +92,12 @@ }). +-type shortage_reason() :: 'etnospc' | 'epnospc'. +-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'. +-type common_reason() :: 'econn' | 'eclosed' | term(). +-type file_write_error_reason() :: term(). % See file:write for more info + + %%%========================================================================= %%% API - CLIENT FUNCTIONS %%%========================================================================= @@ -108,6 +112,9 @@ %% Description: Start an ftp client and connect to a host. %%-------------------------------------------------------------------------- +-spec open(Host :: string() | inet:ip_address()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open({option_list, Options}) when is_list(Options) -> try @@ -128,16 +135,14 @@ open({option_list, Options}) when is_list(Options) -> open(Host) -> open(Host, []). +-spec open(Host :: string() | inet:ip_address(), Opts :: list()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% <BACKWARD-COMPATIBILLITY> open(Host, Port) when is_integer(Port) -> open(Host, [{port, Port}]); %% </BACKWARD-COMPATIBILLITY> -%% <BACKWARD-COMPATIBILLITY> -open(Host, [H|_] = Flags) when is_atom(H) -> - open(Host, ?FTP_PORT, Flags); -%% </BACKWARD-COMPATIBILLITY> - open(Host, Opts) when is_list(Opts) -> ?fcrt("open", [{host, Host}, {opts, Opts}]), try @@ -160,32 +165,6 @@ open(Host, Opts) when is_list(Opts) -> end. -%% <BACKWARD-COMPATIBILLITY> -open(Host, Port, Flags) when is_integer(Port) andalso is_list(Flags) -> - ?fcrt("open", [{host, Host}, {port, Port}, {flags, Flags}]), - try - {ok, StartOptions} = start_options([{flags, Flags}]), - ?fcrt("open", [{start_options, StartOptions}]), - {ok, OpenOptions} = open_options([{host, Host}, {port, Port}|Flags]), - ?fcrt("open", [{open_options, OpenOptions}]), - case ftp_sup:start_child([[{client, self()} | StartOptions], []]) of - {ok, Pid} -> - ?fcrt("open - ok", [{pid, Pid}]), - call(Pid, {open, ip_comm, OpenOptions}, plain); - Error1 -> - ?fcrt("open - error", [{error1, Error1}]), - Error1 - end - catch - throw:Error2 -> - Error2 - end. -%% </BACKWARD-COMPATIBILLITY> - - - - - %%-------------------------------------------------------------------------- %% user(Pid, User, Pass, <Acc>) -> ok | {error, euser} | {error, econn} %% | {error, eacct} @@ -194,12 +173,24 @@ open(Host, Port, Flags) when is_integer(Port) andalso is_list(Flags) -> %% %% Description: Login with or without a supplied account name. %%-------------------------------------------------------------------------- +-spec user(Pid :: pid(), + User :: string(), + Pass :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass) -> call(Pid, {user, User, Pass}, atom). +-spec user(Pid :: pid(), + User :: string(), + Pass :: string(), + Acc :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass, Acc) -> call(Pid, {user, User, Pass, Acc}, atom). + %%-------------------------------------------------------------------------- %% account(Pid, Acc) -> ok | {error, eacct} %% Pid = pid() @@ -207,9 +198,14 @@ user(Pid, User, Pass, Acc) -> %% %% Description: Set a user Account. %%-------------------------------------------------------------------------- + +-spec account(Pid :: pid(), Acc :: string()) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + account(Pid, Acc) -> call(Pid, {account, Acc}, atom). + %%-------------------------------------------------------------------------- %% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn} %% Pid = pid() @@ -217,19 +213,30 @@ account(Pid, Acc) -> %% %% Description: Get the current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec pwd(Pid :: pid()) -> + {'ok', Dir :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + pwd(Pid) -> call(Pid, pwd, ctrl). + %%-------------------------------------------------------------------------- -%% lpwd(Pid) -> {ok, Dir} | {error, elogin} +%% lpwd(Pid) -> {ok, Dir} %% Pid = pid() %% Dir = string() %% %% Description: Get the current working directory at local server. %%-------------------------------------------------------------------------- + +-spec lpwd(Pid :: pid()) -> + {'ok', Dir :: string()}. + lpwd(Pid) -> call(Pid, lpwd, string). + %%-------------------------------------------------------------------------- %% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid() @@ -237,9 +244,14 @@ lpwd(Pid) -> %% %% Description: Change current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec cd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + cd(Pid, Dir) -> call(Pid, {cd, Dir}, atom). + %%-------------------------------------------------------------------------- %% lcd(Pid, Dir) -> ok | {error, epath} %% Pid = pid() @@ -247,9 +259,14 @@ cd(Pid, Dir) -> %% %% Description: Change current working directory for the local client. %%-------------------------------------------------------------------------- + +-spec lcd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason()}. + lcd(Pid, Dir) -> call(Pid, {lcd, Dir}, string). + %%-------------------------------------------------------------------------- %% ls(Pid) -> Result %% ls(Pid, <Dir>) -> Result @@ -262,11 +279,22 @@ lcd(Pid, Dir) -> %% %% Description: Returns a list of files in long format. %%-------------------------------------------------------------------------- + +-spec ls(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid) -> ls(Pid, ""). + +-spec ls(Pid :: pid(), Dir :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid, Dir) -> call(Pid, {dir, long, Dir}, string). + %%-------------------------------------------------------------------------- %% nlist(Pid) -> Result %% nlist(Pid, Pathname) -> Result @@ -279,21 +307,37 @@ ls(Pid, Dir) -> %% %% Description: Returns a list of files in short format %%-------------------------------------------------------------------------- + +-spec nlist(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid) -> nlist(Pid, ""). + +-spec nlist(Pid :: pid(), Pathname :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid, Dir) -> call(Pid, {dir, short, Dir}, string). + %%-------------------------------------------------------------------------- -%% rename(Pid, CurrFile, NewFile) -> ok | {error, epath} | {error, elogin} -%% | {error, econn} +%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin} +%% | {error, econn} %% Pid = pid() %% CurrFile = NewFile = string() %% %% Description: Rename a file at remote server. %%-------------------------------------------------------------------------- -rename(Pid, CurrFile, NewFile) -> - call(Pid, {rename, CurrFile, NewFile}, string). + +-spec rename(Pid :: pid(), Old :: string(), New :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + +rename(Pid, Old, New) -> + call(Pid, {rename, Old, New}, string). + %%-------------------------------------------------------------------------- %% delete(Pid, File) -> ok | {error, epath} | {error, elogin} | @@ -303,9 +347,14 @@ rename(Pid, CurrFile, NewFile) -> %% %% Description: Remove file at remote server. %%-------------------------------------------------------------------------- + +-spec delete(Pid :: pid(), File :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + delete(Pid, File) -> call(Pid, {delete, File}, string). + %%-------------------------------------------------------------------------- %% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -313,9 +362,14 @@ delete(Pid, File) -> %% %% Description: Make directory at remote server. %%-------------------------------------------------------------------------- + +-spec mkdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + mkdir(Pid, Dir) -> call(Pid, {mkdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -323,9 +377,14 @@ mkdir(Pid, Dir) -> %% %% Description: Remove directory at remote server. %%-------------------------------------------------------------------------- + +-spec rmdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + rmdir(Pid, Dir) -> call(Pid, {rmdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn} %% Pid = pid() @@ -333,23 +392,41 @@ rmdir(Pid, Dir) -> %% %% Description: Set transfer type. %%-------------------------------------------------------------------------- + +-spec type(Pid :: pid(), Type :: ascii | binary) -> + 'ok' | + {'error', Reason :: 'etype' | restriction_reason() | common_reason()}. + type(Pid, Type) -> call(Pid, {type, Type}, atom). + %%-------------------------------------------------------------------------- -%% recv(Pid, RemoteFileName <LocalFileName>) -> ok | {error, epath} | +%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} | %% {error, elogin} | {error, econn} %% Pid = pid() %% RemoteFileName = LocalFileName = string() %% %% Description: Transfer file from remote server. %%-------------------------------------------------------------------------- + +-spec recv(Pid :: pid(), RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | + common_reason() | + file_write_error_reason()}. + recv(Pid, RemotFileName) -> recv(Pid, RemotFileName, RemotFileName). +-spec recv(Pid :: pid(), + RemoteFileName :: string(), + LocalFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + recv(Pid, RemotFileName, LocalFileName) -> call(Pid, {recv, RemotFileName, LocalFileName}, atom). + %%-------------------------------------------------------------------------- %% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin} %% | {error, econn} @@ -359,9 +436,16 @@ recv(Pid, RemotFileName, LocalFileName) -> %% %% Description: Transfer file from remote server into binary. %%-------------------------------------------------------------------------- + +-spec recv_bin(Pid :: pid(), + RemoteFile :: string()) -> + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_bin(Pid, RemoteFile) -> call(Pid, {recv_bin, RemoteFile}, bin). + %%-------------------------------------------------------------------------- %% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -370,9 +454,15 @@ recv_bin(Pid, RemoteFile) -> %% %% Description: Start receive of chunks of remote file. %%-------------------------------------------------------------------------- + +-spec recv_chunk_start(Pid :: pid(), + RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk_start(Pid, RemoteFile) -> call(Pid, {recv_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason} %% Pid = pid() @@ -380,24 +470,47 @@ recv_chunk_start(Pid, RemoteFile) -> %% %% Description: Transfer file from remote server into binary in chunks %%-------------------------------------------------------------------------- + +-spec recv_chunk(Pid :: pid()) -> + 'ok' | + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk(Pid) -> call(Pid, recv_chunk, atom). + %%-------------------------------------------------------------------------- -%% send(Pid, LocalFileName <RemotFileName>) -> ok | {error, epath} -%% | {error, elogin} -%% | {error, econn} +%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Transfer file to remote server. %%-------------------------------------------------------------------------- + +-spec send(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName) -> send(Pid, LocalFileName, LocalFileName). +-spec send(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName, RemotFileName) -> call(Pid, {send, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -407,11 +520,19 @@ send(Pid, LocalFileName, RemotFileName) -> %% %% Description: Transfer a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {send_bin, Bin, RemoteFile}, atom); send_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -420,9 +541,14 @@ send_bin(_Pid, _Bin, _RemoteFile) -> %% %% Description: Start transfer of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + send_chunk_start(Pid, RemoteFile) -> call(Pid, {send_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | %% {error, epath} | {error, econn} @@ -431,9 +557,14 @@ send_chunk_start(Pid, RemoteFile) -> %% %% Description: Start append chunks of data to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: term()}. + append_chunk_start(Pid, RemoteFile) -> call(Pid, {append_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -442,11 +573,19 @@ append_chunk_start(Pid, RemoteFile) -> %% %% Purpose: Send chunk to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + send_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); send_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -455,11 +594,19 @@ send_chunk(_Pid, _Bin) -> %% %% Description: Append chunk to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + append_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); append_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -467,9 +614,17 @@ append_chunk(_Pid, _Bin) -> %% %% Description: End sending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- %% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -477,23 +632,47 @@ send_chunk_end(Pid) -> %% %% Description: End appending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- -%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} -%% | {error, elogin} | {error, econn} +%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Append the local file to the remote file %%-------------------------------------------------------------------------- + +-spec append(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: 'epath' | + 'elogin' | + 'etnospc' | + 'epnospc' | + 'efnamena' | common_reason()}. + append(Pid, LocalFileName) -> append(Pid, LocalFileName, LocalFileName). +-spec append(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + append(Pid, LocalFileName, RemotFileName) -> call(Pid, {append, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -503,41 +682,48 @@ append(Pid, LocalFileName, RemotFileName) -> %% %% Purpose: Append a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec append_bin(Pid :: pid(), + Bin :: binary(), + RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {append_bin, Bin, RemoteFile}, atom); append_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- -%% quote(Pid, Cmd) -> ok +%% quote(Pid, Cmd) -> list() %% Pid = pid() %% Cmd = string() %% %% Description: Send arbitrary ftp command. %%-------------------------------------------------------------------------- + +-spec quote(Pid :: pid(), Cmd :: string()) -> list(). + quote(Pid, Cmd) when is_list(Cmd) -> call(Pid, {quote, Cmd}, atom). + %%-------------------------------------------------------------------------- %% close(Pid) -> ok %% Pid = pid() %% %% Description: End the ftp session. %%-------------------------------------------------------------------------- + +-spec close(Pid :: pid()) -> 'ok'. + close(Pid) -> cast(Pid, close), ok. -%%-------------------------------------------------------------------------- -%% force_active(Pid) -> ok -%% Pid = pid() -%% -%% Description: Force connection to use active mode. -%%-------------------------------------------------------------------------- -force_active(Pid) -> - error_logger:info_report("This function is deprecated use the mode flag " - "instead"), - call(Pid, force_active, atom). %%-------------------------------------------------------------------------- %% formaterror(Tag) -> string() @@ -545,9 +731,13 @@ force_active(Pid) -> %% %% Description: Return diagnostics. %%-------------------------------------------------------------------------- + +-spec formaterror(Tag :: term()) -> string(). + formaterror(Tag) -> ftp_response:error_string(Tag). + info(Pid) -> call(Pid, info, list). @@ -848,10 +1038,12 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> Port = key_search(port, Opts, ?FTP_PORT), Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), Progress = key_search(progress, Opts, ignore), + IpFamily = key_search(ipfamily, Opts, inet), State2 = State#state{client = From, mode = Mode, - progress = progress(Progress)}, + progress = progress(Progress), + ipfamily = IpFamily}, ?fcrd("handle_call(open) -> setup ctrl connection with", [{host, Host}, {port, Port}, {timeout, Timeout}]), @@ -886,9 +1078,6 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> {stop, normal, State2#state{client = undefined}} end; -handle_call({_, force_active}, _, State) -> - {reply, ok, State#state{mode = active}}; - handle_call({_, {user, User, Password}}, From, #state{csock = CSock} = State) when (CSock =/= undefined) -> handle_user(User, Password, "", State#state{client = From}); diff --git a/lib/inets/src/ftp/ftp_internal.hrl b/lib/inets/src/ftp/ftp_internal.hrl index c3fa1e611d..148f8217ba 100644 --- a/lib/inets/src/ftp/ftp_internal.hrl +++ b/lib/inets/src/ftp/ftp_internal.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -21,7 +21,8 @@ -ifndef(ftp_internal_hrl). -define(ftp_internal_hrl, true). --include("inets_internal.hrl"). +-include_lib("inets/src/inets_app/inets_internal.hrl"). + -define(SERVICE, ftpc). -define(fcri(Label, Content), ?report_important(Label, ?SERVICE, Content)). -define(fcrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)). diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile index 184f45f589..0397b48ab2 100644 --- a/lib/inets/src/http_client/Makefile +++ b/lib/inets/src/http_client/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2011. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -50,7 +50,7 @@ MODULES = \ httpc_handler_sup \ httpc_profile_sup \ httpc_response \ - httpc_request + httpc_request \ HRL_FILES = httpc_internal.hrl @@ -60,20 +60,17 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- -# INETS FLAGS -# ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - - -# ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -INETS_ERL_FLAGS += -I ../http_lib -I ../inets_app -pa ../../ebin -ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \ - $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' +include ../inets_app/inets.mk + +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) \ + -I../../include \ + -I../inets_app \ + -I../http_lib # ---------------------------------------------------- @@ -93,9 +90,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/http_client + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_client + $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/inets/src/http_client/http.erl b/lib/inets/src/http_client/http.erl index 7e1e90b50e..bbe2fec267 100644 --- a/lib/inets/src/http_client/http.erl +++ b/lib/inets/src/http_client/http.erl @@ -18,21 +18,38 @@ %% %% -%% Description: -%%% This version of the HTTP/1.1 client supports: -%%% - RFC 2616 HTTP 1.1 client part -%%% - RFC 2818 HTTP Over TLS +%%% Description: OLD API MODULE - USE httpc INSTEAD -module(http). -%% API --export([request/1, request/2, request/4, request/5, +-deprecated({request, 1, next_major_release}). +-deprecated({request, 2, next_major_release}). +-deprecated({request, 4, next_major_release}). +-deprecated({request, 5, next_major_release}). +-deprecated({cancel_request, 1, next_major_release}). +-deprecated({cancel_request, 2, next_major_release}). +-deprecated({set_option, 2, next_major_release}). +-deprecated({set_option, 3, next_major_release}). +-deprecated({set_options, 1, next_major_release}). +-deprecated({set_options, 2, next_major_release}). +-deprecated({verify_cookies, 2, next_major_release}). +-deprecated({verify_cookies, 3, next_major_release}). +-deprecated({cookie_header, 1, next_major_release}). +-deprecated({cookie_header, 2, next_major_release}). +-deprecated({stream_next, 1, next_major_release}). +-deprecated({default_profile, 0, next_major_release}). + +%% Deprecated +-export([ + request/1, request/2, request/4, request/5, cancel_request/1, cancel_request/2, set_option/2, set_option/3, set_options/1, set_options/2, - verify_cookies/2, verify_cookies/3, cookie_header/1, - cookie_header/2, stream_next/1, - default_profile/0]). + verify_cookies/2, verify_cookies/3, + cookie_header/1, cookie_header/2, + stream_next/1, + default_profile/0 + ]). %%%========================================================================= diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index ca186f46a7..fe8e93af1f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -48,7 +48,7 @@ stop_service/1, services/0, service_info/1]). --include("http_internal.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). -define(DEFAULT_PROFILE, default). @@ -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. %%-------------------------------------------------------------------------- @@ -104,14 +103,22 @@ request(Url, Profile) -> %% HTTPOptions - [HttpOption] %% HTTPOption - {timeout, Time} | {connect_timeout, Time} | %% {ssl, SSLOptions} | {proxy_auth, {User, Password}} -%% Ssloptions = [SSLOption] -%% SSLOption = {verify, code()} | {depth, depth()} | {certfile, path()} | +%% Ssloptions = ssl_options() | +%% {ssl, ssl_options()} | +%% {ossl, ssl_options()} | +%% {essl, ssl_options()} +%% ssl_options() = [ssl_option()] +%% ssl_option() = {verify, code()} | +%% {depth, depth()} | +%% {certfile, path()} | %% {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() @@ -120,7 +127,10 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} | +%% {chunkify, fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% SendFunResult = eof | {ok, iolist(), NewSendAcc} +%% SendAcc = NewSendAcc = term() %% %% Description: Sends a HTTP-request. The function can be both %% syncronus and asynchronous in the later case the function will @@ -246,7 +256,7 @@ set_option(Key, Value, Profile) -> %% Description: Store the cookies from <SetCookieHeaders> %% in the cookie database %% for the profile <Profile>. This function shall be used when the option -%% cookie is set to verify. +%% cookies is set to verify. %%------------------------------------------------------------------------- store_cookies(SetCookieHeaders, Url) -> store_cookies(SetCookieHeaders, Url, default_profile()). @@ -420,14 +430,33 @@ service_info(Pid) -> handle_request(Method, Url, {Scheme, UserInfo, Host, Port, Path, Query}, - Headers, ContentType, Body, + Headers0, ContentType, Body0, HTTPOptions0, Options0, Profile) -> - Started = http_util:timestamp(), - NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers], + Started = http_util:timestamp(), + NewHeaders0 = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers0], try begin + ?hcrt("begin processing", [{started, Started}, + {new_headers, NewHeaders0}]), + + {NewHeaders, Body} = + case Body0 of + {chunkify, ProcessBody, Acc} + when is_function(ProcessBody, 1) -> + NewHeaders1 = ensure_chunked_encoding(NewHeaders0), + Body1 = {mk_chunkify_fun(ProcessBody), Acc}, + {NewHeaders1, Body1}; + {ProcessBody, _} + when is_function(ProcessBody, 1) -> + {NewHeaders0, Body0}; + _ when is_list(Body0) orelse is_binary(Body0) -> + {NewHeaders0, Body0}; + _ -> + throw({error, {bad_body, Body0}}) + end, + HTTPOptions = http_options(HTTPOptions0), Options = request_options(Options0), Sync = proplists:get_value(sync, Options), @@ -436,52 +465,84 @@ handle_request(Method, Url, HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions), Receiver = proplists:get_value(receiver, Options), SocketOpts = proplists:get_value(socket_opts, Options), - MaybeEscPath = maybe_url_encode(HTTPOptions, Path), - MaybeEscQuery = maybe_url_encode(HTTPOptions, Query), - AbsUri = maybe_url_encode(HTTPOptions, Url), + MaybeEscPath = maybe_encode_uri(HTTPOptions, Path), + MaybeEscQuery = maybe_encode_uri(HTTPOptions, Query), + AbsUri = maybe_encode_uri(HTTPOptions, Url), + Request = #request{from = Receiver, scheme = Scheme, address = {Host, Port}, - path = MaybeEscPath, - pquery = MaybeEscQuery, + path = MaybeEscPath, + pquery = MaybeEscQuery, method = Method, headers = HeadersRecord, content = {ContentType, Body}, settings = HTTPOptions, - abs_uri = AbsUri, + abs_uri = AbsUri, userinfo = UserInfo, stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, started = Started}, + case httpc_manager:request(Request, profile_name(Profile)) of {ok, RequestId} -> handle_answer(RequestId, Sync, Options); {error, Reason} -> + ?hcrd("request failed", [{reason, Reason}]), {error, Reason} end end catch error:{noproc, _} -> + ?hcrv("noproc", [{profile, Profile}]), {error, {not_started, Profile}}; throw:Error -> + ?hcrv("throw", [{error, Error}]), Error end. -maybe_url_encode(#http_options{url_encode = true}, URI) -> +ensure_chunked_encoding(Hdrs) -> + Key = "transfer-encoding", + lists:keystore(Key, 1, Hdrs, {Key, "chunked"}). + +maybe_encode_uri(#http_options{url_encode = true}, URI) -> http_uri:encode(URI); -maybe_url_encode(_, URI) -> +maybe_encode_uri(_, URI) -> URI. +mk_chunkify_fun(ProcessBody) -> + fun(eof_body) -> + eof; + (Acc) -> + case ProcessBody(Acc) of + eof -> + {ok, <<"0\r\n\r\n">>, eof_body}; + {ok, Data, NewAcc} -> + Chunk = [ + integer_to_list(iolist_size(Data), 16), + "\r\n", + Data, + "\r\n"], + {ok, Chunk, NewAcc} + end + end. + + handle_answer(RequestId, false, _) -> {ok, RequestId}; handle_answer(RequestId, true, Options) -> receive {http, {RequestId, saved_to_file}} -> + ?hcrt("received saved-to-file", [{request_id, RequestId}]), {ok, saved_to_file}; {http, {RequestId, {_,_,_} = Result}} -> + ?hcrt("received answer", [{request_id, RequestId}, + {result, Result}]), return_answer(Options, Result); {http, {RequestId, {error, Reason}}} -> + ?hcrt("received error", [{request_id, RequestId}, + {reason, Reason}]), {error, Reason} end. @@ -490,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}}; @@ -579,14 +638,16 @@ http_options_default() -> (_) -> error end, - AutoRedirectPost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + AutoRedirectPost = boolfun(), + SslPost = fun(Value) when is_list(Value) -> - {ok, Value}; + {ok, {?HTTP_DEFAULT_SSL_KIND, Value}}; + ({ssl, SslOptions}) when is_list(SslOptions) -> + {ok, {?HTTP_DEFAULT_SSL_KIND, SslOptions}}; + ({ossl, SslOptions}) when is_list(SslOptions) -> + {ok, {ossl, SslOptions}}; + ({essl, SslOptions}) when is_list(SslOptions) -> + {ok, {essl, SslOptions}}; (_) -> error end, @@ -596,12 +657,8 @@ http_options_default() -> (_) -> error end, - RelaxedPost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + RelaxedPost = boolfun(), + ConnTimeoutPost = fun(Value) when is_integer(Value) andalso (Value >= 0) -> {ok, Value}; @@ -610,33 +667,30 @@ http_options_default() -> (_) -> error end, - - UrlDecodePost = fun(Value) when (Value =:= true) orelse - (Value =:= false) -> - {ok, Value}; - (_) -> - error - end, + + UrlDecodePost = boolfun(), [ - {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost}, - {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost}, - {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost}, - {ssl, {value, []}, #http_options.ssl, SslPost}, - {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost}, - {relaxed, {value, false}, #http_options.relaxed, RelaxedPost}, - %% this field has to be *after* the timeout field (as that field is used for the default value) - {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}, - {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost} + {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost}, + {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost}, + {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost}, + {ssl, {value, {?HTTP_DEFAULT_SSL_KIND, []}}, #http_options.ssl, SslPost}, + {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost}, + {relaxed, {value, false}, #http_options.relaxed, RelaxedPost}, + {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost}, + %% this field has to be *after* the timeout option (as that field is used for the default value) + {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost} ]. +boolfun() -> + fun(Value) when (Value =:= true) orelse + (Value =:= false) -> + {ok, Value}; + (_) -> + error + end. request_options_defaults() -> - VerifyBoolean = - fun(Value) when ((Value =:= true) orelse (Value =:= false)) -> - ok; - (_) -> - error - end, + VerifyBoolean = boolfun(), VerifySync = VerifyBoolean, diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl index 4d61f82b5a..69900bae65 100644 --- a/lib/inets/src/http_client/httpc_cookie.erl +++ b/lib/inets/src/http_client/httpc_cookie.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% 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 @@ -18,12 +18,32 @@ %% %% Description: Cookie handling according to RFC 2109 +%% The syntax for the Set-Cookie response header is +%% +%% set-cookie = "Set-Cookie:" cookies +%% cookies = 1#cookie +%% cookie = NAME "=" VALUE *(";" cookie-av) +%% NAME = attr +%% VALUE = value +%% cookie-av = "Comment" "=" value +%% | "Domain" "=" value +%% | "Max-Age" "=" value +%% | "Path" "=" value +%% | "Secure" +%% | "Version" "=" 1*DIGIT + + +%% application:start(inets). +%% httpc:set_options([{cookies, enabled}, {proxy, {{"www-proxy.ericsson.se",8080}, ["*.ericsson.se"]}}]). +%% (catch httpc:request("http://www.expedia.com")). + -module(httpc_cookie). -include("httpc_internal.hrl"). -export([open_db/3, close_db/1, insert/2, header/4, cookies/3]). -export([reset_db/1, which_cookies/1]). +-export([image_of/2, print/2]). -record(cookie_db, {db, session_db}). @@ -125,7 +145,7 @@ insert(#cookie_db{db = Db} = CookieDb, name = Name, path = Path, max_age = 0}) -> - ?hcrt("insert", [{domain, Key}, {name, Name}, {path, Path}]), + ?hcrt("insert cookie", [{domain, Key}, {name, Name}, {path, Path}]), Pattern = #http_cookie{domain = Key, name = Name, path = Path, _ = '_'}, case dets:match_object(Db, Pattern) of [] -> @@ -136,7 +156,7 @@ insert(#cookie_db{db = Db} = CookieDb, ok; insert(#cookie_db{db = Db} = CookieDb, #http_cookie{domain = Key, name = Name, path = Path} = Cookie) -> - ?hcrt("insert", [{cookie, Cookie}]), + ?hcrt("insert cookie", [{cookie, Cookie}]), Pattern = #http_cookie{domain = Key, name = Name, path = Path, @@ -163,6 +183,7 @@ header(CookieDb, Scheme, {Host, _}, Path) -> [] -> {"cookie", ""}; Cookies -> + %% print_cookies("Header Cookies", Cookies), {"cookie", cookies_to_string(Scheme, Cookies)} end. @@ -173,11 +194,20 @@ header(CookieDb, Scheme, {Host, _}, Path) -> %%-------------------------------------------------------------------- cookies(Headers, RequestPath, RequestHost) -> + ?hcrt("cookies", [{headers, Headers}, {request_path, RequestPath}, {request_host, RequestHost}]), + Cookies = parse_set_cookies(Headers, {RequestPath, RequestHost}), - accept_cookies(Cookies, RequestPath, RequestHost). + + %% print_cookies("Parsed Cookies", Cookies), + + AcceptedCookies = accept_cookies(Cookies, RequestPath, RequestHost), + + %% print_cookies("Accepted Cookies", AcceptedCookies), + + AcceptedCookies. %%-------------------------------------------------------------------- @@ -266,7 +296,8 @@ cookies_to_string(_, [], CookieStrs) -> lists:flatten(lists:reverse(CookieStrs)) end; -cookies_to_string(https, [#http_cookie{secure = true} = Cookie| Cookies], +cookies_to_string(https = Scheme, + [#http_cookie{secure = true} = Cookie| Cookies], CookieStrs) -> Str = case Cookies of [] -> @@ -274,7 +305,7 @@ cookies_to_string(https, [#http_cookie{secure = true} = Cookie| Cookies], _ -> cookie_to_string(Cookie) ++ "; " end, - cookies_to_string(https, Cookies, [Str | CookieStrs]); + cookies_to_string(Scheme, Cookies, [Str | CookieStrs]); cookies_to_string(Scheme, [#http_cookie{secure = true}| Cookies], CookieStrs) -> @@ -303,63 +334,54 @@ add_domain(Str, #http_cookie{domain_default = true}) -> add_domain(Str, #http_cookie{domain = Domain}) -> Str ++ "; $Domain=" ++ Domain. -parse_set_cookies(OtherHeaders, DefaultPathDomain) -> - SetCookieHeaders = - lists:foldl(fun({"set-cookie", Value}, Acc) -> - [string:tokens(Value, ",")| Acc]; - (_, Acc) -> - Acc - end, [], OtherHeaders), - - lists:flatten( - lists:map(fun(CookieHeader) -> - NewHeader = fix_netscape_cookie(CookieHeader, []), - parse_set_cookie(NewHeader, [], DefaultPathDomain) - end, - SetCookieHeaders)). - -parse_set_cookie([], AccCookies, _) -> - AccCookies; -parse_set_cookie([CookieHeader | CookieHeaders], AccCookies, - Defaults = {DefaultPath, DefaultDomain}) -> - [CookieStr | Attributes] = case string:tokens(CookieHeader, ";") of - [CStr] -> - [CStr, ""]; - [CStr | Attr] -> - [CStr, Attr] - end, - Pos = string:chr(CookieStr, $=), - Name = string:substr(CookieStr, 1, Pos - 1), - Value = string:substr(CookieStr, Pos + 1), - Cookie = #http_cookie{name = string:strip(Name), - value = string:strip(Value)}, - NewAttributes = parse_set_cookie_attributes(Attributes), - TmpCookie = cookie_attributes(NewAttributes, Cookie), +parse_set_cookies(CookieHeaders, DefaultPathDomain) -> + SetCookieHeaders = [Value || {"set-cookie", Value} <- CookieHeaders], + Cookies = [parse_set_cookie(SetCookieHeader, DefaultPathDomain) || + SetCookieHeader <- SetCookieHeaders], + %% print_cookies("Parsed Cookies", Cookies), + Cookies. + +parse_set_cookie(CookieHeader, {DefaultPath, DefaultDomain}) -> + %% io:format("Raw Cookie: ~s~n", [CookieHeader]), + Pos = string:chr(CookieHeader, $=), + Name = string:substr(CookieHeader, 1, Pos - 1), + {Value, Attrs} = + case string:substr(CookieHeader, Pos + 1) of + [$;|ValueAndAttrs] -> + {"", string:tokens(ValueAndAttrs, ";")}; + ValueAndAttrs -> + [V | A] = string:tokens(ValueAndAttrs, ";"), + {V, A} + end, + Cookie = #http_cookie{name = string:strip(Name), + value = string:strip(Value)}, + Attributes = parse_set_cookie_attributes(Attrs), + TmpCookie = cookie_attributes(Attributes, Cookie), %% Add runtime defult values if necessary - NewCookie = domain_default(path_default(TmpCookie, DefaultPath), - DefaultDomain), - parse_set_cookie(CookieHeaders, [NewCookie | AccCookies], Defaults). - -parse_set_cookie_attributes([]) -> - []; -parse_set_cookie_attributes([Attributes]) -> - lists:map(fun(Attr) -> - [AttrName, AttrValue] = - case string:tokens(Attr, "=") of - %% All attributes have the form - %% Name=Value except "secure"! - [Name] -> - [Name, ""]; - [Name, Value] -> - [Name, Value]; - %% Anything not expected will be - %% disregarded - _ -> - ["Dummy",""] - end, - {http_util:to_lower(string:strip(AttrName)), - string:strip(AttrValue)} - end, Attributes). + NewCookie = domain_default(path_default(TmpCookie, DefaultPath), + DefaultDomain), + NewCookie. + +parse_set_cookie_attributes(Attributes) when is_list(Attributes) -> + [parse_set_cookie_attribute(A) || A <- Attributes]. + +parse_set_cookie_attribute(Attribute) -> + {AName, AValue} = + case string:tokens(Attribute, "=") of + %% All attributes have the form + %% Name=Value except "secure"! + [Name] -> + {Name, ""}; + [Name, Value] -> + {Name, Value}; + %% Anything not expected will be + %% disregarded + _ -> + {"Dummy", ""} + end, + StrippedName = http_util:to_lower(string:strip(AName)), + StrippedValue = string:strip(AValue), + {StrippedName, StrippedValue}. cookie_attributes([], Cookie) -> Cookie; @@ -375,10 +397,15 @@ cookie_attributes([{"max-age", Value}| Attributes], Cookie) -> Cookie#http_cookie{max_age = ExpireTime}); %% Backwards compatibility with netscape cookies cookie_attributes([{"expires", Value}| Attributes], Cookie) -> - Time = http_util:convert_netscapecookie_date(Value), - ExpireTime = calendar:datetime_to_gregorian_seconds(Time), - cookie_attributes(Attributes, - Cookie#http_cookie{max_age = ExpireTime}); + try http_util:convert_netscapecookie_date(Value) of + Time -> + ExpireTime = calendar:datetime_to_gregorian_seconds(Time), + cookie_attributes(Attributes, + Cookie#http_cookie{max_age = ExpireTime}) + catch + _:_ -> + cookie_attributes(Attributes, Cookie) + end; cookie_attributes([{"path", Value}| Attributes], Cookie) -> cookie_attributes(Attributes, Cookie#http_cookie{path = Value}); @@ -404,7 +431,7 @@ path_default(#http_cookie{path = undefined} = Cookie, DefaultPath) -> path_default(Cookie, _) -> Cookie. -%% Note: if the path is only / that / will be keept +%% Note: if the path is only / that / will be kept skip_right_most_slash("/") -> "/"; skip_right_most_slash(Str) -> @@ -476,20 +503,43 @@ path_sort(Cookies)-> lists:reverse(lists:keysort(#http_cookie.path, Cookies)). -%% Informally, the Set-Cookie response header comprises the token -%% Set-Cookie:, followed by a comma-separated list of one or more -%% cookies. Netscape cookies expires attribute may also have a, -%% in this case the header list will have been incorrectly split -%% in parse_set_cookies/2 this functions fix that problem. -fix_netscape_cookie([Cookie1, Cookie2 | Rest], Acc) -> - case inets_regexp:match(string:to_lower(Cookie1), "expires=") of - {_, _, _} -> - fix_netscape_cookie(Rest, [Cookie1 ++ Cookie2 | Acc]); - nomatch -> - fix_netscape_cookie([Cookie2 |Rest], [Cookie1| Acc]) - end; -fix_netscape_cookie([Cookie | Rest], Acc) -> - fix_netscape_cookie(Rest, [Cookie | Acc]); - -fix_netscape_cookie([], Acc) -> - Acc. +%% print_cookies(Header, Cookies) -> +%% io:format("~s:~n", [Header]), +%% Prefix = " ", +%% lists:foreach(fun(Cookie) -> print(Prefix, Cookie) end, Cookies). + +image_of(Prefix, + #http_cookie{domain = Domain, + domain_default = DomainDef, + name = Name, + value = Value, + comment = Comment, + max_age = MaxAge, + path = Path, + path_default = PathDef, + secure = Sec, + version = Version}) -> + lists:flatten( + io_lib:format("~sCookie ~s: " + "~n~s Value: ~p" + "~n~s Domain: ~p" + "~n~s DomainDef: ~p" + "~n~s Comment: ~p" + "~n~s MaxAge: ~p" + "~n~s Path: ~p" + "~n~s PathDef: ~p" + "~n~s Secure: ~p" + "~n~s Version: ~p", + [Prefix, Name, + Prefix, Value, + Prefix, Domain, + Prefix, DomainDef, + Prefix, Comment, + Prefix, MaxAge, + Prefix, Path, + Prefix, PathDef, + Prefix, Sec, + Prefix, Version])). + +print(Prefix, Cookie) when is_record(Cookie, http_cookie) -> + io:format("~s~n", [image_of(Prefix, Cookie)]). diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 5e5a9ce32e..587e24cc8d 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -22,17 +22,17 @@ -behaviour(gen_server). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). --include("http_internal.hrl"). %%-------------------------------------------------------------------- %% Internal Application API -export([ - start_link/2, - connect_and_send/2, + start_link/4, + %% connect_and_send/2, send/2, - cancel/2, + cancel/3, stream/3, stream_next/1, info/1 @@ -51,7 +51,7 @@ -record(state, { request, % #request{} - session, % #tcp_session{} + session, % #session{} status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() @@ -94,13 +94,9 @@ %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -start_link(Options, ProfileName) -> - Args = [Options, ProfileName], - gen_server:start_link(?MODULE, Args, []). - -connect_and_send(Request, HandlerPid) -> - call({connect_and_send, Request}, HandlerPid). - +start_link(Parent, Request, Options, ProfileName) -> + {ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options, + ProfileName]])}. %%-------------------------------------------------------------------- %% Function: send(Request, Pid) -> ok @@ -122,8 +118,8 @@ send(Request, Pid) -> %% Description: Cancels a request. Intended to be called by the httpc %% manager process. %%-------------------------------------------------------------------- -cancel(RequestId, Pid) -> - cast({cancel, RequestId}, Pid). +cancel(RequestId, Pid, From) -> + cast({cancel, RequestId, From}, Pid). %%-------------------------------------------------------------------- @@ -177,8 +173,8 @@ stream(BodyPart, Request = #request{stream = Self}, Code) stream(BodyPart, Request = #request{stream = Self}, 404) when (Self =:= self) orelse (Self =:= {self, once}) -> ?hcrt("stream - self with 404", [{stream, Self}]), - httpc_response:send(Request#request.from, - {Request#request.id, stream, BodyPart}), + httpc_response:send(Request#request.from, + {Request#request.id, stream, BodyPart}), {<<>>, Request}; %% Stream to file @@ -229,16 +225,27 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed %% but we do not want that so errors will be handled by the process %% sending an init_error message to itself. %%-------------------------------------------------------------------- -init([Options, ProfileName]) -> - ?hcrv("init - starting", [{options, Options}, {profile, ProfileName}]), +init([Parent, Request, Options, ProfileName]) -> process_flag(trap_exit, true), - handle_verbose(Options#options.verbose), - State = #state{status = undefined, - options = Options, - profile_name = ProfileName}, - ?hcrd("init - started", []), - {ok, State}. + %% Do not let initial tcp-connection block the manager-process + proc_lib:init_ack(Parent, self()), + handle_verbose(Options#options.verbose), + Address = handle_proxy(Request#request.address, Options#options.proxy), + {ok, State} = + case {Address /= Request#request.address, Request#request.scheme} of + {true, https} -> + Error = https_through_proxy_is_not_currently_supported, + self() ! {init_error, + Error, httpc_response:error(Request, Error)}, + {ok, #state{request = Request, options = Options, + status = ssl_tunnel}}; + {_, _} -> + connect_and_send_first_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}) + end, + gen_server:enter_loop(?MODULE, [], State). %%-------------------------------------------------------------------- %% Function: handle_call(Request, From, State) -> {reply, Reply, State} | @@ -249,45 +256,9 @@ init([Options, ProfileName]) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- - - -%% This is the first request, the reason the proc was started -handle_call({connect_and_send, #request{address = Address0, - scheme = Scheme} = Request}, - _From, - #state{options = #options{proxy = Proxy}, - status = undefined, - session = undefined} = State) -> - ?hcrv("connect and send", [{address0, Address0}, {proxy, Proxy}]), - Address = handle_proxy(Address0, Proxy), - if - ((Address =/= Address0) andalso (Scheme =:= https)) -> - %% This is what we should do if and when ssl supports - %% "socket upgrading" - %%send_ssl_tunnel_request(Address, Request, - %% #state{options = Options, - %% status = ssl_tunnel}); - Reason = {failed_connecting, - https_through_proxy_is_not_currently_supported}, - %% Send a reply to the original caller - ErrorResponse = httpc_response:error(Request, Reason), - httpc_response:send(Request#request.from, ErrorResponse), - %% Reply to the manager - ErrorReply = {error, Reason}, - {stop, normal, ErrorReply, State}; - true -> - case connect_and_send_first_request(Address, Request, State) of - {ok, NewState} -> - {reply, ok, NewState}; - {stop, Error, NewState} -> - {stop, normal, Error, NewState} - end - end; - handle_call(#request{address = Addr} = Request, _, #state{status = Status, - session = #tcp_session{socket = Socket, - type = pipeline} = Session, + session = #session{type = pipeline} = Session, timers = Timers, options = #options{proxy = Proxy} = _Options, profile_name = ProfileName} = State) @@ -301,7 +272,7 @@ handle_call(#request{address = Addr} = Request, _, Address = handle_proxy(Addr, Proxy), - case httpc_request:send(Address, Request, Socket) of + case httpc_request:send(Address, Session, Request) of ok -> ?hcrd("request sent", []), @@ -320,10 +291,10 @@ handle_call(#request{address = Addr} = Request, _, NewTimers = NewState#state.timers, NewPipeline = queue:in(Request, State#state.pipeline), NewSession = - Session#tcp_session{queue_length = - %% Queue + current - queue:len(NewPipeline) + 1, - client_close = ClientClose}, + Session#session{queue_length = + %% Queue + current + queue:len(NewPipeline) + 1, + client_close = ClientClose}, httpc_manager:insert_session(NewSession, ProfileName), ?hcrd("session updated", []), {reply, ok, State#state{pipeline = NewPipeline, @@ -336,8 +307,8 @@ handle_call(#request{address = Addr} = Request, _, cancel_timer(Timers#timers.queue_timer, timeout_queue), NewSession = - Session#tcp_session{queue_length = 1, - client_close = ClientClose}, + Session#session{queue_length = 1, + client_close = ClientClose}, httpc_manager:insert_session(NewSession, ProfileName), Relaxed = (Request#request.settings)#http_options.relaxed, @@ -357,8 +328,7 @@ handle_call(#request{address = Addr} = Request, _, handle_call(#request{address = Addr} = Request, _, #state{status = Status, - session = #tcp_session{socket = Socket, - type = keep_alive} = Session, + session = #session{type = keep_alive} = Session, timers = Timers, options = #options{proxy = Proxy} = _Options, profile_name = ProfileName} = State) @@ -370,7 +340,7 @@ handle_call(#request{address = Addr} = Request, _, {status, Status}]), Address = handle_proxy(Addr, Proxy), - case httpc_request:send(Address, Request, Socket) of + case httpc_request:send(Address, Session, Request) of ok -> ?hcrd("request sent", []), @@ -389,10 +359,10 @@ handle_call(#request{address = Addr} = Request, _, NewTimers = NewState#state.timers, NewKeepAlive = queue:in(Request, State#state.keep_alive), NewSession = - Session#tcp_session{queue_length = - %% Queue + current - queue:len(NewKeepAlive) + 1, - client_close = ClientClose}, + Session#session{queue_length = + %% Queue + current + queue:len(NewKeepAlive) + 1, + client_close = ClientClose}, httpc_manager:insert_session(NewSession, ProfileName), ?hcrd("session updated", []), {reply, ok, State#state{keep_alive = NewKeepAlive, @@ -405,8 +375,8 @@ handle_call(#request{address = Addr} = Request, _, cancel_timer(Timers#timers.queue_timer, timeout_queue), NewSession = - Session#tcp_session{queue_length = 1, - client_close = ClientClose}, + Session#session{queue_length = 1, + client_close = ClientClose}, httpc_manager:insert_session(NewSession, ProfileName), Relaxed = (Request#request.settings)#http_options.relaxed, @@ -447,25 +417,27 @@ handle_call(info, _, State) -> %% handle_keep_alive_queue/2 on the other hand will just skip the %% request as if it was never issued as in this case the request will %% not have been sent. -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{request = #request{id = RequestId} = Request, profile_name = ProfileName, canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], request = Request#request{from = answer_sent}}}; -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{profile_name = ProfileName, + request = #request{id = CurrId}, canceled = Canceled} = State) -> - ?hcrv("cancel", [{request_id, RequestId}, + ?hcrv("cancel", [{request_id, RequestId}, + {curr_req_id, CurrId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; @@ -543,7 +515,7 @@ handle_info({Proto, _Socket, Data}, {stop, normal, NewState} end, - ?hcri("data processed", []), + ?hcri("data processed", [{final_result, FinalResult}]), FinalResult; @@ -589,13 +561,13 @@ handle_info({ssl_closed, _}, State = #state{request = undefined}) -> %%% Error cases handle_info({tcp_closed, _}, #state{session = Session0} = State) -> - Socket = Session0#tcp_session.socket, - Session = Session0#tcp_session{socket = {remote_close, Socket}}, + Socket = Session0#session.socket, + Session = Session0#session{socket = {remote_close, Socket}}, %% {stop, session_remotly_closed, State}; {stop, normal, State#state{session = Session}}; handle_info({ssl_closed, _}, #state{session = Session0} = State) -> - Socket = Session0#tcp_session.socket, - Session = Session0#tcp_session{socket = {remote_close, Socket}}, + Socket = Session0#session.socket, + Session = Session0#session{socket = {remote_close, Socket}}, %% {stop, session_remotly_closed, State}; {stop, normal, State#state{session = Session}}; handle_info({tcp_error, _, _} = Reason, State) -> @@ -657,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}; @@ -704,52 +677,56 @@ terminate(normal, #state{session = undefined}) -> %% Init error sending, no session information has been setup but %% there is a socket that needs closing. terminate(normal, - #state{request = Request, - session = #tcp_session{id = undefined, - socket = Socket}}) -> - http_transport:close(socket_type(Request), Socket); + #state{session = #session{id = undefined} = Session}) -> + close_socket(Session); %% Socket closed remotely terminate(normal, - #state{session = #tcp_session{socket = {remote_close, Socket}, - id = Id}, + #state{session = #session{socket = {remote_close, Socket}, + socket_type = SocketType, + id = Id}, profile_name = ProfileName, - request = Request, - timers = Timers, - pipeline = Pipeline}) -> + request = Request, + timers = Timers, + pipeline = Pipeline, + keep_alive = KeepAlive} = State) -> ?hcrt("terminate(normal) - remote close", [{id, Id}, {profile, ProfileName}]), %% Clobber session (catch httpc_manager:delete_session(Id, ProfileName)), + maybe_retry_queue(Pipeline, State), + maybe_retry_queue(KeepAlive, State), + %% Cancel timers - #timers{request_timers = ReqTmrs, queue_timer = QTmr} = Timers, - cancel_timer(QTmr, timeout_queue), - lists:foreach(fun({_, Timer}) -> cancel_timer(Timer, timeout) end, - ReqTmrs), + cancel_timers(Timers), %% Maybe deliver answers to requests - deliver_answers([Request | queue:to_list(Pipeline)]), + deliver_answer(Request), %% And, just in case, close our side (**really** overkill) - http_transport:close(socket_type(Request), Socket); + http_transport:close(SocketType, Socket); -terminate(_, #state{session = #tcp_session{id = Id, - socket = Socket, - scheme = Scheme}, +terminate(Reason, #state{session = #session{id = Id, + socket = Socket, + socket_type = SocketType}, request = undefined, profile_name = ProfileName, timers = Timers, pipeline = Pipeline, keep_alive = KeepAlive} = State) -> + ?hcrt("terminate", + [{id, Id}, {profile, ProfileName}, {reason, Reason}]), + + %% Clobber session (catch httpc_manager:delete_session(Id, ProfileName)), maybe_retry_queue(Pipeline, State), maybe_retry_queue(KeepAlive, State), cancel_timer(Timers#timers.queue_timer, timeout_queue), - http_transport:close(socket_type(Scheme), Socket); + http_transport:close(SocketType, Socket); terminate(Reason, #state{request = undefined}) -> ?hcrt("terminate", [{reason, Reason}]), @@ -775,65 +752,55 @@ maybe_send_answer(#request{from = answer_sent}, _Reason, State) -> maybe_send_answer(Request, Answer, State) -> answer_request(Request, Answer, State). -deliver_answers([]) -> - ?hcrd("deliver answer done", []), - ok; -deliver_answers([#request{id = Id, from = From} = Request | Requests]) +deliver_answer(#request{id = Id, from = From} = Request) when is_pid(From) -> Response = httpc_response:error(Request, socket_closed_remotely), ?hcrd("deliver answer", [{id, Id}, {from, From}, {response, Response}]), - httpc_response:send(From, Response), - deliver_answers(Requests); -deliver_answers([Request|Requests]) -> + httpc_response:send(From, Response); +deliver_answer(Request) -> ?hcrd("skip deliver answer", [{request, Request}]), - deliver_answers(Requests). + ok. %%-------------------------------------------------------------------- %% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState} %% Purpose: Convert process state when code is changed %%-------------------------------------------------------------------- -code_change(_, #state{request = Request, pipeline = Queue} = State, - from_pre_5_3_5) -> - Settings = new_http_options(Request#request.settings), - NewRequest = Request#request{settings = Settings}, - NewQueue = new_queue(Queue, fun new_http_options/1), - {ok, State#state{request = NewRequest, pipeline = NewQueue}}; - -code_change(_, #state{request = Request, pipeline = Queue} = State, - to_pre_5_3_5) -> - Settings = old_http_options(Request#request.settings), - NewRequest = Request#request{settings = Settings}, - NewQueue = new_queue(Queue, fun old_http_options/1), - {ok, State#state{request = NewRequest, pipeline = NewQueue}}; +%% code_change(_, #state{request = Request, pipeline = Queue} = State, +%% [{from, '5.0.1'}, {to, '5.0.2'}]) -> +%% Settings = new_http_options(Request#request.settings), +%% NewRequest = Request#request{settings = Settings}, +%% NewQueue = new_queue(Queue, fun new_http_options/1), +%% {ok, State#state{request = NewRequest, pipeline = NewQueue}}; + +%% code_change(_, #state{request = Request, pipeline = Queue} = State, +%% [{from, '5.0.2'}, {to, '5.0.1'}]) -> +%% Settings = old_http_options(Request#request.settings), +%% NewRequest = Request#request{settings = Settings}, +%% NewQueue = new_queue(Queue, fun old_http_options/1), +%% {ok, State#state{request = NewRequest, pipeline = NewQueue}}; code_change(_, State, _) -> {ok, State}. -new_http_options({http_options, - Version, Timeout, AutoRedirect, SslOpts, - ProxyAuth, Relaxed, ConnTimeout}) -> - UrlEncoding = false, - {http_options, - Version, Timeout, AutoRedirect, SslOpts, - ProxyAuth, Relaxed, ConnTimeout, UrlEncoding}. - -old_http_options({http_options, - Version, TimeOut, AutoRedirect, SslOpts, - ProxyAuth, Relaxed, ConnTimeout, _UrlEncode}) -> - {http_options, - Version, TimeOut, AutoRedirect, SslOpts, - ProxyAuth, Relaxed, ConnTimeout}. - -new_queue(Queue, TransformSettings) -> - List = queue:to_list(Queue), - NewList = - lists:map(fun(Request) -> - Settings = - TransformSettings(Request#request.settings), - Request#request{settings = Settings} - end, List), - queue:from_list(NewList). +%% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts, +%% Auth, Relaxed}) -> +%% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts, +%% Auth, Relaxed}. + +%% old_http_options({http_options, _, TimeOut, AutoRedirect, +%% SslOpts, Auth, Relaxed}) -> +%% {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}. + +%% new_queue(Queue, Fun) -> +%% List = queue:to_list(Queue), +%% NewList = +%% lists:map(fun(Request) -> +%% Settings = +%% Fun(Request#request.settings), +%% Request#request{settings = Settings} +%% end, List), +%% queue:from_list(NewList). %%%-------------------------------------------------------------------- @@ -863,72 +830,72 @@ connect(SocketType, ToAddress, inet6fb4 -> Opts3 = [inet6 | Opts2], case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of - {error, Reason} when ((Reason =:= nxdomain) orelse - (Reason =:= eafnosupport)) -> + {error, _Reason} = Error -> Opts4 = [inet | Opts2], - http_transport:connect(SocketType, ToAddress, Opts4, Timeout); - Other -> - Other + case http_transport:connect(SocketType, + ToAddress, Opts4, Timeout) of + {error, _} -> + %% Reply with the "original" error + Error; + OK -> + OK + end; + OK -> + OK end; _ -> Opts3 = [IpFamily | Opts2], http_transport:connect(SocketType, ToAddress, Opts3, Timeout) end. - -connect_and_send_first_request(Address, - #request{settings = Settings, - headers = Headers, - address = OrigAddress, - scheme = Scheme} = Request, - #state{options = Options} = State) -> - - ?hcrd("connect", - [{address, Address}, {request, Request}, {options, Options}]), +connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> SocketType = socket_type(Request), - ConnTimeout = Settings#http_options.connect_timeout, + ConnTimeout = (Request#request.settings)#http_options.connect_timeout, + ?hcri("connect", + [{address, Address}, {request, Request}, {options, Options}]), case connect(SocketType, Address, Options, ConnTimeout) of {ok, Socket} -> - ?hcrd("connected - now send first request", [{socket, Socket}]), - case httpc_request:send(Address, Request, Socket) of + ClientClose = + httpc_request:is_client_closing( + Request#request.headers), + SessionType = httpc_manager:session_type(Options), + SocketType = socket_type(Request), + Session = #session{id = {Request#request.address, self()}, + scheme = Request#request.scheme, + socket = Socket, + socket_type = SocketType, + client_close = ClientClose, + type = SessionType}, + ?hcri("connected - now send first request", [{socket, Socket}]), + + case httpc_request:send(Address, Session, Request) of ok -> - ?hcrd("first request sent", []), - ClientClose = - httpc_request:is_client_closing(Headers), - SessionType = httpc_manager:session_type(Options), - Session = - #tcp_session{id = {OrigAddress, self()}, - scheme = Scheme, - socket = Socket, - client_close = ClientClose, - type = SessionType}, - TmpState = - State#state{request = Request, - session = Session, - mfa = init_mfa(Request, State), - status_line = init_status_line(Request), - headers = undefined, - body = undefined, - status = new}, - ?hcrt("activate socket", []), - activate_once(Session), + ?hcri("first request sent", []), + TmpState = State#state{request = Request, + session = Session, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), NewState = activate_request_timeout(TmpState), {ok, NewState}; - - {error, Reason} = Error -> - ?hcrv("failed sending request", [{reason, Reason}]), - {stop, Error, - State#state{session = {send_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_sending, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request, + session = + #session{socket = Socket}}} end; - - {error, Reason} = Error -> - ?hcri("connect failed", [{reason, Reason}]), - {stop, Error, State#state{session = {connect_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_connecting, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request}} end. - handler_info(#state{request = Request, session = Session, status_line = _StatusLine, @@ -958,21 +925,20 @@ handler_info(#state{request = Request, ?hcrt("handler info", [{request_info, RequestInfo}]), %% Info about the current session/socket - SessionType = Session#tcp_session.type, - QueueLen = case Session#tcp_session.type of + SessionType = Session#session.type, + QueueLen = case SessionType of pipeline -> queue:len(Pipeline); keep_alive -> queue:len(KeepAlive) end, - Socket = Session#tcp_session.socket, - Scheme = Session#tcp_session.scheme, - SocketType = socket_type(Scheme), + Scheme = Session#session.scheme, + Socket = Session#session.socket, + SocketType = Session#session.socket_type, ?hcrt("handler info", [{session_type, SessionType}, {queue_length, QueueLen}, {scheme, Scheme}, - {socket_type, SocketType}, {socket, Socket}]), SocketOpts = http_transport:getopts(SocketType, Socket), @@ -1129,9 +1095,7 @@ handle_response(#state{request = Request, ?hcrd("handle response - continue", []), %% Send request body {_, RequestBody} = Request#request.content, - http_transport:send(socket_type(Session#tcp_session.scheme), - Session#tcp_session.socket, - RequestBody), + send_raw(Session, RequestBody), %% Wait for next response activate_once(Session), Relaxed = (Request#request.settings)#http_options.relaxed, @@ -1171,12 +1135,12 @@ handle_response(#state{request = Request, {ok, Msg, Data} -> ?hcrd("handle response - ok", []), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), handle_queue(NewState, Data); {stop, Msg} -> ?hcrd("handle response - stop", [{msg, Msg}]), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), {stop, normal, NewState} end. @@ -1193,7 +1157,7 @@ handle_cookies(Headers, Request, #options{cookies = enabled}, ProfileName) -> httpc_manager:store_cookies(Cookies, Request#request.address, ProfileName). -%% This request could not be pipelined or used as sequential keept alive +%% This request could not be pipelined or used as sequential keep alive %% queue handle_queue(#state{status = close} = State, _) -> {stop, normal, State}; @@ -1228,7 +1192,7 @@ handle_pipeline(#state{status = pipeline, %% If a pipeline that has been idle for some time is not %% closed by the server, the client may want to close it. NewState = activate_queue_timeout(TimeOut, State), - NewSession = Session#tcp_session{queue_length = 0}, + NewSession = Session#session{queue_length = 0}, httpc_manager:insert_session(NewSession, ProfileName), %% Note mfa will be initilized when a new request %% arrives. @@ -1246,13 +1210,14 @@ handle_pipeline(#state{status = pipeline, %% See comment for handle_cast({cancel, RequestId}) {stop, normal, State#state{request = - NextRequest#request{from = answer_sent}}}; + NextRequest#request{from = answer_sent}, + pipeline = Pipeline}}; false -> ?hcrv("next request", [{request, NextRequest}]), NewSession = - Session#tcp_session{queue_length = - %% Queue + current - queue:len(Pipeline) + 1}, + Session#session{queue_length = + %% Queue + current + queue:len(Pipeline) + 1}, httpc_manager:insert_session(NewSession, ProfileName), Relaxed = (NextRequest#request.settings)#http_options.relaxed, @@ -1301,16 +1266,16 @@ handle_keep_alive_queue( %% If a keep_alive session has been idle for some time is not %% closed by the server, the client may want to close it. NewState = activate_queue_timeout(TimeOut, State), - NewSession = Session#tcp_session{queue_length = 0}, + NewSession = Session#session{queue_length = 0}, httpc_manager:insert_session(NewSession, ProfileName), %% Note mfa will be initilized when a new request %% arrives. {noreply, - NewState#state{request = undefined, - mfa = undefined, + NewState#state{request = undefined, + mfa = undefined, status_line = undefined, - headers = undefined, - body = undefined + headers = undefined, + body = undefined } }; {{value, NextRequest}, KeepAlive} -> @@ -1353,10 +1318,12 @@ case_insensitive_header(Str) when is_list(Str) -> case_insensitive_header(Str) -> Str. -activate_once(#tcp_session{scheme = Scheme, socket = Socket}) -> - SocketType = socket_type(Scheme), +activate_once(#session{socket = Socket, socket_type = SocketType}) -> http_transport:setopts(SocketType, Socket, [{active, once}]). +close_socket(#session{socket = Socket, socket_type = SocketType}) -> + http_transport:close(SocketType, Socket). + activate_request_timeout( #state{request = #request{timer = undefined} = Request} = State) -> Timeout = (Request#request.settings)#http_options.timeout, @@ -1389,7 +1356,7 @@ activate_queue_timeout(Time, State) -> State#state{timers = #timers{queue_timer = Ref}}. -is_pipeline_enabled_client(#tcp_session{type = pipeline}) -> +is_pipeline_enabled_client(#session{type = pipeline}) -> true; is_pipeline_enabled_client(_) -> false. @@ -1402,7 +1369,7 @@ is_keep_alive_enabled_server("HTTP/1.0", is_keep_alive_enabled_server(_,_) -> false. -is_keep_alive_connection(Headers, #tcp_session{client_close = ClientClose}) -> +is_keep_alive_connection(Headers, #session{client_close = ClientClose}) -> (not ((ClientClose) orelse httpc_response:is_server_closing(Headers))). try_to_enable_pipeline_or_keep_alive( @@ -1427,7 +1394,7 @@ try_to_enable_pipeline_or_keep_alive( httpc_manager:insert_session(Session, ProfileName), %% Make sure type is keep_alive in session %% as it in this case might be pipeline - NewSession = Session#tcp_session{type = keep_alive}, + NewSession = Session#session{type = keep_alive}, State#state{status = keep_alive, session = NewSession} end; @@ -1437,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 = @@ -1445,10 +1412,17 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), + State#state{request = Request#request{from = answer_sent}, timers = Timers#timers{request_timers = lists:delete(Timer, RequestTimers)}}. + +cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) -> + cancel_timer(QTmr, timeout_queue), + CancelTimer = fun({_, Timer}) -> cancel_timer(Timer, timeout) end, + lists:foreach(CancelTimer, ReqTmrs). + cancel_timer(undefined, _) -> ok; cancel_timer(Timer, TimeoutMsg) -> @@ -1562,11 +1536,11 @@ init_status_line(#request{settings = Settings}) -> socket_type(#request{scheme = http}) -> ip_comm; socket_type(#request{scheme = https, settings = Settings}) -> - {ssl, Settings#http_options.ssl}; -socket_type(http) -> - ip_comm; -socket_type(https) -> - {ssl, []}. %% Dummy value ok for ex setopts that does not use this value + Settings#http_options.ssl. +%% socket_type(http) -> +%% ip_comm; +%% socket_type(https) -> +%% {ssl1, []}. %% Dummy value ok for ex setopts that does not use this value start_stream({_Version, _Code, _ReasonPhrase}, _Headers, #request{stream = none} = Request) -> @@ -1635,18 +1609,15 @@ end_stream(SL, R) -> next_body_chunk(#state{request = #request{stream = {self, once}}, - once = once, session = Session} = State) -> - http_transport:setopts(socket_type(Session#tcp_session.scheme), - Session#tcp_session.socket, - [{active, once}]), + once = once, + session = Session} = State) -> + activate_once(Session), State#state{once = inactive}; next_body_chunk(#state{request = #request{stream = {self, once}}, once = inactive} = State) -> State; %% Wait for user to call stream_next next_body_chunk(#state{session = Session} = State) -> - http_transport:setopts(socket_type(Session#tcp_session.scheme), - Session#tcp_session.socket, - [{active, once}]), + activate_once(Session), State. handle_verbose(verbose) -> @@ -1661,66 +1632,28 @@ handle_verbose(_) -> ok. -%%% Normaly I do not comment out code, I throw it away. But this might -%%% actually be used one day if ssl is improved. -%% send_ssl_tunnel_request(Address, Request = #request{address = {Host, Port}}, -%% State) -> -%% %% A ssl tunnel request is a special http request that looks like -%% %% CONNECT host:port HTTP/1.1 -%% SslTunnelRequest = #request{method = connect, scheme = http, -%% headers = -%% #http_request_h{ -%% host = Host, -%% address = Address, -%% path = Host ++ ":", -%% pquery = integer_to_list(Port), -%% other = [{ "Proxy-Connection", "keep-alive"}]}, -%% Ipv6 = (State#state.options)#options.ipv6, -%% SocketType = socket_type(SslTunnelRequest), -%% case http_transport:connect(SocketType, -%% SslTunnelRequest#request.address, Ipv6) of -%% {ok, Socket} -> -%% case httpc_request:send(Address, SslTunnelRequest, Socket) of -%% ok -> -%% Session = #tcp_session{id = -%% {SslTunnelRequest#request.address, -%% self()}, -%% scheme = -%% SslTunnelRequest#request.scheme, -%% socket = Socket}, -%% NewState = State#state{mfa = -%% {httpc_response, parse, -%% [State#state.max_header_size]}, -%% request = Request, -%% session = Session}, -%% http_transport:setopts(socket_type( -%% SslTunnelRequest#request.scheme), -%% Socket, -%% [{active, once}]), -%% {ok, NewState}; -%% {error, Reason} -> -%% self() ! {init_error, error_sending, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request, -%% session = #tcp_session{socket = -%% Socket}}} -%% end; -%% {error, Reason} -> -%% self() ! {init_error, error_connecting, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request}} -%% end. - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. + +send_raw(#session{socket = Socket, socket_type = SocketType}, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send raw", [{acc, Acc}]), + send_raw(SocketType, Socket, ProcessBody, Acc); +send_raw(#session{socket = Socket, socket_type = SocketType}, Body) -> + http_transport:send(SocketType, Socket, Body). + +send_raw(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{data, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + send_raw(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. call(Msg, Pid) -> @@ -1732,11 +1665,5 @@ call(Msg, Pid, Timeout) -> cast(Msg, Pid) -> gen_server:cast(Pid, Msg). - -%% to(To, Start) when is_integer(Start) andalso (Start >= 0) -> -%% http_util:timeout(To, Start); -%% to(To, _Start) -> -%% http_util:timeout(To, t()). - t() -> http_util:timestamp(). diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl index 2a69fd15d0..f7a0b014b3 100644 --- a/lib/inets/src/http_client/httpc_handler_sup.erl +++ b/lib/inets/src/http_client/httpc_handler_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% 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 @@ -23,7 +23,7 @@ %% API -export([start_link/0]). --export([start_child/2]). +-export([start_child/1]). %% Supervisor callback -export([init/1]). @@ -34,11 +34,9 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -start_child(Options, Profile) -> - Args = [Options, Profile], +start_child(Args) -> supervisor:start_child(?MODULE, Args). - %%%========================================================================= %%% Supervisor callback %%%========================================================================= diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl index 7513aa2838..1d8a5b6a92 100644 --- a/lib/inets/src/http_client/httpc_internal.hrl +++ b/lib/inets/src/http_client/httpc_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -18,7 +18,11 @@ %% %% --include("inets_internal.hrl"). +-ifndef(httpc_internal_hrl). +-define(httpc_internal_hrl, true). + +-include_lib("inets/src/inets_app/inets_internal.hrl"). + -define(SERVICE, httpc). -define(hcri(Label, Data), ?report_important(Label, ?SERVICE, Data)). -define(hcrv(Label, Data), ?report_verbose(Label, ?SERVICE, Data)). @@ -57,7 +61,7 @@ %% integer() - ms before a connect times out connect_timeout = ?HTTP_REQUEST_CTIMEOUT, - + %% bool() - Use %-encoding rfc 2396 url_encode @@ -108,13 +112,14 @@ } ). --record(tcp_session, +-record(session, { id, % {{Host, Port}, HandlerPid} client_close, % true | false scheme, % http (HTTP/TCP) | https (HTTP/SSL/TCP) socket, % Open socket, used by connection - queue_length = 1, % Current length of pipeline or keep alive queue + socket_type, % socket-type, used by connection + queue_length = 1, % Current length of pipeline or keep-alive queue type % pipeline | keep_alive (wait for response before sending new request) }). @@ -142,3 +147,6 @@ %% path, % string() %% q % query: string() %% }). + + +-endif. % -ifdef(httpc_internal_hrl). diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 4738517210..9015bf1ce2 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 @@ -21,15 +21,15 @@ -behaviour(gen_server). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). --include("http_internal.hrl"). %% Internal Application API -export([ start_link/3, request/2, cancel_request/2, - request_canceled/2, + request_canceled/3, request_done/2, retry_request/2, redirect_request/2, @@ -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{} }). @@ -66,6 +66,7 @@ state % State of the handler: initiating | started | operational | canceled }). +-define(DELAY, 500). %%==================================================================== %% Internal Application API @@ -158,7 +159,8 @@ cancel_request(RequestId, ProfileName) -> %% be called by the httpc handler process. %%-------------------------------------------------------------------- -request_canceled(RequestId, ProfileName) -> +request_canceled(RequestId, ProfileName, From) -> + gen_server:reply(From, ok), cast(ProfileName, {request_canceled, RequestId}). @@ -176,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 @@ -324,7 +326,7 @@ do_init(ProfileName, CookiesDir) -> ?hcrt("create session db", []), SessionDbName = session_db_name(ProfileName), ets:new(SessionDbName, - [public, set, named_table, {keypos, #tcp_session.id}]), + [public, set, named_table, {keypos, #session.id}]), %% Create handler db ?hcrt("create handler/request db", []), @@ -355,44 +357,32 @@ do_init(ProfileName, CookiesDir) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({request, Request}, _From, State) -> - ?hcrv("request", [{request, Request}]), +handle_call({request, Request}, _, State) -> + ?hcri("request", [{request, Request}]), case (catch handle_request(Request, State)) of - {ok, ReqId, NewState} -> - {reply, {ok, ReqId}, NewState}; - + {reply, Msg, NewState} -> + {reply, Msg, NewState}; Error -> - NewError = {error, {failed_process_request, Error}}, - {reply, NewError, State} + {stop, Error, httpc_response:error(Request, Error), State} end; - -handle_call({cancel_request, RequestId}, From, - #state{handler_db = HandlerDb} = State) -> - ?hcrv("cancel_request", [{request_id, RequestId}]), + +handle_call({cancel_request, RequestId}, From, State) -> + ?hcri("cancel_request", [{request_id, RequestId}]), case ets:lookup(State#state.handler_db, RequestId) of [] -> - ?hcrd("nothing to cancel", []), - Reply = ok, %% Nothing to cancel - {reply, Reply, State}; - - [#handler_info{handler = Pid}] when is_pid(Pid) -> - ?hcrd("found operational handler for this request", - [{handler, Pid}]), - httpc_handler:cancel(RequestId, Pid), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}}; - - [#handler_info{starter = Pid, state = HandlerState}] - when is_pid(Pid) -> - ?hcri("found *initiating* handler for this request", - [{starter, Pid}, {state, HandlerState}]), - ets:update_element(HandlerDb, RequestId, - {#handler_info.state, canceled}), + %% The request has allready compleated make sure + %% it is deliverd to the client process queue so + %% it can be thrown away by httpc:cancel_request + %% This delay is hopfully a temporary workaround. + %% Note that it will not not delay the manager, + %% only the client that called httpc:cancel_request + timer:apply_after(?DELAY, gen_server, reply, [From, ok]), + {noreply, State}; + [{_, Pid, _}] -> + httpc_handler:cancel(RequestId, Pid, From), {noreply, State#state{cancel = - [{RequestId, Pid, From} | + [{RequestId, Pid, From} | State#state.cancel]}} - end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -437,43 +427,16 @@ handle_call(Req, From, #state{profile_name = ProfileName} = State) -> %%-------------------------------------------------------------------- handle_cast({retry_or_redirect_request, {Time, Request}}, #state{profile_name = ProfileName} = State) -> - ?hcrv("retry or redirect request", [{time, Time}, {request, Request}]), - case timer:apply_after(Time, ?MODULE, retry_request, - [Request, ProfileName]) of - {ok, _} -> - {noreply, State}; - {error, Reason} -> - error_report(ProfileName, - "failed scheduling retry/redirect request" - "~n Time: ~p" - "~n Request: ~p" - "~n Reason: ~p", [Time, Request, Reason]), - {noreply, State} - end; + {ok, _} = timer:apply_after(Time, ?MODULE, retry_request, [Request, ProfileName]), + {noreply, State}; -handle_cast({retry_or_redirect_request, Request}, - #state{profile_name = Profile, - handler_db = HandlerDb} = State) -> - ?hcrv("retry or redirect request", [{request, Request}]), +handle_cast({retry_or_redirect_request, Request}, State) -> case (catch handle_request(Request, State)) of - {ok, _, NewState} -> + {reply, {ok, _}, NewState} -> {noreply, NewState}; - Error -> - ReqId = Request#request.id, - error_report(Profile, - "failed to retry or redirect request ~p" - "~n Error: ~p", [ReqId, Error]), - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{from = From}] -> - Error2 = httpc_response:error(Request, Error), - httpc_response:send(From, Error2), - ok; - - _ -> - ok - end, - {noreply, State} + httpc_response:error(Request, Error), + {stop, Error, State} end; handle_cast({request_canceled, RequestId}, State) -> @@ -482,7 +445,6 @@ handle_cast({request_canceled, RequestId}, State) -> case lists:keysearch(RequestId, 1, State#state.cancel) of {value, Entry = {RequestId, _, From}} -> ?hcrt("found in cancel", [{from, From}]), - gen_server:reply(From, ok), {noreply, State#state{cancel = lists:delete(Entry, State#state.cancel)}}; Else -> @@ -539,8 +501,6 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> "recived unknown message" "~n Msg: ~p", [Msg]), {noreply, State}. - - %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -548,39 +508,17 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling all non call/cast messages %%--------------------------------------------------------- - -handle_info({started, StarterPid, ReqId, HandlerPid}, State) -> - handle_started(StarterPid, ReqId, HandlerPid, State), - {noreply, State}; - -handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) -> - handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State), - {noreply, State}; - -handle_info({failed_starting_handler, StarterPid, ReqId, Res}, State) -> - handle_failed_starting_handler(StarterPid, ReqId, Res, State), - {noreply, State}; - -handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) -> - maybe_handle_terminating_starter(Pid, Reason, HandlerDb), +handle_info({'EXIT', _, _}, State) -> + %% Handled in DOWN {noreply, State}; - handle_info({'DOWN', _, _, Pid, _}, State) -> - - %% - %% Normally this should have been cleaned up already - %% (when receiving {request_done, PequestId}), but - %% just in case there is a glitch, cleanup anyway. - %% - - Pattern = #handler_info{handler = Pid, _ = '_'}, - ets:match_delete(State#state.handler_db, Pattern), + ets:match_delete(State#state.handler_db, {'_', Pid, '_'}), %% If there where any canceled request, handled by the %% the process that now has terminated, the %% cancelation can be viewed as sucessfull! - NewCanceledList = - lists:foldl(fun({_, HandlerPid, From} = Entry, Acc) -> + NewCanceldList = + lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) -> case HandlerPid of Pid -> gen_server:reply(From, ok), @@ -589,15 +527,13 @@ handle_info({'DOWN', _, _, Pid, _}, State) -> Acc end end, State#state.cancel, State#state.cancel), - {noreply, State#state{cancel = NewCanceledList}}; - -handle_info(Info, #state{profile_name = ProfileName} = State) -> - error_report(ProfileName, - "received unknown info" - "~n Info: ~p", [Info]), + {noreply, State#state{cancel = NewCanceldList}}; +handle_info(Info, State) -> + Report = io_lib:format("Unknown message in " + "httpc_manager:handle_info ~p~n", [Info]), + error_logger:error_report(Report), {noreply, State}. - %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> _ (ignored by gen_server) %% Description: Shutdown the httpc_handler @@ -655,239 +591,95 @@ get_handler_info(Tab) -> {Pid, State} <- Handlers2], Handlers3. - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When the handler has sucessfully been started, -%% this message (started) is sent. -%% - -handle_started(StarterPid, ReqId, HandlerPid, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = initiating} = HandlerInfo] -> - ?hcri("received started ack for initiating handler", []), - %% As a last resort, make sure we know when it exits, - %% in case it forgets to notify us. - %% We dont need to know the ref id? - erlang:monitor(process, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid, - state = started}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] -> - error_report(Profile, - "unexpected (started) message for handler (~p) in state " - "~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]), - ?hcri("received unexpected started message", [{state, State}]), - ok; - - [] -> - error_report(Profile, - "unknown handler ~p (~p) started for request ~w - canceling", - [HandlerPid, StarterPid, ReqId]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When that process terminates it sends -%% one of two messages. These ara handled by the two functions -%% below. -%% - -handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = started} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for started handler", []), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid, - state = operational}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = canceled} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for canceled handler", []), - httpc_handler:cancel(ReqId, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] when Result =/= ok -> - error_report(Profile, - "handler (~p, ~w) failed to connect and/or " - "send request ~p" - "~n Result: ~p", - [HandlerPid, State, ReqId, Result]), - ?hcri("received connect-and-send error", - [{result, Result}, {state, State}]), - %% We don't need to send a response to the original caller - %% because the handler already sent one in its terminate - %% function. - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - error_report(Profile, - "handler (~p) successfully started " - "for unknown request ~p => canceling", - [HandlerPid, ReqId]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -handle_failed_starting_handler(_StarterPid, ReqId, Error, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = canceled}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - request_canceled(Profile, ReqId), % Fake signal from handler - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{from = From}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - Reason2 = - case Error of - {error, Reason} -> - {failed_connecting, Reason}; - _ -> - {failed_connecting, Error} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - error_report(Profile, - "failed starting handler for unknown request ~p" - "~n Error: ~p", [ReqId, Error]), - ok - end. - - -maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) -> - Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'}, - case ets:match_object(HandlerDb, Pattern) of - [#handler_info{id = ReqId, from = From, state = initiating}] -> - %% The starter process crashed before it could start the - %% the handler process, therefor we need to answer the - %% original caller. - ?hcri("starter process crashed bfore starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}]), - Reason2 = - case Reason of - {error, Error} -> - {failed_connecting, Error}; - _ -> - {failed_connecting, Reason} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{state = State} = HandlerInfo] -> - %% The starter process crashed after the handler was started. - %% The handler will answer to the original caller. - ?hcri("starter process crashed after starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - _ -> - ok - end. - - -%% ----- -%% Act as an HTTP/0.9 client that does not know anything -%% about persistent connections handle_request(#request{settings = - #http_options{version = "HTTP/0.9"}} = Request0, + #http_options{version = "HTTP/0.9"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = undefined}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - -%% ----- -%% Act as an HTTP/1.0 client that does not -%% use persistent connections + %% Act as an HTTP/0.9 client that does not know anything + %% about persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = undefined}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + handle_request(#request{settings = - #http_options{version = "HTTP/1.0"}} = Request0, + #http_options{version = "HTTP/1.0"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - - -%% ----- -handle_request(#request{method = Method, - address = Address, - scheme = Scheme} = Request0, - #state{options = Opts} = State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - SessionType = session_type(Opts), - case select_session(Method, Address, Scheme, SessionType, State) of + %% Act as an HTTP/1.0 client that does not + %% use persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + +handle_request(Request, State = #state{options = Options}) -> + + NewRequest = handle_cookies(generate_request_id(Request), State), + SessionType = session_type(Options), + case select_session(Request#request.method, + Request#request.address, + Request#request.scheme, SessionType, State) of {ok, HandlerPid} -> - pipeline_or_keep_alive(Request1, HandlerPid, State); + pipeline_or_keep_alive(NewRequest, HandlerPid, State); no_connection -> - create_handler_starter(Request1, State); - {no_session, OpenSessions} - when OpenSessions < Opts#options.max_sessions -> - create_handler_starter(Request1, State); + start_handler(NewRequest, State); + {no_session, OpenSessions} when OpenSessions + < Options#options.max_sessions -> + start_handler(NewRequest, State); {no_session, _} -> %% Do not start any more persistent connections %% towards this server. - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State) + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State) end, - {ok, Request1#request.id, State}. + {reply, {ok, NewRequest#request.id}, State}. + + +start_handler(Request, State) -> + {ok, Pid} = + case is_inets_manager() of + true -> + httpc_handler_sup:start_child([whereis(httpc_handler_sup), + Request, State#state.options, + State#state.profile_name]); + false -> + httpc_handler:start_link(self(), Request, State#state.options, + State#state.profile_name) + end, + ets:insert(State#state.handler_db, {Request#request.id, + Pid, Request#request.from}), + erlang:monitor(process, Pid). select_session(Method, HostPort, Scheme, SessionType, #state{options = #options{max_pipeline_length = MaxPipe, max_keep_alive_length = MaxKeepAlive}, session_db = SessionDb}) -> - ?hcrd("select session", [{session_type, SessionType}, - {max_pipeline_length, MaxPipe}, + ?hcrd("select session", [{session_type, SessionType}, + {max_pipeline_length, MaxPipe}, {max_keep_alive_length, MaxKeepAlive}]), case httpc_request:is_idempotent(Method) orelse (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 %% specified. - Pattern = #tcp_session{id = {HostPort, '$1'}, - client_close = false, - scheme = Scheme, - socket = '_', - queue_length = '$2', - type = SessionType}, + Pattern = #session{id = {HostPort, '$1'}, + client_close = false, + scheme = Scheme, + queue_length = '$2', + type = SessionType, + _ = '_'}, %% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp}, Candidates = ets:match(SessionDb, Pattern), ?hcrd("select session", [{host_port, HostPort}, @@ -917,92 +709,17 @@ select_session(Candidates, Max) -> ?hcrd("select session - found one", [{handler, HandlerPid}]), {ok, HandlerPid} end. - -pipeline_or_keep_alive(#request{id = Id} = Request, HandlerPid, State) -> - ?hcrd("pipeline of keep-alive", [{id, Id}, {handler, HandlerPid}]), + +pipeline_or_keep_alive(Request, HandlerPid, State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ?hcrd("pipeline or keep-alive - successfully sent", []), - Entry = #handler_info{id = Id, - handler = HandlerPid, - state = operational}, - ets:insert(State#state.handler_db, Entry); - - _ -> %% timeout pipelining failed - ?hcrd("pipeline or keep-alive - failed sending -> " - "start a new handler", []), - create_handler_starter(Request, State) + ets:insert(State#state.handler_db, {Request#request.id, + HandlerPid, + Request#request.from}); + _ -> %timeout pipelining failed + start_handler(Request, State) end. - -create_handler_starter(#request{socket_opts = SocketOpts} = Request, - #state{options = Options} = State) - when is_list(SocketOpts) -> - %% The user provided us with (override) socket options - ?hcrt("create handler starter", [{socket_opts, SocketOpts}, {options, Options}]), - Options2 = Options#options{socket_opts = SocketOpts}, - create_handler_starter(Request#request{socket_opts = undefined}, - State#state{options = Options2}); - -create_handler_starter(#request{id = Id, - from = From} = Request, - #state{profile_name = ProfileName, - options = Options, - handler_db = HandlerDb} = _State) -> - ?hcrv("create handler starter", [{id, Id}, {profile, ProfileName}]), - IsInetsManager = is_inets_manager(), - ManagerPid = self(), - StarterFun = - fun() -> - ?hcrd("handler starter - start", - [{id, Id}, - {profile, ProfileName}, - {inets_manager, IsInetsManager}]), - Result1 = - case IsInetsManager of - true -> - httpc_handler_sup:start_child(Options, - ProfileName); - false -> - httpc_handler:start_link(Options, - ProfileName) - end, - ?hcrd("handler starter - maybe connect and send", - [{id, Id}, {profile, ProfileName}, {result, Result1}]), - case Result1 of - {ok, HandlerPid} -> - StartedMessage = - {started, self(), Id, HandlerPid}, - ManagerPid ! StartedMessage, - Result2 = httpc_handler:connect_and_send(Request, - HandlerPid), - ?hcrd("handler starter - connected and sent", - [{id, Id}, {profile, ProfileName}, - {handler, HandlerPid}, {result, Result2}]), - ConnAndSendMessage = - {connect_and_send, - self(), Id, HandlerPid, Result2}, - ManagerPid ! ConnAndSendMessage; - {error, Reason} -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Reason}, - ManagerPid ! StartFailureMessage; - _ -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Result1}, - ManagerPid ! StartFailureMessage - end - end, - Starter = erlang:spawn_link(StarterFun), - ?hcrd("create handler starter - started", [{id, Id}, {starter, Starter}]), - Entry = #handler_info{id = Id, - starter = Starter, - from = From, - state = initiating}, - ets:insert(HandlerDb, Entry), - ok. - - is_inets_manager() -> case get('$ancestors') of [httpc_profile_sup | _] -> @@ -1044,8 +761,6 @@ do_store_cookies([Cookie | Cookies], #state{cookie_db = CookieDb} = State) -> ok = httpc_cookie:insert(CookieDb, Cookie), do_store_cookies(Cookies, State). - - session_db_name(ProfileName) -> make_db_name(ProfileName, "__session_db"). @@ -1073,7 +788,6 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). - get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). @@ -1132,20 +846,6 @@ handle_verbose(trace) -> handle_verbose(_) -> ok. - error_report(Profile, F, A) -> Report = io_lib:format("HTTPC-MANAGER<~p> " ++ F ++ "~n", [Profile | A]), error_logger:error_report(Report). - - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index 55e0af4b42..879053f0f2 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% 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 @@ -19,12 +19,13 @@ -module(httpc_request). --include("http_internal.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). %%% Internal API -export([send/3, is_idempotent/1, is_client_closing/1]). + %%%========================================================================= %%% Internal application API %%%========================================================================= @@ -39,10 +40,9 @@ %% %% Description: Composes and sends a HTTP-request. %%------------------------------------------------------------------------- -send(SendAddr, #request{scheme = Scheme, socket_opts = SocketOpts} = Request, - Socket) +send(SendAddr, #session{socket = Socket, socket_type = SocketType}, + #request{socket_opts = SocketOpts} = Request) when is_list(SocketOpts) -> - SocketType = socket_type(Scheme), case http_transport:setopts(SocketType, Socket, SocketOpts) of ok -> send(SendAddr, Socket, SocketType, @@ -50,8 +50,7 @@ send(SendAddr, #request{scheme = Scheme, socket_opts = SocketOpts} = Request, {error, Reason} -> {error, {setopts_failed, Reason}} end; -send(SendAddr, #request{scheme = Scheme} = Request, Socket) -> - SocketType = socket_type(Scheme), +send(SendAddr, #session{socket = Socket, socket_type = SocketType}, Request) -> send(SendAddr, Socket, SocketType, Request). send(SendAddr, Socket, SocketType, @@ -80,36 +79,62 @@ send(SendAddr, Socket, SocketType, {settings, HttpOptions}, {userinfo, UserInfo}]), - TmpHeaders = handle_user_info(UserInfo, Headers), + TmpHdrs = handle_user_info(UserInfo, Headers), - {TmpHeaders2, Body} = - post_data(Method, TmpHeaders, Content, HeadersAsIs), + {TmpHdrs2, Body} = post_data(Method, TmpHdrs, Content, HeadersAsIs), + + {NewHeaders, Uri} = + case Address of + SendAddr -> + {TmpHdrs2, Path ++ Query}; + _Proxy -> + TmpHdrs3 = handle_proxy(HttpOptions, TmpHdrs2), + {TmpHdrs3, AbsUri} + end, - {NewHeaders, Uri} = case Address of - SendAddr -> - {TmpHeaders2, Path ++ Query}; - _Proxy -> - TmpHeaders3 = - handle_proxy(HttpOptions, TmpHeaders2), - {TmpHeaders3, AbsUri} - end, - - FinalHeaders = case NewHeaders of - HeaderList when is_list(HeaderList) -> - http_headers(HeaderList, []); - _ -> - http_request:http_headers(NewHeaders) - end, + FinalHeaders = + case NewHeaders of + HeaderList when is_list(HeaderList) -> + http_headers(HeaderList, []); + _ -> + http_request:http_headers(NewHeaders) + end, Version = HttpOptions#http_options.version, - Message = [method(Method), " ", Uri, " ", - version(Version), ?CRLF, - headers(FinalHeaders, Version), ?CRLF, Body], + do_send_body(SocketType, Socket, Method, Uri, Version, FinalHeaders, Body). + + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, + {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> + ?hcrt("send", [{acc, Acc}]), + case do_send_body(SocketType, Socket, Method, Uri, Version, Headers, []) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, Acc); + Error -> + Error + end; +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> + ?hcrt("create message", [{body, Body}]), + Message = [method(Method), " ", Uri, " ", + version(Version), ?CRLF, + headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - - http_transport:send(SocketType, Socket, lists:append(Message)). + http_transport:send(SocketType, Socket, Message). + +do_send_body(SocketType, Socket, ProcessBody, Acc) -> + case ProcessBody(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + case http_transport:send(SocketType, Socket, Data) of + ok -> + do_send_body(SocketType, Socket, ProcessBody, NewAcc); + Error -> + Error + end + end. %%------------------------------------------------------------------------- @@ -162,7 +187,6 @@ is_client_closing(Headers) -> %%%======================================================================== post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) when (Method =:= post) orelse (Method =:= put) -> - ContentLength = body_length(Body), NewBody = case Headers#http_request_h.expect of "100-continue" -> ""; @@ -171,14 +195,22 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) end, NewHeaders = case HeadersAsIs of - [] -> - Headers#http_request_h{'content-type' = - ContentType, - 'content-length' = - ContentLength}; - _ -> - HeadersAsIs - end, + [] -> + Headers#http_request_h{ + 'content-type' = ContentType, + 'content-length' = case body_length(Body) of + undefined -> + % on upload streaming the caller must give a + % value to the Content-Length header + % (or use chunked Transfer-Encoding) + Headers#http_request_h.'content-length'; + Len when is_list(Len) -> + Len + end + }; + _ -> + HeadersAsIs + end, {NewHeaders, NewBody}; @@ -191,7 +223,10 @@ body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); body_length(Body) when is_list(Body) -> - integer_to_list(length(Body)). + integer_to_list(length(Body)); + +body_length({DataFun, _Acc}) when is_function(DataFun, 1) -> + undefined. method(Method) -> http_util:to_upper(atom_to_list(Method)). @@ -209,10 +244,6 @@ headers(_, "HTTP/0.9") -> headers(Headers, _) -> Headers. -socket_type(http) -> - ip_comm; -socket_type(https) -> - {ssl, []}. http_headers([], Headers) -> lists:flatten(Headers); diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index df7d40a33e..207b96271c 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -19,10 +19,12 @@ -module(httpc_response). --include("http_internal.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpc_internal.hrl"). %% API +%% Avoid warning for local function error/2 clashing with autoimported BIF. +-compile({no_auto_import,[error/2]}). -export([parse/1, result/2, send/2, error/2, is_server_closing/1, stream_start/3]). diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile index a715e3c9af..aaf3cfb995 100644 --- a/lib/inets/src/http_lib/Makefile +++ b/lib/inets/src/http_lib/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2011. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -56,24 +56,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- -# INETS FLAGS -# ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - - -# ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -INETS_ERL_FLAGS += -I ../inets_app -ifeq ($(WARN_UNUSED_WARS),true) -ERL_COMPILE_FLAGS += +warn_unused_vars -endif +include ../inets_app/inets.mk -ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \ - $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) \ + -I../../include \ + -I../inets_app # ---------------------------------------------------- @@ -95,9 +87,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/http_lib + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_lib + $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl index 621bc68eae..57647438e9 100644 --- a/lib/inets/src/http_lib/http_chunk.erl +++ b/lib/inets/src/http_lib/http_chunk.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% 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 @@ -17,7 +17,8 @@ %% %CopyrightEnd% %% %% Description: Implements chunked transfer encoding see RFC2616 section -%% 3.6.1 +%% 3.6.1 + -module(http_chunk). -include("http_internal.hrl"). @@ -28,6 +29,7 @@ %% little at a time on a socket. -export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]). + %%%========================================================================= %%% API %%%========================================================================= @@ -81,6 +83,9 @@ encode(Chunk) when is_binary(Chunk)-> HEXSize = list_to_binary(http_util:integer_to_hexlist(size(Chunk))), <<HEXSize/binary, ?CR, ?LF, Chunk/binary, ?CR, ?LF>>; +encode([<<>>]) -> + []; + encode(Chunk) when is_list(Chunk)-> HEXSize = http_util:integer_to_hexlist(erlang:iolist_size(Chunk)), [HEXSize, ?CR, ?LF, Chunk, ?CR, ?LF]. @@ -88,6 +93,7 @@ encode(Chunk) when is_list(Chunk)-> encode_last() -> <<$0, ?CR, ?LF, ?CR, ?LF >>. + %%------------------------------------------------------------------------- %% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord %% diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl index bb2e831727..2e924667c6 100644 --- a/lib/inets/src/http_lib/http_internal.hrl +++ b/lib/inets/src/http_lib/http_internal.hrl @@ -1,28 +1,37 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2002-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% --include("inets_internal.hrl"). +-ifndef(http_internal_hrl). +-define(http_internal_hrl, true). --define(HTTP_MAX_BODY_SIZE, nolimit). +-include_lib("inets/src/inets_app/inets_internal.hrl"). + +-define(HTTP_MAX_BODY_SIZE, nolimit). -define(HTTP_MAX_HEADER_SIZE, 10240). --define(HTTP_MAX_URI_SIZE, nolimit). +-define(HTTP_MAX_URI_SIZE, nolimit). + +-ifndef(HTTP_DEFAULT_SSL_KIND). +%% -define(HTTP_DEFAULT_SSL_KIND, ossl). +-define(HTTP_DEFAULT_SSL_KIND, essl). +-endif. % -ifdef(HTTP_DEFAULT_SSL_KIND). + %%% Response headers -record(http_response_h,{ @@ -106,3 +115,5 @@ 'last-modified', other=[] % list() - Key/Value list with other headers }). + +-endif. % -ifdef(http_internal_hrl). diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 587d215051..9b8190ebed 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -23,7 +23,7 @@ -export([ start/1, connect/3, connect/4, - listen/2, listen/3, + listen/2, listen/3, listen/4, accept/2, accept/3, close/2, send/3, @@ -33,10 +33,12 @@ 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"). --include("inets_internal.hrl"). -define(SERVICE, httpl). -define(hlri(Label, Content), ?report_important(Label, ?SERVICE, Content)). -define(hlrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)). @@ -55,6 +57,18 @@ %% Description: Makes sure inet_db or ssl is started. %%------------------------------------------------------------------------- start(ip_comm) -> + do_start_ip_comm(); + +%% This is just for backward compatibillity +start({ssl, _}) -> + do_start_ssl(); +start({ossl, _}) -> + do_start_ssl(); +start({essl, _}) -> + do_start_ssl(). + + +do_start_ip_comm() -> case inet_db:start() of {ok, _} -> ok; @@ -62,8 +76,9 @@ start(ip_comm) -> ok; Error -> Error - end; -start({ssl, _}) -> + end. + +do_start_ssl() -> case ssl:start() of ok -> ok; @@ -95,28 +110,61 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], ?hlrt("connect using gen_tcp", [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]), - gen_tcp:connect(Host, Port, Opts, Timeout); + try gen_tcp:connect(Host, Port, Opts, Timeout) of + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + catch + exit:{badarg, _} -> + {error, {eoptions, Opts}}; + exit:badarg -> + {error, {eoptions, Opts}} + end; -connect({ssl, SslConfig}, {Host, Port}, _, Timeout) -> - Opts = [binary, {active, false}] ++ SslConfig, - ?hlrt("connect using ssl", - [{host, Host}, {port, Port}, {ssl_config, SslConfig}, - {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout); +%% Wrapper for backaward compatibillity +connect({ssl, SslConfig}, Address, Opts, Timeout) -> + connect({?HTTP_DEFAULT_SSL_KIND, SslConfig}, Address, Opts, Timeout); + +connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> + Opts = [binary, {active, false}, {ssl_imp, old}] ++ SslConfig, + ?hlrt("connect using ossl", + [{host, Host}, + {port, Port}, + {ssl_config, SslConfig}, + {timeout, Timeout}]), + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end; -connect({erl_ssl, SslConfig}, {Host, Port}, _, Timeout) -> - Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, - ?hlrt("connect using erl_ssl", - [{host, Host}, {port, Port}, {ssl_config, SslConfig}, - {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout). +connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> + Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig, + ?hlrt("connect using essl", + [{host, Host}, + {port, Port}, + {ssl_config, SslConfig}, + {timeout, Timeout}]), + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end. %%------------------------------------------------------------------------- -%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason} +%% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason} %% SocketType = ip_comm | {ssl, SSLConfig} %% Port = integer() -%% Socket = socket() +%% Socket = socket() +%% Fd = undefined | fd() %% %% Description: Sets up socket to listen on the port Port on the local %% host using either gen_tcp or ssl. In the gen_tcp case the port @@ -128,25 +176,45 @@ connect({erl_ssl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm, Addr, Port) -> - case (catch listen_ip_comm(Addr, Port)) of +listen(ip_comm = _SocketType, Addr, Port) -> + listen_ip_comm(Addr, Port, undefined); + +%% Wrapper for backaward compatibillity +listen({ssl, SSLConfig}, Addr, Port) -> + ?hlrt("listen (wrapper)", + [{addr, Addr}, + {port, Port}, + {ssl_config, SSLConfig}]), + listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port); + +listen({ossl, SSLConfig}, Addr, Port) -> + ?hlrt("listen (ossl)", + [{addr, Addr}, + {port, Port}, + {ssl_config, SSLConfig}]), + listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]); + +listen({essl, SSLConfig}, Addr, Port) -> + ?hlrt("listen (essl)", + [{addr, Addr}, + {port, Port}, + {ssl_config, SSLConfig}]), + listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]). + + +listen(ip_comm, Addr, Port, Fd) -> + 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({ssl, SSLConfig} = Ssl, Addr, Port) -> - Opt = sock_opt(Ssl, Addr, SSLConfig), - ssl:listen(Port, Opt); - -listen({erl_ssl, SSLConfig} = Ssl, Addr, Port) -> - Opt = sock_opt(Ssl, Addr, SSLConfig), - ssl:listen(Port, [{ssl_imp, new} | Opt]). - + end. -listen_ip_comm(Addr, Port) -> - {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port), +do_listen_ip_comm(Addr, Port, Fd) -> + {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts], @@ -156,7 +224,7 @@ listen_ip_comm(Addr, Port) -> (Reason =:= eafnosupport)) -> Opts3 = [inet | Opts], ?hlrt("ipv6 listen failed - try ipv4 instead", - [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), + [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), gen_tcp:listen(NewPort, Opts3); %% This is when a given hostname has resolved to a @@ -165,10 +233,11 @@ listen_ip_comm(Addr, Port) -> {'EXIT', Reason} -> Opts3 = [inet | Opts], ?hlrt("ipv6 listen exit - try ipv4 instead", - [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), + [{reason, Reason}, {port, NewPort}, {opts, Opts3}]), gen_tcp:listen(NewPort, Opts3); Other -> + ?hlrt("ipv6 listen done", [{other, Other}]), Other end; _ -> @@ -177,32 +246,74 @@ listen_ip_comm(Addr, Port) -> 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). -get_socket_info(Addr, Port) -> - Key = list_to_atom("httpd_" ++ integer_to_list(Port)), - BaseOpts = [{backlog, 128}, {reuseaddr, true}], +get_socket_info(Addr, Port, Fd0) -> + BaseOpts = [{backlog, 128}, {reuseaddr, true}], IpFamilyDefault = ipfamily_default(Addr, Port), - case init:get_argument(Key) of - {ok, [[Value]]} -> - {Fd, IpFamily} = - case string:tokens(Value, [$|]) of - [FdStr, IpFamilyStr] -> - Fd0 = fd_of(FdStr), - IpFamily0 = ip_family_of(IpFamilyStr), - {Fd0, IpFamily0}; - [FdStr] -> - {fd_of(FdStr), IpFamilyDefault}; - _ -> - throw({error, {bad_descriptor, Value}}) - end, - {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily}; - error -> - {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} + %% The presence of a file descriptor takes precedence + case get_fd(Port, Fd0, IpFamilyDefault) of + {Fd, IpFamily} -> + {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily}; + undefined -> + {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault} end. +get_fd(Port, undefined = _Fd, IpFamilyDefault) -> + FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), + case init:get_argument(FdKey) of + {ok, [[Value]]} -> + case string:tokens(Value, [$|]) of + [FdStr, IpFamilyStr] -> + {fd_of(FdStr), ip_family_of(IpFamilyStr)}; + [FdStr] -> + {fd_of(FdStr), IpFamilyDefault}; + _ -> + throw({error, {bad_descriptor, Value}}) + end; + error -> + undefined + end; +get_fd(_Port, Fd, IpFamilyDefault) -> + {Fd, IpFamilyDefault}. + fd_of(FdStr) -> case (catch list_to_integer(FdStr)) of Fd when is_integer(Fd) -> @@ -234,9 +345,17 @@ ip_family_of(IpFamilyStr) -> %%------------------------------------------------------------------------- accept(SocketType, ListenSocket) -> accept(SocketType, ListenSocket, infinity). + accept(ip_comm, ListenSocket, Timeout) -> gen_tcp:accept(ListenSocket, Timeout); -accept({ssl,_SSLConfig}, ListenSocket, Timeout) -> + +%% Wrapper for backaward compatibillity +accept({ssl, SSLConfig}, ListenSocket, Timeout) -> + accept({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, ListenSocket, Timeout); + +accept({ossl, _SSLConfig}, ListenSocket, Timeout) -> + ssl:transport_accept(ListenSocket, Timeout); +accept({essl, _SSLConfig}, ListenSocket, Timeout) -> ssl:transport_accept(ListenSocket, Timeout). @@ -250,7 +369,15 @@ accept({ssl,_SSLConfig}, ListenSocket, Timeout) -> %%------------------------------------------------------------------------- controlling_process(ip_comm, Socket, NewOwner) -> gen_tcp:controlling_process(Socket, NewOwner); -controlling_process({ssl, _}, Socket, NewOwner) -> + +%% Wrapper for backaward compatibillity +controlling_process({ssl, SSLConfig}, Socket, NewOwner) -> + controlling_process({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, NewOwner); + +controlling_process({ossl, _}, Socket, NewOwner) -> + ssl:controlling_process(Socket, NewOwner); + +controlling_process({essl, _}, Socket, NewOwner) -> ssl:controlling_process(Socket, NewOwner). @@ -265,9 +392,23 @@ controlling_process({ssl, _}, Socket, NewOwner) -> setopts(ip_comm, Socket, Options) -> ?hlrt("ip_comm setopts", [{socket, Socket}, {options, Options}]), inet:setopts(Socket, Options); -setopts({ssl, _}, Socket, Options) -> - ?hlrt("ssl setopts", [{socket, Socket}, {options, Options}]), - ssl:setopts(Socket, Options). + +%% Wrapper for backaward compatibillity +setopts({ssl, SSLConfig}, Socket, Options) -> + setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); + +setopts({ossl, _}, Socket, Options) -> + ?hlrt("[o]ssl setopts", [{socket, Socket}, {options, Options}]), + Reason = (catch ssl:setopts(Socket, Options)), + ?hlrt("[o]ssl setopts result", [{reason, Reason}]), + Reason; + + +setopts({essl, _}, Socket, Options) -> + ?hlrt("[e]ssl setopts", [{socket, Socket}, {options, Options}]), + Reason = (catch ssl:setopts(Socket, Options)), + ?hlrt("[e]ssl setopts result", [{reason, Reason}]), + Reason. %%------------------------------------------------------------------------- @@ -289,15 +430,27 @@ getopts(ip_comm, Socket, Options) -> {error, _} -> [] end; -getopts({ssl, _}, Socket, Options) -> + +%% Wrapper for backaward compatibillity +getopts({ssl, SSLConfig}, Socket, Options) -> + getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options); + +getopts({ossl, _}, Socket, Options) -> ?hlrt("ssl getopts", [{socket, Socket}, {options, Options}]), + getopts_ssl(Socket, Options); + +getopts({essl, _}, Socket, Options) -> + ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]), + getopts_ssl(Socket, Options). + +getopts_ssl(Socket, Options) -> case ssl:getopts(Socket, Options) of {ok, SocketOpts} -> SocketOpts; {error, _} -> [] end. - + %%------------------------------------------------------------------------- %% getstat(SocketType, Socket) -> socket_stats() @@ -314,8 +467,15 @@ getstat(ip_comm = _SocketType, Socket) -> {error, _} -> [] end; -getstat({ssl, _} = _SocketType, _Socket) -> - %% ?hlrt("ssl getstat", [{socket, Socket}]), + +%% Wrapper for backaward compatibillity +getstat({ssl, SSLConfig}, Socket) -> + getstat({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); + +getstat({ossl, _} = _SocketType, _Socket) -> + []; + +getstat({essl, _} = _SocketType, _Socket) -> []. @@ -328,7 +488,15 @@ getstat({ssl, _} = _SocketType, _Socket) -> %%------------------------------------------------------------------------- send(ip_comm, Socket, Message) -> gen_tcp:send(Socket, Message); -send({ssl, _}, Socket, Message) -> + +%% Wrapper for backaward compatibillity +send({ssl, SSLConfig}, Socket, Message) -> + send({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Message); + +send({ossl, _}, Socket, Message) -> + ssl:send(Socket, Message); + +send({essl, _}, Socket, Message) -> ssl:send(Socket, Message). @@ -341,9 +509,18 @@ send({ssl, _}, Socket, Message) -> %%------------------------------------------------------------------------- close(ip_comm, Socket) -> gen_tcp:close(Socket); -close({ssl, _}, Socket) -> + +%% Wrapper for backaward compatibillity +close({ssl, SSLConfig}, Socket) -> + close({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); + +close({ossl, _}, Socket) -> + ssl:close(Socket); + +close({essl, _}, Socket) -> ssl:close(Socket). + %%------------------------------------------------------------------------- %% peername(SocketType, Socket) -> {Port, SockName} %% SocketType = ip_comm | {ssl, _} @@ -355,48 +532,28 @@ close({ssl, _}, Socket) -> %% connection, usning either gen_tcp or ssl. %%------------------------------------------------------------------------- peername(ip_comm, Socket) -> - case inet:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - PeerName = 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) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, PeerName}; - {error, Reason} -> - Report = io_lib:format("~p Failed getting PeerName for socket ~p: " - "~n Reason: ~p" - "~n Socket stat: ~p" - "~n IfList: ~p" - "~n Fd: ~p" - "~n", - [self(), - Socket, - Reason, - (catch inet:getstat(Socket)), - (catch inet:getiflist(Socket)), - (catch inet:getfd(Socket)) - ]), - (catch error_logger:error_report(Report)), - {-1, "unknown"} - end; + do_peername(inet:peername(Socket)); -peername({ssl, _}, Socket) -> - case ssl:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {error, _} -> - {-1, "unknown"} - end. +%% Wrapper for backaward compatibillity +peername({ssl, SSLConfig}, Socket) -> + peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); + +peername({ossl, _}, Socket) -> + do_peername(ssl:peername(Socket)); + +peername({essl, _}, Socket) -> + 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"}. %%------------------------------------------------------------------------- @@ -410,34 +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,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - SockName = 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) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end; + do_sockname(inet:sockname(Socket)); -sockname({ssl, _}, Socket) -> - case ssl:sockname(Socket) of - {ok,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end. +%% Wrapper for backaward compatibillity +sockname({ssl, SSLConfig}, Socket) -> + sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); + +sockname({ossl, _}, Socket) -> + do_sockname(ssl:sockname(Socket)); + +sockname({essl, _}, Socket) -> + 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"}. %%------------------------------------------------------------------------- @@ -451,46 +602,75 @@ resolve() -> Name. +%%------------------------------------------------------------------------- +%% 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) ++ "." ++ + integer_to_list(C) ++ "." ++ + integer_to_list(D). + +ipv6_name({A, B, C, D, E, F, G, H}) -> + 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) ++ ":" ++ + http_util:integer_to_hexlist(F) ++ ":" ++ + http_util:integer_to_hexlist(G) ++ ":" ++ + 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; -negotiate({ssl,_},Socket,Timeout) -> - negotiate(Socket, Timeout); -negotiate({erl_ssl, _}, Socket, Timeout) -> - negotiate(Socket, Timeout). - -negotiate(Socket, Timeout) -> +negotiate({ssl, SSLConfig}, Socket, Timeout) -> + ?hlrt("negotiate(ssl)", []), + negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout); +negotiate({ossl, _}, Socket, Timeout) -> + ?hlrt("negotiate(ossl)", []), + negotiate_ssl(Socket, Timeout); +negotiate({essl, _}, Socket, Timeout) -> + ?hlrt("negotiate(essl)", []), + negotiate_ssl(Socket, Timeout). + +negotiate_ssl(Socket, Timeout) -> + ?hlrt("negotiate_ssl", [{socket, Socket}, {timeout, Timeout}]), case ssl:ssl_accept(Socket, Timeout) of ok -> ok; - {error, Error} -> - case lists:member(Error, - [timeout,econnreset,esslaccept,esslerrssl]) of + {error, Reason} -> + ?hlrd("negotiate_ssl - accept failed", [{reason, Reason}]), + %% Look for "valid" error reasons + ValidReasons = [timeout, econnreset, esslaccept, esslerrssl], + case lists:member(Reason, ValidReasons) of true -> - {error,normal}; + {error, normal}; false -> - {error, Error} + {error, Reason} end end. diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl index 3804af60f4..44b9face0b 100644 --- a/lib/inets/src/http_lib/http_uri.erl +++ b/lib/inets/src/http_lib/http_uri.erl @@ -1,26 +1,25 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2006-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% -module(http_uri). --export([parse/1]). -export([parse/1, encode/1, decode/1]). %%%========================================================================= @@ -35,7 +34,7 @@ parse(AbsURI) -> {UserInfo, Host, Port, Path, Query} -> {Scheme, UserInfo, Host, Port, Path, Query}; _ -> - {error, {malformed_url, AbsURI}} + {error, {malformed_url, AbsURI}} end end. @@ -43,7 +42,9 @@ encode(URI) -> Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?, $#, $[, $], $<, $>, $\", ${, $}, $|, $\\, $', $^, $%, $ ]), - lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)). + lists:append(lists:map(fun(Char) -> + uri_encode(Char, Reserved) + end, URI)). decode([$%,Hex1,Hex2|Rest]) -> [hex2dec(Hex1)*16+hex2dec(Hex2)|decode(Rest)]; @@ -69,7 +70,8 @@ parse_scheme(AbsURI) -> end. parse_uri_rest(Scheme, "//" ++ URIPart) -> - {Authority, PathQuery} = + + {Authority, PathQuery} = case split_uri(URIPart, "/", URIPart, 1, 0) of Split = {_, _} -> Split; @@ -81,7 +83,7 @@ parse_uri_rest(Scheme, "//" ++ URIPart) -> {URIPart,""} end end, - + {UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1), {Host, Port} = parse_host_port(Scheme, HostPort), {Path, Query} = parse_path_query(PathQuery), @@ -91,7 +93,6 @@ parse_uri_rest(Scheme, "//" ++ URIPart) -> parse_path_query(PathQuery) -> {Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0), {path(Path), Query}. - parse_host_port(Scheme,"[" ++ HostPort) -> %ipv6 DefaultPort = default_port(Scheme), @@ -103,12 +104,12 @@ parse_host_port(Scheme, HostPort) -> DefaultPort = default_port(Scheme), {Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1), {Host, int_port(Port)}. - + split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) -> case inets_regexp:first_match(UriPart, SplitChar) of {match, Match, _} -> {string:substr(UriPart, 1, Match - SkipLeft), - string:substr(UriPart, Match + SkipRight, length(UriPart))}; + string:substr(UriPart, Match + SkipRight, length(UriPart))}; nomatch -> NoMatchResult end. @@ -132,9 +133,9 @@ uri_encode(Char, Reserved) -> case sets:is_element(Char, Reserved) of true -> [ $% | http_util:integer_to_hexlist(Char)]; - false -> - [Char] - end. + false -> + [Char] + end. hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0; hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10; diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index be0602ff6e..973600d7be 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -24,9 +24,9 @@ convert_netscapecookie_date/1, hexlist_to_integer/1, integer_to_hexlist/1, convert_month/1, - is_hostname/1, - html_encode/1, - timestamp/0, timeout/2 + is_hostname/1, + timestamp/0, timeout/2, + html_encode/1 ]). @@ -104,6 +104,22 @@ convert_netscapecookie_date([_D,_A,_Y, $ , Sec = list_to_integer([S1,S2]), {{Year,Month,Day},{Hour,Min,Sec}}; +%% Example: Tue Jan 01 08:00:01 2036 GMT +convert_netscapecookie_date([_D,_A,_Y, $ , + M,O,N, $ , + D1,D2, $ , + H1,H2, $:, + M1,M2, $:, + S1,S2, $ , + Y1,Y2,Y3,Y4, $ |_Rest]) -> + Year = list_to_integer([Y1,Y2,Y3,Y4]), + Day = list_to_integer([D1,D2]), + Month = convert_month([M,O,N]), + Hour = list_to_integer([H1,H2]), + Min = list_to_integer([M1,M2]), + Sec = list_to_integer([S1,S2]), + {{Year,Month,Day},{Hour,Min,Sec}}; + %% Sloppy... convert_netscapecookie_date([_D,_A,_Y, $,, _SP, D1,D2,_DA, @@ -186,13 +202,13 @@ timeout(Timeout, Started) -> _ -> 0 end. - + html_encode(Chars) -> Reserved = sets:from_list([$&, $<, $>, $\", $', $/]), lists:append(lists:map(fun(Char) -> - char_to_html_entity(Char, Reserved) - end, Chars)). + char_to_html_entity(Char, Reserved) + end, Chars)). %%%======================================================================== @@ -246,8 +262,8 @@ convert_to_ascii([Num | Reversed], Number) char_to_html_entity(Char, Reserved) -> case sets:is_element(Char, Reserved) of - true -> - "&#" ++ integer_to_list(Char) ++ ";"; - false -> - [Char] + true -> + "&#" ++ integer_to_list(Char) ++ ";"; + false -> + [Char] end. diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 3c36b384b8..55cc68dede 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2011. All Rights Reserved. +# Copyright Ericsson AB 2005-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 @@ -82,7 +82,7 @@ MODULES = \ mod_security \ mod_security_server -HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl +HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -90,24 +90,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- -# INETS FLAGS -# ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - - -# ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -INETS_ERL_FLAGS += \ - -I ../http_lib \ - -I ../inets_app \ - -pa $(ERL_TOP)/lib/inets/ebin + +include ../inets_app/inets.mk ERL_COMPILE_FLAGS += \ - $(INETS_ERL_FLAGS) \ $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' + $(INETS_ERL_COMPILE_FLAGS) \ + -I../inets_app \ + -I../http_lib \ # ---------------------------------------------------- @@ -129,9 +121,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/http_server + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_server + $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl index a88d002b03..93608dbf96 100644 --- a/lib/inets/src/http_server/httpd.erl +++ b/lib/inets/src/http_server/httpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -24,54 +24,24 @@ -include("httpd.hrl"). --deprecated({start, 0, next_major_release}). --deprecated({start, 1, next_major_release}). --deprecated({start_link, 1, next_major_release}). --deprecated({start_child, 0, next_major_release}). --deprecated({start_child, 1, next_major_release}). --deprecated({stop, 0, next_major_release}). --deprecated({stop, 1, next_major_release}). --deprecated({stop, 2, next_major_release}). --deprecated({stop_child, 0, next_major_release}). --deprecated({stop_child, 1, next_major_release}). --deprecated({stop_child, 2, next_major_release}). --deprecated({restart, 0, next_major_release}). --deprecated({restart, 1, next_major_release}). --deprecated({restart, 2, next_major_release}). --deprecated({block, 0, next_major_release}). --deprecated({block, 1, next_major_release}). --deprecated({block, 2, next_major_release}). --deprecated({block, 3, next_major_release}). --deprecated({block, 4, next_major_release}). --deprecated({unblock, 0, next_major_release}). --deprecated({unblock, 1, next_major_release}). --deprecated({unblock, 2, next_major_release}). - %% Behavior callbacks --export([start_standalone/1, start_service/1, stop_service/1, services/0, - service_info/1]). +-export([ + start_standalone/1, + start_service/1, + stop_service/1, + services/0, + service_info/1 + ]). %% API -export([parse_query/1, reload_config/2, info/1, info/2, info/3]). -%% Deprecated --export([start/0, start/1, - start_link/0, start_link/1, - start_child/0,start_child/1, - stop/0,stop/1,stop/2, - stop_child/0,stop_child/1,stop_child/2, - restart/0,restart/1,restart/2]). - -%% Management stuff should be internal functions -%% Will be from r13 --export([block/0,block/1,block/2,block/3,block/4, - unblock/0,unblock/1,unblock/2]). - -%% Internal Debugging and status info stuff... -%% Keep for now should probably be moved to test catalog --export([get_status/1,get_status/2,get_status/3, - get_admin_state/0,get_admin_state/1,get_admin_state/2, - get_usage_state/0,get_usage_state/1,get_usage_state/2]). +%% Internal debugging and status info stuff... +-export([ + get_status/1, get_status/2, get_status/3, + get_admin_state/0, get_admin_state/1, get_admin_state/2, + get_usage_state/0, get_usage_state/1, get_usage_state/2 + ]). %%%======================================================================== %%% API @@ -111,6 +81,7 @@ info(Address, Port, Properties) when is_integer(Port) andalso is_list(Properties) -> httpd_conf:get_config(Address, Port, Properties). + %%%======================================================================== %%% Behavior callbacks %%%======================================================================== @@ -149,6 +120,8 @@ service_info(Pid) -> exit:{noproc, _} -> {error, service_not_available} end. + + %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- @@ -176,6 +149,7 @@ child_name2info({httpd_instance_sup, Address, Port}) -> {ok, [{bind_address, Address}, {port, Port} | Info]} end. + reload(Config, Address, Port) -> Name = make_name(Address,Port), case whereis(Name) of @@ -185,26 +159,12 @@ reload(Config, Address, Port) -> {error,not_started} end. -reload(Addr, Port) when is_integer(Port) -> - Name = make_name(Addr,Port), - case whereis(Name) of - Pid when is_pid(Pid) -> - httpd_manager:reload(Pid, undefined); - _ -> - {error,not_started} - end. %%% ========================================================= -%%% Function: block/0, block/1, block/2, block/3, block/4 -%%% block() -%%% block(Port) -%%% block(ConfigFile) -%%% block(Addr,Port) -%%% block(Port,Mode) -%%% block(ConfigFile,Mode) -%%% block(Addr,Port,Mode) -%%% block(ConfigFile,Mode,Timeout) -%%% block(Addr,Port,Mode,Timeout) +%%% Function: block/3, block/4 +%%% block(Addr, Port, Mode) +%%% block(ConfigFile, Mode, Timeout) +%%% block(Addr, Port, Mode, Timeout) %%% %%% Returns: ok | {error,Reason} %%% @@ -237,58 +197,32 @@ reload(Addr, Port) when is_integer(Port) -> %%% Mode -> disturbing | non_disturbing %%% Timeout -> integer() %%% -block() -> block(undefined,8888,disturbing). -block(Port) when is_integer(Port) -> - block(undefined,Port,disturbing); +block(Addr, Port, disturbing) when is_integer(Port) -> + do_block(Addr, Port, disturbing); +block(Addr, Port, non_disturbing) when is_integer(Port) -> + do_block(Addr, Port, non_disturbing); -block(ConfigFile) when is_list(ConfigFile) -> +block(ConfigFile, Mode, Timeout) + when is_list(ConfigFile) andalso + is_atom(Mode) andalso + is_integer(Timeout) -> case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - block(Addr,Port,disturbing); - Error -> - Error - end. - -block(Addr,Port) when is_integer(Port) -> - block(Addr,Port,disturbing); - -block(Port,Mode) when is_integer(Port) andalso is_atom(Mode) -> - block(undefined,Port,Mode); - -block(ConfigFile,Mode) when is_list(ConfigFile) andalso is_atom(Mode) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - block(Addr,Port,Mode); - Error -> - Error - end. - - -block(Addr,Port,disturbing) when is_integer(Port) -> - do_block(Addr,Port,disturbing); -block(Addr,Port,non_disturbing) when is_integer(Port) -> - do_block(Addr,Port,non_disturbing); - -block(ConfigFile,Mode,Timeout) when is_list(ConfigFile) andalso - is_atom(Mode) andalso - is_integer(Timeout) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - block(Addr,Port,Mode,Timeout); + {ok, Addr, Port} -> + block(Addr, Port, Mode, Timeout); Error -> Error end. -block(Addr,Port,non_disturbing,Timeout) +block(Addr, Port, non_disturbing, Timeout) + when is_integer(Port) andalso is_integer(Timeout) -> + do_block(Addr, Port, non_disturbing, Timeout); +block(Addr,Port,disturbing,Timeout) when is_integer(Port) andalso is_integer(Timeout) -> - do_block(Addr,Port,non_disturbing,Timeout); -block(Addr,Port,disturbing,Timeout) when is_integer(Port) andalso - is_integer(Timeout) -> - do_block(Addr,Port,disturbing,Timeout). + do_block(Addr, Port, disturbing, Timeout). -do_block(Addr,Port,Mode) when is_integer(Port) andalso is_atom(Mode) -> +do_block(Addr, Port, Mode) when is_integer(Port) andalso is_atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of Pid when is_pid(Pid) -> @@ -298,7 +232,7 @@ do_block(Addr,Port,Mode) when is_integer(Port) andalso is_atom(Mode) -> end. -do_block(Addr,Port,Mode,Timeout) +do_block(Addr, Port, Mode, Timeout) when is_integer(Port) andalso is_atom(Mode) -> Name = make_name(Addr,Port), case whereis(Name) of @@ -310,11 +244,8 @@ do_block(Addr,Port,Mode,Timeout) %%% ========================================================= -%%% Function: unblock/0, unblock/1, unblock/2 -%%% unblock() -%%% unblock(Port) -%%% unblock(ConfigFile) -%%% unblock(Addr,Port) +%%% Function: unblock/2 +%%% unblock(Addr, Port) %%% %%% Description: This function is used to reverse a previous block %%% operation on the HTTP server. @@ -323,16 +254,6 @@ do_block(Addr,Port,Mode,Timeout) %%% Addr -> {A,B,C,D} | string() | undefined %%% ConfigFile -> string() %%% -unblock() -> unblock(undefined,8888). -unblock(Port) when is_integer(Port) -> unblock(undefined,Port); - -unblock(ConfigFile) when is_list(ConfigFile) -> - case get_addr_and_port(ConfigFile) of - {ok,Addr,Port} -> - unblock(Addr,Port); - Error -> - Error - end. unblock(Addr, Port) when is_integer(Port) -> Name = make_name(Addr,Port), @@ -349,8 +270,8 @@ foreach([KeyValue|Rest]) -> {ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "), case inets_regexp:split(Plus2Space,"=") of {ok,[Key|Value]} -> - [{http_uri:decode(Key), http_uri:decode(lists:flatten(Value))}| - foreach(Rest)]; + [{http_uri:decode(Key), + http_uri:decode(lists:flatten(Value))}|foreach(Rest)]; {ok,_} -> foreach(Rest) end. @@ -521,80 +442,81 @@ do_reload_config(ConfigList, Mode) -> %%%-------------------------------------------------------------- %%% Deprecated %%%-------------------------------------------------------------- -start() -> - start("/var/tmp/server_root/conf/8888.conf"). -start(ConfigFile) -> - {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone), - unlink(Pid), - {ok, Pid}. +%% start() -> +%% start("/var/tmp/server_root/conf/8888.conf"). -start_link() -> - start("/var/tmp/server_root/conf/8888.conf"). +%% start(ConfigFile) -> +%% {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone), +%% unlink(Pid), +%% {ok, Pid}. -start_link(ConfigFile) when is_list(ConfigFile) -> - inets:start(httpd, ConfigFile, stand_alone). +%% start_link() -> +%% start("/var/tmp/server_root/conf/8888.conf"). -stop() -> - stop(8888). +%% start_link(ConfigFile) when is_list(ConfigFile) -> +%% inets:start(httpd, ConfigFile, stand_alone). -stop(Port) when is_integer(Port) -> - stop(undefined, Port); -stop(Pid) when is_pid(Pid) -> - old_stop(Pid); -stop(ConfigFile) when is_list(ConfigFile) -> - old_stop(ConfigFile). +%% stop() -> +%% stop(8888). -stop(Addr, Port) when is_integer(Port) -> - old_stop(Addr, Port). +%% stop(Port) when is_integer(Port) -> +%% stop(undefined, Port); +%% stop(Pid) when is_pid(Pid) -> +%% old_stop(Pid); +%% stop(ConfigFile) when is_list(ConfigFile) -> +%% old_stop(ConfigFile). -start_child() -> - start_child("/var/tmp/server_root/conf/8888.conf"). +%% stop(Addr, Port) when is_integer(Port) -> +%% old_stop(Addr, Port). -start_child(ConfigFile) -> - httpd_sup:start_child(ConfigFile). +%% start_child() -> +%% start_child("/var/tmp/server_root/conf/8888.conf"). -stop_child() -> - stop_child(8888). +%% start_child(ConfigFile) -> +%% httpd_sup:start_child(ConfigFile). -stop_child(Port) -> - stop_child(undefined, Port). +%% stop_child() -> +%% stop_child(8888). -stop_child(Addr, Port) when is_integer(Port) -> - httpd_sup:stop_child(Addr, Port). +%% stop_child(Port) -> +%% stop_child(undefined, Port). -restart() -> reload(undefined, 8888). +%% stop_child(Addr, Port) when is_integer(Port) -> +%% httpd_sup:stop_child(Addr, Port). -restart(Port) when is_integer(Port) -> - reload(undefined, Port). -restart(Addr, Port) -> - reload(Addr, Port). +%% restart() -> reload(undefined, 8888). -old_stop(Pid) when is_pid(Pid) -> - do_stop(Pid); -old_stop(ConfigFile) when is_list(ConfigFile) -> - case get_addr_and_port(ConfigFile) of - {ok, Addr, Port} -> - old_stop(Addr, Port); - - Error -> - Error - end; -old_stop(_StartArgs) -> - ok. +%% restart(Port) when is_integer(Port) -> +%% reload(undefined, Port). +%% restart(Addr, Port) -> +%% reload(Addr, Port). -old_stop(Addr, Port) when is_integer(Port) -> - Name = old_make_name(Addr, Port), - case whereis(Name) of - Pid when is_pid(Pid) -> - do_stop(Pid), - ok; - _ -> - not_started - end. +%% old_stop(Pid) when is_pid(Pid) -> +%% do_stop(Pid); +%% old_stop(ConfigFile) when is_list(ConfigFile) -> +%% case get_addr_and_port(ConfigFile) of +%% {ok, Addr, Port} -> +%% old_stop(Addr, Port); + +%% Error -> +%% Error +%% end; +%% old_stop(_StartArgs) -> +%% ok. + +%% old_stop(Addr, Port) when is_integer(Port) -> +%% Name = old_make_name(Addr, Port), +%% case whereis(Name) of +%% Pid when is_pid(Pid) -> +%% do_stop(Pid), +%% ok; +%% _ -> +%% not_started +%% end. -do_stop(Pid) -> - exit(Pid, shutdown). +%% do_stop(Pid) -> +%% exit(Pid, shutdown). -old_make_name(Addr,Port) -> - httpd_util:make_name("httpd_instance_sup",Addr,Port). +%% old_make_name(Addr,Port) -> +%% httpd_util:make_name("httpd_instance_sup",Addr,Port). diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl index 0db8a029bb..4eba833e2c 100644 --- a/lib/inets/src/http_server/httpd.hrl +++ b/lib/inets/src/http_server/httpd.hrl @@ -1,82 +1,27 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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/. -%% +%% %% 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. -%% -%% %CopyrightEnd% %% +%% %CopyrightEnd% %% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. --include_lib("kernel/include/file.hrl"). - --ifndef(SERVER_SOFTWARE). --define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile! --endif. --define(SERVER_PROTOCOL,"HTTP/1.1"). --define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, - mod_dir, mod_get, mod_head, mod_log, mod_disk_log]). --define(SOCKET_CHUNK_SIZE,8192). --define(SOCKET_MAX_POLL,25). --define(FILE_CHUNK_SIZE,64*1024). --define(GATEWAY_INTERFACE,"CGI/1.1"). --define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)). --define(DEFAULT_CONTEXT, - [{errmsg,"[an error occurred while processing this directive]"}, - {timefmt,"%A, %d-%b-%y %T %Z"}, - {sizefmt,"abbrev"}]). - - --ifdef(inets_error). --define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n", - [self(),?MODULE,?LINE]++Args)). --else. --define(ERROR(F,A),[]). --endif. - --ifdef(inets_log). --define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n", - [self(),?MODULE,?LINE]++Args)). --else. --define(LOG(F,A),[]). --endif. - --ifdef(inets_debug). --define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n", - [self(),?MODULE,?LINE]++Args)). --else. --define(DEBUG(F,A),[]). --endif. - --ifdef(inets_cdebug). --define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n", - [self(),?MODULE,?LINE]++Args)). --else. --define(CDEBUG(F,A),[]). --endif. +-ifndef(src_httpd_hrl). +-define(src_httpd_hrl, true). +-include_lib("inets/include/httpd.hrl"). --record(init_data,{peername,resolve}). --record(mod,{init_data, - data=[], - socket_type=ip_comm, - socket, - config_db, - method, - absolute_uri=[], - request_uri, - http_version, - request_line, - parsed_header=[], - entity_body, - connection}). +-endif. % -ifdef(src_httpd_hrl). diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl index fef3fe58c6..bcebb6a9e3 100644 --- a/lib/inets/src/http_server/httpd_acceptor.erl +++ b/lib/inets/src/http_server/httpd_acceptor.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -139,9 +139,9 @@ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) -> handle_error(Reason, ConfigDb), ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout); - {'EXIT', Reason} -> - ?hdri("accept exited", [{reason, Reason}]), - handle_error({'EXIT', Reason}, ConfigDb), + {'EXIT', _Reason} = EXIT -> + ?hdri("accept exited", [{reason, _Reason}]), + handle_error(EXIT, ConfigDb), ?MODULE:acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) end. diff --git a/lib/inets/src/http_server/httpd_cgi.erl b/lib/inets/src/http_server/httpd_cgi.erl index 0532d7d100..c06a06aad3 100644 --- a/lib/inets/src/http_server/httpd_cgi.erl +++ b/lib/inets/src/http_server/httpd_cgi.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -21,7 +21,8 @@ -export([parse_headers/1, handle_headers/1]). --include("inets_internal.hrl"). +-include_lib("inets/src/inets_app/inets_internal.hrl"). + %%%========================================================================= %%% Internal application API diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 2faf13e9b3..5352eb8bb9 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -25,9 +25,10 @@ %% Application internal API -export([load/1, load/2, load_mime_types/1, store/1, store/2, - remove/1, remove_all/1, config/1, get_config/2, get_config/3, - lookup/2, lookup/3, lookup/4, - validate_properties/1]). + remove/1, remove_all/1, get_config/2, get_config/3, + lookup_socket_type/1, + lookup/2, lookup/3, lookup/4, + validate_properties/1]). -define(VMODULE,"CONF"). -include("httpd_internal.hrl"). @@ -217,9 +218,12 @@ load("ServerName " ++ ServerName, []) -> {ok,[],{server_name,clean(ServerName)}}; load("SocketType " ++ SocketType, []) -> - case check_enum(clean(SocketType),["ssl","ip_comm"]) of + %% ssl is the same as HTTP_DEFAULT_SSL_KIND + %% ossl is ssl based on OpenSSL (the "old" ssl) + %% essl is the pure Erlang-based ssl (the "new" ssl) + case check_enum(clean(SocketType), ["ssl", "ossl", "essl", "ip_comm"]) of {ok, ValidSocketType} -> - {ok, [], {socket_type,ValidSocketType}}; + {ok, [], {socket_type, ValidSocketType}}; {error,_} -> {error, ?NICE(clean(SocketType) ++ " is an invalid SocketType")} end; @@ -227,7 +231,7 @@ load("SocketType " ++ SocketType, []) -> load("Port " ++ Port, []) -> case make_integer(Port) of {ok, Integer} -> - {ok, [], {port,Integer}}; + {ok, [], {port, Integer}}; {error, _} -> {error, ?NICE(clean(Port)++" is an invalid Port")} end; @@ -301,7 +305,7 @@ load("MaxKeepAliveRequests " ++ MaxRequests, []) -> " is an invalid MaxKeepAliveRequests")} end; -%% This clause is keept for backwards compability +%% This clause is kept for backwards compatibility load("MaxKeepAliveRequest " ++ MaxRequests, []) -> case make_integer(MaxRequests) of {ok, Integer} -> @@ -535,7 +539,10 @@ validate_config_params([{server_name, Value} | _]) -> throw({server_name, Value}); validate_config_params([{socket_type, Value} | Rest]) - when (Value =:= ip_comm) orelse (Value =:= ssl) -> + when (Value =:= ip_comm) orelse + (Value =:= ssl) orelse + (Value =:= ossl) orelse + (Value =:= essl) -> validate_config_params(Rest); validate_config_params([{socket_type, Value} | _]) -> throw({socket_type, Value}); @@ -696,6 +703,8 @@ store(ConfigList0) -> ConfigList) catch throw:Error -> + ?hdri("store - config parameter validation failed", + [{error, Error}]), {error, {invalid_option, Error}} end. @@ -742,27 +751,27 @@ remove(ConfigDB) -> ets:delete(ConfigDB), ok. -config(ConfigDB) -> - case httpd_util:lookup(ConfigDB, socket_type,ip_comm) of - ssl -> - case ssl_certificate_file(ConfigDB) of - undefined -> - {error, - "Directive SSLCertificateFile " - "not found in the config file"}; - SSLCertificateFile -> - {ssl, - SSLCertificateFile++ - ssl_certificate_key_file(ConfigDB)++ - ssl_verify_client(ConfigDB)++ - ssl_ciphers(ConfigDB)++ - ssl_password(ConfigDB)++ - ssl_verify_depth(ConfigDB)++ - ssl_ca_certificate_file(ConfigDB)} - end; - ip_comm -> - ip_comm - end. +%% config(ConfigDB) -> +%% case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of +%% ssl -> +%% case ssl_certificate_file(ConfigDB) of +%% undefined -> +%% {error, +%% "Directive SSLCertificateFile " +%% "not found in the config file"}; +%% SSLCertificateFile -> +%% {ssl, +%% SSLCertificateFile++ +%% ssl_certificate_key_file(ConfigDB)++ +%% ssl_verify_client(ConfigDB)++ +%% ssl_ciphers(ConfigDB)++ +%% ssl_password(ConfigDB)++ +%% ssl_verify_depth(ConfigDB)++ +%% ssl_ca_certificate_file(ConfigDB)} +%% end; +%% ip_comm -> +%% ip_comm +%% end. get_config(Address, Port) -> @@ -798,6 +807,38 @@ table(Address, Port) -> httpd_util:make_name("httpd_conf", Address, Port). +lookup_socket_type(ConfigDB) -> + case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of + ip_comm -> + ip_comm; + SSL when (SSL =:= ssl) orelse (SSL =:= ossl) orelse (SSL =:= essl) -> + SSLTag = + if + (SSL =:= ssl) -> + ?HTTP_DEFAULT_SSL_KIND; + true -> + SSL + end, + case ssl_certificate_file(ConfigDB) of + undefined -> + Reason = "Directive SSLCertificateFile " + "not found in the config file", + throw({error, Reason}); + SSLCertificateFile -> + {SSLTag, SSLCertificateFile ++ ssl_config(ConfigDB)} + end + end. + +ssl_config(ConfigDB) -> + ssl_certificate_key_file(ConfigDB) ++ + ssl_verify_client(ConfigDB) ++ + ssl_ciphers(ConfigDB) ++ + ssl_password(ConfigDB) ++ + ssl_verify_depth(ConfigDB) ++ + ssl_ca_certificate_file(ConfigDB). + + + %%%======================================================================== %%% Internal functions %%%======================================================================== diff --git a/lib/inets/src/http_server/httpd_esi.erl b/lib/inets/src/http_server/httpd_esi.erl index b1a75fda52..000874d0a3 100644 --- a/lib/inets/src/http_server/httpd_esi.erl +++ b/lib/inets/src/http_server/httpd_esi.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -21,7 +21,8 @@ -export([parse_headers/1, handle_headers/1]). --include("inets_internal.hrl"). +-include_lib("inets/src/inets_app/inets_internal.hrl"). + %%%========================================================================= %%% Internal application API @@ -38,7 +39,7 @@ %% body part. Note that it is presumed that <Data> starts with a %% string including "\r\n\r\n" if there is any header information %% present. The returned headers will not contain the HTTP header body -%% delimiter \r\n. (All header, header delimiters are keept.) +%% delimiter \r\n. (All header, header delimiters are kept.) %% Ex: ["Content-Type : text/html\r\n Connection : closing \r\n\r\n" | %% io_list()] --> {"Content-Type : text/html\r\n Connection : closing \r\n", %% io_list()} diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl index fbe713ecd1..e8a8ab6411 100644 --- a/lib/inets/src/http_server/httpd_file.erl +++ b/lib/inets/src/http_server/httpd_file.erl @@ -24,23 +24,23 @@ -include("httpd.hrl"). -include("httpd_internal.hrl"). + handle_error(eacces, Op, ModData, Path) -> - handle_error(403, Op, ModData, Path,""); + handle_error(403, Op, ModData, Path, ": Forbidden"); handle_error(enoent, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path,""); + handle_error(404, Op, ModData, Path, ": File not found"); handle_error(enotdir, Op, ModData, Path) -> handle_error(404, Op, ModData, Path, - ": A component of the file name is not a directory"); + ": A component of the file name is not a directory"); handle_error(emfile, Op, _ModData, Path) -> - handle_error(500, Op, none, Path, ": To many open files"); + handle_error(500, Op, none, Path, ": Too many open files"); handle_error({enfile,_}, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": File table overflow"); -handle_error(_Reason, Op, _ModData, Path) -> - handle_error(500, Op, none, Path, ""). - -handle_error(StatusCode, Op, none, Path, Reason) -> - {StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)}; +handle_error(_Reason, Op, ModData, Path) -> + handle_error(404, Op, ModData, Path, ": File not found"). +handle_error(StatusCode, Op, none, Path, Reason) -> + {StatusCode, none, ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}; handle_error(StatusCode, Op, ModData, Path, Reason) -> {StatusCode, ModData#mod.request_uri, - ?NICE("Can't " ++ Op ++ Path ++ Reason)}. + ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}. diff --git a/lib/inets/src/http_server/httpd_internal.hrl b/lib/inets/src/http_server/httpd_internal.hrl index 7795ab6c18..108469ea0a 100644 --- a/lib/inets/src/http_server/httpd_internal.hrl +++ b/lib/inets/src/http_server/httpd_internal.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2009-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -21,7 +21,51 @@ -ifndef(httpd_internal_hrl). -define(httpd_internal_hrl, true). --include("inets_internal.hrl"). +-ifndef(SERVER_SOFTWARE). +-define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile! +-endif. +-define(SERVER_PROTOCOL,"HTTP/1.1"). +-define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, + mod_dir, mod_get, mod_head, mod_log, mod_disk_log]). +-define(SOCKET_CHUNK_SIZE,8192). +-define(SOCKET_MAX_POLL,25). +-define(FILE_CHUNK_SIZE,64*1024). +-define(GATEWAY_INTERFACE,"CGI/1.1"). +-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)). +-define(DEFAULT_CONTEXT, + [{errmsg,"[an error occurred while processing this directive]"}, + {timefmt,"%A, %d-%b-%y %T %Z"}, + {sizefmt,"abbrev"}]). + + +-ifdef(inets_error). +-define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n", + [self(),?MODULE,?LINE]++Args)). +-else. +-define(ERROR(F,A),[]). +-endif. + +-ifdef(inets_log). +-define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n", + [self(),?MODULE,?LINE]++Args)). +-else. +-define(LOG(F,A),[]). +-endif. + +-ifdef(inets_debug). +-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n", + [self(),?MODULE,?LINE]++Args)). +-else. +-define(DEBUG(F,A),[]). +-endif. + +-ifdef(inets_cdebug). +-define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n", + [self(),?MODULE,?LINE]++Args)). +-else. +-define(CDEBUG(F,A),[]). +-endif. + -define(SERVICE, httpd). -define(hdri(Label, Content), ?report_important(Label, ?SERVICE, Content)). -define(hdrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)). diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index f3ea3aa0e2..db1e2c627a 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -113,7 +113,7 @@ do_error_entry(ConfigDB, RemoteHost, undefined, Date, Reason) -> do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) -> case httpd_util:lookup(ConfigDB, error_log_format, pretty) of pretty -> - io_lib:format("[~s] access to ~s failed for ~s reason: ~n~p~n", + io_lib:format("[~s] access to ~s failed for ~s, reason: ~n~p~n", [Date, URI, RemoteHost, Reason]); compact -> io_lib:format( "[~s] access to ~s failed for ~s, reason: ~w~n", diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index f2e8763907..b44bc77c41 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -238,24 +238,25 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port]) -> case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port)) of {error, Reason} -> String = lists:flatten( - io_lib:format("Failed initiating " - "web server: ~n~p~n~p~n", - [ConfigFile,Reason])), + io_lib:format("Failed initiating web server: " + "~n~p" + "~n~p" + "~n", [ConfigFile, Reason])), error_logger:error_report(String), {stop, {error, Reason}}; {ok, State} -> {ok, State} end; -init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, - ListenInfo]) -> +init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) -> process_flag(trap_exit, true), case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo)) of {error, Reason} -> String = lists:flatten( - io_lib:format("Failed initiating " - "web server: ~n~p~n~p~n", - [ConfigFile,Reason])), + io_lib:format("Failed initiating web server: " + "~n~p" + "~n~p" + "~n", [ConfigFile, Reason])), error_logger:error_report(String), {stop, {error, Reason}}; {ok, State} -> @@ -264,13 +265,14 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) -> NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), - ConfigDB = do_initial_store(ConfigList), - SocketType = httpd_conf:config(ConfigDB), + ConfigDB = do_initial_store(ConfigList), + SocketType = httpd_conf:lookup_socket_type(ConfigDB), case httpd_acceptor_sup:start_acceptor(SocketType, Addr, Port, ConfigDB, AcceptTimeout) of {ok, _Pid} -> - Status = [{max_conn,0}, {last_heavy_load,never}, - {last_connection,never}], + Status = [{max_conn, 0}, + {last_heavy_load, never}, + {last_connection, never}], State = #state{socket_type = SocketType, config_file = NewConfigFile, config_db = ConfigDB, @@ -284,7 +286,7 @@ do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) -> do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) -> NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile), ConfigDB = do_initial_store(ConfigList), - SocketType = httpd_conf:config(ConfigDB), + SocketType = httpd_conf:lookup_socket_type(ConfigDB), case httpd_acceptor_sup:start_acceptor(SocketType, Addr, Port, ConfigDB, AcceptTimeout, ListenInfo) of diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl index 75f03c4fc2..a04bcc2778 100644 --- a/lib/inets/src/http_server/httpd_request.erl +++ b/lib/inets/src/http_server/httpd_request.erl @@ -19,22 +19,35 @@ -module(httpd_request). --include("http_internal.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). -include("httpd.hrl"). +-include("httpd_internal.hrl"). --export([parse/1, whole_body/2, validate/3, update_mod_data/5, - body_data/2]). +-export([ + parse/1, + whole_body/2, + validate/3, + update_mod_data/5, + body_data/2 + ]). %% Callback API - used for example if the header/body is received a %% little at a time on a socket. --export([parse_method/1, parse_uri/1, parse_version/1, parse_headers/1, - whole_body/1]). +-export([ + parse_method/1, parse_uri/1, parse_version/1, parse_headers/1, + whole_body/1 + ]). + %%%========================================================================= %%% Internal application API %%%========================================================================= parse([Bin, MaxSizes]) -> - parse_method(Bin, [], MaxSizes, []). + ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]), + parse_method(Bin, [], MaxSizes, []); +parse(Unknown) -> + ?hdrt("parse", [{unknown, Unknown}]), + exit({bad_args, Unknown}). %% Functions that may be returned during the decoding process %% if the input data is incompleate. @@ -119,30 +132,65 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)-> %%% Internal functions %%%======================================================================== parse_method(<<>>, Method, MaxSizes, Result) -> + ?hdrt("parse_method - empty bin", + [{method, Method}, {max_sizes, MaxSizes}, {result, Result}]), {?MODULE, parse_method, [Method, MaxSizes, Result]}; parse_method(<<?SP, Rest/binary>>, Method, MaxSizes, Result) -> + ?hdrt("parse_method - SP begin", + [{rest, Rest}, + {method, Method}, + {max_sizes, MaxSizes}, + {result, Result}]), parse_uri(Rest, [], 0, MaxSizes, [string:strip(lists:reverse(Method)) | Result]); parse_method(<<Octet, Rest/binary>>, Method, MaxSizes, Result) -> + ?hdrt("parse_method", + [{octet, Octet}, + {rest, Rest}, + {method, Method}, + {max_sizes, MaxSizes}, + {result, Result}]), parse_method(Rest, [Octet | Method], MaxSizes, Result). -parse_uri(_, _, CurrSize, {MaxURI, _}, _) when CurrSize > MaxURI, - MaxURI =/= nolimit -> +parse_uri(_, _, CurrSize, {MaxURI, _}, _) + when (CurrSize > MaxURI) andalso (MaxURI =/= nolimit) -> + ?hdrt("parse_uri", + [{current_size, CurrSize}, + {max_uri, MaxURI}]), %% We do not know the version of the client as it comes after the %% uri send the lowest version in the response so that the client %% will be able to handle it. HttpVersion = "HTTP/0.9", {error, {uri_too_long, MaxURI}, HttpVersion}; parse_uri(<<>>, URI, CurrSize, MaxSizes, Result) -> + ?hdrt("parse_uri - empty bin", + [{uri, URI}, + {current_size, CurrSize}, + {max_sz, MaxSizes}, + {result, Result}]), {?MODULE, parse_uri, [URI, CurrSize, MaxSizes, Result]}; parse_uri(<<?SP, Rest/binary>>, URI, _, MaxSizes, Result) -> + ?hdrt("parse_uri - SP begin", + [{uri, URI}, + {max_sz, MaxSizes}, + {result, Result}]), parse_version(Rest, [], MaxSizes, [string:strip(lists:reverse(URI)) | Result]); %% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n" -parse_uri(<<?CR, _Rest/binary>> = Data, URI, _,MaxSizes, Result) -> +parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, MaxSizes, Result) -> + ?hdrt("parse_uri - CR begin", + [{uri, URI}, + {max_sz, MaxSizes}, + {result, Result}]), parse_version(Data, [], MaxSizes, [string:strip(lists:reverse(URI)) | Result]); parse_uri(<<Octet, Rest/binary>>, URI, CurrSize, MaxSizes, Result) -> + ?hdrt("parse_uri", + [{octet, Octet}, + {uri, URI}, + {curr_sz, CurrSize}, + {max_sz, MaxSizes}, + {result, Result}]), parse_uri(Rest, [Octet | URI], CurrSize + 1, MaxSizes, Result). parse_version(<<>>, Version, MaxSizes, Result) -> @@ -264,8 +312,8 @@ validate_uri(RequestURI) -> {'EXIT',_Reason} -> {error, {bad_request, {malformed_syntax, RequestURI}}}; _ -> - Path = format_request_uri(UriNoQueryNoHex), - Path2=[X||X<-string:tokens(Path, "/"),X=/="."], %% OTP-5938 + Path = format_request_uri(UriNoQueryNoHex), + Path2 = [X||X<-string:tokens(Path, "/\\"),X=/="."], validate_path( Path2,0, RequestURI) end. diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index fa832cba3f..c3b47ce390 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -101,11 +101,13 @@ init([Manager, ConfigDB, AcceptTimeout]) -> Then = erlang:now(), + ?hdrd("negotiate", []), case http_transport:negotiate(SocketType, Socket, TimeOut) of {error, Error} -> + ?hdrd("negotiation failed", [{error, Error}]), exit(Error); %% Can be 'normal'. ok -> - ?hdrt("negotiated", []), + ?hdrt("negotiation successfull", []), NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000, continue_init(Manager, ConfigDB, SocketType, Socket, NewTimeout) end. @@ -121,12 +123,9 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> socket = Socket, init_data = InitData}, - MaxHeaderSize = httpd_util:lookup(ConfigDB, max_header_size, - ?HTTP_MAX_HEADER_SIZE), - MaxURISize = httpd_util:lookup(ConfigDB, max_uri_size, - ?HTTP_MAX_URI_SIZE), - NrOfRequest = httpd_util:lookup(ConfigDB, - max_keep_alive_request, infinity), + MaxHeaderSize = max_header_size(ConfigDB), + MaxURISize = max_uri_size(ConfigDB), + NrOfRequest = max_keep_alive_request(ConfigDB), {_, Status} = httpd_manager:new_connection(Manager), @@ -142,9 +141,10 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> ?hdrt("activate request timeout", []), NewState = activate_request_timeout(State), - ?hdrt("update socket options", []), - http_transport:setopts(SocketType, Socket, [binary,{packet, 0}, - {active, once}]), + ?hdrt("set socket options (binary, packet & active)", []), + http_transport:setopts(SocketType, Socket, + [binary, {packet, 0}, {active, once}]), + ?hdrt("init done", []), gen_server:enter_loop(?MODULE, [], NewState). @@ -180,21 +180,29 @@ handle_cast(Msg, State) -> %% {stop, Reason, State} %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- -handle_info({Proto, Socket, Data}, State = +handle_info({Proto, Socket, Data}, #state{mfa = {Module, Function, Args} = MFA, mod = #mod{socket_type = SockType, socket = Socket} = ModData} = State) when (((Proto =:= tcp) orelse (Proto =:= ssl) orelse (Proto =:= dummy)) andalso is_binary(Data)) -> + ?hdrd("received data", [{data, Data}, {proto, Proto}, {socket, Socket}, {socket_type, SockType}, {mfa, MFA}]), - case Module:Function([Data | Args]) of + +%% case (catch Module:Function([Data | Args])) of + PROCESSED = (catch Module:Function([Data | Args])), + + ?hdrt("data processed", [{processing_result, PROCESSED}]), + + case PROCESSED of {ok, Result} -> ?hdrd("data processed", [{result, Result}]), NewState = cancel_request_timeout(State), handle_http_msg(Result, NewState); + {error, {uri_too_long, MaxSize}, Version} -> ?hdrv("uri too long", [{max_size, MaxSize}, {version, Version}]), NewModData = ModData#mod{http_version = Version}, @@ -205,7 +213,8 @@ handle_info({Proto, Socket, Data}, State = {stop, normal, State#state{response_sent = true, mod = NewModData}}; {error, {header_too_long, MaxSize}, Version} -> - ?hdrv("header too long", [{max_size, MaxSize}, {version, Version}]), + ?hdrv("header too long", + [{max_size, MaxSize}, {version, Version}]), NewModData = ModData#mod{http_version = Version}, httpd_response:send_status(NewModData, 413, "Header too long"), Reason = io_lib:format("Header too long, max size is ~p~n", @@ -231,13 +240,13 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Timeouts handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> - error_log("No request received on keep-alive connection" + error_log("No request received on keep-alive connection " "before server side timeout", ModData), %% No response should be sent! {stop, normal, State#state{response_sent = true}}; handle_info(timeout, #state{mod = ModData} = State) -> httpd_response:send_status(ModData, 408, "Request timeout"), - error_log("The client did not send the whole request before the" + error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; @@ -263,14 +272,16 @@ terminate(Reason, #state{response_sent = false, mod = ModData} = State) -> httpd_response:send_status(ModData, 500, none), error_log(httpd_util:reason_phrase(500), ModData), terminate(Reason, State#state{response_sent = true, mod = ModData}); -terminate(_, State) -> +terminate(_Reason, State) -> do_terminate(State). do_terminate(#state{mod = ModData, manager = Manager} = State) -> catch httpd_manager:done_connection(Manager), cancel_request_timeout(State), + %% receive after 5000 -> ok end, httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket). + %%-------------------------------------------------------------------- %% code_change(OldVsn, State, Extra) -> {ok, NewState} %% @@ -279,6 +290,7 @@ do_terminate(#state{mod = ModData, manager = Manager} = State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -383,9 +395,8 @@ is_host_specified_if_required(_, _, _) -> handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) -> ?hdrt("handle body", []), - MaxHeaderSize = - httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE), - MaxBodySize = httpd_util:lookup(ConfigDB, max_body_size, nolimit), + MaxHeaderSize = max_header_size(ConfigDB), + MaxBodySize = max_body_size(ConfigDB), case handle_expect(State, MaxBodySize) of ok -> @@ -538,24 +549,23 @@ handle_response(#state{body = Body, {stop, normal, State#state{response_sent = true}}. handle_next_request(#state{mod = #mod{connection = true} = ModData, - max_keep_alive_request = Max} = State, Data) -> + max_keep_alive_request = Max} = State, Data) -> ?hdrt("handle next request", [{max, Max}]), + NewModData = #mod{socket_type = ModData#mod.socket_type, - socket = ModData#mod.socket, - config_db = ModData#mod.config_db, - init_data = ModData#mod.init_data}, - MaxHeaderSize = - httpd_util:lookup(ModData#mod.config_db, - max_header_size, ?HTTP_MAX_HEADER_SIZE), - MaxURISize = httpd_util:lookup(ModData#mod.config_db, max_uri_size, - ?HTTP_MAX_URI_SIZE), - TmpState = State#state{mod = NewModData, - mfa = {httpd_request, parse, [{MaxURISize, - MaxHeaderSize}]}, + socket = ModData#mod.socket, + config_db = ModData#mod.config_db, + init_data = ModData#mod.init_data}, + MaxHeaderSize = max_header_size(ModData#mod.config_db), + MaxURISize = max_uri_size(ModData#mod.config_db), + + MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]}, + TmpState = State#state{mod = NewModData, + mfa = MFA, max_keep_alive_request = decrease(Max), - headers = undefined, - body = undefined, - response_sent = false}, + headers = undefined, + body = undefined, + response_sent = false}, NewState = activate_request_timeout(TmpState), @@ -596,7 +606,7 @@ decrease(N) -> error_log(ReasonString, Info) -> Error = lists:flatten( - io_lib:format("Error reading request:~s",[ReasonString])), + io_lib:format("Error reading request: ~s", [ReasonString])), error_log(mod_log, Info, Error), error_log(mod_disk_log, Info, Error). @@ -609,3 +619,21 @@ error_log(Mod, #mod{config_db = ConfigDB} = Info, String) -> _ -> ok end. + + +%%-------------------------------------------------------------------- +%% Config access wrapper functions +%%-------------------------------------------------------------------- + +max_header_size(ConfigDB) -> + httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE). + +max_uri_size(ConfigDB) -> + httpd_util:lookup(ConfigDB, max_uri_size, ?HTTP_MAX_URI_SIZE). + +max_body_size(ConfigDB) -> + httpd_util:lookup(ConfigDB, max_body_size, nolimit). + +max_keep_alive_request(ConfigDB) -> + httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity). + diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl index a742cbef76..d3115150b0 100644 --- a/lib/inets/src/http_server/httpd_script_env.erl +++ b/lib/inets/src/http_server/httpd_script_env.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -23,6 +23,7 @@ -export([create_env/3]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). %%%========================================================================= %%% Internal application API diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index 90d74e3a14..264dc9f006 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -39,7 +39,6 @@ -include("httpd_internal.hrl"). -include("inets_internal.hrl"). - %%%========================================================================= %%% API %%%========================================================================= @@ -91,7 +90,7 @@ id(Address, Port) -> %%% Supervisor callback %%%========================================================================= init([HttpdServices]) -> - ?hdrd("starting", []), + ?hdrd("starting", [{httpd_service, HttpdServices}]), RestartStrategy = one_for_one, MaxR = 10, MaxT = 3600, @@ -183,24 +182,32 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> Error end. -httpd_child_spec(Config, AcceptTimeout, Debug, Addr, 0) -> - case start_listen(Addr, 0, Config) of - {Pid, {NewPort, NewConfig, ListenSocket}} -> - Name = {httpd_instance_sup, Addr, NewPort}, - StartFunc = {httpd_instance_sup, start_link, - [NewConfig, AcceptTimeout, - {Pid, ListenSocket}, Debug]}, - Restart = permanent, - Shutdown = infinity, - Modules = [httpd_instance_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}; - {Pid, {error, Reason}} -> - exit(Pid, normal), - {error, Reason} - end; - httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> + case Port == 0 orelse proplists:is_defined(fd, Config) of + true -> + httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); + false -> + httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) + end. + +httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) -> + case start_listen(Addr, Port, Config) of + {Pid, {NewPort, NewConfig, ListenSocket}} -> + Name = {httpd_instance_sup, Addr, NewPort}, + StartFunc = {httpd_instance_sup, start_link, + [NewConfig, AcceptTimeout, + {Pid, ListenSocket}, Debug]}, + Restart = permanent, + Shutdown = infinity, + Modules = [httpd_instance_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}; + {Pid, {error, Reason}} -> + exit(Pid, normal), + {error, Reason} + end. + +httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) -> Name = {httpd_instance_sup, Addr, Port}, StartFunc = {httpd_instance_sup, start_link, [Config, AcceptTimeout, Debug]}, @@ -225,7 +232,8 @@ listen(Address, Port, Config) -> SocketType = proplists:get_value(socket_type, Config, ip_comm), case http_transport:start(SocketType) of ok -> - case http_transport:listen(SocketType, Address, Port) of + Fd = proplists:get_value(fd, Config), + case http_transport:listen(SocketType, Address, Port, Fd) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), {ok, NewPort} = inet:port(ListenSocket), diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 7fe5d6d152..c051422529 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -21,7 +21,7 @@ -export([ip_address/2, lookup/2, lookup/3, multi_lookup/2, lookup_mime/2, lookup_mime/3, lookup_mime_default/2, lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0, - rfc1123_date/1, day/1, month/1, + rfc1123_date/1, day/1, month/1, flatlength/1, split_path/1, split_script_path/1, suffix/1, split/3, uniq/1, make_name/2,make_name/3,make_name/4,strip/1, @@ -33,7 +33,6 @@ mime_types_validate/1, custom_date/0]). -export([encode_hex/1, decode_hex/1]). - -include_lib("kernel/include/file.hrl"). ip_address({_,_,_,_} = Address, _IpFamily) -> @@ -176,15 +175,14 @@ reason_phrase(_) -> "Internal Server Error". %% message message(301,URL,_) -> - "The document has moved <A HREF=\"" ++ maybe_encode(URL) ++"\">here</A>."; + "The document has moved <A HREF=\""++ maybe_encode(URL) ++"\">here</A>."; message(304, _URL,_) -> "The document has not been changed."; -message(400, none, _) -> +message(400,none,_) -> "Your browser sent a query that this server could not understand."; -message(400, Msg, _) -> - "Your browser sent a query that this server could not understand. " ++ - http_util:html_encode(Msg); -message(401, none, _) -> +message(400,Msg,_) -> + "Your browser sent a query that this server could not understand. "++ http_util:html_encode(Msg); +message(401,none,_) -> "This server could not verify that you are authorized to access the document you requested. Either you supplied the wrong @@ -192,57 +190,48 @@ credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required."; message(403,RequestURI,_) -> - "You don't have permission to access " ++ - http_util:html_encode(RequestURI) ++ - " on this server."; + "You don't have permission to access "++ http_util:html_encode(RequestURI) ++" on this server."; message(404,RequestURI,_) -> - "The requested URL " ++ - http_util:html_encode(RequestURI) ++ - " was not found on this server."; + "The requested URL " ++ http_util:html_encode(RequestURI) ++ " was not found on this server."; message(408, Timeout, _) -> Timeout; message(412,none,_) -> - "The requested preconditions where false"; + "The requested preconditions were false"; message(413, Reason,_) -> "Entity: " ++ http_util:html_encode(Reason); message(414,ReasonPhrase,_) -> - "Message " ++ http_util:html_encode(ReasonPhrase) ++ "."; + "Message "++ http_util:html_encode(ReasonPhrase) ++"."; message(416,ReasonPhrase,_) -> http_util:html_encode(ReasonPhrase); message(500,_,ConfigDB) -> - ServerAdmin = lookup(ConfigDB, server_admin, "unknown@unknown"), + ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"), "The server encountered an internal error or " "misconfiguration and was unable to complete " "your request.<P>Please contact the server administrator " - ++ http_util:html_encode(ServerAdmin) ++ - ", and inform them of the time the error occurred " + ++ http_util:html_encode(ServerAdmin) ++ ", and inform them of the time the error occurred " "and anything you might have done that may have caused the error."; message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) -> if is_atom(Method) -> - atom_to_list(Method)++ - " to " ++ - http_util:html_encode(RequestURI) ++ - " (" ++ HTTPVersion ++ ") not supported."; + http_util:html_encode(atom_to_list(Method))++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported."; is_list(Method) -> - Method++ - " to " ++ - http_util:html_encode(RequestURI) ++ - " (" ++ HTTPVersion ++ ") not supported." + http_util:html_encode(Method)++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported." end; message(503, String, _ConfigDB) -> - "This service in unavailable due to: " ++ http_util:html_encode(String). + "This service in unavailable due to: "++ http_util:html_encode(String). maybe_encode(URI) -> - case lists:member($%, URI) of - true -> - URI; - false -> - http_uri:encode(URI) - end. + Decoded = try http_uri:decode(URI) of + N -> N + catch + error:_ -> URI + end, + http_uri:encode(Decoded). %%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}} @@ -435,7 +424,7 @@ split_path(Path) -> split_path([],SoFar) -> {http_uri:decode(lists:reverse(SoFar)),[]}; split_path([$/|Rest],SoFar) -> - Path = http_uri:decode(lists:reverse(SoFar)), + Path=http_uri:decode(lists:reverse(SoFar)), case file:read_file_info(Path) of {ok,FileInfo} when FileInfo#file_info.type =:= regular -> {Path,[$/|Rest]}; @@ -622,7 +611,6 @@ hexlist_to_integer(List)-> %%---------------------------------------------------------------------- %%Converts an integer to an hexlist %%---------------------------------------------------------------------- - integer_to_hexlist(Num) when is_integer(Num) -> http_util:integer_to_hexlist(Num). @@ -747,7 +735,6 @@ valid_accept_timeout(A) -> valid_config(_) -> ok. - %%---------------------------------------------------------------------- %% Enable debugging, %%---------------------------------------------------------------------- diff --git a/lib/inets/src/http_server/mod_actions.erl b/lib/inets/src/http_server/mod_actions.erl index d50ed4b16c..c3946ff9b4 100644 --- a/lib/inets/src/http_server/mod_actions.erl +++ b/lib/inets/src/http_server/mod_actions.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -21,6 +21,7 @@ -export([do/1,load/2, store/2]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). %% do diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl index 41fcdb5e3a..0b9fe4cfe0 100644 --- a/lib/inets/src/http_server/mod_alias.erl +++ b/lib/inets/src/http_server/mod_alias.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -104,6 +104,19 @@ real_name(ConfigDB, RequestURI, []) -> httpd_util:split_path(default_index(ConfigDB, RealName)), {ShortPath, Path, AfterPath}; +real_name(ConfigDB, RequestURI, [{MP,Replacement}|Rest]) + when element(1, MP) =:= re_pattern -> + case re:run(RequestURI, MP, [{capture,[]}]) of + match -> + NewURI = re:replace(RequestURI, MP, Replacement, [{return,list}]), + {ShortPath,_} = httpd_util:split_path(NewURI), + {Path,AfterPath} = + httpd_util:split_path(default_index(ConfigDB, NewURI)), + {ShortPath, Path, AfterPath}; + nomatch -> + real_name(ConfigDB, RequestURI, Rest) + end; + real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) -> case inets_regexp:match(RequestURI, "^" ++ FakeName) of {match, _, _} -> @@ -121,6 +134,18 @@ real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) -> real_script_name(_ConfigDB, _RequestURI, []) -> not_a_script; + +real_script_name(ConfigDB, RequestURI, [{MP,Replacement} | Rest]) + when element(1, MP) =:= re_pattern -> + case re:run(RequestURI, MP, [{capture,[]}]) of + match -> + ActualName = + re:replace(RequestURI, MP, Replacement, [{return,list}]), + httpd_util:split_script_path(default_index(ConfigDB, ActualName)); + nomatch -> + real_script_name(ConfigDB, RequestURI, Rest) + end; + real_script_name(ConfigDB, RequestURI, [{FakeName,RealName} | Rest]) -> case inets_regexp:match(RequestURI, "^" ++ FakeName) of {match,_,_} -> @@ -181,6 +206,8 @@ load("Alias " ++ Alias, []) -> {ok, _} -> {error,?NICE(httpd_conf:clean(Alias)++" is an invalid Alias")} end; +load("ReWrite " ++ Rule, Acc) -> + load_re_write(Rule, Acc, "ReWrite", re_write); load("ScriptAlias " ++ ScriptAlias, []) -> case inets_regexp:split(ScriptAlias, " ") of {ok, [FakeName, RealName]} -> @@ -190,6 +217,24 @@ load("ScriptAlias " ++ ScriptAlias, []) -> {ok, _} -> {error, ?NICE(httpd_conf:clean(ScriptAlias)++ " is an invalid ScriptAlias")} + end; +load("ScriptReWrite " ++ Rule, Acc) -> + load_re_write(Rule, Acc, "ScriptReWrite", script_re_write). + +load_re_write(Rule0, Acc, Type, Tag) -> + case lists:dropwhile( + fun ($\s) -> true; ($\t) -> true; (_) -> false end, + Rule0) of + "" -> + {error, ?NICE(httpd_conf:clean(Rule0)++" is an invalid "++Type)}; + Rule -> + case string:chr(Rule, $\s) of + 0 -> + {ok, Acc, {Tag, {Rule, ""}}}; + N -> + {Re, [_|Replacement]} = lists:split(N-1, Rule), + {ok, Acc, {Tag, {Re, Replacement}}} + end end. store({directory_index, Value} = Conf, _) when is_list(Value) -> @@ -201,16 +246,36 @@ store({directory_index, Value} = Conf, _) when is_list(Value) -> end; store({directory_index, Value}, _) -> {error, {wrong_type, {directory_index, Value}}}; -store({alias, {Fake, Real}} = Conf, _) - when is_list(Fake) andalso is_list(Real) -> +store({alias, {Fake, Real}} = Conf, _) + when is_list(Fake), is_list(Real) -> {ok, Conf}; store({alias, Value}, _) -> {error, {wrong_type, {alias, Value}}}; +store({re_write, {Re, Replacement}} = Conf, _) + when is_list(Re), is_list(Replacement) -> + case re:compile(Re) of + {ok, MP} -> + {ok, {alias, {MP, Replacement}}}; + {error,_} -> + {error, {re_compile, Conf}} + end; +store({re_write, _} = Conf, _) -> + {error, {wrong_type, Conf}}; store({script_alias, {Fake, Real}} = Conf, _) - when is_list(Fake) andalso is_list(Real) -> + when is_list(Fake), is_list(Real) -> {ok, Conf}; store({script_alias, Value}, _) -> - {error, {wrong_type, {script_alias, Value}}}. + {error, {wrong_type, {script_alias, Value}}}; +store({script_re_write, {Re, Replacement}} = Conf, _) + when is_list(Re), is_list(Replacement) -> + case re:compile(Re) of + {ok, MP} -> + {ok, {script_alias, {MP, Replacement}}}; + {error,_} -> + {error, {re_compile, Conf}} + end; +store({script_re_write, _} = Conf, _) -> + {error, {wrong_type, Conf}}. is_directory_index_list([]) -> true; diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl index b1d8b03fe4..85a87ab884 100644 --- a/lib/inets/src/http_server/mod_auth.erl +++ b/lib/inets/src/http_server/mod_auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 diff --git a/lib/inets/src/http_server/mod_auth.hrl b/lib/inets/src/http_server/mod_auth.hrl index 9b316cecc4..674e6d1652 100644 --- a/lib/inets/src/http_server/mod_auth.hrl +++ b/lib/inets/src/http_server/mod_auth.hrl @@ -1,29 +1,27 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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/. -%% +%% %% 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. -%% -%% %CopyrightEnd% %% +%% %CopyrightEnd% %% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. + +-ifndef(src_mod_auth_hrl). +-define(src_mod_auth_hrl, true). --record(httpd_user, - {username, - password, - user_data}). +-include_lib("inets/include/mod_auth.hrl"). --record(httpd_group, - {name, - userlist}). - +-endif. % -ifdef(src_mod_auth_hrl). diff --git a/lib/inets/src/http_server/mod_auth_dets.erl b/lib/inets/src/http_server/mod_auth_dets.erl index 2abf5c517a..a48725d5d9 100644 --- a/lib/inets/src/http_server/mod_auth_dets.erl +++ b/lib/inets/src/http_server/mod_auth_dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -35,7 +35,7 @@ -export([store_directory_data/3]). -include("httpd.hrl"). --include("inets_internal.hrl"). +-include("httpd_internal.hrl"). -include("mod_auth.hrl"). store_directory_data(_Directory, DirData, Server_root) -> diff --git a/lib/inets/src/http_server/mod_auth_mnesia.erl b/lib/inets/src/http_server/mod_auth_mnesia.erl index ffe028617b..91beb0e062 100644 --- a/lib/inets/src/http_server/mod_auth_mnesia.erl +++ b/lib/inets/src/http_server/mod_auth_mnesia.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -55,7 +55,7 @@ store_directory_data(_Directory, _DirData, _Server_root) -> %% API %% -%% Compability API +%% Compatibility API store_user(UserName, Password, Port, Dir, _AccessPassword) -> %% AccessPassword is ignored - was not used in previous version diff --git a/lib/inets/src/http_server/mod_auth_plain.erl b/lib/inets/src/http_server/mod_auth_plain.erl index 12342ba8da..c0a83711ba 100644 --- a/lib/inets/src/http_server/mod_auth_plain.erl +++ b/lib/inets/src/http_server/mod_auth_plain.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -24,6 +24,7 @@ -include("httpd_internal.hrl"). -include("inets_internal.hrl"). + -define(VMODULE,"AUTH_PLAIN"). %% Internal API diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl index fc50356838..947273bd9e 100644 --- a/lib/inets/src/http_server/mod_auth_server.erl +++ b/lib/inets/src/http_server/mod_auth_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index 33605b9698..c854166c29 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -27,6 +27,7 @@ -export([do/1, load/2, store/2]). -include("http_internal.hrl"). +-include("httpd_internal.hrl"). -include("httpd.hrl"). -define(VMODULE,"CGI"). diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl index 35e9de24e2..d791ee28e9 100644 --- a/lib/inets/src/http_server/mod_dir.erl +++ b/lib/inets/src/http_server/mod_dir.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -19,11 +19,11 @@ %% -module(mod_dir). --export([do/1]). - -include("httpd.hrl"). -include("httpd_internal.hrl"). +-export([do/1]). + %% do do(Info) -> diff --git a/lib/inets/src/http_server/mod_disk_log.erl b/lib/inets/src/http_server/mod_disk_log.erl index 95e0d00c70..5a3766de66 100644 --- a/lib/inets/src/http_server/mod_disk_log.erl +++ b/lib/inets/src/http_server/mod_disk_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -28,7 +28,7 @@ -define(VMODULE,"DISK_LOG"). -include("httpd.hrl"). - +-include("httpd_internal.hrl"). %%%========================================================================= %%% API diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index bbdb67bb0e..e36c33b282 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -29,6 +29,7 @@ -export([do/1, load/2, store/2]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). -include("inets_internal.hrl"). -define(VMODULE,"ESI"). @@ -38,6 +39,7 @@ %%%========================================================================= %%% API %%%========================================================================= + %%-------------------------------------------------------------------------- %% deliver(SessionID, Data) -> ok | {error, bad_sessionID} %% SessionID = pid() @@ -49,7 +51,7 @@ %% request handling process so it can forward it to the client. %%------------------------------------------------------------------------- deliver(SessionID, Data) when is_pid(SessionID) -> - SessionID ! {ok, Data}, + SessionID ! {esi_data, Data}, ok; deliver(_SessionID, _Data) -> {error, bad_sessionID}. @@ -66,6 +68,7 @@ deliver(_SessionID, _Data) -> %% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS %%------------------------------------------------------------------------- do(ModData) -> + ?hdrt("do", []), case proplists:get_value(status, ModData#mod.data) of {_StatusCode, _PhraseArgs, _Reason} -> {proceed, ModData#mod.data}; @@ -185,6 +188,7 @@ store({erl_script_nocache, Value}, _) -> %%% Internal functions %%%======================================================================== generate_response(ModData) -> + ?hdrt("generate response", []), case scheme(ModData#mod.request_uri, ModData#mod.config_db) of {eval, ESIBody, Modules} -> eval(ModData, ESIBody, Modules); @@ -236,6 +240,7 @@ alias_match_str(Alias, eval_script_alias) -> erl(#mod{method = Method} = ModData, ESIBody, Modules) when (Method =:= "GET") orelse (Method =:= "HEAD") -> + ?hdrt("erl", [{method, Method}]), case httpd_util:split(ESIBody,":|%3A|/",2) of {ok, [ModuleName, FuncAndInput]} -> case httpd_util:split(FuncAndInput,"[\?/]",2) of @@ -261,6 +266,7 @@ erl(#mod{request_uri = ReqUri, method = "PUT", http_version = Version, data = Data}, _ESIBody, _Modules) -> + ?hdrt("erl", [{method, put}]), {proceed, [{status,{501,{"PUT", ReqUri, Version}, ?NICE("Erl mechanism doesn't support method PUT")}}| Data]}; @@ -269,12 +275,14 @@ erl(#mod{request_uri = ReqUri, method = "DELETE", http_version = Version, data = Data}, _ESIBody, _Modules) -> + ?hdrt("erl", [{method, delete}]), {proceed,[{status,{501,{"DELETE", ReqUri, Version}, ?NICE("Erl mechanism doesn't support method DELETE")}}| Data]}; erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) -> + ?hdrt("erl", [{method, post}]), case httpd_util:split(ESIBody,":|%3A|/",2) of {ok,[ModuleName, Function]} -> generate_webpage(ModData, ESIBody, Modules, @@ -290,6 +298,7 @@ generate_webpage(ModData, ESIBody, [all], Module, FunctionName, FunctionName, Input, ScriptElements); generate_webpage(ModData, ESIBody, Modules, Module, FunctionName, Input, ScriptElements) -> + ?hdrt("generate webpage", []), Function = list_to_atom(FunctionName), case lists:member(Module, Modules) of true -> @@ -310,8 +319,9 @@ generate_webpage(ModData, ESIBody, Modules, Module, FunctionName, %% Old API that waits for the dymnamic webpage to be totally generated %% before anythig is sent back to the client. -erl_scheme_webpage_whole(Module, Function, Env, Input, ModData) -> - case (catch Module:Function(Env, Input)) of +erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) -> + ?hdrt("erl_scheme_webpage_whole", [{module, Mod}, {function, Func}]), + case (catch Mod:Func(Env, Input)) of {'EXIT',{undef, _}} -> {proceed, [{status, {404, ModData#mod.request_uri, "Not found"}} | ModData#mod.data]}; @@ -348,6 +358,7 @@ erl_scheme_webpage_whole(Module, Function, Env, Input, ModData) -> %% in small chunks at the time during generation. erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) -> process_flag(trap_exit, true), + ?hdrt("erl_scheme_webpage_chunk", [{module, Mod}, {function, Func}]), Self = self(), %% Spawn worker that generates the webpage. %% It would be nicer to use erlang:function_exported/3 but if the @@ -373,9 +384,12 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid) -> deliver_webpage_chunk(ModData, Pid, Timeout). deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> + ?hdrt("deliver_webpage_chunk", [{timeout, Timeout}]), case receive_headers(Timeout) of {error, Reason} -> %% Happens when webpage generator callback/3 is undefined + ?hdrv("deliver_webpage_chunk - failed receiving headers", + [{reason, Reason}]), {error, Reason}; {Headers, Body} -> case httpd_esi:handle_headers(Headers) of @@ -400,7 +414,8 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> IsDisableChunkedSend) end; timeout -> - send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]), + ?hdrv("deliver_webpage_chunk - timeout", []), + send_headers(ModData, 504, [{"connection", "close"}]), httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket), process_flag(trap_exit,false), {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]} @@ -408,11 +423,17 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> receive_headers(Timeout) -> receive + {esi_data, Chunk} -> + ?hdrt("receive_headers - received esi data (esi)", []), + httpd_esi:parse_headers(lists:flatten(Chunk)); {ok, Chunk} -> + ?hdrt("receive_headers - received esi data (ok)", []), httpd_esi:parse_headers(lists:flatten(Chunk)); {'EXIT', Pid, erl_scheme_webpage_chunk_undefined} when is_pid(Pid) -> + ?hdrd("receive_headers - exit:chunk-undef", []), {error, erl_scheme_webpage_chunk_undefined}; {'EXIT', Pid, Reason} when is_pid(Pid) -> + ?hdrv("receive_headers - exit", [{reason, Reason}]), exit({mod_esi_linked_process_died, Pid, Reason}) after Timeout -> timeout @@ -428,19 +449,33 @@ handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) -> {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]}; handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) -> + ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]), httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend), receive + {esi_data, Data} when is_binary(Data) -> + ?hdrt("handle_body - received binary data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), + IsDisableChunkedSend); + {esi_data, Data} -> + ?hdrt("handle_body - received data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + length(Data), + IsDisableChunkedSend); {ok, Data} -> + ?hdrt("handle_body - received data (ok)", []), handle_body(Pid, ModData, Data, Timeout, Size + length(Data), IsDisableChunkedSend); {'EXIT', Pid, normal} when is_pid(Pid) -> + ?hdrt("handle_body - exit:normal", []), httpd_response:send_final_chunk(ModData, IsDisableChunkedSend), {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]}; {'EXIT', Pid, Reason} when is_pid(Pid) -> + ?hdrv("handle_body - exit", [{reason, Reason}]), httpd_response:send_final_chunk(ModData, IsDisableChunkedSend), exit({mod_esi_linked_process_died, Pid, Reason}) + after Timeout -> + ?hdrv("handle_body - timeout", []), process_flag(trap_exit,false), httpd_response:send_final_chunk(ModData, IsDisableChunkedSend), exit({mod_esi_linked_process_timeout, Pid}) @@ -474,6 +509,7 @@ eval(#mod{request_uri = ReqUri, method = "PUT", http_version = Version, data = Data}, _ESIBody, _Modules) -> + ?hdrt("eval", [{method, put}]), {proceed,[{status,{501,{"PUT", ReqUri, Version}, ?NICE("Eval mechanism doesn't support method PUT")}}| Data]}; @@ -482,6 +518,7 @@ eval(#mod{request_uri = ReqUri, method = "DELETE", http_version = Version, data = Data}, _ESIBody, _Modules) -> + ?hdrt("eval", [{method, delete}]), {proceed,[{status,{501,{"DELETE", ReqUri, Version}, ?NICE("Eval mechanism doesn't support method DELETE")}}| Data]}; @@ -490,12 +527,14 @@ eval(#mod{request_uri = ReqUri, method = "POST", http_version = Version, data = Data}, _ESIBody, _Modules) -> + ?hdrt("eval", [{method, post}]), {proceed,[{status,{501,{"POST", ReqUri, Version}, ?NICE("Eval mechanism doesn't support method POST")}}| Data]}; eval(#mod{method = Method} = ModData, ESIBody, Modules) - when Method == "GET"; Method == "HEAD" -> + when (Method =:= "GET") orelse (Method =:= "HEAD") -> + ?hdrt("eval", [{method, Method}]), case is_authorized(ESIBody, Modules) of true -> case generate_webpage(ESIBody) of diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl index 9fd1fcec47..5cb30e3d97 100644 --- a/lib/inets/src/http_server/mod_get.erl +++ b/lib/inets/src/http_server/mod_get.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -20,7 +20,7 @@ -module(mod_get). -export([do/1]). -include("httpd.hrl"). - +-include("httpd_internal.hrl"). %% do do(Info) -> diff --git a/lib/inets/src/http_server/mod_head.erl b/lib/inets/src/http_server/mod_head.erl index 8b08d61651..c346fd4d23 100644 --- a/lib/inets/src/http_server/mod_head.erl +++ b/lib/inets/src/http_server/mod_head.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl index d8835198f5..e1f66d01c8 100644 --- a/lib/inets/src/http_server/mod_htaccess.erl +++ b/lib/inets/src/http_server/mod_htaccess.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -23,6 +23,7 @@ -export([do/1, load/2, store/2]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Public methods that interface the eswapi %% diff --git a/lib/inets/src/http_server/mod_include.erl b/lib/inets/src/http_server/mod_include.erl index 790bf8f937..35f45bdd33 100644 --- a/lib/inets/src/http_server/mod_include.erl +++ b/lib/inets/src/http_server/mod_include.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -21,6 +21,7 @@ -export([do/1,parse/2,config/6,include/6,echo/6,fsize/6,flastmod/6,exec/6]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). -define(VMODULE,"INCLUDE"). diff --git a/lib/inets/src/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl index de24d5a569..c8a2ec0dc4 100644 --- a/lib/inets/src/http_server/mod_log.erl +++ b/lib/inets/src/http_server/mod_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -26,6 +26,7 @@ -export([do/1, load/2, store/2, remove/1]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). -define(VMODULE,"LOG"). %%%========================================================================= diff --git a/lib/inets/src/http_server/mod_range.erl b/lib/inets/src/http_server/mod_range.erl index 0698fb9099..a0408cba79 100644 --- a/lib/inets/src/http_server/mod_range.erl +++ b/lib/inets/src/http_server/mod_range.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -20,7 +20,7 @@ -module(mod_range). -export([do/1]). -include("httpd.hrl"). - +-include("httpd_internal.hrl"). %% do do(Info) -> diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl index 05b5ba1609..989f45db20 100644 --- a/lib/inets/src/http_server/mod_responsecontrol.erl +++ b/lib/inets/src/http_server/mod_responsecontrol.erl @@ -22,6 +22,7 @@ -export([do/1]). -include("httpd.hrl"). +-include("httpd_internal.hrl"). do(Info) -> ?DEBUG("do -> response_control",[]), diff --git a/lib/inets/src/http_server/mod_security.erl b/lib/inets/src/http_server/mod_security.erl index 5cdbeb9792..41988732ad 100644 --- a/lib/inets/src/http_server/mod_security.erl +++ b/lib/inets/src/http_server/mod_security.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 diff --git a/lib/inets/src/http_server/mod_security_server.erl b/lib/inets/src/http_server/mod_security_server.erl index ca8bee0c8e..784b3eba70 100644 --- a/lib/inets/src/http_server/mod_security_server.erl +++ b/lib/inets/src/http_server/mod_security_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 diff --git a/lib/inets/src/http_server/mod_trace.erl b/lib/inets/src/http_server/mod_trace.erl index df482228d8..7233925783 100644 --- a/lib/inets/src/http_server/mod_trace.erl +++ b/lib/inets/src/http_server/mod_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index 33c9e34a3a..20e22917e2 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -47,7 +47,9 @@ MODULES = \ inets_sup \ inets_regexp -HRL_FILES = inets_internal.hrl +INTERNAL_HRL_FILES = inets_internal.hrl +EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ + ../../include/mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -67,18 +69,14 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- -# INETS FLAGS -# ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - - -# ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' +include inets.mk + +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) # ---------------------------------------------------- @@ -112,7 +110,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app + $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 04f6365b98..cb036157a5 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -107,5 +107,6 @@ tftp_sup ]}, {registered,[inets_sup, httpc_manager]}, + %% If the "new" ssl is used then 'crypto' must be started before inets. {applications,[kernel,stdlib]}, {mod,{inets_app,[]}}]}. diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 2ec0964b3e..f4eb612881 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -1,107 +1,119 @@ %% This is an -*- erlang -*- file. %% %CopyrightBegin% -%% +%% %% Copyright Ericsson AB 1999-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% {"%VSN%", [ - {"5.3.5", + {"5.7.1", [ {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]} ] }, - {"5.3.4", + {"5.7", [ - {restart_application, inets} - ] - }, - {"5.3.3", - [ - {restart_application, inets} - ] - }, - {"5.3.2", - [ - {restart_application, inets} + {load_module, httpd_request, soft_purge, soft_purge, []}, + {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]} ] }, - {"5.3.1", + {"5.6", [ - {restart_application, inets} + {load_module, httpd_request, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, + {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, []} ] }, - {"5.3", + {"5.5.2", [ {restart_application, inets} ] }, - {"5.2", + {"5.5.1", [ {restart_application, inets} ] }, - {"5.1.3", + {"5.5", [ {restart_application, inets} ] }, - {"5.1.2", + {"5.4", [ {restart_application, inets} ] - } + } ], - [ - {"5.3.4", - [ - {restart_application, inets} - ] - }, - {"5.3.3", + [ + {"5.7.1", [ - {restart_application, inets} + {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]} ] }, - {"5.3.2", + {"5.7", [ - {restart_application, inets} + {load_module, httpd_request, soft_purge, soft_purge, []}, + {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]} ] }, - {"5.3.1", + {"5.6", [ - {restart_application, inets} + {load_module, httpd_request, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {load_module, httpc_cookie, soft_purge, soft_purge, [http_util]}, + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, mod_responsecontrol, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, [mod_responsecontrol]}, + {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, []} ] }, - {"5.3", + {"5.5.2", [ {restart_application, inets} ] - }, - {"5.2", + }, + {"5.5.1", [ {restart_application, inets} ] }, - {"5.1.3", + {"5.5", [ {restart_application, inets} ] - }, - {"5.1.2", + }, + {"5.4", [ {restart_application, inets} ] diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk new file mode 100644 index 0000000000..b6e9fe1d96 --- /dev/null +++ b/lib/inets/src/inets_app/inets.mk @@ -0,0 +1,45 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode + +# %CopyrightBegin% +# +# Copyright Ericsson AB 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/. +# +# 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. +# +# %CopyrightEnd% + +ifeq ($(INETS_TRACE), io) +ERL_COMPILE_FLAGS += -Dinets_trace_io +endif + +ifeq ($(INETS_DEBUG), true) +ERL_COMPILE_FLAGS += -Dinets_debug +endif + +ifeq ($(USE_INETS_HIPE), true) +ERL_COMPILE_FLAGS += +native +endif + +ifeq ($(WARN_UNUSED_WARS), true) +ERL_COMPILE_FLAGS += +warn_unused_vars +endif + +INETS_APP_VSN_COMPILE_FLAGS = \ + +'{parse_transform,sys_pre_attributes}' \ + +'{attribute,insert,app_vsn,$(APP_VSN)}' + +INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' + +INETS_ERL_COMPILE_FLAGS += \ + -pa $(ERL_TOP)/lib/inets/ebin \ + $(INETS_APP_VSN_COMPILE_FLAGS) + diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl index 3499314d54..e9eb9892f2 100644 --- a/lib/inets/src/inets_app/inets_service.erl +++ b/lib/inets/src/inets_app/inets_service.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% 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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -61,5 +61,5 @@ behaviour_info(_) -> %% service_info() -> [{Property, Value}] | {error, Reason} -%% ex: http:service_info() -> [{profile, ProfileName}] +%% ex: httpc:service_info() -> [{profile, ProfileName}] %% httpd:service_info() -> [{host, Host}, {port, Port}] diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile index b4339da1e2..759b70c8e4 100644 --- a/lib/inets/src/tftp/Makefile +++ b/lib/inets/src/tftp/Makefile @@ -56,17 +56,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- -# INETS FLAGS +# FLAGS # ---------------------------------------------------- -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' +include ../inets_app/inets.mk -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -ERL_COMPILE_FLAGS += $(INETS_FLAGS) \ - +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' +ERL_COMPILE_FLAGS += \ + $(INETS_FLAGS) \ + $(INETS_ERL_COMPILE_FLAGS) \ + -I../../include \ + -I../inets_app # ---------------------------------------------------- @@ -87,9 +86,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(RELSYSDIR)/src/tftp + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/tftp + $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin release_docs_spec: diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 668752da9e..87ca60e4b3 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-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 @@ -143,6 +143,8 @@ else INETS_FLAGS += -Dhttpd_security_verbosity=log endif +INETS_FLAGS += -pa ../../inets/ebin + INETS_ROOT = ../../inets MODULES = \ @@ -198,7 +200,8 @@ SOURCE = $(ERL_FILES) $(HRL_FILES) TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) -INETS_SPECS = inets.spec inets.spec.vxworks +INETS_SPECS = inets.spec +COVER_FILE = inets.cover INETS_FILES = inets.config $(INETS_SPECS) # SUB_SUITES = \ @@ -220,10 +223,10 @@ MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile) ifeq ($(MAKE_EMAKE),) BUILDTARGET = $(TARGET_FILES) -RELTEST_FILES = $(INETS_SPECS) $(SOURCE) +RELTEST_FILES = $(COVER_FILE) $(INETS_SPECS) $(SOURCE) else BUILDTARGET = emakebuild -RELTEST_FILES = $(EMAKEFILE) $(INETS_SPECS) $(SOURCE) +RELTEST_FILES = $(EMAKEFILE) $(COVER_FILE) $(INETS_SPECS) $(SOURCE) endif @@ -241,8 +244,11 @@ RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin # The path to the test_server ebin dir is needed when # running the target "targets". # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \ - $(INCLUDES) $(FTP_FLAGS) $(INETS_FLAGS) +ERL_COMPILE_FLAGS += \ + -pa ../../../internal_tools/test_server/ebin \ + $(INCLUDES) \ + $(FTP_FLAGS) \ + $(INETS_FLAGS) # ---------------------------------------------------- # Targets @@ -283,26 +289,30 @@ release_spec: opt $(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test @for d in $(DATADIRS); do \ echo "installing data dir $$d"; \ - echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \ - cat $$d/TAR.exclude >> $$d/TAR.exclude2; \ - find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \ - find $$d -name '*.keep*' >> $$d/TAR.exclude2; \ - find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \ - find $$d -name '*~' >> $$d/TAR.exclude2; \ - find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \ - find $$d -name 'core' >> $$d/TAR.exclude2; \ - find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \ - tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + if test -f $$d/TAR.exclude; then \ + echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \ + cat $$d/TAR.exclude >> $$d/TAR.exclude2; \ + find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \ + find $$d -name '*.keep*' >> $$d/TAR.exclude2; \ + find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \ + find $$d -name '*~' >> $$d/TAR.exclude2; \ + find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \ + find $$d -name 'core' >> $$d/TAR.exclude2; \ + find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \ + tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + else \ + tar cf - $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + fi; \ done release_tests_spec: opt $(INSTALL_DIR) $(RELTESTSYSDIR) $(INSTALL_DATA) $(RELTEST_FILES) $(RELTESTSYSDIR) - chmod -f -R u+w $(RELTESTSYSDIR) + chmod -R u+w $(RELTESTSYSDIR) tar chf - $(DATADIRS) | (cd $(RELTESTSYSDIR); tar xf -) $(INSTALL_DIR) $(RELTESTSYSALLDATADIR) $(INSTALL_DIR) $(RELTESTSYSBINDIR) - chmod -f -R +x $(RELTESTSYSBINDIR) + chmod -R +x $(RELTESTSYSBINDIR) $(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib release_docs_spec: diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index e7404f945b..17e5f6777e 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -20,29 +20,14 @@ -module(ftp_SUITE). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). %% Test server specific exports --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). % -export([init_per_testcase/2, end_per_testcase/2]). -export([init_per_suite/1, end_per_suite/1]). -%% Test cases must be exported. --export([solaris8_test/1, - solaris9_test/1, - solaris10_test/1, - linux_x86_test/1, - linux_ppc_test/1, - macosx_x86_test/1, - macosx_ppc_test/1, - openbsd_test/1, - freebsd_test/1, - netbsd_test/1, - windows_xp_test/1, - windows_2003_server_test/1, - ticket_tests/1]). - -define(FTP_USER, "anonymous"). -define(FTP_PASS, passwd()). -define(FTP_PORT, 21). @@ -72,52 +57,51 @@ %% Description: Returns documentation/test cases in this test suite %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -all(doc) -> - ["Test the ftp client in the inets application."]; -all(suite) -> +suite() -> [{ct_hooks, [ts_install_cth]}]. + +all() -> + [ + {group, solaris8_test}, + {group, solaris9_test}, + {group, solaris10_test}, + {group, linux_x86_test}, + {group, linux_ppc_test}, + {group, macosx_x86_test}, + {group, macosx_ppc_test}, + {group, openbsd_test}, + {group, freebsd_test}, + {group, netbsd_test}, + {group, windows_xp_test}, + {group, windows_2003_server_test}, + {group, ticket_tests} + ]. + +groups() -> [ - solaris8_test, - solaris9_test, - solaris10_test, - linux_x86_test, - linux_ppc_test, - macosx_x86_test, - macosx_ppc_test, - openbsd_test, - freebsd_test, - netbsd_test, - windows_xp_test, - windows_2003_server_test, - ticket_tests + {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, + {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, + {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, + {ftp_solaris10_x86_test, all}]}, + {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, + {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, + {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, + {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, + {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, + {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, + {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, + {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, + {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, + {ticket_tests, [], [{ftp_ticket_test, all}]} ]. -solaris8_test(suite) -> - [{ftp_solaris8_sparc_test,all}]. -solaris9_test(suite) -> - [{ftp_solaris9_sparc_test,all}]. -solaris10_test(suite) -> - [{ftp_solaris10_sparc_test,all}, {ftp_solaris10_x86_test,all}]. -linux_x86_test(suite) -> - [{ftp_linux_x86_test,all}]. -linux_ppc_test(suite) -> - [{ftp_linux_ppc_test,all}]. -macosx_x86_test(suite) -> - [{ftp_macosx_x86_test,all}]. -macosx_ppc_test(suite) -> - [{ftp_macosx_ppc_test,all}]. -openbsd_test(suite) -> - [{ftp_openbsd_x86_test,all}]. -freebsd_test(suite) -> - [{ftp_freebsd_x86_test,all}]. -netbsd_test(suite) -> - [{ftp_netbsd_x86_test,all}]. -windows_xp_test(suite) -> - [{ftp_windows_xp_test,all}]. -windows_2003_server_test(suite) -> - [{ftp_windows_2003_server_test,all}]. - -ticket_tests(suite) -> - [{ftp_ticket_test, all}]. +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl index 9ca6575b2d..cbc1b04bbb 100644 --- a/lib/inets/test/ftp_format_SUITE.erl +++ b/lib/inets/test/ftp_format_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -20,23 +20,44 @@ -module(ftp_format_SUITE). -author('[email protected]'). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). -include("ftp_internal.hrl"). %% Test server specific exports --export([all/1, init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. --export([ftp_response/1, ftp_150/1, - ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1, - ftp_other_status_codes/1, ftp_multiple_lines/1, - ftp_multipel_ctrl_messages/1, format_error/1]). +-export([ ftp_150/1, + ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1, + ftp_other_status_codes/1, ftp_multiple_lines/1, + ftp_multipel_ctrl_messages/1, format_error/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, ftp_response}, format_error]. + +groups() -> + [{ftp_response, [], + [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331, + ftp_425, ftp_other_status_codes, ftp_multiple_lines, + ftp_multipel_ctrl_messages]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(doc) -> - ["Test library functions for the ftp client."]; -all(suite) -> - [ftp_response, format_error]. init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(1)), @@ -51,14 +72,6 @@ end_per_testcase(_, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -ftp_response(doc) -> - ["Test ftp_response:parse_lines/3 and ftp_response:interpret/1." - " This test case will simulate that the " - "message will be recived a little at the time on a socket and the " - "package may be broken up into smaller parts at arbitrary point."]; -ftp_response(suite) -> - [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331, ftp_425, - ftp_other_status_codes, ftp_multiple_lines, ftp_multipel_ctrl_messages]. ftp_150(doc) -> ["Especially check that respons can be devided in a random place."]; diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl index 457e18ffbe..1d66779882 100644 --- a/lib/inets/test/ftp_freebsd_x86_test.erl +++ b/lib/inets/test/ftp_freebsd_x86_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,23 +86,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl index ad38137678..bba97237f1 100644 --- a/lib/inets/test/ftp_linux_ppc_test.erl +++ b/lib/inets/test/ftp_linux_ppc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -23,7 +23,7 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -87,23 +87,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_linux_x86_test.erl b/lib/inets/test/ftp_linux_x86_test.erl index b9c88d121a..bbefd8231e 100644 --- a/lib/inets/test/ftp_linux_x86_test.erl +++ b/lib/inets/test/ftp_linux_x86_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,30 +86,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; - -all(suite) -> - [ - open, - open_port, - passive, - active, - api_missuse, - not_owner, - progress_report - ]. +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_macosx_ppc_test.erl b/lib/inets/test/ftp_macosx_ppc_test.erl index cf548a73c0..c9f33b8beb 100644 --- a/lib/inets/test/ftp_macosx_ppc_test.erl +++ b/lib/inets/test/ftp_macosx_ppc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -87,21 +87,28 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> +[open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_macosx_x86_test.erl b/lib/inets/test/ftp_macosx_x86_test.erl index 5566d4feaa..17b7160b95 100644 --- a/lib/inets/test/ftp_macosx_x86_test.erl +++ b/lib/inets/test/ftp_macosx_x86_test.erl @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,22 +86,29 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> +[open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl index a5711b7bde..bb474852c5 100644 --- a/lib/inets/test/ftp_netbsd_x86_test.erl +++ b/lib/inets/test/ftp_netbsd_x86_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,22 +86,29 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_openbsd_x86_test.erl b/lib/inets/test/ftp_openbsd_x86_test.erl index 4833b6332b..54fce702a0 100644 --- a/lib/inets/test/ftp_openbsd_x86_test.erl +++ b/lib/inets/test/ftp_openbsd_x86_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -23,7 +23,7 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -87,23 +87,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_solaris10_sparc_test.erl b/lib/inets/test/ftp_solaris10_sparc_test.erl index 6066195f9b..0da50dc91b 100644 --- a/lib/inets/test/ftp_solaris10_sparc_test.erl +++ b/lib/inets/test/ftp_solaris10_sparc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -87,23 +87,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_solaris10_x86_test.erl b/lib/inets/test/ftp_solaris10_x86_test.erl index 3bd99fc3f2..3e7045bb4d 100644 --- a/lib/inets/test/ftp_solaris10_x86_test.erl +++ b/lib/inets/test/ftp_solaris10_x86_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009. All Rights Reserved. +%% Copyright Ericsson AB 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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD, ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_), ?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -88,23 +88,30 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. %%-------------------------------------------------------------------- open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_solaris8_sparc_test.erl b/lib/inets/test/ftp_solaris8_sparc_test.erl index 9764071cd9..23dbfc8fe3 100644 --- a/lib/inets/test/ftp_solaris8_sparc_test.erl +++ b/lib/inets/test/ftp_solaris8_sparc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,22 +86,29 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_solaris9_sparc_test.erl b/lib/inets/test/ftp_solaris9_sparc_test.erl index a9f77bbdac..896e2f497f 100644 --- a/lib/inets/test/ftp_solaris9_sparc_test.erl +++ b/lib/inets/test/ftp_solaris9_sparc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -85,22 +85,29 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl index b3c4ff2657..3ebd02229e 100644 --- a/lib/inets/test/ftp_suite_lib.erl +++ b/lib/inets/test/ftp_suite_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -21,8 +21,8 @@ -module(ftp_suite_lib). --include("test_server.hrl"). --include("test_server_line.hrl"). +-include_lib("test_server/include/test_server.hrl"). +-include_lib("test_server/include/test_server_line.hrl"). -include("inets_test_lib.hrl"). %% Test server specific exports @@ -48,14 +48,17 @@ -ifdef(ftp_debug_client). -define(ftp_open(Host, Flags), - do_ftp_open(Host, [debug, {timeout, timer:seconds(15)}] ++ Flags)). + do_ftp_open(Host, [{debug, debug}, + {timeout, timer:seconds(15)} | Flags])). -else. -ifdef(ftp_trace_client). -define(ftp_open(Host, Flags), - do_ftp_open(Host, [trace, {timeout, timer:seconds(15)}] ++ Flags)). + do_ftp_open(Host, [{debug, trace}, + {timeout, timer:seconds(15)} | Flags])). -else. -define(ftp_open(Host, Flags), - do_ftp_open(Host, [verbose, {timeout, timer:seconds(15)}] ++ Flags)). + do_ftp_open(Host, [{verbose, true}, + {timeout, timer:seconds(15)} | Flags])). -endif. -endif. @@ -71,7 +74,7 @@ tickets(suite) -> ftpd_init(FtpdTag, Config) -> %% Get the host name(s) of FTP server Hosts = - case ?config(ftpd_hosts, Config) of + case ct:get_config(ftpd_hosts) of undefined -> ftpd_hosts(data_dir(Config)); H -> @@ -113,9 +116,7 @@ get_ftpd_host([Host|Hosts]) -> p("get_ftpd_host -> entry with" "~n Host: ~p" "~n", [Host]), - case (catch ftp:open({option_list, - [{host, Host}, {port, ?FTP_PORT}, - {timeout, 20000}]})) of + case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of {ok, Pid} -> (catch ftp:close(Pid)), {ok, Host}; @@ -128,7 +129,7 @@ get_ftpd_host([Host|Hosts]) -> dirty_select_ftpd_host(Config) -> Hosts = - case ?config(ftpd_hosts, Config) of + case ct:get_config(ftpd_hosts) of undefined -> ftpd_hosts(data_dir(Config)); H -> @@ -212,7 +213,7 @@ do_init_per_testcase(Case, Config) inets:start(), NewConfig = close_connection(watch_dog(Config)), Host = ftp_host(Config), - case (catch ?ftp_open(Host, [])) of + case (catch ?ftp_open(Host, [{mode, passive}])) of {ok, Pid} -> [{ftp, Pid} | data_dir(NewConfig)]; {skip, _} = SKIP -> @@ -225,9 +226,8 @@ do_init_per_testcase(Case, Config) inets:start(), NewConfig = close_connection(watch_dog(Config)), Host = ftp_host(Config), - case (catch ?ftp_open(Host, [])) of + case (catch ?ftp_open(Host, [{mode, active}])) of {ok, Pid} -> - ok = ftp:force_active(Pid), [{ftp, Pid} | data_dir(NewConfig)]; {skip, _} = SKIP -> SKIP @@ -240,11 +240,10 @@ do_init_per_testcase(Case, Config) io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), NewConfig = close_connection(watch_dog(Config)), Host = ftp_host(Config), - Opts = [{host, Host}, - {port, ?FTP_PORT}, - {flags, [verbose]}, + Opts = [{port, ?FTP_PORT}, + {verbose, true}, {progress, {?MODULE, progress, #progress{}}}], - case ftp:open({option_list, Opts}) of + case ftp:open(Host, Opts) of {ok, Pid} -> ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), [{ftp, Pid} | data_dir(NewConfig)]; @@ -257,22 +256,23 @@ do_init_per_testcase(Case, Config) -> inets:start(), NewConfig = close_connection(watch_dog(Config)), Host = ftp_host(Config), - Flags = + Opts1 = if ((Case =:= passive_ip_v6_disabled) orelse (Case =:= active_ip_v6_disabled)) -> - [ip_v6_disabled]; + [{ipfamily, inet}]; true -> [] end, - case (catch ?ftp_open(Host, Flags)) of + Opts2 = + case string:tokens(atom_to_list(Case), [$_]) of + [_, "active" | _] -> + [{mode, active} | Opts1]; + _ -> + [{mode, passive} | Opts1] + end, + case (catch ?ftp_open(Host, Opts2)) of {ok, Pid} -> - case string:tokens(atom_to_list(Case), [$_]) of - [_, "active"|_] -> - ok = ftp:force_active(Pid); - _ -> - ok - end, ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), [{ftp, Pid} | data_dir(NewConfig)]; {skip, _} = SKIP -> @@ -365,6 +365,7 @@ open(Config) when is_list(Config) -> Host = ftp_host(Config), (catch tc_open(Host)). + tc_open(Host) -> {ok, Pid} = ?ftp_open(Host, []), ok = ftp:close(Pid), @@ -374,8 +375,9 @@ tc_open(Host) -> {flags, [verbose]}, {timeout, 30000}]}), ok = ftp:close(Pid1), - {error, ehost} = ftp:open({option_list, [{port, ?FTP_PORT}, - {flags, [verbose]}]}), + + {error, ehost} = + ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}), {ok, Pid2} = ftp:open(Host), ok = ftp:close(Pid2), @@ -408,6 +410,15 @@ tc_open(Host) -> {mode, cool}]}), test_server:sleep(100), ok = ftp:close(Pid6), + + {ok, Pid7} = + ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]), + ok = ftp:close(Pid7), + + {ok, Pid8} = + ftp:open(Host, ?FTP_PORT), + ok = ftp:close(Pid8), + ok. @@ -420,7 +431,7 @@ open_port(suite) -> []; open_port(Config) when is_list(Config) -> Host = ftp_host(Config), - {ok, Pid} = ftp:open(Host, ?FTP_PORT), + {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]), ok = ftp:close(Pid), {error, ehost} = ftp:open(?BAD_HOST, []), ok. @@ -956,26 +967,39 @@ api_missuse(doc)-> ["Test that behaviour of the ftp process if the api is abused"]; api_missuse(suite) -> []; api_missuse(Config) when is_list(Config) -> + io:format("api_missuse -> entry~n", []), + Flag = process_flag(trap_exit, true), Pid = ?config(ftp, Config), Host = ftp_host(Config), - + %% Serious programming fault, connetion will be shut down - {error, {connection_terminated, 'API_violation'}} = - gen_server:call(Pid, {self(), foobar, 10}, infinity), + io:format("api_missuse -> verify bad call termination (~p)~n", [Pid]), + case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of + {error, {connection_terminated, 'API_violation'}} -> + ok; + Unexpected1 -> + exit({unexpected_result, Unexpected1}) + end, test_server:sleep(500), undefined = process_info(Pid, status), + io:format("api_missuse -> start new client~n", []), {ok, Pid2} = ?ftp_open(Host, []), %% Serious programming fault, connetion will be shut down + io:format("api_missuse -> verify bad cast termination~n", []), gen_server:cast(Pid2, {self(), foobar, 10}), test_server:sleep(500), undefined = process_info(Pid2, status), + io:format("api_missuse -> start new client~n", []), {ok, Pid3} = ?ftp_open(Host, []), %% Could be an innocent misstake the connection lives. + io:format("api_missuse -> verify bad bang~n", []), Pid3 ! foobar, test_server:sleep(500), {status, _} = process_info(Pid3, status), + process_flag(trap_exit, Flag), + io:format("api_missuse -> done~n", []), ok. @@ -1105,10 +1129,16 @@ ticket_6035(Config) -> LogFile = filename:join([PrivDir,"ticket_6035.log"]), try begin + p("ticket_6035 -> select ftpd host"), Host = dirty_select_ftpd_host(Config), + p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), + p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", + [Pid, LogFile]), error_logger:logfile({open, LogFile}), - ok = kill_ftp_proc_6035(Pid,LogFile), + p("ticket_6035 -> error logfile open => now kill waiter process"), + true = kill_ftp_proc_6035(Pid, LogFile), + p("ticket_6035 -> waiter process killed => now close error logfile"), error_logger:logfile(close), p("ticket_6035 -> done", []), ok @@ -1122,7 +1152,7 @@ kill_ftp_proc_6035(Pid, LogFile) -> p("kill_ftp_proc_6035 -> entry"), receive open -> - p("kill_ftp_proc_6035 -> received open: send shutdown"), + p("kill_ftp_proc_6035 -> received open => now issue shutdown"), exit(Pid, shutdown), kill_ftp_proc_6035(Pid, LogFile); {open_failed, Reason} -> @@ -1135,11 +1165,11 @@ kill_ftp_proc_6035(Pid, LogFile) -> is_error_report_6035(LogFile) end. -open_wait_6035(FtpServer, From) -> - p("open_wait_6035 -> try connect to ~s", [FtpServer]), +open_wait_6035({Tag, FtpServer}, From) -> + p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of {ok, Pid} -> - p("open_wait_6035 -> connected, now login"), + p("open_wait_6035 -> connected (~p), now login", [Pid]), LoginResult = ftp:user(Pid,"anonymous","kldjf"), p("open_wait_6035 -> login result: ~p", [LoginResult]), From ! open, @@ -1167,22 +1197,27 @@ is_error_report_6035(LogFile) -> Res = case file:read_file(LogFile) of {ok, Bin} -> - p("is_error_report_6035 -> logfile read"), - read_log_6035(binary_to_list(Bin)); + Txt = binary_to_list(Bin), + p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), + read_log_6035(Txt); _ -> - ok + false end, p("is_error_report_6035 -> logfile read result: " "~n ~p", [Res]), - file:delete(LogFile), + %% file:delete(LogFile), Res. read_log_6035("=ERROR REPORT===="++_Rest) -> - error_report; -read_log_6035([_H|T]) -> + p("read_log_6035 -> ERROR REPORT detected"), + true; +read_log_6035([H|T]) -> + p("read_log_6035 -> OTHER: " + "~p", [H]), read_log_6035(T); read_log_6035([]) -> - ok. + p("read_log_6035 -> done"), + false. %%-------------------------------------------------------------------- @@ -1531,11 +1566,11 @@ split([C| Cs], I, Is) -> split([], I, Is) -> lists:reverse([lists:reverse(I)| Is]). -do_ftp_open(Host, Flags) -> +do_ftp_open(Host, Opts) -> io:format("do_ftp_open -> entry with" - "~n Host: ~p" - "~n Flags: ~p", [Host, Flags]), - case ftp:open(Host, Flags) of + "~n Host: ~p" + "~n Opts: ~p", [Host, Opts]), + case ftp:open(Host, Opts) of {ok, _} = OK -> OK; {error, Reason} -> diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl index 6748df03bb..fe4ab35728 100644 --- a/lib/inets/test/ftp_ticket_test.erl +++ b/lib/inets/test/ftp_ticket_test.erl @@ -35,17 +35,27 @@ end_per_testcase(Case, Config) -> ftp_suite_lib:end_per_testcase(Case, Config). -all(suite) -> - {conf,init,tickets(),fin}. +all() -> + tickets(). -init(Config) -> +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_suite(Config) -> ?LIB_MOD:ftpd_init(ticket_test, Config). -tickets() -> +tickets() -> [ticket_6035]. -fin(Config) -> +end_per_suite(Config) -> ?LIB_MOD:ftpd_fin(Config). ticket_6035(X) -> ?LIB_MOD:ticket_6035(X). diff --git a/lib/inets/test/ftp_windows_2003_server_test.erl b/lib/inets/test/ftp_windows_2003_server_test.erl index d24318d04f..57f1ae8358 100644 --- a/lib/inets/test/ftp_windows_2003_server_test.erl +++ b/lib/inets/test/ftp_windows_2003_server_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,22 +86,29 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. %% Test cases starts here. open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/ftp_windows_xp_test.erl b/lib/inets/test/ftp_windows_xp_test.erl index bc161e4f6a..06d919ba00 100644 --- a/lib/inets/test/ftp_windows_xp_test.erl +++ b/lib/inets/test/ftp_windows_xp_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -22,7 +22,7 @@ -compile(export_all). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(LIB_MOD,ftp_suite_lib). -define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)). @@ -86,20 +86,27 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Test ftp client"]; +all() -> + [open, open_port, {group, passive}, {group, active}, + api_missuse, not_owner, {group, progress_report}]. + +groups() -> + [{passive, [], ftp_suite_lib:passive(suite)}, + {active, [], ftp_suite_lib:active(suite)}, + {progress_report, [], + ftp_suite_lib:progress_report(suite)}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [open, open_port, passive, active, api_missuse, - not_owner, progress_report]. open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1). open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1). -passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1). -active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1). api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1). not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1). -progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1). passive_user(X) -> ?LIB_MOD:passive_user(X). passive_pwd(X) -> ?LIB_MOD:passive_pwd(X). diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 79945f0f4d..04c7358715 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% 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 @@ -21,28 +21,49 @@ -module(http_format_SUITE). -author('[email protected]'). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). -include("http_internal.hrl"). %% Test server specific exports --export([all/1, init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. --export([chunk/1, chunk_decode/1, chunk_encode/1, - chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1, - chunk_decode_empty_chunk_otp_6511/1, - chunk_decode_trailer/1, - http_response/1, http_request/1, validate_request_line/1, script/1, - esi_parse_headers/1, cgi_parse_headers/1, - is_absolut_uri/1, convert_netscapecookie_date/1]). - -all(doc) -> - ["Test library functions to the http client and server."]; -all(suite) -> - [chunk, - http_response, http_request, validate_request_line, - script, is_absolut_uri, convert_netscapecookie_date]. +-export([ chunk_decode/1, chunk_encode/1, + chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1, + chunk_decode_empty_chunk_otp_6511/1, + chunk_decode_trailer/1, + http_response/1, http_request/1, validate_request_line/1, + esi_parse_headers/1, cgi_parse_headers/1, + is_absolut_uri/1, convert_netscapecookie_date/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, chunk}, http_response, http_request, + validate_request_line, {group, script}, is_absolut_uri, + convert_netscapecookie_date]. + +groups() -> + [{script, [], [esi_parse_headers, cgi_parse_headers]}, + {chunk, [], + [chunk_decode, chunk_encode, chunk_extensions_otp_6005, + chunk_decode_otp_6264, + chunk_decode_empty_chunk_otp_6511, + chunk_decode_trailer]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(1)), @@ -57,17 +78,7 @@ end_per_testcase(_, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -script(doc) -> - ["Test header parsing in esi/cgi functionality."]; -script(suite) -> - [esi_parse_headers, cgi_parse_headers]. - -chunk(doc) -> - ["Test chunk encoding"]; -chunk(suite) -> - [chunk_decode, chunk_encode, chunk_extensions_otp_6005, - chunk_decode_otp_6264, chunk_decode_empty_chunk_otp_6511, - chunk_decode_trailer]. + %%------------------------------------------------------------------------- chunk_decode(doc) -> @@ -573,7 +584,9 @@ convert_netscapecookie_date(Config) when is_list(Config) -> http_util:convert_netscapecookie_date("Sun, 12-Dec-06 08:59:38 GMT"), {{2006,12,12},{8,59,38}} = http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"), - ok. + {{2036,1,1},{8,0,1}} = + http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"), + ok. %%-------------------------------------------------------------------- %%% Internal functions diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 71f017dae6..6edd5371af 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -24,10 +24,11 @@ -module(httpc_SUITE). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). +-include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -59,58 +60,102 @@ %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -all(doc) -> - ["Test the http client in the intes application."]; -all(suite) -> +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, http_post, - http_dummy_pipe, - http_inets_pipe, + http_post_streaming, + http_dummy_pipe, + http_inets_pipe, http_trace, - http_async, - http_save_to_file, + http_async, + http_save_to_file, http_save_to_file_async, - http_headers, - http_headers_dummy, + http_headers, + http_headers_dummy, http_bad_response, - ssl_head, - ssl_get, - ssl_trace, http_redirect, http_redirect_loop, - http_internal_server_error, - http_userinfo, - http_cookie, - http_server_does_not_exist, + http_internal_server_error, + http_userinfo, http_cookie, + http_server_does_not_exist, http_invalid_http, - http_emulate_lower_versions, - http_relaxed, + 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, + parse_url, options, - ipv6, - headers_as_is, - tickets + headers_as_is, + {group, proxy}, + {group, ssl}, + {group, stream}, + {group, ipv6}, + {group, tickets}, + initial_server_connect ]. - + +groups() -> + [ + {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, + proxy_not_modified_otp_6821, + no_content_204_otp_6982, + missing_CR_otp_7304, + {group, otp_7883}, + {group, otp_8154}, + {group, otp_8106}, + otp_8056, + otp_8352, + otp_8371, + otp_8739]}, + {otp_7883, [], [otp_7883_1, + otp_7883_2]}, + {otp_8154, [], [otp_8154_1]}, + {otp_8106, [], [otp_8106_pid, + otp_8106_fun, + otp_8106_mfa]}, + {ipv6, [], [ipv6_ipcomm, ipv6_essl]} + ]. + + + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -149,6 +194,7 @@ init_per_suite(Config) -> {local_port, ?IP_PORT}, {local_ssl_port, ?SSL_PORT} | Config]. + %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -174,54 +220,153 @@ 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 = Case, Config) -> + %% Try to check if crypto actually exist or not, + %% this test case does not work unless it does + try + begin + ensure_started(crypto), + ensure_started(public_key), + ensure_started(ssl), + inets:start(), + 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(Case, Timeout, Config) -> io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", - [?MODULE, Timeout, Case]), - PrivDir = ?config(priv_dir, Config), + [?MODULE, Case, Timeout]), + PrivDir = ?config(priv_dir, Config), + tsp("init_per_testcase -> stop inets"), application:stop(inets), - Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), - TmpConfig = lists:keydelete(watchdog, 1, Config), - IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", + Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), + TmpConfig = lists:keydelete(watchdog, 1, Config), + IpConfFile = integer_to_list(?IP_PORT) ++ ".conf", SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf", + %% inets:enable_trace(max, io, httpd), + %% inets:enable_trace(max, io, httpc), + inets:enable_trace(max, io, all), + NewConfig = case atom_to_list(Case) of - "ssl" ++ _ -> - application:stop(ssl), - TmpConfig2 = - lists:keydelete(local_ssl_server, 1, TmpConfig), - %% Will start inets - Server = - inets_test_lib:start_http_server( - filename:join(PrivDir, SslConfFile)), - [{watchdog, Dog}, {local_ssl_server, Server} | TmpConfig2]; - "proxy" ++ Rest -> - case Rest of - "_https_not_supported" -> - inets:start(), - case (catch application:start(ssl)) of - ok -> - [{watchdog, Dog} | TmpConfig]; - _ -> - [{skip, - "SSL does not seem to be supported"} - | TmpConfig] - end; - _ -> - case is_proxy_available(?PROXY, ?PROXY_PORT) of - true -> - inets:start(), - [{watchdog, Dog} | TmpConfig]; - false -> - [{skip, "Failed to contact proxy"} | - TmpConfig] - end - end; + [$s, $s, $l | _] -> + init_per_testcase_ssl(ssl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); + + [$o, $s, $s, $l | _] -> + init_per_testcase_ssl(ossl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); + + [$e, $s, $s, $l | _] -> + init_per_testcase_ssl(essl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); + + "proxy_" ++ Rest -> + io:format("init_per_testcase -> Rest: ~p~n", [Rest]), + case Rest of + "https_not_supported" -> + tsp("init_per_testcase -> [proxy case] start inets"), + inets:start(), + tsp("init_per_testcase -> " + "[proxy case] start crypto, public_key and ssl"), + try ensure_started([crypto, public_key, ssl]) of + ok -> + [{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 + %% of the test cases no longer work (erlang.org + %% previously run on Apache). + %% Until we have had time to update inets + %% (and updated erlang.org to use that inets) + %% and the test cases, we simply skip the + %% problematic test cases. + %% This is not ideal, but I am busy.... + case is_proxy_available(?PROXY, ?PROXY_PORT) of + true -> + BadCases = + [ + "delete", + "get", + "head", + "not_modified_otp_6821", + "options", + "page_does_not_exist", + "post", + "put", + "stream" + ], + case lists:member(Rest, BadCases) of + true -> + [{skip, "TC and server not compatible"}| + TmpConfig]; + false -> + inets:start(), + [{watchdog, Dog} | TmpConfig] + end; + false -> + [{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 = @@ -231,13 +376,28 @@ init_per_testcase(Case, Timeout, Config) -> [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, - http:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, - ["localhost", ?IPV6_LOCAL_HOST]}}]), - inets:enable_trace(max, io, httpc), - %% inets:enable_trace(max, io, all), - %% snmp:set_trace([gen_tcp, inet_tcp, prim_inet]), + %% This will fail for the ipv6_ - cases (but that is ok) + httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, + ["localhost", ?IPV6_LOCAL_HOST]}}, + {ipfamily, inet6fb4}]), + + %% snmp:set_trace([gen_tcp]), NewConfig. + +init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> + tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]), + application:stop(ssl), + Config2 = lists:keydelete(local_ssl_server, 1, Config), + %% Will start inets + tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)", + [Tag]), + Server = inets_test_lib:start_http_server( + filename:join(PrivDir, SslConfFile), Tag), + tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]), + [{local_ssl_server, Server} | Config2]. + + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -246,13 +406,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) -> @@ -261,34 +444,14 @@ finish(Config) -> undefined -> ok; _ -> + tsp("finish -> stop watchdog (~p)", [Dog]), test_server:timetrap_cancel(Dog) - end, - (catch application:stop(inets)). - + end. %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -tickets(doc) -> - ["."]; -tickets(suite) -> - [ - hexed_query_otp_6191, - empty_body_otp_6243, - empty_response_header_otp_6830, - transfer_encoding_otp_6807, - proxy_not_modified_otp_6821, - no_content_204_otp_6982, - missing_CR_otp_7304, - otp_7883, - otp_8154, - otp_8106, - otp_8056, - otp_8352, - otp_8371, - otp_8739 - ]. %%------------------------------------------------------------------------- @@ -309,7 +472,7 @@ http_head(Config) when is_list(Config) -> ok -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - case http:request(head, {URL, []}, [], []) of + case httpc:request(head, {URL, []}, [], []) of {ok, {{_,200,_}, [_ | _], []}} -> ok; {ok, WrongReply} -> @@ -340,7 +503,7 @@ http_get(Config) when is_list(Config) -> HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], Options1 = [], Body = - case http:request(Method, Request, HttpOptions1, Options1) of + case httpc:request(Method, Request, HttpOptions1, Options1) of {ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} -> ReplyBody; {ok, UnexpectedReply1} -> @@ -349,12 +512,12 @@ http_get(Config) when is_list(Config) -> tsf({bad_reply, Error1}) end, - %% eqvivivalent to http:request(get, {URL, []}, [], []), + %% eqvivivalent to httpc:request(get, {URL, []}, [], []), inets_test_lib:check_body(Body), HttpOptions2 = [], Options2 = [{body_format, binary}], - case http:request(Method, Request, HttpOptions2, Options2) of + case httpc:request(Method, Request, HttpOptions2, Options2) of {ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) -> ok; {ok, {{_,200,_}, [_ | _], BadBin}} -> @@ -393,17 +556,64 @@ http_post(Config) when is_list(Config) -> Body = lists:duplicate(100, "1"), {ok, {{_,200,_}, [_ | _], [_ | _]}} = - http:request(post, {URL, [{"expect","100-continue"}], + httpc:request(post, {URL, [{"expect","100-continue"}], "text/plain", Body}, [], []), {ok, {{_,504,_}, [_ | _], []}} = - http:request(post, {URL, [{"expect","100-continue"}], + httpc:request(post, {URL, [{"expect","100-continue"}], "text/plain", "foobar"}, [], []); _ -> {skip, "Failed to start local http-server"} end. %%------------------------------------------------------------------------- +http_post_streaming(doc) -> + ["Test streaming http post request against local server. " + "We only care about the client side of the the post. " + "The server script will not actually use the post data."]; +http_post_streaming(suite) -> + []; +http_post_streaming(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = case test_server:os_type() of + {win32, _} -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo.exe"; + _ -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo" + end, + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + io:format("~w:http_post_streaming_fun -> " + "zero~n", [?MODULE]), + eof; + (LenLeft) -> + io:format("~w:http_post_streaming_fun -> " + "LenLeft: ~p~n", [?MODULE, LenLeft]), + {ok, lists:duplicate(10, "1"), LenLeft - 10} + end, + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "100"}], + "text/plain", {BodyFun, 100}}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, + {"content-length", "10"}], + "text/plain", {BodyFun, 10}}, [], []); + + _ -> + {skip, "Failed to start local http-server"} + end. + + +%%------------------------------------------------------------------------- http_emulate_lower_versions(doc) -> ["Perform request as 0.9 and 1.0 clients."]; http_emulate_lower_versions(suite) -> @@ -414,13 +624,13 @@ http_emulate_lower_versions(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, Body0} = - http:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), + httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []), inets_test_lib:check_body(Body0), {ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} = - http:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), + httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []), inets_test_lib:check_body(Body1), {ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} = - http:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), + httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), inets_test_lib:check_body(Body2); _-> {skip, "Failed to start local http-server"} @@ -434,24 +644,24 @@ http_relaxed(doc) -> http_relaxed(suite) -> []; http_relaxed(Config) when is_list(Config) -> - ok = http:set_options([{ipv6, disabled}]), % also test the old option - %% ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipv6, disabled}]), % also test the old option + %% ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_reason_phrase.html", {error, Reason} = - http:request(get, {URL, []}, [{relaxed, false}], []), + httpc:request(get, {URL, []}, [{relaxed, false}], []), test_server:format("Not relaxed: ~p~n", [Reason]), {ok, {{_, 200, _}, [_ | _], [_ | _]}} = - http:request(get, {URL, []}, [{relaxed, true}], []), + httpc:request(get, {URL, []}, [{relaxed, true}], []), DummyServerPid ! stop, - ok = http:set_options([{ipv6, enabled}]), - %% ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipv6, enabled}]), + %% ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -461,15 +671,15 @@ http_dummy_pipe(doc) -> http_dummy_pipe(suite) -> []; http_dummy_pipe(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", test_pipeline(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. http_inets_pipe(doc) -> @@ -487,34 +697,35 @@ http_inets_pipe(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. + test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), - http:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = - http:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), - - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = - http:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), - - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = - http:request(get, {URL, []}, [], []), + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1"), + {ok, RequestId1} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + p("test_pipeline -> RequestId1: ~p", [RequestId1]), + + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request 2"), + {ok, RequestId2} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId2: ~p", [RequestId2]), + p("test_pipeline -> RequestId2: ~p", [RequestId2]), + + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(get, {URL, []}, [], []), p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive + receive {http, {RequestId1, {{_, 200, _}, _, _}}} -> p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), receive @@ -532,46 +743,46 @@ test_pipeline(URL) -> ok; {http, Msg2} -> test_server:fail(Msg2) - end; + end; {http, Msg3} -> test_server:fail(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - test_server:fail({error, {timeout, Any1}}) - end - end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = - http:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), - - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = - http:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = http:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - test_server:fail(http_cancel_request_failed) - after 3000 -> - ok + after 60000 -> + receive Any1 -> + tsp("received crap after timeout: ~n ~p", [Any1]), + test_server:fail({error, {timeout, Any1}}) + end end, - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> + p("test_pipeline -> sleep some"), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request 4"), + {ok, RequestId3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId3: ~p", [RequestId3]), + p("test_pipeline -> RequestId3: ~p", [RequestId3]), + + p("test_pipeline -> issue (async) request 5"), + {ok, RequestId4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId4: ~p~n", [RequestId4]), + p("test_pipeline -> RequestId4: ~p", [RequestId4]), + + p("test_pipeline -> cancel (async) request 4"), + ok = httpc:cancel_request(RequestId3), + + p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request 4"), + Body = + receive + {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> p("test_pipeline -> received reply for (async) request 5"), tsp("Receive : ~p", [Res]), BinBody4; @@ -586,9 +797,9 @@ test_pipeline(URL) -> p("test_pipeline -> check reply for (async) request 5"), inets_test_lib:check_body(binary_to_list(Body)), - + p("test_pipeline -> ensure no unexpected incomming"), - receive + receive {http, Any} -> test_server:fail({unexpected_message, Any}) after 500 -> @@ -598,8 +809,6 @@ test_pipeline(URL) -> p("test_pipeline -> done"), ok. - - %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -610,7 +819,7 @@ http_trace(Config) when is_list(Config) -> ok -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - case http:request(trace, {URL, []}, [], []) of + case httpc:request(trace, {URL, []}, [], []) of {ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} -> ok; {ok, {{_,200,_}, [_ | _], WrongBody}} -> @@ -634,7 +843,7 @@ http_async(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, RequestId} = - http:request(get, {URL, []}, [], [{sync, false}]), + httpc:request(get, {URL, []}, [], [{sync, false}]), Body = receive @@ -647,8 +856,8 @@ http_async(Config) when is_list(Config) -> inets_test_lib:check_body(binary_to_list(Body)), {ok, NewRequestId} = - http:request(get, {URL, []}, [], [{sync, false}]), - ok = http:cancel_request(NewRequestId), + httpc:request(get, {URL, []}, [], [{sync, false}]), + ok = httpc:cancel_request(NewRequestId), receive {http, {NewRequestId, _NewResult}} -> test_server:fail(http_cancel_request_failed) @@ -672,9 +881,9 @@ http_save_to_file(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, saved_to_file} - = http:request(get, {URL, []}, [], [{stream, FilePath}]), + = httpc:request(get, {URL, []}, [], [{stream, FilePath}]), {ok, Bin} = file:read_file(FilePath), - {ok, {{_,200,_}, [_ | _], Body}} = http:request(URL), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> {skip, "Failed to start local http-server"} @@ -693,7 +902,7 @@ http_save_to_file_async(Config) when is_list(Config) -> FilePath = filename:join(PrivDir, "dummy.html"), Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - {ok, RequestId} = http:request(get, {URL, []}, [], + {ok, RequestId} = httpc:request(get, {URL, []}, [], [{stream, FilePath}, {sync, false}]), receive @@ -704,7 +913,7 @@ http_save_to_file_async(Config) when is_list(Config) -> end, {ok, Bin} = file:read_file(FilePath), - {ok, {{_,200,_}, [_ | _], Body}} = http:request(URL), + {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> {skip, "Failed to start local http-server"} @@ -734,7 +943,7 @@ http_headers(Config) when is_list(Config) -> Date = httpd_util:rfc1123_date({date(), time()}), {ok, {{_,200,_}, [_ | _], [_ | _]}} = - http:request(get, {URL, [{"If-Modified-Since", + httpc:request(get, {URL, [{"If-Modified-Since", Mod}, {"From","[email protected]"}, {"Date", Date} @@ -745,7 +954,7 @@ http_headers(Config) when is_list(Config) -> CreatedSec+1)), {ok, {{_,200,_}, [_ | _], [_ | _]}} = - http:request(get, {URL, [{"If-UnModified-Since", + httpc:request(get, {URL, [{"If-UnModified-Since", Mod1} ]}, [], []), @@ -753,12 +962,12 @@ http_headers(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], [_ | _]}} = - http:request(get, {URL, [{"If-Match", + httpc:request(get, {URL, [{"If-Match", Tag} ]}, [], []), {ok, {{_,200,_}, [_ | _], _}} = - http:request(get, {URL, [{"If-None-Match", + httpc:request(get, {URL, [{"If-None-Match", "NotEtag,NeihterEtag"}, {"Connection", "Close"} ]}, [], []), @@ -776,8 +985,8 @@ http_headers_dummy(doc) -> http_headers_dummy(suite) -> []; http_headers_dummy(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html", @@ -792,7 +1001,7 @@ http_headers_dummy(Config) when is_list(Config) -> %% that the client header-handling code. This would not %% be a vaild http-request! {ok, {{_,200,_}, [_ | _], [_|_]}} = - http:request(post, + httpc:request(post, {URL, [{"Via", "1.0 fred, 1.1 nowhere.com (Apache/1.1)"}, @@ -831,7 +1040,7 @@ http_headers_dummy(Config) when is_list(Config) -> ], "text/plain", FooBar}, [], []), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -841,21 +1050,21 @@ http_bad_response(doc) -> http_bad_response(suite) -> []; http_bad_response(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html", - {error, timeout} = http:request(get, {URL, []}, [{timeout, 400}], []), + {error, timeout} = httpc:request(get, {URL, []}, [{timeout, 400}], []), - {error, Reason} = http:request(URL1), + {error, Reason} = httpc:request(URL1), test_server:format("Wrong Statusline: ~p~n", [Reason]), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -865,69 +1074,168 @@ ssl_head(doc) -> ssl_head(suite) -> []; ssl_head(Config) when is_list(Config) -> + ssl_head(ssl, Config). + +ossl_head(doc) -> + ["Same as http_head/1 but over ssl sockets."]; +ossl_head(suite) -> + []; +ossl_head(Config) when is_list(Config) -> + ssl_head(ossl, Config). + +essl_head(doc) -> + ["Same as http_head/1 but over ssl sockets."]; +essl_head(suite) -> + []; +essl_head(Config) when is_list(Config) -> + ssl_head(essl, Config). + +ssl_head(SslTag, Config) -> + tsp("ssl_head -> entry with" + "~n SslTag: ~p" + "~n Config: ~p", [SslTag, Config]), case ?config(local_ssl_server, Config) of ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + ossl -> + {ossl, SSLOptions}; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_head -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), {ok, {{_,200, _}, [_ | _], []}} = - http:request(head, {URL, []}, [{ssl, SSLOptions}], []); + httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []); {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. + + %%------------------------------------------------------------------------- ssl_get(doc) -> ["Same as http_get/1 but over ssl sockets."]; ssl_get(suite) -> []; ssl_get(Config) when is_list(Config) -> + ssl_get(ssl, Config). + +ossl_get(doc) -> + ["Same as http_get/1 but over ssl sockets."]; +ossl_get(suite) -> + []; +ossl_get(Config) when is_list(Config) -> + ssl_get(ossl, Config). + +essl_get(doc) -> + ["Same as http_get/1 but over ssl sockets."]; +essl_get(suite) -> + []; +essl_get(Config) when is_list(Config) -> + ssl_get(essl, Config). + +ssl_get(SslTag, Config) when is_list(Config) -> case ?config(local_ssl_server, Config) of ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} = - http:request(get, {URL, []}, [{ssl, SSLOptions}], []), - inets_test_lib:check_body(Body); + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + ossl -> + {ossl, SSLOptions}; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_get -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), + {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} = + httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []), + inets_test_lib:check_body(Body); {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. + + %%------------------------------------------------------------------------- ssl_trace(doc) -> ["Same as http_trace/1 but over ssl sockets."]; ssl_trace(suite) -> []; ssl_trace(Config) when is_list(Config) -> + ssl_trace(ssl, Config). + +ossl_trace(doc) -> + ["Same as http_trace/1 but over ssl sockets."]; +ossl_trace(suite) -> + []; +ossl_trace(Config) when is_list(Config) -> + ssl_trace(ossl, Config). + +essl_trace(doc) -> + ["Same as http_trace/1 but over ssl sockets."]; +essl_trace(suite) -> + []; +essl_trace(Config) when is_list(Config) -> + ssl_trace(essl, Config). + +ssl_trace(SslTag, Config) when is_list(Config) -> case ?config(local_ssl_server, Config) of ok -> - DataDir = ?config(data_dir, Config), - Port = ?config(local_ssl_port, Config), - URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", - CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + DataDir = ?config(data_dir, Config), + Port = ?config(local_ssl_port, Config), + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html", + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], - case http:request(trace, {URL, []}, [{ssl, SSLOptions}], []) of + SSLConfig = + case SslTag of + ssl -> + SSLOptions; + ossl -> + {ossl, SSLOptions}; + essl -> + {essl, SSLOptions} + end, + tsp("ssl_trace -> make request using: " + "~n URL: ~p" + "~n SslTag: ~p" + "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), + case httpc:request(trace, {URL, []}, [{ssl, SSLConfig}], []) of {ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} -> ok; {ok, {{_,200,_}, [_ | _], WrongBody}} -> - test_server:fail({wrong_body, WrongBody}); + tsf({wrong_body, WrongBody}); {ok, WrongReply} -> - test_server:fail({wrong_reply, WrongReply}); + tsf({wrong_reply, WrongReply}); Error -> - test_server:fail({failed, Error}) + tsf({failed, Error}) end; {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. + + %%------------------------------------------------------------------------- http_redirect(doc) -> ["Test redirect with dummy server as httpd does not implement" @@ -940,10 +1248,10 @@ http_redirect(Config) when is_list(Config) -> case ?config(local_server, Config) of ok -> tsp("http_redirect -> set ipfamily option to inet"), - ok = http:set_options([{ipfamily, inet}]), + 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", @@ -951,29 +1259,29 @@ http_redirect(Config) when is_list(Config) -> tsp("http_redirect -> issue request 1: " "~n ~p", [URL300]), {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URL300, []}, [], []), + = httpc:request(get, {URL300, []}, [], []), tsp("http_redirect -> issue request 2: " "~n ~p", [URL300]), {ok, {{_,300,_}, [_ | _], _}} = - http:request(get, {URL300, []}, [{autoredirect, false}], []), + httpc:request(get, {URL300, []}, [{autoredirect, false}], []), URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html", tsp("http_redirect -> issue request 3: " "~n ~p", [URL301]), {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URL301, []}, [], []), + = httpc:request(get, {URL301, []}, [], []), tsp("http_redirect -> issue request 4: " "~n ~p", [URL301]), {ok, {{_,200,_}, [_ | _], []}} - = http:request(head, {URL301, []}, [], []), + = httpc:request(head, {URL301, []}, [], []), tsp("http_redirect -> issue request 5: " "~n ~p", [URL301]), {ok, {{_,301,_}, [_ | _], [_|_]}} - = http:request(post, {URL301, [],"text/plain", "foobar"}, + = httpc:request(post, {URL301, [],"text/plain", "foobar"}, [], []), URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html", @@ -981,8 +1289,8 @@ http_redirect(Config) when is_list(Config) -> tsp("http_redirect -> issue request 6: " "~n ~p", [URL302]), {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URL302, []}, [], []), - case http:request(get, {URL302, []}, [], []) of + = httpc:request(get, {URL302, []}, [], []), + case httpc:request(get, {URL302, []}, [], []) of {ok, Reply7} -> case Reply7 of {{_,200,_}, [_ | _], [_|_]} -> @@ -1009,12 +1317,12 @@ http_redirect(Config) when is_list(Config) -> tsp("http_redirect -> issue request 7: " "~n ~p", [URL302]), {ok, {{_,200,_}, [_ | _], []}} - = http:request(head, {URL302, []}, [], []), + = httpc:request(head, {URL302, []}, [], []), tsp("http_redirect -> issue request 8: " "~n ~p", [URL302]), {ok, {{_,302,_}, [_ | _], [_|_]}} - = http:request(post, {URL302, [],"text/plain", "foobar"}, + = httpc:request(post, {URL302, [],"text/plain", "foobar"}, [], []), URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html", @@ -1022,23 +1330,23 @@ http_redirect(Config) when is_list(Config) -> tsp("http_redirect -> issue request 9: " "~n ~p", [URL307]), {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URL307, []}, [], []), + = httpc:request(get, {URL307, []}, [], []), tsp("http_redirect -> issue request 10: " "~n ~p", [URL307]), {ok, {{_,200,_}, [_ | _], []}} - = http:request(head, {URL307, []}, [], []), + = httpc:request(head, {URL307, []}, [], []), tsp("http_redirect -> issue request 11: " "~n ~p", [URL307]), {ok, {{_,307,_}, [_ | _], [_|_]}} - = http:request(post, {URL307, [],"text/plain", "foobar"}, + = httpc:request(post, {URL307, [],"text/plain", "foobar"}, [], []), tsp("http_redirect -> stop dummy server"), DummyServerPid ! stop, tsp("http_redirect -> reset ipfamily option (to inet6fb4)"), - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), tsp("http_redirect -> done"), ok; @@ -1054,15 +1362,15 @@ http_redirect_loop(doc) -> http_redirect_loop(suite) -> []; http_redirect_loop(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", {ok, {{_,300,_}, [_ | _], _}} - = http:request(get, {URL, []}, [], []), + = httpc:request(get, {URL, []}, [], []), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. %%------------------------------------------------------------------------- @@ -1071,13 +1379,13 @@ http_internal_server_error(doc) -> http_internal_server_error(suite) -> []; http_internal_server_error(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", {ok, {{_,500,_}, [_ | _], _}} - = http:request(get, {URL500, []}, [], []), + = httpc:request(get, {URL500, []}, [], []), URL503 = ?URL_START ++ integer_to_list(Port) ++ "/503.html", @@ -1087,16 +1395,16 @@ http_internal_server_error(Config) when is_list(Config) -> ets:insert(unavailable, {503, unavailable}), {ok, {{_,200, _}, [_ | _], [_|_]}} = - http:request(get, {URL503, []}, [], []), + httpc:request(get, {URL503, []}, [], []), ets:insert(unavailable, {503, long_unavailable}), {ok, {{_,503, _}, [_ | _], [_|_]}} = - http:request(get, {URL503, []}, [], []), + httpc:request(get, {URL503, []}, [], []), ets:delete(unavailable), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1106,24 +1414,24 @@ http_userinfo(doc) -> http_userinfo(suite) -> []; http_userinfo(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), + 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", {ok, {{_,200,_}, [_ | _], _}} - = http:request(get, {URLAuth, []}, [], []), + = httpc:request(get, {URLAuth, []}, [], []), URLUnAuth = "http://alladin:foobar@localhost:" ++ integer_to_list(Port) ++ "/userinfo.html", {ok, {{_,401, _}, [_ | _], _}} = - http:request(get, {URLUnAuth, []}, [], []), + httpc:request(get, {URLUnAuth, []}, [], []), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1133,8 +1441,8 @@ http_cookie(doc) -> http_cookie(suite) -> []; http_cookie(Config) when is_list(Config) -> - ok = http:set_options([{cookies, enabled}, {ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URLStart = ?URL_START ++ integer_to_list(Port), @@ -1142,19 +1450,19 @@ http_cookie(Config) when is_list(Config) -> URLCookie = URLStart ++ "/cookie.html", {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URLCookie, []}, [], []), + = httpc:request(get, {URLCookie, []}, [], []), ets:new(cookie, [named_table, public, set]), ets:insert(cookie, {cookies, true}), {ok, {{_,200,_}, [_ | _], [_|_]}} - = http:request(get, {URLStart ++ "/", []}, [], []), + = httpc:request(get, {URLStart ++ "/", []}, [], []), ets:delete(cookie), - ok = http:set_options([{cookies, disabled}, {ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{cookies, disabled}]), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6************ + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. %%------------------------------------------------------------------------- @@ -1163,28 +1471,24 @@ proxy_options(doc) -> proxy_options(suite) -> []; proxy_options(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets, which + %% do not implement "options". case ?config(skip, Config) of undefined -> - Command = - fun() -> http:request(options, {?PROXY_URL, []}, [], []) end, - Verify = - fun({ok, {{_,200,_}, Headers, _}}) -> - case lists:keysearch("allow", 1, Headers) of - {value, {"allow", _}} -> - ok; - _ -> - tsf(http_options_request_failed) - end; - ({ok, {{_, 501, "Not Implemented"}, _Hdrs, _}}) -> - skip(options_not_implemented_in_server_501_ni); - ({ok, Unexpected}) -> - tsf({unexpected_success, Unexpected}); - (Unexpected) -> - tsf({unexpected_result, Unexpected}) - end, - expect(Command, Verify); + case httpc:request(options, {?PROXY_URL, []}, [], []) of + {ok, {{_,200,_}, Headers, _}} -> + case lists:keysearch("allow", 1, Headers) of + {value, {"allow", _}} -> + ok; + _ -> + test_server:fail(http_options_request_failed) + end; + Unexpected -> + test_server:fail({unexpected_result, Unexpected}) + end; Reason -> - skip(Reason) + {skip, Reason} end. @@ -1194,28 +1498,22 @@ proxy_head(doc) -> proxy_head(suite) -> []; proxy_head(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. case ?config(skip, Config) of undefined -> - Command = - fun() -> http:request(head, {?PROXY_URL, []}, [], []) end, - Verify = - fun({ok, {{_,200, _}, [_ | _], []}}) -> - ok; - ({ok, {{_,503,"Service Unavailable"}, [_ | _], []}}) -> - skip(head_not_implemented_in_server_503_su); - ({ok, Result}) -> - tsf({unexpected_success, Result}); - (Unexpected) -> - tsf({unexpected_result, Unexpected}) - end, - expect(Command, Verify); + case httpc:request(head, {?PROXY_URL, []}, [], []) of + {ok, {{_,200, _}, [_ | _], []}} -> + ok; + Unexpected -> + test_server:fail({unexpected_result, Unexpected}) + end; Reason -> - skip(Reason) + {skip, Reason} end. %%------------------------------------------------------------------------- - proxy_get(doc) -> ["Perform a GET request that goes through a proxy."]; proxy_get(suite) -> @@ -1223,7 +1521,7 @@ proxy_get(suite) -> proxy_get(Config) when is_list(Config) -> case ?config(skip, Config) of undefined -> - case http:request(get, {?PROXY_URL, []}, [], []) of + case httpc:request(get, {?PROXY_URL, []}, [], []) of {ok, {{_,200,_}, [_ | _], Body = [_ | _]}} -> inets_test_lib:check_body(Body); Unexpected -> @@ -1233,9 +1531,7 @@ proxy_get(Config) when is_list(Config) -> {skip, Reason} end. - %%------------------------------------------------------------------------- - proxy_emulate_lower_versions(doc) -> ["Perform requests as 0.9 and 1.0 clients."]; proxy_emulate_lower_versions(suite) -> @@ -1277,24 +1573,21 @@ proxy_emulate_lower_versions(Config) when is_list(Config) -> end. pelv_get(Version) -> - http:request(get, {?PROXY_URL, []}, [{version, Version}], []). - + httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []). %%------------------------------------------------------------------------- - proxy_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; proxy_trace(suite) -> []; proxy_trace(Config) when is_list(Config) -> %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} = - %% http:request(trace, {?PROXY_URL, []}, [], []), - skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due " - "to security reasons"). + %% httpc:request(trace, {?PROXY_URL, []}, [], []), + {skip, "HTTP TRACE is no longer allowed on the ?PROXY_URL server due " + "to security reasons"}. %%------------------------------------------------------------------------- - proxy_post(doc) -> ["Perform a POST request that goes through a proxy. Note the server" " will reject the request this is a test of the sending of the" @@ -1302,34 +1595,23 @@ proxy_post(doc) -> proxy_post(suite) -> []; proxy_post(Config) when is_list(Config) -> - %% After the switch to the erlang.org app, - %% the post will succeed, so we skip this - %% until we can find a new way of testing - %% this. - %% case ?config(skip, Config) of - %% undefined -> - %% Command = - %% fun() -> - %% http:request(post, {?PROXY_URL, [], - %% "text/plain", "foobar"}, [],[]) - %% end, - %% Verify = - %% fun({ok, {{_,405,_}, [_ | _], [_ | _]}}) -> - %% ok; - %% ({ok, Result}) -> - %% tsf({unexpected_success, Result}); - %% (Unexpected) -> - %% tsf({unexpected_result, Unexpected}) - %% end, - %% expect(Command, Verify); - %% Reason -> - %% skip(Reason) - %% end. - skip("temporary skip"). + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + case ?config(skip, Config) of + undefined -> + case httpc:request(post, {?PROXY_URL, [], + "text/plain", "foobar"}, [],[]) of + {ok, {{_,405,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({unexpected_result, Unexpected}) + end; + Reason -> + {skip, Reason} + end. %%------------------------------------------------------------------------- - proxy_put(doc) -> ["Perform a PUT request that goes through a proxy. Note the server" " will reject the request this is a test of the sending of the" @@ -1337,28 +1619,24 @@ proxy_put(doc) -> proxy_put(suite) -> []; proxy_put(Config) when is_list(Config) -> - %% After the switch to the erlang.org app, - %% the post will succeed, so we skip this - %% until we can find a new way of testing - %% this. - %% case ?config(skip, Config) of - %% undefined -> - %% case http:request(put, {"http://www.erlang.org/foobar.html", [], - %% "html", "<html> <body><h1> foo </h1>" - %% "<p>bar</p> </body></html>"}, [], []) of - %% {ok, {{_,405,_}, [_ | _], [_ | _]}} -> - %% ok; - %% Unexpected -> - %% test_server:fail({unexpected_result, Unexpected}) - %% end; - %% Reason -> - %% {skip, Reason} - %% end. - skip("temporary skip"). + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + case ?config(skip, Config) of + undefined -> + case httpc:request(put, {"http://www.erlang.org/foobar.html", [], + "html", "<html> <body><h1> foo </h1>" + "<p>bar</p> </body></html>"}, [], []) of + {ok, {{_,405,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({unexpected_result, Unexpected}) + end; + Reason -> + {skip, Reason} + end. %%------------------------------------------------------------------------- - proxy_delete(doc) -> ["Perform a DELETE request that goes through a proxy. Note the server" " will reject the request this is a test of the sending of the" @@ -1367,32 +1645,23 @@ proxy_delete(doc) -> proxy_delete(suite) -> []; proxy_delete(Config) when is_list(Config) -> - %% After the switch to the erlang.org app, - %% the post will succeed, so we skip this - %% until we can find a new way of testing - %% this. - %% case ?config(skip, Config) of - %% undefined -> - %% URL = ?PROXY_URL ++ "/foobar.html", - %% Command = fun() -> http:request(delete, {URL, []}, [], []) end, - %% Verify = - %% fun({ok, {{_,404,_}, [_ | _], [_ | _]}}) -> - %% ok; - %% ({ok, Result}) -> - %% tsf({unexpected_success, Result}); - %% (Unexpected) -> - %% tsf({unexpected_result, Unexpected}) - %% end, - %% expect(Command, Verify); - - %% Reason -> - %% skip(Reason) - %% end. - skip("temporary skip"). + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + case ?config(skip, Config) of + undefined -> + URL = ?PROXY_URL ++ "/foobar.html", + case httpc:request(delete, {URL, []}, [], []) of + {ok, {{_,404,_}, [_ | _], [_ | _]}} -> + ok; + Unexpected -> + test_server:fail({unexpected_result, Unexpected}) + end; + Reason -> + {skip, Reason} + end. %%------------------------------------------------------------------------- - proxy_headers(doc) -> ["Use as many request headers as possible"]; proxy_headers(suite) -> @@ -1401,7 +1670,7 @@ proxy_headers(Config) when is_list(Config) -> case ?config(skip, Config) of undefined -> {ok, {{_,200,_}, [_ | _], [_ | _]}} - = http:request(get, {?PROXY_URL, + = httpc:request(get, {?PROXY_URL, [ {"Accept", "text/*, text/html," @@ -1436,7 +1705,7 @@ proxy_auth(Config) when is_list(Config) -> %% atleast the code for sending the header does not crash! case ?config(skip, Config) of undefined -> - case http:request(get, {?PROXY_URL, []}, + case httpc:request(get, {?PROXY_URL, []}, [{proxy_auth, {"foo", "bar"}}], []) of {ok, {{_,200, _}, [_ | _], [_|_]}} -> ok; @@ -1456,7 +1725,7 @@ http_server_does_not_exist(suite) -> []; http_server_does_not_exist(Config) when is_list(Config) -> {error, _} = - http:request(get, {"http://localhost:" ++ + httpc:request(get, {"http://localhost:" ++ integer_to_list(?NOT_IN_USE_PORT) ++ "/", []},[], []), ok. @@ -1471,7 +1740,7 @@ page_does_not_exist(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/doesnotexist.html", {ok, {{_,404,_}, [_ | _], [_ | _]}} - = http:request(get, {URL, []}, [], []), + = httpc:request(get, {URL, []}, [], []), ok. @@ -1485,7 +1754,7 @@ proxy_page_does_not_exist(Config) when is_list(Config) -> undefined -> URL = ?PROXY_URL ++ "/doesnotexist.html", {ok, {{_,404,_}, [_ | _], [_ | _]}} = - http:request(get, {URL, []}, [], []), + httpc:request(get, {URL, []}, [], []), ok; Reason -> {skip, Reason} @@ -1499,27 +1768,13 @@ proxy_https_not_supported(doc) -> proxy_https_not_supported(suite) -> []; proxy_https_not_supported(Config) when is_list(Config) -> - Result = http:request(get, {"https://login.yahoo.com", []}, [], []), + Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []), case Result of - {error, Reason} -> - %% ok so far - case Reason of - {failed_connecting, Why} -> - %% ok, now check why - case Why of - https_through_proxy_is_not_currently_supported -> - ok; - _ -> - tsf({unexpected_why, Why}) - end; - _ -> - tsf({unexpected_reason, Reason}) - end; + {error, https_through_proxy_is_not_currently_supported} -> + ok; _ -> - tsf({unexpected_result, Result}) - end, - ok. - + tsf({unexpected_reason, Result}) + end. %%------------------------------------------------------------------------- @@ -1531,10 +1786,10 @@ http_stream(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, {{_,200,_}, [_ | _], Body}} = - http:request(get, {URL, []}, [], []), + httpc:request(get, {URL, []}, [], []), {ok, RequestId} = - http:request(get, {URL, []}, [], [{sync, false}, + httpc:request(get, {URL, []}, [], [{sync, false}, {stream, self}]), receive @@ -1559,9 +1814,9 @@ http_stream_once(Config) when is_list(Config) -> "~n Config: ~p", [Config]), p("http_stream_once -> set ipfamily to inet", []), - ok = http:set_options([{ipfamily, 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", []), @@ -1574,18 +1829,18 @@ http_stream_once(Config) when is_list(Config) -> p("http_stream_once -> stop dummy server", []), DummyServerPid ! stop, p("http_stream_once -> set ipfamily to inet6fb4", []), - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), p("http_stream_once -> done", []), ok. once(URL) -> p("once -> issue sync request for ~p", [URL]), {ok, {{_,200,_}, [_ | _], Body}} = - http:request(get, {URL, []}, [], []), + httpc:request(get, {URL, []}, [], []), p("once -> issue async (self stream) request for ~p", [URL]), {ok, RequestId} = - http:request(get, {URL, []}, [], [{sync, false}, + httpc:request(get, {URL, []}, [], [{sync, false}, {stream, {self, once}}]), p("once -> await stream_start reply for (async) request ~p", [RequestId]), @@ -1629,10 +1884,10 @@ proxy_stream(Config) when is_list(Config) -> case ?config(skip, Config) of undefined -> {ok, {{_,200,_}, [_ | _], Body}} = - http:request(get, {?PROXY_URL, []}, [], []), + httpc:request(get, {?PROXY_URL, []}, [], []), {ok, RequestId} = - http:request(get, {?PROXY_URL, []}, [], + httpc:request(get, {?PROXY_URL, []}, [], [{sync, false}, {stream, self}]), receive @@ -1697,26 +1952,79 @@ parse_url(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -ipv6(doc) -> - ["Test ipv6."]; -ipv6(suite) -> + +ipv6_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_ipcomm(doc) -> + ["Test ip_comm ipv6."]; +ipv6_ipcomm(suite) -> []; -ipv6(Config) when is_list(Config) -> - {ok, Hostname} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname), - ?config(ipv6_hosts, Config)) of - true -> - {DummyServerPid, Port} = dummy_server(self(), ipv6), - - URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++ +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,_}, [_ | _], [_|_]}} = - http: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. @@ -1730,11 +2038,11 @@ headers_as_is(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, {{_,200,_}, [_|_], [_|_]}} = - http:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, + httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]}, [], [{headers_as_is, true}]), {ok, {{_,400,_}, [_|_], [_|_]}} = - http:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]), + httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]), ok. @@ -1749,13 +2057,13 @@ options(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", {ok, {{_,200,_}, [_ | _], Bin}} - = http:request(get, {URL, []}, [{foo, bar}], + = httpc:request(get, {URL, []}, [{foo, bar}], %% Ignore unknown options [{body_format, binary}, {foo, bar}]), true = is_binary(Bin), {ok, {200, [_|_]}} - = http:request(get, {URL, []}, [{timeout, infinity}], + = httpc:request(get, {URL, []}, [{timeout, infinity}], [{full_result, false}]); _ -> {skip, "Failed to start local http-server"} @@ -1768,17 +2076,17 @@ http_invalid_http(doc) -> http_invalid_http(suite) -> []; http_invalid_http(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", {error, {could_not_parse_as_http, _} = Reason} = - http:request(get, {URL, []}, [], []), + httpc:request(get, {URL, []}, [], []), test_server:format("Parse error: ~p ~n", [Reason]), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1815,7 +2123,7 @@ empty_body_otp_6243(Config) when is_list(Config) -> Port = ?config(local_port, Config), URL = ?URL_START ++ integer_to_list(Port) ++ "/empty.html", {ok, {{_,200,_}, [_ | _], []}} = - http:request(get, {URL, []}, [{timeout, 500}], []). + httpc:request(get, {URL, []}, [{timeout, 500}], []). %%------------------------------------------------------------------------- @@ -1825,14 +2133,14 @@ transfer_encoding_otp_6807(doc) -> transfer_encoding_otp_6807(suite) -> []; transfer_encoding_otp_6807(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/capital_transfer_encoding.html", - {ok, {{_,200,_}, [_|_], [_ | _]}} = http:request(URL), + {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1858,13 +2166,13 @@ empty_response_header_otp_6830(doc) -> empty_response_header_otp_6830(suite) -> []; empty_response_header_otp_6830(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", - {ok, {{_,200,_}, [], [_ | _]}} = http:request(URL), + {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1875,13 +2183,13 @@ no_content_204_otp_6982(doc) -> no_content_204_otp_6982(suite) -> []; no_content_204_otp_6982(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", - {ok, {{_,204,_}, [], []}} = http:request(URL), + {ok, {{_,204,_}, [], []}} = httpc:request(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. @@ -1893,35 +2201,33 @@ missing_CR_otp_7304(doc) -> missing_CR_otp_7304(suite) -> []; missing_CR_otp_7304(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipfamily, inet}]), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", - {ok, {{_,200,_}, _, [_ | _]}} = http:request(URL), + {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. %%------------------------------------------------------------------------- -otp_7883(suite) -> - [otp_7883_1, otp_7883_2]. otp_7883_1(doc) -> ["OTP-7883-sync"]; otp_7883_1(suite) -> []; otp_7883_1(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), + 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} = http:request(URL), + {error, socket_closed_remotely} = httpc:request(URL), DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. otp_7883_2(doc) -> @@ -1929,18 +2235,18 @@ otp_7883_2(doc) -> otp_7883_2(suite) -> []; otp_7883_2(Config) when is_list(Config) -> - ok = http:set_options([{ipfamily, inet}]), + 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, Request = {URL, []}, HttpOptions = [], Options = [{sync, false}], - Profile = http:default_profile(), + Profile = httpc:default_profile(), {ok, RequestId} = - http:request(Method, Request, HttpOptions, Options, Profile), + httpc:request(Method, Request, HttpOptions, Options, Profile), ok = receive {http, {RequestId, {error, socket_closed_remotely}}} -> @@ -1948,14 +2254,12 @@ otp_7883_2(Config) when is_list(Config) -> end, DummyServerPid ! stop, - ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 ************* + ok = httpc:set_options([{ipfamily, inet6fb4}]), ok. %%------------------------------------------------------------------------- -otp_8154(suite) -> - [otp_8154_1]. otp_8154_1(doc) -> ["OTP-8154"]; @@ -2019,7 +2323,7 @@ run_clients(NumClients, ServerPort, SeqNumServer) -> fun() -> io:format("[~w] client started - " "issue request~n", [Id]), - case http:request(Url) of + case httpc:request(Url) of {ok, {{_,200,_}, _, Resp}} -> io:format("[~w] 200 response: " "~p~n", [Id, Resp]), @@ -2235,12 +2539,6 @@ f(F, A) -> lists:flatten(io_lib:format(F,A)). %%------------------------------------------------------------------------- -otp_8106(suite) -> - [ - otp_8106_pid, - otp_8106_fun, - otp_8106_mfa - ]. otp_8106_pid(doc) -> @@ -2280,7 +2578,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> {skip, "Failed to start local http-server"} - end. + end. otp_8106_mfa(doc) -> @@ -2407,7 +2705,7 @@ otp_8352(Config) when is_list(Config) -> ConnOptions = [{max_sessions, MaxSessions}, {max_keep_alive_length, MaxKeepAlive}, {keep_alive_timeout, KeepAliveTimeout}], - http:set_options(ConnOptions), + httpc:set_options(ConnOptions), Method = get, Port = ?config(local_port, Config), @@ -2419,9 +2717,9 @@ otp_8352(Config) when is_list(Config) -> Options1 = [{socket_opts, [{tos, 87}, {recbuf, 16#FFFF}, {sndbuf, 16#FFFF}]}], - case http:request(Method, Request, HttpOptions1, Options1) of + case httpc:request(Method, Request, HttpOptions1, Options1) of {ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} -> - %% equivaliant to http:request(get, {URL, []}, [], []), + %% equivaliant to httpc:request(get, {URL, []}, [], []), inets_test_lib:check_body(ReplyBody1); {ok, UnexpectedReply1} -> tsf({unexpected_reply, UnexpectedReply1}); @@ -2435,9 +2733,9 @@ otp_8352(Config) when is_list(Config) -> Options2 = [{socket_opts, [{tos, 84}, {recbuf, 32#1FFFF}, {sndbuf, 32#1FFFF}]}], - case http:request(Method, Request, HttpOptions2, Options2) of + case httpc:request(Method, Request, HttpOptions2, Options2) of {ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} -> - %% equivaliant to http:request(get, {URL, []}, [], []), + %% equivaliant to httpc:request(get, {URL, []}, [], []), inets_test_lib:check_body(ReplyBody2); {ok, UnexpectedReply2} -> tsf({unexpected_reply, UnexpectedReply2}); @@ -2459,13 +2757,13 @@ otp_8371(doc) -> otp_8371(suite) -> []; otp_8371(Config) when is_list(Config) -> - ok = http:set_options([{ipv6, disabled}]), % also test the old option - {DummyServerPid, Port} = dummy_server(self(), ipv4), + ok = httpc:set_options([{ipv6, disabled}]), % also test the old option + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/ensure_host_header_with_port.html", - case http:request(get, {URL, []}, [], []) of + case httpc:request(get, {URL, []}, [], []) of {ok, Result} -> case Result of {{_, 200, _}, _Headers, Body} -> @@ -2489,7 +2787,7 @@ otp_8371(Config) when is_list(Config) -> end, DummyServerPid ! stop, - ok = http:set_options([{ipv6, enabled}]), + ok = httpc:set_options([{ipv6, enabled}]), ok. @@ -2506,7 +2804,7 @@ otp_8739(Config) when is_list(Config) -> Request = {URL, []}, HttpOptions = [{connect_timeout, 500}, {timeout, 1}], Options = [{sync, true}], - case http:request(Method, Request, HttpOptions, Options) of + case httpc:request(Method, Request, HttpOptions, Options) of {error, timeout} -> %% And now we check the size of the handler db Info = httpc:info(), @@ -2563,7 +2861,31 @@ otp_8739_dummy_server_main(_Parent, ListenSocket) -> exit(Error) end. - +%%------------------------------------------------------------------------- + +initial_server_connect(doc) -> + ["If this test cases times out the init of httpc_handler process is" + "blocking the manager/client process (implementation dependent which) but nither" + "should be blocked."]; +initial_server_connect(suite) -> + []; +initial_server_connect(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + ok = httpc:set_options([{ipfamily, inet}]), + + CertFile = filename:join(DataDir, "ssl_server_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + + {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions), + + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html", + + httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]), + + [{session_cookies,[]}] = httpc:which_cookies(), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]). %%-------------------------------------------------------------------- %% Internal functions @@ -2661,7 +2983,7 @@ receive_streamed_body(RequestId, Body) -> end. receive_streamed_body(RequestId, Body, Pid) -> - http:stream_next(Pid), + httpc:stream_next(Pid), test_server:format("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), receive {http, {RequestId, stream, BinBodyPart}} -> @@ -2674,75 +2996,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(Caller, IpV) -> - Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]), +dummy_server(SocketType, IpV, Extra) -> + dummy_server(self(), SocketType, IpV, Extra). + +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" @@ -2751,7 +3177,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; <<>> -> @@ -2759,7 +3185,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 -> " @@ -2767,7 +3194,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" @@ -2924,16 +3351,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" ++ @@ -2952,20 +3379,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"; @@ -2988,9 +3415,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. @@ -3042,46 +3469,29 @@ content_length([_Head | Tail]) -> content_length(Tail). provocate_not_modified_bug(Url) -> - tsp("provocate_not_modified_bug -> entry with" - "~n Url: ~p", [Url]), - Timeout = 15000, %% 15s should be plenty - case http:request(get, {Url, []}, [{timeout, Timeout}], []) of - {ok, {{_, 200, _}, ReplyHeaders, _Body}} -> - tsp("provocate_not_modified_bug -> received 200 reply" - "~n ReplyHeaders: ~p", [ReplyHeaders]), - Etag = pick_header(ReplyHeaders, "ETag"), - Last = pick_header(ReplyHeaders, "last-modified"), - case http:request(get, {Url, [{"If-None-Match", Etag}, - {"If-Modified-Since", Last}]}, - [{timeout, 15000}], - []) of - {ok, {{_, 304, _}, _, _}} -> %% The expected reply - tsp("provocate_not_modified_bug -> unchanged", []), - page_unchanged; - {ok, {{_, 200, _}, _, _}} -> - %% If the page has changed since the - %% last request we retry to - %% trigger the bug - tsp("provocate_not_modified_bug -> changed", []), - provocate_not_modified_bug(Url); - {error, timeout} -> - %% Not what we expected. Tcpdump can be used to - %% verify that we receive the complete http-reply - %% but still time out. - tsp("provocate_not_modified_bug -> timeout", []), - incorrect_result - end; - {ok, UnexpectedReply, ReplyHeaders, _} -> - tsp("provocate_not_modified_bug -> received unexpected reply" - "~n UnexpectedReply: ~p" - "~n ReplyHeaders: ~p", [UnexpectedReply, ReplyHeaders]), - unexpected_reply; - {error, Reason} -> - tsp("provocate_not_modified_bug -> unexpected failure" - "~n Reason: ~p", [Reason]), - unexpected_error + {ok, {{_, 200, _}, ReplyHeaders, _Body}} = + httpc:request(get, {Url, []}, [{timeout, Timeout}], []), + Etag = pick_header(ReplyHeaders, "ETag"), + Last = pick_header(ReplyHeaders, "last-modified"), + + case httpc:request(get, {Url, [{"If-None-Match", Etag}, + {"If-Modified-Since", Last}]}, + [{timeout, 15000}], + []) of + {ok, {{_, 304, _}, _, _}} -> %% The expected reply + page_unchanged; + {ok, {{_, 200, _}, _, _}} -> + %% If the page has changed since the + %% last request we retry to + %% trigger the bug + provocate_not_modified_bug(Url); + {error, timeout} -> + %% Not what we expected. Tcpdump can be used to + %% verify that we receive the complete http-reply + %% but still time out. + incorrect_result end. pick_header(Headers, Name) -> @@ -3093,15 +3503,9 @@ pick_header(Headers, Name) -> Val end. - not_implemented_yet() -> exit(not_implemented_yet). - -expect(Command, Verify) -> - Result = (catch Command()), - Verify(Result). - p(F) -> p(F, []). @@ -3111,24 +3515,58 @@ p(F, A) -> tsp(F) -> tsp(F, []). tsp(F, A) -> - Timestamp = formated_timestamp(), - test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n", [Timestamp, self(), ?MODULE | A]). - -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). + test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). -skip(Reason) -> - {skip, Reason}. + +dummy_ssl_server_hang(Caller, IpV, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + ssl:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt); + ipv6 -> + ssl:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt) + end, + {ok, {_,Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + dummy_ssl_server_hang_loop(AcceptSocket). + +dummy_ssl_server_hang_loop(_) -> + %% Do not do ssl:ssl_accept as we + %% want to time out the underlying gen_tcp:connect + receive + 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/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl index ad5df656c6..866fa9d525 100644 --- a/lib/inets/test/httpc_cookie_SUITE.erl +++ b/lib/inets/test/httpc_cookie_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -19,11 +19,11 @@ %% -module(httpc_cookie_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). %% Test server specific exports --export([all/1, init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. -export([session_cookies_only/1, netscape_cookies/1, @@ -116,23 +116,38 @@ end_per_testcase(Case, Config) -> %% Name of a test case. %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- -all(doc) -> - ["Describe the main purpose of this suite"]; +suite() -> [{ct_hooks,[ts_install_cth]}]. -all(suite) -> +all() -> [ session_cookies_only, netscape_cookies, cookie_cancel, cookie_expires, persistent_cookie, - domain_cookie, - secure_cookie, + domain_cookie, + secure_cookie, update_cookie, - update_cookie_session, + update_cookie_session, cookie_attributes ]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %% Test cases starts here. %%-------------------------------------------------------------------- session_cookies_only(doc) -> @@ -298,38 +313,93 @@ secure_cookie(Config) when is_list(Config) -> tsp("secure_cookie -> done"), ok. +expect_cookie_header(No, ExpectedCookie) -> + case httpc:cookie_header(?URL) of + {"cookie", ExpectedCookie} -> + ok; + {"cookie", BadCookie} -> + io:format("Bad Cookie ~w: " + "~n Expected: ~s" + "~n Received: ~s" + "~n", [No, ExpectedCookie, BadCookie]), + exit({bad_cookie_header, No, ExpectedCookie, BadCookie}) + end. + +print_cookies(Pre) -> + io:format("~s: ", [Pre]), + print_cookies2(httpc:which_cookies()). + +print_cookies2([]) -> + ok; +print_cookies2([{cookies, Cookies}|Rest]) -> + print_cookies3("Cookies", Cookies), + print_cookies2(Rest); +print_cookies2([{session_cookies, Cookies}|Rest]) -> + print_cookies3("Session Cookies", Cookies), + print_cookies2(Rest); +print_cookies2([_|Rest]) -> + print_cookies2(Rest). + +print_cookies3(Header, []) -> + io:format(" ~s: []", [Header]); +print_cookies3(Header, Cookies) -> + io:format(" ~s: ", [Header]), + Prefix = " ", + PrintCookie = + fun(Cookie) -> + io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)]) + end, + lists:foreach(PrintCookie, Cookies). + update_cookie(doc)-> - ["Test that a cookie can be updated."]; + ["Test that a (plain) cookie can be updated."]; update_cookie(suite) -> []; -update_cookie(Config) when is_list(Config)-> - SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;" - "max-age=6500"}, - {"set-cookie", "test_cookie2=true; path=/;" - "max-age=6500"}], - http:verify_cookies(SetCookieHeaders, ?URL), - {"cookie", "$Version=0; test_cookie2=true; $Path=/; " - "test_cookie=true; $Path=/"} = http:cookie_header(?URL), - NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; " - "path=/;max-age=6500"}], - http:verify_cookies(NewSetCookieHeaders, ?URL), - {"cookie", "$Version=0; test_cookie2=true; $Path=/; " - "test_cookie=false; $Path=/"} = http:cookie_header(?URL). - +update_cookie(Config) when is_list(Config) -> + print_cookies("Cookies before store"), + + SetCookieHeaders = + [{"set-cookie", "test_cookie=true; path=/; max-age=6500"}, + {"set-cookie", "test_cookie2=true; path=/; max-age=6500"}], + httpc:store_cookies(SetCookieHeaders, ?URL), + print_cookies("Cookies after first store"), + ExpectCookie1 = + "$Version=0; " + "test_cookie=true; $Path=/; " + "test_cookie2=true; $Path=/", + expect_cookie_header(1, ExpectCookie1), + + NewSetCookieHeaders = + [{"set-cookie", "test_cookie=false; path=/; max-age=6500"}], + httpc:store_cookies(NewSetCookieHeaders, ?URL), + print_cookies("Cookies after second store"), + ExpectCookie2 = + "$Version=0; " + "test_cookie2=true; $Path=/; " + "test_cookie=false; $Path=/", + expect_cookie_header(2, ExpectCookie2). + update_cookie_session(doc)-> - ["Test that a cookie can be updated."]; + ["Test that a session cookie can be updated."]; update_cookie_session(suite) -> []; update_cookie_session(Config) when is_list(Config)-> + print_cookies("Cookies before store"), + SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/"}, {"set-cookie", "test_cookie2=true; path=/"}], - http:verify_cookies(SetCookieHeaders, ?URL), - {"cookie", "$Version=0; test_cookie2=true; $Path=/; " - "test_cookie=true; $Path=/"} = http:cookie_header(?URL), + httpc:store_cookies(SetCookieHeaders, ?URL), + print_cookies("Cookies after first store"), + ExpectedCookie1 = + "$Version=0; test_cookie=true; $Path=/; test_cookie2=true; $Path=/", + expect_cookie_header(1, ExpectedCookie1), + NewSetCookieHeaders = [{"set-cookie", "test_cookie=false; path=/"}], - http:verify_cookies(NewSetCookieHeaders, ?URL), - {"cookie", "$Version=0; test_cookie2=true; $Path=/; " - "test_cookie=false; $Path=/"} = http:cookie_header(?URL). + httpc:store_cookies(NewSetCookieHeaders, ?URL), + print_cookies("Cookies after second store"), + ExpectedCookie2 = + "$Version=0; test_cookie2=true; $Path=/; test_cookie=false; $Path=/", + expect_cookie_header(2, ExpectedCookie2). cookie_attributes(doc) -> diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 3662a517aa..1cee9d17f1 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -20,56 +20,177 @@ -module(httpd_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include("test_server_line.hrl"). -include("inets_test_lib.hrl"). -include_lib("kernel/include/file.hrl"). %% Test server specific exports --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). -%% Test cases must be exported. --export([ip/1, ssl/1, http_1_1_ip/1, http_1_0_ip/1, http_0_9_ip/1, - ipv6/1, tickets/1]). - %% Core Server tests --export([ip_mod_alias/1, ip_mod_actions/1, ip_mod_security/1, ip_mod_auth/1, - ip_mod_auth_api/1, ip_mod_auth_mnesia_api/1, - ip_mod_htaccess/1, ip_mod_cgi/1, ip_mod_esi/1, - ip_mod_get/1, ip_mod_head/1, ip_mod_all/1, ip_load_light/1, - ip_load_medium/1, ip_load_heavy/1, ip_dos_hostname/1, - ip_time_test/1, ip_block_disturbing_idle/1, - ip_block_non_disturbing_idle/1, ip_block_503/1, - ip_block_disturbing_active/1, ip_block_non_disturbing_active/1, +-export([ + ip_mod_alias/1, + ip_mod_actions/1, + ip_mod_security/1, + ip_mod_auth/1, + ip_mod_auth_api/1, + ip_mod_auth_mnesia_api/1, + ip_mod_htaccess/1, + ip_mod_cgi/1, + ip_mod_esi/1, + ip_mod_get/1, + ip_mod_head/1, + ip_mod_all/1, + ip_load_light/1, + ip_load_medium/1, + ip_load_heavy/1, + ip_dos_hostname/1, + ip_time_test/1, + ip_block_disturbing_idle/1, + ip_block_non_disturbing_idle/1, + ip_block_503/1, + ip_block_disturbing_active/1, + ip_block_non_disturbing_active/1, ip_block_disturbing_active_timeout_not_released/1, ip_block_disturbing_active_timeout_released/1, ip_block_non_disturbing_active_timeout_not_released/1, ip_block_non_disturbing_active_timeout_released/1, ip_block_disturbing_blocker_dies/1, ip_block_non_disturbing_blocker_dies/1, - ip_restart_no_block/1, ip_restart_disturbing_block/1, + ip_restart_no_block/1, + ip_restart_disturbing_block/1, ip_restart_non_disturbing_block/1 ]). --export([ssl_mod_alias/1, ssl_mod_actions/1, ssl_mod_security/1, - ssl_mod_auth/1, ssl_mod_auth_api/1, - ssl_mod_auth_mnesia_api/1, ssl_mod_htaccess/1, - ssl_mod_cgi/1, ssl_mod_esi/1, ssl_mod_get/1, ssl_mod_head/1, - ssl_mod_all/1, ssl_load_light/1, ssl_load_medium/1, - ssl_load_heavy/1, ssl_dos_hostname/1, ssl_time_test/1, - ssl_restart_no_block/1, ssl_restart_disturbing_block/1, - ssl_restart_non_disturbing_block/1, ssl_block_disturbing_idle/1, - ssl_block_non_disturbing_idle/1, ssl_block_503/1, - ssl_block_disturbing_active/1, ssl_block_non_disturbing_active/1, - ssl_block_disturbing_active_timeout_not_released/1, - ssl_block_disturbing_active_timeout_released/1, - ssl_block_non_disturbing_active_timeout_not_released/1, - ssl_block_non_disturbing_active_timeout_released/1, - ssl_block_disturbing_blocker_dies/1, - ssl_block_non_disturbing_blocker_dies/1]). +-export([ + pssl_mod_alias/1, + ossl_mod_alias/1, + essl_mod_alias/1, + + pssl_mod_actions/1, + ossl_mod_actions/1, + essl_mod_actions/1, + + pssl_mod_security/1, + ossl_mod_security/1, + essl_mod_security/1, + + pssl_mod_auth/1, + ossl_mod_auth/1, + essl_mod_auth/1, + + pssl_mod_auth_api/1, + ossl_mod_auth_api/1, + essl_mod_auth_api/1, + + pssl_mod_auth_mnesia_api/1, + ossl_mod_auth_mnesia_api/1, + essl_mod_auth_mnesia_api/1, + + pssl_mod_htaccess/1, + ossl_mod_htaccess/1, + essl_mod_htaccess/1, + + pssl_mod_cgi/1, + ossl_mod_cgi/1, + essl_mod_cgi/1, + + pssl_mod_esi/1, + ossl_mod_esi/1, + essl_mod_esi/1, + + pssl_mod_get/1, + ossl_mod_get/1, + essl_mod_get/1, + + pssl_mod_head/1, + ossl_mod_head/1, + essl_mod_head/1, + + pssl_mod_all/1, + ossl_mod_all/1, + essl_mod_all/1, + + pssl_load_light/1, + ossl_load_light/1, + essl_load_light/1, + + pssl_load_medium/1, + ossl_load_medium/1, + essl_load_medium/1, + + pssl_load_heavy/1, + ossl_load_heavy/1, + essl_load_heavy/1, + + pssl_dos_hostname/1, + ossl_dos_hostname/1, + essl_dos_hostname/1, + + pssl_time_test/1, + ossl_time_test/1, + essl_time_test/1, + + pssl_restart_no_block/1, + ossl_restart_no_block/1, + essl_restart_no_block/1, + + pssl_restart_disturbing_block/1, + ossl_restart_disturbing_block/1, + essl_restart_disturbing_block/1, + + pssl_restart_non_disturbing_block/1, + ossl_restart_non_disturbing_block/1, + essl_restart_non_disturbing_block/1, + + pssl_block_disturbing_idle/1, + ossl_block_disturbing_idle/1, + essl_block_disturbing_idle/1, + + pssl_block_non_disturbing_idle/1, + ossl_block_non_disturbing_idle/1, + essl_block_non_disturbing_idle/1, + + pssl_block_503/1, + ossl_block_503/1, + essl_block_503/1, + + pssl_block_disturbing_active/1, + ossl_block_disturbing_active/1, + essl_block_disturbing_active/1, + + pssl_block_non_disturbing_active/1, + ossl_block_non_disturbing_active/1, + essl_block_non_disturbing_active/1, + + pssl_block_disturbing_active_timeout_not_released/1, + ossl_block_disturbing_active_timeout_not_released/1, + essl_block_disturbing_active_timeout_not_released/1, + + pssl_block_disturbing_active_timeout_released/1, + ossl_block_disturbing_active_timeout_released/1, + essl_block_disturbing_active_timeout_released/1, + + pssl_block_non_disturbing_active_timeout_not_released/1, + ossl_block_non_disturbing_active_timeout_not_released/1, + essl_block_non_disturbing_active_timeout_not_released/1, + + pssl_block_non_disturbing_active_timeout_released/1, + ossl_block_non_disturbing_active_timeout_released/1, + essl_block_non_disturbing_active_timeout_released/1, + + pssl_block_disturbing_blocker_dies/1, + ossl_block_disturbing_blocker_dies/1, + essl_block_disturbing_blocker_dies/1, + + pssl_block_non_disturbing_blocker_dies/1, + ossl_block_non_disturbing_blocker_dies/1, + essl_block_non_disturbing_blocker_dies/1 + ]). %%% HTTP 1.1 tests -export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1, @@ -86,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]). @@ -103,8 +227,8 @@ %% Seconds before successful auths timeout. -define(AUTH_TIMEOUT,5). --record(httpd_user, {user_name, password, user_data}). --record(httpd_group,{group_name, userlist}). +-record(httpd_user, {user_name, password, user_data}). +-record(httpd_group, {group_name, userlist}). %%-------------------------------------------------------------------- @@ -117,19 +241,116 @@ %% Description: Returns documentation/test cases in this test suite %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -all(doc) -> - ["Test the http server in the intes application."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [ - ip, - ssl, - http_1_1_ip, - http_1_0_ip, - http_0_9_ip, - %% ipv6, - 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, [], + [ip_mod_alias, ip_mod_actions, ip_mod_security, + ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api, + ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get, + ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium, + ip_load_heavy, ip_dos_hostname, ip_time_test, + ip_restart_no_block, ip_restart_disturbing_block, + ip_restart_non_disturbing_block, + ip_block_disturbing_idle, ip_block_non_disturbing_idle, + ip_block_503, ip_block_disturbing_active, + ip_block_non_disturbing_active, + ip_block_disturbing_active_timeout_not_released, + ip_block_disturbing_active_timeout_released, + ip_block_non_disturbing_active_timeout_not_released, + ip_block_non_disturbing_active_timeout_released, + ip_block_disturbing_blocker_dies, + ip_block_non_disturbing_blocker_dies]}, + {ssl, [], + [{group, pssl}, {group, ossl}, {group, essl}]}, + {pssl, [], + [pssl_mod_alias, pssl_mod_actions, pssl_mod_security, + pssl_mod_auth, pssl_mod_auth_api, + pssl_mod_auth_mnesia_api, pssl_mod_htaccess, + pssl_mod_cgi, pssl_mod_esi, pssl_mod_get, pssl_mod_head, + pssl_mod_all, pssl_load_light, pssl_load_medium, + pssl_load_heavy, pssl_dos_hostname, pssl_time_test, + pssl_restart_no_block, pssl_restart_disturbing_block, + pssl_restart_non_disturbing_block, + pssl_block_disturbing_idle, + pssl_block_non_disturbing_idle, pssl_block_503, + pssl_block_disturbing_active, + pssl_block_non_disturbing_active, + pssl_block_disturbing_active_timeout_not_released, + pssl_block_disturbing_active_timeout_released, + pssl_block_non_disturbing_active_timeout_not_released, + pssl_block_non_disturbing_active_timeout_released, + pssl_block_disturbing_blocker_dies, + pssl_block_non_disturbing_blocker_dies]}, + {ossl, [], + [ossl_mod_alias, ossl_mod_actions, ossl_mod_security, + ossl_mod_auth, ossl_mod_auth_api, + ossl_mod_auth_mnesia_api, ossl_mod_htaccess, + ossl_mod_cgi, ossl_mod_esi, ossl_mod_get, ossl_mod_head, + ossl_mod_all, ossl_load_light, ossl_load_medium, + ossl_load_heavy, ossl_dos_hostname, ossl_time_test, + ossl_restart_no_block, ossl_restart_disturbing_block, + ossl_restart_non_disturbing_block, + ossl_block_disturbing_idle, + ossl_block_non_disturbing_idle, ossl_block_503, + ossl_block_disturbing_active, + ossl_block_non_disturbing_active, + ossl_block_disturbing_active_timeout_not_released, + ossl_block_disturbing_active_timeout_released, + ossl_block_non_disturbing_active_timeout_not_released, + ossl_block_non_disturbing_active_timeout_released, + ossl_block_disturbing_blocker_dies, + ossl_block_non_disturbing_blocker_dies]}, + {essl, [], + [essl_mod_alias, essl_mod_actions, essl_mod_security, + essl_mod_auth, essl_mod_auth_api, + essl_mod_auth_mnesia_api, essl_mod_htaccess, + essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head, + essl_mod_all, essl_load_light, essl_load_medium, + essl_load_heavy, essl_dos_hostname, essl_time_test, + essl_restart_no_block, essl_restart_disturbing_block, + essl_restart_non_disturbing_block, + essl_block_disturbing_idle, + essl_block_non_disturbing_idle, essl_block_503, + essl_block_disturbing_active, + essl_block_non_disturbing_active, + essl_block_disturbing_active_timeout_not_released, + essl_block_disturbing_active_timeout_released, + essl_block_non_disturbing_active_timeout_not_released, + essl_block_non_disturbing_active_timeout_released, + essl_block_disturbing_blocker_dies, + essl_block_non_disturbing_blocker_dies]}, + {http_1_1_ip, [], + [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test, + ip_http_trace, ip_http1_1_head, + ip_mod_cgi_chunked_encoding_test]}, + {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_ipcomm, ipv6_address_ipcomm, + ipv6_hostname_essl, ipv6_address_essl]}, + {tickets, [], + [ticket_5775, ticket_5865, ticket_5913, ticket_6003, + ticket_7304]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -197,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), @@ -210,8 +431,8 @@ init_per_testcase2(Case, Config) -> "~n DataDir: ~p" "~n", [?MODULE, Case, SuiteTopDir, DataDir]), - TcTopDir = filename:join(SuiteTopDir, Case), - ?line ok = file:make_dir(TcTopDir), + TcTopDir = filename:join(SuiteTopDir, Case), + ?line ok = file:make_dir(TcTopDir), io:format(user, "~w:init_per_testcase2(~w) -> " "~n TcTopDir: ~p" @@ -260,17 +481,29 @@ 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", [?MODULE, Case]), - create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig], - normal_acess, SslNormal), - create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig], - mod_htaccess, SslHtacess), + SocketType = + case atom_to_list(Case) of + [X, $s, $s, $l | _] -> + case X of + $p -> ssl; + $o -> ossl; + $e -> essl + end; + _ -> + ssl + end, + + create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], + normal_access, SslNormal), + create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], + 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>) @@ -278,30 +511,66 @@ 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) -> io:format(user, "~w:init_per_testcase3(~w) -> entry with" "~n Config: ~p", [?MODULE, Case, Config]), + +%% %% Create a new fresh node to be used by the server in this test-case + +%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"), +%% Node = inets_test_lib:start_node(NodeName), + %% Clean up (we do not want this clean up in end_per_testcase - %% if init_per_testcase crases for some testcase it will + %% if init_per_testcase crashes for some testcase it will %% have contaminated the environment and there will be no clean up.) %% This init can take a few different paths so that one crashes %% does not mean that all invocations will. @@ -310,15 +579,26 @@ init_per_testcase3(Case, Config) -> application:stop(inets), application:stop(ssl), cleanup_mnesia(), - - %% TraceLevel = max, - TraceLevel = 70, - TraceDest = io, - inets:enable_trace(TraceLevel, TraceDest), + %% Set trace + case lists:reverse(atom_to_list(Case)) of + "tset_emit" ++ _Rest -> % test-cases ending with time_test + io:format(user, "~w:init_per_testcase3(~w) -> disabling trace", + [?MODULE, Case]), + inets:disable_trace(); + _ -> + io:format(user, "~w:init_per_testcase3(~w) -> enabling trace", + [?MODULE, Case]), + %% TraceLevel = 70, + TraceLevel = max, + TraceDest = io, + inets:enable_trace(TraceLevel, TraceDest, httpd) + end, + %% Start initialization io:format(user, "~w:init_per_testcase3(~w) -> start init", [?MODULE, Case]), + Dog = test_server:timetrap(inets_test_lib:minutes(10)), NewConfig = lists:keydelete(watchdog, 1, Config), @@ -329,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( @@ -351,39 +631,49 @@ init_per_testcase3(Case, Config) -> filename:join(TcTopDir, integer_to_list(?IP_PORT) ++ ".conf")}]), Rest; - "ssl_mod_htaccess" -> + + [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] -> + SslTag = + case X of + $p -> ssl; % plain + $o -> ossl; % OpenSSL based ssl + $e -> essl % Erlang based ssl + end, case inets_test_lib:start_http_server_ssl( filename:join(TcTopDir, integer_to_list(?SSL_PORT) ++ - "htacess.conf")) of + "htaccess.conf"), SslTag) of ok -> "mod_htaccess"; Other -> - error_logger:info_report("Other: ~p~n", [Other]), + error_logger:info_msg("Other: ~p~n", [Other]), {skip, "SSL does not seem to be supported"} end; - "ssl_" ++ Rest -> + [X, $s, $s, $l, $_ | Rest] -> + SslTag = + case X of + $p -> ssl; + $o -> ossl; + $e -> essl + end, case inets_test_lib:start_http_server_ssl( filename:join(TcTopDir, integer_to_list(?SSL_PORT) ++ - ".conf")) of + ".conf"), SslTag) of ok -> Rest; Other -> - error_logger:info_report("Other: ~p~n", [Other]), + error_logger:info_msg("Other: ~p~n", [Other]), {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, @@ -397,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), @@ -431,6 +721,7 @@ end_per_testcase2(Case, Config) -> application:unset_env(inets, services), application:stop(inets), application:stop(ssl), + application:stop(crypto), % used by the new ssl (essl test cases) cleanup_mnesia(), io:format(user, "~w:end_per_testcase2(~w) -> done~n", [?MODULE, Case]), @@ -440,134 +731,36 @@ end_per_testcase2(Case, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -ip(doc) -> - ["HTTP tests using TCP/IP"]; -ip(suite) -> - [ - ip_mod_alias, - ip_mod_actions, - ip_mod_security, - ip_mod_auth, - ip_mod_auth_api, - ip_mod_auth_mnesia_api, - ip_mod_htaccess, - ip_mod_cgi, - ip_mod_esi, - ip_mod_get, - ip_mod_head, - ip_mod_all, - ip_load_light, - ip_load_medium, - ip_load_heavy, - ip_dos_hostname, - ip_time_test, - ip_block_disturbing_idle, - ip_block_non_disturbing_idle, - ip_block_503, - ip_block_disturbing_active, - ip_block_non_disturbing_active, - ip_block_disturbing_active_timeout_not_released, - ip_block_disturbing_active_timeout_released, - ip_block_non_disturbing_active_timeout_not_released, - ip_block_non_disturbing_active_timeout_released, - ip_block_disturbing_blocker_dies, - ip_block_non_disturbing_blocker_dies, - ip_restart_no_block, - ip_restart_disturbing_block, - ip_restart_non_disturbing_block - ]. %%------------------------------------------------------------------------- -ssl(doc) -> - ["HTTP test using SSL"]; -ssl(suite) -> - [ - ssl_mod_alias, - ssl_mod_actions, - ssl_mod_security, - ssl_mod_auth, - ssl_mod_auth_api, - ssl_mod_auth_mnesia_api, - ssl_mod_htaccess, - ssl_mod_cgi, - ssl_mod_esi, - ssl_mod_get, - ssl_mod_head, - ssl_mod_all, - ssl_load_light, - ssl_load_medium, - ssl_load_heavy, - ssl_dos_hostname, - ssl_time_test, - ssl_restart_no_block, - ssl_restart_disturbing_block, - ssl_restart_non_disturbing_block, - ssl_block_disturbing_idle, - ssl_block_non_disturbing_idle, - ssl_block_503, - ssl_block_disturbing_active, - ssl_block_non_disturbing_active, - ssl_block_disturbing_active_timeout_not_released, - ssl_block_disturbing_active_timeout_released, - ssl_block_non_disturbing_active_timeout_not_released, - ssl_block_non_disturbing_active_timeout_released, - ssl_block_disturbing_blocker_dies, - ssl_block_non_disturbing_blocker_dies - ]. + + + + + %%------------------------------------------------------------------------- http_1_1_ip(doc) -> ["HTTP/1.1"]; http_1_1_ip(suite) -> [ - %% ip_host, - %% ip_chunked, - %% ip_expect, - %% ip_range, - ip_if_test%% , - %% ip_http_trace, - %% ip_http1_1_head, - %% ip_mod_cgi_chunked_encoding_test + ip_host, + ip_chunked, + ip_expect, + ip_range, + ip_if_test, + ip_http_trace, + ip_http1_1_head, + ip_mod_cgi_chunked_encoding_test ]. %%------------------------------------------------------------------------- -http_1_0_ip(doc) -> - ["HTTP/1.0"]; -http_1_0_ip(suite) -> - [ - ip_head_1_0, - ip_get_1_0, - ip_post_1_0 - ]. %%------------------------------------------------------------------------- -http_0_9_ip(doc) -> - ["HTTP/0.9"]; -http_0_9_ip(suite) -> - [ - ip_get_0_9 - ]. %%------------------------------------------------------------------------- -ipv6(doc) -> - ["Tests ipv6 functionality."]; -ipv6(suite) -> - [ - ipv6_hostname, - ipv6_address - ]. %%------------------------------------------------------------------------- -tickets(doc) -> - ["Test cases for reported bugs."]; -tickets(suite) -> - [ - ticket_5775, - ticket_5865, - ticket_5913, - ticket_6003, - ticket_7304 - ]. %%------------------------------------------------------------------------- ip_mod_alias(doc) -> @@ -721,6 +914,8 @@ ip_load_heavy(Config) when is_list(Config) -> ?config(node, Config), get_nof_clients(ip_comm, heavy)), ok. + + %%------------------------------------------------------------------------- ip_dos_hostname(doc) -> ["Denial Of Service (DOS) attack test case"]; @@ -730,6 +925,8 @@ ip_dos_hostname(Config) when is_list(Config) -> dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config), ?config(node, Config), ?MAX_HEADER_SIZE), ok. + + %%------------------------------------------------------------------------- ip_time_test(doc) -> [""]; @@ -966,363 +1163,1072 @@ ip_restart_non_disturbing_block(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -ssl_mod_alias(doc) -> - ["Module test: mod_alias"]; -ssl_mod_alias(suite) -> + +pssl_mod_alias(doc) -> + ["Module test: mod_alias - old SSL config"]; +pssl_mod_alias(suite) -> + []; +pssl_mod_alias(Config) when is_list(Config) -> + ssl_mod_alias(ssl, Config). + +ossl_mod_alias(doc) -> + ["Module test: mod_alias - using new of configure old SSL"]; +ossl_mod_alias(suite) -> []; -ssl_mod_alias(Config) when is_list(Config) -> - httpd_mod:alias(ssl, ?SSL_PORT, +ossl_mod_alias(Config) when is_list(Config) -> + ssl_mod_alias(ossl, Config). + +essl_mod_alias(doc) -> + ["Module test: mod_alias - using new of configure new SSL"]; +essl_mod_alias(suite) -> + []; +essl_mod_alias(Config) when is_list(Config) -> + ssl_mod_alias(essl, Config). + + +ssl_mod_alias(Tag, Config) -> + httpd_mod:alias(Tag, ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_actions(doc) -> - ["Module test: mod_actions"]; -ssl_mod_actions(suite) -> + +pssl_mod_actions(doc) -> + ["Module test: mod_actions - old SSL config"]; +pssl_mod_actions(suite) -> []; -ssl_mod_actions(Config) when is_list(Config) -> - httpd_mod:actions(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_actions(Config) when is_list(Config) -> + ssl_mod_actions(ssl, Config). + +ossl_mod_actions(doc) -> + ["Module test: mod_actions - using new of configure old SSL"]; +ossl_mod_actions(suite) -> + []; +ossl_mod_actions(Config) when is_list(Config) -> + ssl_mod_actions(ossl, Config). + +essl_mod_actions(doc) -> + ["Module test: mod_actions - using new of configure new SSL"]; +essl_mod_actions(suite) -> + []; +essl_mod_actions(Config) when is_list(Config) -> + ssl_mod_actions(essl, Config). + + +ssl_mod_actions(Tag, Config) -> + httpd_mod:actions(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_security(doc) -> - ["Module test: mod_security"]; -ssl_mod_security(suite) -> + +pssl_mod_security(doc) -> + ["Module test: mod_security - old SSL config"]; +pssl_mod_security(suite) -> + []; +pssl_mod_security(Config) when is_list(Config) -> + ssl_mod_security(ssl, Config). + +ossl_mod_security(doc) -> + ["Module test: mod_security - using new of configure old SSL"]; +ossl_mod_security(suite) -> []; -ssl_mod_security(Config) when is_list(Config) -> +ossl_mod_security(Config) when is_list(Config) -> + ssl_mod_security(ossl, Config). + +essl_mod_security(doc) -> + ["Module test: mod_security - using new of configure new SSL"]; +essl_mod_security(suite) -> + []; +essl_mod_security(Config) when is_list(Config) -> + ssl_mod_security(essl, Config). + +ssl_mod_security(Tag, Config) -> ServerRoot = ?config(server_root, Config), - httpd_mod:security(ServerRoot, ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), + httpd_mod:security(ServerRoot, + Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_auth(doc) -> - ["Module test: mod_auth"]; -ssl_mod_auth(suite) -> + +pssl_mod_auth(doc) -> + ["Module test: mod_auth - old SSL config"]; +pssl_mod_auth(suite) -> []; -ssl_mod_auth(Config) when is_list(Config) -> - httpd_mod:auth(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_auth(Config) when is_list(Config) -> + ssl_mod_auth(ssl, Config). + +ossl_mod_auth(doc) -> + ["Module test: mod_auth - using new of configure old SSL"]; +ossl_mod_auth(suite) -> + []; +ossl_mod_auth(Config) when is_list(Config) -> + ssl_mod_auth(ossl, Config). + +essl_mod_auth(doc) -> + ["Module test: mod_auth - using new of configure new SSL"]; +essl_mod_auth(suite) -> + []; +essl_mod_auth(Config) when is_list(Config) -> + ssl_mod_auth(essl, Config). + +ssl_mod_auth(Tag, Config) -> + httpd_mod:auth(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_auth_api(doc) -> - ["Module test: mod_auth"]; -ssl_mod_auth_api(suite) -> + +pssl_mod_auth_api(doc) -> + ["Module test: mod_auth - old SSL config"]; +pssl_mod_auth_api(suite) -> + []; +pssl_mod_auth_api(Config) when is_list(Config) -> + ssl_mod_auth_api(ssl, Config). + +ossl_mod_auth_api(doc) -> + ["Module test: mod_auth - using new of configure old SSL"]; +ossl_mod_auth_api(suite) -> []; -ssl_mod_auth_api(Config) when is_list(Config) -> +ossl_mod_auth_api(Config) when is_list(Config) -> + ssl_mod_auth_api(ossl, Config). + +essl_mod_auth_api(doc) -> + ["Module test: mod_auth - using new of configure new SSL"]; +essl_mod_auth_api(suite) -> + []; +essl_mod_auth_api(Config) when is_list(Config) -> + ssl_mod_auth_api(essl, Config). + +ssl_mod_auth_api(Tag, Config) -> ServerRoot = ?config(server_root, Config), - Host = ?config(host, Config), - Node = ?config(node, Config), - httpd_mod:auth_api(ServerRoot, "", ssl, ?SSL_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "dets_", ssl, ?SSL_PORT, Host, Node), - httpd_mod:auth_api(ServerRoot, "mnesia_", ssl, ?SSL_PORT, Host, Node), + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node), + httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node), ok. + %%------------------------------------------------------------------------- -ssl_mod_auth_mnesia_api(doc) -> - ["Module test: mod_auth_mnesia_api"]; -ssl_mod_auth_mnesia_api(suite) -> + +pssl_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api - old SSL config"]; +pssl_mod_auth_mnesia_api(suite) -> []; -ssl_mod_auth_mnesia_api(Config) when is_list(Config) -> - httpd_mod:auth_mnesia_api(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_auth_mnesia_api(Config) when is_list(Config) -> + ssl_mod_auth_mnesia_api(ssl, Config). + +ossl_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api - using new of configure old SSL"]; +ossl_mod_auth_mnesia_api(suite) -> + []; +ossl_mod_auth_mnesia_api(Config) when is_list(Config) -> + ssl_mod_auth_mnesia_api(ossl, Config). + +essl_mod_auth_mnesia_api(doc) -> + ["Module test: mod_auth_mnesia_api - using new of configure new SSL"]; +essl_mod_auth_mnesia_api(suite) -> + []; +essl_mod_auth_mnesia_api(Config) when is_list(Config) -> + ssl_mod_auth_mnesia_api(essl, Config). + +ssl_mod_auth_mnesia_api(Tag, Config) -> + httpd_mod:auth_mnesia_api(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_htaccess(doc) -> - ["Module test: mod_htaccess"]; -ssl_mod_htaccess(suite) -> + +pssl_mod_htaccess(doc) -> + ["Module test: mod_htaccess - old SSL config"]; +pssl_mod_htaccess(suite) -> []; -ssl_mod_htaccess(Config) when is_list(Config) -> - httpd_mod:htaccess(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_htaccess(Config) when is_list(Config) -> + ssl_mod_htaccess(ssl, Config). + +ossl_mod_htaccess(doc) -> + ["Module test: mod_htaccess - using new of configure old SSL"]; +ossl_mod_htaccess(suite) -> + []; +ossl_mod_htaccess(Config) when is_list(Config) -> + ssl_mod_htaccess(ossl, Config). + +essl_mod_htaccess(doc) -> + ["Module test: mod_htaccess - using new of configure new SSL"]; +essl_mod_htaccess(suite) -> + []; +essl_mod_htaccess(Config) when is_list(Config) -> + ssl_mod_htaccess(essl, Config). + +ssl_mod_htaccess(Tag, Config) -> + httpd_mod:htaccess(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_cgi(doc) -> - ["Module test: mod_cgi"]; -ssl_mod_cgi(suite) -> + +pssl_mod_cgi(doc) -> + ["Module test: mod_cgi - old SSL config"]; +pssl_mod_cgi(suite) -> + []; +pssl_mod_cgi(Config) when is_list(Config) -> + ssl_mod_cgi(ssl, Config). + +ossl_mod_cgi(doc) -> + ["Module test: mod_cgi - using new of configure old SSL"]; +ossl_mod_cgi(suite) -> []; -ssl_mod_cgi(Config) when is_list(Config) -> +ossl_mod_cgi(Config) when is_list(Config) -> + ssl_mod_cgi(ossl, Config). + +essl_mod_cgi(doc) -> + ["Module test: mod_cgi - using new of configure new SSL"]; +essl_mod_cgi(suite) -> + []; +essl_mod_cgi(Config) when is_list(Config) -> + ssl_mod_cgi(essl, Config). + +ssl_mod_cgi(Tag, Config) -> case test_server:os_type() of vxworks -> {skip, cgi_not_supported_on_vxwoks}; _ -> - httpd_mod:cgi(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), + httpd_mod:cgi(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok end. + + %%------------------------------------------------------------------------- -ssl_mod_esi(doc) -> - ["Module test: mod_esi"]; -ssl_mod_esi(suite) -> + +pssl_mod_esi(doc) -> + ["Module test: mod_esi - old SSL config"]; +pssl_mod_esi(suite) -> []; -ssl_mod_esi(Config) when is_list(Config) -> - httpd_mod:esi(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_esi(Config) when is_list(Config) -> + ssl_mod_esi(ssl, Config). + +ossl_mod_esi(doc) -> + ["Module test: mod_esi - using new of configure old SSL"]; +ossl_mod_esi(suite) -> + []; +ossl_mod_esi(Config) when is_list(Config) -> + ssl_mod_esi(ossl, Config). + +essl_mod_esi(doc) -> + ["Module test: mod_esi - using new of configure new SSL"]; +essl_mod_esi(suite) -> + []; +essl_mod_esi(Config) when is_list(Config) -> + ssl_mod_esi(essl, Config). + +ssl_mod_esi(Tag, Config) -> + httpd_mod:esi(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + %%------------------------------------------------------------------------- -ssl_mod_get(doc) -> - ["Module test: mod_get"]; -ssl_mod_get(suite) -> + +pssl_mod_get(doc) -> + ["Module test: mod_get - old SSL config"]; +pssl_mod_get(suite) -> []; -ssl_mod_get(Config) when is_list(Config) -> - httpd_mod:get(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_get(Config) when is_list(Config) -> + ssl_mod_get(ssl, Config). + +ossl_mod_get(doc) -> + ["Module test: mod_get - using new of configure old SSL"]; +ossl_mod_get(suite) -> + []; +ossl_mod_get(Config) when is_list(Config) -> + ssl_mod_get(ossl, Config). + +essl_mod_get(doc) -> + ["Module test: mod_get - using new of configure new SSL"]; +essl_mod_get(suite) -> + []; +essl_mod_get(Config) when is_list(Config) -> + ssl_mod_get(essl, Config). + +ssl_mod_get(Tag, Config) -> + httpd_mod:get(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_head(doc) -> - ["Module test: mod_head"]; -ssl_mod_head(suite) -> + +pssl_mod_head(doc) -> + ["Module test: mod_head - old SSL config"]; +pssl_mod_head(suite) -> []; -ssl_mod_head(Config) when is_list(Config) -> - httpd_mod:head(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_head(Config) when is_list(Config) -> + ssl_mod_head(ssl, Config). + +ossl_mod_head(doc) -> + ["Module test: mod_head - using new of configure old SSL"]; +ossl_mod_head(suite) -> + []; +ossl_mod_head(Config) when is_list(Config) -> + ssl_mod_head(ossl, Config). + +essl_mod_head(doc) -> + ["Module test: mod_head - using new of configure new SSL"]; +essl_mod_head(suite) -> + []; +essl_mod_head(Config) when is_list(Config) -> + ssl_mod_head(essl, Config). + +ssl_mod_head(Tag, Config) -> + httpd_mod:head(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_mod_all(doc) -> - ["All modules test"]; -ssl_mod_all(suite) -> + +pssl_mod_all(doc) -> + ["All modules test - old SSL config"]; +pssl_mod_all(suite) -> []; -ssl_mod_all(Config) when is_list(Config) -> - httpd_mod:all(ssl, ?SSL_PORT, - ?config(host, Config), ?config(node, Config)), +pssl_mod_all(Config) when is_list(Config) -> + ssl_mod_all(ssl, Config). + +ossl_mod_all(doc) -> + ["All modules test - using new of configure old SSL"]; +ossl_mod_all(suite) -> + []; +ossl_mod_all(Config) when is_list(Config) -> + ssl_mod_all(ossl, Config). + +essl_mod_all(doc) -> + ["All modules test - using new of configure new SSL"]; +essl_mod_all(suite) -> + []; +essl_mod_all(Config) when is_list(Config) -> + ssl_mod_all(essl, Config). + +ssl_mod_all(Tag, Config) -> + httpd_mod:all(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + %%------------------------------------------------------------------------- -ssl_load_light(doc) -> - ["Test light load"]; -ssl_load_light(suite) -> + +pssl_load_light(doc) -> + ["Test light load - old SSL config"]; +pssl_load_light(suite) -> []; -ssl_load_light(Config) when is_list(Config) -> - httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), +pssl_load_light(Config) when is_list(Config) -> + ssl_load_light(ssl, Config). + +ossl_load_light(doc) -> + ["Test light load - using new of configure old SSL"]; +ossl_load_light(suite) -> + []; +ossl_load_light(Config) when is_list(Config) -> + ssl_load_light(ossl, Config). + +essl_load_light(doc) -> + ["Test light load - using new of configure new SSL"]; +essl_load_light(suite) -> + []; +essl_load_light(Config) when is_list(Config) -> + ssl_load_light(essl, Config). + +ssl_load_light(Tag, Config) -> + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), ?config(node, Config), get_nof_clients(ssl, light)), ok. + %%------------------------------------------------------------------------- -ssl_load_medium(doc) -> - ["Test medium load"]; -ssl_load_medium(suite) -> + +pssl_load_medium(doc) -> + ["Test medium load - old SSL config"]; +pssl_load_medium(suite) -> []; -ssl_load_medium(Config) when is_list(Config) -> +pssl_load_medium(Config) when is_list(Config) -> + ssl_load_medium(ssl, Config). + +ossl_load_medium(doc) -> + ["Test medium load - using new of configure old SSL"]; +ossl_load_medium(suite) -> + []; +ossl_load_medium(Config) when is_list(Config) -> + ssl_load_medium(ossl, Config). + +essl_load_medium(doc) -> + ["Test medium load - using new of configure new SSL"]; +essl_load_medium(suite) -> + []; +essl_load_medium(Config) when is_list(Config) -> + ssl_load_medium(essl, Config). + +ssl_load_medium(Tag, Config) -> %% <CONDITIONAL-SKIP> Skippable = [win32], Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, ?NON_PC_TC_MAYBE_SKIP(Config, Condition), %% </CONDITIONAL-SKIP> - httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), ?config(node, Config), get_nof_clients(ssl, medium)), ok. + %%------------------------------------------------------------------------- -ssl_load_heavy(doc) -> - ["Test heavy load"]; -ssl_load_heavy(suite) -> + +pssl_load_heavy(doc) -> + ["Test heavy load - old SSL config"]; +pssl_load_heavy(suite) -> + []; +pssl_load_heavy(Config) when is_list(Config) -> + ssl_load_heavy(ssl, Config). + +ossl_load_heavy(doc) -> + ["Test heavy load - using new of configure old SSL"]; +ossl_load_heavy(suite) -> []; -ssl_load_heavy(Config) when is_list(Config) -> +ossl_load_heavy(Config) when is_list(Config) -> + ssl_load_heavy(ossl, Config). + +essl_load_heavy(doc) -> + ["Test heavy load - using new of configure new SSL"]; +essl_load_heavy(suite) -> + []; +essl_load_heavy(Config) when is_list(Config) -> + ssl_load_heavy(essl, Config). + +ssl_load_heavy(Tag, Config) -> %% <CONDITIONAL-SKIP> Skippable = [win32], Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, ?NON_PC_TC_MAYBE_SKIP(Config, Condition), %% </CONDITIONAL-SKIP> - httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config), + httpd_load:load_test(Tag, + ?SSL_PORT, + ?config(host, Config), ?config(node, Config), get_nof_clients(ssl, heavy)), ok. + %%------------------------------------------------------------------------- -ssl_dos_hostname(doc) -> - ["Denial Of Service (DOS) attack test case"]; -ssl_dos_hostname(suite) -> + +pssl_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case - old SSL config"]; +pssl_dos_hostname(suite) -> []; -ssl_dos_hostname(Config) when is_list(Config) -> - dos_hostname(ssl, ?SSL_PORT, ?config(host, Config), - ?config(node, Config), ?MAX_HEADER_SIZE), +pssl_dos_hostname(Config) when is_list(Config) -> + ssl_dos_hostname(ssl, Config). + +ossl_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case - using new of configure old SSL"]; +ossl_dos_hostname(suite) -> + []; +ossl_dos_hostname(Config) when is_list(Config) -> + ssl_dos_hostname(ossl, Config). + +essl_dos_hostname(doc) -> + ["Denial Of Service (DOS) attack test case - using new of configure new SSL"]; +essl_dos_hostname(suite) -> + []; +essl_dos_hostname(Config) when is_list(Config) -> + ssl_dos_hostname(essl, Config). + +ssl_dos_hostname(Tag, Config) -> + dos_hostname(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config), + ?MAX_HEADER_SIZE), ok. + + %%------------------------------------------------------------------------- -ssl_time_test(doc) -> - [""]; -ssl_time_test(suite) -> + +pssl_time_test(doc) -> + ["old SSL config"]; +pssl_time_test(suite) -> + []; +pssl_time_test(Config) when is_list(Config) -> + ssl_time_test(ssl, Config). + +ossl_time_test(doc) -> + ["using new of configure old SSL"]; +ossl_time_test(suite) -> []; -ssl_time_test(Config) when is_list(Config) -> +ossl_time_test(Config) when is_list(Config) -> + ssl_time_test(ossl, Config). + +essl_time_test(doc) -> + ["using new of configure new SSL"]; +essl_time_test(suite) -> + []; +essl_time_test(Config) when is_list(Config) -> + ssl_time_test(essl, Config). + +ssl_time_test(Tag, Config) when is_list(Config) -> %% <CONDITIONAL-SKIP> - Condition = fun() -> true end, + FreeBSDVersionVerify = + fun() -> + case os:version() of + {7, 1, _} -> % We only have one such machine, so... + true; + _ -> + false + end + end, + Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, ?NON_PC_TC_MAYBE_SKIP(Config, Condition), %% </CONDITIONAL-SKIP> - httpd_time_test:t(ssl, ?config(host, Config), ?SSL_PORT), + httpd_time_test:t(Tag, + ?config(host, Config), + ?SSL_PORT), ok. + %%------------------------------------------------------------------------- -ssl_block_503(doc) -> + +pssl_block_503(doc) -> ["Check that you will receive status code 503 when the server" - " is blocked and 200 when its not blocked."]; -ssl_block_503(suite) -> + " is blocked and 200 when its not blocked - old SSL config."]; +pssl_block_503(suite) -> + []; +pssl_block_503(Config) when is_list(Config) -> + ssl_block_503(ssl, Config). + +ossl_block_503(doc) -> + ["Check that you will receive status code 503 when the server" + " is blocked and 200 when its not blocked - using new of configure old SSL."]; +ossl_block_503(suite) -> + []; +ossl_block_503(Config) when is_list(Config) -> + ssl_block_503(ossl, Config). + +essl_block_503(doc) -> + ["Check that you will receive status code 503 when the server" + " is blocked and 200 when its not blocked - using new of configure new SSL."]; +essl_block_503(suite) -> []; -ssl_block_503(Config) when is_list(Config) -> - httpd_block:block_503(ssl, ?SSL_PORT, ?config(host, Config), +essl_block_503(Config) when is_list(Config) -> + ssl_block_503(essl, Config). + +ssl_block_503(Tag, Config) -> + httpd_block:block_503(Tag, + ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_block_disturbing_idle(doc) -> + +pssl_block_disturbing_idle(doc) -> ["Check that you can block/unblock an idle server. The strategy " - "distribing does not really make a difference in this case."]; -ssl_block_disturbing_idle(suite) -> + "distribing does not really make a difference in this case." + "Old SSL config"]; +pssl_block_disturbing_idle(suite) -> + []; +pssl_block_disturbing_idle(Config) when is_list(Config) -> + ssl_block_disturbing_idle(ssl, Config). + +ossl_block_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "distribing does not really make a difference in this case." + "Using new of configure old SSL"]; +ossl_block_disturbing_idle(suite) -> + []; +ossl_block_disturbing_idle(Config) when is_list(Config) -> + ssl_block_disturbing_idle(ossl, Config). + +essl_block_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "distribing does not really make a difference in this case." + "Using new of configure new SSL"]; +essl_block_disturbing_idle(suite) -> []; -ssl_block_disturbing_idle(Config) when is_list(Config) -> - httpd_block:block_disturbing_idle(ssl, ?SSL_PORT, +essl_block_disturbing_idle(Config) when is_list(Config) -> + ssl_block_disturbing_idle(essl, Config). + +ssl_block_disturbing_idle(Tag, Config) -> + httpd_block:block_disturbing_idle(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_block_non_disturbing_idle(doc) -> + +pssl_block_non_disturbing_idle(doc) -> ["Check that you can block/unblock an idle server. The strategy " - "non distribing does not really make a difference in this case."]; -ssl_block_non_disturbing_idle(suite) -> + "non distribing does not really make a difference in this case." + "Old SSL config"]; +pssl_block_non_disturbing_idle(suite) -> []; -ssl_block_non_disturbing_idle(Config) when is_list(Config) -> - httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT, +pssl_block_non_disturbing_idle(Config) when is_list(Config) -> + ssl_block_non_disturbing_idle(ssl, Config). + +ossl_block_non_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing does not really make a difference in this case." + "Using new of configure old SSL"]; +ossl_block_non_disturbing_idle(suite) -> + []; +ossl_block_non_disturbing_idle(Config) when is_list(Config) -> + ssl_block_non_disturbing_idle(ossl, Config). + +essl_block_non_disturbing_idle(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing does not really make a difference in this case." + "Using new of configure new SSL"]; +essl_block_non_disturbing_idle(suite) -> + []; +essl_block_non_disturbing_idle(Config) when is_list(Config) -> + ssl_block_non_disturbing_idle(essl, Config). + +ssl_block_non_disturbing_idle(Tag, Config) -> + httpd_block:block_non_disturbing_idle(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_block_disturbing_active(doc) -> + +pssl_block_disturbing_active(doc) -> ["Check that you can block/unblock an active server. The strategy " - "distribing means ongoing requests should be terminated."]; -ssl_block_disturbing_active(suite) -> + "distribing means ongoing requests should be terminated." + "Old SSL config"]; +pssl_block_disturbing_active(suite) -> []; -ssl_block_disturbing_active(Config) when is_list(Config) -> - httpd_block:block_disturbing_active(ssl, ?SSL_PORT, +pssl_block_disturbing_active(Config) when is_list(Config) -> + ssl_block_disturbing_active(ssl, Config). + +ossl_block_disturbing_active(doc) -> + ["Check that you can block/unblock an active server. The strategy " + "distribing means ongoing requests should be terminated." + "Using new of configure old SSL"]; +ossl_block_disturbing_active(suite) -> + []; +ossl_block_disturbing_active(Config) when is_list(Config) -> + ssl_block_disturbing_active(ossl, Config). + +essl_block_disturbing_active(doc) -> + ["Check that you can block/unblock an active server. The strategy " + "distribing means ongoing requests should be terminated." + "Using new of configure new SSL"]; +essl_block_disturbing_active(suite) -> + []; +essl_block_disturbing_active(Config) when is_list(Config) -> + ssl_block_disturbing_active(essl, Config). + +ssl_block_disturbing_active(Tag, Config) -> + httpd_block:block_disturbing_active(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_block_non_disturbing_active(doc) -> + +pssl_block_non_disturbing_active(doc) -> ["Check that you can block/unblock an idle server. The strategy " - "non distribing means the ongoing requests should be compleated."]; -ssl_block_non_disturbing_active(suite) -> + "non distribing means the ongoing requests should be compleated." + "Old SSL config"]; +pssl_block_non_disturbing_active(suite) -> + []; +pssl_block_non_disturbing_active(Config) when is_list(Config) -> + ssl_block_non_disturbing_active(ssl, Config). + +ossl_block_non_disturbing_active(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing means the ongoing requests should be compleated." + "Using new of configure old SSL"]; +ossl_block_non_disturbing_active(suite) -> + []; +ossl_block_non_disturbing_active(Config) when is_list(Config) -> + ssl_block_non_disturbing_active(ossl, Config). + +essl_block_non_disturbing_active(doc) -> + ["Check that you can block/unblock an idle server. The strategy " + "non distribing means the ongoing requests should be compleated." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active(suite) -> []; -ssl_block_non_disturbing_active(Config) when is_list(Config) -> - httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT, +essl_block_non_disturbing_active(Config) when is_list(Config) -> + ssl_block_non_disturbing_active(essl, Config). + +ssl_block_non_disturbing_active(Tag, Config) -> + httpd_block:block_non_disturbing_idle(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + %%------------------------------------------------------------------------- -ssl_block_disturbing_active_timeout_not_released(doc) -> + +pssl_block_disturbing_active_timeout_not_released(doc) -> ["Check that you can block an active server. The strategy " "distribing means ongoing requests should be compleated" - "if the timeout does not occur."]; -ssl_block_disturbing_active_timeout_not_released(suite) -> + "if the timeout does not occur." + "Old SSL config"]; +pssl_block_disturbing_active_timeout_not_released(suite) -> []; -ssl_block_disturbing_active_timeout_not_released(Config) +pssl_block_disturbing_active_timeout_not_released(Config) when is_list(Config) -> - httpd_block: - block_disturbing_active_timeout_not_released(ssl, - ?SSL_PORT, - ?config(host, - Config), - ?config(node, - Config)), + ssl_block_disturbing_active_timeout_not_released(ssl, Config). + +ossl_block_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be compleated" + "if the timeout does not occur." + "Using new of configure old SSL"]; +ossl_block_disturbing_active_timeout_not_released(suite) -> + []; +ossl_block_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_not_released(ossl, Config). + +essl_block_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be compleated" + "if the timeout does not occur." + "Using new of configure new SSL"]; +essl_block_disturbing_active_timeout_not_released(suite) -> + []; +essl_block_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_not_released(essl, Config). + +ssl_block_disturbing_active_timeout_not_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_disturbing_active_timeout_not_released(Tag, + Port, Host, Node), ok. + + %%------------------------------------------------------------------------- -ssl_block_disturbing_active_timeout_released(doc) -> + +pssl_block_disturbing_active_timeout_released(doc) -> ["Check that you can block an active server. The strategy " "distribing means ongoing requests should be terminated when" - "the timeout occurs."]; -ssl_block_disturbing_active_timeout_released(suite) -> + "the timeout occurs." + "Old SSL config"]; +pssl_block_disturbing_active_timeout_released(suite) -> []; -ssl_block_disturbing_active_timeout_released(Config) +pssl_block_disturbing_active_timeout_released(Config) when is_list(Config) -> - httpd_block:block_disturbing_active_timeout_released(ssl, - ?SSL_PORT, - ?config(host, - Config), - ?config(node, - Config)), + ssl_block_disturbing_active_timeout_released(ssl, Config). + +ossl_block_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be terminated when" + "the timeout occurs." + "Using new of configure old SSL"]; +ossl_block_disturbing_active_timeout_released(suite) -> + []; +ossl_block_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_released(ossl, Config). + +essl_block_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "distribing means ongoing requests should be terminated when" + "the timeout occurs." + "Using new of configure new SSL"]; +essl_block_disturbing_active_timeout_released(suite) -> + []; +essl_block_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_disturbing_active_timeout_released(essl, Config). + +ssl_block_disturbing_active_timeout_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_disturbing_active_timeout_released(Tag, + Port, + Host, + Node), ok. + %%------------------------------------------------------------------------- -ssl_block_non_disturbing_active_timeout_not_released(doc) -> + +pssl_block_non_disturbing_active_timeout_not_released(doc) -> ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed."]; -ssl_block_non_disturbing_active_timeout_not_released(suite) -> + "non non distribing means ongoing requests should be completed." + "Old SSL config"]; +pssl_block_non_disturbing_active_timeout_not_released(suite) -> []; -ssl_block_non_disturbing_active_timeout_not_released(Config) +pssl_block_non_disturbing_active_timeout_not_released(Config) when is_list(Config) -> - httpd_block: - block_non_disturbing_active_timeout_not_released(ssl, - ?SSL_PORT, - ?config(host, - Config), - ?config(node, - Config)), + ssl_block_non_disturbing_active_timeout_not_released(ssl, Config). + +ossl_block_non_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "non non distribing means ongoing requests should be completed." + "Using new of configure old SSL"]; +ossl_block_non_disturbing_active_timeout_not_released(suite) -> + []; +ossl_block_non_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_not_released(ossl, Config). + +essl_block_non_disturbing_active_timeout_not_released(doc) -> + ["Check that you can block an active server. The strategy " + "non non distribing means ongoing requests should be completed." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active_timeout_not_released(suite) -> + []; +essl_block_non_disturbing_active_timeout_not_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_not_released(essl, Config). + +ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_non_disturbing_active_timeout_not_released(Tag, + Port, + Host, + Node), ok. + + %%------------------------------------------------------------------------- -ssl_block_non_disturbing_active_timeout_released(doc) -> + +pssl_block_non_disturbing_active_timeout_released(doc) -> ["Check that you can block an active server. The strategy " - "non non distribing means ongoing requests should be completed. " - "When the timeout occurs the block operation sohould be canceled." ]; -ssl_block_non_disturbing_active_timeout_released(suite) -> + "non distribing means ongoing requests should be completed. " + "When the timeout occurs the block operation sohould be canceled." + "Old SSL config"]; +pssl_block_non_disturbing_active_timeout_released(suite) -> []; -ssl_block_non_disturbing_active_timeout_released(Config) +pssl_block_non_disturbing_active_timeout_released(Config) when is_list(Config) -> - httpd_block: - block_non_disturbing_active_timeout_released(ssl, - ?SSL_PORT, - ?config(host, - Config), - ?config(node, - Config)), + ssl_block_non_disturbing_active_timeout_released(ssl, Config). + +ossl_block_non_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "non distribing means ongoing requests should be completed. " + "When the timeout occurs the block operation sohould be canceled." + "Using new of configure old SSL"]; +ossl_block_non_disturbing_active_timeout_released(suite) -> + []; +ossl_block_non_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_released(ossl, Config). + +essl_block_non_disturbing_active_timeout_released(doc) -> + ["Check that you can block an active server. The strategy " + "non distribing means ongoing requests should be completed. " + "When the timeout occurs the block operation sohould be canceled." + "Using new of configure new SSL"]; +essl_block_non_disturbing_active_timeout_released(suite) -> + []; +essl_block_non_disturbing_active_timeout_released(Config) + when is_list(Config) -> + ssl_block_non_disturbing_active_timeout_released(essl, Config). + +ssl_block_non_disturbing_active_timeout_released(Tag, Config) + when is_list(Config) -> + Port = ?SSL_PORT, + Host = ?config(host, Config), + Node = ?config(node, Config), + httpd_block:block_non_disturbing_active_timeout_released(Tag, + Port, + Host, + Node), + ok. + %%------------------------------------------------------------------------- -ssl_block_disturbing_blocker_dies(doc) -> + +pssl_block_disturbing_blocker_dies(doc) -> + ["old SSL config"]; +pssl_block_disturbing_blocker_dies(suite) -> []; -ssl_block_disturbing_blocker_dies(suite) -> +pssl_block_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_disturbing_blocker_dies(ssl, Config). + +ossl_block_disturbing_blocker_dies(doc) -> + ["using new of configure old SSL"]; +ossl_block_disturbing_blocker_dies(suite) -> []; -ssl_block_disturbing_blocker_dies(Config) when is_list(Config) -> - httpd_block:disturbing_blocker_dies(ssl, ?SSL_PORT, +ossl_block_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_disturbing_blocker_dies(ossl, Config). + +essl_block_disturbing_blocker_dies(doc) -> + ["using new of configure new SSL"]; +essl_block_disturbing_blocker_dies(suite) -> + []; +essl_block_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_disturbing_blocker_dies(essl, Config). + +ssl_block_disturbing_blocker_dies(Tag, Config) -> + httpd_block:disturbing_blocker_dies(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_block_non_disturbing_blocker_dies(doc) -> + +pssl_block_non_disturbing_blocker_dies(doc) -> + ["old SSL config"]; +pssl_block_non_disturbing_blocker_dies(suite) -> []; -ssl_block_non_disturbing_blocker_dies(suite) -> +pssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_non_disturbing_blocker_dies(ssl, Config). + +ossl_block_non_disturbing_blocker_dies(doc) -> + ["using new of configure old SSL"]; +ossl_block_non_disturbing_blocker_dies(suite) -> []; -ssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> - httpd_block:non_disturbing_blocker_dies(ssl, ?SSL_PORT, +ossl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_non_disturbing_blocker_dies(ossl, Config). + +essl_block_non_disturbing_blocker_dies(doc) -> + ["using new of configure new SSL"]; +essl_block_non_disturbing_blocker_dies(suite) -> + []; +essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) -> + ssl_block_non_disturbing_blocker_dies(essl, Config). + +ssl_block_non_disturbing_blocker_dies(Tag, Config) -> + httpd_block:non_disturbing_blocker_dies(Tag, + ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_restart_no_block(doc) -> - [""]; -ssl_restart_no_block(suite) -> + +pssl_restart_no_block(doc) -> + ["old SSL config"]; +pssl_restart_no_block(suite) -> + []; +pssl_restart_no_block(Config) when is_list(Config) -> + ssl_restart_no_block(ssl, Config). + +ossl_restart_no_block(doc) -> + ["using new of configure old SSL"]; +ossl_restart_no_block(suite) -> + []; +ossl_restart_no_block(Config) when is_list(Config) -> + ssl_restart_no_block(ossl, Config). + +essl_restart_no_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_no_block(suite) -> []; -ssl_restart_no_block(Config) when is_list(Config) -> - httpd_block:restart_no_block(ssl, ?SSL_PORT, ?config(host, Config), +essl_restart_no_block(Config) when is_list(Config) -> + ssl_restart_no_block(essl, Config). + +ssl_restart_no_block(Tag, Config) -> + httpd_block:restart_no_block(Tag, + ?SSL_PORT, + ?config(host, Config), ?config(node, Config)), ok. + + %%------------------------------------------------------------------------- -ssl_restart_disturbing_block(doc) -> - [""]; -ssl_restart_disturbing_block(suite) -> + +pssl_restart_disturbing_block(doc) -> + ["old SSL config"]; +pssl_restart_disturbing_block(suite) -> + []; +pssl_restart_disturbing_block(Config) when is_list(Config) -> + ssl_restart_disturbing_block(ssl, Config). + +ossl_restart_disturbing_block(doc) -> + ["using new of configure old SSL"]; +ossl_restart_disturbing_block(suite) -> []; -ssl_restart_disturbing_block(Config) when is_list(Config) -> +ossl_restart_disturbing_block(Config) when is_list(Config) -> + ssl_restart_disturbing_block(ossl, Config). + +essl_restart_disturbing_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_disturbing_block(suite) -> + []; +essl_restart_disturbing_block(Config) when is_list(Config) -> + ssl_restart_disturbing_block(essl, Config). + +ssl_restart_disturbing_block(Tag, Config) -> %% <CONDITIONAL-SKIP> Condition = fun() -> case os:type() of {unix, linux} -> - HW = string:strip(os:cmd("uname -m"), right, $\n), - case HW of + case ?OSCMD("uname -m") of "ppc" -> - case inet:gethostname() of - {ok, "peach"} -> - true; + case file:read_file_info("/etc/fedora-release") of + {ok, _} -> + case ?OSCMD("awk '{print $2}' /etc/fedora-release") of + "release" -> + %% Fedora 7 and later + case ?OSCMD("awk '{print $3}' /etc/fedora-release") of + "7" -> + true; + _ -> + false + end; + _ -> + false + end; _ -> false end; @@ -1336,17 +2242,36 @@ ssl_restart_disturbing_block(Config) when is_list(Config) -> ?NON_PC_TC_MAYBE_SKIP(Config, Condition), %% </CONDITIONAL-SKIP> - httpd_block:restart_disturbing_block(ssl, ?SSL_PORT, + httpd_block:restart_disturbing_block(Tag, ?SSL_PORT, ?config(host, Config), ?config(node, Config)), ok. + %%------------------------------------------------------------------------- -ssl_restart_non_disturbing_block(doc) -> - [""]; -ssl_restart_non_disturbing_block(suite) -> + +pssl_restart_non_disturbing_block(doc) -> + ["old SSL config"]; +pssl_restart_non_disturbing_block(suite) -> + []; +pssl_restart_non_disturbing_block(Config) when is_list(Config) -> + ssl_restart_non_disturbing_block(ssl, Config). + +ossl_restart_non_disturbing_block(doc) -> + ["using new of configure old SSL"]; +ossl_restart_non_disturbing_block(suite) -> []; -ssl_restart_non_disturbing_block(Config) when is_list(Config) -> +ossl_restart_non_disturbing_block(Config) when is_list(Config) -> + ssl_restart_non_disturbing_block(ossl, Config). + +essl_restart_non_disturbing_block(doc) -> + ["using new of configure new SSL"]; +essl_restart_non_disturbing_block(suite) -> + []; +essl_restart_non_disturbing_block(Config) when is_list(Config) -> + ssl_restart_non_disturbing_block(essl, Config). + +ssl_restart_non_disturbing_block(Tag, Config) -> %% <CONDITIONAL-SKIP> Condition = fun() -> @@ -1371,11 +2296,13 @@ ssl_restart_non_disturbing_block(Config) when is_list(Config) -> ?NON_PC_TC_MAYBE_SKIP(Config, Condition), %% </CONDITIONAL-SKIP> - httpd_block:restart_non_disturbing_block(ssl, ?SSL_PORT, - ?config(host, Config), - ?config(node, Config)), + httpd_block:restart_non_disturbing_block(Tag, + ?SSL_PORT, + ?config(host, Config), + ?config(node, Config)), ok. + %%------------------------------------------------------------------------- ip_host(doc) -> ["Control that the server accepts/rejects requests with/ without host"]; @@ -1532,30 +2459,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"]; @@ -1665,17 +2638,29 @@ dos_hostname(Type, Port, Host, Node, Max) -> %% Other help functions create_config(Config, Access, FileName) -> ServerRoot = ?config(server_root, Config), - TcTopDir = ?config(tc_top_dir, Config), - Port = ?config(port, Config), - Type = ?config(sock_type, Config), - Host = ?config(host, Config), - Mods = io_lib:format("~p", [httpd_mod]), - Funcs = io_lib:format("~p", [ssl_password_cb]), - MaxHdrSz = io_lib:format("~p", [256]), - MaxHdrAct = io_lib:format("~p", [close]), + TcTopDir = ?config(tc_top_dir, Config), + Port = ?config(port, Config), + Type = ?config(sock_type, Config), + Host = ?config(host, Config), + Mods = io_lib:format("~p", [httpd_mod]), + Funcs = io_lib:format("~p", [ssl_password_cb]), + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + + io:format(user, + "create_config -> " + "~n ServerRoot: ~p" + "~n TcTopDir: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n", [ServerRoot, TcTopDir, Port, Type, Host]), + SSL = - case Type of - ssl -> + if + (Type =:= ssl) orelse + (Type =:= ossl) orelse + (Type =:= essl) -> [cline(["SSLCertificateFile ", filename:join(ServerRoot, "ssl/ssl_server.pem")]), cline(["SSLCertificateKeyFile ", @@ -1686,25 +2671,25 @@ create_config(Config, Access, FileName) -> cline(["SSLPasswordCallbackFunction ", Funcs]), cline(["SSLVerifyClient 0"]), cline(["SSLVerifyDepth 1"])]; - _ -> + true -> [] end, - Mod_order = case Access of - mod_htaccess -> - "Modules mod_alias mod_htaccess mod_auth " - "mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log"; - _ -> - "Modules mod_alias mod_auth mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log" - end, - + ModOrder = case Access of + mod_htaccess -> + "Modules mod_alias mod_htaccess mod_auth " + "mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log"; + _ -> + "Modules mod_alias mod_auth mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log" + end, + %% The test suite currently does not handle an explicit BindAddress. %% They assume any has been used, that is Addr is always set to undefined! @@ -1720,7 +2705,7 @@ create_config(Config, Access, FileName) -> cline(["Port ", integer_to_list(Port)]), cline(["ServerName ", Host]), cline(["SocketType ", atom_to_list(Type)]), - cline([Mod_order]), + cline([ModOrder]), %% cline(["LogFormat ", "erlang"]), cline(["ServerAdmin [email protected]"]), cline(["BindAddress ", BindAddress]), @@ -1882,18 +2867,18 @@ start_mnesia(Node) -> ok -> ok; Other -> - test_server:fail({failed_to_cleanup_mnesia, Other}) + tsf({failed_to_cleanup_mnesia, Other}) end, - case rpc:call(Node, ?MODULE, setup_mnesia, []) of + case rpc:call(Node, ?MODULE, setup_mnesia, []) of {atomic, ok} -> ok; Other2 -> - test_server:fail({failed_to_setup_mnesia, Other2}) + tsf({failed_to_setup_mnesia, Other2}) end, ok. setup_mnesia() -> - setup_mnesia([node()]). + setup_mnesia([node()]). setup_mnesia(Nodes) -> ok = mnesia:create_schema(Nodes), @@ -1916,22 +2901,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"]), @@ -1946,7 +2931,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 ++ @@ -1955,7 +2940,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 " ++ @@ -1969,14 +2954,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"])), @@ -1999,7 +2984,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"])), @@ -2010,7 +2995,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) -> @@ -2029,20 +3014,20 @@ dos_hostname_request(Host) -> get_nof_clients(Mode, Load) -> get_nof_clients(test_server:os_type(), Mode, Load). -get_nof_clients(vxworks, _, light) -> 1; +get_nof_clients(vxworks, _, light) -> 1; get_nof_clients(vxworks, ip_comm, medium) -> 3; -get_nof_clients(vxworks, ssl, medium) -> 3; +get_nof_clients(vxworks, ssl, medium) -> 3; get_nof_clients(vxworks, ip_comm, heavy) -> 5; -get_nof_clients(vxworks, ssl, heavy) -> 5; -get_nof_clients(_, ip_comm, light) -> 5; -get_nof_clients(_, ssl, light) -> 2; -get_nof_clients(_, ip_comm, medium) -> 10; -get_nof_clients(_, ssl, medium) -> 4; -get_nof_clients(_, ip_comm, heavy) -> 20; -get_nof_clients(_, ssl, heavy) -> 6. +get_nof_clients(vxworks, ssl, heavy) -> 5; +get_nof_clients(_, ip_comm, light) -> 5; +get_nof_clients(_, ssl, light) -> 2; +get_nof_clients(_, ip_comm, medium) -> 10; +get_nof_clients(_, ssl, medium) -> 4; +get_nof_clients(_, ip_comm, heavy) -> 20; +get_nof_clients(_, ssl, heavy) -> 6. %% Make a file 100 bytes long containing 012...9*10 -create_range_data(Path)-> +create_range_data(Path) -> PathAndFileName=filename:join([Path,"range.txt"]), file:write_file(PathAndFileName,list_to_binary(["12345678901234567890", "12345678901234567890", @@ -2050,32 +3035,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) -> + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_SUITE_data/server_root/Makefile b/lib/inets/test/httpd_SUITE_data/server_root/Makefile new file mode 100644 index 0000000000..d7a3231068 --- /dev/null +++ b/lib/inets/test/httpd_SUITE_data/server_root/Makefile @@ -0,0 +1,209 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-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/. +# +# 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. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(INETS_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +MODULE= + +AUTH_FILES = auth/group \ + auth/passwd +CGI_FILES = cgi-bin/printenv.sh +CONF_FILES = conf/8080.conf \ + conf/8888.conf \ + conf/httpd.conf \ + conf/ssl.conf \ + conf/mime.types +OPEN_FILES = htdocs/open/dummy.html +MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html +MISC_FILES = htdocs/misc/friedrich.html \ + htdocs/misc/oech.html +SECRET_FILES = htdocs/secret/dummy.html +MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html +HTDOCS_FILES = htdocs/index.html \ + htdocs/config.shtml \ + htdocs/echo.shtml \ + htdocs/exec.shtml \ + htdocs/flastmod.shtml \ + htdocs/fsize.shtml \ + htdocs/include.shtml +ICON_FILES = icons/README \ + icons/a.gif \ + icons/alert.black.gif \ + icons/alert.red.gif \ + icons/apache_pb.gif \ + icons/back.gif \ + icons/ball.gray.gif \ + icons/ball.red.gif \ + icons/binary.gif \ + icons/binhex.gif \ + icons/blank.gif \ + icons/bomb.gif \ + icons/box1.gif \ + icons/box2.gif \ + icons/broken.gif \ + icons/burst.gif \ + icons/button1.gif \ + icons/button10.gif \ + icons/button2.gif \ + icons/button3.gif \ + icons/button4.gif \ + icons/button5.gif \ + icons/button6.gif \ + icons/button7.gif \ + icons/button8.gif \ + icons/button9.gif \ + icons/buttonl.gif \ + icons/buttonr.gif \ + icons/c.gif \ + icons/comp.blue.gif \ + icons/comp.gray.gif \ + icons/compressed.gif \ + icons/continued.gif \ + icons/dir.gif \ + icons/down.gif \ + icons/dvi.gif \ + icons/f.gif \ + icons/folder.gif \ + icons/folder.open.gif \ + icons/folder.sec.gif \ + icons/forward.gif \ + icons/generic.gif \ + icons/generic.red.gif \ + icons/generic.sec.gif \ + icons/hand.right.gif \ + icons/hand.up.gif \ + icons/htdig.gif \ + icons/icon.sheet.gif \ + icons/image1.gif \ + icons/image2.gif \ + icons/image3.gif \ + icons/index.gif \ + icons/layout.gif \ + icons/left.gif \ + icons/link.gif \ + icons/movie.gif \ + icons/p.gif \ + icons/patch.gif \ + icons/pdf.gif \ + icons/pie0.gif \ + icons/pie1.gif \ + icons/pie2.gif \ + icons/pie3.gif \ + icons/pie4.gif \ + icons/pie5.gif \ + icons/pie6.gif \ + icons/pie7.gif \ + icons/pie8.gif \ + icons/portal.gif \ + icons/poweredby.gif \ + icons/ps.gif \ + icons/quill.gif \ + icons/right.gif \ + icons/screw1.gif \ + icons/screw2.gif \ + icons/script.gif \ + icons/sound1.gif \ + icons/sound2.gif \ + icons/sphere1.gif \ + icons/sphere2.gif \ + icons/star.gif \ + icons/star_blank.gif \ + icons/tar.gif \ + icons/tex.gif \ + icons/text.gif \ + icons/transfer.gif \ + icons/unknown.gif \ + icons/up.gif \ + icons/uu.gif \ + icons/uuencoded.gif \ + icons/world1.gif \ + icons/world2.gif + +SSL_FILES = ssl/ssl_client.pem \ + ssl/ssl_server.pem + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: + +clean: + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DATA) $(OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DATA) $(MISC_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + $(INSTALL_DATA) $(SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret + $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + +release_docs_spec: + diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf index 8a74ed1afd..ceb94237d2 100644 --- a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf +++ b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-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 diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index ed0fe942cf..f23d0b4765 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -19,24 +19,32 @@ %% -module(httpd_basic_SUITE). --include("test_server.hrl"). --include("test_server_line.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). -define(URL_START, "http://localhost:"). -all(doc) -> - ["Basic test of httpd."]; +suite() -> [{ct_hooks,[ts_install_cth]}]. -all(suite) -> +all() -> [ - uri_too_long_414, - header_too_long_413, + uri_too_long_414, + header_too_long_413, escaped_url_in_error_body ]. +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -47,6 +55,8 @@ all(suite) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + tsp("init_per_suite -> entry with" + "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), HttpdConf = [{port, 0}, {ipfamily, inet}, @@ -61,6 +71,8 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> + tsp("end_per_suite -> entry with" + "~n Config: ~p", [_Config]), inets:stop(), ok. @@ -76,9 +88,12 @@ 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(_Case, Config) -> +init_per_testcase(Case, Config) -> + tsp("init_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -87,9 +102,12 @@ init_per_testcase(_Case, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + tsp("end_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- @@ -134,50 +152,35 @@ header_too_long_413(Config) when is_list(Config) -> {version, "HTTP/1.1"}]), inets:stop(httpd, Pid). - escaped_url_in_error_body(doc) -> ["Test Url-encoding see OTP-8940"]; escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), - {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), - Info = httpd:info(Pid), - Port = proplists:get_value(port, Info), - _Address = proplists:get_value(bind_address, Info), - Path = "/<b>this_is_bold<b>", - URL = ?URL_START ++ integer_to_list(Port) ++ Path, + tsp("escaped_url_in_error_body -> entry with" + "~n Config: ~p", [Config]), + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + _Address = proplists:get_value(bind_address, Info), + Path = "/<b>this_is_bold</b>", + URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), - case httpc:request(get, {URL, []}, - [{url_encode, true}, - {version, "HTTP/1.0"}], - [{full_result, false}]) of - {ok, {404, Body1}} -> - case find_URL_path(string:tokens(Body1, " ")) of - EscapedPath -> - ok; - BadPath1 -> - tsf({unexpected_path_1, EscapedPath, BadPath1}) - end; - {ok, UnexpectedOK1} -> - tsf({unexpected_ok_1, UnexpectedOK1}) - end, - - case httpc:request(get, {URL, []}, - [{version, "HTTP/1.0"}], - [{full_result, false}]) of - {ok, {404, Body2}} -> - HTMLEncodedPath = http_util:html_encode(Path), - case find_URL_path(string:tokens(Body2, " ")) of - HTMLEncodedPath -> - ok; - BadPath2 -> - tsf({unexpected_path_2, EscapedPath, BadPath2}) - end; - {ok, UnexpectedOK2} -> - tsf({unexpected_ok_2, UnexpectedOK2}) - end, - inets:stop(httpd, Pid). + {ok, {404, Body1}} = httpc:request(get, {URL, []}, + [{url_encode, true}, + {version, "HTTP/1.0"}], + [{full_result, false}]), + EscapedPath = find_URL_path(string:tokens(Body1, " ")), + {ok, {404, Body2}} = httpc:request(get, {URL, []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]), + HTMLEncodedPath = http_util:html_encode(Path), + HTMLEncodedPath = find_URL_path(string:tokens(Body2, " ")), + inets:stop(httpd, Pid), + tsp("escaped_url_in_error_body -> done"), + ok. find_URL_path([]) -> ""; @@ -186,6 +189,9 @@ find_URL_path(["URL", URL | _]) -> find_URL_path([_ | Rest]) -> find_URL_path(Rest). -tsf(Reason) -> - test_server:fail(Reason). + +tsp(F) -> + tsp(F, []). +tsp(F, A) -> + test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl index f967d8172a..ac1bf43ff5 100644 --- a/lib/inets/test/httpd_block.erl +++ b/lib/inets/test/httpd_block.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -36,6 +36,7 @@ ]). %% Help functions +-export([httpd_block/3, httpd_block/4, httpd_unblock/2, httpd_restart/2]). -export([do_block_server/4, do_block_nd_server/5, do_long_poll/6]). -define(report(Label, Content), @@ -47,18 +48,24 @@ %% Test cases starts here. %%------------------------------------------------------------------------- block_disturbing_idle(_Type, Port, Host, Node) -> - unblocked = get_admin_state(Node, Host, Port), + io:format("block_disturbing_idle -> entry~n", []), + validate_admin_state(Node, Host, Port, unblocked), block_server(Node, Host, Port), - blocked = get_admin_state(Node, Host, Port), + validate_admin_state(Node, Host, Port, blocked), unblock_server(Node, Host, Port), - unblocked = get_admin_state(Node, Host, Port). + validate_admin_state(Node, Host, Port, unblocked), + io:format("block_disturbing_idle -> done~n", []), + ok. + %%-------------------------------------------------------------------- block_non_disturbing_idle(_Type, Port, Host, Node) -> unblocked = get_admin_state(Node, Host, Port), block_nd_server(Node, Host, Port), blocked = get_admin_state(Node, Host, Port), unblock_server(Node, Host, Port), - unblocked = get_admin_state(Node, Host, Port). + unblocked = get_admin_state(Node, Host, Port), + ok. + %%-------------------------------------------------------------------- block_503(Type, Port, Host, Node) -> Req = "GET / HTTP/1.0\r\ndummy-host.ericsson.se:\r\n\r\n", @@ -76,6 +83,7 @@ block_503(Type, Port, Host, Node) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req, [{statuscode, 200}, {version, "HTTP/1.0"}]). + %%-------------------------------------------------------------------- block_disturbing_active(Type, Port, Host, Node) -> process_flag(trap_exit, true), @@ -87,6 +95,7 @@ block_disturbing_active(Type, Port, Host, Node) -> blocked = get_admin_state(Node, Host, Port), process_flag(trap_exit, false), ok. + %%-------------------------------------------------------------------- block_non_disturbing_active(Type, Port, Host, Node) -> process_flag(trap_exit, true), @@ -219,32 +228,91 @@ do_block_nd_server(Node, Host, Port, Timeout, Reply) -> restart_server(Node, _Host, Port) -> Addr = undefined, - rpc:call(Node, httpd, restart, [Addr, Port]). + rpc:call(Node, ?MODULE, httpd_restart, [Addr, Port]). + block_server(Node, _Host, Port) -> + io:format("block_server -> entry~n", []), Addr = undefined, - rpc:call(Node, httpd, block, [Addr, Port]). + rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing]). + block_server(Node, _Host, Port, Timeout) -> Addr = undefined, - rpc:call(Node, httpd, block, [Addr, Port, disturbing, Timeout]). + rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing, Timeout]). + block_nd_server(Node, _Host, Port) -> Addr = undefined, - rpc:call(Node, httpd, block, [Addr, Port, non_disturbing]). + rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing]). block_nd_server(Node, _Host, Port, Timeout) -> Addr = undefined, - rpc:call(Node, httpd, block, [Addr, Port, non_disturbing, Timeout]). + rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing, Timeout]). unblock_server(Node, _Host, Port) -> + io:format("~p:~p:block_server -> entry~n", [node(),self()]), Addr = undefined, - rpc:call(Node, httpd, unblock, [Addr, Port]). + rpc:call(Node, ?MODULE, httpd_unblock, [Addr, Port]). + + +httpd_block(Addr, Port, Mode) -> + io:format("~p:~p:httpd_block -> entry~n", [node(),self()]), + Name = make_name(Addr, Port), + case whereis(Name) of + Pid when is_pid(Pid) -> + httpd_manager:block(Pid, Mode); + _ -> + {error, not_started} + end. + +httpd_block(Addr, Port, Mode, Timeout) -> + Name = make_name(Addr, Port), + case whereis(Name) of + Pid when is_pid(Pid) -> + httpd_manager:block(Pid, Mode, Timeout); + _ -> + {error, not_started} + end. + +httpd_unblock(Addr, Port) -> + io:format("~p:~p:httpd_unblock -> entry~n", [node(),self()]), + Name = make_name(Addr, Port), + case whereis(Name) of + Pid when is_pid(Pid) -> + httpd_manager:unblock(Pid); + _ -> + {error, not_started} + end. + +httpd_restart(Addr, Port) -> + Name = make_name(Addr, Port), + case whereis(Name) of + Pid when is_pid(Pid) -> + httpd_manager:reload(Pid, undefined); + _ -> + {error, not_started} + end. + +make_name(Addr, Port) -> + httpd_util:make_name("httpd", Addr, Port). -get_admin_state(Node,_Host,Port) -> +get_admin_state(Node, _Host, Port) -> Addr = undefined, rpc:call(Node, httpd, get_admin_state, [Addr, Port]). +validate_admin_state(Node, Host, Port, Expect) -> + io:format("try validating server admin state: ~p~n", [Expect]), + case get_admin_state(Node, Host, Port) of + Expect -> + ok; + Unexpected -> + io:format("failed validating server admin state: ~p~n", + [Unexpected]), + exit({unexpected_admin_state, Unexpected, Expect}) + end. + + await_normal_process_exit(Pid, Name, Timeout) -> receive {'EXIT', Pid, normal} -> @@ -260,6 +328,7 @@ await_normal_process_exit(Pid, Name, Timeout) -> test_server:fail("timeout while waiting for " ++ Name) end. + await_suite_failed_process_exit(Pid, Name, Timeout, Why) -> receive {'EXIT', Pid, {suite_failed, Why}} -> diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl index 9bb9f9f94e..83520033dc 100644 --- a/lib/inets/test/httpd_load.erl +++ b/lib/inets/test/httpd_load.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 617851c77d..5016cdb9e6 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -8,18 +8,17 @@ %% 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. -%% +%% %% %CopyrightEnd% %% %% -module(httpd_mod). --author('[email protected]'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -40,6 +39,13 @@ %% Test cases starts here. %%------------------------------------------------------------------------- alias(Type, Port, Host, Node) -> +%% io:format(user, "~w:alias -> entry with" +%% "~n Type: ~p" +%% "~n Port: ~p" +%% "~n Host: ~p" +%% "~n Node: ~p" +%% "~n", [?MODULE, Type, Port, Host, Node]), + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /pics/icon.sheet.gif " "HTTP/1.0\r\n\r\n", @@ -90,6 +96,7 @@ security(ServerRoot, Type, Port, Host, Node) -> %% "~n Node: ~p" %% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]), +%% io:format(user, "~w:security -> register~n", [?MODULE]), global:register_name(mod_security_test, self()), % Receive events test_server:sleep(5000), @@ -99,54 +106,71 @@ security(ServerRoot, Type, Port, Host, Node) -> %% Test blocking / unblocking of users. %% /open, require user one Aladdin +%% io:format(user, "~w:security -> remove user~n", [?MODULE]), remove_users(Node, ServerRoot, Host, Port, "open"), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "onePassword"}]}, Node, Port), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "two"}, {password, "twoPassword"}]}, Node, Port), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "Aladdin", "AladdinPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "Aladdin"}, {password, "AladdinPassword"}]}, Node, Port), +%% io:format(user, "~w:security -> add users~n", [?MODULE]), add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), +%% io:format(user, "~w:security -> await block security event~n", [?MODULE]), receive_security_event({event, user_block, Port, OpenDir, [{user, "one"}]}, Node, Port), +%% io:format(user, "~w:security -> unregister~n", [?MODULE]), global:unregister_name(mod_security_test), % No more events. +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 403}]), %% User "one" should be blocked now.. %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), case list_blocked_users(Node, Port) of [{"one",_, Port, OpenDir,_}] -> ok; @@ -156,35 +180,54 @@ security(ServerRoot, Type, Port, Host, Node) -> exit({unexpected_blocked, Blocked}) end, +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir), +%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), true = unblock_user(Node, "one", Port, OpenDir), %% User "one" should not be blocked any more.. +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [] = list_blocked_users(Node, Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [] = list_blocked_users(Node, Port, OpenDir), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 200}]), %% Test list_auth_users & auth_timeout +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port, OpenDir), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port, OpenDir), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), ["one"] = list_auth_users(Node, Port, OpenDir), %% Wait for successful auth to timeout. test_server:sleep(?AUTH_TIMEOUT*1001), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [] = list_auth_users(Node, Port), +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [] = list_auth_users(Node, Port, OpenDir), %% "two" is blocked. +%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), true = unblock_user(Node, "two", Port, OpenDir), %% Test explicit blocking. Block user 'two'. +%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), [] = list_blocked_users(Node,Port,OpenDir), +%% io:format(user, "~w:security -> block user~n", [?MODULE]), true = block_user(Node, "two", Port, OpenDir, 10), +%% io:format(user, "~w:security -> auth request~n", [?MODULE]), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]). @@ -600,6 +643,11 @@ htaccess(Type, Port, Host, Node) -> {header, "WWW-Authenticate"}]). %%-------------------------------------------------------------------- cgi(Type, Port, Host, Node) -> +%% tsp("cgi -> entry with" +%% "~n Type: ~p" +%% "~n Port: ~p" +%% "~n Host: ~p" +%% "~n Node: ~p", []), {Script, Script2, Script3} = case test_server:os_type() of {win32, _} -> @@ -609,6 +657,7 @@ cgi(Type, Port, Host, Node) -> end, %% The length (> 100) is intentional +%% tsp("cgi -> request 01 with length > 100"), ok = httpd_test_lib: verify_request(Type, Host, Port, Node, "POST /cgi-bin/" ++ Script3 ++ @@ -636,46 +685,55 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}, {header, "content-type", "text/plain"}]), +%% tsp("cgi -> request 02"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 03"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/not_there " "HTTP/1.0\r\n\r\n", [{statuscode, 404},{statuscode, 500}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 04"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /cgi-bin/"++ Script ++ "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 04"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /cgi-bin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 05"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 06"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/not_there " "HTTP/1.0\r\n\r\n", [{statuscode, 404},{statuscode, 500}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 07"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "GET /htbin/"++ Script ++ "?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 08"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", [{statuscode, 200}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 09"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script ++ " HTTP/1.0\r\n\r\n", @@ -683,19 +741,24 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}]), %% Execute an existing, but bad CGI script.. +%% tsp("cgi -> request 10 - bad script"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /htbin/"++ Script2 ++ " HTTP/1.0\r\n\r\n", [{statuscode, 404}, {version, "HTTP/1.0"}]), +%% tsp("cgi -> request 11 - bad script"), ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "POST /cgi-bin/"++ Script2 ++ " HTTP/1.0\r\n\r\n", [{statuscode, 404}, {version, "HTTP/1.0"}]), + +%% tsp("cgi -> done"), ok. + %%-------------------------------------------------------------------- esi(Type, Port, Host, Node) -> %% Check "ErlScriptAlias" and "EvalScriptAlias" directives @@ -751,6 +814,8 @@ esi(Type, Port, Host, Node) -> [{statuscode, 302}, {version, "HTTP/1.0"}]), ok. + + %%-------------------------------------------------------------------- get(Type, Port, Host, Node) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Node, @@ -850,6 +915,7 @@ list_users(Node, Root, _Host, Port, Dir) -> Directory = filename:join([Root, "htdocs", Dir]), rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]). + receive_security_event(Event, Node, Port) -> %% io:format(user, "~w:receive_security_event -> entry with" %% "~n Event: ~p" @@ -860,15 +926,33 @@ receive_security_event(Event, Node, Port) -> Event -> ok; {'EXIT', _, _} -> - receive_security_event(Event, Node, Port); - Other -> - test_server:fail({unexpected_event, - {expected, Event}, {received, Other}}) + receive_security_event(Event, Node, Port) after 5000 -> - test_server:fail(no_event_recived) + %% Flush the message queue, to see if we got something... + Msgs = inets_test_lib:flush(), + tsf({expected_event_not_received, Msgs}) end. +%% receive_security_event(Event, Node, Port) -> +%% io:format(user, "~w:receive_security_event -> entry with" +%% "~n Event: ~p" +%% "~n Node: ~p" +%% "~n Port: ~p" +%% "~n", [?MODULE, Event, Node, Port]), +%% receive +%% Event -> +%% ok; +%% {'EXIT', _, _} -> +%% receive_security_event(Event, Node, Port); +%% Other -> +%% test_server:fail({unexpected_event, +%% {expected, Event}, {received, Other}}) +%% after 5000 -> +%% test_server:fail(no_event_recived) + +%% end. + list_blocked_users(Node,Port) -> Addr = undefined, % Assumed to be on the same host rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]). @@ -945,3 +1029,12 @@ check_lists_members1(L,L) -> ok; check_lists_members1(L1,L2) -> {error,{lists_not_equal,L1,L2}}. + + +%% tsp(F) -> +%% tsp(F, []). +%% tsp(F, A) -> +%% test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + +tsf(Reason) -> + test_server:fail(Reason). diff --git a/lib/inets/test/httpd_poll.erl b/lib/inets/test/httpd_poll.erl index 1cc10365a7..32335cabcf 100644 --- a/lib/inets/test/httpd_poll.erl +++ b/lib/inets/test/httpd_poll.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -27,7 +27,8 @@ %% gen_server exports -export([init/1, - handle_call/3, handle_cast/2, handle_info/2, terminate/2]). + handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). -define(default_verbosity,error). @@ -86,8 +87,8 @@ options(Options) -> options([], Defaults, Options) -> Options ++ Defaults; -options([{Key,Val} = Opt|Opts], Defaults, Options) -> - options(Opts, lists:keydelete(Key, 1, Defaults), [Opt|Options]). +options([{Key, _Val} = Opt|Opts], Defaults, Options) -> + options(Opts, lists:keydelete(Key, 1, Defaults), [Opt | Options]). verbosity(silence) -> @@ -134,10 +135,9 @@ uris(otp) -> uri_top_index(), uri_internal_product1(), uri_internal_product2(), - uri_p7a_test_results(), + uri_r13b03_test_results(), uri_bjorn1(), - uri_bjorn2(), - uri_top_ronja() + uri_bjorn2() ]. uri_top_index() -> @@ -149,9 +149,9 @@ uri_internal_product1() -> uri_internal_product2() -> {"product internal page (2)","/product/internal"}. -uri_p7a_test_results() -> - {"test summery index page", - "/product/internal/test/test_results/progress_P7A/index.html"}. +uri_r13b03_test_results() -> + {"daily build index page", + "/product/internal/test/daily/logs.html"}. uri_bjorn1() -> {"bjorns home page (1)","/~bjorn/"}. @@ -159,9 +159,6 @@ uri_bjorn1() -> uri_bjorn2() -> {"bjorns home page (2)","/~bjorn"}. -uri_top_ronja() -> - {"ronja top page","/ronja/"}. - handle_call(stop, _From, State) -> vlog("stop request"), @@ -199,7 +196,11 @@ handle_info(Info, State) -> {noreply, State}. -terminate(Reason,State) -> +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + +terminate(_Reason, State) -> tcancel(State#state.tref), log_close(get(log_file)), ok. @@ -287,16 +288,16 @@ trash_the_rest(Socket,N) -> end. -add(N1,N2) when integer(N1),integer(N2) -> +add(N1, N2) when is_integer(N1) andalso is_integer(N2) -> N1 + N2; -add(N1,N2) when integer(N1) -> +add(N1, _N2) when is_integer(N1) -> N1; -add(N1,N2) when integer(N2) -> +add(_N1, N2) when is_integer(N2) -> N2. -sz(L) when list(L) -> +sz(L) when is_list(L) -> length(lists:flatten(L)); -sz(B) when binary(B) -> +sz(B) when is_binary(B) -> size(B); sz(O) -> {unknown_size,O}. @@ -307,9 +308,9 @@ sz(O) -> %% Status code to printable string %% -status_to_message(L) when list(L) -> +status_to_message(L) when is_list(L) -> case (catch list_to_integer(L)) of - I when integer(I) -> + I when is_integer(I) -> status_to_message(I); _ -> io_lib:format("UNKNOWN STATUS CODE: '~p'",[L]) @@ -470,12 +471,12 @@ vlog(F,A) -> vprint(get(verbosity),log,F,A). verror(F) -> vprint(get(verbosity),error,F,[]). verror(F,A) -> vprint(get(verbosity),error,F,A). -vprint(trace,Severity,F,A) -> vprint(Severity,F,A); -vprint(debug,trace,F,A) -> ok; -vprint(debug,Severity,F,A) -> vprint(Severity,F,A); -vprint(log,log,F,A) -> vprint(log,F,A); -vprint(log,error,F,A) -> vprint(log,F,A); -vprint(error,error,F,A) -> vprint(error,F,A); +vprint(trace, Severity, F, A) -> vprint(Severity,F,A); +vprint(debug, trace, _F, _A) -> ok; +vprint(debug, Severity, F, A) -> vprint(Severity,F,A); +vprint(log, log, F, A) -> vprint(log,F,A); +vprint(log, error, F, A) -> vprint(log,F,A); +vprint(error, error, F, A) -> vprint(error,F,A); vprint(_Verbosity,_Severity,_F,_A) -> ok. vprint(Severity,F,A) -> @@ -491,6 +492,3 @@ image_of(trace) -> "TRC: ". local_time() -> calendar:local_time(). - - - diff --git a/lib/inets/test/httpd_test_data/server_root/Makefile b/lib/inets/test/httpd_test_data/server_root/Makefile new file mode 100644 index 0000000000..d7a3231068 --- /dev/null +++ b/lib/inets/test/httpd_test_data/server_root/Makefile @@ -0,0 +1,209 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-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/. +# +# 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. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(INETS_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +MODULE= + +AUTH_FILES = auth/group \ + auth/passwd +CGI_FILES = cgi-bin/printenv.sh +CONF_FILES = conf/8080.conf \ + conf/8888.conf \ + conf/httpd.conf \ + conf/ssl.conf \ + conf/mime.types +OPEN_FILES = htdocs/open/dummy.html +MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html +MISC_FILES = htdocs/misc/friedrich.html \ + htdocs/misc/oech.html +SECRET_FILES = htdocs/secret/dummy.html +MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html +HTDOCS_FILES = htdocs/index.html \ + htdocs/config.shtml \ + htdocs/echo.shtml \ + htdocs/exec.shtml \ + htdocs/flastmod.shtml \ + htdocs/fsize.shtml \ + htdocs/include.shtml +ICON_FILES = icons/README \ + icons/a.gif \ + icons/alert.black.gif \ + icons/alert.red.gif \ + icons/apache_pb.gif \ + icons/back.gif \ + icons/ball.gray.gif \ + icons/ball.red.gif \ + icons/binary.gif \ + icons/binhex.gif \ + icons/blank.gif \ + icons/bomb.gif \ + icons/box1.gif \ + icons/box2.gif \ + icons/broken.gif \ + icons/burst.gif \ + icons/button1.gif \ + icons/button10.gif \ + icons/button2.gif \ + icons/button3.gif \ + icons/button4.gif \ + icons/button5.gif \ + icons/button6.gif \ + icons/button7.gif \ + icons/button8.gif \ + icons/button9.gif \ + icons/buttonl.gif \ + icons/buttonr.gif \ + icons/c.gif \ + icons/comp.blue.gif \ + icons/comp.gray.gif \ + icons/compressed.gif \ + icons/continued.gif \ + icons/dir.gif \ + icons/down.gif \ + icons/dvi.gif \ + icons/f.gif \ + icons/folder.gif \ + icons/folder.open.gif \ + icons/folder.sec.gif \ + icons/forward.gif \ + icons/generic.gif \ + icons/generic.red.gif \ + icons/generic.sec.gif \ + icons/hand.right.gif \ + icons/hand.up.gif \ + icons/htdig.gif \ + icons/icon.sheet.gif \ + icons/image1.gif \ + icons/image2.gif \ + icons/image3.gif \ + icons/index.gif \ + icons/layout.gif \ + icons/left.gif \ + icons/link.gif \ + icons/movie.gif \ + icons/p.gif \ + icons/patch.gif \ + icons/pdf.gif \ + icons/pie0.gif \ + icons/pie1.gif \ + icons/pie2.gif \ + icons/pie3.gif \ + icons/pie4.gif \ + icons/pie5.gif \ + icons/pie6.gif \ + icons/pie7.gif \ + icons/pie8.gif \ + icons/portal.gif \ + icons/poweredby.gif \ + icons/ps.gif \ + icons/quill.gif \ + icons/right.gif \ + icons/screw1.gif \ + icons/screw2.gif \ + icons/script.gif \ + icons/sound1.gif \ + icons/sound2.gif \ + icons/sphere1.gif \ + icons/sphere2.gif \ + icons/star.gif \ + icons/star_blank.gif \ + icons/tar.gif \ + icons/tex.gif \ + icons/text.gif \ + icons/transfer.gif \ + icons/unknown.gif \ + icons/up.gif \ + icons/uu.gif \ + icons/uuencoded.gif \ + icons/world1.gif \ + icons/world2.gif + +SSL_FILES = ssl/ssl_client.pem \ + ssl/ssl_server.pem + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: + +clean: + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DATA) $(OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DATA) $(MISC_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/misc + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + $(INSTALL_DIR) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + $(INSTALL_DATA) $(SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/secret + $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ + $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl + $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + +release_docs_spec: + diff --git a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf index 8a74ed1afd..ceb94237d2 100644 --- a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf +++ b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# Copyright Ericsson AB 1997-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 diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 02c0200c8e..e5b0415c35 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -8,12 +8,12 @@ %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -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() @@ -72,6 +72,8 @@ 'last-modified', other=[] % list() - Key/Value list with other headers }). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%-------------------------------------------------------------------- @@ -79,41 +81,62 @@ %%------------------------------------------------------------------ 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) -> - io:format("~p ~w[~w]verify_request -> entry with" - "~n Host: ~p" - "~n Port: ~p" - "~n RequestStr: ~p" - "~n Options: ~p" - "~n", [self(), ?MODULE, ?LINE, Host, Port, RequestStr, Options]), - {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port), - 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, Reson} -> - {error, Reson}; - NewState -> - ValidateResult = validate(RequestStr, NewState, Options, - Node, Port), - inets_test_lib:close(SocketType, Socket), - ValidateResult +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; + + ConnectError -> + tsp("verify_request -> connect failed: " + "~n ~p" + "~n", [ConnectError]), + tsf({connect_failure, ConnectError}) end. -request(#state{mfa = {Module, Function, Args}, - request = RequestStr, - socket = Socket} = State, TimeOut) -> - io:format("~p ~w[~w]request -> entry with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p" - "~n", [self(), ?MODULE, ?LINE, Module, Function, Args]), +request(#state{mfa = {Module, Function, Args}, + request = RequestStr, socket = Socket} = State, TimeOut) -> + HeadRequest = lists:sublist(RequestStr, 1, 4), receive {tcp, Socket, Data} -> @@ -124,12 +147,12 @@ request(#state{mfa = {Module, Function, Args}, case Module:Function([Data | Args]) of {ok, Parsed} -> handle_http_msg(Parsed, State); - {_, whole_body, _} when HeadRequest == "HEAD" -> + {_, whole_body, _} when HeadRequest =:= "HEAD" -> State#state{body = <<>>}; NewMFA -> request(State#state{mfa = NewMFA}, TimeOut) end; - {tcp_closed, Socket} when Function == whole_body -> + {tcp_closed, Socket} when Function =:= whole_body -> io:format("~p ~w[~w]request -> " "received (tcp) closed when whole_body" "~n", [self(), ?MODULE, ?LINE]), @@ -149,12 +172,12 @@ request(#state{mfa = {Module, Function, Args}, case Module:Function([Data | Args]) of {ok, Parsed} -> handle_http_msg(Parsed, State); - {_, whole_body, _} when HeadRequest == "HEAD" -> + {_, whole_body, _} when HeadRequest =:= "HEAD" -> State#state{body = <<>>}; NewMFA -> request(State#state{mfa = NewMFA}, TimeOut) end; - {ssl_closed, Socket} when Function == whole_body -> + {ssl_closed, Socket} when Function =:= whole_body -> print(ssl, "closed", State), State#state{body = hd(Args)}; {ssl_closed, Socket} -> @@ -236,7 +259,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, _} -> @@ -363,3 +389,11 @@ print(Proto, Data, #state{print = true}) -> print(_, _, #state{print = false}) -> ok. + +tsp(F) -> + inets_test_lib:tsp(F). +tsp(F, A) -> + inets_test_lib:tsp(F, A). + +tsf(Reason) -> + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_time_test.erl b/lib/inets/test/httpd_time_test.erl index 7d6aa08542..f39f9faff0 100644 --- a/lib/inets/test/httpd_time_test.erl +++ b/lib/inets/test/httpd_time_test.erl @@ -1,25 +1,25 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% -module(httpd_time_test). --export([t/3, t1/2, t2/2]). +-export([t/3, t1/2, t2/2, t3/2, t4/2]). -export([do/1, do/2, do/3, do/4, do/5]). @@ -29,6 +29,9 @@ -record(stat, {pid, time = undefined, count = undefined, res}). +%% -define(NUM_POLLERS, 10). +-define(NUM_POLLERS, 1). + %%% ----------------------------------------------------------------- %%% Test suite interface @@ -42,9 +45,17 @@ t2(Host, Port) -> t(ssl, Host, Port). +t3(Host, Port) -> + t(ossl, Host, Port). + + +t4(Host, Port) -> + t(essl, Host, Port). + + t(SocketType, Host, Port) -> %% put(dbg,true), - main(1, SocketType, Host, Port, 60000). + main(?NUM_POLLERS, SocketType, Host, Port, 60000). @@ -111,28 +122,40 @@ loop(Pollers, Timeout) -> "~n Timeout: ~p", [Timeout]), Start = t(), receive - {'EXIT', Pid, {poller_stat_failure, Time, Reason}} -> + {'EXIT', Pid, {poller_stat_failure, SocketType, Host, Port, Time, Reason}} -> case is_poller(Pid, Pollers) of true -> error_msg("received unexpected exit from poller ~p~n" "befor completion of test " - "(after ~p micro sec):~n" - "~p~n", [Pid,Time,Reason]), - exit({fail, {poller_exit, Pid, Reason}}); + "after ~p micro sec" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p" + "~n~p~n", + [Pid, SocketType, Host, Port, Time, Reason]), + exit({fail, {poller_exit, Pid, Time, Reason}}); false -> error_msg("received unexpected ~p from ~p" "befor completion of test", [Reason, Pid]), loop(Pollers, to(Timeout, Start)) end; - {poller_stat_failure, Pid, {Time, Reason}} -> + {poller_stat_failure, Pid, {SocketType, Host, Port, Time, Reason}} -> error_msg("received stat failure ~p from poller ~p after ~p " - "befor completion of test", [Reason, Pid, Time]), - exit({fail, {poller_failure, Pid, Reason}}); - - {poller_stat_failure, Pid, Reason} -> + "befor completion of test" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p", + [Reason, Pid, Time, SocketType, Host, Port]), + exit({fail, {poller_failure, Pid, Time, Reason}}); + + {poller_stat_failure, Pid, SocketType, Host, Port, Reason} -> error_msg("received stat failure ~p from poller ~p " - "befor completion of test", [Reason, Pid]), + "befor completion of test" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p", + [Reason, Pid, SocketType, Host, Port]), exit({fail, {poller_failure, Pid, Reason}}); Any -> @@ -250,16 +273,16 @@ is_poller(Pid, [_|Rest]) -> poller_main(Parent, SocketType, Host, Port) -> process_flag(trap_exit,true), - put(sname,poller), + put(sname, poller), case timer:tc(?MODULE, poller_loop, [SocketType, Host, Port, uris()]) of {Time, Count} when is_integer(Time) andalso is_integer(Count) -> Parent ! {poller_statistics, self(), {Time, Count}}; {Time, {'EXIT', Reason}} when is_integer(Time) -> - exit({poller_stat_failure, Time, Reason}); + exit({poller_stat_failure, SocketType, Host, Port, Time, Reason}); {Time, Other} when is_integer(Time) -> - Parent ! {poller_stat_failure, self(), {Time, Other}}; + Parent ! {poller_stat_failure, self(), {SocketType, Host, Port, Time, Other}}; Else -> - Parent ! {poller_stat_failure, self(), Else} + Parent ! {poller_stat_failure, self(), SocketType, Host, Port, Else} end. diff --git a/lib/inets/test/inets.cover b/lib/inets/test/inets.cover new file mode 100644 index 0000000000..fd0ca41db3 --- /dev/null +++ b/lib/inets/test/inets.cover @@ -0,0 +1,2 @@ +{incl_app,inets,details}. + diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec index ba525f62c1..ed102f8219 100644 --- a/lib/inets/test/inets.spec +++ b/lib/inets/test/inets.spec @@ -1,2 +1 @@ -{topcase, {dir, "../inets_test"}}. -{hosts, ["fobi"]}. +{suites,"../inets_test",all}. diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl index 56983caace..6fa0f44d77 100644 --- a/lib/inets/test/inets_SUITE.erl +++ b/lib/inets/test/inets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -19,7 +19,7 @@ %% -module(inets_SUITE). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). -include("inets_test_lib.hrl"). @@ -28,25 +28,26 @@ -define(NUM_DEFAULT_SERVICES, 1). -all(doc) -> - ["Test suites for the inets application."]; +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, app_test}, {group, appup_test}, + {group, services_test}, httpd_reload]. + +groups() -> + [{services_test, [], + [start_inets, start_httpc, start_httpd, start_ftpc, + start_tftpd]}, + {app_test, [], [{inets_app_test, all}]}, + {appup_test, [], [{inets_appup_test, all}]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [ - app_test, - appup_test, - services_test, - httpd_reload - ]. -services_test(suite) -> - [ - start_inets, - start_httpc, - start_httpd, - start_ftpc, - start_tftpd - ]. %%-------------------------------------------------------------------- @@ -100,11 +101,7 @@ end_per_testcase(_, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -app_test(suite) -> - [{inets_app_test, all}]. -appup_test(suite) -> - [{inets_appup_test, all}]. %%------------------------------------------------------------------------- diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl index 6bdb9bb308..9d7202e087 100644 --- a/lib/inets/test/inets_app_test.erl +++ b/lib/inets/test/inets_app_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 @@ -39,28 +39,31 @@ init_per_testcase(undef_funcs, Config) -> init_per_testcase(_, Config) -> Config. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> - Cases = - [ - fields, - modules, - exportall, - app_depend, - undef_funcs - ], - {req, [], {conf, app_init, Cases, app_fin}}. +all() -> + [fields, modules, exportall, app_depend, + undef_funcs]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -app_init(suite) -> []; -app_init(doc) -> []; -app_init(Config) when is_list(Config) -> +init_per_suite(suite) -> []; +init_per_suite(doc) -> []; +init_per_suite(Config) when is_list(Config) -> case is_app(inets) of {ok, AppFile} -> io:format("AppFile: ~n~p~n", [AppFile]), @@ -81,9 +84,9 @@ is_app(App) -> end. -app_fin(suite) -> []; -app_fin(doc) -> []; -app_fin(Config) when is_list(Config) -> +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config) when is_list(Config) -> Config. @@ -238,6 +241,20 @@ undef_funcs(suite) -> undef_funcs(doc) -> []; undef_funcs(Config) when is_list(Config) -> + %% We need to check if there is a point to run this test. + %% On some platforms, crypto will not build, which in turn + %% causes ssl to not to not build (at this time, this will + %% change in the future). + %% So, we first check if we can start crypto, and if not, + %% we skip this test case! + case (catch crypto:start()) of + ok -> + ok; + {error, {already_started, crypto}} -> + ok; + _ -> + ?SKIP(crypto_start_check_failed) + end, App = inets, AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl index d580c6c4c5..7ed237243e 100644 --- a/lib/inets/test/inets_appup_test.erl +++ b/lib/inets/test/inets_appup_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 @@ -18,40 +18,47 @@ %% %% %%---------------------------------------------------------------------- -%% Purpose: Verify the application specifics of the Megaco application +%% Purpose: Verify the application specifics of the Inets application %%---------------------------------------------------------------------- -module(inets_appup_test). -compile(export_all). +-compile({no_auto_import,[error/1]}). -include("inets_test_lib.hrl"). -% t() -> megaco_test_lib:t(?MODULE). -% t(Case) -> megaco_test_lib:t({?MODULE, Case}). + % t() -> megaco_test_lib:t(?MODULE). + % t(Case) -> megaco_test_lib:t({?MODULE, Case}). %% Test server callbacks init_per_testcase(_Case, Config) -> Config. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> - Cases = - [ - appup - ], - {req, [], {conf, appup_init, Cases, appup_fin}}. +all() -> + [appup]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup_init(suite) -> []; -appup_init(doc) -> []; -appup_init(Config) when is_list(Config) -> +init_per_suite(suite) -> []; +init_per_suite(doc) -> []; +init_per_suite(Config) when is_list(Config) -> AppFile = file_name(inets, ".app"), AppupFile = file_name(inets, ".appup"), [{app_file, AppFile}, {appup_file, AppupFile}|Config]. @@ -62,9 +69,9 @@ file_name(App, Ext) -> filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). -appup_fin(suite) -> []; -appup_fin(doc) -> []; -appup_fin(Config) when is_list(Config) -> +end_per_suite(suite) -> []; +end_per_suite(doc) -> []; +end_per_suite(Config) when is_list(Config) -> Config. diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl index ba41e0960c..1d262a2739 100644 --- a/lib/inets/test/inets_sup_SUITE.erl +++ b/lib/inets/test/inets_sup_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% 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 @@ -20,22 +20,27 @@ -module(inets_sup_SUITE). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). -all(doc) -> - ["Test that the inets supervisorstructur is the expected one."]; -all(suite) -> - [ - default_tree, - ftpc_worker, - tftpd_worker, - httpd_subtree, - httpc_subtree - ]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [default_tree, ftpc_worker, tftpd_worker, httpd_subtree, + httpc_subtree]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config @@ -372,11 +377,11 @@ httpc_subtree(Config) when is_list(Config) -> "~n Config: ~p", [Config]), tsp("httpc_subtree -> start inets service httpc with profile foo"), - {ok, Foo} = inets:start(httpc, [{profile, foo}]), + {ok, _Foo} = inets:start(httpc, [{profile, foo}]), tsp("httpc_subtree -> " "start stand-alone inets service httpc with profile bar"), - {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone), + {ok, _Bar} = inets:start(httpc, [{profile, bar}], stand_alone), tsp("httpc_subtree -> retreive list of httpc instances"), HttpcChildren = supervisor:which_children(httpc_profile_sup), diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 609bc89e15..2e19c41f16 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -1,93 +1,184 @@ %% %% %CopyrightBegin% -%% +%% %% Copyright Ericsson AB 2001-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% -module(inets_test_lib). -include("inets_test_lib.hrl"). +-include_lib("inets/src/http_lib/http_internal.hrl"). %% Various small utility functions --export([start_http_server/1, start_http_server_ssl/1]). +-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([non_pc_tc_maybe_skip/4, os_based_skip/1]). - -start_http_server(Conf) -> - ?DEBUG("start_http_server -> entry with" - "~n Conf: ~p", [Conf]), - inets_ensure_loaded(), - inets_set_env(services, [{httpd, Conf}]), - inets_ensure_started(), - ok. - - -start_http_server_ssl(FileName) -> - application:start(ssl), - catch start_http_server(FileName). - -inets_ensure_loaded() -> - ensure_loaded(inets). +-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, 1, 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 -ensure_loaded(App) -> - case application:load(App) of - ok -> - ok; - {error, {already_loaded, _}} -> - ok; - LoadRes -> - ?LOG("start_http_server -> failed loading ~p: ~p", [App, LoadRes]), - tsf({failed_loading, LoadRes}) end. +oscmd(Cmd) -> + string:strip(os:cmd(Cmd), right, $\n). + +%% -- Misc node operation wrapper functions -- + +start_node(Name) -> + Pa = filename:dirname(code:which(?MODULE)), + Args = case init:get_argument('CC_TEST') of + {ok, [[]]} -> + " -pa /clearcase/otp/libraries/snmp/ebin "; + {ok, [[Path]]} -> + " -pa " ++ Path; + error -> + "" + end, + A = Args ++ " -pa " ++ Pa, + Opts = [{cleanup,false}, {args, A}], + case (catch test_server:start_node(Name, slave, Opts)) of + {ok, Node} -> + Node; + Else -> + exit({failed_starting_node, Name, Else}) + end. -inets_set_env(Service, Config) -> - app_set_env(inets, Service, Config). +stop_node(Node) -> + rpc:cast(Node, erlang, halt, []), + await_stopped(Node, 5). -app_set_env(App, Param, Value) -> - case application:set_env(App, Param, Value) of - ok -> - ?DEBUG("start_http_server -> env set", []), - ok; - SetEnvRes -> - ?LOG("start_http_server -> failed set env for ~p: ~p", - [App, SetEnvRes]), - exit({failed_set_env, App, SetEnvRes}) +await_stopped(_, 0) -> + ok; +await_stopped(Node, N) -> + Nodes = erlang:nodes(), + case lists:member(Node, Nodes) of + true -> + sleep(1000), + await_stopped(Node, N-1); + false -> + ok end. -inets_ensure_started() -> - ensure_app_started(inets). -ensure_app_started(App) -> - case application:start(App) of +%% ---------------------------------------------------------------- +%% HTTPD starter functions +%% + +start_http_server(Conf) -> + start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND). + +start_http_server(Conf, essl = _SslTag) -> + tsp("start_http_server(essl) -> entry - try start crypto and public_key"), + application:start(crypto), + application:start(public_key), + do_start_http_server(Conf); +start_http_server(Conf, SslTag) -> + tsp("start_http_server(~w) -> entry", [SslTag]), + do_start_http_server(Conf). + +do_start_http_server(Conf) -> + tsp("do_start_http_server -> entry with" + "~n Conf: ~p" + "~n", [Conf]), + application:load(inets), + case application:set_env(inets, services, [{httpd, Conf}]) of ok -> - ?DEBUG("start_http_server -> ~p started", [App]), - ok; - {error, {already_started, _}} -> - ok; - StartRes -> - ?LOG("start_http_server -> failed starting ~p: ~p", - [App, StartRes]), - exit({failed_starting, App, StartRes}) + tsp("start_http_server -> httpd conf stored in inets app env"), + case application:start(inets) of + ok -> + tsp("start_http_server -> inets started"), + ok; + Error1 -> + tsp("<ERROR> Failed starting application: " + "~n Error1: ~p", [Error1]), + Error1 + end; + Error2 -> + tsp("<ERROR> Failed set application env: " + "~n Error: ~p", [Error2]), + Error2 end. + +start_http_server_ssl(FileName) -> + start_http_server_ssl(FileName, ?HTTP_DEFAULT_SSL_KIND). + +start_http_server_ssl(FileName, essl = _SslTag) -> + application:start(crypto), + do_start_http_server_ssl(FileName); +start_http_server_ssl(FileName, _SslTag) -> + do_start_http_server_ssl(FileName). + +do_start_http_server_ssl(FileName) -> + tsp("start (ssl) http server with " + "~n FileName: ~p" + "~n", [FileName]), + application:start(ssl), + catch do_start_http_server(FileName). %% ---------------------------------------------------------------------- @@ -134,27 +225,17 @@ copy_files(FromDir, ToDir) -> copy_dirs(FromDirRoot, ToDirRoot) -> -%% io:format("~w:copy_dirs -> entry with" -%% "~n FromDirRoot: ~p" -%% "~n ToDirRoot: ~p" -%% "~n", [?MODULE, FromDirRoot, ToDirRoot]), {ok, Files} = file:list_dir(FromDirRoot), lists:foreach( fun(FileOrDir) -> %% Check if it's a directory or a file -%% io:format("~w:copy_dirs -> check ~p" -%% "~n", [?MODULE, FileOrDir]), case filelib:is_dir(filename:join(FromDirRoot, FileOrDir)) of true -> -%% io:format("~w:copy_dirs -> ~p is a directory" -%% "~n", [?MODULE, FileOrDir]), FromDir = filename:join([FromDirRoot, FileOrDir]), ToDir = filename:join([ToDirRoot, FileOrDir]), ok = file:make_dir(ToDir), copy_dirs(FromDir, ToDir); false -> -%% io:format("~w:copy_dirs -> ~p is a file" -%% "~n", [?MODULE, FileOrDir]), copy_file(FileOrDir, FromDirRoot, ToDirRoot) end end, Files). @@ -183,8 +264,8 @@ check_body(Body) -> 0 -> case string:rstr(Body, "</HTML>") of 0 -> - test_server:format("Body ~p~n", [Body]), - test_server:fail(did_not_receive_whole_body); + tsp("Body ~p", [Body]), + tsf(did_not_receive_whole_body); _ -> ok end; @@ -253,10 +334,48 @@ os_based_skip(_) -> %% Host -> atom() | string() | {A, B, C, D} %% Port -> integer() -connect_bin(ssl, Host, Port) -> +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, 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, [binary, {packet,0}]) of + case ssl:connect(Host, Port, Opts) of {ok, Socket} -> {ok, Socket}; {error, Reason} -> @@ -264,61 +383,58 @@ connect_bin(ssl, Host, Port) -> Error -> Error end; -connect_bin(ip_comm, Host, Port) -> - Opts = [inet6, binary, {packet,0}], - connect(ip_comm, Host, Port, Opts). - - connect(ip_comm, Host, Port, Opts) -> - test_server:format("gen_tcp:connect(~p, ~p, ~p) ~n", [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} -> - test_server:format("connect success~n", []), + tsp("connect success"), {ok, Socket}; {error, nxdomain} -> - test_server:format("nxdomain opts: ~p~n", [Opts]), + tsp("connect error nxdomain when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, eafnosupport} -> - test_server:format("eafnosupport opts: ~p~n", [Opts]), + tsp("connect error eafnosupport when opts: ~p", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, econnreset} -> + tsp("connect error econnreset when opts: ~p", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, enetunreach} -> + tsp("connect error eafnosupport when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, {enfile,_}} -> - test_server:format("Error enfile~n", []), + tsp("connect error enfile when opts: ~p", [Opts]), {error, enfile}; Error -> - test_server:format("Unexpected error: " - "~n Error: ~p" - "~nwhen" - "~n Host: ~p" - "~n Port: ~p" - "~n Opts: ~p" - "~n", [Error, Host, Port, Opts]), + tsp("Unexpected error: " + "~n Error: ~p" + "~nwhen" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p" + "~n", [Error, Host, Port, Opts]), Error end. -connect_byte(ip_comm, Host, Port) -> - Opts = [inet6, {packet,0}], - connect(ip_comm, Host, Port, Opts); - -connect_byte(ssl, Host, Port) -> - ssl:start(), - %% Does not support ipv6 in old ssl - case ssl:connect(Host,Port,[{packet,0}]) of - {ok,Socket} -> - {ok,Socket}; - {error,{enfile,_}} -> - {error, enfile}; - Error -> - Error - end. send(ssl, Socket, Data) -> ssl:send(Socket, Data); +send(ossl, Socket, Data) -> + ssl:send(Socket, Data); +send(essl, Socket, Data) -> + ssl:send(Socket, Data); send(ip_comm,Socket,Data) -> gen_tcp:send(Socket,Data). close(ssl,Socket) -> catch ssl:close(Socket); +close(ossl,Socket) -> + catch ssl:close(Socket); +close(essl,Socket) -> + catch ssl:close(Socket); close(ip_comm,Socket) -> catch gen_tcp:close(Socket). @@ -351,5 +467,41 @@ sleep(MSecs) -> skip(Reason, File, Line) -> exit({skipped, {Reason, File, Line}}). +fail(Reason, File, Line) -> + String = lists:flatten(io_lib:format("Failure ~p(~p): ~p~n", + [File, Line, Reason])), + tsf(String). + + + +flush() -> + receive + Msg -> + [Msg | flush()] + after 1000 -> + [] + end. + + +tsp(F) -> + tsp(F, []). +tsp(F, 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). + diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index 12a43fa136..4dd81093a2 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-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/. -%% +%% %% 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. -%% +%% %% %CopyrightEnd% %% %% @@ -46,6 +46,11 @@ -endif. +%% - OS Command and stuff + +-define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)). + + %% - Test case macros - -define(EXPANDABLE(I, C, F), inets_test_lib:expandable(I, C, F)). @@ -67,7 +72,7 @@ %% - Test case macros - --define(SKIP(Reason), inets_test_lib:skip(Reason)). +-define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)). -define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/inets/test/tftp_SUITE.erl index 5768fff88b..59fb644667 100644 --- a/lib/inets/test/tftp_SUITE.erl +++ b/lib/inets/test/tftp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-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 @@ -64,24 +64,34 @@ default_config() -> init_per_testcase(Case, Config) -> tftp_test_lib:init_per_testcase(Case, Config). -fin_per_testcase(Case, Config) when is_list(Config) -> - tftp_test_lib:fin_per_testcase(Case, Config). +end_per_testcase(Case, Config) when is_list(Config) -> + tftp_test_lib:end_per_testcase(Case, Config). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Top test case %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Test suites for TFTP."]; - -all(suite) -> - [ - simple, - extra, - reuse_connection, - resend_client, - resend_server - ]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [simple, extra, reuse_connection, resend_client, + resend_server]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Simple diff --git a/lib/inets/test/tftp_test_lib.erl b/lib/inets/test/tftp_test_lib.erl index 3729309b0e..e9b691828f 100644 --- a/lib/inets/test/tftp_test_lib.erl +++ b/lib/inets/test/tftp_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% 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 @@ -32,7 +32,7 @@ init_per_testcase(_Case, Config) when is_list(Config) -> ?IGNORE(application:stop(inets)), Config. -fin_per_testcase(_Case, Config) when is_list(Config) -> +end_per_testcase(_Case, Config) when is_list(Config) -> ?IGNORE(application:stop(inets)), Config. @@ -143,7 +143,7 @@ eval(Mod, Fun, Config) -> Config2 = Mod:init_per_testcase(Fun, Config), Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]), R = wait_for_evaluator(Pid, Mod, Fun, Config2, []), - Mod:fin_per_testcase(Fun, Config2), + Mod:end_per_testcase(Fun, Config2), global:unregister_name(tftp_test_case_sup), process_flag(trap_exit, Flag), R. diff --git a/lib/inets/test/tftp_test_lib.hrl b/lib/inets/test/tftp_test_lib.hrl index da4b065976..bef024720a 100644 --- a/lib/inets/test/tftp_test_lib.hrl +++ b/lib/inets/test/tftp_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% 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 diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index f085c89008..d294d0006e 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -1,84 +1,24 @@ +#-*-makefile-*- ; force emacs to enter makefile-mode + +# %CopyrightBegin% +# +# Copyright Ericsson AB 2001-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/. +# +# 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. +# +# %CopyrightEnd% + APPLICATION = inets -INETS_VSN = 5.3.6 +INETS_VSN = 5.7.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -TICKETS = OTP-9674 - -TICKETS_5_3_5 = OTP-8940 - -TICKETS_5_3_4 = \ - OTP-8739 \ - OTP-8741 \ - OTP-8742 - -TICKETS_5_3_3 = \ - OTP-8609 \ - OTP-8610 \ - OTP-8624 - -TICKETS_5_3_2 = \ - OTP-8542 \ - OTP-8607 - -TICKETS_5_3_1 = \ - OTP-8508 \ - OTP-8509 - -TICKETS_5_3 = \ - OTP-8016 \ - OTP-8056 \ - OTP-8103 \ - OTP-8106 \ - OTP-8312 \ - OTP-8315 \ - OTP-8327 \ - OTP-8349 \ - OTP-8351 \ - OTP-8352 \ - OTP-8359 \ - OTP-8371 - -TICKETS_5_2 = \ - OTP-8204 \ - OTP-8206 \ - OTP-8247 \ - OTP-8248 \ - OTP-8249 \ - OTP-8258 \ - OTP-8280 - -TICKETS_5_1_3 = \ - OTP-8154 - -TICKETS_5_1_2 = \ - OTP-7298 \ - OTP-8101 \ - OTP-8118 - -TICKETS_5_1_1 = \ - OTP-8052 \ - OTP-8069 - -TICKETS_5_1 = \ - OTP-7994 \ - OTP-7998 \ - OTP-8001 \ - OTP-8004 \ - OTP-8005 - -TICKETS_5_0_14 = \ - OTP-7882 \ - OTP-7883 \ - OTP-7888 \ - OTP-7950 \ - OTP-7976 - -TICKETS_5.0.13 = \ - OTP-7723 \ - OTP-7724 \ - OTP-7726 \ - OTP-7463 \ - OTP-7815 \ - OTP-7857 - |