aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorErlang/OTP <otp@erlang.org>2011-09-15 09:54:39 +0200
committerErlang/OTP <otp@erlang.org>2011-09-15 09:54:39 +0200
commit801ec3847e330b7d67b1e4ae700211380da0d6bd (patch)
tree8861e1e85f352d828cf31f0690feaae63c0088bd /lib
parent50261525973798faf7f62ea02356447b16e5fc56 (diff)
parent98fd9df4c4a04554fd2f707ca9ea2d674fad984d (diff)
downloadotp-801ec3847e330b7d67b1e4ae700211380da0d6bd.tar.gz
otp-801ec3847e330b7d67b1e4ae700211380da0d6bd.tar.bz2
otp-801ec3847e330b7d67b1e4ae700211380da0d6bd.zip
Merge branch 'bmk/inets/httpd/cross_site_scripting_attacks/OTP-9535' into maint-r13
* bmk/inets/httpd/cross_site_scripting_attacks/OTP-9535: Updated http-server to make sure URLs in error-messages are URL-encoded. Added support in http-client to use URL-encoding. Also added the missing include directory for the inets application.
Diffstat (limited to 'lib')
-rw-r--r--lib/inets/doc/src/http_client.xml6
-rw-r--r--lib/inets/doc/src/httpd.xml4
-rw-r--r--lib/inets/doc/src/notes.xml31
-rw-r--r--lib/inets/src/http_client/Makefile5
-rw-r--r--lib/inets/src/http_client/httpc.erl29
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl42
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl8
-rw-r--r--lib/inets/src/http_lib/Makefile5
-rw-r--r--lib/inets/src/http_lib/http_transport.erl26
-rw-r--r--lib/inets/src/http_lib/http_uri.erl (renamed from lib/inets/src/http_client/http_uri.erl)31
-rw-r--r--lib/inets/src/http_lib/http_util.erl20
-rw-r--r--lib/inets/src/http_server/Makefile18
-rw-r--r--lib/inets/src/http_server/httpd.erl6
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl3
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl5
-rw-r--r--lib/inets/src/http_server/httpd_file.erl3
-rw-r--r--lib/inets/src/http_server/httpd_request.erl6
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl3
-rw-r--r--lib/inets/src/http_server/httpd_util.erl82
-rw-r--r--lib/inets/src/http_server/mod_alias.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_dets.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_plain.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_server.erl3
-rw-r--r--lib/inets/src/http_server/mod_dir.erl6
-rw-r--r--lib/inets/src/http_server/mod_esi.erl3
-rw-r--r--lib/inets/src/http_server/mod_include.erl6
-rw-r--r--lib/inets/src/http_server/mod_security.erl3
-rw-r--r--lib/inets/src/http_server/mod_security_server.erl3
-rw-r--r--lib/inets/src/inets_app/inets.appup.src62
-rw-r--r--lib/inets/test/httpc_SUITE.erl256
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl59
-rw-r--r--lib/inets/test/inets.spec2
-rw-r--r--lib/inets/test/inets_test_lib.erl61
-rw-r--r--lib/inets/vsn.mk6
35 files changed, 567 insertions, 248 deletions
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index ea8053cafa..49327ca80f 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2010</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -57,7 +57,7 @@
[{inets, [{services, [{httpc, PropertyList}]}]}]
</pre>
<p>For valid properties see
- <seealso marker="http">httpc(3)</seealso>. </p>
+ <seealso marker="httpc">httpc(3)</seealso>. </p>
</section>
<section>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 7dabeb33e9..f061488ac3 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1997</year><year>2010</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 9ab35ff38b..ffbe4bd58f 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2002</year><year>2010</year>
+ <year>2002</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,33 @@
<file>notes.xml</file>
</header>
+ <section><title>Inets 5.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Updated http-server to make sure URLs in error-messages
+ are URL-encoded. Added support in http-client to use
+ URL-encoding. Also added the missing include directory
+ for the inets application.</p>
+ <p>Own Id: OTP-8940</p>
+ <p>Aux Id: seq11735</p>
+ </item>
+
+ <item>
+ <p>[httpd] Prevent XSS in error pages.
+ Prevent user controlled input from being interpreted
+ as HTML in error pages by encoding the reserved HTML
+ characters. </p>
+ <p>Michael Santos</p>
+ <p>Own Id: OTP-9124</p>
+ </item>
+ </list>
+ </section>
+
+ </section> <!-- 5.3.5 -->
+
+
<section><title>Inets 5.3.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index 628c91421f..184f45f589 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -50,8 +50,7 @@ MODULES = \
httpc_handler_sup \
httpc_profile_sup \
httpc_response \
- httpc_request \
- http_uri \
+ httpc_request
HRL_FILES = httpc_internal.hrl
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 6deeab6948..ca186f46a7 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -434,18 +434,21 @@ handle_request(Method, Url,
Stream = proplists:get_value(stream, Options),
Host2 = header_host(Scheme, Host, Port),
HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions),
- Receiver = proplists:get_value(receiver, Options),
- SocketOpts = proplists:get_value(socket_opts, Options),
+ Receiver = proplists:get_value(receiver, Options),
+ SocketOpts = proplists:get_value(socket_opts, Options),
+ MaybeEscPath = maybe_url_encode(HTTPOptions, Path),
+ MaybeEscQuery = maybe_url_encode(HTTPOptions, Query),
+ AbsUri = maybe_url_encode(HTTPOptions, Url),
Request = #request{from = Receiver,
scheme = Scheme,
address = {Host, Port},
- path = Path,
- pquery = Query,
+ path = MaybeEscPath,
+ pquery = MaybeEscQuery,
method = Method,
headers = HeadersRecord,
content = {ContentType, Body},
settings = HTTPOptions,
- abs_uri = Url,
+ abs_uri = AbsUri,
userinfo = UserInfo,
stream = Stream,
headers_as_is = headers_as_is(Headers, Options),
@@ -465,6 +468,10 @@ handle_request(Method, Url,
Error
end.
+maybe_url_encode(#http_options{url_encode = true}, URI) ->
+ http_uri:encode(URI);
+maybe_url_encode(_, URI) ->
+ URI.
handle_answer(RequestId, false, _) ->
{ok, RequestId};
@@ -603,6 +610,13 @@ http_options_default() ->
(_) ->
error
end,
+
+ UrlDecodePost = fun(Value) when (Value =:= true) orelse
+ (Value =:= false) ->
+ {ok, Value};
+ (_) ->
+ error
+ end,
[
{version, {value, "HTTP/1.1"}, #http_options.version, VersionPost},
{timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost},
@@ -611,7 +625,8 @@ http_options_default() ->
{proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost},
{relaxed, {value, false}, #http_options.relaxed, RelaxedPost},
%% this field has to be *after* the timeout field (as that field is used for the default value)
- {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}
+ {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost},
+ {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost}
].
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index db5ff3036a..5e5a9ce32e 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -794,37 +794,43 @@ deliver_answers([Request|Requests]) ->
%% Purpose: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.1'}, {to, '5.0.2'}]) ->
- Settings = new_http_options(Request#request.settings),
+ from_pre_5_3_5) ->
+ Settings = new_http_options(Request#request.settings),
NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun new_http_options/1),
+ NewQueue = new_queue(Queue, fun new_http_options/1),
{ok, State#state{request = NewRequest, pipeline = NewQueue}};
code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.2'}, {to, '5.0.1'}]) ->
- Settings = old_http_options(Request#request.settings),
+ to_pre_5_3_5) ->
+ Settings = old_http_options(Request#request.settings),
NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun old_http_options/1),
+ NewQueue = new_queue(Queue, fun old_http_options/1),
{ok, State#state{request = NewRequest, pipeline = NewQueue}};
code_change(_, State, _) ->
{ok, State}.
-new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}) ->
- {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}.
-
-old_http_options({http_options, _, TimeOut, AutoRedirect,
- SslOpts, Auth, Relaxed}) ->
- {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
-
-new_queue(Queue, Fun) ->
+new_http_options({http_options,
+ Version, Timeout, AutoRedirect, SslOpts,
+ ProxyAuth, Relaxed, ConnTimeout}) ->
+ UrlEncoding = false,
+ {http_options,
+ Version, Timeout, AutoRedirect, SslOpts,
+ ProxyAuth, Relaxed, ConnTimeout, UrlEncoding}.
+
+old_http_options({http_options,
+ Version, TimeOut, AutoRedirect, SslOpts,
+ ProxyAuth, Relaxed, ConnTimeout, _UrlEncode}) ->
+ {http_options,
+ Version, TimeOut, AutoRedirect, SslOpts,
+ ProxyAuth, Relaxed, ConnTimeout}.
+
+new_queue(Queue, TransformSettings) ->
List = queue:to_list(Queue),
NewList =
lists:map(fun(Request) ->
Settings =
- Fun(Request#request.settings),
+ TransformSettings(Request#request.settings),
Request#request{settings = Settings}
end, List),
queue:from_list(NewList).
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 4d76c4beb3..7513aa2838 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -56,7 +56,11 @@
relaxed = false,
%% integer() - ms before a connect times out
- connect_timeout = ?HTTP_REQUEST_CTIMEOUT
+ connect_timeout = ?HTTP_REQUEST_CTIMEOUT,
+
+ %% bool() - Use %-encoding rfc 2396
+ url_encode
+
}
).
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 7f4c92861c..a715e3c9af 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -45,7 +45,8 @@ MODULES = \
http_transport\
http_util \
http_request \
- http_response
+ http_response \
+ http_uri
HRL_FILES = http_internal.hrl
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 7c2ac626e6..587d215051 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -150,17 +150,22 @@ listen_ip_comm(Addr, Port) ->
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', _} ->
+ {'EXIT', Reason} ->
Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen exit - try ipv4 instead",
+ [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
gen_tcp:listen(NewPort, Opts3);
Other ->
@@ -168,6 +173,7 @@ listen_ip_comm(Addr, Port) ->
end;
_ ->
Opts2 = [IpFamily | Opts],
+ ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]),
gen_tcp:listen(NewPort, Opts2)
end.
@@ -364,7 +370,21 @@ peername(ip_comm, Socket) ->
http_util:integer_to_hexlist(G) ++":"++
http_util:integer_to_hexlist(H),
{Port, PeerName};
- {error, _} ->
+ {error, Reason} ->
+ Report = io_lib:format("~p Failed getting PeerName for socket ~p: "
+ "~n Reason: ~p"
+ "~n Socket stat: ~p"
+ "~n IfList: ~p"
+ "~n Fd: ~p"
+ "~n",
+ [self(),
+ Socket,
+ Reason,
+ (catch inet:getstat(Socket)),
+ (catch inet:getiflist(Socket)),
+ (catch inet:getfd(Socket))
+ ]),
+ (catch error_logger:error_report(Report)),
{-1, "unknown"}
end;
diff --git a/lib/inets/src/http_client/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 615a0d8ec4..3804af60f4 100644
--- a/lib/inets/src/http_client/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-module(http_uri).
-export([parse/1]).
+-export([parse/1, encode/1, decode/1]).
%%%=========================================================================
%%% API
@@ -34,10 +35,23 @@ parse(AbsURI) ->
{UserInfo, Host, Port, Path, Query} ->
{Scheme, UserInfo, Host, Port, Path, Query};
_ ->
- {error, {malformed_url, AbsURI}}
+ {error, {malformed_url, AbsURI}}
end
end.
+encode(URI) ->
+ Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
+ $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $\\, $', $^, $%, $ ]),
+ lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)).
+
+decode([$%,Hex1,Hex2|Rest]) ->
+ [hex2dec(Hex1)*16+hex2dec(Hex2)|decode(Rest)];
+decode([First|Rest]) ->
+ [First|decode(Rest)];
+decode([]) ->
+ [].
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
@@ -55,7 +69,6 @@ parse_scheme(AbsURI) ->
end.
parse_uri_rest(Scheme, "//" ++ URIPart) ->
-
{Authority, PathQuery} =
case split_uri(URIPart, "/", URIPart, 1, 0) of
Split = {_, _} ->
@@ -114,3 +127,15 @@ path("") ->
"/";
path(Path) ->
Path.
+
+uri_encode(Char, Reserved) ->
+ case sets:is_element(Char, Reserved) of
+ true ->
+ [ $% | http_util:integer_to_hexlist(Char)];
+ false ->
+ [Char]
+ end.
+
+hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
+hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
+hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index 4f1147176c..be0602ff6e 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,8 @@
convert_netscapecookie_date/1,
hexlist_to_integer/1, integer_to_hexlist/1,
convert_month/1,
- is_hostname/1,
+ is_hostname/1,
+ html_encode/1,
timestamp/0, timeout/2
]).
@@ -185,7 +186,14 @@ timeout(Timeout, Started) ->
_ ->
0
end.
+
+html_encode(Chars) ->
+ Reserved = sets:from_list([$&, $<, $>, $\", $', $/]),
+ lists:append(lists:map(fun(Char) ->
+ char_to_html_entity(Char, Reserved)
+ end, Chars)).
+
%%%========================================================================
%%% Internal functions
@@ -235,3 +243,11 @@ convert_to_ascii([Num | Reversed], 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
+ true ->
+ "&#" ++ integer_to_list(Char) ++ ";";
+ false ->
+ [Char]
+ end.
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index ce1405011e..3c36b384b8 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2010. All Rights Reserved.
+# Copyright Ericsson AB 2005-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -98,12 +98,16 @@ INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-INETS_ERL_FLAGS += -I ../http_lib -I ../inets_app -pa ../../ebin
-
-ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
- $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+INETS_ERL_FLAGS += \
+ -I ../http_lib \
+ -I ../inets_app \
+ -pa $(ERL_TOP)/lib/inets/ebin
+
+ERL_COMPILE_FLAGS += \
+ $(INETS_ERL_FLAGS) \
+ $(INETS_FLAGS) \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
# ----------------------------------------------------
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index 8fe54ccef6..a88d002b03 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -349,8 +349,8 @@ foreach([KeyValue|Rest]) ->
{ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "),
case inets_regexp:split(Plus2Space,"=") of
{ok,[Key|Value]} ->
- [{httpd_util:decode_hex(Key),
- httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)];
+ [{http_uri:decode(Key), http_uri:decode(lists:flatten(Value))}|
+ foreach(Rest)];
{ok,_} ->
foreach(Rest)
end.
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
index 568fd3c610..fef3fe58c6 100644
--- a/lib/inets/src/http_server/httpd_acceptor.erl
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
%% Internal application API
-export([start_link/5, start_link/6]).
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 5ca2e47eb5..2faf13e9b3 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,8 +30,9 @@
validate_properties/1]).
-define(VMODULE,"CONF").
--include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("httpd.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
%%%=========================================================================
diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl
index 5fd529100e..fbe713ecd1 100644
--- a/lib/inets/src/http_server/httpd_file.erl
+++ b/lib/inets/src/http_server/httpd_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-export([handle_error/4]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
handle_error(eacces, Op, ModData, Path) ->
handle_error(403, Op, ModData, Path,"");
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 8eee08e766..75f03c4fc2 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -256,9 +256,9 @@ validate_uri(RequestURI) ->
UriNoQueryNoHex =
case string:str(RequestURI, "?") of
0 ->
- (catch httpd_util:decode_hex(RequestURI));
+ (catch http_uri:decode(RequestURI));
Ndx ->
- (catch httpd_util:decode_hex(string:left(RequestURI, Ndx)))
+ (catch http_uri:decode(string:left(RequestURI, Ndx)))
end,
case UriNoQueryNoHex of
{'EXIT',_Reason} ->
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 1507c6852a..90d74e3a14 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,6 +37,7 @@
-define(TIMEOUT, 15000).
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
%%%=========================================================================
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index cfad79638f..7fe5d6d152 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
-export([ip_address/2, lookup/2, lookup/3, multi_lookup/2,
lookup_mime/2, lookup_mime/3, lookup_mime_default/2,
lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0,
- rfc1123_date/1, day/1, month/1, decode_hex/1,
+ rfc1123_date/1, day/1, month/1,
flatlength/1, split_path/1, split_script_path/1,
suffix/1, split/3, uniq/1,
make_name/2,make_name/3,make_name/4,strip/1,
@@ -32,7 +32,8 @@
dir_validate/2, file_validate/2, mime_type_validate/1,
mime_types_validate/1, custom_date/0]).
--export([encode_hex/1]).
+-export([encode_hex/1, decode_hex/1]).
+
-include_lib("kernel/include/file.hrl").
ip_address({_,_,_,_} = Address, _IpFamily) ->
@@ -175,14 +176,15 @@ reason_phrase(_) -> "Internal Server Error".
%% message
message(301,URL,_) ->
- "The document has moved <A HREF=\""++URL++"\">here</A>.";
+ "The document has moved <A HREF=\"" ++ maybe_encode(URL) ++"\">here</A>.";
message(304, _URL,_) ->
"The document has not been changed.";
-message(400,none,_) ->
+message(400, none, _) ->
"Your browser sent a query that this server could not understand.";
-message(400,Msg,_) ->
- "Your browser sent a query that this server could not understand. "++Msg;
-message(401,none,_) ->
+message(400, Msg, _) ->
+ "Your browser sent a query that this server could not understand. " ++
+ http_util:html_encode(Msg);
+message(401, none, _) ->
"This server could not verify that you
are authorized to access the document you
requested. Either you supplied the wrong
@@ -190,40 +192,57 @@ credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.";
message(403,RequestURI,_) ->
- "You don't have permission to access "++RequestURI++" on this server.";
+ "You don't have permission to access " ++
+ http_util:html_encode(RequestURI) ++
+ " on this server.";
message(404,RequestURI,_) ->
- "The requested URL "++RequestURI++" was not found on this server.";
+ "The requested URL " ++
+ http_util:html_encode(RequestURI) ++
+ " was not found on this server.";
message(408, Timeout, _) ->
Timeout;
message(412,none,_) ->
"The requested preconditions where false";
message(413, Reason,_) ->
- "Entity: " ++ Reason;
+ "Entity: " ++ http_util:html_encode(Reason);
message(414,ReasonPhrase,_) ->
- "Message "++ReasonPhrase++".";
+ "Message " ++ http_util:html_encode(ReasonPhrase) ++ ".";
message(416,ReasonPhrase,_) ->
- ReasonPhrase;
+ http_util:html_encode(ReasonPhrase);
message(500,_,ConfigDB) ->
- ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"),
+ ServerAdmin = lookup(ConfigDB, server_admin, "unknown@unknown"),
"The server encountered an internal error or "
"misconfiguration and was unable to complete "
"your request.<P>Please contact the server administrator "
- ++ ServerAdmin ++ ", and inform them of the time the error occurred "
+ ++ http_util:html_encode(ServerAdmin) ++
+ ", and inform them of the time the error occurred "
"and anything you might have done that may have caused the error.";
message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
if
is_atom(Method) ->
atom_to_list(Method)++
- " to "++RequestURI++" ("++HTTPVersion++") not supported.";
+ " to " ++
+ http_util:html_encode(RequestURI) ++
+ " (" ++ HTTPVersion ++ ") not supported.";
is_list(Method) ->
Method++
- " to "++RequestURI++" ("++HTTPVersion++") not supported."
+ " to " ++
+ http_util:html_encode(RequestURI) ++
+ " (" ++ HTTPVersion ++ ") not supported."
end;
message(503, String, _ConfigDB) ->
- "This service in unavailable due to: "++String.
+ "This service in unavailable due to: " ++ http_util:html_encode(String).
+
+maybe_encode(URI) ->
+ case lists:member($%, URI) of
+ true ->
+ URI;
+ false ->
+ http_uri:encode(URI)
+ end.
%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
@@ -381,16 +400,11 @@ month(12) -> "Dec".
%% decode_hex
-decode_hex([$%,Hex1,Hex2|Rest]) ->
- [hex2dec(Hex1)*16+hex2dec(Hex2)|decode_hex(Rest)];
-decode_hex([First|Rest]) ->
- [First|decode_hex(Rest)];
-decode_hex([]) ->
- [].
+decode_hex(URI) ->
+ http_uri:decode(URI).
-hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
-hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
-hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
+encode_hex(URI) ->
+ http_uri:encode(URI).
%% flatlength
flatlength(List) ->
@@ -411,7 +425,7 @@ split_path(Path) ->
case inets_regexp:match(Path,"[\?].*\$") of
%% A QUERY_STRING exists!
{match,Start,Length} ->
- {httpd_util:decode_hex(string:substr(Path,1,Start-1)),
+ {http_uri:decode(string:substr(Path,1,Start-1)),
string:substr(Path,Start,Length)};
%% A possible PATH_INFO exists!
nomatch ->
@@ -419,9 +433,9 @@ split_path(Path) ->
end.
split_path([],SoFar) ->
- {httpd_util:decode_hex(lists:reverse(SoFar)),[]};
+ {http_uri:decode(lists:reverse(SoFar)),[]};
split_path([$/|Rest],SoFar) ->
- Path=httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path,[$/|Rest]};
@@ -454,7 +468,7 @@ pathinfo_querystring([C|Rest], SoFar) ->
pathinfo_querystring(Rest, [C|SoFar]).
split_script_path([$?|QueryString], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$?|QueryString]};
@@ -464,7 +478,7 @@ split_script_path([$?|QueryString], SoFar) ->
not_a_script
end;
split_script_path([], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, []};
@@ -474,7 +488,7 @@ split_script_path([], SoFar) ->
not_a_script
end;
split_script_path([$/|Rest], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok, FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$/|Rest]};
@@ -608,8 +622,6 @@ hexlist_to_integer(List)->
%%----------------------------------------------------------------------
%%Converts an integer to an hexlist
%%----------------------------------------------------------------------
-encode_hex(Num)->
- integer_to_hexlist(Num).
integer_to_hexlist(Num) when is_integer(Num) ->
http_util:integer_to_hexlist(Num).
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index ec0a12242f..41fcdb5e3a 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ALIAS").
diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl
index 07cafb4726..b1d8b03fe4 100644
--- a/lib/inets/src/http_server/mod_auth.erl
+++ b/lib/inets/src/http_server/mod_auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,7 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"AUTH").
diff --git a/lib/inets/src/http_server/mod_auth_dets.erl b/lib/inets/src/http_server/mod_auth_dets.erl
index bc6c2b70a0..2abf5c517a 100644
--- a/lib/inets/src/http_server/mod_auth_dets.erl
+++ b/lib/inets/src/http_server/mod_auth_dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,7 @@
-export([store_directory_data/3]).
-include("httpd.hrl").
+-include("inets_internal.hrl").
-include("mod_auth.hrl").
store_directory_data(_Directory, DirData, Server_root) ->
diff --git a/lib/inets/src/http_server/mod_auth_plain.erl b/lib/inets/src/http_server/mod_auth_plain.erl
index d88859d28a..12342ba8da 100644
--- a/lib/inets/src/http_server/mod_auth_plain.erl
+++ b/lib/inets/src/http_server/mod_auth_plain.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"AUTH_PLAIN").
diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl
index 5f9e59be9d..fc50356838 100644
--- a/lib/inets/src/http_server/mod_auth_server.erl
+++ b/lib/inets/src/http_server/mod_auth_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl
index cdc7cc01e4..35e9de24e2 100644
--- a/lib/inets/src/http_server/mod_dir.erl
+++ b/lib/inets/src/http_server/mod_dir.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,11 @@
%%
%%
-module(mod_dir).
+
-export([do/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%% do
@@ -57,7 +59,7 @@ do_dir(Info) ->
case file:read_file_info(DefaultPath) of
{ok,FileInfo} when FileInfo#file_info.type == directory ->
DecodedRequestURI =
- httpd_util:decode_hex(Info#mod.request_uri),
+ http_uri:decode(Info#mod.request_uri),
?DEBUG("do_dir -> ~n"
" Path: ~p~n"
" DefaultPath: ~p~n"
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index cb33544540..bbdb67bb0e 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,7 @@
-export([do/1, load/2, store/2]).
-include("httpd.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ESI").
-define(DEFAULT_ERL_TIMEOUT,15000).
diff --git a/lib/inets/src/http_server/mod_include.erl b/lib/inets/src/http_server/mod_include.erl
index 534eba8a36..790bf8f937 100644
--- a/lib/inets/src/http_server/mod_include.erl
+++ b/lib/inets/src/http_server/mod_include.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -186,9 +186,9 @@ document_uri(ConfigDB, RequestURI) ->
FileName = string:substr(Path,Start,Length),
case inets_regexp:match(VirtualPath, FileName++"\$") of
{match, _, _} ->
- httpd_util:decode_hex(VirtualPath)++AfterPath;
+ http_uri:decode(VirtualPath)++AfterPath;
nomatch ->
- string:strip(httpd_util:decode_hex(VirtualPath),right,$/)++
+ string:strip(http_uri:decode(VirtualPath),right,$/)++
"/"++FileName++AfterPath
end.
diff --git a/lib/inets/src/http_server/mod_security.erl b/lib/inets/src/http_server/mod_security.erl
index 95793e1cfb..5cdbeb9792 100644
--- a/lib/inets/src/http_server/mod_security.erl
+++ b/lib/inets/src/http_server/mod_security.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,6 +32,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"SEC").
diff --git a/lib/inets/src/http_server/mod_security_server.erl b/lib/inets/src/http_server/mod_security_server.erl
index 58060686b3..ca8bee0c8e 100644
--- a/lib/inets/src/http_server/mod_security_server.erl
+++ b/lib/inets/src/http_server/mod_security_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index d86e998f01..c31b0deb30 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
%% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,41 +18,29 @@
{"%VSN%",
[
+ {"5.3.4",
+ [
+ {restart_application, inets}
+ ]
+ },
{"5.3.3",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3.2",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3.1",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []},
- {load_module, mod_esi, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.2",
@@ -72,41 +60,29 @@
}
],
[
+ {"5.3.4",
+ [
+ {restart_application, inets}
+ ]
+ },
{"5.3.3",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3.2",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, []},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3.1",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.3",
[
- {load_module, inets, soft_purge, soft_purge, []},
- {load_module, http_util, soft_purge, soft_purge, []},
- {load_module, httpc, soft_purge, soft_purge, []},
- {load_module, httpc_cookie, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []},
- {load_module, mod_esi, soft_purge, soft_purge, []}
+ {restart_application, inets}
]
},
{"5.2",
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index f2e8bebe07..71f017dae6 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -262,7 +262,9 @@ finish(Config) ->
ok;
_ ->
test_server:timetrap_cancel(Dog)
- end.
+ end,
+ (catch application:stop(inets)).
+
%%-------------------------------------------------------------------------
%% Test cases starts here.
@@ -1163,19 +1165,26 @@ proxy_options(suite) ->
proxy_options(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(options, {?PROXY_URL, []}, [], []) of
- {ok, {{_,200,_}, Headers, _}} ->
- case lists:keysearch("allow", 1, Headers) of
- {value, {"allow", _}} ->
- ok;
- _ ->
- test_server:fail(http_options_request_failed)
- end;
- Unexpected ->
- test_server:fail({unexpected_result, Unexpected})
- end;
+ Command =
+ fun() -> http:request(options, {?PROXY_URL, []}, [], []) end,
+ Verify =
+ fun({ok, {{_,200,_}, Headers, _}}) ->
+ case lists:keysearch("allow", 1, Headers) of
+ {value, {"allow", _}} ->
+ ok;
+ _ ->
+ tsf(http_options_request_failed)
+ end;
+ ({ok, {{_, 501, "Not Implemented"}, _Hdrs, _}}) ->
+ skip(options_not_implemented_in_server_501_ni);
+ ({ok, Unexpected}) ->
+ tsf({unexpected_success, Unexpected});
+ (Unexpected) ->
+ tsf({unexpected_result, Unexpected})
+ end,
+ expect(Command, Verify);
Reason ->
- {skip, Reason}
+ skip(Reason)
end.
@@ -1187,18 +1196,26 @@ proxy_head(suite) ->
proxy_head(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(head, {?PROXY_URL, []}, [], []) of
- {ok, {{_,200, _}, [_ | _], []}} ->
- ok;
- Unexpected ->
- test_server:fail({unexpected_result, Unexpected})
- end;
+ Command =
+ fun() -> http:request(head, {?PROXY_URL, []}, [], []) end,
+ Verify =
+ fun({ok, {{_,200, _}, [_ | _], []}}) ->
+ ok;
+ ({ok, {{_,503,"Service Unavailable"}, [_ | _], []}}) ->
+ skip(head_not_implemented_in_server_503_su);
+ ({ok, Result}) ->
+ tsf({unexpected_success, Result});
+ (Unexpected) ->
+ tsf({unexpected_result, Unexpected})
+ end,
+ expect(Command, Verify);
Reason ->
- {skip, Reason}
+ skip(Reason)
end.
%%-------------------------------------------------------------------------
+
proxy_get(doc) ->
["Perform a GET request that goes through a proxy."];
proxy_get(suite) ->
@@ -1216,7 +1233,9 @@ proxy_get(Config) when is_list(Config) ->
{skip, Reason}
end.
+
%%-------------------------------------------------------------------------
+
proxy_emulate_lower_versions(doc) ->
["Perform requests as 0.9 and 1.0 clients."];
proxy_emulate_lower_versions(suite) ->
@@ -1260,7 +1279,9 @@ proxy_emulate_lower_versions(Config) when is_list(Config) ->
pelv_get(Version) ->
http:request(get, {?PROXY_URL, []}, [{version, Version}], []).
+
%%-------------------------------------------------------------------------
+
proxy_trace(doc) ->
["Perform a TRACE request that goes through a proxy."];
proxy_trace(suite) ->
@@ -1268,11 +1289,12 @@ proxy_trace(suite) ->
proxy_trace(Config) when is_list(Config) ->
%%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} =
%% http:request(trace, {?PROXY_URL, []}, [], []),
- {skip, "HTTP TRACE is no longer allowed on the ?PROXY_URL server due "
- "to security reasons"}.
+ skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due "
+ "to security reasons").
%%-------------------------------------------------------------------------
+
proxy_post(doc) ->
["Perform a POST request that goes through a proxy. Note the server"
" will reject the request this is a test of the sending of the"
@@ -1280,21 +1302,34 @@ proxy_post(doc) ->
proxy_post(suite) ->
[];
proxy_post(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- case http:request(post, {?PROXY_URL, [],
- "text/plain", "foobar"}, [],[]) of
- {ok, {{_,405,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- test_server:fail({unexpected_result, Unexpected})
- end;
- Reason ->
- {skip, Reason}
- end.
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% case ?config(skip, Config) of
+ %% undefined ->
+ %% Command =
+ %% fun() ->
+ %% http:request(post, {?PROXY_URL, [],
+ %% "text/plain", "foobar"}, [],[])
+ %% end,
+ %% Verify =
+ %% fun({ok, {{_,405,_}, [_ | _], [_ | _]}}) ->
+ %% ok;
+ %% ({ok, Result}) ->
+ %% tsf({unexpected_success, Result});
+ %% (Unexpected) ->
+ %% tsf({unexpected_result, Unexpected})
+ %% end,
+ %% expect(Command, Verify);
+ %% Reason ->
+ %% skip(Reason)
+ %% end.
+ skip("temporary skip").
%%-------------------------------------------------------------------------
+
proxy_put(doc) ->
["Perform a PUT request that goes through a proxy. Note the server"
" will reject the request this is a test of the sending of the"
@@ -1302,22 +1337,28 @@ proxy_put(doc) ->
proxy_put(suite) ->
[];
proxy_put(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- case http:request(put, {"http://www.erlang.org/foobar.html", [],
- "html", "<html> <body><h1> foo </h1>"
- "<p>bar</p> </body></html>"}, [], []) of
- {ok, {{_,405,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- test_server:fail({unexpected_result, Unexpected})
- end;
- Reason ->
- {skip, Reason}
- end.
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% case ?config(skip, Config) of
+ %% undefined ->
+ %% case http:request(put, {"http://www.erlang.org/foobar.html", [],
+ %% "html", "<html> <body><h1> foo </h1>"
+ %% "<p>bar</p> </body></html>"}, [], []) of
+ %% {ok, {{_,405,_}, [_ | _], [_ | _]}} ->
+ %% ok;
+ %% Unexpected ->
+ %% test_server:fail({unexpected_result, Unexpected})
+ %% end;
+ %% Reason ->
+ %% {skip, Reason}
+ %% end.
+ skip("temporary skip").
%%-------------------------------------------------------------------------
+
proxy_delete(doc) ->
["Perform a DELETE request that goes through a proxy. Note the server"
" will reject the request this is a test of the sending of the"
@@ -1326,21 +1367,32 @@ proxy_delete(doc) ->
proxy_delete(suite) ->
[];
proxy_delete(Config) when is_list(Config) ->
- case ?config(skip, Config) of
- undefined ->
- URL = ?PROXY_URL ++ "/foobar.html",
- case http:request(delete, {URL, []}, [], []) of
- {ok, {{_,404,_}, [_ | _], [_ | _]}} ->
- ok;
- Unexpected ->
- test_server:fail({unexpected_result, Unexpected})
- end;
- Reason ->
- {skip, Reason}
- end.
+ %% After the switch to the erlang.org app,
+ %% the post will succeed, so we skip this
+ %% until we can find a new way of testing
+ %% this.
+ %% case ?config(skip, Config) of
+ %% undefined ->
+ %% URL = ?PROXY_URL ++ "/foobar.html",
+ %% Command = fun() -> http:request(delete, {URL, []}, [], []) end,
+ %% Verify =
+ %% fun({ok, {{_,404,_}, [_ | _], [_ | _]}}) ->
+ %% ok;
+ %% ({ok, Result}) ->
+ %% tsf({unexpected_success, Result});
+ %% (Unexpected) ->
+ %% tsf({unexpected_result, Unexpected})
+ %% end,
+ %% expect(Command, Verify);
+
+ %% Reason ->
+ %% skip(Reason)
+ %% end.
+ skip("temporary skip").
%%-------------------------------------------------------------------------
+
proxy_headers(doc) ->
["Use as many request headers as possible"];
proxy_headers(suite) ->
@@ -2489,7 +2541,7 @@ otp_8739_dummy_server_init(Parent) ->
Parent ! {port, Port},
otp_8739_dummy_server_main(Parent, ListenSocket).
-otp_8739_dummy_server_main(Parent, ListenSocket) ->
+otp_8739_dummy_server_main(_Parent, ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Sock} ->
%% Ignore the request, and simply wait for the socket to close
@@ -2990,29 +3042,46 @@ content_length([_Head | Tail]) ->
content_length(Tail).
provocate_not_modified_bug(Url) ->
+ tsp("provocate_not_modified_bug -> entry with"
+ "~n Url: ~p", [Url]),
+
Timeout = 15000, %% 15s should be plenty
- {ok, {{_, 200, _}, ReplyHeaders, _Body}} =
- http:request(get, {Url, []}, [{timeout, Timeout}], []),
- Etag = pick_header(ReplyHeaders, "ETag"),
- Last = pick_header(ReplyHeaders, "last-modified"),
-
- case http:request(get, {Url, [{"If-None-Match", Etag},
- {"If-Modified-Since", Last}]},
- [{timeout, 15000}],
- []) of
- {ok, {{_, 304, _}, _, _}} -> %% The expected reply
- page_unchanged;
- {ok, {{_, 200, _}, _, _}} ->
- %% If the page has changed since the
- %% last request we retry to
- %% trigger the bug
- provocate_not_modified_bug(Url);
- {error, timeout} ->
- %% Not what we expected. Tcpdump can be used to
- %% verify that we receive the complete http-reply
- %% but still time out.
- incorrect_result
+ case http:request(get, {Url, []}, [{timeout, Timeout}], []) of
+ {ok, {{_, 200, _}, ReplyHeaders, _Body}} ->
+ tsp("provocate_not_modified_bug -> received 200 reply"
+ "~n ReplyHeaders: ~p", [ReplyHeaders]),
+ Etag = pick_header(ReplyHeaders, "ETag"),
+ Last = pick_header(ReplyHeaders, "last-modified"),
+ case http:request(get, {Url, [{"If-None-Match", Etag},
+ {"If-Modified-Since", Last}]},
+ [{timeout, 15000}],
+ []) of
+ {ok, {{_, 304, _}, _, _}} -> %% The expected reply
+ tsp("provocate_not_modified_bug -> unchanged", []),
+ page_unchanged;
+ {ok, {{_, 200, _}, _, _}} ->
+ %% If the page has changed since the
+ %% last request we retry to
+ %% trigger the bug
+ tsp("provocate_not_modified_bug -> changed", []),
+ provocate_not_modified_bug(Url);
+ {error, timeout} ->
+ %% Not what we expected. Tcpdump can be used to
+ %% verify that we receive the complete http-reply
+ %% but still time out.
+ tsp("provocate_not_modified_bug -> timeout", []),
+ incorrect_result
+ end;
+ {ok, UnexpectedReply, ReplyHeaders, _} ->
+ tsp("provocate_not_modified_bug -> received unexpected reply"
+ "~n UnexpectedReply: ~p"
+ "~n ReplyHeaders: ~p", [UnexpectedReply, ReplyHeaders]),
+ unexpected_reply;
+ {error, Reason} ->
+ tsp("provocate_not_modified_bug -> unexpected failure"
+ "~n Reason: ~p", [Reason]),
+ unexpected_error
end.
pick_header(Headers, Name) ->
@@ -3029,6 +3098,10 @@ not_implemented_yet() ->
exit(not_implemented_yet).
+expect(Command, Verify) ->
+ Result = (catch Command()),
+ Verify(Result).
+
p(F) ->
p(F, []).
@@ -3038,7 +3111,24 @@ p(F, A) ->
tsp(F) ->
tsp(F, []).
tsp(F, A) ->
- test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+ Timestamp = formated_timestamp(),
+ test_server:format("** ~s ** ~p ~p:" ++ F ++ "~n", [Timestamp, self(), ?MODULE | A]).
+
+formated_timestamp() ->
+ format_timestamp( os:timestamp() ).
+
+format_timestamp({_N1, _N2, N3} = Now) ->
+ {Date, Time} = calendar:now_to_datetime(Now),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+ [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+ lists:flatten(FormatDate).
tsf(Reason) ->
test_server:fail(Reason).
+
+skip(Reason) ->
+ {skip, Reason}.
+
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index f86c1fcb49..ed0fe942cf 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,13 +25,16 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
+-define(URL_START, "http://localhost:").
+
all(doc) ->
["Basic test of httpd."];
all(suite) ->
[
uri_too_long_414,
- header_too_long_413
+ header_too_long_413,
+ escaped_url_in_error_body
].
%%--------------------------------------------------------------------
@@ -132,5 +135,57 @@ header_too_long_413(Config) when is_list(Config) ->
inets:stop(httpd, Pid).
+escaped_url_in_error_body(doc) ->
+ ["Test Url-encoding see OTP-8940"];
+escaped_url_in_error_body(suite) ->
+ [];
+escaped_url_in_error_body(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ _Address = proplists:get_value(bind_address, Info),
+ Path = "/<b>this_is_bold<b>",
+ URL = ?URL_START ++ integer_to_list(Port) ++ Path,
+ EscapedPath = http_uri:encode(Path),
+ case httpc:request(get, {URL, []},
+ [{url_encode, true},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {404, Body1}} ->
+ case find_URL_path(string:tokens(Body1, " ")) of
+ EscapedPath ->
+ ok;
+ BadPath1 ->
+ tsf({unexpected_path_1, EscapedPath, BadPath1})
+ end;
+ {ok, UnexpectedOK1} ->
+ tsf({unexpected_ok_1, UnexpectedOK1})
+ end,
+
+ case httpc:request(get, {URL, []},
+ [{version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {404, Body2}} ->
+ HTMLEncodedPath = http_util:html_encode(Path),
+ case find_URL_path(string:tokens(Body2, " ")) of
+ HTMLEncodedPath ->
+ ok;
+ BadPath2 ->
+ tsf({unexpected_path_2, EscapedPath, BadPath2})
+ end;
+ {ok, UnexpectedOK2} ->
+ tsf({unexpected_ok_2, UnexpectedOK2})
+ end,
+ inets:stop(httpd, Pid).
+
+find_URL_path([]) ->
+ "";
+find_URL_path(["URL", URL | _]) ->
+ URL;
+find_URL_path([_ | Rest]) ->
+ find_URL_path(Rest).
+tsf(Reason) ->
+ test_server:fail(Reason).
diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec
index a9b4524295..ba525f62c1 100644
--- a/lib/inets/test/inets.spec
+++ b/lib/inets/test/inets.spec
@@ -1,2 +1,2 @@
{topcase, {dir, "../inets_test"}}.
-{hosts, ["tuor"]}.
+{hosts, ["fobi"]}.
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index 6af2ad32f7..609bc89e15 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,14 +32,64 @@
-export([non_pc_tc_maybe_skip/4, os_based_skip/1]).
start_http_server(Conf) ->
- application:load(inets),
- ok = application:set_env(inets, services, [{httpd, Conf}]),
- ok = application:start(inets).
+ ?DEBUG("start_http_server -> entry with"
+ "~n Conf: ~p", [Conf]),
+ inets_ensure_loaded(),
+ inets_set_env(services, [{httpd, Conf}]),
+ inets_ensure_started(),
+ ok.
+
start_http_server_ssl(FileName) ->
application:start(ssl),
catch start_http_server(FileName).
+inets_ensure_loaded() ->
+ ensure_loaded(inets).
+
+ensure_loaded(App) ->
+ case application:load(App) of
+ ok ->
+ ok;
+ {error, {already_loaded, _}} ->
+ ok;
+ LoadRes ->
+ ?LOG("start_http_server -> failed loading ~p: ~p", [App, LoadRes]),
+ tsf({failed_loading, LoadRes})
+ end.
+
+
+inets_set_env(Service, Config) ->
+ app_set_env(inets, Service, Config).
+
+app_set_env(App, Param, Value) ->
+ case application:set_env(App, Param, Value) of
+ ok ->
+ ?DEBUG("start_http_server -> env set", []),
+ ok;
+ SetEnvRes ->
+ ?LOG("start_http_server -> failed set env for ~p: ~p",
+ [App, SetEnvRes]),
+ exit({failed_set_env, App, SetEnvRes})
+ end.
+
+inets_ensure_started() ->
+ ensure_app_started(inets).
+
+ensure_app_started(App) ->
+ case application:start(App) of
+ ok ->
+ ?DEBUG("start_http_server -> ~p started", [App]),
+ ok;
+ {error, {already_started, _}} ->
+ ok;
+ StartRes ->
+ ?LOG("start_http_server -> failed starting ~p: ~p",
+ [App, StartRes]),
+ exit({failed_starting, App, StartRes})
+ end.
+
+
%% ----------------------------------------------------------------------
%% print functions
%%
@@ -300,3 +350,6 @@ sleep(MSecs) ->
skip(Reason, File, Line) ->
exit({skipped, {Reason, File, Line}}).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 408f49c19f..feb29107bf 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -1,9 +1,11 @@
APPLICATION = inets
-INETS_VSN = 5.3.4
+INETS_VSN = 5.3.5
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
-TICKETS = OTP-8739 OTP-8741 OTP-8742
+TICKETS = OTP-8940
+
+TICKETS_5_3_4 = OTP-8739 OTP-8741 OTP-8742
TICKETS_5_3_3 = \
OTP-8609 \