diff options
Diffstat (limited to 'lib/inets')
35 files changed, 655 insertions, 247 deletions
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 741f2abaef..d9a27e7d1e 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -300,11 +300,11 @@ filename() = string() process or to a file. When streaming to the calling process using the option <c>self</c> the following stream messages will be sent to that process: <c>{http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When + stream_start, Headers}}, {http, {RequestId, stream, + BinBodyPart}}, {http, {RequestId, stream_end, Headers}}</c>. When streaming to to the calling processes using the option <c>{self, once}</c> the first message will have an additional - element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, + element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}}</c>, this is the process id that should be used as an argument to <c>http:stream_next/1</c> to trigger the next message to be sent to the calling process. </p> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 8438961511..4210aea3ec 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -251,14 +251,14 @@ </item> <marker id="prop_max_uri"></marker> - <tag>{max_uri, integer()}</tag> + <tag>{max_uri_size, integer()}</tag> <item> <p>Limits the size of the HTTP request URI. By default there is no limit. </p> </item> <marker id="prop_max_keep_alive_req"></marker> - <tag>{max_keep_alive_requests, integer()}</tag> + <tag>{max_keep_alive_request, integer()}</tag> <item> <p>The number of request that a client can do on one connection. When the server has responded to the number of @@ -406,7 +406,7 @@ bytes begins with url-path is mapped to local files that begins with directory-filename, for example: - <code>{alias, {"/image", "/ftp/pub/image"}</code> + <code>{alias, {"/image", "/ftp/pub/image"}}</code> and an access to http://your.server.org/image/foo.gif would refer to the file /ftp/pub/image/foo.gif. </p> @@ -421,7 +421,7 @@ bytes by re:replace/3 to produce a path in the local filesystem. For example: - <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}</code> + <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}}</code> and an access to http://your.server.org/~bob/foo.gif would refer to the file /home/bob/public/foo.gif. @@ -468,7 +468,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> + <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}}</code> and an access to http://your.server.org/cgi-bin/foo would cause the server to run the script /web/cgi-bin/foo. </p> @@ -483,7 +483,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> + <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}}</code> and an access to http://your.server.org/cgi-bin/17/foo would cause the server to run the script /web/17/cgi-bin/foo. </p> @@ -517,7 +517,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}</code> + <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}}</code> </p> </item> @@ -532,7 +532,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code>{script, {"PUT", "/cgi-bin/put"}</code> + <code>{script, {"PUT", "/cgi-bin/put"}}</code> </p> </item> @@ -549,7 +549,7 @@ bytes scheme scripts. A matching URL is mapped into a specific module and function. For example: - <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]} + <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]}} </code> and a request to @@ -632,7 +632,7 @@ bytes </item> <marker id="prop_edlog"></marker> - <tag>{error_disk_log, internal | external}</tag> + <tag>{error_disk_log, path()}</tag> <item> <p>Defines the filename of the (disk_log(3)) error log file to be used to log server errors. If the filename does not begin @@ -706,7 +706,7 @@ bytes For example: - <code>{allow_from, ["123.34.56.11", "150.100.23"] </code> + <code>{allow_from, ["123.34.56.11", "150.100.23"]}</code> The host 123.34.56.11 and all machines on the 150.100.23 subnet are allowed access. </p> @@ -719,7 +719,7 @@ bytes which should be denied access to a given directory. For example: - <code>{deny_from, ["123.34.56.11", "150.100.23"] </code> + <code>{deny_from, ["123.34.56.11", "150.100.23"]}</code> The host 123.34.56.11 and all machines on the 150.100.23 subnet are not allowed access. </p> @@ -835,7 +835,7 @@ bytes <p><em>Security properties - requires mod_security </em></p> <marker id="prop_sec_dir"></marker> - <p><em>{security_directory, {path(), [{property(), term()}]}</em></p> + <p><em>{security_directory, {path(), [{property(), term()}]}}</em></p> <marker id="props_sdir"></marker> <p>Here follows the valid properties for security directories</p> @@ -1067,7 +1067,7 @@ bytes <fsummary>Called for each request to the Web server.</fsummary> <type> <v>OldData = list()</v> - <v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}] </v> + <v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}}] </v> <v>StausCode = integer()</v> <v>Body = io_list() | nobody | {Fun, Arg}</v> <v>Head = [HeaderOption]</v> diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 9f290084d2..9218ee91e2 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -337,10 +337,10 @@ <func> <name>rfc1123_date() -> RFC1123Date</name> - <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}}) -> RFC1123Date</name> + <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name> <fsummary>Return the current date in RFC 1123 format.</fsummary> <type> - <v>YYYY = MM = DD = Hour = Min =Sec = integer()</v> + <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v> <v>RFC1123Date = string()</v> </type> <desc> diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index 265a1b8e76..b38be5db28 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -118,7 +118,7 @@ </func> <func> - <name>real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret</name> + <name>real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret</name> <fsummary>Expand a request uri using ScriptAlias config directives.</fsummary> <type> <v>ConfigDB = config_db()</v> @@ -129,7 +129,7 @@ </type> <desc> <marker id="real_script_name"></marker> - <p><c>real_name/3</c> traverses <c>ScriptAliases</c>, + <p><c>real_script_name/3</c> traverses <c>ScriptAliases</c>, typically extracted from <c>ConfigDB</c>, and matches each <c>FakeName</c> with <c>RequestURI</c>. If a match is found <c>FakeName</c> is replaced with <c>RealName</c> in the diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 525beecd31..4e0dc8bd37 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -32,13 +32,112 @@ <file>notes.xml</file> </header> - - <section><title>Inets 5.9.4</title> + <section><title>Inets 5.9.6</title> <section><title>Improvements and New Features</title> <list> <item> <p> + httpc: Allow content body in DELETE requests. Thanks to + James Wheare.</p> + <p> + Own Id: OTP-11190</p> + </item> + <item> + <p> + Add missing brackets to report formatting on ftp_progress + process exit. Thanks to Artur Wilniewczyc.</p> + <p> + Own Id: OTP-11202</p> + </item> + <item> + <p> + Fix some errors in the inets documentation. Thanks to + Johannes Weissl.</p> + <p> + Own Id: OTP-11210</p> + </item> + <item> + <p> + Fix various typos in httpd, inets. Thanks to Tomohiko + Aono.</p> + <p> + Own Id: OTP-11226</p> + </item> + <item> + <p> + Fix httpd config option 'erl_script_nocache'. Thanks to + Johannes Weissl.</p> + <p> + Own Id: OTP-11260</p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 5.9.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Reverted incorrect commit that broke cookie handling when + using httpc-profiles.</p> + <p> + Own Id: OTP-10956</p> + </item> + </list> + </section> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Fix http_request:http_headers/1 to send content-length + when length is zero. Thanks to CA Meijer.</p> + <p> + Own Id: OTP-10934</p> + </item> + <item> + <p> + Integrate elliptic curve contribution from Andreas + Schultz </p> + <p> + In order to be able to support elliptic curve cipher + suites in SSL/TLS, additions to handle elliptic curve + infrastructure has been added to public_key and crypto.</p> + <p> + This also has resulted in a rewrite of the crypto API to + gain consistency and remove unnecessary overhead. All OTP + applications using crypto has been updated to use the new + API.</p> + <p> + Impact: Elliptic curve cryptography (ECC) offers + equivalent security with smaller key sizes than other + public key algorithms. Smaller key sizes result in + savings for power, memory, bandwidth, and computational + cost that make ECC especially attractive for constrained + environments.</p> + <p> + Own Id: OTP-11009</p> + </item> + <item> + <p> + Fix {stream, {self, once}} in httpc to work as expected. + Thanks to Masatake Daimon</p> + <p> + Own Id: OTP-11122</p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 5.9.4</title> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> httpd: The modules option now defaults to the documented value.</p> <p> @@ -101,6 +200,32 @@ </section> </section> +<section><title>Inets 5.9.2.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Make log_alert configurable as option in ssl, SSLLogLevel + added as option to inets conf file</p> + <p> + Own Id: OTP-11259</p> + </item> + </list> + </section> +</section> +<section><title>Inets 5.9.2.1</title> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Fixed obsolete error report in inets.</p> + <p> + Own Id: OTP-11185 Aux Id: seq12357 </p> + </item> + </list> + </section> +</section> <section><title>Inets 5.9.2</title> @@ -535,7 +660,7 @@ <p>[httpd] Fix httpd directory traversal on Windows. Directory traversal was possible on Windows where backward slash is used as directory separator. </p> - <p>Andr�s Veres-Szentkir�lyi.</p> + <p>András Veres-Szentkirályi.</p> <p>Own Id: OTP-9561</p> </item> @@ -643,7 +768,7 @@ <item> <p>[httpd] Improved error messages. </p> - <p>Ricardo Catalinas Jim�nez</p> + <p>Ricardo Catalinas Jiménez</p> <p>Own Id: OTP-9157</p> </item> diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index bd59c1ba47..4162ab97bb 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -834,7 +834,7 @@ <list type="bulleted"> <item> <p>[ftp, client] - A new option {progress, {CBmodule, - CBFunction, InitProgressTerm} has been added to allow + CBFunction, InitProgressTerm}} has been added to allow users to create things such as progress bars in there GUI's. The option affects ftp:send/[3,4] and ftp:recv/[3,4].</p> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 132a384a49..5d9887a9a4 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1417,7 +1417,7 @@ handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> handle_info({'EXIT', Pid, Reason}, #state{progress = Pid} = State) -> Report = io_lib:format("Progress reporting stopped for reason ~p~n", - Reason), + [Reason]), error_logger:info_report(Report), {noreply, State#state{progress = ignore}}; diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index ede649a5a9..4d7023a8e9 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-2012. All Rights Reserved. +%% Copyright Ericsson AB 2009-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -163,14 +163,19 @@ request(Method, {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> - handle_request(Method, Url, ParsedUrl, Headers, [], [], - HTTPOptions, Options, Profile) + case header_parse(Headers) of + {error, Reason} -> + {error, Reason}; + _ -> + handle_request(Method, Url, ParsedUrl, Headers, [], [], + HTTPOptions, Options, Profile) + end end; request(Method, {Url, Headers, ContentType, Body}, HTTPOptions, Options, Profile) - when ((Method =:= post) orelse (Method =:= put)) andalso + when ((Method =:= post) orelse (Method =:= put) orelse (Method =:= delete)) andalso (is_atom(Profile) orelse is_pid(Profile)) -> ?hcrt("request", [{method, Method}, {url, Url}, @@ -1247,7 +1252,12 @@ uri_parse(URI, Opts) -> %%-------------------------------------------------------------------------- - +header_parse([]) -> + ok; +header_parse([{Field, Value}|T]) when is_list(Field), is_list(Value) -> + header_parse(T); +header_parse(_) -> + {error, {headers_error, not_strings}}. child_name2info(undefined) -> {error, no_such_service}; child_name2info(httpc_manager) -> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 857043bae2..55794f57dc 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -33,7 +33,6 @@ %% connect_and_send/2, send/2, cancel/3, - stream/3, stream_next/1, info/1 ]). @@ -65,7 +64,7 @@ options, % #options{} timers = #timers{}, % #timers{} profile_name, % atom() - id of httpc_manager process. - once % send | undefined + once = inactive % inactive | once }). @@ -231,6 +230,8 @@ init([Parent, Request, Options, ProfileName]) -> ProxyOptions = handle_proxy_options(Request#request.scheme, Options), Address = handle_proxy(Request#request.address, ProxyOptions), {ok, State} = + %% #state.once should initially be 'inactive' because we + %% activate the socket at first regardless of the state. case {Address /= Request#request.address, Request#request.scheme} of {true, https} -> connect_and_send_upgrade_request(Address, Request, @@ -425,7 +426,9 @@ handle_cast({cancel, RequestId, From}, handle_cast(stream_next, #state{session = Session} = State) -> activate_once(Session), - {noreply, State#state{once = once}}. + %% Inactivate the #state.once here because we don't want + %% next_body_chunk/1 to activate the socket twice. + {noreply, State#state{once = inactive}}. %%-------------------------------------------------------------------- @@ -478,6 +481,41 @@ handle_info({Proto, _Socket, Data}, NewMFA = {Module, whole_body, [NewBody, NewLength]}, {noreply, NewState#state{mfa = NewMFA, request = NewRequest}}; + {Module, decode_size, + [TotalChunk, HexList, + {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]} + when BodySoFar =/= <<>> -> + ?hcrd("data processed - decode_size", []), + %% 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), + NewMFA = {Module, decode_size, + [TotalChunk, HexList, + {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}, + {noreply, NewState#state{mfa = NewMFA, + request = NewRequest}}; + {Module, decode_data, + [ChunkSize, TotalChunk, + {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}]} + when TotalChunk =/= <<>> orelse BodySoFar =/= <<>> -> + ?hcrd("data processed - decode_data", []), + %% The response body is chunk-encoded. Steal decoded + %% chunks as much as possible to stream. + ChunkSizeToSteal = min(ChunkSize, byte_size(TotalChunk)), + <<StolenChunk:ChunkSizeToSteal/binary, NewTotalChunk/binary>> = TotalChunk, + StolenBody = <<BodySoFar/binary, StolenChunk/binary>>, + NewChunkSize = ChunkSize - ChunkSizeToSteal, + {_, Code, _} = StatusLine, + + {NewBody, NewRequest} = stream(StolenBody, Request, Code), + NewState = next_body_chunk(State), + NewMFA = {Module, decode_data, + [NewChunkSize, NewTotalChunk, + {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}, + {noreply, NewState#state{mfa = NewMFA, + request = NewRequest}}; NewMFA -> ?hcrd("data processed - new mfa", []), activate_once(Session), @@ -1027,11 +1065,15 @@ handle_http_msg({Version, StatusCode, ReasonPharse, Headers, Body}, status_line = StatusLine, headers = Headers}) end; -handle_http_msg({ChunkedHeaders, Body}, #state{headers = Headers} = State) -> +handle_http_msg({ChunkedHeaders, Body}, + #state{status_line = {_, Code, _}, headers = Headers} = State) -> ?hcrt("handle_http_msg", [{chunked_headers, ChunkedHeaders}, {headers, Headers}]), NewHeaders = http_chunk:handle_headers(Headers, ChunkedHeaders), - handle_response(State#state{headers = NewHeaders, body = Body}); + {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), @@ -1070,8 +1112,7 @@ handle_http_body(Body, #state{headers = Headers, "chunked" -> ?hcrt("handle_http_body - chunked", []), case http_chunk:decode(Body, State#state.max_body_size, - State#state.max_header_size, - {Code, Request}) of + State#state.max_header_size) of {Module, Function, Args} -> ?hcrt("handle_http_body - new mfa", [{module, Module}, @@ -1142,7 +1183,7 @@ handle_response(#state{request = Request, {session, Session}, {status_line, StatusLine}]), - handle_cookies(Headers, Request, Options, httpc_manager), %% FOO profile_name + handle_cookies(Headers, Request, Options, ProfileName), case httpc_response:result({StatusLine, Headers, Body}, Request) of %% 100-continue continue -> diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index f177aac8f2..9107dfbf05 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -430,8 +430,6 @@ format_response({StatusLine, Headers, Body}) -> Length = list_to_integer(Headers#http_response_h.'content-length'), {NewBody, Data} = case Length of - 0 -> - {Body, <<>>}; -1 -> % When no lenght indicator is provided {Body, <<>>}; Length when (Length =< size(Body)) -> diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl index 57647438e9..d15d7ba49a 100644 --- a/lib/inets/src/http_lib/http_chunk.erl +++ b/lib/inets/src/http_lib/http_chunk.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ -include("http_internal.hrl"). %% API --export([decode/3, decode/4, encode/1, encode_last/0, handle_headers/2]). +-export([decode/3, encode/1, encode_last/0, 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]). @@ -34,20 +34,14 @@ %%% API %%%========================================================================= %%------------------------------------------------------------------------- -%% decode(ChunkedBody, MaxBodySize, MaxHeaderSize, <Stream>) -> +%% decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> %% {ok, {Headers, Body}} | {Module, Function, Args} %% %% Headers = ["Header:Value"] %% ChunkedBody = binary() %% MaxBodySize = integer() %% MaxHeaderSize = integer() -%% Stream = {Code, Request} - if Request#request.stream =/= none -%% and Code == 200 the side effect of sending each decode chunk to the -%% client/file before the whole body is received will take place. %% -%% Note: decode/4 should only be used from httpc_handler module. -%% Otherwhise use the side effect free decode/3. -%% %% Description: Decodes a body encoded by the chunked transfer %% encoding. If the ChunkedBody is not compleate it returns {Module, %% Function, Args} so that decoding can be continued when more of the @@ -61,12 +55,9 @@ %% the next pass in the loop. %%------------------------------------------------------------------------- decode(ChunkedBody, MaxBodySize, MaxHeaderSize) -> - decode(ChunkedBody, MaxBodySize, MaxHeaderSize, false). - -decode(ChunkedBody, MaxBodySize, MaxHeaderSize, Stream) -> %% Note decode_size will call decode_data. - decode_size([ChunkedBody, <<>>, [], - {MaxBodySize, <<>>, 0, MaxHeaderSize, Stream}]). + decode_size([ChunkedBody, <<>>, [], + {MaxBodySize, <<>>, 0, MaxHeaderSize}]). %%------------------------------------------------------------------------- %% encode(Chunk) -> EncodedChunk @@ -150,7 +141,7 @@ decode_size(<<>>, HexList, Info) -> decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, {MaxBodySize, Body, AccLength, - MaxHeaderSize, Stream}) -> + MaxHeaderSize}) -> ChunkSize = http_util:hexlist_to_integer(lists:reverse(HexList)), case ChunkSize of 0 -> % Last chunk, there was no data @@ -164,7 +155,7 @@ decode_size(Data = <<?CR, ?LF, ChunkRest/binary>>, HexList, %% to this function comes in. decode_data(ChunkSize, ChunkRest, {MaxBodySize, Body, ChunkSize + AccLength , - MaxHeaderSize, Stream}) + MaxHeaderSize}) end; decode_size(<<";", Rest/binary>>, HexList, Info) -> %% Note ignore_extensions will call decode_size/1 again when @@ -189,50 +180,42 @@ ignore_extensions(<<_Octet, Rest/binary>>, NextFunction) -> ignore_extensions(Rest, NextFunction). decode_data(ChunkSize, TotalChunk, - Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize, Stream}) + Info = {MaxBodySize, BodySoFar, AccLength, MaxHeaderSize}) when ChunkSize =< size(TotalChunk) -> case TotalChunk of %% Last chunk <<Data:ChunkSize/binary, ?CR, ?LF, "0", ";">> -> %% Note ignore_extensions will call decode_trailer/1 %% once it ignored all extensions. - {NewBody, _} = - stream(<<BodySoFar/binary, Data/binary>>, Stream), {?MODULE, ignore_extensions, [<<>>, {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, - NewBody, + <<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. - {NewBody, _} = stream(<<BodySoFar/binary, Data/binary>>, Stream), ignore_extensions(Rest, {?MODULE, decode_trailer, [<<>>, [],[], MaxHeaderSize, - NewBody, + <<BodySoFar/binary, Data/binary>>, integer_to_list(AccLength)]}); <<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF>> -> - {NewBody, _} = stream(<<BodySoFar/binary, Data/binary>>, Stream), {?MODULE, decode_trailer, [<<?CR, ?LF>>, [],[], MaxHeaderSize, - NewBody, + <<BodySoFar/binary, Data/binary>>, integer_to_list(AccLength)]}; <<Data:ChunkSize/binary, ?CR, ?LF, "0", ?CR, ?LF, Rest/binary>> -> - {NewBody,_}= stream(<<BodySoFar/binary, Data/binary>>, Stream), decode_trailer(<<?CR, ?LF, Rest/binary>>, [],[], MaxHeaderSize, - NewBody, + <<BodySoFar/binary, Data/binary>>, integer_to_list(AccLength)); %% There are more chunks, so here we go agin... <<Data:ChunkSize/binary, ?CR, ?LF>> -> - {NewBody, NewStream} = - stream(<<BodySoFar/binary, Data/binary>>, Stream), - {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize, NewStream}]}; + NewBody = <<BodySoFar/binary, Data/binary>>, + {?MODULE, decode_size, [<<>>, [], {MaxBodySize, NewBody, AccLength, MaxHeaderSize}]}; <<Data:ChunkSize/binary, ?CR, ?LF, Rest/binary>> when (AccLength < MaxBodySize) or (MaxBodySize == nolimit) -> - {NewBody, NewStream} = - stream(<<BodySoFar/binary, Data/binary>>, Stream), decode_size(Rest, [], - {MaxBodySize, NewBody, - AccLength, MaxHeaderSize, NewStream}); + {MaxBodySize, <<BodySoFar/binary, Data/binary>>, + AccLength, MaxHeaderSize}); <<_:ChunkSize/binary, ?CR, ?LF, _/binary>> -> throw({error, body_too_big}); _ -> @@ -286,9 +269,3 @@ decode_trailer(<<Octet, Rest/binary>>, Header, Headers, MaxHeaderSize, Body, BodyLength) -> decode_trailer(Rest, [Octet | Header], Headers, MaxHeaderSize, Body, BodyLength). - -stream(BodyPart, false) -> - {BodyPart, false}; -stream(BodyPart, {Code, Request}) -> - {NewBody, NewRequest} = httpc_handler:stream(BodyPart, Request, Code), - {NewBody, {Code, NewRequest}}. diff --git a/lib/inets/src/http_lib/http_request.erl b/lib/inets/src/http_lib/http_request.erl index c214aca4a4..aa9f9f6774 100644 --- a/lib/inets/src/http_lib/http_request.erl +++ b/lib/inets/src/http_lib/http_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -248,13 +248,8 @@ key_value_str(Key = 'content-language', Headers) -> key_value_str(atom_to_list(Key), Headers#http_request_h.'content-language'); key_value_str(Key = 'content-length', Headers) -> - case Headers#http_request_h.'content-length' of - "0" -> - undefined; - _ -> - key_value_str(atom_to_list(Key), - Headers#http_request_h.'content-length') - end; + key_value_str(atom_to_list(Key), + Headers#http_request_h.'content-length'); key_value_str(Key = 'content-location', Headers) -> key_value_str(atom_to_list(Key), Headers#http_request_h.'content-location'); diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index df58fa1b81..7e679531cf 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -159,7 +159,7 @@ listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) -> listen_ip_comm(Addr, Port, Fd, IpFamily); listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) -> - listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily). + listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []). listen(ip_comm = _SocketType, Addr, Port, IpFamily) -> listen_ip_comm(Addr, Port, undefined, IpFamily); @@ -178,7 +178,13 @@ listen({essl, SSLConfig}, Addr, Port, IpFamily) -> [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - listen_ssl(Addr, Port, undefined, SSLConfig, IpFamily). + {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of + undefined -> + {SSLConfig, []}; + LogAlert -> + {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]} + 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 @@ -221,24 +227,23 @@ do_listen_ip_comm(Addr, Port, Fd, IpFamily) -> gen_tcp:listen(NewPort, Opts2) end. - -listen_ssl(Addr, Port, Fd, Opts0, IpFamily) -> +listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) -> {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd), Opts = SockOpt ++ Opts0, case IpFamily of inet6fb4 -> - Opts2 = [inet6 | Opts], + 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], + Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen failed - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), ssl:listen(NewPort, Opts3); {'EXIT', Reason} -> - Opts3 = [inet | Opts], + Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen exit - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), ssl:listen(NewPort, Opts3); @@ -251,7 +256,7 @@ listen_ssl(Addr, Port, Fd, Opts0, IpFamily) -> _ -> Opts2 = [IpFamily | Opts], ?hlrt("listen", [{opts, Opts2}]), - ssl:listen(NewPort, Opts2) + ssl:listen(NewPort, Opts2 ++ ExtraOpts) end. diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index d45f3c0048..b3ca13e2fe 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -390,6 +390,13 @@ load("SSLCertificateFile " ++ SSLCertificateFile, []) -> {error, ?NICE(clean(SSLCertificateFile)++ " is an invalid SSLCertificateFile")} end; +load("SSLLogLevel " ++ SSLLogAlert, []) -> + case SSLLogAlert of + "none" -> + {ok, [], {ssl_log_alert, false}}; + _ -> + {ok, [], {ssl_log_alert, true}} + end; load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) -> case is_file(clean(SSLCertificateKeyFile)) of {ok, File} -> @@ -948,7 +955,8 @@ ssl_config(ConfigDB) -> ssl_ciphers(ConfigDB) ++ ssl_password(ConfigDB) ++ ssl_verify_depth(ConfigDB) ++ - ssl_ca_certificate_file(ConfigDB). + ssl_ca_certificate_file(ConfigDB) ++ + ssl_log_level(ConfigDB). @@ -1214,6 +1222,14 @@ ssl_certificate_key_file(ConfigDB) -> [{keyfile,SSLCertificateKeyFile}] end. +ssl_log_level(ConfigDB) -> + case httpd_util:lookup(ConfigDB,ssl_log_alert) of + undefined -> + []; + SSLLogLevel -> + [{log_alert,SSLLogLevel}] + end. + ssl_verify_client(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_verify_client) of undefined -> diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl index 672a70a394..00384fa108 100644 --- a/lib/inets/src/http_server/httpd_manager.erl +++ b/lib/inets/src/http_server/httpd_manager.erl @@ -507,37 +507,8 @@ code_change(_FromVsn, State, _Extra) -> check_connections(#state{connections = []} = State, _Pid, _Reason) -> State; -check_connections(#state{admin_state = shutting_down, - connections = Connections} = State, Pid, Reason) -> - %% Could be a crashing request handler - case lists:delete(Pid, Connections) of - [] -> % Crashing request handler => block complete - String = - lists:flatten( - io_lib:format("request handler (~p) crashed:" - "~n ~p", [Pid, Reason])), - report_error(State, String), - demonitor_blocker(State#state.blocker_ref), - {Tmr,From,Ref} = State#state.blocking_tmr, - stop_block_tmr(Tmr), - From ! {block_reply,ok,Ref}, - State#state{admin_state = blocked, connections = [], - blocker_ref = undefined}; - Connections1 -> - State#state{connections = Connections1} - end; -check_connections(#state{connections = Connections} = State, Pid, Reason) -> - case lists:delete(Pid, Connections) of - Connections -> % Not a request handler, so ignore - State; - NewConnections -> - String = - lists:flatten( - io_lib:format("request handler (~p) crashed:" - "~n ~p", [Pid, Reason])), - report_error(State, String), - State#state{connections = NewConnections} - end. +check_connections(#state{connections = Connections} = State, Pid, _Reason) -> + State#state{connections = lists:delete(Pid, Connections)}. %% ------------------------------------------------------------------------- @@ -691,11 +662,11 @@ handle_unblock(S, FromA) -> handle_unblock(S, _FromA, unblocked) -> {ok,S}; handle_unblock(S, FromA, _AdminState) -> - stop_block_tmr(S#state.blocking_tmr), case S#state.blocking_tmr of - {_Tmr,FromB,Ref} -> + {Tmr,FromB,Ref} -> %% Another process is trying to unblock %% Inform the blocker + stop_block_tmr(Tmr), FromB ! {block_reply, {error,{unblocked,FromA}},Ref}; _ -> ok diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 0f47d785ef..cb20159794 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -106,7 +106,7 @@ init([Manager, ConfigDB, AcceptTimeout]) -> case http_transport:negotiate(SocketType, Socket, TimeOut) of {error, Error} -> ?hdrd("negotiation failed", [{error, Error}]), - exit(Error); %% Can be 'normal'. + exit(shutdown); %% Can be 'normal'. ok -> ?hdrt("negotiation successfull", []), NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000, diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 6b6532266b..a45b04f275 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -20,7 +20,7 @@ -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/1]). + is_disable_chunked_send/1, cache_headers/2]). -export([map_status_code/2]). -include("httpd.hrl"). @@ -266,8 +266,8 @@ get_connection(false,"HTTP/1.1") -> get_connection(_,_) -> "". -cache_headers(#mod{config_db = Db}) -> - case httpd_util:lookup(Db, script_nocache, false) of +cache_headers(#mod{config_db = Db}, NoCacheType) -> + case httpd_util:lookup(Db, NoCacheType, false) of true -> Date = httpd_util:rfc1123_date(), [{"cache-control", "no-cache"}, diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index c854166c29..f1b73810e6 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -295,7 +295,7 @@ receive_headers(Port, Module, Function, Args, Timeout) -> end. send_headers(ModData, {StatusCode, _}, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index e36c33b282..b11df34f9e 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -440,7 +440,7 @@ receive_headers(Timeout) -> end. send_headers(ModData, StatusCode, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index dfa86906fd..2f2f6ec16e 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -215,7 +215,7 @@ INETS_FILES = inets.config $(INETS_SPECS) # inets_tftp_suite INETS_DATADIRS = inets_SUITE_data inets_sup_SUITE_data -HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data +HTTPD_DATADIRS = httpd_test_data httpd_SUITE_data httpd_basic_SUITE_data HTTPC_DATADIRS = httpc_SUITE_data httpc_proxy_SUITE_data FTP_DATADIRS = ftp_SUITE_data diff --git a/lib/inets/test/erl_make_certs.erl b/lib/inets/test/erl_make_certs.erl index 5b92e551a5..22dc951ac1 100644 --- a/lib/inets/test/erl_make_certs.erl +++ b/lib/inets/test/erl_make_certs.erl @@ -45,7 +45,7 @@ %% {dnQualifer, DnQ} %% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) %% (obs IssuerKey migth be {Key, Password} -%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% key = KeyFile|KeyBin|rsa|dsa|ec Subject PublicKey rsa, dsa or ec generates key %% %% %% (OBS: The generated keys are for testing only) @@ -91,6 +91,16 @@ gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> {Key, encode_key(Key)}. %%-------------------------------------------------------------------- +%% @doc Creates a ec key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_ec(Curve) when is_atom(Curve) -> + Key = gen_ec2(Curve), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- %% @doc Verifies cert signatures %% @spec (::binary(), ::tuple()) -> ::boolean() %% @end @@ -102,7 +112,10 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) -> public_key:pkix_verify(DerEncodedCert, #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}); #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> - public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}) + public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}); + #'ECPrivateKey'{version = _Version, privateKey = _PrivKey, + parameters = Params, publicKey = {0, PubKey}} -> + public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params}) end. %%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -112,6 +125,7 @@ get_key(Opts) -> undefined -> make_key(rsa, Opts); rsa -> make_key(rsa, Opts); dsa -> make_key(dsa, Opts); + ec -> make_key(ec, Opts); Key -> Password = proplists:get_value(password, Opts, no_passwd), decode_key(Key, Password) @@ -129,6 +143,8 @@ decode_key(#'RSAPrivateKey'{} = Key,_) -> Key; decode_key(#'DSAPrivateKey'{} = Key,_) -> Key; +decode_key(#'ECPrivateKey'{} = Key,_) -> + Key; decode_key(PemEntry = {_,_,_}, Pw) -> public_key:pem_entry_decode(PemEntry, Pw); decode_key(PemBin, Pw) -> @@ -140,7 +156,10 @@ encode_key(Key = #'RSAPrivateKey'{}) -> {'RSAPrivateKey', Der, not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', Der, not_encrypted}. + {'DSAPrivateKey', Der, not_encrypted}; +encode_key(Key = #'ECPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('ECPrivateKey', Key), + {'ECPrivateKey', Der, not_encrypted}. make_tbs(SubjectKey, Opts) -> Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), @@ -277,7 +296,14 @@ publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}}, - #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}; +publickey(#'ECPrivateKey'{version = _Version, + privateKey = _PrivKey, + parameters = Params, + publicKey = {0, PubKey}}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = #'ECPoint'{point = PubKey}}. validity(Opts) -> DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1), @@ -298,13 +324,24 @@ sign_algorithm(#'RSAPrivateKey'{}, Opts) -> end, {Type, 'NULL'}; sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> - {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}. + {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}; +sign_algorithm(#'ECPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'ecdsa-with-SHA1'; + sha512 -> ?'ecdsa-with-SHA512'; + sha384 -> ?'ecdsa-with-SHA384'; + sha256 -> ?'ecdsa-with-SHA256' + end, + {Type, 'NULL'}. make_key(rsa, _Opts) -> %% (OBS: for testing only) gen_rsa2(64); make_key(dsa, _Opts) -> - gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + gen_dsa2(128, 20); %% Bytes i.e. {1024, 160} +make_key(ec, _Opts) -> + %% (OBS: for testing only) + gen_ec2(secp256k1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% RSA key generation (OBS: for testing only) @@ -349,24 +386,37 @@ gen_dsa2(LSize, NSize) -> X0 = prime(LSize), P0 = prime((LSize div 2) +1), - %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of error -> gen_dsa2(LSize, NSize); P -> - G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. - %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. X = prime(20), %% Choose x by some random method, where 0 < x < q. - Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p. - #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + #'DSAPrivateKey'{version=0, p = P, q = Q, + g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X} end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EC key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +gen_ec2(CurveId) -> + {PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId), + + #'ECPrivateKey'{version = 1, + privateKey = binary_to_list(PrivKey), + parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)}, + publicKey = {0, PubKey}}. + %% See fips_186-3.pdf dsa_search(T, P0, Q, Iter) when Iter > 0 -> P = 2*T*Q*P0 + 1, - case is_prime(crypto:mpint(P), 50) of + case is_prime(P, 50) of true -> P; false -> dsa_search(T+1, P0, Q, Iter-1) end; @@ -377,38 +427,40 @@ dsa_search(_,_,_,_) -> %%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% prime(ByteSize) -> Rand = odd_rand(ByteSize), - crypto:erlint(prime_odd(Rand, 0)). + prime_odd(Rand, 0). prime_odd(Rand, N) -> case is_prime(Rand, 50) of true -> Rand; false -> - NotPrime = crypto:erlint(Rand), - prime_odd(crypto:mpint(NotPrime+2), N+1) + prime_odd(Rand+2, N+1) end. %% see http://en.wikipedia.org/wiki/Fermat_primality_test is_prime(_, 0) -> true; is_prime(Candidate, Test) -> - CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), - case crypto:mod_exp(CoPrime, Candidate, Candidate) of - CoPrime -> is_prime(Candidate, Test-1); - _ -> false - end. + CoPrime = odd_rand(10000, Candidate), + Result = crypto:mod_pow(CoPrime, Candidate, Candidate) , + is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test). + +is_prime(CoPrime, CoPrime, Candidate, Test) -> + is_prime(Candidate, Test-1); +is_prime(_,_,_,_) -> + false. odd_rand(Size) -> Min = 1 bsl (Size*8-1), Max = (1 bsl (Size*8))-1, - odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + odd_rand(Min, Max). odd_rand(Min,Max) -> - Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), - BitSkip = (Sz+4)*8-1, - case Rand of - Odd = <<_:BitSkip, 1:1>> -> Odd; - Even = <<_:BitSkip, 0:1>> -> - crypto:mpint(crypto:erlint(Even)+1) + Rand = crypto:rand_uniform(Min,Max), + case Rand rem 2 of + 0 -> + Rand + 1; + _ -> + Rand end. extended_gcd(A, B) -> @@ -427,3 +479,4 @@ pem_to_der(File) -> der_to_pem(File, Entries) -> PemBin = public_key:pem_encode(Entries), file:write_file(File, PemBin). + diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 04c7358715..c5920a3968 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,14 +35,15 @@ 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]). + is_absolut_uri/1, convert_netscapecookie_date/1, + check_content_length_encoding/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, chunk}, http_response, http_request, validate_request_line, {group, script}, is_absolut_uri, - convert_netscapecookie_date]. + convert_netscapecookie_date, check_content_length_encoding]. groups() -> [{script, [], [esi_parse_headers, cgi_parse_headers]}, @@ -456,6 +457,25 @@ validate_request_line(Config) when is_list(Config) -> httpd_request:validate("GET", NewForbiddenUri1, "HTTP/1.1"), ok. + +%%------------------------------------------------------------------------- +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(Config) when is_list(Config) -> + + %% Check that the content-length is preserved. + %% Sanity check. + Header1 = http_request:http_headers(#http_request_h{'content-length'="123"}), + 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. + %%------------------------------------------------------------------------- esi_parse_headers(doc) -> ["Test httpd_esi:*. All header values are received in the same" diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 8df5964193..0c35f284f7 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -83,12 +83,14 @@ real_requests()-> stream_through_fun, stream_through_mfa, streaming_error, - inet_opts + inet_opts, + invalid_headers ]. only_simulated() -> [ cookie, + cookie_profile, trace, stream_once, no_content_204, @@ -488,9 +490,37 @@ cookie(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], [_|_]}} = httpc:request(get, Request1, [], []), + [{session_cookies, [_|_]}] = httpc:which_cookies(httpc:default_profile()), + ets:delete(cookie), ok = httpc:set_options([{cookies, disabled}]). + + +%%------------------------------------------------------------------------- +cookie_profile() -> + [{doc, "Test cookies on a non default profile."}]. +cookie_profile(Config) when is_list(Config) -> + inets:start(httpc, [{profile, cookie_test}]), + ok = httpc:set_options([{cookies, enabled}], cookie_test), + + Request0 = {url(group_name(Config), "/cookie.html", Config), []}, + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request0, [], [], cookie_test), + + %% Populate table to be used by the "dummy" server + ets:new(cookie, [named_table, public, set]), + ets:insert(cookie, {cookies, true}), + + Request1 = {url(group_name(Config), "/", Config), []}, + + {ok, {{_,200,_}, [_ | _], [_|_]}} + = httpc:request(get, Request1, [], [], cookie_test), + + ets:delete(cookie), + inets:stop(httpc, cookie_test). + %%------------------------------------------------------------------------- headers_as_is(doc) -> ["Test the option headers_as_is"]; @@ -795,6 +825,10 @@ headers_dummy(Config) when is_list(Config) -> %%------------------------------------------------------------------------- +invalid_headers(Config) -> + Request = {url(group_name(Config), "/dummy.html", Config), [{"cookie", undefined}]}, + {error, _} = httpc:request(get, Request, [], []). + remote_socket_close(Config) when is_list(Config) -> URL = url(group_name(Config), "/just_close.html", Config), {error, socket_closed_remotely} = httpc:request(URL). @@ -1659,6 +1693,15 @@ receive_streamed_body(RequestId, Body, Pid) -> ct:print("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]), receive {http, {RequestId, stream, BinBodyPart}} -> + %% Make sure the httpc hasn't sent us the next 'stream' + %% without our request. + receive + {http, {RequestId, stream, _}} = Msg -> + ct:fail({unexpected_flood_of_stream, Msg}) + after + 1000 -> + ok + end, receive_streamed_body(RequestId, <<Body/binary, BinBodyPart/binary>>, Pid); diff --git a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem index f274d2021d..427447958d 100644 --- a/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem +++ b/lib/inets/test/httpc_SUITE_data/ssl_client_cert.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBANz7eFvORmJDi1XJMM2U3uHC5wmp/DXTLMw08XaEvtZ73wgVg84E -V0oyX3Kh1thRE3Hch9AyrHjgpizCj9/Ra38CAwEAAQJACzpz2SZYCTIpaEh6xFdm -I86FcsZCXHHIeu/NvRntoHQ+nfM7Np379+z6XNJWIcWh/QgG/jNJalR1BO+eyc6/ -YQIhAP3m8M0LDxJwSgHFtGAGatQqaqw9l48Kq5xdMFqvdpiHAiEA3s7lld6yCJYu -6q7fZjTH+eKUwgg0vpgJutP7Fsok60kCIHHesQBEhW3vjkFdOZgXSLH+k/jLZr1w -O6bU5GrHZpjhAiEAyTvGYcjDtTunXjDY9l+fadK6FlEBCk8ZIpNIiTnDhHkCIQDr -QxxLLuNHRj8iWNbuVVZ99SJy8zC33pMgPFaFKaZesQ== +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD -VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx -FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy -aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcxNDI1WhcNOTgxMjEwMDcxNDI1WjCB -gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp -Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG -CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB -AQUAA0sAMEgCQQDc+3hbzkZiQ4tVyTDNlN7hwucJqfw10yzMNPF2hL7We98IFYPO -BFdKMl9yodbYURNx3IfQMqx44KYswo/f0Wt/AgMBAAEwDQYJKoZIhvcNAQEEBQAD -QQC2++hLIaQJ4ChCjFE9UCfXO9cZ3Vq/FT9VjE+G4MRBDo4LQ5mBKNXcPF6EFZmi -7XrlvopXkVPlRguTi2SLRPkY +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 -----END CERTIFICATE----- diff --git a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem index f01b6c992b..4aac86db49 100644 --- a/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem +++ b/lib/inets/test/httpc_SUITE_data/ssl_server_cert.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOQIBAAJBAMe2WhP6s+JeKOwWPEjI9susfN4Vjn2dd1X4QUlOETcWVLoF916m -M4JU+ms7+ciMR8GRNCsIeqZGY8/GSqm74ccCAwEAAQJAF08YKlbLYfM9cXiS5qfV -7iWemUkIzW5wfC8yZ3zeE4Cp6R9ViUfs/dadQ/23Cw0Bpo2t8UdTUdCa4KpmqOem -cQIhAOnxTWZ5eo6h6PXDp7L5FZUACg8+wT3qf5f2is2mbSZPAiEA2orUY8JZDTSk -Rm7q9WxLiLNtORsXdTCmnCWhqBOYpwkCIErdowRxScxNekz0IT3AQqzdR1rbnWHg -IpcSGhd39CQ3AiA1XvQxjLP8wp9fyBS/bPwhXVhOOuyGpSP7PEF3b5m3KQIgGQWc -/a5wuWx3pc3mLx0ILwNoJr2ubFEuW1PJPsPJPv0= +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIB7jCCAZgCAQAwDQYJKoZIhvcNAQEEBQAwgYExCzAJBgNVBAYTAlNFMRIwEAYD -VQQHEwlTdG9ja2hvbG0xETAPBgNVBAoTCEVyaWNzc29uMQwwCgYDVQQLEwNFVFgx -FjAUBgNVBAMTDUhlbGVuIEFpcml5YW4xJTAjBgkqhkiG9w0BCQEWFmhlbGVuQGVy -aXguZXJpY3Nzb24uc2UwHhcNOTcwNzI4MDcyMTAwWhcNOTgxMjEwMDcyMTAwWjCB -gTELMAkGA1UEBhMCU0UxEjAQBgNVBAcTCVN0b2NraG9sbTERMA8GA1UEChMIRXJp -Y3Nzb24xDDAKBgNVBAsTA0VUWDEWMBQGA1UEAxMNSGVsZW4gQWlyaXlhbjElMCMG -CSqGSIb3DQEJARYWaGVsZW5AZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEB -AQUAA0sAMEgCQQDHtloT+rPiXijsFjxIyPbLrHzeFY59nXdV+EFJThE3FlS6Bfde -pjOCVPprO/nIjEfBkTQrCHqmRmPPxkqpu+HHAgMBAAEwDQYJKoZIhvcNAQEEBQAD -QQCnU1TkxmfbLdUwjdECb5x9QHCevAR7AmTms4Csn2oOEyPX+bgF2d94xhrV1sxO -Rs0yigk1PtN17Ci0Dey0LYkR +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M -----END CERTIFICATE----- diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl index 84db39e76b..ddd23d0c65 100644 --- a/lib/inets/test/httpc_proxy_SUITE.erl +++ b/lib/inets/test/httpc_proxy_SUITE.erl @@ -69,6 +69,7 @@ local_proxy_cases() -> http_post, http_put, http_delete, + http_delete_body, http_headers, http_proxy_auth, http_doesnotexist, @@ -262,6 +263,22 @@ http_delete(Config) when is_list(Config) -> %%-------------------------------------------------------------------- +http_delete_body(doc) -> + ["Perform a DELETE request with a content body. The server will not allow it " + "but we only test sending the request."]; +http_delete_body(Config) when is_list(Config) -> + Method = delete, + URL = url("/delete.html", Config), + Content = "foo=bar", + Request = {URL,[],"application/x-www-form-urlencoded",Content}, + HttpOpts = [], + Opts = [], + {ok,{{_,405,_},[_|_],[_|_]}} = + httpc:request(Method, Request, HttpOpts, Opts), + ok. + +%%-------------------------------------------------------------------- + http_headers(doc) -> ["Use as many request headers as possible"]; http_headers(Config) when is_list(Config) -> diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 1efa78a63e..5dca76b76b 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1919,7 +1919,7 @@ ticket_5865(Config) -> " HTTP/1.1\r\nHost:" ++Host++"\r\n\r\n", [{statuscode, 200}, - {no_last_modified, + {no_header, "last-modified"}]), ok; {error, Reason} -> diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem index 8221139eb4..427447958d 100644 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_client.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBPAIBAAJBAL6Ym/bgUvhhnPkw08sggGg8Tnp759ThGMEjkmDzhuJ3w3PfnF65 -mgHcgunku4G6LxAQfEUougJWf9Phmjj3oRUCAwEAAQJBAKMjvVvzZxFzfAlP4flc -OI0AEayFokp04dtvtzuFN09f+aBo2dP18xHmKLCZvxrBOaRAROoQYscALiIVpN07 -GAECIQDfi+sSfAFaDlT3vzpL3xE5UEH6IzY8jWpaZfM1QaToJQIhANpEF50H4wGO -8Sbh7dUutNd+s+NYUjsMySW2DjLKMsoxAiEAzzb2ftrdsempD0F+O0gZwiPIFKLB -Kp33YLYyHEKuJtUCIDGi+pvDh2R7VWw6RRQOIyI+tjolg83aAoSI+oGiahqBAiEA -xzmNNajwoaokvWvlaz0na8rhxu45grOvDrflBT9XvSQ= +MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n +0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr +3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB +AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB +I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna +QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f +eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws +/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI +ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV +LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj +40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6 +UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80 +t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD -VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w -DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG -9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzNDM2WhcN -MDMwMjIyMTUzNDM2WjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s -bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ -MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA -ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC+mJv24FL4 -YZz5MNPLIIBoPE56e+fU4RjBI5Jg84bid8Nz35xeuZoB3ILp5LuBui8QEHxFKLoC -Vn/T4Zo496EVAgMBAAEwDQYJKoZIhvcNAQEEBQADQQBYxQVfTydyZCE0UXvZd7Ei -josNsAaWJk9fFIJaG9uyXCEfg2dVgoT2eBk3D9DI+7OB+78isM5CVlFbL7hilvP8 +MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV +wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53 +h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A +PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD +4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ +S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5 -----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem index fe739c15f7..4aac86db49 100644 --- a/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem +++ b/lib/inets/test/httpd_SUITE_data/server_root/ssl/ssl_server.pem @@ -1,22 +1,31 @@ -----BEGIN RSA PRIVATE KEY----- -MIIBOwIBAAJBAL9Bozj3BIjL5Cy8b3rjMT2kPZRychX4wz9bHoIIiKnKo1xXHYjw -g3N9zWM1f1ZzMADwVry1uAInA8q09+7hL20CAwEAAQJACwu2ao7RozjrV64WXimK -6X131P/7GMvCMwGHNIlbozqoOqmZcYrbKaF61l+XuwA2QvTo3ywW1Ivxcyr6TeAr -PQIhAOX+WXT6yiqqwjt08kjBCJyMgfZtdAO6pc/6pKjNWiZfAiEA1OH1iPW/OQe5 -tlQXpiRVdLyneNsPygPRJc4Bdwu3hbMCIQDbI5pA56QxOzqOREOGJsb5wrciAfAE -jZbnr72sSN2YqQIgAWFpvzagw9Tp/mWzNY+cwkIK7/yzsIKv04fveH8p9IMCIQCr -td4IiukeUwXmPSvYM4uCE/+J89wEL9qU8Mlc3gDLXA== +MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6 +7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt +NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB +AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm +FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo +T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd +VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb +Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7 +5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX +BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm +zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa +GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z +h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICDDCCAbYCAQAwDQYJKoZIhvcNAQEEBQAwgZAxCzAJBgNVBAYTAlNFMRIwEAYD -VQQIEwlTdG9ja2hvbG0xDzANBgNVBAcTBkFsdnNqbzEMMAoGA1UEChMDRVRYMQ4w -DAYDVQQLEwVETi9TUDEXMBUGA1UEAxMOSm9ha2ltIEdyZWJlbm8xJTAjBgkqhkiG -9w0BCQEWFmpvY2tlQGVyaXguZXJpY3Nzb24uc2UwHhcNOTcwNzE1MTUzMzQxWhcN -MDMwMjIyMTUzMzQxWjCBkDELMAkGA1UEBhMCU0UxEjAQBgNVBAgTCVN0b2NraG9s -bTEPMA0GA1UEBxMGQWx2c2pvMQwwCgYDVQQKEwNFVFgxDjAMBgNVBAsTBUROL1NQ -MRcwFQYDVQQDEw5Kb2FraW0gR3JlYmVubzElMCMGCSqGSIb3DQEJARYWam9ja2VA -ZXJpeC5lcmljc3Nvbi5zZTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC/QaM49wSI -y+QsvG964zE9pD2UcnIV+MM/Wx6CCIipyqNcVx2I8INzfc1jNX9WczAA8Fa8tbgC -JwPKtPfu4S9tAgMBAAEwDQYJKoZIhvcNAQEEBQADQQAmXDY1CyJjzvQZX442kkHG -ic9QFY1UuVfzokzNMwlHYl1Qx9zaodx0cJCrcH5GF9O9LJbhhV77LzoxT1Q5wZp5 +MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT +BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE +BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD +VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw +MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl +cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD +VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9 +Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ +4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID +AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk +QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L +psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg +/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M -----END CERTIFICATE----- diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 523cf9d38c..b1fe373cff 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -19,6 +19,7 @@ %% -module(httpd_basic_SUITE). +-include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). -include("inets_test_lib.hrl"). @@ -33,7 +34,9 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ uri_too_long_414, - header_too_long_413, + header_too_long_413, + erl_script_nocache_opt, + script_nocache, escaped_url_in_error_body, slowdose ]. @@ -62,6 +65,7 @@ init_per_suite(Config) -> "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), Dummy = "<HTML> @@ -74,6 +78,18 @@ DUMMY </HTML>", DummyFile = filename:join([PrivDir,"dummy.html"]), + CgiDir = filename:join(PrivDir, "cgi-bin"), + ok = file:make_dir(CgiDir), + Cgi = case test_server:os_type() of + {win32, _} -> + "printenv.bat"; + _ -> + "printenv.sh" + end, + inets_test_lib:copy_file(Cgi, DataDir, CgiDir), + AbsCgi = filename:join([CgiDir, Cgi]), + {ok, FileInfo} = file:read_file_info(AbsCgi), + ok = file:write_file_info(AbsCgi, FileInfo#file_info{mode = 8#00755}), {ok, Fd} = file:open(DummyFile, [write]), ok = file:write(Fd, Dummy), ok = file:close(Fd), @@ -84,7 +100,7 @@ DUMMY {document_root, PrivDir}, {bind_address, "localhost"}], - [{httpd_conf, HttpdConf} | Config]. + [{httpd_conf, HttpdConf}, {cgi_dir, CgiDir}, {cgi_script, Cgi} | Config]. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -178,6 +194,74 @@ header_too_long_413(Config) when is_list(Config) -> {version, "HTTP/1.1"}]), inets:stop(httpd, Pid). +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + +erl_script_nocache_opt(doc) -> + ["Test that too long headers's get 413 HTTP code"]; +erl_script_nocache_opt(suite) -> + []; +erl_script_nocache_opt(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + _Address = proplists:get_value(bind_address, Info), + URL1 = ?URL_START ++ integer_to_list(Port), + case httpc:request(get, {URL1 ++ "/dummy.html", []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]) of + {ok, {200, _}} -> + ok + end, + inets:stop(httpd, Pid). + +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + +script_nocache(doc) -> + ["Test nocache option for mod_cgi and mod_esi"]; +script_nocache(suite) -> + []; +script_nocache(Config) when is_list(Config) -> + Normal = {no_header, "cache-control"}, + NoCache = {header, "cache-control", "no-cache"}, + verify_script_nocache(Config, false, false, Normal, Normal), + verify_script_nocache(Config, true, false, NoCache, Normal), + verify_script_nocache(Config, false, true, Normal, NoCache), + verify_script_nocache(Config, true, true, NoCache, NoCache), + ok. + +verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) -> + HttpdConf = ?config(httpd_conf, Config), + CgiScript = ?config(cgi_script, Config), + CgiDir = ?config(cgi_dir, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, + {script_alias, + {"/cgi-bin/", CgiDir ++ "/"}}, + {script_nocache, CgiNoCache}, + {erl_script_alias, + {"/cgi-bin/erl", [httpd_example,io]}}, + {erl_script_nocache, EsiNoCache} + | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + Address = proplists:get_value(bind_address, Info), + ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(), + "GET /cgi-bin/" ++ CgiScript ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + CgiOption, + {version, "HTTP/1.0"}]), + ok = httpd_test_lib:verify_request(ip_comm, Address, Port, node(), + "GET /cgi-bin/erl/httpd_example:get " + "HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + EsiOption, + {version, "HTTP/1.0"}]), + inets:stop(httpd, Pid). + %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.bat b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat new file mode 120000 index 0000000000..1bc8e52059 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.bat @@ -0,0 +1 @@ +../httpd_SUITE_data/server_root/cgi-bin/printenv.bat
\ No newline at end of file diff --git a/lib/inets/test/httpd_basic_SUITE_data/printenv.sh b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh new file mode 120000 index 0000000000..0136a3fa23 --- /dev/null +++ b/lib/inets/test/httpd_basic_SUITE_data/printenv.sh @@ -0,0 +1 @@ +../httpd_SUITE_data/server_root/cgi-bin/printenv.sh
\ No newline at end of file diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index df4ed6b179..7d3326fb65 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -842,6 +842,14 @@ cgi(Type, Port, Host, Node) -> {version, "HTTP/1.0"}]), %% tsp("cgi -> done"), + + %% Check "ScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/" ++ Script ++ + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]), ok. @@ -899,6 +907,13 @@ esi(Type, Port, Host, Node) -> " HTTP/1.0\r\n\r\n", [{statuscode, 302}, {version, "HTTP/1.0"}]), + %% Check "ErlScriptNoCache" directive (default: false) + ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + "GET /cgi-bin/erl/httpd_example:get" + " HTTP/1.0\r\n\r\n", + [{statuscode, 200}, + {no_header, "cache-control"}, + {version, "HTTP/1.0"}]), ok. diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 13584c50f6..3e82324a30 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -361,7 +361,7 @@ do_validate(Header, [{header, HeaderField, Value}|Rest],N,P) -> tsf({wrong_header_field_value, LowerHeaderField, Header}) end, do_validate(Header, Rest, N, P); -do_validate(Header,[{no_last_modified, HeaderField}|Rest],N,P) -> +do_validate(Header,[{no_header, HeaderField}|Rest],N,P) -> case lists:keysearch(HeaderField,1,Header) of {value,_} -> tsf({wrong_header_field_value, HeaderField, Header}); diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index f5abaf9764..36463d9388 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.9.4 +INETS_VSN = 5.9.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" |