aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets')
-rw-r--r--lib/inets/doc/src/http_server.xml31
-rw-r--r--lib/inets/doc/src/http_uri.xml10
-rw-r--r--lib/inets/doc/src/httpc.xml10
-rw-r--r--lib/inets/doc/src/httpd.xml254
-rw-r--r--lib/inets/doc/src/notes.xml99
-rw-r--r--lib/inets/doc/src/tftp.xml21
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl85
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl184
-rw-r--r--lib/inets/src/http_lib/http_response.erl2
-rw-r--r--lib/inets/src/http_lib/http_transport.erl206
-rw-r--r--lib/inets/src/http_lib/http_util.erl69
-rw-r--r--lib/inets/src/http_server/Makefile2
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl101
-rw-r--r--lib/inets/src/http_server/httpd_custom_api.erl3
-rw-r--r--lib/inets/src/http_server/httpd_example.erl10
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl25
-rw-r--r--lib/inets/src/http_server/httpd_response.erl27
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl4
-rw-r--r--lib/inets/src/http_server/httpd_util.erl28
-rw-r--r--lib/inets/src/http_server/mod_esi.erl47
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/test/http_format_SUITE.erl238
-rw-r--r--lib/inets/test/httpc_SUITE.erl76
-rw-r--r--lib/inets/test/httpd_1_1.erl11
-rw-r--r--lib/inets/test/httpd_SUITE.erl33
-rw-r--r--lib/inets/test/httpd_SUITE_data/Makefile.src5
-rw-r--r--lib/inets/test/httpd_test_lib.erl16
-rw-r--r--lib/inets/vsn.mk2
28 files changed, 784 insertions, 816 deletions
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index 4b6d64fc8f..aeda961714 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,18 +21,8 @@
limitations under the License.
</legalnotice>
-
<title>HTTP server</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible></responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date></date>
- <rev></rev>
<file>http_server.xml</file>
-
- <marker id="intro"></marker>
</header>
<section>
@@ -65,12 +55,9 @@
<p>As of <c>Inets</c> 5.0 the HTTP server is an easy to
start/stop and customize web server providing the most basic
- web server functionality. Depending on your needs, there
- are also other Erlang-based web servers that can be of interest
- such as <url href=" http://yaws.hyber.org ">Yaws</url>, which,
- for example, has its own
- markup support to generate HTML and supports certain buzzword
- technologies, such as SOAP.</p>
+ web server functionality. Inets is designed for embedded systems
+ and if you want a full-fledged web server there are exists other
+ erlang open source alternatives.</p>
<p>Almost all server functionality has been implemented using an
especially crafted server API, which is described in the Erlang Web
@@ -356,9 +343,9 @@ UserName:Password</pre>
</taglist>
<section>
- <title>CGI Version 1.1,
- <url href="http://www.ietf.org/rfc/rfc3875.txt">RFC 3875</url></title>
- <p>The module <c>mod_cgi</c> enables execution of CGI scripts
+ <title>CGI Version 1.1, RFC 3875</title>
+ <p>The module <c>mod_cgi</c> enables execution of
+ <url href="http://www.ietf.org/rfc/rfc3875.txt">CGI scripts</url>
on the server. A file matching the definition of a
ScriptAlias config directive is treated as a CGI script. A CGI
script is executed by the server and its output is returned to
@@ -541,7 +528,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<title>mod_action - Filetype/Method-Based Script Execution</title>
<p>This module runs CGI scripts whenever a file of a
certain type or HTTP method (see
- <url href="http://tools.ietf.org/html/rfc1945">RFC 1945</url>RFC 1945)
+ <url href="http://tools.ietf.org/html/rfc1945">RFC 1945</url>)
is requested.
</p>
<p>Uses the following Erlang Web Server API interaction data:
@@ -568,7 +555,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<taglist>
<tag><c>{real_name, PathData}</c></tag>
<item><c>PathData</c> is the argument used for API function
- <seealso marker="mod_alias:path/3">mod_alias:path/3</seealso>.</item>
+ <seealso marker="mod_alias#path/3">mod_alias:path/3</seealso>.</item>
</taglist>
</section>
diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml
index 64e6c7a6cc..8e0301c520 100644
--- a/lib/inets/doc/src/http_uri.xml
+++ b/lib/inets/doc/src/http_uri.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year><year>2013</year>
+ <year>2012</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -142,14 +142,14 @@
<p>If the fragment option is <c>true</c>, the URI fragment is returned as
part of the parsing result, otherwise it is ignored.</p>
- <p>Scheme validation fun is to be defined as follows:
+ <p>Scheme validation fun is to be defined as follows:</p>
- <code>
+ <code>
fun(SchemeStr :: string()) ->
valid | {error, Reason :: term()}.
- </code>
+ </code>
- It is called before scheme string gets converted into scheme atom and
+ <p>It is called before scheme string gets converted into scheme atom and
thus possible atom leak could be prevented</p>
<marker id="encode"></marker>
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 31e44f405c..ca9b268a03 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -554,8 +554,8 @@
<v>IpDesc = string()</v>
<d>Example: "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>
- <d><c>proxy</c> defaults to <c>{undefined, []}</c>,
+ "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP address).
+ <c>proxy</c> defaults to <c>{undefined, []}</c>,
that is, no proxy is configured and
<c>https_proxy</c> defaults to the value of <c>proxy</c>.</d>
<v>MaxSessions = integer()</v>
@@ -597,8 +597,8 @@
for details.</d>
<v>socket_opts() = [socket_opt()]</v>
<d>The options are appended to the socket options used by the
- client.</d>
- <d>These are the default values when a new request handler
+ client.
+ These are the default values when a new request handler
is started (for the initial connect). They are passed directly
to the underlying transport (<c>gen_tcp</c> or <c>SSL</c>)
<em>without</em> verification.</d>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 2a4aea41c2..62b92b8356 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -30,13 +30,14 @@
<file>httpd.sgml</file>
</header>
<module>httpd</module>
- <modulesummary>An implementation of an HTTP
- 1.1 compliant web server, as defined in <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>
+ <modulesummary>
+ HTTP server API
</modulesummary>
<description>
- <p>This module provides the HTTP server start options, some administrative
- functions, and specifies the Erlang web server callback
- API.</p>
+ <p>An implementation of an HTTP 1.1 compliant web server, as defined in
+ <url href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</url>.
+ Provides web server start options, administrative functions, and
+ an Erlang callback API.</p>
</description>
<section>
@@ -78,8 +79,7 @@
list.</p>
<taglist>
- <marker id="prop_proplist_file"></marker>
- <tag>{proplist_file, path()}</tag>
+ <tag><marker id="prop_proplist_file"></marker>{proplist_file, path()}</tag>
<item>
<p>If this property is defined, <c>Inets</c> expects to find
all other properties defined in this file. The
@@ -87,8 +87,7 @@
properties.</p>
</item>
- <marker id="prop_file"></marker>
- <tag>{file, path()}</tag>
+ <tag><marker id="prop_file"></marker>{file, path()}</tag>
<item>
<p>If this property is defined, <c>Inets</c> expects to find all
other properties defined in this file, which uses Apache-like
@@ -121,8 +120,7 @@
<marker id="props_mand"></marker>
<p><em>Mandatory Properties</em></p>
<taglist>
- <marker id="prop_port"></marker>
- <tag>{port, integer()} </tag>
+ <tag><marker id="prop_port"></marker>{port, integer()} </tag>
<item>
<p>The port that the HTTP server listen to.
If zero is specified as port, an arbitrary available port
@@ -130,22 +128,19 @@
determine which port was picked.</p>
</item>
- <marker id="prop_server_name"></marker>
- <tag>{server_name, string()}</tag>
+ <tag><marker id="prop_server_name"></marker>{server_name, string()}</tag>
<item>
<p>The name of your server, normally a fully qualified domain name.</p>
</item>
- <marker id="prop_server_root"></marker>
- <tag>{server_root, path()}</tag>
+ <tag><marker id="prop_server_root"></marker>{server_root, path()}</tag>
<item>
<p>Defines the home directory of the server, where log files, and so on,
can be stored. Relative paths specified in other properties refer
to this directory.</p>
</item>
- <marker id="prop_doc_root"></marker>
- <tag>{document_root, path()}</tag>
+ <tag> <marker id="prop_doc_root"></marker>{document_root, path()}</tag>
<item>
<p>Defines the top directory for the documents that
are available on the HTTP server.</p>
@@ -155,15 +150,13 @@
<marker id="props_comm"></marker>
<p><em>Communication Properties</em></p>
<taglist>
- <marker id="prop_bind_address"></marker>
- <tag>{bind_address, ip_address() | hostname() | any}</tag>
+ <tag><marker id="prop_bind_address"></marker>{bind_address, ip_address() | hostname() | any}</tag>
<item>
<p>Default is <c>any</c>. <c>any</c> is denoted <em>*</em>
in the Apache-like configuration file.</p>
</item>
- <marker id="profile"></marker>
- <tag>{profile, atom()}</tag>
+ <tag><marker id="profile"></marker>{profile, atom()}</tag>
<item>
<p>Used together with <seealso marker="#prop_bind_address"><c>bind_address</c></seealso>
and <seealso marker="#prop_port"><c>port</c></seealso> to uniquely identify
@@ -176,25 +169,23 @@
</p>
</item>
- <marker id="prop_socket_type"></marker>
- <tag>{socket_type, ip_comm | {essl, Config::proplist()}}</tag>
+ <tag><marker id="prop_socket_type"></marker>{socket_type, ip_comm | {ip_comm, Config::proplist()} | {essl, Config::proplist()}}</tag>
<item>
+ <p>For <c>ip_comm</c> configuration options, see
+ <seealso marker="kernel:gen_tcp#listen-2">gen_tcp:listen/2</seealso>, some options
+ that are used internally by httpd can not be set.</p>
<p>For <c>SSL</c> configuration options, see
<seealso marker="ssl:ssl#listen-2">ssl:listen/2</seealso>.</p>
<p>Default is <c>ip_comm</c>.</p>
</item>
- <marker id="prop_ipfamily"></marker>
- <tag>{ipfamily, inet | inet6 | inet6fb4}</tag>
+ <tag><marker id="prop_ipfamily"></marker>{ipfamily, inet | inet6}</tag>
<item>
- <p>This option is only used when option
- <c>socket_type</c> has value <c>ip_comm</c>.</p>
- <p>Default is <c>inet6fb4</c>.</p>
+ <p>Default is <c>inet</c>, legacy option <c>inet6fb4</c> no longer makes sense and will be translated
+ to inet.</p>
</item>
-
- <marker id="prop_minimum_bytes_per_second"></marker>
- <tag>{minimum_bytes_per_second, integer()}</tag>
+ <tag><marker id="prop_minimum_bytes_per_second"></marker>{minimum_bytes_per_second, integer()}</tag>
<item>
<p>If given, sets a minimum of bytes per second value for connections.</p>
<p>If the value is unreached, the socket closes for that connection.</p>
@@ -205,8 +196,7 @@
<marker id="props_api_modules"></marker>
<p><em>Erlang Web Server API Modules</em> </p>
<taglist>
- <marker id="prop_modules"></marker>
- <tag>{modules, [atom()]} </tag>
+ <tag><marker id="prop_modules"></marker>{modules, [atom()]} </tag>
<item>
<p>Defines which modules the HTTP server uses when handling
requests. Default is <c>[mod_alias, mod_auth, mod_esi,
@@ -223,60 +213,52 @@
<p><em>Limit properties</em> </p>
<taglist>
- <marker id="prop_customize"></marker>
- <tag>{customize, atom()}</tag>
+ <tag><marker id="prop_customize"></marker>{customize, atom()}</tag>
<item>
<p>A callback module to customize the inets HTTP servers behaviour
see <seealso marker="httpd_custom_api"> httpd_custom_api</seealso> </p>
</item>
- <marker id="prop_disable_chunked_encoding"></marker>
- <tag>{disable_chunked_transfer_encoding_send, boolean()}</tag>
+ <tag><marker id="prop_disable_chunked_encoding"></marker>{disable_chunked_transfer_encoding_send, boolean()}</tag>
<item>
<p>Allows you to disable chunked
transfer-encoding when sending a response to an HTTP/1.1
client. Default is <c>false</c>.</p>
</item>
- <marker id="prop_keep_alive"></marker>
- <tag>{keep_alive, boolean()}</tag>
+ <tag><marker id="prop_keep_alive"></marker>{keep_alive, boolean()}</tag>
<item>
<p>Instructs the server whether to use persistent
connections when the client claims to be HTTP/1.1
compliant. Default is <c>true</c>.</p>
</item>
- <marker id="prop_keep_alive_timeout"></marker>
- <tag>{keep_alive_timeout, integer()}</tag>
+ <tag><marker id="prop_keep_alive_timeout"></marker>{keep_alive_timeout, integer()}</tag>
<item>
<p>The number of seconds the server waits for a
subsequent request from the client before closing the
connection. Default is <c>150</c>.</p>
</item>
- <marker id="prop_max_body_size"></marker>
- <tag>{max_body_size, integer()}</tag>
+ <tag><marker id="prop_max_body_size"></marker>{max_body_size, integer()}</tag>
<item>
<p>Limits the size of the message body of an HTTP request.
Default is no limit.</p>
</item>
- <marker id="prop_max_clients"></marker>
- <tag>{max_clients, integer()}</tag>
+ <tag><marker id="prop_max_clients"></marker>{max_clients, integer()}</tag>
<item>
<p>Limits the number of simultaneous requests that can be
supported. Default is <c>150</c>.</p>
</item>
- <marker id="prop_max_header_size"></marker>
- <tag>{max_header_size, integer()}</tag>
+ <tag><marker id="prop_max_header_size"></marker>{max_header_size, integer()}</tag>
<item>
<p>Limits the size of the message header of an HTTP request.
Default is <c>10240</c>.</p>
</item>
- <marker id="prop_max_content_length"></marker>
- <tag>{max_content_length, integer()}</tag>
+ <tag><marker id="prop_max_content_length"></marker>{max_content_length, integer()}</tag>
<item>
<p>Maximum content-length in an incoming request, in bytes. Requests
with content larger than this are answered with status 413.
@@ -284,15 +266,13 @@
</p>
</item>
- <marker id="prop_max_uri"></marker>
- <tag>{max_uri_size, integer()}</tag>
+ <tag><marker id="prop_max_uri"></marker>{max_uri_size, integer()}</tag>
<item>
<p>Limits the size of the HTTP request URI.
Default is no limit.</p>
</item>
- <marker id="prop_max_keep_alive_req"></marker>
- <tag>{max_keep_alive_request, integer()}</tag>
+ <tag><marker id="prop_max_keep_alive_req"></marker>{max_keep_alive_request, integer()}</tag>
<item>
<p>The number of requests that a client can do on one
connection. When the server has responded to the number of
@@ -305,8 +285,7 @@
<marker id="props_admin"></marker>
<p><em>Administrative Properties</em></p>
<taglist>
- <marker id="prop_mime_types"></marker>
- <tag>{mime_types, [{MimeType, Extension}] | path()}</tag>
+ <tag><marker id="prop_mime_types"></marker>{mime_types, [{MimeType, Extension}] | path()}</tag>
<item>
<p><c>MimeType = string()</c> and <c>Extension = string()</c>.
Files delivered to the client are MIME typed according to RFC
@@ -322,24 +301,21 @@ text/plain asc txt</pre>
<p>Default is [{"html","text/html"},{"htm","text/html"}].</p>
</item>
- <marker id="prop_mime_type"></marker>
- <tag>{mime_type, string()}</tag>
+ <tag><marker id="prop_mime_type"></marker>{mime_type, string()}</tag>
<item>
<p>When the server is asked to provide a document type that
cannot be determined by the MIME Type Settings, the server
uses this default type.</p>
</item>
- <marker id="prop_server_admin"></marker>
- <tag>{server_admin, string()}</tag>
+ <tag><marker id="prop_server_admin"></marker>{server_admin, string()}</tag>
<item>
<p>Defines the email-address of the server
administrator to be included in any error messages returned by
the server.</p>
</item>
- <marker id="prop_server_tokens"></marker>
- <tag>{server_tokens, none|prod|major|minor|minimal|os|full|{private, string()}}</tag>
+ <tag><marker id="prop_server_tokens"></marker>{server_tokens, none|prod|major|minor|minimal|os|full|{private, string()}}</tag>
<item>
<p>Defines the look of the value of the server header.</p>
<p>Example: Assuming the version of <c>Inets</c> is 5.8.1,
@@ -366,8 +342,7 @@ text/plain asc txt</pre>
<p>By default, the value is as before, that is, <c>minimal</c>.</p>
</item>
- <marker id="prop_log_format"></marker>
- <tag>{log_format, common | combined}</tag>
+ <tag><marker id="prop_log_format"></marker>{log_format, common | combined}</tag>
<item>
<p>Defines if access logs are to be written according to the <c>common</c>
log format or the extended common log format.
@@ -410,8 +385,7 @@ text/plain asc txt</pre>
</p>
</item>
- <marker id="prop_elog_format"></marker>
- <tag>{error_log_format, pretty | compact}</tag>
+ <tag><marker id="prop_elog_format"></marker>{error_log_format, pretty | compact}</tag>
<item>
<p>Default is <c>pretty</c>. If the error log is meant to be read
directly by a human, <c>pretty</c> is the best option.</p>
@@ -433,60 +407,57 @@ text/plain asc txt</pre>
<marker id="props_alias"></marker>
<p><em>URL Aliasing Properties - Requires mod_alias</em></p>
<taglist>
- <marker id="prop_alias"></marker>
- <tag>{alias, {Alias, RealName}}</tag>
+ <tag><marker id="prop_alias"></marker>{alias, {Alias, RealName}}</tag>
<item>
<p><c>Alias = string()</c> and <c>RealName = string()</c>.
<c>alias</c> allows documents to be stored in the local file
system instead of the <c>document_root</c> location. URLs with a path
beginning with url-path is mapped to local files beginning with
- directory-filename, for example:
+ directory-filename, for example:</p>
<code>{alias, {"/image", "/ftp/pub/image"}}</code>
- Access to http://your.server.org/image/foo.gif would refer to
+ <p>Access to http://your.server.org/image/foo.gif would refer to
the file /ftp/pub/image/foo.gif.</p>
</item>
- <marker id="prop_re_write"></marker>
- <tag>{re_write, {Re, Replacement}}</tag>
+ <tag><marker id="prop_re_write"></marker>{re_write, {Re, Replacement}}</tag>
<item>
<p><c>Re = string()</c> and <c>Replacement = string()</c>.
<c>re_write</c> allows documents to be stored in the local file
system instead of the <c>document_root</c> location. URLs are rewritten
by <c>re:replace/3</c> to produce a path in the local file-system,
- for example:
+ for example:</p>
<code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}}</code>
- Access to http://your.server.org/~bob/foo.gif would refer to
+ <p>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, <c>Re</c> is separated
from <c>Replacement</c> with one single space, and as expected
backslashes do not need to be backslash escaped, the
- same example would become:
+ same example would become:</p>
<code>ReWrite ^/[~]([^/]+)(.*)$ /home/\1/public\2</code>
- Beware of trailing space in <c>Replacement</c> to be used.
+ <p>Beware of trailing space in <c>Replacement</c> to be used.
If you must have a space in <c>Re</c>, use, for example, the character
encoding <c>\040</c>, see
<seealso marker="stdlib:re">re(3)</seealso>.</p>
</item>
- <marker id="prop_dir_idx"></marker>
- <tag>{directory_index, [string()]}</tag>
+ <tag><marker id="prop_dir_idx"></marker>{directory_index, [string()]}</tag>
<item>
<p><c>directory_index</c> specifies a list of resources to look for
if a client requests a directory using a <c>/</c> at the end of the
directory name. <c>file</c> depicts the name of a file in the
directory. Several files can be given, in which case the server
- returns the first it finds, for example:
+ returns the first it finds, for example:</p>
<code>{directory_index, ["index.hml", "welcome.html"]}</code>
- Access to http://your.server.org/docs/ would return
+ <p>Access to http://your.server.org/docs/ would return
http://your.server.org/docs/index.html or
http://your.server.org/docs/welcome.html if index.html does not
exist.</p>
@@ -496,38 +467,35 @@ text/plain asc txt</pre>
<marker id="props_cgi"></marker>
<p><em>CGI Properties - Requires mod_cgi</em></p>
<taglist>
- <marker id="prop_script_alias"></marker>
- <tag>{script_alias, {Alias, RealName}}</tag>
+ <tag><marker id="prop_script_alias"></marker>{script_alias, {Alias, RealName}}</tag>
<item>
<p><c>Alias = string()</c> and <c>RealName = string()</c>.
Have the same behavior as property <c>alias</c>, except that
they also mark 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:
+ scripts beginning with directory-filename, for example:</p>
<code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}}</code>
- Access to http://your.server.org/cgi-bin/foo would cause
+ <p>Access to http://your.server.org/cgi-bin/foo would cause
the server to run the script /web/cgi-bin/foo.</p>
</item>
- <marker id="prop_script_re_write"></marker>
- <tag>{script_re_write, {Re, Replacement}}</tag>
+ <tag><marker id="prop_script_re_write"></marker>{script_re_write, {Re, Replacement}}</tag>
<item>
<p><c>Re = string()</c> and <c>Replacement = string()</c>.
Have the same behavior as property <c>re_write</c>, except that
they also mark 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:
+ scripts beginning with directory-filename, for example:</p>
<code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}}</code>
- Access to http://your.server.org/cgi-bin/17/foo would cause
+ <p>Access to http://your.server.org/cgi-bin/17/foo would cause
the server to run the script /web/17/cgi-bin/foo.</p>
</item>
- <marker id="prop_script_nocache"></marker>
- <tag>{script_nocache, boolean()}</tag>
+ <tag><marker id="prop_script_nocache"></marker>{script_nocache, boolean()}</tag>
<item>
<p>If <c>script_nocache</c> is set to <c>true</c>, the HTTP server by
default adds the header fields necessary to prevent proxies from
@@ -535,8 +503,7 @@ text/plain asc txt</pre>
Default to <c>false</c>.</p>
</item>
- <marker id="prop_script_timeout"></marker>
- <tag>{script_timeout, integer()}</tag>
+ <tag><marker id="prop_script_timeout"></marker>{script_timeout, integer()}</tag>
<item>
<p>The time in seconds the web server waits between each
chunk of data from the script. If the CGI script does not deliver
@@ -544,8 +511,7 @@ text/plain asc txt</pre>
closed. Default is <c>15</c>.</p>
</item>
- <marker id="prop_action"></marker>
- <tag>{action, {MimeType, CgiScript}} - requires mod_action</tag>
+ <tag><marker id="prop_action"></marker>{action, {MimeType, CgiScript}} - requires mod_action</tag>
<item>
<p><c>MimeType = string()</c> and <c>CgiScript = string()</c>.
<c>action</c> adds an action activating a CGI script
@@ -558,8 +524,7 @@ text/plain asc txt</pre>
<code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}}</code>
</item>
- <marker id="prop_script"></marker>
- <tag>{script, {Method, CgiScript}} - requires mod_action</tag>
+ <tag><marker id="prop_script"></marker>{script, {Method, CgiScript}} - requires mod_action</tag>
<item>
<p><c>Method = string()</c> and <c>CgiScript = string()</c>.
<c>script</c> adds an action activating a CGI script
@@ -578,17 +543,16 @@ text/plain asc txt</pre>
<marker id="props_esi"></marker>
<p><em>ESI Properties - Requires mod_esi</em></p>
<taglist>
- <marker id="prop_esi_alias"></marker>
- <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag>
+ <tag><marker id="prop_esi_alias"></marker>{erl_script_alias, {URLPath, [AllowedModule]}}</tag>
<item>
<p><c>URLPath = string()</c> and <c>AllowedModule = atom()</c>.
<c>erl_script_alias</c> marks all URLs matching url-path as erl
scheme scripts. A matching URL is mapped into a specific module
- and function, for example:
+ and function, for example:</p>
<code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]}}</code>
- A request to
+ <p>A request to
http://your.server.org/cgi-bin/example/httpd_example:yahoo
would refer to httpd_example:yahoo/3 or, if that does not exist,
httpd_example:yahoo/2 and
@@ -596,8 +560,7 @@ text/plain asc txt</pre>
not be allowed to execute.</p>
</item>
- <marker id="prop_esi_nocache"></marker>
- <tag>{erl_script_nocache, boolean()}</tag>
+ <tag><marker id="prop_esi_nocache"></marker>{erl_script_nocache, boolean()}</tag>
<item>
<p>If <c>erl_script_nocache</c> is set to <c>true</c>, the server adds
HTTP header fields preventing proxies from caching the
@@ -606,8 +569,7 @@ text/plain asc txt</pre>
Default is <c>false</c>.</p>
</item>
- <marker id="prop_esi_timeout"></marker>
- <tag>{erl_script_timeout, integer()}</tag>
+ <tag><marker id="prop_esi_timeout"></marker>{erl_script_timeout, integer()}</tag>
<item>
<p>If <c>erl_script_timeout</c> sets the time in seconds the server
waits between each chunk of data to be delivered through
@@ -615,8 +577,7 @@ text/plain asc txt</pre>
for scripts that use the erl scheme.</p>
</item>
- <marker id="prop_esi_timeout"></marker>
- <tag>{eval_script_alias, {URLPath, [AllowedModule]}}</tag>
+ <tag><marker id="prop_esi_timeout"></marker>{eval_script_alias, {URLPath, [AllowedModule]}}</tag>
<item>
<p><c>URLPath = string()</c> and <c>AllowedModule = atom()</c>.
Same as <c>erl_script_alias</c> but for scripts
@@ -628,24 +589,21 @@ text/plain asc txt</pre>
<marker id="props_log"></marker>
<p><em>Log Properties - Requires mod_log</em></p>
<taglist>
- <marker id="prop_elog"></marker>
- <tag>{error_log, path()}</tag>
+ <tag><marker id="prop_elog"></marker>{error_log, path()}</tag>
<item>
<p>Defines the filename of the error log file to be used to log
server errors. If the filename does not begin with a slash (/),
it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_slog"></marker>
- <tag>{security_log, path()}</tag>
+ <tag><marker id="prop_slog"></marker>{security_log, path()}</tag>
<item>
<p>Defines the filename of the access log file to be used to
log security events. If the filename does not begin with a slash
(/), it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_tlog"></marker>
- <tag>{transfer_log, path()}</tag>
+ <tag><marker id="prop_tlog"></marker>{transfer_log, path()}</tag>
<item>
<p>Defines the filename of the access log file to be used to
log incoming requests. If the filename does not begin with a
@@ -656,8 +614,7 @@ text/plain asc txt</pre>
<marker id="props_dlog"></marker>
<p><em>Disk Log Properties - Requires mod_disk_log</em></p>
<taglist>
- <marker id="prop_dlog_format"></marker>
- <tag>{disk_log_format, internal | external}</tag>
+ <tag><marker id="prop_dlog_format"></marker>{disk_log_format, internal | external}</tag>
<item>
<p>Defines the file format of the log files. See <c>disk_log</c> for
details. If the internal file format is used, the
@@ -667,16 +624,14 @@ text/plain asc txt</pre>
<c>external</c>.</p>
</item>
- <marker id="prop_edlog"></marker>
- <tag>{error_disk_log, path()}</tag>
+ <tag><marker id="prop_edlog"></marker>{error_disk_log, path()}</tag>
<item>
<p>Defines the filename of the (<c>disk_log(3)</c>) error log file
to be used to log server errors. If the filename does not begin
with a slash (/), it is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_edlog_size"></marker>
- <tag>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_edlog_size"></marker>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
<p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
Defines the properties of the (<c>disk_log(3)</c>) error log
@@ -685,8 +640,7 @@ text/plain asc txt</pre>
used before the first file is truncated and reused.</p>
</item>
- <marker id="prop_sdlog"></marker>
- <tag>{security_disk_log, path()}</tag>
+ <tag><marker id="prop_sdlog"></marker>{security_disk_log, path()}</tag>
<item>
<p>Defines the filename of the (<c>disk_log(3)</c>) access log file
logging incoming security events, that is, authenticated
@@ -694,8 +648,7 @@ text/plain asc txt</pre>
is assumed to be relative to the <c>server_root</c>.</p>
</item>
- <marker id="prop_sdlog_size"></marker>
- <tag>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_sdlog_size"></marker>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
<p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
Defines the properties of the <c>disk_log(3)</c> access log
@@ -704,8 +657,7 @@ text/plain asc txt</pre>
used before the first file is truncated and reused.</p>
</item>
- <marker id="prop_tdlog"></marker>
- <tag>{transfer_disk_log, path()}</tag>
+ <tag><marker id="prop_tdlog"></marker>{transfer_disk_log, path()}</tag>
<item>
<p>Defines the filename of the (<c>disk_log(3)</c>) access log file
logging incoming requests. If the filename does not begin
@@ -713,8 +665,7 @@ text/plain asc txt</pre>
<c>server_root</c>.</p>
</item>
- <marker id="prop_tdlog_size"></marker>
- <tag>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag>
+ <tag><marker id="prop_tdlog_size"></marker>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag>
<item>
<p><c>MaxBytes = integer()</c> and <c>MaxFiles = integer()</c>.
Defines the properties of the <c>disk_log(3)</c> access log
@@ -734,32 +685,29 @@ text/plain asc txt</pre>
<p>The properties for directories are as follows:</p>
<taglist>
- <marker id="prop_allow_from"></marker>
- <tag>{allow_from, all | [RegxpHostString]}</tag>
+ <tag><marker id="prop_allow_from"></marker>{allow_from, all | [RegxpHostString]}</tag>
<item>
<p>Defines a set of hosts to be granted access to a
- given directory, for example:
+ given directory, for example:</p>
<code>{allow_from, ["123.34.56.11", "150.100.23"]}</code>
- The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
+ <p>The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
subnet are allowed access.</p>
</item>
- <marker id="prop_deny_from"></marker>
- <tag>{deny_from, all | [RegxpHostString]}</tag>
+ <tag><marker id="prop_deny_from"></marker>{deny_from, all | [RegxpHostString]}</tag>
<item>
<p>Defines a set of hosts
- to be denied access to a given directory, for example:
+ to be denied access to a given directory, for example:</p>
<code>{deny_from, ["123.34.56.11", "150.100.23"]}</code>
- The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
+ <p>The host <c>123.34.56.11</c> and all machines on the <c>150.100.23</c>
subnet are not allowed access.</p>
</item>
- <marker id="prop_auth_type"></marker>
- <tag>{auth_type, plain | dets | mnesia}</tag>
+ <tag><marker id="prop_auth_type"></marker>{auth_type, plain | dets | mnesia}</tag>
<item>
<p>Sets the type of authentication database that is used for the
directory. The key difference between the different methods is
@@ -769,8 +717,7 @@ text/plain asc txt</pre>
configuration files.</p>
</item>
- <marker id="prop_auth_user_file"></marker>
- <tag>{auth_user_file, path()}</tag>
+ <tag><marker id="prop_auth_user_file"></marker>{auth_user_file, path()}</tag>
<item>
<p>Sets the name of a file containing the list of users and
passwords for user authentication. The filename can be either
@@ -794,8 +741,7 @@ text/plain asc txt</pre>
clients can download it.</p>
</item>
- <marker id="prop_auth_group_file"></marker>
- <tag>{auth_group_file, path()}</tag>
+ <tag><marker id="prop_auth_group_file"></marker>{auth_group_file, path()}</tag>
<item>
<p>Sets the name of a file containing the list of user
groups for user authentication. The filename can be either
@@ -817,16 +763,14 @@ text/plain asc txt</pre>
can download it.</p>
</item>
- <marker id="prop_auth_name"></marker>
- <tag>{auth_name, string()}</tag>
+ <tag><marker id="prop_auth_name"></marker>{auth_name, string()}</tag>
<item>
<p>Sets the name of the authorization realm (auth-domain) for
a directory. This string informs the client about which
username and password to use.</p>
</item>
- <marker id="prop_auth_access_passwd"></marker>
- <tag>{auth_access_password, string()}</tag>
+ <tag><marker id="prop_auth_access_passwd"></marker>{auth_access_password, string()}</tag>
<item>
<p>If set to other than "NoPassword", the password is required
for all API calls. If the password is set to "DummyPassword", the
@@ -836,15 +780,13 @@ text/plain asc txt</pre>
text in the configuration file.</p>
</item>
- <marker id="prop_req_user"></marker>
- <tag>{require_user, [string()]}</tag>
+ <tag><marker id="prop_req_user"></marker>{require_user, [string()]}</tag>
<item>
<p>Defines users to grant access to a given
directory using a secret password.</p>
</item>
- <marker id="prop_req_grp"></marker>
- <tag>{require_group, [string()]}</tag>
+ <tag><marker id="prop_req_grp"></marker>{require_group, [string()]}</tag>
<item>
<p>Defines users to grant access to a given
directory using a secret password.</p>
@@ -855,8 +797,7 @@ text/plain asc txt</pre>
<marker id="props_htaccess"></marker>
<p><em>Htaccess Authentication Properties - Requires mod_htaccess</em></p>
<taglist>
- <marker id="prop_access_files"></marker>
- <tag>{access_files, [path()]}</tag>
+ <tag><marker id="prop_access_files"></marker>{access_files, [path()]}</tag>
<item>
<p>Specifies the filenames that are used for
access files. When a request comes, every directory in the path
@@ -876,16 +817,14 @@ text/plain asc txt</pre>
<marker id="props_sdir"></marker>
<p>The properties for the security directories are as follows:</p>
<taglist>
- <marker id="prop_data_file"></marker>
- <tag>{data_file, path()}</tag>
+ <tag><marker id="prop_data_file"></marker>{data_file, path()}</tag>
<item>
<p>Name of the security data file. The filename can either be
absolute or relative to the <c>server_root</c>. This file is used to
store persistent data for module <c>mod_security</c>.</p>
</item>
- <marker id="prop_max_retries"></marker>
- <tag>{max_retries, integer()}</tag>
+ <tag><marker id="prop_max_retries"></marker>{max_retries, integer()}</tag>
<item>
<p>Specifies the maximum number of attempts to authenticate a
user before the user is blocked out. If a user
@@ -897,16 +836,14 @@ text/plain asc txt</pre>
Default is <c>3</c>. Can be set to infinity.</p>
</item>
- <marker id="prop_block_time"></marker>
- <tag>{block_time, integer()}</tag>
+ <tag><marker id="prop_block_time"></marker>{block_time, integer()}</tag>
<item>
<p>Specifies the number of minutes a user is blocked. After
this timehas passed, the user automatically regains access.
Default is <c>60</c>.</p>
</item>
- <marker id="prop_fail_exp_time"></marker>
- <tag>{fail_expire_time, integer()}</tag>
+ <tag><marker id="prop_fail_exp_time"></marker>{fail_expire_time, integer()}</tag>
<item>
<p>Specifies the number of minutes a failed user authentication
is remembered. If a user authenticates after this
@@ -915,8 +852,7 @@ text/plain asc txt</pre>
Default is <c>30</c>.</p>
</item>
- <marker id="prop_auth_timeout"></marker>
- <tag>{auth_timeout, integer()}</tag>
+ <tag><marker id="prop_auth_timeout"></marker>{auth_timeout, integer()}</tag>
<item>
Specifies the number of seconds a successful user
authentication is remembered. After this time has passed, the
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index ef11fdc10c..7ebb3ddffa 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2014</year>
+ <year>2002</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,7 +33,40 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 6.0.2</title>
+ <section><title>Inets 6.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Improved error handling and gracfully termination when an
+ invalid chunked length header is encountered.</p>
+ <p>
+ Own Id: OTP-13061</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add possibility to set socket options, such as nodelay,
+ for httpd. Also phase out legacy option value inet6bf4
+ for the ipfamily option. This value will be translated to
+ the value inet.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13062</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 6.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -1774,23 +1807,21 @@
<item>
<p>[httpd] - Issues with ESI erl_script_timeout. </p>
- <p>
- <list type="bulleted">
- <item>
- <p>The <c>erl_script_timeout</c> config option is ducumented
- as a number of seconds. But when parsing the config, in the
- new format (not a config file), it was handled as if in
- number of milliseconds. </p>
- </item>
- <item>
- <p>When the erl-script-timeout time was exceeded, the server
- incorrectly marked the answer as sent, thereby leaving
- client hanging (with an incomplete answer).
- This has been changed, so that now the socket will be
- closed. </p>
- </item>
- </list>
- </p>
+ <list type="bulleted">
+ <item>
+ <p>The <c>erl_script_timeout</c> config option is ducumented
+ as a number of seconds. But when parsing the config, in the
+ new format (not a config file), it was handled as if in
+ number of milliseconds. </p>
+ </item>
+ <item>
+ <p>When the erl-script-timeout time was exceeded, the server
+ incorrectly marked the answer as sent, thereby leaving
+ client hanging (with an incomplete answer).
+ This has been changed, so that now the socket will be
+ closed. </p>
+ </item>
+ </list>
<p>Own Id: OTP-8509</p>
</item>
</list>
@@ -1866,20 +1897,19 @@
<item>
<p>[httpc] Several more or less critical fixes:</p>
- <p>
- <list type="bulleted">
- <item>
- <p>Initial call between the httpc manager and request
- handler was synchronous. </p>
- <p>When the manager starts a new request handler,
- this is no longer a synchronous operation. Previously,
- the new request handler made the connection to the
- server and issuing of the first request (the reason
- for starting it) in the gen_server init function.
- If the connection for some reason "took some time",
- the manager hanged, leaving all other activities by
- that manager also hanging. </p>
- </item>
+ <list type="bulleted">
+ <item>
+ <p>Initial call between the httpc manager and request
+ handler was synchronous. </p>
+ <p>When the manager starts a new request handler,
+ this is no longer a synchronous operation. Previously,
+ the new request handler made the connection to the
+ server and issuing of the first request (the reason
+ for starting it) in the gen_server init function.
+ If the connection for some reason "took some time",
+ the manager hanged, leaving all other activities by
+ that manager also hanging. </p>
+ </item>
<!--
<item>
<p>Copying of data between processes</p>
@@ -1890,8 +1920,7 @@
<p>TBD</p>
</item>
-->
- </list>
- </p>
+ </list>
<p>As a side-effect of these changes, some modules was also
renamed, and a new api module,
<seealso marker="httpc">httpc</seealso>, has been introduced
diff --git a/lib/inets/doc/src/tftp.xml b/lib/inets/doc/src/tftp.xml
index 00d9d53376..10398f5088 100644
--- a/lib/inets/doc/src/tftp.xml
+++ b/lib/inets/doc/src/tftp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2013</year>
+ <year>2006</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -216,12 +216,9 @@
five times when the time-out expires.</p>
</item>
</taglist>
-
- <marker id="start1"></marker>
</section>
<funcs>
- <marker id="change_config_daemons"></marker>
<func>
<name>change_config(daemons, Options) -> [{Pid, Result}]</name>
<fsummary>Changes configuration for all daemons.
@@ -234,8 +231,6 @@
</type>
<desc>
<p>Changes configuration for all TFTP daemon processes. </p>
-
- <marker id="change_config_servers"></marker>
</desc>
</func>
@@ -251,8 +246,6 @@
</type>
<desc>
<p>Changes configuration for all TFTP server processes.</p>
-
- <marker id="change_config_pid"></marker>
</desc>
</func>
@@ -268,7 +261,6 @@
</type>
<desc>
<p>Changes configuration for a TFTP daemon, server, or client process.</p>
- <marker id="info_daemons"></marker>
</desc>
</func>
@@ -282,8 +274,6 @@
</type>
<desc>
<p>Returns information about all TFTP daemon processes.</p>
-
- <marker id="info_servers"></marker>
</desc>
</func>
@@ -297,8 +287,6 @@
</type>
<desc>
<p>Returns information about all TFTP server processes. </p>
-
- <marker id="info_pid"></marker>
</desc>
</func>
@@ -341,9 +329,7 @@
the regexps of these and the callback module corresponding to
the first match is used, or an error tuple is returned if no
matching regexp is found.</p>
- </desc>
-
- <marker id="write_file"></marker>
+ </desc>
</func>
<func>
@@ -359,8 +345,6 @@
port. When it receives a request for read or write, it spawns
a temporary server process handling the actual transfer
of the (virtual) file.</p>
-
- <marker id="read_file"></marker>
</desc>
</func>
@@ -393,7 +377,6 @@
matching regexp is found.</p>
</desc>
</func>
-
</funcs>
<section>
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 6e6cc38c06..d1c52dcc78 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -26,6 +26,7 @@
-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
+-define(IS_STREAMED(Code), ((Code =:= 200) orelse (Code =:= 206))).
%%--------------------------------------------------------------------
%% Internal Application API
@@ -163,22 +164,22 @@ info(Pid) ->
%% Request should not be streamed
stream(BodyPart, #request{stream = none} = Request, _) ->
?hcrt("stream - none", []),
- {BodyPart, Request};
+ {false, BodyPart, Request};
%% Stream to caller
stream(BodyPart, #request{stream = Self} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso
+ when ?IS_STREAMED(Code) andalso
((Self =:= self) orelse (Self =:= {self, once})) ->
?hcrt("stream - self", [{stream, Self}, {code, Code}]),
httpc_response:send(Request#request.from,
{Request#request.id, stream, BodyPart}),
- {<<>>, Request};
+ {true, <<>>, Request};
%% Stream to file
%% This has been moved to start_stream/3
%% We keep this for backward compatibillity...
stream(BodyPart, #request{stream = Filename} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("stream - filename", [{stream, Filename}, {code, Code}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -190,18 +191,18 @@ stream(BodyPart, #request{stream = Filename} = Request, Code)
%% Stream to file
stream(BodyPart, #request{stream = Fd} = Request, Code)
- when ((Code =:= 200) orelse (Code =:= 206)) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("stream to file", [{stream, Fd}, {code, Code}]),
case file:write(Fd, BodyPart) of
ok ->
- {<<>>, Request};
+ {true, <<>>, Request};
{error, Reason} ->
exit({stream_to_file_failed, Reason})
end;
stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed
?hcrt("stream - ignore", [{request, Request}]),
- {BodyPart, Request}.
+ {false, BodyPart, Request}.
%%====================================================================
@@ -474,18 +475,18 @@ handle_info({Proto, _Socket, Data},
{Module, whole_body, [Body, Length]} ->
?hcrd("data processed - whole body", [{length, Length}]),
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(Body, Request, Code),
+ {Streamed, NewBody, NewRequest} = stream(Body, Request, Code),
%% When we stream we will not keep the already
%% streamed data, that would be a waste of memory.
NewLength =
- case Stream of
- none ->
+ case Streamed of
+ false ->
Length;
- _ ->
+ true ->
Length - size(Body)
end,
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, whole_body, [NewBody, NewLength]},
{noreply, NewState#state{mfa = NewMFA,
request = NewRequest}};
@@ -497,8 +498,8 @@ handle_info({Proto, _Socket, Data},
%% The response body is chunk-encoded. Steal decoded
%% chunks as much as possible to stream.
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(BodySoFar, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(BodySoFar, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_size,
[TotalChunk, HexList,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -517,8 +518,8 @@ handle_info({Proto, _Socket, Data},
NewChunkSize = ChunkSize - ChunkSizeToSteal,
{_, Code, _} = StatusLine,
- {NewBody, NewRequest} = stream(StolenBody, Request, Code),
- NewState = next_body_chunk(State),
+ {_, NewBody, NewRequest} = stream(StolenBody, Request, Code),
+ NewState = next_body_chunk(State, Code),
NewMFA = {Module, decode_data,
[NewChunkSize, NewTotalChunk,
{MaxBodySize, NewBody, AccLength, MaxHeaderSize}]},
@@ -1071,13 +1072,13 @@ handle_http_msg({ChunkedHeaders, Body},
?hcrt("handle_http_msg",
[{chunked_headers, ChunkedHeaders}, {headers, Headers}]),
NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{headers = NewHeaders,
body = NewBody,
request = NewRequest});
handle_http_msg(Body, #state{status_line = {_,Code, _}} = State) ->
?hcrt("handle_http_msg", [{code, Code}]),
- {NewBody, NewRequest} = stream(Body, State#state.request, Code),
+ {_, NewBody, NewRequest} = stream(Body, State#state.request, Code),
handle_response(State#state{body = NewBody, request = NewRequest}).
handle_http_body(_, #state{status = {ssl_tunnel, _},
@@ -1112,14 +1113,14 @@ handle_http_body(Body, #state{headers = Headers,
case case_insensitive_header(TransferEnc) of
"chunked" ->
?hcrt("handle_http_body - chunked", []),
- case http_chunk:decode(Body, State#state.max_body_size,
- State#state.max_header_size) of
+ try http_chunk:decode(Body, State#state.max_body_size,
+ State#state.max_header_size) of
{Module, Function, Args} ->
?hcrt("handle_http_body - new mfa",
[{module, Module},
{function, Function},
{args, Args}]),
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa =
{Module, Function, Args}}};
{ok, {ChunkedHeaders, NewBody}} ->
@@ -1133,11 +1134,18 @@ handle_http_body(Body, #state{headers = Headers,
handle_response(State#state{headers = NewHeaders,
body = NewBody});
_ ->
- {NewBody2, _NewRequest} =
+ {_, NewBody2, _} =
stream(NewBody, Request, Code),
handle_response(State#state{headers = NewHeaders,
body = NewBody2})
end
+ catch throw:{error, Reason} ->
+ NewState =
+ answer_request(Request,
+ httpc_response:error(Request,
+ Reason),
+ State),
+ {stop, normal, NewState}
end;
Enc when Enc =:= "identity"; Enc =:= undefined ->
?hcrt("handle_http_body - identity", []),
@@ -1147,12 +1155,12 @@ handle_http_body(Body, #state{headers = Headers,
true ->
case httpc_response:whole_body(Body, Length) of
{ok, Body} ->
- {NewBody, NewRequest} =
+ {_, NewBody, NewRequest} =
stream(Body, Request, Code),
handle_response(State#state{body = NewBody,
request = NewRequest});
MFA ->
- NewState = next_body_chunk(State),
+ NewState = next_body_chunk(State, Code),
{noreply, NewState#state{mfa = MFA}}
end;
false ->
@@ -1646,21 +1654,21 @@ start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = self} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, ignore),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, Headers,
#request{stream = {self, once}} = Request)
- when (Code =:= 200) orelse (Code =:= 206) ->
+ when ?IS_STREAMED(Code) ->
?hcrt("start stream - self:once", [{code, Code}]),
Msg = httpc_response:stream_start(Headers, Request, self()),
httpc_response:send(Request#request.from, Msg),
{ok, Request};
start_stream({_Version, Code, _ReasonPhrase}, _Headers,
#request{stream = Filename} = Request)
- when ((Code =:= 200) orelse (Code =:= 206)) andalso is_list(Filename) ->
+ when ?IS_STREAMED(Code) andalso is_list(Filename) ->
?hcrt("start stream", [{code, Code}, {filename, Filename}]),
case file:open(Filename, [write, raw, append, delayed_write]) of
{ok, Fd} ->
@@ -1712,13 +1720,15 @@ end_stream(SL, R) ->
next_body_chunk(#state{request = #request{stream = {self, once}},
once = once,
- session = Session} = State) ->
+ session = Session} = State,
+ Code) when ?IS_STREAMED(Code) ->
activate_once(Session),
State#state{once = inactive};
next_body_chunk(#state{request = #request{stream = {self, once}},
- once = inactive} = State) ->
+ once = inactive} = State,
+ Code) when ?IS_STREAMED(Code) ->
State; %% Wait for user to call stream_next
-next_body_chunk(#state{session = Session} = State) ->
+next_body_chunk(#state{session = Session} = State, _) ->
activate_once(Session),
State.
@@ -1817,11 +1827,13 @@ host_header(_, URI) ->
tls_upgrade(#state{status =
{ssl_tunnel,
#request{settings =
- #http_options{ssl = {_, TLSOptions} = SocketType},
- address = Address} = Request},
+ #http_options{ssl = {_, TLSOptions0} = SocketType},
+ address = {Host, _} = Address} = Request},
session = #session{socket = TCPSocket} = Session0,
options = Options} = State) ->
+ TLSOptions = maybe_add_sni(Host, TLSOptions0),
+
case ssl:connect(TCPSocket, TLSOptions) of
{ok, TLSSocket} ->
ClientClose = httpc_request:is_client_closing(Request#request.headers),
@@ -1852,6 +1864,15 @@ tls_upgrade(#state{status =
{stop, normal, State#state{request = Request}}
end.
+maybe_add_sni(Host, Options) ->
+ case http_util:is_hostname(Host) andalso
+ not lists:keymember(server_name_indication, 1, Options) of
+ true ->
+ [{server_name_indication, Host} | Options];
+ false ->
+ Options
+ end.
+
%% ---------------------------------------------------------------------
%% Session wrappers
%% ---------------------------------------------------------------------
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
index 9476ea9f5f..7325f24809 100644
--- a/lib/inets/src/http_lib/http_chunk.erl
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -25,7 +25,7 @@
-include("http_internal.hrl").
%% API
--export([decode/3, encode/1, encode_last/0, handle_headers/2]).
+-export([decode/3, encode/1, encode_last/0, encode_last/1, handle_headers/2]).
%% Callback API - used for example if the chunkedbody is received a
%% little at a time on a socket.
-export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]).
@@ -57,7 +57,7 @@
%%-------------------------------------------------------------------------
decode(ChunkedBody, MaxBodySize, MaxHeaderSize) ->
%% Note decode_size will call decode_data.
- decode_size([ChunkedBody, <<>>, [],
+ decode_size([ChunkedBody, <<>>, [], 0,
{MaxBodySize, <<>>, 0, MaxHeaderSize}]).
%%-------------------------------------------------------------------------
@@ -85,6 +85,11 @@ encode(Chunk) when is_list(Chunk)->
encode_last() ->
<<$0, ?CR, ?LF, ?CR, ?LF >>.
+encode_last([]) ->
+ encode_last();
+encode_last(Trailers0) ->
+ Trailers = list_to_binary(encode_trailers(Trailers0)),
+ <<$0, ?CR, ?LF, Trailers/binary>>.
%%-------------------------------------------------------------------------
%% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord
@@ -120,65 +125,80 @@ handle_headers(ResponseHeaderRecord = #http_response_h{}, ChunkedHeaders) ->
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
-decode_size([Bin, Rest, HexList, Info]) ->
- decode_size(<<Rest/binary, Bin/binary>>, HexList, Info).
+decode_size([Bin, Rest, HexList, AccSize, Info]) ->
+ decode_size(<<Rest/binary, Bin/binary>>, HexList, AccSize, Info).
-ignore_extensions([Bin, Rest, NextFunction]) ->
- ignore_extensions(<<Rest/binary, Bin/binary>>, NextFunction).
+ignore_extensions([Bin, Rest, RemainingSize, TotalMaxHeaderSize, NextFunction]) ->
+ ignore_extensions(<<Rest/binary, Bin/binary>>, RemainingSize, TotalMaxHeaderSize, NextFunction).
decode_data([Bin, ChunkSize, TotalChunk, Info]) ->
decode_data(ChunkSize, <<TotalChunk/binary, Bin/binary>>, Info).
-decode_trailer([Bin, Rest, Header, Headers, MaxHeaderSize, Body,
- BodyLength]) ->
+decode_trailer([Bin, Rest, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]) ->
decode_trailer(<<Rest/binary, Bin/binary>>,
- Header, Headers, MaxHeaderSize, Body, BodyLength).
+ Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize).
%%%========================================================================
%%% Internal functions
%%%========================================================================
-decode_size(<<>>, HexList, Info) ->
- {?MODULE, decode_size, [<<>>, HexList, Info]};
-decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList,
+decode_size(_, _, AccHeaderSize, {_,_,_, MaxHeaderSize}) when
+ AccHeaderSize > MaxHeaderSize ->
+ throw({error, {header_too_long, {max, MaxHeaderSize}}});
+
+decode_size(<<>>, HexList, AccHeaderSize, Info) ->
+ {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]};
+decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, AccHeaderSize,
{MaxBodySize, Body,
AccLength,
MaxHeaderSize}) ->
- ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)),
- case ChunkSize of
+ try http_util:hexlist_to_integer(lists:reverse(string:strip(HexList, left))) of
0 -> % Last chunk, there was no data
- ignore_extensions(Data, {?MODULE, decode_trailer,
- [<<>>, [],[], MaxHeaderSize,
- Body,
- integer_to_list(AccLength)]});
- _ ->
+ ignore_extensions(Data, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize,
+ {?MODULE, decode_trailer,
+ [<<>>, [],[],
+ Body,
+ integer_to_list(AccLength)]});
+ ChunkSize ->
%% Note decode_data may call decode_size again if there
%% is more than one chunk, hence here is where the last parameter
%% to this function comes in.
decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body,
- ChunkSize + AccLength ,
+ ChunkSize + AccLength,
MaxHeaderSize})
+ catch
+ _:_ ->
+ throw({error, {chunk_size, lists:reverse(HexList)}})
end;
-decode_size(<<";", Rest/binary>>, HexList, Info) ->
+decode_size(<<";", Rest/binary>>, HexList, AccHeaderSize, {_,_,_, MaxHeaderSize} = Info) ->
%% Note ignore_extensions will call decode_size/1 again when
%% it ignored all extensions.
- ignore_extensions(Rest, {?MODULE, decode_size, [<<>>, HexList, Info]});
-decode_size(<<?CR>> = Data, HexList, Info) ->
- {?MODULE, decode_size, [Data, HexList, Info]};
-decode_size(<<Octet, Rest/binary>>, HexList, Info) ->
- decode_size(Rest, [Octet | HexList], Info).
+ ignore_extensions(Rest, remaing_size(MaxHeaderSize, AccHeaderSize), MaxHeaderSize,
+ {?MODULE, decode_size, [<<>>, HexList, AccHeaderSize, Info]});
+decode_size(<<?CR>> = Data, HexList, AccHeaderSize, Info) ->
+ {?MODULE, decode_size, [Data, HexList, AccHeaderSize, Info]};
+decode_size(<<Octet, Rest/binary>>, HexList, AccHeaderSize, Info) ->
+ decode_size(Rest, [Octet | HexList], AccHeaderSize + 1, Info).
%% "All applications MUST ignore chunk-extension extensions they
%% do not understand.", see RFC 2616 Section 3.6.1 We don't
%% understand any extension...
-ignore_extensions(<<>>, NextFunction) ->
- {?MODULE, ignore_extensions, [<<>>, NextFunction]};
-ignore_extensions(Data = <<?CR, ?LF, _ChunkRest/binary>>,
+ignore_extensions(_, 0, TotalMaxHeaderSize, _) ->
+ throw({error, {header_too_long, {max, TotalMaxHeaderSize}}});
+ignore_extensions(<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ {?MODULE, ignore_extensions, [<<>>, RemainingSize, TotalMaxHeaderSize, NextFunction]};
+ignore_extensions(Data = <<?CR, ?LF, _ChunkRest/binary>>, RemainingSize, TotalMaxHeaderSize,
{Module, Function, Args}) ->
- Module:Function([Data | Args]);
-ignore_extensions(<<?CR>> = Data, NextFunction) ->
- {?MODULE, ignore_extensions, [Data, NextFunction]};
-ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) ->
- ignore_extensions(Rest, NextFunction).
+ case Function of
+ decode_trailer ->
+ Module:Function([Data | Args ++ [RemainingSize, TotalMaxHeaderSize]]);
+ _ ->
+ Module:Function([Data | Args])
+ end;
+ignore_extensions(<<?CR>> = Data, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ {?MODULE, ignore_extensions, [Data, RemainingSize, TotalMaxHeaderSize, NextFunction]};
+ignore_extensions(<<_Octet, Rest/binary>>, RemainingSize, TotalMaxHeaderSize, NextFunction) ->
+ ignore_extensions(Rest, remaing_size(RemainingSize, 1), TotalMaxHeaderSize, NextFunction).
decode_data(ChunkSize, TotalChunk,
Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize})
@@ -190,83 +210,89 @@ decode_data(ChunkSize, TotalChunk,
%% once it ignored all extensions.
{?MODULE, ignore_extensions,
[<<>>,
- {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize,
+ {?MODULE, decode_trailer, [<<>>, [],[],
<<BodySoFar/binary, Data/binary>>,
integer_to_list(AccLength)]}]};
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ";", Rest/binary>> ->
%% Note ignore_extensions will call decode_trailer/1
%% once it ignored all extensions.
- ignore_extensions(Rest, {?MODULE, decode_trailer,
- [<<>>, [],[], MaxHeaderSize,
+ ignore_extensions(Rest, MaxHeaderSize, MaxHeaderSize,
+ {?MODULE, decode_trailer,
+ [<<>>, [],[],
<<BodySoFar/binary, Data/binary>>,
integer_to_list(AccLength)]});
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF>> ->
- {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[], MaxHeaderSize,
+ {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[],
<<BodySoFar/binary, Data/binary>>,
- integer_to_list(AccLength)]};
+ integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize]};
<<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF, Rest/binary>> ->
- decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[], MaxHeaderSize,
+ decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[],
<<BodySoFar/binary, Data/binary>>,
- integer_to_list(AccLength));
- %% There are more chunks, so here we go agin...
+ integer_to_list(AccLength), MaxHeaderSize, MaxHeaderSize);
+ %% There are more chunks, so here we go again...
<<Data:ChunkSize/binary, ?CR, ?LF>> ->
NewBody = <<BodySoFar/binary, Data/binary>>,
- {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]};
+ {?MODULE, decode_size, [<<>>, [], 0, {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]};
<<Data:ChunkSize/binary, ?CR, ?LF, Rest/binary>>
when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) ->
- decode_size(Rest, [],
+ decode_size(Rest, [], 0,
{MaxBodySize, <<BodySoFar/binary, Data/binary>>,
AccLength, MaxHeaderSize});
<<_:ChunkSize/binary, ?CR, ?LF, _/binary>> ->
- throw({error, body_too_big});
+ throw({error, {body_too_big, {max, MaxBodySize}}});
_ ->
{?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}
end;
decode_data(ChunkSize, TotalChunk, Info) ->
{?MODULE, decode_data, [ChunkSize, TotalChunk, Info]}.
-decode_trailer(<<>>, Header, Headers, MaxHeaderSize, Body, BodyLength) ->
- {?MODULE, decode_trailer, [<<>>, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-
+decode_trailer(_,_,_,_,_, 0, TotalMaxHeaderSize) ->
+ throw({error, {header_too_long, {max, TotalMaxHeaderSize}}});
+decode_trailer(<<>>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [<<>>, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
%% Note: If Bin is not empty it is part of a pipelined request/response.
-decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, [], [], _, Body, BodyLength) ->
+decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>, [], [], Body, BodyLength, _, _) ->
{ok, {["content-length:" ++ BodyLength], <<Body/binary, Bin/binary>>}};
decode_trailer(<<?CR,?LF,?CR,?LF, Bin/binary>>,
- Header, Headers, MaxHeaderSize, Body, BodyLength) ->
+ Header, Headers, Body, BodyLength, _, _) ->
NewHeaders = case Header of
[] ->
Headers;
_ ->
[lists:reverse(Header) | Headers]
end,
- Length = length(NewHeaders),
- case Length > MaxHeaderSize of
- true ->
- throw({error, {header_too_long, MaxHeaderSize,
- MaxHeaderSize-Length}});
- false ->
- {ok, {["content-length:" ++ BodyLength | NewHeaders],
- <<Body/binary, Bin/binary>>}}
- end;
-decode_trailer(<<?CR,?LF,?CR>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR,?LF>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR>> = Data, Header, Headers, MaxHeaderSize,
- Body, BodyLength) ->
- {?MODULE, decode_trailer, [Data, Header, Headers, MaxHeaderSize, Body,
- BodyLength]};
-decode_trailer(<<?CR, ?LF, Rest/binary>>, Header, Headers,
- MaxHeaderSize, Body, BodyLength) ->
+ {ok, {["content-length:" ++ BodyLength | NewHeaders],
+ <<Body/binary, Bin/binary>>}};
+decode_trailer(<<?CR,?LF,?CR>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR,?LF>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR>> = Data, Header, Headers,
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ {?MODULE, decode_trailer, [Data, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize]};
+decode_trailer(<<?CR, ?LF, Rest/binary>>, Header, Headers, Body, BodyLength, RemainingSize, TotalMaxHeaderSize) ->
decode_trailer(Rest, [], [lists:reverse(Header) | Headers],
- MaxHeaderSize, Body, BodyLength);
+ Body, BodyLength, RemainingSize, TotalMaxHeaderSize);
+decode_trailer(<<Octet, Rest/binary>>, Header, Headers, Body,
+ BodyLength, RemainingSize, TotalMaxHeaderSize) ->
+ decode_trailer(Rest, [Octet | Header], Headers,
+ Body, BodyLength, remaing_size(RemainingSize, 1), TotalMaxHeaderSize).
+
+remaing_size(nolimit, _) ->
+ nolimit;
+remaing_size(Total, Consumed) ->
+ Total - Consumed.
-decode_trailer(<<Octet, Rest/binary>>, Header, Headers, MaxHeaderSize, Body,
- BodyLength) ->
- decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize,
- Body, BodyLength).
+encode_trailers(Trailers) ->
+ encode_trailers(Trailers, "").
+
+encode_trailers([], Acc) ->
+ Acc ++ ?CRLF ++ ?CRLF;
+encode_trailers([{Header, Value} | Rest], Acc) ->
+ encode_trailers(Rest, Header ++ ":" ++ Value ++ ?CRLF ++ Acc).
diff --git a/lib/inets/src/http_lib/http_response.erl b/lib/inets/src/http_lib/http_response.erl
index d13670700c..42e5dd263d 100644
--- a/lib/inets/src/http_lib/http_response.erl
+++ b/lib/inets/src/http_lib/http_response.erl
@@ -65,6 +65,8 @@ header_list(Headers) ->
%%%========================================================================
fill_headers([], _, Headers) ->
Headers;
+fill_headers([[]], _, Headers) ->
+ Headers;
fill_headers([[Ch|HeaderFold]|Tail], Folded, Headers)
when Ch == $\t; Ch == $\s ->
fill_headers(Tail, [HeaderFold|Folded], Headers);
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 719dc4c425..ab6afe9c6c 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -40,12 +40,6 @@
-include_lib("inets/src/inets_app/inets_internal.hrl").
-include("http_internal.hrl").
--define(SERVICE, httpl).
--define(hlri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
--define(hlrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
--define(hlrd(Label, Content), ?report_debug(Label, ?SERVICE, Content)).
--define(hlrt(Label, Content), ?report_trace(Label, ?SERVICE, Content)).
-
%%%=========================================================================
%%% Internal application API
@@ -55,38 +49,27 @@
%% start(SocketType) -> ok | {error, Reason}
%% SocketType = ip_comm | {ssl, _}
%%
-%% Description: Makes sure inet_db or ssl is started.
+%% Description: Makes sure ssl is started.
%%-------------------------------------------------------------------------
start(ip_comm) ->
- do_start_ip_comm();
-
-%% This is just for backward compatibillity
+ ok;
+start({ip_comm, _}) ->
+ ok;
start({ssl, _}) ->
do_start_ssl();
start({essl, _}) ->
do_start_ssl().
-
-do_start_ip_comm() ->
- case inet_db:start() of
- {ok, _} ->
- ok;
- {error, {already_started, _}} ->
- ok;
- Error ->
- Error
- end.
-
do_start_ssl() ->
- case ssl:start() of
- ok ->
- ok;
- {error, {already_started,_}} ->
- ok;
- Error ->
- Error
+ try lists:foreach(fun(App) ->
+ ok = application:ensure_started(App)
+ end,
+ [crypto, asn1, public_key, ssl])
+ catch
+ _:Reason ->
+ {error, Reason}
end.
-
+
%%-------------------------------------------------------------------------
%% connect(SocketType, Address, Options, Timeout) ->
@@ -103,12 +86,8 @@ do_start_ssl() ->
connect(SocketType, Address, Opts) ->
connect(SocketType, Address, Opts, infinity).
-
-connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout)
- when is_list(Opts0) ->
- Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0],
- ?hlrt("connect using gen_tcp",
- [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]),
+connect(ip_comm, {Host, Port}, Opts0, Timeout) ->
+ Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0 ],
try gen_tcp:connect(Host, Port, Opts, Timeout) of
{ok, _} = OK ->
OK;
@@ -127,11 +106,6 @@ connect({ssl, SslConfig}, Address, 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}};
@@ -156,29 +130,23 @@ connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) ->
%% reason for this to enable a HTTP-server not running as root to use
%% port 80.
%%-------------------------------------------------------------------------
-listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) ->
- listen_ip_comm(Addr, Port, Fd, IpFamily);
-
+listen(ip_comm, Addr, Port, Fd, IpFamily) ->
+ listen_ip_comm(Addr, Port, [], Fd, IpFamily);
+
+listen({ip_comm, SockOpts}, Addr, Port, Fd, IpFamily) ->
+ listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily);
+
listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []).
-listen(ip_comm = _SocketType, Addr, Port, IpFamily) ->
- listen_ip_comm(Addr, Port, undefined, IpFamily);
+listen(ip_comm, Addr, Port, IpFamily) ->
+ listen_ip_comm(Addr, Port, [], undefined, IpFamily);
%% Wrapper for backaward compatibillity
listen({ssl, SSLConfig}, Addr, Port, IpFamily) ->
- ?hlrt("listen (wrapper)",
- [{addr, Addr},
- {port, Port},
- {ssl_config, SSLConfig}]),
listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port, IpFamily);
-
listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
- ?hlrt("listen (essl)",
- [{addr, Addr},
- {port, Port},
- {ssl_config, SSLConfig}]),
{SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of
undefined ->
{SSLConfig, []};
@@ -187,83 +155,30 @@ listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
end,
listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts).
-listen_ip_comm(Addr, Port, Fd, IpFamily) ->
- case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of
+listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
+ case (catch do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily)) of
{'EXIT', Reason} ->
{error, {exit, Reason}};
Else ->
Else
end.
-do_listen_ip_comm(Addr, Port, Fd, IpFamily) ->
- {NewPort, Opts} = get_socket_info(Addr, Port, Fd),
- case IpFamily of
- inet6fb4 ->
- Opts2 = [inet6 | Opts],
- ?hlrt("try ipv6 listen", [{port, NewPort}, {opts, Opts2}]),
- case (catch gen_tcp:listen(NewPort, Opts2)) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen failed - try ipv4 instead",
- [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
- gen_tcp:listen(NewPort, Opts3);
-
- %% This is when a given hostname has resolved to a
- %% IPv4-address. The inet6-option together with a
- %% {ip, IPv4} option results in badarg
- {'EXIT', Reason} ->
- Opts3 = [inet | Opts],
- ?hlrt("ipv6 listen exit - try ipv4 instead",
- [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
- gen_tcp:listen(NewPort, Opts3);
-
- Other ->
- ?hlrt("ipv6 listen done", [{other, Other}]),
- Other
- end;
- _ ->
- Opts2 = [IpFamily | Opts],
- ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]),
- gen_tcp:listen(NewPort, Opts2)
- end.
+do_listen_ip_comm(Addr, Port, SockOpts, Fd, IpFamily) ->
+ Backlog = proplists:get_value(backlog, SockOpts, 128),
+ {NewPort, Opts} = get_socket_info(Addr, Port, Fd,
+ [{backlog, Backlog}, {reuseaddr, true} | SockOpts]),
+ Opts2 = [IpFamily | Opts],
+ gen_tcp:listen(NewPort, Opts2).
listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) ->
- {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd),
+ Backlog = proplists:get_value(backlog, Opts0, 128),
+ {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd,
+ [{backlog, Backlog}, {reuseaddr, true}]),
Opts = SockOpt ++ Opts0,
- case IpFamily of
- inet6fb4 ->
- Opts2 = [inet6 | Opts] ++ ExtraOpts,
- ?hlrt("try ipv6 listen", [{opts, Opts2}]),
- case (catch ssl:listen(Port, Opts2)) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
- Opts3 = [inet | Opts] ++ ExtraOpts,
- ?hlrt("ipv6 listen failed - try ipv4 instead",
- [{reason, Reason}, {opts, Opts3}]),
- ssl:listen(NewPort, Opts3);
-
- {'EXIT', Reason} ->
- Opts3 = [inet | Opts] ++ ExtraOpts,
- ?hlrt("ipv6 listen exit - try ipv4 instead",
- [{reason, Reason}, {opts, Opts3}]),
- ssl:listen(NewPort, Opts3);
-
- Other ->
- ?hlrt("ipv6 listen done", [{other, Other}]),
- Other
- end;
-
- _ ->
- Opts2 = [IpFamily | Opts],
- ?hlrt("listen", [{opts, Opts2}]),
- ssl:listen(NewPort, Opts2 ++ ExtraOpts)
- end.
+ Opts2 = [IpFamily | Opts],
+ ssl:listen(NewPort, Opts2 ++ ExtraOpts).
-
-
-get_socket_info(Addr, Port, Fd) ->
- BaseOpts = [{backlog, 128}, {reuseaddr, true}],
+get_socket_info(Addr, Port, Fd, BaseOpts) ->
%% The presence of a file descriptor takes precedence
case Fd of
undefined ->
@@ -288,6 +203,8 @@ accept(SocketType, ListenSocket) ->
accept(ip_comm, ListenSocket, Timeout) ->
gen_tcp:accept(ListenSocket, Timeout);
+accept({ip_comm, _}, ListenSocket, Timeout) ->
+ gen_tcp:accept(ListenSocket, Timeout);
%% Wrapper for backaward compatibillity
accept({ssl, SSLConfig}, ListenSocket, Timeout) ->
@@ -307,6 +224,8 @@ accept({essl, _SSLConfig}, ListenSocket, Timeout) ->
%%-------------------------------------------------------------------------
controlling_process(ip_comm, Socket, NewOwner) ->
gen_tcp:controlling_process(Socket, NewOwner);
+controlling_process({ip_comm, _}, Socket, NewOwner) ->
+ gen_tcp:controlling_process(Socket, NewOwner);
%% Wrapper for backaward compatibillity
controlling_process({ssl, SSLConfig}, Socket, NewOwner) ->
@@ -325,7 +244,8 @@ controlling_process({essl, _}, Socket, NewOwner) ->
%% gen_tcp or ssl.
%%-------------------------------------------------------------------------
setopts(ip_comm, Socket, Options) ->
- ?hlrt("ip_comm setopts", [{socket, Socket}, {options, Options}]),
+ inet:setopts(Socket, Options);
+setopts({ip_comm, _}, Socket, Options) ->
inet:setopts(Socket, Options);
%% Wrapper for backaward compatibillity
@@ -333,10 +253,7 @@ setopts({ssl, SSLConfig}, Socket, Options) ->
setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
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.
+ (catch ssl:setopts(Socket, Options)).
%%-------------------------------------------------------------------------
@@ -350,8 +267,10 @@ getopts(SocketType, Socket) ->
Opts = [packet, packet_size, recbuf, sndbuf, priority, tos, send_timeout],
getopts(SocketType, Socket, Opts).
+getopts({ip_comm, _}, Socket, Options) ->
+ getopts(ip_comm, Socket, Options);
+
getopts(ip_comm, Socket, Options) ->
- ?hlrt("ip_comm getopts", [{socket, Socket}, {options, Options}]),
case inet:getopts(Socket, Options) of
{ok, SocketOpts} ->
SocketOpts;
@@ -364,7 +283,6 @@ getopts({ssl, SSLConfig}, Socket, Options) ->
getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
getopts({essl, _}, Socket, Options) ->
- ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]),
getopts_ssl(Socket, Options).
getopts_ssl(Socket, Options) ->
@@ -384,7 +302,6 @@ getopts_ssl(Socket, Options) ->
%% Description: Gets the socket stats values for the socket
%%-------------------------------------------------------------------------
getstat(ip_comm = _SocketType, Socket) ->
- ?hlrt("ip_comm getstat", [{socket, Socket}]),
case inet:getstat(Socket) of
{ok, Stats} ->
Stats;
@@ -409,6 +326,8 @@ getstat({essl, _} = _SocketType, _Socket) ->
%%-------------------------------------------------------------------------
send(ip_comm, Socket, Message) ->
gen_tcp:send(Socket, Message);
+send({ip_comm, _}, Socket, Message) ->
+ gen_tcp:send(Socket, Message);
%% Wrapper for backaward compatibillity
send({ssl, SSLConfig}, Socket, Message) ->
@@ -417,7 +336,6 @@ send({ssl, SSLConfig}, Socket, Message) ->
send({essl, _}, Socket, Message) ->
ssl:send(Socket, Message).
-
%%-------------------------------------------------------------------------
%% close(SocketType, Socket) -> ok | {error, Reason}
%% SocketType = ip_comm | {ssl, _}
@@ -427,6 +345,8 @@ send({essl, _}, Socket, Message) ->
%%-------------------------------------------------------------------------
close(ip_comm, Socket) ->
gen_tcp:close(Socket);
+close({ip_comm, []}, Socket) ->
+ gen_tcp:close(Socket);
%% Wrapper for backaward compatibillity
close({ssl, SSLConfig}, Socket) ->
@@ -448,6 +368,8 @@ close({essl, _}, Socket) ->
%%-------------------------------------------------------------------------
peername(ip_comm, Socket) ->
do_peername(inet:peername(Socket));
+peername({ip_comm, _}, Socket) ->
+ do_peername(inet:peername(Socket));
%% Wrapper for backaward compatibillity
peername({ssl, SSLConfig}, Socket) ->
@@ -480,7 +402,8 @@ do_peername({error, _}) ->
%%-------------------------------------------------------------------------
sockname(ip_comm, Socket) ->
do_sockname(inet:sockname(Socket));
-
+sockname({ip_comm, _}, Socket) ->
+ do_sockname(inet:sockname(Socket));
%% Wrapper for backaward compatibillity
sockname({ssl, SSLConfig}, Socket) ->
sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
@@ -555,28 +478,13 @@ sock_opts(Opts) ->
%% -- negotiate --
negotiate(ip_comm,_,_) ->
- ?hlrt("negotiate(ip_comm)", []),
+ ok;
+negotiate({ip_comm, _},_,_) ->
ok;
negotiate({ssl, SSLConfig}, Socket, Timeout) ->
- ?hlrt("negotiate(ssl)", []),
negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, 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, 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};
- false ->
- {error, Reason}
- end
- end.
+ ssl:ssl_accept(Socket, Timeout).
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index 0d07231302..aafa97afee 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -152,27 +152,11 @@ convert_netscapecookie_date([_D,_A,_Y, _SP,
Sec=list_to_integer([S1,S2]),
{{Year,Month,Day},{Hour,Min,Sec}}.
-hexlist_to_integer([]) ->
- empty;
-%%When the string only contains one value its eaasy done.
-%% 0-9
-hexlist_to_integer([Size]) when (Size >= 48) andalso (Size =< 57) ->
- Size - 48;
-%% A-F
-hexlist_to_integer([Size]) when (Size >= 65) andalso (Size =< 70) ->
- Size - 55;
-%% a-f
-hexlist_to_integer([Size]) when (Size >= 97) andalso (Size =< 102) ->
- Size - 87;
-hexlist_to_integer([_Size]) ->
- not_a_num;
+hexlist_to_integer(List) ->
+ list_to_integer(List, 16).
-hexlist_to_integer(Size) ->
- Len = string:span(Size, "1234567890abcdefABCDEF"),
- hexlist_to_integer2(Size, 16 bsl (4 *(Len-2)),0).
-
-integer_to_hexlist(Num)->
- integer_to_hexlist(Num, get_size(Num), []).
+integer_to_hexlist(Int) ->
+ integer_to_list(Int, 16).
convert_month("Jan") -> 1;
convert_month("Feb") -> 2;
@@ -213,51 +197,6 @@ html_encode(Chars) ->
%%%========================================================================
%%% Internal functions
%%%========================================================================
-hexlist_to_integer2([],_Pos,Sum)->
- Sum;
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal >= 48, HexVal =< 57 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-48) * Pos));
-
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal >= 65, HexVal =<70 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-55) * Pos));
-
-hexlist_to_integer2([HexVal | HexString], Pos, Sum)
- when HexVal>=97, HexVal=<102 ->
- hexlist_to_integer2(HexString, Pos bsr 4, Sum + ((HexVal-87) * Pos));
-
-hexlist_to_integer2(_AfterHexString, _Pos, Sum)->
- Sum.
-
-integer_to_hexlist(Num, Pot, Res) when Pot < 0 ->
- convert_to_ascii([Num | Res]);
-
-integer_to_hexlist(Num,Pot,Res) ->
- Position = (16 bsl (Pot*4)),
- PosVal = Num div Position,
- integer_to_hexlist(Num - (PosVal*Position), Pot-1, [PosVal | Res]).
-
-get_size(Num)->
- get_size(Num, 0).
-
-get_size(Num, Pot) when Num < (16 bsl(Pot *4)) ->
- Pot-1;
-
-get_size(Num, Pot) ->
- get_size(Num, Pot+1).
-
-convert_to_ascii(RevesedNum) ->
- convert_to_ascii(RevesedNum, []).
-
-convert_to_ascii([], Num)->
- Num;
-convert_to_ascii([Num | Reversed], Number)
- when (Num > -1) andalso (Num < 10) ->
- convert_to_ascii(Reversed, [Num + 48 | Number]);
-convert_to_ascii([Num | Reversed], Number)
- when (Num > 9) andalso (Num < 16) ->
- convert_to_ascii(Reversed, [Num + 55 | Number]).
char_to_html_entity(Char, Reserved) ->
case sets:is_element(Char, Reserved) of
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index b9f2290289..1c05d454a5 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -137,7 +137,7 @@ release_spec: opt
$(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"
+ $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin"
release_docs_spec:
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 7d31989244..62e8a95b19 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -156,7 +156,7 @@ load("BindAddress " ++ Address0, []) ->
case string:tokens(Address0, [$|]) of
[Address1] ->
?hdrv("load BindAddress", [{address1, Address1}]),
- {clean_address(Address1), inet6fb4};
+ {clean_address(Address1), inet};
[Address1, IpFamilyStr] ->
?hdrv("load BindAddress",
[{address1, Address1},
@@ -353,14 +353,21 @@ clean_address(Addr) ->
make_ipfamily(IpFamilyStr) ->
- IpFamily = list_to_atom(IpFamilyStr),
- case lists:member(IpFamily, [inet, inet6, inet6fb4]) of
- true ->
- IpFamily;
- false ->
- throw({error, {bad_ipfamily, IpFamilyStr}})
- end.
-
+ validate_ipfamily(list_to_atom(IpFamilyStr)).
+
+validate_ipfamily(inet) ->
+ inet;
+validate_ipfamily(inet6) ->
+ inet6;
+%% Backwards compatibility wrapper,
+%% fallback to the default, IPV4,
+%% as it will most proably work.
+%% IPv6 standard moved away from
+%% beeing able to fallback to ipv4
+validate_ipfamily(inet6fb4) ->
+ inet;
+validate_ipfamily(IpFamilyStr) ->
+ throw({error, {bad_ipfamily, IpFamilyStr}}).
%%
%% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason}
@@ -393,20 +400,16 @@ validate_properties2(Properties) ->
undefined ->
case proplists:get_value(sock_type, Properties, ip_comm) of
ip_comm ->
- case proplists:get_value(ipfamily, Properties) of
- undefined ->
- [{bind_address, any},
- {ipfamily, inet6fb4} | Properties];
- _ ->
- [{bind_address, any} | Properties]
- end;
+ add_inet_defaults(Properties);
+ {ip_comm, _} ->
+ add_inet_defaults(Properties);
_ ->
[{bind_address, any} | Properties]
end;
any ->
Properties;
Address0 ->
- IpFamily = proplists:get_value(ipfamily, Properties, inet6fb4),
+ IpFamily = proplists:get_value(ipfamily, Properties, inet),
case httpd_util:ip_address(Address0, IpFamily) of
{ok, Address} ->
Properties1 = proplists:delete(bind_address, Properties),
@@ -418,6 +421,16 @@ validate_properties2(Properties) ->
throw(Error)
end
end.
+
+add_inet_defaults(Properties) ->
+ case proplists:get_value(ipfamily, Properties) of
+ undefined ->
+ [{bind_address, any},
+ {ipfamily, inet} | Properties];
+ _ ->
+ [{bind_address, any} | Properties]
+ end.
+
check_minimum_bytes_per_second(Properties) ->
case proplists:get_value(minimum_bytes_per_second, Properties, false) of
false ->
@@ -487,12 +500,11 @@ validate_config_params([{server_tokens, Value} | _]) ->
validate_config_params([{socket_type, ip_comm} | Rest]) ->
validate_config_params(Rest);
-validate_config_params([{socket_type, Value} | Rest])
- when Value == ssl; Value == essl ->
- validate_config_params(Rest);
-
-validate_config_params([{socket_type, {Value, _}} | Rest])
- when Value == essl orelse Value == ssl ->
+validate_config_params([{socket_type, {Value, Opts}} | Rest]) when Value == ip_comm;
+ Value == ssl;
+ Value == essl ->
+ %% Make sure not to set socket values used internaly
+ validate_config_params(Opts),
validate_config_params(Rest);
validate_config_params([{socket_type, Value} | _]) ->
@@ -622,21 +634,32 @@ validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
validate_config_params([{disable_chunked_transfer_encoding_send, Value} |
_ ]) ->
throw({disable_chunked_transfer_encoding_send, Value});
+validate_config_params([{Name, _} = Opt | _]) when Name == packet;
+ Name == mode;
+ Name == active;
+ Name == reuseaddr ->
+ throw({internaly_handled_opt_can_not_be_set, Opt});
validate_config_params([_| Rest]) ->
validate_config_params(Rest).
-%% It is actually pointless to check bind_address in this way since
-%% we need ipfamily to do it properly...
is_bind_address(any) ->
true;
is_bind_address(Value) ->
- case httpd_util:ip_address(Value, inet6fb4) of
+ case is_bind_address(Value, inet) of
+ false ->
+ is_bind_address(Value, inet6);
+ True ->
+ True
+ end.
+
+is_bind_address(Value, IpFamily) ->
+ case httpd_util:ip_address(Value, IpFamily) of
{ok, _} ->
true;
_ ->
false
end.
-
+
store(ConfigList0) ->
?hdrd("store", []),
try validate_config_params(ConfigList0) of
@@ -776,28 +799,6 @@ 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.
-
get_config(Address, Port, Profile) ->
Tab = httpd_util:make_name("httpd_conf", Address, Port, Profile),
@@ -836,6 +837,8 @@ lookup_socket_type(ConfigDB) ->
case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
ip_comm ->
ip_comm;
+ {ip_comm, _} = Type ->
+ Type;
{Tag, Conf} ->
{Tag, Conf};
SSL when (SSL =:= ssl) orelse (SSL =:= essl) ->
diff --git a/lib/inets/src/http_server/httpd_custom_api.erl b/lib/inets/src/http_server/httpd_custom_api.erl
index 282f3a6ee6..d5a6fa8715 100644
--- a/lib/inets/src/http_server/httpd_custom_api.erl
+++ b/lib/inets/src/http_server/httpd_custom_api.erl
@@ -23,7 +23,8 @@
-callback response_default_headers() ->
[{Key::string(), Value::string()}].
-callback response_header({Key::string(), Value::string()}) ->
- {true, {Key::string(), Value::string()}} | false.
+ {true, {Key::string(), Value::string()}} | false |
+ {true, string()}. %% Used internally to avoid traversing headers twice
-callback request_header({Key::string(), Value::string()}) ->
{true, {Key::string(), Value::string()}} | false.
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index d729affd6d..0222487a4b 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -24,7 +24,7 @@
-export([newformat/3]).
%% These are used by the inets test-suite
--export([delay/1]).
+-export([delay/1, chunk_timeout/3]).
print(String) ->
@@ -142,3 +142,11 @@ i(F) -> i(F,[]).
i(F,A) -> io:format(F ++ "~n",A).
sleep(T) -> receive after T -> ok end.
+
+%% ------------------------------------------------------
+
+chunk_timeout(SessionID, _, StrInt) ->
+ mod_esi:deliver(SessionID, "Tranfer-Encoding:chunked/html\r\n\r\n"),
+ mod_esi:deliver(SessionID, top("Test chunk encoding timeout")),
+ timer:sleep(20000),
+ mod_esi:deliver(SessionID, footer()).
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index e5d006c1fd..134576059d 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -443,7 +443,7 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
MaxHeaderSize, MaxBodySize) ->
case Headers#http_request_h.'transfer-encoding' of
"chunked" ->
- case http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
+ try http_chunk:decode(Body, MaxBodySize, MaxHeaderSize) of
{Module, Function, Args} ->
http_transport:setopts(ModData#mod.socket_type,
ModData#mod.socket,
@@ -455,6 +455,14 @@ handle_body(#state{headers = Headers, body = Body, mod = ModData} = State,
http_chunk:handle_headers(Headers, ChunkedHeaders),
handle_response(State#state{headers = NewHeaders,
body = NewBody})
+ catch
+ throw:Error ->
+ httpd_response:send_status(ModData, 400,
+ "Bad input"),
+ Reason = io_lib:format("Chunk decoding failed: ~p~n",
+ [Error]),
+ error_log(Reason, ModData),
+ {stop, normal, State#state{response_sent = true}}
end;
Encoding when is_list(Encoding) ->
httpd_response:send_status(ModData, 501,
@@ -622,21 +630,10 @@ decrease(N) when is_integer(N) ->
decrease(N) ->
N.
-error_log(ReasonString, Info) ->
+error_log(ReasonString, #mod{config_db = ConfigDB}) ->
Error = lists:flatten(
io_lib:format("Error reading request: ~s", [ReasonString])),
- error_log(mod_log, Info, Error),
- error_log(mod_disk_log, Info, Error).
-
-error_log(Mod, #mod{config_db = ConfigDB} = Info, String) ->
- Modules = httpd_util:lookup(ConfigDB, modules,
- [mod_get, mod_head, mod_log]),
- case lists:member(Mod, Modules) of
- true ->
- Mod:error_log(Info, String);
- _ ->
- ok
- end.
+ httpd_util:error_log(ConfigDB, Error).
%%--------------------------------------------------------------------
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 71243f525a..c0b5f09faf 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -20,8 +20,8 @@
%%
-module(httpd_response).
-export([generate_and_send_response/1, send_status/3, send_header/3,
- send_body/3, send_chunk/3, send_final_chunk/2, split_header/2,
- is_disable_chunked_send/1, cache_headers/2]).
+ send_body/3, send_chunk/3, send_final_chunk/2, send_final_chunk/3,
+ split_header/2, is_disable_chunked_send/1, cache_headers/2]).
-export([map_status_code/2]).
-include_lib("inets/src/inets_app/inets_internal.hrl").
@@ -89,8 +89,7 @@ traverse_modules(ModData,[Module|Rest]) ->
"~n Error: ~p"
"~n Stack trace: ~p",
[Module, T, E, ?STACK()])),
- report_error(mod_log, ModData#mod.config_db, String),
- report_error(mod_disk_log, ModData#mod.config_db, String),
+ httpd_util:error_log(ModData#mod.config_db, String),
send_status(ModData, 500, none),
done
end.
@@ -245,7 +244,6 @@ send_chunk(_, <<>>, _) ->
ok;
send_chunk(_, [], _) ->
ok;
-
send_chunk(#mod{http_version = "HTTP/1.1",
socket_type = Type, socket = Sock}, Response0, false) ->
Response = http_chunk:encode(Response0),
@@ -254,10 +252,13 @@ send_chunk(#mod{http_version = "HTTP/1.1",
send_chunk(#mod{socket_type = Type, socket = Sock} = _ModData, Response, _) ->
httpd_socket:deliver(Type, Sock, Response).
+send_final_chunk(Mod, IsDisableChunkedSend) ->
+ send_final_chunk(Mod, [], IsDisableChunkedSend).
+
send_final_chunk(#mod{http_version = "HTTP/1.1",
- socket_type = Type, socket = Sock}, false) ->
- httpd_socket:deliver(Type, Sock, http_chunk:encode_last());
-send_final_chunk(#mod{socket_type = Type, socket = Sock}, _) ->
+ socket_type = Type, socket = Sock}, Trailers, false) ->
+ httpd_socket:deliver(Type, Sock, http_chunk:encode_last(Trailers));
+send_final_chunk(#mod{socket_type = Type, socket = Sock}, _, _) ->
httpd_socket:close(Type, Sock).
is_disable_chunked_send(Db) ->
@@ -397,16 +398,6 @@ send_response_old(#mod{socket_type = Type,
content_length(Body)->
integer_to_list(httpd_util:flatlength(Body)).
-report_error(Mod, ConfigDB, Error) ->
- Modules = httpd_util:lookup(ConfigDB, modules,
- [mod_get, mod_head, mod_log]),
- case lists:member(Mod, Modules) of
- true ->
- Mod:report_error(ConfigDB, Error);
- _ ->
- ok
- end.
-
handle_headers([], NewHeaders) ->
{ok, NewHeaders};
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index f0b1942e2f..bf40cedd5c 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -241,7 +241,7 @@ listen(Address, Port, Config) ->
case http_transport:start(SocketType) of
ok ->
{ok, Fd} = get_fd(Port),
- IpFamily = proplists:get_value(ipfamily, Config, inet6fb4),
+ IpFamily = proplists:get_value(ipfamily, Config, inet),
case http_transport:listen(SocketType, Address, Port, Fd, IpFamily) of
{ok, ListenSocket} ->
NewConfig = proplists:delete(port, Config),
@@ -286,6 +286,8 @@ socket_type(Config) ->
socket_type(ip_comm = SocketType, _) ->
SocketType;
+socket_type({ip_comm, _} = SocketType, _) ->
+ SocketType;
socket_type({essl, _} = SocketType, _) ->
SocketType;
socket_type(_, Config) ->
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index fc69baf829..ab43f0b378 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -31,7 +31,7 @@
convert_netscapecookie_date/1, enable_debug/1, valid_options/3,
modules_validate/1, module_validate/1,
dir_validate/2, file_validate/2, mime_type_validate/1,
- mime_types_validate/1, custom_date/0]).
+ mime_types_validate/1, custom_date/0, error_log/2]).
-export([encode_hex/1, decode_hex/1]).
-include_lib("kernel/include/file.hrl").
@@ -42,17 +42,7 @@ ip_address({_,_,_,_,_,_,_,_} = Address, _IpFamily) ->
{ok, Address};
ip_address(Host, IpFamily)
when ((IpFamily =:= inet) orelse (IpFamily =:= inet6)) ->
- inet:getaddr(Host, IpFamily);
-ip_address(Host, inet6fb4 = _IpFamily) ->
- Inet = case gen_tcp:listen(0, [inet6]) of
- {ok, Dummyport} ->
- gen_tcp:close(Dummyport),
- inet6;
- _ ->
- inet
- end,
- inet:getaddr(Host, Inet).
-
+ inet:getaddr(Host, IpFamily).
%% lookup
@@ -786,3 +776,17 @@ do_enable_debug([{Level,Modules}|Rest])
ok
end,
do_enable_debug(Rest).
+
+error_log(ConfigDb, Error) ->
+ error_log(mod_log, ConfigDb, Error),
+ error_log(mod_disk_log, ConfigDb, Error).
+
+error_log(Mod, ConfigDB, Error) ->
+ Modules = httpd_util:lookup(ConfigDB, modules,
+ [mod_get, mod_head, mod_log]),
+ case lists:member(Mod, Modules) of
+ true ->
+ Mod:report_error(ConfigDB, Error);
+ _ ->
+ ok
+ end.
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index b9a0797977..1923411449 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -376,7 +376,6 @@ erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
end),
Response = deliver_webpage_chunk(ModData, Pid),
-
process_flag(trap_exit,false),
Response.
@@ -418,7 +417,6 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
?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]}
end.
@@ -446,7 +444,6 @@ send_headers(ModData, StatusCode, HTTPHeaders) ->
ExtraHeaders ++ HTTPHeaders).
handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- process_flag(trap_exit,false),
{proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
@@ -454,34 +451,54 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
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})
-
+ Error = lists:flatten(io_lib:format("mod_esi process failed with reason ~p", [Reason])),
+ httpd_util:error_log(ModData#mod.config_db, Error),
+ httpd_response:send_final_chunk(ModData,
+ [{"Warning", "199 inets server - body maybe incomplete, "
+ "internal server error"}],
+ IsDisableChunkedSend),
+ done
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})
+ kill_esi_delivery_process(Pid),
+ httpd_response:send_final_chunk(ModData, [{"Warning", "199 inets server - "
+ "body maybe incomplete, timed out"}],
+ IsDisableChunkedSend),
+ done
end.
+kill_esi_delivery_process(Pid) ->
+ exit(Pid, kill),
+ receive
+ {'EXIT', Pid, killed} ->
+ %% Clean message queue
+ receive
+ {esi_data, _} ->
+ ok
+ after 0 ->
+ ok
+ end,
+ receive
+ {ok, _} ->
+ ok
+ after 0 ->
+ ok
+ end
+ end.
+
+
erl_script_timeout(Db) ->
httpd_util:lookup(Db, erl_script_timeout, ?DEFAULT_ERL_TIMEOUT).
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 2b9b8f5f32..883ba84e8e 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -65,6 +65,7 @@
httpd_connection_sup,
httpd_conf,
httpd_custom,
+ httpd_custom_api,
httpd_esi,
httpd_example,
httpd_file,
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index a97b51601f..e977bd1b9b 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -20,26 +20,12 @@
%%
-module(http_format_SUITE).
--author('[email protected]').
-include_lib("common_test/include/ct.hrl").
--include("test_server_line.hrl").
-include("http_internal.hrl").
-%% Test server specific exports
--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_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,
- check_content_length_encoding/1]).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
all() ->
[{group, chunk}, http_response, http_request,
@@ -52,7 +38,8 @@ groups() ->
[chunk_decode, chunk_encode, chunk_extensions_otp_6005,
chunk_decode_otp_6264,
chunk_decode_empty_chunk_otp_6511,
- chunk_decode_trailer]}].
+ chunk_whitespace_suffix,
+ chunk_decode_trailer, chunk_max_headersize, chunk_max_bodysize, chunk_not_hex]}].
init_per_suite(Config) ->
Config.
@@ -81,12 +68,8 @@ end_per_testcase(_, Config) ->
%% Test cases starts here.
%%-------------------------------------------------------------------------
-
-%%-------------------------------------------------------------------------
-chunk_decode(doc) ->
- ["Test http_chunk:decode/3"];
-chunk_decode(suite) ->
- [];
+chunk_decode() ->
+ [{doc, "Test http_chunk:decode/3"}].
chunk_decode(Config) when is_list(Config) ->
ReqHeaders = #http_request_h{'transfer-encoding' = "chunked"},
ChunkedBody = "A" ++ ?CRLF ++ "1234567890" ++ ?CRLF ++ "4" ++
@@ -109,15 +92,11 @@ chunk_decode(Config) when is_list(Config) ->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, Body} = parse(Module, Function, Args, tl(NewChunkedBody)),
- "1234567890HEJ!" = binary_to_list(Body),
-
- ok.
+ "1234567890HEJ!" = binary_to_list(Body).
%%-------------------------------------------------------------------------
-chunk_extensions_otp_6005(doc) ->
- ["Make sure so called extensions are ignored"];
-chunk_extensions_otp_6005(suite) ->
- [];
+chunk_extensions_otp_6005() ->
+ [{doc, "Make sure so called extensions are ignored"}].
chunk_extensions_otp_6005(Config) when is_list(Config)->
ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++
?CRLF ++ "4" ++ ?CRLF ++ "HEJ!"++ ?CRLF ++ "0" ++
@@ -136,14 +115,11 @@ chunk_extensions_otp_6005(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody1)),
- "1234567890HEJ!" = binary_to_list(NewBody),
- ok.
+ "1234567890HEJ!" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_decode_otp_6264(doc) ->
- ["Check that 0 in the body does not count as the last chunk"];
-chunk_decode_otp_6264(suite) ->
- [];
+chunk_decode_otp_6264() ->
+ [{doc, "Check that 0 in the body does not count as the last chunk"}].
chunk_decode_otp_6264(Config) when is_list(Config)->
ChunkedBody = "A;ignore this" ++ ?CRLF ++ "1234567890" ++
?CRLF ++ "4" ++ ?CRLF ++ "0123"++ ?CRLF ++ "0" ++
@@ -173,27 +149,33 @@ chunk_decode_otp_6264(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(NewChunkedBody1)),
- "12345678900" = binary_to_list(NewBody),
-
- ok.
+ "12345678900" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_decode_empty_chunk_otp_6511(doc) ->
- [""];
-chunk_decode_empty_chunk_otp_6511(suite) ->
- [];
chunk_decode_empty_chunk_otp_6511(Config) when is_list(Config) ->
ChunkedBody = "0" ++ ?CRLF ++ ?CRLF,
{ok,{["content-length:0"],<<>>}} =
http_chunk:decode(list_to_binary(ChunkedBody),
- ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
- ok.
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
%%-------------------------------------------------------------------------
-chunk_decode_trailer(doc) ->
- ["Make sure trailers are handled correctly. Trailers should"
- "become new headers"];
-chunk_decode_trailer(suite) ->
- [];
+chunk_whitespace_suffix() ->
+ [{doc, "Test whitespace after chunked length header"}].
+chunk_whitespace_suffix(Config) when is_list(Config) ->
+ ChunkedBody = "1a ; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10 " ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0 " ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {ok, {["content-length:42", "another-footer:another-value",
+ "some-footer:some-value", ""],
+ <<"abcdefghijklmnopqrstuvwxyz1234567890abcdef">>}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE).
+
+%%-------------------------------------------------------------------------
+chunk_decode_trailer() ->
+ [{doc,"Make sure trailers are handled correctly. Trailers should"
+ "become new headers"}].
chunk_decode_trailer(Config) when is_list(Config)->
ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
"abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
@@ -249,30 +231,79 @@ chunk_decode_trailer(Config) when is_list(Config)->
?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
{_, NewBody} = parse(Module1, Function1, Args1, tl(ChunkedBody3)),
- "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody),
-
- ok.
+ "abcdefghijklmnopqrstuvwxyz1234567890abcdef" = binary_to_list(NewBody).
%%-------------------------------------------------------------------------
-chunk_encode(doc) ->
- ["Test http_chunk:encode/1 & http_chunk:encode_last/0"];
-chunk_encode(suite) ->
- [];
+chunk_encode() ->
+ [{doc, "Test http_chunk:encode/1 & http_chunk:encode_last/0"}].
chunk_encode(Config) when is_list(Config) ->
<<54, ?CR, ?LF, 102,111,111,98,97,114, ?CR, ?LF>> =
http_chunk:encode(list_to_binary("foobar")),
["6", ?CR, ?LF,"foobar", ?CR, ?LF] = http_chunk:encode("foobar"),
- <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last(),
- ok.
-
+ <<$0, ?CR, ?LF, ?CR, ?LF >> = http_chunk:encode_last().
+%%-------------------------------------------------------------------------
+chunk_max_headersize() ->
+ [{doc, "Test max header limit"}].
+chunk_max_headersize(Config) when is_list(Config) ->
+ ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+
+ {ok, {_, _}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
+
+ %% Too long in length header
+ {error,{header_too_long, {max, 1}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 1)),
+
+ %% Too long in extension field
+ {error,{header_too_long, {max, 10}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 10)),
+
+ %% Too long in trailer
+ {error,{header_too_long, {max, 30}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, 30)).
+%%-------------------------------------------------------------------------
+chunk_not_hex() ->
+ [{doc, "Test bad chunked length header"}].
+chunk_not_hex(Config) when is_list(Config) ->
+ ChunkedBody = "åäö; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {error,{chunk_size, "åäö"}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE)).
+%%-------------------------------------------------------------------------
+chunk_max_bodysize() ->
+ [{doc, "Test max body limit"}].
+chunk_max_bodysize(Config) when is_list(Config) ->
+ ChunkedBody = "1a; ignore-stuff-here" ++ ?CRLF ++
+ "abcdefghijklmnopqrstuvwxyz" ++ ?CRLF ++ "10" ++ ?CRLF
+ ++ "1234567890abcdef" ++ ?CRLF ++ "0" ++ ?CRLF
+ ++ "some-footer:some-value" ++ ?CRLF
+ ++ "another-footer:another-value" ++ ?CRLF ++ ?CRLF,
+ {ok, {_, _}} =
+ http_chunk:decode(list_to_binary(ChunkedBody),
+ ?HTTP_MAX_BODY_SIZE, ?HTTP_MAX_HEADER_SIZE),
+
+ %% Too long body
+ {error,{body_too_big, {max, 10}}} =
+ (catch http_chunk:decode(list_to_binary(ChunkedBody),
+ 10, ?HTTP_MAX_HEADER_SIZE)).
%%-------------------------------------------------------------------------
-http_response(doc) ->
- ["Test httpc_response:parse*. This test case will simulate that the "
+http_response() ->
+ [{doc, "Test httpc_response:parse*. 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."];
-http_response(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
http_response(Config) when is_list(Config) ->
HttpHead1 = ["HTTP", "/1.1 ", "20", "0 ", "ok", [?CR, ?LF],
@@ -340,12 +371,10 @@ http_response(Config) when is_list(Config) ->
[<<>>,Length1], HttpBody1)),
ok.
%%-------------------------------------------------------------------------
-http_request(doc) ->
- ["Test httpd_request:parse* This test case will simulate that the "
+http_request() ->
+ [{doc, "Test httpd_request:parse* 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."];
-http_request(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
http_request(Config) when is_list(Config) ->
HttpHead = ["GE", "T ", "http://www.erlang", ".org ", "HTTP",
@@ -407,15 +436,12 @@ http_request(Config) when is_list(Config) ->
NewBody1 =
binary_to_list(parse
(httpd_request, whole_body,
- [<<>>, Length1], HttpBody1)),
- ok.
+ [<<>>, Length1], HttpBody1)).
%%-------------------------------------------------------------------------
-validate_request_line(doc) ->
- ["Test httpd_request:validate/3. Makes sure you can not get past"
+validate_request_line() ->
+ [{doc, "Test httpd_request:validate/3. Makes sure you can not get past"
" the server_root and that the request is recognized by the server"
- " and protcol version." ];
-validate_request_line(suite) ->
- [];
+ " and protcol version."}].
validate_request_line(Config) when is_list(Config) ->
%% HTTP/0.9 only has GET requests
@@ -468,16 +494,12 @@ validate_request_line(Config) when is_list(Config) ->
NewForbiddenUri1 =
"http://127.0.0.1:8888/../home/ingela/test.html",
{error, {bad_request, {forbidden, NewForbiddenUri1}}} =
- httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"),
-
- ok.
+ httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1").
%%-------------------------------------------------------------------------
-check_content_length_encoding(doc) ->
- ["Test http_request:headers/2. Check that the content-length is"
- " encoded even when it is zero." ];
-check_content_length_encoding(suite) ->
- [];
+check_content_length_encoding() ->
+ [{doc, "Test http_request:headers/2. Check that the content-length is"
+ " encoded even when it is zero."}].
check_content_length_encoding(Config) when is_list(Config) ->
%% Check that the content-length is preserved.
@@ -486,16 +508,12 @@ check_content_length_encoding(Config) when is_list(Config) ->
true = (string:str(Header1, "content-length: 123\r\n") > 0),
%% Check that content-length=0 is handled correctly.
Header2 = http_request:http_headers(#http_request_h{'content-length'="0"}),
- true = (string:str(Header2, "content-length: 0\r\n") > 0),
-
- ok.
+ true = (string:str(Header2, "content-length: 0\r\n") > 0).
%%-------------------------------------------------------------------------
-esi_parse_headers(doc) ->
- ["Test httpd_esi:*. All header values are received in the same"
- " erlang message."];
-esi_parse_headers(suite) ->
- [];
+esi_parse_headers() ->
+ [{doc, "Test httpd_esi:*. All header values are received in the same"
+ " erlang message."}].
esi_parse_headers(Config) when is_list(Config) ->
ESIResult = "content-type:text/html\r\ndate:Thu, 28 Oct 2004 07:57:43 "
@@ -522,16 +540,14 @@ esi_parse_headers(Config) when is_list(Config) ->
httpd_esi:handle_headers(Headers2),
{proceed,"/foo/bar.html"} =
- httpd_esi:handle_headers("location:/foo/bar.html\r\n"),
- ok.
+ httpd_esi:handle_headers("location:/foo/bar.html\r\n").
%%--------------------------------------------------------------------
-cgi_parse_headers(doc) ->
- ["Test httpd_cgi:*. This test case will simulate that the "
+cgi_parse_headers() ->
+ [{doc, "Test httpd_cgi:*. 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."];
-cgi_parse_headers(suite) ->
- [];
+ "package may be broken up into smaller parts at arbitrary point."}].
+
cgi_parse_headers(Config) when is_list(Config) ->
CGIResult = ["content-type:text", "/html\ndate:Thu, 28 Oct 2004 07:57:43 "
@@ -567,26 +583,18 @@ cgi_parse_headers(Config) when is_list(Config) ->
{ok,[{"content-type","text/html"},
{"connection","close"},
{"content-language","en"},
- {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3),
-
- ok.
-
+ {"age","4711"}], {200,"ok"}} = httpd_cgi:handle_headers(Headers3).
%%-------------------------------------------------------------------------
-is_absolut_uri(doc) ->
- ["Test http_request:is_absolut_uri/1."];
-is_absolut_uri(suite) ->
- [];
+is_absolut_uri() ->
+ [{doc, "Test http_request:is_absolut_uri/1."}].
is_absolut_uri(Config) when is_list(Config) ->
true = http_request:is_absolut_uri("http://www.erlang.org"),
true = http_request:is_absolut_uri("https://www.erlang.org"),
false = http_request:is_absolut_uri("index.html").
-
%%-------------------------------------------------------------------------
-convert_netscapecookie_date(doc) ->
- ["Test http_util:convert_netscapecookie_date/1."];
-convert_netscapecookie_date(suite) ->
- [];
+convert_netscapecookie_date() ->
+ [{doc, "Test http_util:convert_netscapecookie_date/1."}].
convert_netscapecookie_date(Config) when is_list(Config) ->
{{2006,1,6},{8,59,38}} =
http_util:convert_netscapecookie_date("Mon, 06-Jan-2006 08:59:38 GMT"),
@@ -619,9 +627,7 @@ convert_netscapecookie_date(Config) when is_list(Config) ->
{{2006,12,12},{8,59,38}} =
http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"),
{{2036,1,1},{8,0,1}} =
- http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT"),
- ok.
-
+ http_util:convert_netscapecookie_date("Tue Jan 01 08:00:01 2036 GMT").
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 2ad00bdf76..c6c59ab1af 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -98,12 +98,15 @@ only_simulated() ->
stream_once,
stream_single_chunk,
stream_no_length,
+ not_streamed_once,
+ stream_large_not_200_or_206,
no_content_204,
tolerate_missing_CR,
userinfo,
bad_response,
internal_server_error,
invalid_http,
+ invalid_chunk_size,
headers_dummy,
headers_with_obs_fold,
empty_response_header,
@@ -408,6 +411,21 @@ stream_no_length(Config) when is_list(Config) ->
stream_test(Request1, {stream, self}),
Request2 = {url(group_name(Config), "/http_1_0_no_length_multiple.html", Config), []},
stream_test(Request2, {stream, self}).
+%%-------------------------------------------------------------------------
+stream_large_not_200_or_206() ->
+ [{doc, "Test the option stream for large responses with status codes "
+ "other than 200 or 206" }].
+stream_large_not_200_or_206(Config) when is_list(Config) ->
+ Request = {url(group_name(Config), "/large_404_response.html", Config), []},
+ {404, _} = not_streamed_test(Request, {stream, self}).
+%%-------------------------------------------------------------------------
+not_streamed_once() ->
+ [{doc, "Test not streamed responses with once streaming"}].
+not_streamed_once(Config) when is_list(Config) ->
+ Request0 = {url(group_name(Config), "/404.html", Config), []},
+ {404, _} = not_streamed_test(Request0, {stream, {self, once}}),
+ Request1 = {url(group_name(Config), "/404_chunked.html", Config), []},
+ {404, _} = not_streamed_test(Request1, {stream, {self, once}}).
%%-------------------------------------------------------------------------
@@ -748,6 +766,22 @@ invalid_http(Config) when is_list(Config) ->
ct:print("Parse error: ~p ~n", [Reason]).
%%-------------------------------------------------------------------------
+
+invalid_chunk_size(doc) ->
+ ["Test parse error of HTTP chunk size"];
+invalid_chunk_size(suite) ->
+ [];
+invalid_chunk_size(Config) when is_list(Config) ->
+
+ URL = url(group_name(Config), "/invalid_chunk_size.html", Config),
+
+ {error, {chunk_size, _} = Reason} =
+ httpc:request(get, {URL, []}, [], []),
+
+ ct:print("Parse error: ~p ~n", [Reason]).
+
+%%-------------------------------------------------------------------------
+
emulate_lower_versions(doc) ->
[{doc, "Perform request as 0.9 and 1.0 clients."}];
emulate_lower_versions(Config) when is_list(Config) ->
@@ -1117,6 +1151,19 @@ stream_test(Request, To) ->
Body = binary_to_list(StreamedBody).
+not_streamed_test(Request, To) ->
+ {ok, {{_,Code,_}, [_ | _], Body}} =
+ httpc:request(get, Request, [], [{body_format, binary}]),
+ {ok, RequestId} =
+ httpc:request(get, Request, [], [{body_format, binary}, {sync, false}, To]),
+
+ receive
+ {http, {RequestId, {{_, Code, _}, _Headers, Body}}} ->
+ {Code, binary_to_list(Body)};
+ {http, Msg} ->
+ ct:fail(Msg)
+ end.
+
url(http, End, Config) ->
Port = ?config(port, Config),
{ok,Host} = inet:gethostname(),
@@ -1648,6 +1695,11 @@ handle_uri(_,"/307.html",Port,_,Socket,_) ->
"Content-Length:" ++ integer_to_list(length(Body))
++ "\r\n\r\n" ++ Body;
+handle_uri(_,"/404.html",_,_,_,_) ->
+ "HTTP/1.1 404 not found\r\n" ++
+ "Content-Length:14\r\n\r\n" ++
+ "Page not found";
+
handle_uri(_,"/500.html",_,_,_,_) ->
"HTTP/1.1 500 Internal Server Error\r\n" ++
"Content-Length:47\r\n\r\n" ++
@@ -1783,6 +1835,15 @@ handle_uri(_,"/once_chunked.html",_,_,Socket,_) ->
http_chunk:encode("obar</BODY></HTML>")),
http_chunk:encode_last();
+handle_uri(_,"/404_chunked.html",_,_,Socket,_) ->
+ Head = "HTTP/1.1 404 not found\r\n" ++
+ "Transfer-Encoding:Chunked\r\n\r\n",
+ send(Socket, Head),
+ send(Socket, http_chunk:encode("<HTML><BODY>Not ")),
+ send(Socket,
+ http_chunk:encode("found</BODY></HTML>")),
+ http_chunk:encode_last();
+
handle_uri(_,"/single_chunk.html",_,_,Socket,_) ->
Chunk = "HTTP/1.1 200 ok\r\n" ++
"Transfer-Encoding:Chunked\r\n\r\n" ++
@@ -1807,6 +1868,17 @@ handle_uri(_,"/http_1_0_no_length_multiple.html",_,_,Socket,_) ->
send(Socket, string:copies("other multiple packets ", 200)),
close(Socket);
+handle_uri(_,"/large_404_response.html",_,_,Socket,_) ->
+ %% long body to make sure it will be sent in multiple tcp packets
+ Body = string:copies("other multiple packets ", 200),
+ Head = io_lib:format("HTTP/1.1 404 not found\r\n"
+ "Content-length: ~B\r\n"
+ "Content-type: text/plain\r\n\r\n",
+ [length(Body)]),
+ send(Socket, Head),
+ send(Socket, Body),
+ close(Socket);
+
handle_uri(_,"/once.html",_,_,Socket,_) ->
Head = "HTTP/1.1 200 ok\r\n" ++
"Content-Length:32\r\n\r\n",
@@ -1821,6 +1893,10 @@ handle_uri(_,"/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";
+handle_uri(_,"/invalid_chunk_size.html",_,_,_,_) ->
+ "HTTP/1.1 200 ok\r\n" ++
+ "Transfer-Encoding:chunked\r\n\r\nåäö\r\n";
+
handle_uri(_,"/missing_reason_phrase.html",_,_,_,_) ->
"HTTP/1.1 200\r\n" ++
"Content-Length: 32\r\n\r\n"
diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl
index dd9d21bbfc..db6def9d17 100644
--- a/lib/inets/test/httpd_1_1.erl
+++ b/lib/inets/test/httpd_1_1.erl
@@ -24,7 +24,7 @@
-include_lib("kernel/include/file.hrl").
-export([host/4, chunked/4, expect/4, range/4, if_test/5, trace/4,
- head/4, mod_cgi_chunked_encoding_test/5]).
+ head/4, mod_cgi_chunked_encoding_test/5, mod_esi_chunk_timeout/4]).
%% -define(all_keys_lower_case,true).
-ifndef(all_keys_lower_case).
@@ -274,6 +274,15 @@ mod_cgi_chunked_encoding_test(Type, Port, Host, Node, [Request| Rest])->
[{statuscode, 200}]),
mod_cgi_chunked_encoding_test(Type, Port, Host, Node, Rest).
+
+mod_esi_chunk_timeout(Type, Port, Host, Node) ->
+ ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
+ "GET /cgi-bin/erl/httpd_example/chunk_timeout?input=20000 HTTP/1.1\r\n"
+ "Host:"++ Host ++"\r\n"
+ "\r\n",
+ [{statuscode, 200},
+ {header, "warning"}]).
+
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index b50d31a5c1..1d8a603981 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -117,7 +117,7 @@ groups() ->
{htaccess, [], [htaccess_1_1, htaccess_1_0, htaccess_0_9]},
{security, [], [security_1_1, security_1_0]}, %% Skip 0.9 as causes timing issus in test code
{http_1_1, [], [host, chunked, expect, cgi, cgi_chunked_encoding_test,
- trace, range, if_modified_since] ++ http_head() ++ http_get() ++ load()},
+ trace, range, if_modified_since, mod_esi_chunk_timeout] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()}
].
@@ -757,6 +757,13 @@ esi(Config) when is_list(Config) ->
Config, [{statuscode, 200},
{no_header, "cache-control"}]).
%%-------------------------------------------------------------------------
+mod_esi_chunk_timeout(Config) when is_list(Config) ->
+ ok = httpd_1_1:mod_esi_chunk_timeout(?config(type, Config),
+ ?config(port, Config),
+ ?config(host, Config),
+ ?config(node, Config)).
+
+%%-------------------------------------------------------------------------
cgi() ->
[{doc, "Test mod_cgi"}].
@@ -1434,9 +1441,11 @@ server_config(http_reload, Config) ->
server_config(https_reload, Config) ->
[{keep_alive_timeout, 2}] ++ server_config(https, Config);
server_config(http_limit, Config) ->
- [{max_clients, 1},
- %% Make sure option checking code is run
- {max_content_length, 100000002}] ++ server_config(http, Config);
+ Conf = [{max_clients, 1},
+ %% Make sure option checking code is run
+ {max_content_length, 100000002}] ++ server_config(http, Config),
+ ct:pal("Received message ~p~n", [Conf]),
+ Conf;
server_config(http_custom, Config) ->
[{customize, ?MODULE}] ++ server_config(http, Config);
server_config(https_custom, Config) ->
@@ -1486,6 +1495,7 @@ server_config(http_mime_types, Config0) ->
server_config(http, Config) ->
ServerRoot = ?config(server_root, Config),
[{port, 0},
+ {socket_type, {ip_comm, [{nodelay, true}]}},
{server_name,"httpd_test"},
{server_root, ServerRoot},
{document_root, ?config(doc_root, Config)},
@@ -1507,13 +1517,14 @@ server_config(http, Config) ->
server_config(https, Config) ->
PrivDir = ?config(priv_dir, Config),
[{socket_type, {essl,
- [{cacertfile,
- filename:join(PrivDir, "public_key_cacert.pem")},
- {certfile,
- filename:join(PrivDir, "public_key_cert.pem")},
- {keyfile,
- filename:join(PrivDir, "public_key_cert_key.pem")}
- ]}}] ++ server_config(http, Config).
+ [{nodelay, true},
+ {cacertfile,
+ filename:join(PrivDir, "public_key_cacert.pem")},
+ {certfile,
+ filename:join(PrivDir, "public_key_cert.pem")},
+ {keyfile,
+ filename:join(PrivDir, "public_key_cert_key.pem")}
+ ]}}] ++ proplists:delete(socket_type, server_config(http, Config)).
init_httpd(Group, Config0) ->
Config1 = proplists:delete(port, Config0),
diff --git a/lib/inets/test/httpd_SUITE_data/Makefile.src b/lib/inets/test/httpd_SUITE_data/Makefile.src
index b0fdb43d8d..cea40dd8cb 100644
--- a/lib/inets/test/httpd_SUITE_data/Makefile.src
+++ b/lib/inets/test/httpd_SUITE_data/Makefile.src
@@ -10,5 +10,10 @@ all: $(PROGS)
cgi_echo@exe@: cgi_echo@obj@
$(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@
+@IFEQ@ (@CC@, cl -nologo)
+cgi_echo@obj@: cgi_echo.c
+ $(CC) /c /Focgi_echo@obj@ $(CFLAGS) cgi_echo.c
+@ELSE@
cgi_echo@obj@: cgi_echo.c
$(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c
+@ENDIF@
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index a5b836f651..c58966ce10 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -235,11 +235,17 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _},
_ ->
ok
end,
- do_validate(http_response:header_list(Headers), Options, N, P),
- check_body(RequestStr, StatusCode,
- Headers#http_response_h.'content-type',
- list_to_integer(Headers#http_response_h.'content-length'),
- Body).
+ HList = http_response:header_list(Headers),
+ do_validate(HList, Options, N, P),
+ case lists:keysearch("warning", 1, HList) of
+ {value, _} ->
+ ok;
+ _ ->
+ check_body(RequestStr, StatusCode,
+ Headers#http_response_h.'content-type',
+ list_to_integer(Headers#http_response_h.'content-length'),
+ Body)
+ end.
%--------------------------------------------------------------------
%% Internal functions
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 480caeca4b..7cc95fa6d3 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 6.0.2
+INETS_VSN = 6.0.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"