From f9ec3cbca0f05fd9640bbd5cd3e21942c4512d3d Mon Sep 17 00:00:00 2001 From: Filipe David Manana Date: Sun, 26 Sep 2010 11:58:45 +0100 Subject: httpc: allow streaming of PUT and POST request bodies This is a must when uploading large bodies that are to large to store in a string or binary. Besides a string or binary, a body can now be a function and an accumulator. Example: -module(httpc_post_stream_test). -compile(export_all). -define(LEN, 1024 * 1024). prepare_data() -> {ok, Fd} = file:open("test_data.dat", [binary, write]), ok = file:write(Fd, lists:duplicate(?LEN, "1")), ok = file:close(Fd). test() -> inets:start(), ok = prepare_data(), {ok, Fd1} = file:open("test_data.dat", [binary, read]), BodyFun = fun(Fd) -> case file:read(Fd, 512) of eof -> eof; {ok, Data} -> {ok, Data, Fd} end end, {ok, {{_,200,_}, _, _}} = httpc:request(post, {"http://localhost:8888", [{"content-length", integer_to_list(?LEN)}], "text/plain", {BodyFun, Fd1}}, [], []), ok = file:close(Fd1). --- lib/inets/doc/src/httpc.xml | 4 +- lib/inets/src/http_client/httpc.erl | 4 +- lib/inets/src/http_client/httpc_request.erl | 64 ++++++++++++++++++++++------- lib/inets/test/httpc_SUITE.erl | 40 ++++++++++++++++++ 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 9c8df28fec..df333074cd 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -89,7 +89,9 @@ headers() = [header()] header() = {field(), value()} field() = string() value() = string() -body() = string() | binary() +body() = string() | binary() | {fun(acc()) -> send_fun_result(), acc()} +send_fun_result() = eof | {ok, iolist(), acc()} +acc() = term() filename() = string() ]]> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 851364001c..b82a9db4c9 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,9 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% SendFunResult = eof | {ok, iolist(), NewSendAcc} +%% SendAcc = NewSendAcc = term() %% %% Description: Sends a HTTP-request. The function can be both %% syncronus and asynchronous in the later case the function will diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index d4df97ad40..5386d1eb4a 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -101,15 +101,41 @@ send(SendAddr, Socket, SocketType, end, Version = HttpOptions#http_options.version, - Message = [method(Method), " ", Uri, " ", - version(Version), ?CRLF, - headers(FinalHeaders, Version), ?CRLF, Body], + do_send_body(SocketType, Socket, Method, Uri, Version, FinalHeaders, Body). + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, {DataFun, Acc}) + when is_function(DataFun, 1) -> + case do_send_body(SocketType, Socket, Method, Uri, Version, Headers, []) of + ok -> + data_fun_loop(SocketType, Socket, DataFun, Acc); + Error -> + Error + end; + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> + Message = [method(Method), " ", Uri, " ", + version(Version), ?CRLF, + headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - http_transport:send(SocketType, Socket, lists:append(Message)). +data_fun_loop(SocketType, Socket, DataFun, Acc) -> + case DataFun(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{message, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + data_fun_loop(SocketType, Socket, DataFun, NewAcc); + Error -> + Error + end + end. + %%------------------------------------------------------------------------- %% is_idempotent(Method) -> @@ -161,7 +187,6 @@ is_client_closing(Headers) -> %%%======================================================================== post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) when (Method =:= post) orelse (Method =:= put) -> - ContentLength = body_length(Body), NewBody = case Headers#http_request_h.expect of "100-continue" -> ""; @@ -170,14 +195,22 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) end, NewHeaders = case HeadersAsIs of - [] -> - Headers#http_request_h{'content-type' = - ContentType, - 'content-length' = - ContentLength}; - _ -> - HeadersAsIs - end, + [] -> + Headers#http_request_h{ + 'content-type' = ContentType, + 'content-length' = case body_length(Body) of + undefined -> + % on upload streaming the caller must give a + % value to the Content-Length header + % (or use chunked Transfer-Encoding) + Headers#http_request_h.'content-length'; + Len when is_list(Len) -> + Len + end + }; + _ -> + HeadersAsIs + end, {NewHeaders, NewBody}; @@ -190,7 +223,10 @@ body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); body_length(Body) when is_list(Body) -> - integer_to_list(length(Body)). + integer_to_list(length(Body)); + +body_length({DataFun, _Acc}) when is_function(DataFun, 1) -> + undefined. method(Method) -> http_util:to_upper(atom_to_list(Method)). diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 902e440c80..6947f75b3d 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -77,6 +77,7 @@ all(suite) -> http_head, http_get, http_post, + http_post_streaming, http_dummy_pipe, http_inets_pipe, http_trace, @@ -423,6 +424,45 @@ http_post(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. +%%------------------------------------------------------------------------- +http_post_streaming(doc) -> + ["Test streaming http post request against local server. We" + " only care about the client side of the the post. The server" + " script will not actually use the post data."]; +http_post_streaming(suite) -> + []; +http_post_streaming(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = case test_server:os_type() of + {win32, _} -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo.exe"; + _ -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo" + end, + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + eof; + (LenLeft) -> + {ok, lists:duplicate(10, "1"), LenLeft - 10} + end, + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, {"content-length", "100"}], + "text/plain", {BodyFun, 100}}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, {"content-length", "10"}], + "text/plain", {BodyFun, 10}}, [], []); + _ -> + {skip, "Failed to start local http-server"} + end. + %%------------------------------------------------------------------------- http_emulate_lower_versions(doc) -> ["Perform request as 0.9 and 1.0 clients."]; -- cgit v1.2.3 From 6951ed1075b8c36d5b6f51e5e5df7bd14602c1d8 Mon Sep 17 00:00:00 2001 From: Filipe David Manana Date: Tue, 5 Oct 2010 00:26:33 +0100 Subject: httpc: add option to do automatic chunked transfer-encoding This is specially useful when a client doesn't know in advance the length of the payload (so that it can't set the Content-Length header). Example: -module(httpc_post_stream_test). -compile(export_all). prepare_data() -> crypto:start(), {ok, Fd} = file:open("test_data.dat", [binary, write]), ok = file:write(Fd, lists:duplicate(crypto:rand_uniform(8182, 32768), "1")), ok = file:close(Fd). test() -> inets:start(), ok = prepare_data(), {ok, Fd1} = file:open("test_data.dat", [binary, read]), BodyFun = fun(Fd) -> case file:read(Fd, 512) of eof -> eof; {ok, Data} -> {ok, Data, Fd} end end, %% header 'Transfer-Encoding: chunked' is added by httpc {ok, {{_,200,_}, _, _}} = httpc:request(post, {"http://localhost:8888", [], "text/plain", {chunkify, BodyFun, Fd1}}, [], []), ok = file:close(Fd1). --- lib/inets/doc/src/httpc.xml | 4 +++- lib/inets/src/http_client/httpc.erl | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index df333074cd..8b04b4c7f3 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -89,7 +89,9 @@ headers() = [header()] header() = {field(), value()} field() = string() value() = string() -body() = string() | binary() | {fun(acc()) -> send_fun_result(), acc()} +body() = string() | binary() | + {fun(acc()) -> send_fun_result(), acc()} | + {chunkify, fun(acc()) -> send_fun_result(), acc()} send_fun_result() = eof | {ok, iolist(), acc()} acc() = term() filename() = string() diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index b82a9db4c9..8cf82df809 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,8 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} | +%% {chunkify, fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code %% SendFunResult = eof | {ok, iolist(), NewSendAcc} %% SendAcc = NewSendAcc = term() %% @@ -428,11 +429,20 @@ service_info(Pid) -> handle_request(Method, Url, {Scheme, UserInfo, Host, Port, Path, Query}, - Headers, ContentType, Body, + Headers0, ContentType, Body0, HTTPOptions0, Options0, Profile) -> Started = http_util:timestamp(), - NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers], + NewHeaders0 = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers0], + + {NewHeaders, Body} = case Body0 of + {chunkify, BodyFun, Acc} -> + NewHeaders1 = lists:keystore("transfer-encoding", 1, + NewHeaders0, {"transfer-encoding", "chunked"}), + {NewHeaders1, {chunkify_fun(BodyFun), Acc}}; + _ -> + {NewHeaders0, Body0} + end, try begin @@ -456,7 +466,7 @@ handle_request(Method, Url, abs_uri = Url, userinfo = UserInfo, stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, started = Started}, case httpc_manager:request(Request, profile_name(Profile)) of @@ -473,6 +483,22 @@ handle_request(Method, Url, Error end. +chunkify_fun(BodyFun) -> + fun(eof_body_fun) -> + eof; + (Acc) -> + case BodyFun(Acc) of + eof -> + {ok, <<"0\r\n\r\n">>, eof_body_fun}; + {ok, Data, NewAcc} -> + Bin = iolist_to_binary(Data), + Chunk = [hex_size(Bin), "\r\n", Bin, "\r\n"], + {ok, iolist_to_binary(Chunk), NewAcc} + end + end. + +hex_size(Bin) -> + hd(io_lib:format("~.16B", [size(Bin)])). handle_answer(RequestId, false, _) -> {ok, RequestId}; -- cgit v1.2.3 From 4827d5db5fb2ca10772f70fbb6ad7f7f99285d96 Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Mon, 21 Feb 2011 13:53:56 -0500 Subject: inets: prevent XSS in error pages Prevent user controlled input from being interpreted as HTML in error pages by encoding the reserved HTML characters. The reserved character set should be safe for displaying data within the body of HTML pages as outlined here: http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet Previously, weird URLs were URI encoded in the error page. This worked quite well but the URL would be displayed in the HTML in percent encoded format. There was also a check for URIs that were already escaped (by the browser) that would fail if the browser sent an URI containing a "%", e.g.: w3m "http://localhost:8080/foo?%" Also encode the HTTP method and version, since it's possible they may be manipulated: FOO /index.html HTTP/1.0 GET /index.html foo/1.0 Encode the static messages to prevent characters from being interpreted as HTML such as "heavy load (>~w processes)". --- lib/inets/src/http_lib/http_util.erl | 18 ++++++++++++++- lib/inets/src/http_server/httpd_util.erl | 38 ++++++++++++++++---------------- lib/inets/test/httpd_basic_SUITE.erl | 11 ++++----- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index 4f1147176c..5e6b69ac5e 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -25,7 +25,8 @@ hexlist_to_integer/1, integer_to_hexlist/1, convert_month/1, is_hostname/1, - timestamp/0, timeout/2 + timestamp/0, timeout/2, + html_encode/1 ]). @@ -187,6 +188,13 @@ timeout(Timeout, Started) -> 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/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 789f12652b..c1aff65d5e 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -181,7 +181,7 @@ message(304, _URL,_) -> 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. "++ maybe_encode(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 @@ -190,48 +190,48 @@ credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required."; message(403,RequestURI,_) -> - "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server."; + "You don't have permission to access "++ http_util:html_encode(RequestURI) ++" on this server."; message(404,RequestURI,_) -> - "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server."; + "The requested URL " ++ http_util:html_encode(RequestURI) ++ " was not found on this server."; message(408, Timeout, _) -> Timeout; message(412,none,_) -> - "The requested preconditions where false"; + "The requested preconditions were false"; message(413, Reason,_) -> - "Entity: " ++ 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"), "The server encountered an internal error or " "misconfiguration and was unable to complete " "your request.

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 "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."; + http_util:html_encode(atom_to_list(Method))++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported."; is_list(Method) -> - Method++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported." + http_util:html_encode(Method)++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported." end; message(503, String, _ConfigDB) -> - "This service in unavailable due to: "++String. + "This service in unavailable due to: "++ http_util:html_encode(String). maybe_encode(URI) -> - case lists:member($%, URI) of - true -> - URI; - false -> - http_uri:encode(URI) - end. + Decoded = try http_uri:decode(URI) of + N -> N + catch + error:_ -> URI + end, + http_uri:encode(Decoded). %%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}} diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index dcea200a1a..1cb07c2f5b 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -151,12 +151,13 @@ escaped_url_in_error_body(Config) when is_list(Config) -> URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), {ok, {404, Body}} = httpc:request(get, {URL, []}, - [{url_encode, true}], - [{version, "HTTP/1.0"}, {full_result, false}]), + [{url_encode, true}, {version, "HTTP/1.0"}], + [{full_result, false}]), EscapedPath = find_URL_path(string:tokens(Body, " ")), - {ok, {404, Body1}} = httpc:request(get, {URL, []}, [], - [{version, "HTTP/1.0"}, {full_result, false}]), - EscapedPath = find_URL_path(string:tokens(Body1, " ")), + {ok, {404, Body1}} = httpc:request(get, {URL, []}, + [{version, "HTTP/1.0"}], [{full_result, false}]), + HTMLEncodedPath = http_util:html_encode(Path), + HTMLEncodedPath = find_URL_path(string:tokens(Body1, " ")), inets:stop(httpd, Pid). find_URL_path([]) -> -- cgit v1.2.3 From 73f261d2f44a58fda92e3d6e035051c11c3e4521 Mon Sep 17 00:00:00 2001 From: Bernard Duggan Date: Wed, 23 Feb 2011 15:55:37 +1100 Subject: Modify mod_esi:deliver/2 to accept binary data This change allows for more efficient delivery of large amounts of data through the mod_esi interface when the handling process has that data in binary format. It avoids the need to convert to list and the extra memory involved in passing that list between processes. --- lib/inets/doc/src/mod_esi.xml | 6 ++++-- lib/inets/src/http_server/mod_esi.erl | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index 3c473d3f94..e063088eb4 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -41,7 +41,7 @@ Sends Data back to client. SessionID = term() - Data = string() | io_list() + Data = string() | io_list() | binary() Reason = term() @@ -54,7 +54,9 @@

Note that if any HTTP-header fields should be added by the script they must be in the first call to deliver/2 and the - data in the call must be a string. Do not + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of SessionID, the SessionID must be the value given as input to the esi call back function that you implemented.

diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..b85c479693 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -452,6 +452,10 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) -> ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]), httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend), receive + {esi_data, Data} when is_binary(Data) -> + ?hdrt("handle_body - received binary data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), + IsDisableChunkedSend); {esi_data, Data} -> ?hdrt("handle_body - received data (esi)", []), handle_body(Pid, ModData, Data, Timeout, Size + length(Data), -- cgit v1.2.3 From db29f9ede14ff5b8d747230fcad8ffa1b157f1e1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 7 Mar 2011 14:52:30 +0100 Subject: Adding missing "send loop" for raw sending. Also fixed some of the documentation (types). --- lib/inets/doc/src/httpc.xml | 52 ++++--- lib/inets/doc/src/notes.xml | 85 +++++++---- lib/inets/src/http_client/httpc.erl | 85 +++++++---- lib/inets/src/http_client/httpc_handler.erl | 18 +++ lib/inets/src/http_client/httpc_request.erl | 52 ++++--- lib/inets/test/httpc_SUITE.erl | 229 ++++++++++------------------ 6 files changed, 269 insertions(+), 252 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 6dcf2d6d17..12f4fa535e 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -76,25 +76,29 @@ socket_opt() = See the Options used by gen_tcp(3) and

For more information about HTTP see rfc 2616

send_fun_result(), acc()} | - {chunkify, fun(acc()) -> send_fun_result(), acc()} -send_fun_result() = eof | {ok, iolist(), acc()} -acc() = term() -filename() = string() +method() = head | get | put | post | trace | options | delete +request() = {url(), headers()} | + {url(), headers(), content_type(), body()} +url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" +status_line() = {http_version(), status_code(), reason_phrase()} +http_version() = string() ex: "HTTP/1.1" +status_code() = integer() +reason_phrase() = string() +content_type() = string() +headers() = [header()] +header() = {field(), value()} +field() = string() +value() = string() +body() = string() | + binary() | + {fun(accumulator()) -> body_processing_result(), + accumulator()} | + {chunkify, + fun(accumulator()) -> body_processing_result(), + accumulator()} +body_processing_result() = eof | {ok, iolist(), accumulator()} +accumulator() = term() +filename() = string() ]]> @@ -146,8 +150,9 @@ ssl_options() = {verify, code()} | Sends a get HTTP-request Url = url() - Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() + Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() + Body = string() | binary() Profile = profile() Reason = term() @@ -195,8 +200,9 @@ ssl_options() = {verify, code()} | Function = atom() Args = list() body_format() = string | binary - Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() + Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() + Body = string() | binary() Profile = profile() Reason = {connect_failed, term()} | {send_failed, term()} | term() diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 11b0af4310..8c0d683a90 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -32,50 +32,80 @@ notes.xml -
Inets 5.5.1 +
Inets 5.6 -
Fixed Bugs and Malfunctions +
Improvements and New Features + -

Fix format_man_pages so it handles all man sections - and remove warnings/errors in various man pages.

-

- Own Id: OTP-8600

+

[httpc] Add support for upload body streaming (PUT and POST).

+

For more info, + see the definition of the Body argument of the + request/4,5 + function.

+

Filipe David Manana

+

Own Id: OTP-9094

+
+
+ +
Fixed Bugs and Malfunctions +

-

+ +
+
+ + +
Inets 5.5.1 +
Improvements and New Features -

- Miscellaneous inet6 related problems.

-

- Own Id: OTP-8927

+

Miscellaneous inet6 related problems.

+

Own Id: OTP-8927

-

- 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.

-

- Own Id: OTP-8940 Aux Id: seq11735

+

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.

+

Own Id: OTP-8940, Aux Id: seq11735

-
+
Fixed Bugs and Malfunctions + + +

Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages.

+

Own Id: OTP-8600

+
+ +

[httpc] Pipelined and queued requests not processed when + connection closed remotelly.

+

Own Id: OTP-8906

+
+
+
+ +
+ -
Inets 5.5 +
Inets 5.5
Fixed Bugs and Malfunctions @@ -120,9 +150,10 @@
-
+
+ -
Inets 5.4 +
Inets 5.4
Improvements and New Features + + + +

[ftp] Added (type) spec for all exported functions.

+

Own Id: OTP-9114 Aux Id: seq11799

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ + +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 5ad74851c8..fe6cb0c191 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.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 @@ -92,6 +92,12 @@ }). +-type shortage_reason() :: 'etnospc' | 'epnospc'. +-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'. +-type common_reason() :: 'econn' | 'eclosed' | term(). +-type file_write_error_reason() :: term(). % See file:write for more info + + %%%========================================================================= %%% API - CLIENT FUNCTIONS %%%========================================================================= @@ -106,6 +112,9 @@ %% Description: Start an ftp client and connect to a host. %%-------------------------------------------------------------------------- +-spec open(Host :: string() | inet:ip_address()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% open({option_list, Options}) when is_list(Options) -> try @@ -126,6 +135,9 @@ open({option_list, Options}) when is_list(Options) -> open(Host) -> open(Host, []). +-spec open(Host :: string() | inet:ip_address(), Opts :: list()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% open(Host, Port) when is_integer(Port) -> open(Host, [{port, Port}]); @@ -161,12 +173,24 @@ open(Host, Opts) when is_list(Opts) -> %% %% Description: Login with or without a supplied account name. %%-------------------------------------------------------------------------- +-spec user(Pid :: pid(), + User :: string(), + Pass :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass) -> call(Pid, {user, User, Pass}, atom). +-spec user(Pid :: pid(), + User :: string(), + Pass :: string(), + Acc :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass, Acc) -> call(Pid, {user, User, Pass, Acc}, atom). + %%-------------------------------------------------------------------------- %% account(Pid, Acc) -> ok | {error, eacct} %% Pid = pid() @@ -174,9 +198,14 @@ user(Pid, User, Pass, Acc) -> %% %% Description: Set a user Account. %%-------------------------------------------------------------------------- + +-spec account(Pid :: pid(), Acc :: string()) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + account(Pid, Acc) -> call(Pid, {account, Acc}, atom). + %%-------------------------------------------------------------------------- %% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn} %% Pid = pid() @@ -184,19 +213,30 @@ account(Pid, Acc) -> %% %% Description: Get the current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec pwd(Pid :: pid()) -> + {'ok', Dir :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + pwd(Pid) -> call(Pid, pwd, ctrl). + %%-------------------------------------------------------------------------- -%% lpwd(Pid) -> {ok, Dir} | {error, elogin} +%% lpwd(Pid) -> {ok, Dir} %% Pid = pid() %% Dir = string() %% %% Description: Get the current working directory at local server. %%-------------------------------------------------------------------------- + +-spec lpwd(Pid :: pid()) -> + {'ok', Dir :: string()}. + lpwd(Pid) -> call(Pid, lpwd, string). + %%-------------------------------------------------------------------------- %% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid() @@ -204,9 +244,14 @@ lpwd(Pid) -> %% %% Description: Change current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec cd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + cd(Pid, Dir) -> call(Pid, {cd, Dir}, atom). + %%-------------------------------------------------------------------------- %% lcd(Pid, Dir) -> ok | {error, epath} %% Pid = pid() @@ -214,9 +259,14 @@ cd(Pid, Dir) -> %% %% Description: Change current working directory for the local client. %%-------------------------------------------------------------------------- + +-spec lcd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason()}. + lcd(Pid, Dir) -> call(Pid, {lcd, Dir}, string). + %%-------------------------------------------------------------------------- %% ls(Pid) -> Result %% ls(Pid, ) -> Result @@ -229,11 +279,22 @@ lcd(Pid, Dir) -> %% %% Description: Returns a list of files in long format. %%-------------------------------------------------------------------------- + +-spec ls(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid) -> ls(Pid, ""). + +-spec ls(Pid :: pid(), Dir :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid, Dir) -> call(Pid, {dir, long, Dir}, string). + %%-------------------------------------------------------------------------- %% nlist(Pid) -> Result %% nlist(Pid, Pathname) -> Result @@ -246,21 +307,37 @@ ls(Pid, Dir) -> %% %% Description: Returns a list of files in short format %%-------------------------------------------------------------------------- + +-spec nlist(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid) -> nlist(Pid, ""). + +-spec nlist(Pid :: pid(), Pathname :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid, Dir) -> call(Pid, {dir, short, Dir}, string). + %%-------------------------------------------------------------------------- -%% rename(Pid, CurrFile, NewFile) -> ok | {error, epath} | {error, elogin} -%% | {error, econn} +%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin} +%% | {error, econn} %% Pid = pid() %% CurrFile = NewFile = string() %% %% Description: Rename a file at remote server. %%-------------------------------------------------------------------------- -rename(Pid, CurrFile, NewFile) -> - call(Pid, {rename, CurrFile, NewFile}, string). + +-spec rename(Pid :: pid(), Old :: string(), New :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + +rename(Pid, Old, New) -> + call(Pid, {rename, Old, New}, string). + %%-------------------------------------------------------------------------- %% delete(Pid, File) -> ok | {error, epath} | {error, elogin} | @@ -270,9 +347,14 @@ rename(Pid, CurrFile, NewFile) -> %% %% Description: Remove file at remote server. %%-------------------------------------------------------------------------- + +-spec delete(Pid :: pid(), File :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + delete(Pid, File) -> call(Pid, {delete, File}, string). + %%-------------------------------------------------------------------------- %% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -280,9 +362,14 @@ delete(Pid, File) -> %% %% Description: Make directory at remote server. %%-------------------------------------------------------------------------- + +-spec mkdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + mkdir(Pid, Dir) -> call(Pid, {mkdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -290,9 +377,14 @@ mkdir(Pid, Dir) -> %% %% Description: Remove directory at remote server. %%-------------------------------------------------------------------------- + +-spec rmdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + rmdir(Pid, Dir) -> call(Pid, {rmdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn} %% Pid = pid() @@ -300,23 +392,41 @@ rmdir(Pid, Dir) -> %% %% Description: Set transfer type. %%-------------------------------------------------------------------------- + +-spec type(Pid :: pid(), Type :: ascii | binary) -> + 'ok' | + {'error', Reason :: 'etype' | restriction_reason() | common_reason()}. + type(Pid, Type) -> call(Pid, {type, Type}, atom). + %%-------------------------------------------------------------------------- -%% recv(Pid, RemoteFileName ) -> ok | {error, epath} | +%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} | %% {error, elogin} | {error, econn} %% Pid = pid() %% RemoteFileName = LocalFileName = string() %% %% Description: Transfer file from remote server. %%-------------------------------------------------------------------------- + +-spec recv(Pid :: pid(), RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | + common_reason() | + file_write_error_reason()}. + recv(Pid, RemotFileName) -> recv(Pid, RemotFileName, RemotFileName). +-spec recv(Pid :: pid(), + RemoteFileName :: string(), + LocalFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + recv(Pid, RemotFileName, LocalFileName) -> call(Pid, {recv, RemotFileName, LocalFileName}, atom). + %%-------------------------------------------------------------------------- %% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin} %% | {error, econn} @@ -326,9 +436,16 @@ recv(Pid, RemotFileName, LocalFileName) -> %% %% Description: Transfer file from remote server into binary. %%-------------------------------------------------------------------------- + +-spec recv_bin(Pid :: pid(), + RemoteFile :: string()) -> + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_bin(Pid, RemoteFile) -> call(Pid, {recv_bin, RemoteFile}, bin). + %%-------------------------------------------------------------------------- %% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -337,9 +454,15 @@ recv_bin(Pid, RemoteFile) -> %% %% Description: Start receive of chunks of remote file. %%-------------------------------------------------------------------------- + +-spec recv_chunk_start(Pid :: pid(), + RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk_start(Pid, RemoteFile) -> call(Pid, {recv_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason} %% Pid = pid() @@ -347,24 +470,47 @@ recv_chunk_start(Pid, RemoteFile) -> %% %% Description: Transfer file from remote server into binary in chunks %%-------------------------------------------------------------------------- + +-spec recv_chunk(Pid :: pid()) -> + 'ok' | + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk(Pid) -> call(Pid, recv_chunk, atom). + %%-------------------------------------------------------------------------- -%% send(Pid, LocalFileName ) -> ok | {error, epath} -%% | {error, elogin} -%% | {error, econn} +%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Transfer file to remote server. %%-------------------------------------------------------------------------- + +-spec send(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName) -> send(Pid, LocalFileName, LocalFileName). +-spec send(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName, RemotFileName) -> call(Pid, {send, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -374,11 +520,19 @@ send(Pid, LocalFileName, RemotFileName) -> %% %% Description: Transfer a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {send_bin, Bin, RemoteFile}, atom); send_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -387,9 +541,14 @@ send_bin(_Pid, _Bin, _RemoteFile) -> %% %% Description: Start transfer of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + send_chunk_start(Pid, RemoteFile) -> call(Pid, {send_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | %% {error, epath} | {error, econn} @@ -398,9 +557,14 @@ send_chunk_start(Pid, RemoteFile) -> %% %% Description: Start append chunks of data to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: term()}. + append_chunk_start(Pid, RemoteFile) -> call(Pid, {append_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -409,11 +573,19 @@ append_chunk_start(Pid, RemoteFile) -> %% %% Purpose: Send chunk to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + send_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); send_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -422,11 +594,19 @@ send_chunk(_Pid, _Bin) -> %% %% Description: Append chunk to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + append_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); append_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -434,9 +614,17 @@ append_chunk(_Pid, _Bin) -> %% %% Description: End sending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- %% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -444,23 +632,47 @@ send_chunk_end(Pid) -> %% %% Description: End appending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- -%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} -%% | {error, elogin} | {error, econn} +%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Append the local file to the remote file %%-------------------------------------------------------------------------- + +-spec append(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: 'epath' | + 'elogin' | + 'etnospc' | + 'epnospc' | + 'efnamena' | common_reason()}. + append(Pid, LocalFileName) -> append(Pid, LocalFileName, LocalFileName). +-spec append(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + append(Pid, LocalFileName, RemotFileName) -> call(Pid, {append, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -470,27 +682,44 @@ append(Pid, LocalFileName, RemotFileName) -> %% %% Purpose: Append a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec append_bin(Pid :: pid(), + Bin :: binary(), + RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {append_bin, Bin, RemoteFile}, atom); append_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- -%% quote(Pid, Cmd) -> ok +%% quote(Pid, Cmd) -> list() %% Pid = pid() %% Cmd = string() %% %% Description: Send arbitrary ftp command. %%-------------------------------------------------------------------------- + +-spec quote(Pid :: pid(), Cmd :: string()) -> list(). + quote(Pid, Cmd) when is_list(Cmd) -> call(Pid, {quote, Cmd}, atom). + %%-------------------------------------------------------------------------- %% close(Pid) -> ok %% Pid = pid() %% %% Description: End the ftp session. %%-------------------------------------------------------------------------- + +-spec close(Pid :: pid()) -> 'ok'. + close(Pid) -> cast(Pid, close), ok. @@ -502,9 +731,13 @@ close(Pid) -> %% %% Description: Return diagnostics. %%-------------------------------------------------------------------------- + +-spec formaterror(Tag :: term()) -> string(). + formaterror(Tag) -> ftp_response:error_string(Tag). + info(Pid) -> call(Pid, info, list). diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..6f32a31c1c 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,8 +18,14 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ + {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []} ] }, @@ -35,8 +41,14 @@ } ], [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ + {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []} ] }, diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 7059bb12cf..17e5f6777e 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-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 @@ -57,35 +57,42 @@ %% Description: Returns documentation/test cases in this test suite %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, solaris8_test}, {group, solaris9_test}, - {group, solaris10_test}, {group, linux_x86_test}, - {group, linux_ppc_test}, {group, macosx_x86_test}, - {group, macosx_ppc_test}, {group, openbsd_test}, - {group, freebsd_test}, {group, netbsd_test}, + [ + {group, solaris8_test}, + {group, solaris9_test}, + {group, solaris10_test}, + {group, linux_x86_test}, + {group, linux_ppc_test}, + {group, macosx_x86_test}, + {group, macosx_ppc_test}, + {group, openbsd_test}, + {group, freebsd_test}, + {group, netbsd_test}, {group, windows_xp_test}, {group, windows_2003_server_test}, - {group, ticket_tests}]. + {group, ticket_tests} + ]. groups() -> - [{solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, - {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, - {solaris10_test, [], - [{ftp_solaris10_sparc_test, all}, - {ftp_solaris10_x86_test, all}]}, - {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, - {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, - {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, - {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, - {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, - {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, - {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, - {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, - {windows_2003_server_test, [], - [{ftp_windows_2003_server_test, all}]}, - {ticket_tests, [], [{ftp_ticket_test, all}]}]. + [ + {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, + {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, + {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, + {ftp_solaris10_x86_test, all}]}, + {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, + {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, + {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, + {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, + {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, + {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, + {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, + {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, + {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, + {ticket_tests, [], [{ftp_ticket_test, all}]} + ]. init_per_group(_GroupName, Config) -> Config. diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 52b0134e8f7b871051af9be6cb7354553a0f72cd Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 11:20:16 +0100 Subject: Added proper release notes, appup and version. --- lib/inets/doc/src/mod_esi.xml | 29 +++++++++++++----------- lib/inets/doc/src/notes.xml | 39 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 12 +++++++++- lib/inets/vsn.mk | 2 +- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index e063088eb4..7b1e93e313 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -1,10 +1,10 @@ - +
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -32,9 +32,12 @@ Erlang Server Interface

This module defines the API - Erlang Server Interface (ESI). - Which is a more efficient way of writing erlang scripts - for your Inets web server than writing them as common CGI scripts.

+ Which is a more efficient way of writing erlang scripts + for your Inets web server than writing them as common CGI scripts.

+ +
+ deliver(SessionID, Data) -> ok | {error, Reason} @@ -51,15 +54,15 @@ parts of the content to the user.

Sends data from a Erl Scheme script back to the client.

-

Note - that if any HTTP-header fields should be added by the - script they must be in the first call to deliver/2 and the - data in the call must be a string. Calls after the headers - are complete may contain binary data to reduce copying - overhead. Do not - assume anything about the data type of SessionID, the - SessionID must be the value given as input to the esi - call back function that you implemented.

+ +

Note that if any HTTP-header fields should be added by the + script they must be in the first call to deliver/2 and the + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of + SessionID, the SessionID must be the value given as input to + the esi call back function that you implemented.

+
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..8e1acba9b8 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,45 @@ notes.xml
+
Inets 5.6 + +
Improvements and New Features + + + + +

[httpd] + mod_esi:deliver/2 + made to accept binary data.

+

Own Id: OTP-9123

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..b75277706f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,11 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, mod_esi, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} @@ -34,7 +39,12 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, mod_esi, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 06b742fd9fcdbff2cec092565d920462335cfbc0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 11:24:15 +0100 Subject: Removed email address. --- lib/inets/test/httpd_mod.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index f2c1fd6a65..1754cec7bc 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,7 +19,6 @@ %% -module(httpd_mod). --author('ingela@erix.ericsson.se'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -815,6 +814,8 @@ esi(Type, Port, Host, Node) -> [{statuscode, 302}, {version, "HTTP/1.0"}]), ok. + + %%-------------------------------------------------------------------- get(Type, Port, Host, Node) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Node, -- cgit v1.2.3 From 0422eb9016dd7bea2dff4004895ed45815ef0f48 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 16:15:35 +0100 Subject: Added proper release notes, appup and version. --- lib/inets/doc/src/notes.xml | 41 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 13 ++++++++++- lib/inets/vsn.mk | 2 +- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..5dbe761988 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,47 @@ notes.xml +
Inets 5.6 + +
Improvements and New Features + + + + +

[httpd] Prevent XSS in error pages. + Prevent user controlled input from being interpreted + as HTML in error pages by encoding the reserved HTML + characters.

+

Michael Santos

+

Own Id: OTP-9124

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..7e3785e240 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,12 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} @@ -34,7 +40,12 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]} + ] {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From a5d14f14e04ca7ca5fd34a7811cefbccdd024ce4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 16:19:20 +0100 Subject: Add original auther (Bernard Duggan) in release notes. --- lib/inets/doc/src/notes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 8e1acba9b8..50dbb7d3da 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -44,6 +44,7 @@

[httpd] mod_esi:deliver/2 made to accept binary data.

+

Bernard Duggan

Own Id: OTP-9123

-- cgit v1.2.3 From bf6a8c53023a284b9f2de8d13538db1ab3ff8dee Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 11 Mar 2011 12:27:47 +0100 Subject: [httpd] Prevent XSS in error pages. Prevent user controlled input from being interpreted as HTML in error pages by encoding the reserved HTML characters. --- lib/inets/src/inets_app/inets.appup.src | 1 + lib/inets/test/httpd_basic_SUITE.erl | 55 +++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 7e3785e240..b89ce0fbb2 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -46,6 +46,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]} ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 1cb07c2f5b..ced0d3d6d0 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -29,7 +29,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [uri_too_long_414, header_too_long_413, escaped_url_in_error_body]. + [ + uri_too_long_414, + header_too_long_413, + escaped_url_in_error_body + ]. groups() -> []. @@ -40,6 +44,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -50,6 +55,8 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + tsp("init_per_suite -> entry with" + "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), HttpdConf = [{port, 0}, {ipfamily, inet}, @@ -64,6 +71,8 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> + tsp("end_per_suite -> entry with" + "~n Config: ~p", [_Config]), inets:stop(), ok. @@ -79,9 +88,12 @@ end_per_suite(_Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- -init_per_testcase(_Case, Config) -> +init_per_testcase(Case, Config) -> + tsp("init_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -90,9 +102,12 @@ init_per_testcase(_Case, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + tsp("end_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- @@ -142,23 +157,30 @@ escaped_url_in_error_body(doc) -> escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + tsp("escaped_url_in_error_body -> entry with" + "~n Config: ~p", [Config]), + HttpdConf = ?config(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), - Address = proplists:get_value(bind_address, Info), - Path = "/this_is_bold", + _Address = proplists:get_value(bind_address, Info), + Path = "/this_is_bold", URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), - {ok, {404, Body}} = httpc:request(get, {URL, []}, - [{url_encode, true}, {version, "HTTP/1.0"}], - [{full_result, false}]), - EscapedPath = find_URL_path(string:tokens(Body, " ")), {ok, {404, Body1}} = httpc:request(get, {URL, []}, - [{version, "HTTP/1.0"}], [{full_result, false}]), + [{url_encode, true}, + {version, "HTTP/1.0"}], + [{full_result, false}]), + EscapedPath = find_URL_path(string:tokens(Body1, " ")), + {ok, {404, Body2}} = httpc:request(get, {URL, []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]), HTMLEncodedPath = http_util:html_encode(Path), - HTMLEncodedPath = find_URL_path(string:tokens(Body1, " ")), - inets:stop(httpd, Pid). + HTMLEncodedPath = find_URL_path(string:tokens(Body2, " ")), + inets:stop(httpd, Pid), + tsp("escaped_url_in_error_body -> done"), + ok. find_URL_path([]) -> ""; @@ -166,3 +188,10 @@ find_URL_path(["URL", URL | _]) -> URL; find_URL_path([_ | Rest]) -> find_URL_path(Rest). + + +tsp(F) -> + tsp(F, []). +tsp(F, A) -> + test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + -- cgit v1.2.3 From 61c6577254568abd92c2d8d00c948cf230e602ca Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 14 Mar 2011 12:17:12 +0100 Subject: Used the wrong security property names in documentation. --- lib/inets/doc/src/httpd.xml | 12 +++++------ lib/inets/doc/src/mod_security.xml | 4 ++-- lib/inets/doc/src/notes.xml | 37 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 14 ++++++++++--- lib/inets/vsn.mk | 2 +- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 62f4e18f82..91a55f6920 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -4,7 +4,7 @@
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -793,14 +793,14 @@ bytes

Here follows the valid properties for security directories

- {security_data_file, path()} + {data_file, path()} Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to store persistent data for the mod_security module. - {security_max_retries, integer()} + {max_retries, integer()} Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user @@ -810,13 +810,13 @@ bytes server will return 401 (Unauthorized), for security reasons. Defaults to 3 may also be set to infinity. - {security_block_time, integer()} + {block_time, integer()} Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. Defaults to 60 - {security_fail_expire_time, integer()} + {fail_expire_time, integer()} Specifies the number of minutes a failed user authentication @@ -824,7 +824,7 @@ bytes time, his previous failed authentications are forgotten. Defaults to 30 - {security_auth_timeout, integer()} + {auth_timeout, integer()} Specifies the number of seconds a successful user diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml index 2a871d29d8..a3c91dca5b 100644 --- a/lib/inets/doc/src/mod_security.xml +++ b/lib/inets/doc/src/mod_security.xml @@ -1,10 +1,10 @@ - +
- 19982010 + 19982011 Ericsson AB. All Rights Reserved. diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..b7899400fa 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,43 @@ notes.xml
+
Inets 5.6 + +
Improvements and New Features +

-

+ + + +
+ +
Fixed Bugs and Malfunctions + + +

[httpd] Wrong + security property + names used in documentation.

+

security_data_file used instead of data_file. + security_max_retries instead of max_retries. + security_block_time instead of block_time. + security_fail_expire_time instead of fail_expire_time. + security_auth_timeout instead of auth_timeout.

+

Garrett Smith

+

Own Id: OTP-9131

+
+
+
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..e8a9601f4f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,11 +18,15 @@ {"%VSN%", [ - {"5.5.1", + {"5.5.2", [ - {load_module, http_chunk, soft_purge, soft_purge, []} ] }, + {"5.5.1", + [ + {load_module, http_chunk, soft_purge, soft_purge, []} + ] + }, {"5.5", [ {restart_application, inets} @@ -34,7 +38,11 @@ ] } ], - [ + [ + {"5.5.2", + [ + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From cbe8b2e034a8b2cd6052df5e27f1b229ded9748e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Catalinas=20Jim=C3=A9nez?= Date: Thu, 17 Mar 2011 01:30:51 +0100 Subject: Fix log messages formating in httpd --- lib/inets/src/http_server/httpd_file.erl | 15 +++++++-------- lib/inets/src/http_server/httpd_log.erl | 2 +- lib/inets/src/http_server/httpd_request_handler.erl | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl index 7e21d9e158..dc1dae8bc9 100644 --- a/lib/inets/src/http_server/httpd_file.erl +++ b/lib/inets/src/http_server/httpd_file.erl @@ -26,22 +26,21 @@ handle_error(eacces, Op, ModData, Path) -> - handle_error(403, Op, ModData, Path,"Forbidden"); + handle_error(403, Op, ModData, Path, ": Forbidden"); handle_error(enoent, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path,"File not found"); + handle_error(404, Op, ModData, Path, ": File not found"); handle_error(enotdir, Op, ModData, Path) -> handle_error(404, Op, ModData, Path, - ": A component of the file name is not a directory"); + ": A component of the file name is not a directory"); handle_error(emfile, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": To many open files"); handle_error({enfile,_}, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": File table overflow"); handle_error(_Reason, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path, "File not found"). - -handle_error(StatusCode, Op, none, Path, Reason) -> - {StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)}; + handle_error(404, Op, ModData, Path, ": File not found"). +handle_error(StatusCode, Op, none, Path, Reason) -> + {StatusCode, none, ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}; handle_error(StatusCode, Op, ModData, Path, Reason) -> {StatusCode, ModData#mod.request_uri, - ?NICE("Can't " ++ Op ++ Path ++ Reason)}. + ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}. diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index f3ea3aa0e2..4b0f49d560 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -113,7 +113,7 @@ do_error_entry(ConfigDB, RemoteHost, undefined, Date, Reason) -> do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) -> case httpd_util:lookup(ConfigDB, error_log_format, pretty) of pretty -> - io_lib:format("[~s] access to ~s failed for ~s reason: ~n~p~n", + io_lib:format("[~s] access to ~s failed for ~s, reason: ~n~p~n", [Date, URI, RemoteHost, Reason]); compact -> io_lib:format( "[~s] access to ~s failed for ~s, reason: ~w~n", diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index a9db6e2058..31ec416b2d 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -240,13 +240,13 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Timeouts handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> - error_log("No request received on keep-alive connection" + error_log("No request received on keep-alive connection " "before server side timeout", ModData), %% No response should be sent! {stop, normal, State#state{response_sent = true}}; handle_info(timeout, #state{mod = ModData} = State) -> httpd_response:send_status(ModData, 408, "Request timeout"), - error_log("The client did not send the whole request before the" + error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; -- cgit v1.2.3 From eea0a7bdf77179c98e41f867398253c4c6f0811f Mon Sep 17 00:00:00 2001 From: Bernard Duggan Date: Thu, 17 Mar 2011 12:18:29 +1100 Subject: Fix timeout message generated by mod_esi When a mod_esi request times out, the code to send a timeout response was incorrect and generated an internal server error as well as an invalid response line. --- lib/inets/src/http_server/mod_esi.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..a8e3b4ab47 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -415,7 +415,7 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> end; timeout -> ?hdrv("deliver_webpage_chunk - timeout", []), - send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]), + send_headers(ModData, 504, [{"connection", "close"}]), httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket), process_flag(trap_exit,false), {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]} -- cgit v1.2.3 From f10de199e5aad1aecbff03a5e7af9e1c5d017301 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 18 Mar 2011 10:20:16 +0100 Subject: Fixed debug macro. Fixed appup file (not updated). Fixed application version. --- lib/inets/src/http_client/httpc.erl | 4 ++-- lib/inets/src/inets_app/inets.appup.src | 28 +++++++++++++++++++++++++--- lib/inets/vsn.mk | 2 +- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index c7f8a02a0b..b70b16f57f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -494,10 +494,10 @@ handle_request(Method, Url, end catch error:{noproc, _} -> - ?hcrl("noproc", [{profile, Profile}]), + ?hcrv("noproc", [{profile, Profile}]), {error, {not_started, Profile}}; throw:Error -> - ?hcrl("throw", [{error, Error}]), + ?hcrv("throw", [{error, Error}]), Error end. diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..65ce0cf0eb 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,9 +18,20 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request,http_chunk]} ] }, {"5.5", @@ -34,10 +45,21 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request,http_chunk]} ] }, {"5.5", diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From a0ab2e0fa31a2599d0532ef87c944837e96153c0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 14:07:44 +0200 Subject: A slightly more usefull debug printout (including httpd services). --- lib/inets/src/http_server/httpd_sup.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index f94e5459c1..b248c9bcf0 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -90,7 +90,7 @@ id(Address, Port) -> %%% Supervisor callback %%%========================================================================= init([HttpdServices]) -> - ?hdrd("starting", []), + ?hdrd("starting", [{httpd_service, HttpdServices}]), RestartStrategy = one_for_one, MaxR = 10, MaxT = 3600, -- cgit v1.2.3 From 6ebe1ea0e4b9bf58f6a27bac75c7c5e700044682 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 14:10:52 +0200 Subject: A (hopefully) temporary skip of some of the httpc proxy test cases. --- lib/inets/test/httpc_SUITE.erl | 21 +++++++++++++++++++-- lib/inets/test/inets_test_lib.erl | 11 +++++++++-- lib/inets/test/inets_test_lib.hrl | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index c1a36d23de..d8848412c5 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -28,6 +28,7 @@ -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). +-include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -228,8 +229,8 @@ init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> [{local_ssl_server, Server} | Config2]. init_per_testcase(Case, Timeout, Config) -> - io:format(user, "~n~n*** INIT ~w:[~w][~w] ***~n~n", - [?MODULE, Timeout, Case]), + io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", + [?MODULE, Case, Timeout]), PrivDir = ?config(priv_dir, Config), tsp("init_per_testcase -> stop inets"), application:stop(inets), @@ -1348,6 +1349,10 @@ proxy_options(doc) -> proxy_options(suite) -> []; proxy_options(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets, which + %% do not implement "options". + ?SKIP(server_does_not_implement_options), case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1372,6 +1377,9 @@ proxy_head(doc) -> proxy_head(suite) -> []; proxy_head(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1467,6 +1475,9 @@ proxy_post(doc) -> proxy_post(suite) -> []; proxy_post(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1489,6 +1500,9 @@ proxy_put(doc) -> proxy_put(suite) -> []; proxy_put(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1513,6 +1527,9 @@ proxy_delete(doc) -> proxy_delete(suite) -> []; proxy_delete(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index c56a714f5a..c837326bb5 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -32,7 +32,7 @@ -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). -export([oscmd/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1]). +-export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). @@ -395,6 +395,13 @@ sleep(MSecs) -> skip(Reason, File, Line) -> exit({skipped, {Reason, File, Line}}). +fail(Reason, File, Line) -> + String = lists:flatten(io_lib:format("Failure ~p(~p): ~p~n", + [File, Line, Reason])), + tsf(String). + + + flush() -> receive Msg -> @@ -407,7 +414,7 @@ flush() -> tsp(F) -> tsp(F, []). tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index 0cdb04139c..cc83a309b5 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -72,7 +72,7 @@ %% - Test case macros - --define(SKIP(Reason), inets_test_lib:skip(Reason)). +-define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)). -define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). -- cgit v1.2.3 From fe83491166fd1a394f768c33234150c67de98289 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Mar 2011 11:35:40 +0200 Subject: Improved (httpc proxy) "test case skipping". --- lib/inets/test/httpc_SUITE.erl | 44 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index d8848412c5..81e9c2b230 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -254,9 +254,10 @@ init_per_testcase(Case, Timeout, Config) -> [$e, $s, $s, $l | _] -> init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); - "proxy" ++ Rest -> + "proxy_" ++ Rest -> + io:format("init_per_testcase -> Rest: ~p~n", [Rest]), case Rest of - "_https_not_supported" -> + "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), tsp("init_per_testcase -> [proxy case] start ssl"), @@ -270,13 +271,39 @@ init_per_testcase(Case, Timeout, Config) -> | TmpConfig] end; _ -> + %% We use erlang.org for the proxy tests + %% and after the switch to erlang-web, many + %% of the test cases no longer work (erlang.org + %% previously run on Apache). + %% Until we have had time to update inets + %% (and updated erlang.org to use that inets) + %% and the test cases, we simply skip the + %% problematic test cases. + %% This is not ideal, but I am busy.... case is_proxy_available(?PROXY, ?PROXY_PORT) of true -> - inets:start(), - [{watchdog, Dog} | TmpConfig]; + BadCases = + [ + "delete", + "get", + "head", + "not_modified_otp_6821", + "options", + "page_does_not_exist", + "post", + "put", + "stream" + ], + case lists:member(Rest, BadCases) of + true -> + [{skip, "TC and server not compatible"}| + TmpConfig]; + false -> + inets:start(), + [{watchdog, Dog} | TmpConfig] + end; false -> - [{skip, "Failed to contact proxy"} | - TmpConfig] + [{skip, "proxy not responding"} | TmpConfig] end end; _ -> @@ -1352,7 +1379,6 @@ proxy_options(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets, which %% do not implement "options". - ?SKIP(server_does_not_implement_options), case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1379,7 +1405,6 @@ proxy_head(suite) -> proxy_head(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1477,7 +1502,6 @@ proxy_post(suite) -> proxy_post(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1502,7 +1526,6 @@ proxy_put(suite) -> proxy_put(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1529,7 +1552,6 @@ proxy_delete(suite) -> proxy_delete(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", -- cgit v1.2.3 From 3bc0fccb951ffb0909d2824b65d58ad31ad16cc5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 1 Apr 2011 11:51:58 +0200 Subject: Added crypto-start-check to undef_funcs test case. --- lib/inets/test/inets_app_test.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl index 11b507fa26..49ea18501f 100644 --- a/lib/inets/test/inets_app_test.erl +++ b/lib/inets/test/inets_app_test.erl @@ -241,6 +241,20 @@ undef_funcs(suite) -> undef_funcs(doc) -> []; undef_funcs(Config) when is_list(Config) -> + %% We need to check if there is a point to run this test. + %% On some platforms, crypto will not build, which in turn + %% causes ssl to not to not build (at this time, this will + %% change in the future). + %% So, we first check if we can start crypto, and if not, + %% we skip this test case! + case (catch crypto:start()) of + ok -> + ok; + {error, {already_started, crypto}} -> + ok; + _ -> + ?SKIP(crypto_start_check_failed) + end, App = inets, AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), -- cgit v1.2.3 From 7861327b7830c64675dd73d424de7d62007fd5a0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 17:40:42 +0200 Subject: Accepting file descriptors (fd) in the config for socket type ip_comm. --- lib/inets/src/http_lib/http_transport.erl | 69 ++++++++++++++++++------------- lib/inets/src/http_server/httpd_sup.erl | 45 ++++++++++++-------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 0024d19fc1..173911b868 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -23,7 +23,7 @@ -export([ start/1, connect/3, connect/4, - listen/2, listen/3, + listen/2, listen/3, listen/4, accept/2, accept/3, close/2, send/3, @@ -136,10 +136,11 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> %%------------------------------------------------------------------------- -%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason} +%% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason} %% SocketType = ip_comm | {ssl, SSLConfig} %% Port = integer() -%% Socket = socket() +%% Socket = socket() +%% Fd = undefined | fd() %% %% Description: Sets up socket to listen on the port Port on the local %% host using either gen_tcp or ssl. In the gen_tcp case the port @@ -151,13 +152,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm, Addr, Port) -> - case (catch listen_ip_comm(Addr, Port)) of - {'EXIT', Reason} -> - {error, {exit, Reason}}; - Else -> - Else - end; +listen(ip_comm = SocketType, Addr, Port) -> + listen(SocketType, Addr, Port, undefined); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port) -> @@ -186,9 +182,17 @@ listen({essl, SSLConfig} = Ssl, Addr, Port) -> Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt], ssl:listen(Port, Opt2). +listen(ip_comm, Addr, Port, Fd) -> + case (catch listen_ip_comm(Addr, Port, Fd)) of + {'EXIT', Reason} -> + {error, {exit, Reason}}; + Else -> + Else + end. + -listen_ip_comm(Addr, Port) -> - {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port), +listen_ip_comm(Addr, Port, Fd) -> + {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts], @@ -223,29 +227,36 @@ listen_ip_comm(Addr, Port) -> ipfamily_default(Addr, Port) -> httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). -get_socket_info(Addr, Port) -> - Key = list_to_atom("httpd_" ++ integer_to_list(Port)), - BaseOpts = [{backlog, 128}, {reuseaddr, true}], +get_socket_info(Addr, Port, Fd0) -> + BaseOpts = [{backlog, 128}, {reuseaddr, true}], IpFamilyDefault = ipfamily_default(Addr, Port), - case init:get_argument(Key) of - {ok, [[Value]]} -> - {Fd, IpFamily} = - case string:tokens(Value, [$|]) of - [FdStr, IpFamilyStr] -> - Fd0 = fd_of(FdStr), - IpFamily0 = ip_family_of(IpFamilyStr), - {Fd0, IpFamily0}; - [FdStr] -> - {fd_of(FdStr), IpFamilyDefault}; - _ -> - throw({error, {bad_descriptor, Value}}) - end, + %% The presence of a file descriptor takes precedence + case get_fd(Port, Fd0, IpFamilyDefault) of + {Fd, IpFamily} -> {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily}; - error -> + undefined -> {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} end. +get_fd(Port, undefined = _Fd, IpFamilyDefault) -> + FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), + case init:get_argument(FdKey) of + {ok, [[Value]]} -> + case string:tokens(Value, [$|]) of + [FdStr, IpFamilyStr] -> + {fd_of(FdStr), ip_family_of(IpFamilyStr)}; + [FdStr] -> + {fd_of(FdStr), IpFamilyDefault}; + _ -> + throw({error, {bad_descriptor, Value}}) + end; + error -> + undefined + end; +get_fd(_Port, Fd, IpFamilyDefault) -> + {Fd, IpFamilyDefault}. + fd_of(FdStr) -> case (catch list_to_integer(FdStr)) of Fd when is_integer(Fd) -> diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index b248c9bcf0..d028a19bf0 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -182,24 +182,32 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> Error end. -httpd_child_spec(Config, AcceptTimeout, Debug, Addr, 0) -> - case start_listen(Addr, 0, Config) of - {Pid, {NewPort, NewConfig, ListenSocket}} -> - Name = {httpd_instance_sup, Addr, NewPort}, - StartFunc = {httpd_instance_sup, start_link, - [NewConfig, AcceptTimeout, - {Pid, ListenSocket}, Debug]}, - Restart = permanent, - Shutdown = infinity, - Modules = [httpd_instance_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}; - {Pid, {error, Reason}} -> - exit(Pid, normal), - {error, Reason} - end; - httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> + case Port == 0 orelse proplists:is_defined(fd, Config) of + true -> + httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); + false -> + httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) + end. + +httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) -> + case start_listen(Addr, Port, Config) of + {Pid, {NewPort, NewConfig, ListenSocket}} -> + Name = {httpd_instance_sup, Addr, NewPort}, + StartFunc = {httpd_instance_sup, start_link, + [NewConfig, AcceptTimeout, + {Pid, ListenSocket}, Debug]}, + Restart = permanent, + Shutdown = infinity, + Modules = [httpd_instance_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}; + {Pid, {error, Reason}} -> + exit(Pid, normal), + {error, Reason} + end. + +httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) -> Name = {httpd_instance_sup, Addr, Port}, StartFunc = {httpd_instance_sup, start_link, [Config, AcceptTimeout, Debug]}, @@ -224,7 +232,8 @@ listen(Address, Port, Config) -> SocketType = proplists:get_value(socket_type, Config, ip_comm), case http_transport:start(SocketType) of ok -> - case http_transport:listen(SocketType, Address, Port) of + Fd = proplists:get_value(fd, Config), + case http_transport:listen(SocketType, Address, Port, Fd) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), {ok, NewPort} = inet:port(ListenSocket), -- cgit v1.2.3 From 2f045f17afd8a286a06ceddf6fced1d3b81b4c5b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 18:11:58 +0200 Subject: Updated release notes and linked file (that is added markers in the inets file). --- lib/inets/doc/src/inets.xml | 54 +++++++++++++++++++++++++++++++-------------- lib/inets/doc/src/notes.xml | 17 +++++++++++++- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml index c367d7fa77..a2bf42320f 100644 --- a/lib/inets/doc/src/inets.xml +++ b/lib/inets/doc/src/inets.xml @@ -1,4 +1,4 @@ - + @@ -32,8 +32,10 @@ The inets services API

This module provides the most basic API to the - clients and servers, that are part of the Inets application, - such as start and stop.

+ clients and servers, that are part of the Inets application, + such as start and stop.

+ +
@@ -42,7 +44,10 @@ this module:

service() = ftpc | tftp | httpc | httpd

property() = atom()

+ +
+ services() -> [{Service, Pid}] @@ -54,11 +59,13 @@

Returns a list of currently running services.

-

Services started as stand_alone will not - be listed.

+

Services started as stand_alone will not be listed.

+ +
+ services_info() -> [{Service, Pid, Info}] Returns a list of currently running services where @@ -73,11 +80,13 @@

Returns a list of currently running services where each - service is described by a [{Option, Value}] list. The - information given in the list is specific for each service - and it is probable that each service will have its own info - function that gives you even more details about the - service.

+ service is described by a [{Option, Value}] list. The + information given in the list is specific for each service + and it is probable that each service will have its own info + function that gives you even more details about the + service.

+ +
@@ -89,6 +98,8 @@

Returns a list of available service names.

+ +
@@ -101,18 +112,24 @@

Starts the Inets application. Default type - is temporary. See also - application(3)

+ is temporary. See also + application(3).

+ +
+ stop() -> ok Stops the inets application.

Stops the inets application. See also - application(3)

+ application(3).

+ +
+ start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason} start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason} @@ -144,8 +161,11 @@ some sense the calling process has now become the top supervisor.

+ +
+ stop(Service, Reference) -> ok | {error, Reason} Stops a started service of the inets application or takes @@ -157,9 +177,11 @@

Stops a started service of the inets application or takes - down a "stand_alone-service" gracefully. When the - stand_alone option is used in start, - only the pid is a valid argument to stop.

+ down a "stand_alone-service" gracefully. When the + stand_alone option is used in start, + only the pid is a valid argument to stop.

+ +
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index f9411fed2a..db1752e6f9 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -77,6 +77,20 @@

Own Id: OTP-9157

+ +

[httpd] Extended support for file descriptors. + In order to be able to bind to a privileged port + without running the erlang VM as root, the support + for using file descriptors has been improved. + It is now possible to add to the config (option fd) when + calling the + inets:start(httpd, ...) + function.

+

Attila Rajmund Nohl

+

Own Id: OTP-9202

+

Aux Id: seq11819

+
+
@@ -162,7 +176,8 @@ are URL-encoded. Added support in http-client to use URL-encoding. Also added the missing include directory for the inets application.

-

Own Id: OTP-8940 Aux Id: seq11735

+

Own Id: OTP-8940

+

Aux Id: seq11735

-- cgit v1.2.3 From d0f399b4b08b5cd9fb54bec5d71c82b560199c86 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Apr 2011 18:06:45 +0200 Subject: Proper release notes. --- lib/inets/src/inets_app/inets.appup.src | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index df18ffa9e8..c14f8d51f6 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -22,12 +22,14 @@ [ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} ] @@ -37,12 +39,14 @@ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request, http_chunk]} @@ -64,12 +68,14 @@ [ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} ] @@ -79,12 +85,14 @@ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request, http_chunk]} -- cgit v1.2.3 From 5488c7843c8930d14d1d0d2ec0aa23d0d491afbc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Apr 2011 13:37:06 +0200 Subject: Default ssl kind changed to essl (from ossl). --- lib/inets/doc/src/httpc.xml | 21 ++++++--------------- lib/inets/doc/src/httpd.xml | 2 +- lib/inets/doc/src/notes.xml | 13 +++++++++++++ lib/inets/src/http_lib/http_internal.hrl | 6 +++--- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 532f5eefde..f6b6827e93 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -105,17 +105,8 @@ filename() = string()
SSL DATA TYPES -

Some type definitions relevant when using https, - for details ssl(3):

- +

See ssl(3) for information + about ssl options (ssloptions()).

@@ -175,9 +166,9 @@ ssl_options() = {verify, code()} | http_options() = [http_option()] http_option() = {timeout, timeout()} | {connect_timeout, timeout()} | - {ssl, ssl_options()} | - {ossl, ssl_options()} | - {essl, ssl_options()} | + {ssl, ssloptions()} | + {ossl, ssloptions()} | + {essl, ssloptions()} | {autoredirect, boolean()} | {proxy_auth, {userstring(), passwordstring()}} | {version, http_version()} | @@ -236,7 +227,7 @@ ssl_options() = {verify, code()} |

This is the default ssl config option, currently defaults to - ossl, see below.

+ essl, see below.

Defaults to [].

diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index ac49a37296..edacb73b65 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -154,7 +154,7 @@ ossl specifically uses the OpenSSL based (old) SSL. essl specifically uses the Erlang based (new) SSL. When using ssl it currently defaults to - ossl.

+ essl.

Defaults to ip_comm.

diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index df3bf19066..b885bcbcdb 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -91,6 +91,19 @@

Aux Id: seq11819

+ +

The default ssl kind has now been changed to essl.

+

ossl will work for as long as the ssl application + supports it.

+

See the httpd + socket_type + communication property or the httpc + request/4,5 function + for more info.

+

Own Id: OTP-9230

+

*** POTENTIAL INCOMPATIBILITY ***

+
+
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl index 5440f214b5..2e924667c6 100644 --- a/lib/inets/src/http_lib/http_internal.hrl +++ b/lib/inets/src/http_lib/http_internal.hrl @@ -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 @@ -28,8 +28,8 @@ -define(HTTP_MAX_URI_SIZE, nolimit). -ifndef(HTTP_DEFAULT_SSL_KIND). --define(HTTP_DEFAULT_SSL_KIND, ossl). -%% -define(HTTP_DEFAULT_SSL_KIND, essl). +%% -define(HTTP_DEFAULT_SSL_KIND, ossl). +-define(HTTP_DEFAULT_SSL_KIND, essl). -endif. % -ifdef(HTTP_DEFAULT_SSL_KIND). -- cgit v1.2.3 From 29d4a0f745b69f7d2a75664b857b6ad65fca920d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Apr 2011 13:43:27 +0200 Subject: Updated appup file. --- lib/inets/src/inets_app/inets.appup.src | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index c14f8d51f6..0a590c9c36 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -24,6 +24,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -41,6 +42,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -70,6 +72,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -87,6 +90,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, -- cgit v1.2.3 From 5d32eaf750cff98d242c8048eeb6b49c94fb6ee6 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 May 2011 12:05:11 +0200 Subject: Fixed httpc manager crash httpc manager crashes.When a request results in a retry, the request id will be "reused" in the previous implementation a race condition could occur causing the manager to crash. This is now avoided by using proc_lib:init_ack and gen_server:enter_loop to allow more advanced initialization of httpc_handlers without blocking the httpc_manger and eliminating extra processes that can cause race conditions. --- lib/inets/src/http_client/httpc_handler.erl | 256 ++++-------- lib/inets/src/http_client/httpc_handler_sup.erl | 8 +- lib/inets/src/http_client/httpc_manager.erl | 509 +++++------------------- lib/inets/src/http_client/httpc_request.erl | 8 +- lib/inets/src/inets_app/inets.appup.src | 60 +-- lib/inets/test/httpc_SUITE.erl | 211 ++++++---- 6 files changed, 324 insertions(+), 728 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 5e22400fe0..1f0e012e7e 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 @@ -29,10 +29,10 @@ %%-------------------------------------------------------------------- %% Internal Application API -export([ - start_link/2, - connect_and_send/2, + start_link/4, + %% connect_and_send/2, send/2, - cancel/2, + cancel/3, stream/3, stream_next/1, info/1 @@ -51,7 +51,7 @@ -record(state, { request, % #request{} - session, % #tcp_session{} + session, % #session{} status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() @@ -94,13 +94,9 @@ %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -start_link(Options, ProfileName) -> - Args = [Options, ProfileName], - gen_server:start_link(?MODULE, Args, []). - -connect_and_send(Request, HandlerPid) -> - call({connect_and_send, Request}, HandlerPid). - +start_link(Parent, Request, Options, ProfileName) -> + {ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options, + ProfileName]])}. %%-------------------------------------------------------------------- %% Function: send(Request, Pid) -> ok @@ -122,8 +118,8 @@ send(Request, Pid) -> %% Description: Cancels a request. Intended to be called by the httpc %% manager process. %%-------------------------------------------------------------------- -cancel(RequestId, Pid) -> - cast({cancel, RequestId}, Pid). +cancel(RequestId, Pid, From) -> + cast({cancel, RequestId, From}, Pid). %%-------------------------------------------------------------------- @@ -229,16 +225,27 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed %% but we do not want that so errors will be handled by the process %% sending an init_error message to itself. %%-------------------------------------------------------------------- -init([Options, ProfileName]) -> - ?hcrv("init - starting", [{options, Options}, {profile, ProfileName}]), +init([Parent, Request, Options, ProfileName]) -> process_flag(trap_exit, true), - handle_verbose(Options#options.verbose), - State = #state{status = undefined, - options = Options, - profile_name = ProfileName}, - ?hcrd("init - started", []), - {ok, State}. + %% Do not let initial tcp-connection block the manager-process + proc_lib:init_ack(Parent, self()), + handle_verbose(Options#options.verbose), + Address = handle_proxy(Request#request.address, Options#options.proxy), + {ok, State} = + case {Address /= Request#request.address, Request#request.scheme} of + {true, https} -> + Error = https_through_proxy_is_not_currently_supported, + self() ! {init_error, + Error, httpc_response:error(Request, Error)}, + {ok, #state{request = Request, options = Options, + status = ssl_tunnel}}; + {_, _} -> + connect_and_send_first_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}) + end, + gen_server:enter_loop(?MODULE, [], State). %%-------------------------------------------------------------------- %% Function: handle_call(Request, From, State) -> {reply, Reply, State} | @@ -249,41 +256,6 @@ init([Options, ProfileName]) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- - - -%% This is the first request, the reason the proc was started -handle_call({connect_and_send, #request{address = Address0, - scheme = Scheme} = Request}, - _From, - #state{options = #options{proxy = Proxy}, - status = undefined, - session = undefined} = State) -> - ?hcrv("connect and send", [{address0, Address0}, {proxy, Proxy}]), - Address = handle_proxy(Address0, Proxy), - if - ((Address =/= Address0) andalso (Scheme =:= https)) -> - %% This is what we should do if and when ssl supports - %% "socket upgrading" - %%send_ssl_tunnel_request(Address, Request, - %% #state{options = Options, - %% status = ssl_tunnel}); - Reason = {failed_connecting, - https_through_proxy_is_not_currently_supported}, - %% Send a reply to the original caller - ErrorResponse = httpc_response:error(Request, Reason), - httpc_response:send(Request#request.from, ErrorResponse), - %% Reply to the manager - ErrorReply = {error, Reason}, - {stop, normal, ErrorReply, State}; - true -> - case connect_and_send_first_request(Address, Request, State) of - {ok, NewState} -> - {reply, ok, NewState}; - {stop, Error, NewState} -> - {stop, normal, Error, NewState} - end - end; - handle_call(#request{address = Addr} = Request, _, #state{status = Status, session = #session{type = pipeline} = Session, @@ -445,25 +417,27 @@ handle_call(info, _, State) -> %% handle_keep_alive_queue/2 on the other hand will just skip the %% request as if it was never issued as in this case the request will %% not have been sent. -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{request = #request{id = RequestId} = Request, profile_name = ProfileName, canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], request = Request#request{from = answer_sent}}}; -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{profile_name = ProfileName, + request = #request{id = CurrId}, canceled = Canceled} = State) -> - ?hcrv("cancel", [{request_id, RequestId}, + ?hcrv("cancel", [{request_id, RequestId}, + {curr_req_id, CurrId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; @@ -872,62 +846,55 @@ connect(SocketType, ToAddress, Opts3 = [IpFamily | Opts2], http_transport:connect(SocketType, ToAddress, Opts3, Timeout) end. - -connect_and_send_first_request(Address, - #request{settings = Settings, - headers = Headers, - address = OrigAddress, - scheme = Scheme} = Request, - #state{options = Options} = State) -> - - ?hcrd("connect", - [{address, Address}, {request, Request}, {options, Options}]), +connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> SocketType = socket_type(Request), - ConnTimeout = Settings#http_options.connect_timeout, + ConnTimeout = (Request#request.settings)#http_options.connect_timeout, + ?hcri("connect", + [{address, Address}, {request, Request}, {options, Options}]), case connect(SocketType, Address, Options, ConnTimeout) of {ok, Socket} -> - Session = #session{id = {OrigAddress, self()}, - scheme = Scheme, - socket = Socket, - socket_type = SocketType}, - ?hcrd("connected - now send first request", [{socket, Socket}]), + ClientClose = + httpc_request:is_client_closing( + Request#request.headers), + SessionType = httpc_manager:session_type(Options), + SocketType = socket_type(Request), + Session = #session{id = {Request#request.address, self()}, + scheme = Request#request.scheme, + socket = Socket, + socket_type = SocketType, + client_close = ClientClose, + type = SessionType}, + ?hcri("connected - now send first request", [{socket, Socket}]), + case httpc_request:send(Address, Session, Request) of ok -> - ?hcrd("first request sent", []), - ClientClose = - httpc_request:is_client_closing(Headers), - SessionType = httpc_manager:session_type(Options), - Session2 = - Session#session{client_close = ClientClose, - type = SessionType}, - TmpState = - State#state{request = Request, - session = Session2, - mfa = init_mfa(Request, State), - status_line = init_status_line(Request), - headers = undefined, - body = undefined, - status = new}, - ?hcrt("activate socket", []), - activate_once(Session), + ?hcri("first request sent", []), + TmpState = State#state{request = Request, + session = Session, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), NewState = activate_request_timeout(TmpState), {ok, NewState}; - - {error, Reason} = Error -> - ?hcrv("failed sending request", [{reason, Reason}]), - {stop, Error, - State#state{session = {send_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_sending, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request, + session = + #session{socket = Socket}}} end; - - {error, Reason} = Error -> - ?hcri("connect failed", [{reason, Reason}]), - {stop, Error, State#state{session = {connect_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_connecting, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request}} end. - handler_info(#state{request = Request, session = Session, status_line = _StatusLine, @@ -1167,12 +1134,12 @@ handle_response(#state{request = Request, {ok, Msg, Data} -> ?hcrd("handle response - ok", []), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), handle_queue(NewState, Data); {stop, Msg} -> ?hcrd("handle response - stop", [{msg, Msg}]), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), {stop, normal, NewState} end. @@ -1242,7 +1209,8 @@ handle_pipeline(#state{status = pipeline, %% See comment for handle_cast({cancel, RequestId}) {stop, normal, State#state{request = - NextRequest#request{from = answer_sent}}}; + NextRequest#request{from = answer_sent}, + pipeline = Pipeline}}; false -> ?hcrv("next request", [{request, NextRequest}]), NewSession = @@ -1443,6 +1411,7 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), + State#state{request = Request#request{from = answer_sent}, timers = Timers#timers{request_timers = @@ -1662,67 +1631,6 @@ handle_verbose(_) -> ok. -%%% Normaly I do not comment out code, I throw it away. But this might -%%% actually be used one day if ssl is improved. -%% send_ssl_tunnel_request(Address, Request = #request{address = {Host, Port}}, -%% State) -> -%% %% A ssl tunnel request is a special http request that looks like -%% %% CONNECT host:port HTTP/1.1 -%% SslTunnelRequest = #request{method = connect, scheme = http, -%% headers = -%% #http_request_h{ -%% host = Host, -%% address = Address, -%% path = Host ++ ":", -%% pquery = integer_to_list(Port), -%% other = [{ "Proxy-Connection", "keep-alive"}]}, -%% Ipv6 = (State#state.options)#options.ipv6, -%% SocketType = socket_type(SslTunnelRequest), -%% case http_transport:connect(SocketType, -%% SslTunnelRequest#request.address, Ipv6) of -%% {ok, Socket} -> -%% case httpc_request:send(Address, SslTunnelRequest, Socket) of -%% ok -> -%% Session = #tcp_session{id = -%% {SslTunnelRequest#request.address, -%% self()}, -%% scheme = -%% SslTunnelRequest#request.scheme, -%% socket = Socket}, -%% NewState = State#state{mfa = -%% {httpc_response, parse, -%% [State#state.max_header_size]}, -%% request = Request, -%% session = Session}, -%% http_transport:setopts(socket_type( -%% SslTunnelRequest#request.scheme), -%% Socket, -%% [{active, once}]), -%% {ok, NewState}; -%% {error, Reason} -> -%% self() ! {init_error, error_sending, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request, -%% session = #tcp_session{socket = -%% Socket}}} -%% end; -%% {error, Reason} -> -%% self() ! {init_error, error_connecting, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request}} -%% end. - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - send_raw(#session{socket = Socket, socket_type = SocketType}, {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> @@ -1756,11 +1664,5 @@ call(Msg, Pid, Timeout) -> cast(Msg, Pid) -> gen_server:cast(Pid, Msg). - -%% to(To, Start) when is_integer(Start) andalso (Start >= 0) -> -%% http_util:timeout(To, Start); -%% to(To, _Start) -> -%% http_util:timeout(To, t()). - t() -> http_util:timestamp(). diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl index 2a69fd15d0..f7a0b014b3 100644 --- a/lib/inets/src/http_client/httpc_handler_sup.erl +++ b/lib/inets/src/http_client/httpc_handler_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ %% API -export([start_link/0]). --export([start_child/2]). +-export([start_child/1]). %% Supervisor callback -export([init/1]). @@ -34,11 +34,9 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -start_child(Options, Profile) -> - Args = [Options, Profile], +start_child(Args) -> supervisor:start_child(?MODULE, Args). - %%%========================================================================= %%% Supervisor callback %%%========================================================================= diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 591cb78c29..7f66b477eb 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ start_link/3, request/2, cancel_request/2, - request_canceled/2, + request_canceled/3, request_done/2, retry_request/2, redirect_request/2, @@ -66,6 +66,7 @@ state % State of the handler: initiating | started | operational | canceled }). +-define(DELAY, 500). %%==================================================================== %% Internal Application API @@ -158,7 +159,8 @@ cancel_request(RequestId, ProfileName) -> %% be called by the httpc handler process. %%-------------------------------------------------------------------- -request_canceled(RequestId, ProfileName) -> +request_canceled(RequestId, ProfileName, From) -> + gen_server:reply(From, ok), cast(ProfileName, {request_canceled, RequestId}). @@ -355,44 +357,32 @@ do_init(ProfileName, CookiesDir) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({request, Request}, _From, State) -> - ?hcrv("request", [{request, Request}]), +handle_call({request, Request}, _, State) -> + ?hcri("request", [{request, Request}]), case (catch handle_request(Request, State)) of - {ok, ReqId, NewState} -> - {reply, {ok, ReqId}, NewState}; - + {reply, Msg, NewState} -> + {reply, Msg, NewState}; Error -> - NewError = {error, {failed_process_request, Error}}, - {reply, NewError, State} + {stop, Error, httpc_response:error(Request, Error), State} end; - -handle_call({cancel_request, RequestId}, From, - #state{handler_db = HandlerDb} = State) -> - ?hcrv("cancel_request", [{request_id, RequestId}]), + +handle_call({cancel_request, RequestId}, From, State) -> + ?hcri("cancel_request", [{request_id, RequestId}]), case ets:lookup(State#state.handler_db, RequestId) of [] -> - ?hcrd("nothing to cancel", []), - Reply = ok, %% Nothing to cancel - {reply, Reply, State}; - - [#handler_info{handler = Pid}] when is_pid(Pid) -> - ?hcrd("found operational handler for this request", - [{handler, Pid}]), - httpc_handler:cancel(RequestId, Pid), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}}; - - [#handler_info{starter = Pid, state = HandlerState}] - when is_pid(Pid) -> - ?hcri("found *initiating* handler for this request", - [{starter, Pid}, {state, HandlerState}]), - ets:update_element(HandlerDb, RequestId, - {#handler_info.state, canceled}), + %% The request has allready compleated make sure + %% it is deliverd to the client process queue so + %% it can be thrown away by httpc:cancel_request + %% This delay is hopfully a temporary workaround. + %% Note that it will not not delay the manager, + %% only the client that called httpc:cancel_request + timer:apply_after(?DELAY, gen_server, reply, [From, ok]), + {noreply, State}; + [{_, Pid, _}] -> + httpc_handler:cancel(RequestId, Pid, From), {noreply, State#state{cancel = - [{RequestId, Pid, From} | + [{RequestId, Pid, From} | State#state.cancel]}} - end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -437,43 +427,16 @@ handle_call(Req, From, #state{profile_name = ProfileName} = State) -> %%-------------------------------------------------------------------- handle_cast({retry_or_redirect_request, {Time, Request}}, #state{profile_name = ProfileName} = State) -> - ?hcrv("retry or redirect request", [{time, Time}, {request, Request}]), - case timer:apply_after(Time, ?MODULE, retry_request, - [Request, ProfileName]) of - {ok, _} -> - {noreply, State}; - {error, Reason} -> - error_report(ProfileName, - "failed scheduling retry/redirect request" - "~n Time: ~p" - "~n Request: ~p" - "~n Reason: ~p", [Time, Request, Reason]), - {noreply, State} - end; + {ok, _} = timer:apply_after(Time, ?MODULE, retry_request, [Request, ProfileName]), + {noreply, State}; -handle_cast({retry_or_redirect_request, Request}, - #state{profile_name = Profile, - handler_db = HandlerDb} = State) -> - ?hcrv("retry or redirect request", [{request, Request}]), +handle_cast({retry_or_redirect_request, Request}, State) -> case (catch handle_request(Request, State)) of - {ok, _, NewState} -> + {reply, {ok, _}, NewState} -> {noreply, NewState}; - Error -> - ReqId = Request#request.id, - error_report(Profile, - "failed to retry or redirect request ~p" - "~n Error: ~p", [ReqId, Error]), - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{from = From}] -> - Error2 = httpc_response:error(Request, Error), - httpc_response:send(From, Error2), - ok; - - _ -> - ok - end, - {noreply, State} + httpc_response:error(Request, Error), + {stop, Error, State} end; handle_cast({request_canceled, RequestId}, State) -> @@ -482,7 +445,6 @@ handle_cast({request_canceled, RequestId}, State) -> case lists:keysearch(RequestId, 1, State#state.cancel) of {value, Entry = {RequestId, _, From}} -> ?hcrt("found in cancel", [{from, From}]), - gen_server:reply(From, ok), {noreply, State#state{cancel = lists:delete(Entry, State#state.cancel)}}; Else -> @@ -539,8 +501,6 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> "recived unknown message" "~n Msg: ~p", [Msg]), {noreply, State}. - - %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -548,39 +508,17 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling all non call/cast messages %%--------------------------------------------------------- - -handle_info({started, StarterPid, ReqId, HandlerPid}, State) -> - handle_started(StarterPid, ReqId, HandlerPid, State), - {noreply, State}; - -handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) -> - handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State), - {noreply, State}; - -handle_info({failed_starting_handler, StarterPid, ReqId, Res}, State) -> - handle_failed_starting_handler(StarterPid, ReqId, Res, State), - {noreply, State}; - -handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) -> - maybe_handle_terminating_starter(Pid, Reason, HandlerDb), +handle_info({'EXIT', _, _}, State) -> + %% Handled in DOWN {noreply, State}; - handle_info({'DOWN', _, _, Pid, _}, State) -> - - %% - %% Normally this should have been cleaned up already - %% (when receiving {request_done, PequestId}), but - %% just in case there is a glitch, cleanup anyway. - %% - - Pattern = #handler_info{handler = Pid, _ = '_'}, - ets:match_delete(State#state.handler_db, Pattern), + ets:match_delete(State#state.handler_db, {'_', Pid, '_'}), %% If there where any canceled request, handled by the %% the process that now has terminated, the %% cancelation can be viewed as sucessfull! - NewCanceledList = - lists:foldl(fun({_, HandlerPid, From} = Entry, Acc) -> + NewCanceldList = + lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) -> case HandlerPid of Pid -> gen_server:reply(From, ok), @@ -589,15 +527,13 @@ handle_info({'DOWN', _, _, Pid, _}, State) -> Acc end end, State#state.cancel, State#state.cancel), - {noreply, State#state{cancel = NewCanceledList}}; - -handle_info(Info, #state{profile_name = ProfileName} = State) -> - error_report(ProfileName, - "received unknown info" - "~n Info: ~p", [Info]), + {noreply, State#state{cancel = NewCanceldList}}; +handle_info(Info, State) -> + Report = io_lib:format("Unknown message in " + "httpc_manager:handle_info ~p~n", [Info]), + error_logger:error_report(Report), {noreply, State}. - %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> _ (ignored by gen_server) %% Description: Shutdown the httpc_handler @@ -655,224 +591,79 @@ get_handler_info(Tab) -> {Pid, State} <- Handlers2], Handlers3. - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When the handler has sucessfully been started, -%% this message (started) is sent. -%% - -handle_started(StarterPid, ReqId, HandlerPid, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = initiating} = HandlerInfo] -> - ?hcri("received started ack for initiating handler", []), - %% As a last resort, make sure we know when it exits, - %% in case it forgets to notify us. - %% We dont need to know the ref id? - erlang:monitor(process, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid, - state = started}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] -> - error_report(Profile, - "unexpected (started) message for handler (~p) in state " - "~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]), - ?hcri("received unexpected started message", [{state, State}]), - ok; - - [] -> - error_report(Profile, - "unknown handler ~p (~p) started for request ~w - canceling", - [HandlerPid, StarterPid, ReqId]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When that process terminates it sends -%% one of two messages. These ara handled by the two functions -%% below. -%% - -handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = started} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for started handler", []), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid, - state = operational}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = canceled} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for canceled handler", []), - httpc_handler:cancel(ReqId, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] when Result =/= ok -> - error_report(Profile, - "handler (~p, ~w) failed to connect and/or " - "send request ~p" - "~n Result: ~p", - [HandlerPid, State, ReqId, Result]), - ?hcri("received connect-and-send error", - [{result, Result}, {state, State}]), - %% We don't need to send a response to the original caller - %% because the handler already sent one in its terminate - %% function. - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - ?hcri("handler successfully started " - "for unknown request => canceling", - [{profile, Profile}, - {handler, HandlerPid}, - {request, ReqId}]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -handle_failed_starting_handler(_StarterPid, ReqId, Error, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = canceled}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - request_canceled(Profile, ReqId), % Fake signal from handler - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{from = From}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - Reason2 = - case Error of - {error, Reason} -> - {failed_connecting, Reason}; - _ -> - {failed_connecting, Error} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - error_report(Profile, - "failed starting handler for unknown request ~p" - "~n Error: ~p", [ReqId, Error]), - ok - end. - - -maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) -> - Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'}, - case ets:match_object(HandlerDb, Pattern) of - [#handler_info{id = ReqId, from = From, state = initiating}] -> - %% The starter process crashed before it could start the - %% the handler process, therefor we need to answer the - %% original caller. - ?hcri("starter process crashed bfore starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}]), - Reason2 = - case Reason of - {error, Error} -> - {failed_connecting, Error}; - _ -> - {failed_connecting, Reason} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{state = State} = HandlerInfo] -> - %% The starter process crashed after the handler was started. - %% The handler will answer to the original caller. - ?hcri("starter process crashed after starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - _ -> - ok - end. - - -%% ----- -%% Act as an HTTP/0.9 client that does not know anything -%% about persistent connections handle_request(#request{settings = - #http_options{version = "HTTP/0.9"}} = Request0, + #http_options{version = "HTTP/0.9"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = undefined}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - -%% ----- -%% Act as an HTTP/1.0 client that does not -%% use persistent connections + %% Act as an HTTP/0.9 client that does not know anything + %% about persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = undefined}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + handle_request(#request{settings = - #http_options{version = "HTTP/1.0"}} = Request0, + #http_options{version = "HTTP/1.0"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - - -%% ----- -handle_request(#request{method = Method, - address = Address, - scheme = Scheme} = Request0, - #state{options = Opts} = State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - SessionType = session_type(Opts), - case select_session(Method, Address, Scheme, SessionType, State) of + %% Act as an HTTP/1.0 client that does not + %% use persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + +handle_request(Request, State = #state{options = Options}) -> + + NewRequest = handle_cookies(generate_request_id(Request), State), + SessionType = session_type(Options), + case select_session(Request#request.method, + Request#request.address, + Request#request.scheme, SessionType, State) of {ok, HandlerPid} -> - pipeline_or_keep_alive(Request1, HandlerPid, State); + pipeline_or_keep_alive(NewRequest, HandlerPid, State); no_connection -> - create_handler_starter(Request1, State); - {no_session, OpenSessions} - when OpenSessions < Opts#options.max_sessions -> - create_handler_starter(Request1, State); + start_handler(NewRequest, State); + {no_session, OpenSessions} when OpenSessions + < Options#options.max_sessions -> + start_handler(NewRequest, State); {no_session, _} -> %% Do not start any more persistent connections %% towards this server. - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State) + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State) end, - {ok, Request1#request.id, State}. + {reply, {ok, NewRequest#request.id}, State}. + + +start_handler(Request, State) -> + {ok, Pid} = + case is_inets_manager() of + true -> + httpc_handler_sup:start_child([whereis(httpc_handler_sup), + Request, State#state.options, + State#state.profile_name]); + false -> + httpc_handler:start_link(self(), Request, State#state.options, + State#state.profile_name) + end, + ets:insert(State#state.handler_db, {Request#request.id, + Pid, Request#request.from}), + erlang:monitor(process, Pid). select_session(Method, HostPort, Scheme, SessionType, #state{options = #options{max_pipeline_length = MaxPipe, max_keep_alive_length = MaxKeepAlive}, session_db = SessionDb}) -> - ?hcrd("select session", [{session_type, SessionType}, - {max_pipeline_length, MaxPipe}, + ?hcrd("select session", [{session_type, SessionType}, + {max_pipeline_length, MaxPipe}, {max_keep_alive_length, MaxKeepAlive}]), case httpc_request:is_idempotent(Method) orelse (SessionType =:= keep_alive) of @@ -918,92 +709,17 @@ select_session(Candidates, Max) -> ?hcrd("select session - found one", [{handler, HandlerPid}]), {ok, HandlerPid} end. - -pipeline_or_keep_alive(#request{id = Id} = Request, HandlerPid, State) -> - ?hcrd("pipeline of keep-alive", [{id, Id}, {handler, HandlerPid}]), + +pipeline_or_keep_alive(Request, HandlerPid, State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ?hcrd("pipeline or keep-alive - successfully sent", []), - Entry = #handler_info{id = Id, - handler = HandlerPid, - state = operational}, - ets:insert(State#state.handler_db, Entry); - - _ -> %% timeout pipelining failed - ?hcrd("pipeline or keep-alive - failed sending -> " - "start a new handler", []), - create_handler_starter(Request, State) + ets:insert(State#state.handler_db, {Request#request.id, + HandlerPid, + Request#request.from}); + _ -> %timeout pipelining failed + start_handler(Request, State) end. - -create_handler_starter(#request{socket_opts = SocketOpts} = Request, - #state{options = Options} = State) - when is_list(SocketOpts) -> - %% The user provided us with (override) socket options - ?hcrt("create handler starter", [{socket_opts, SocketOpts}, {options, Options}]), - Options2 = Options#options{socket_opts = SocketOpts}, - create_handler_starter(Request#request{socket_opts = undefined}, - State#state{options = Options2}); - -create_handler_starter(#request{id = Id, - from = From} = Request, - #state{profile_name = ProfileName, - options = Options, - handler_db = HandlerDb} = _State) -> - ?hcrv("create handler starter", [{id, Id}, {profile, ProfileName}]), - IsInetsManager = is_inets_manager(), - ManagerPid = self(), - StarterFun = - fun() -> - ?hcrd("handler starter - start", - [{id, Id}, - {profile, ProfileName}, - {inets_manager, IsInetsManager}]), - Result1 = - case IsInetsManager of - true -> - httpc_handler_sup:start_child(Options, - ProfileName); - false -> - httpc_handler:start_link(Options, - ProfileName) - end, - ?hcrd("handler starter - maybe connect and send", - [{id, Id}, {profile, ProfileName}, {result, Result1}]), - case Result1 of - {ok, HandlerPid} -> - StartedMessage = - {started, self(), Id, HandlerPid}, - ManagerPid ! StartedMessage, - Result2 = httpc_handler:connect_and_send(Request, - HandlerPid), - ?hcrd("handler starter - connected and sent", - [{id, Id}, {profile, ProfileName}, - {handler, HandlerPid}, {result, Result2}]), - ConnAndSendMessage = - {connect_and_send, - self(), Id, HandlerPid, Result2}, - ManagerPid ! ConnAndSendMessage; - {error, Reason} -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Reason}, - ManagerPid ! StartFailureMessage; - _ -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Result1}, - ManagerPid ! StartFailureMessage - end - end, - Starter = erlang:spawn_link(StarterFun), - ?hcrd("create handler starter - started", [{id, Id}, {starter, Starter}]), - Entry = #handler_info{id = Id, - starter = Starter, - from = From, - state = initiating}, - ets:insert(HandlerDb, Entry), - ok. - - is_inets_manager() -> case get('$ancestors') of [httpc_profile_sup | _] -> @@ -1045,8 +761,6 @@ do_store_cookies([Cookie | Cookies], #state{cookie_db = CookieDb} = State) -> ok = httpc_cookie:insert(CookieDb, Cookie), do_store_cookies(Cookies, State). - - session_db_name(ProfileName) -> make_db_name(ProfileName, "__session_db"). @@ -1074,7 +788,6 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). - get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). @@ -1133,20 +846,6 @@ handle_verbose(trace) -> handle_verbose(_) -> ok. - error_report(Profile, F, A) -> Report = io_lib:format("HTTPC-MANAGER<~p> " ++ F ++ "~n", [Profile | A]), error_logger:error_report(Report). - - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index 0d602adb11..879053f0f2 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -120,7 +120,7 @@ do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> version(Version), ?CRLF, headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - http_transport:send(SocketType, Socket, lists:append(Message)). + http_transport:send(SocketType, Socket, Message). do_send_body(SocketType, Socket, ProcessBody, Acc) -> @@ -128,9 +128,7 @@ do_send_body(SocketType, Socket, ProcessBody, Acc) -> eof -> ok; {ok, Data, NewAcc} -> - DataBin = iolist_to_binary(Data), - ?hcrd("send", [{data, DataBin}]), - case http_transport:send(SocketType, Socket, DataBin) of + case http_transport:send(SocketType, Socket, Data) of ok -> do_send_body(SocketType, Socket, ProcessBody, NewAcc); Error -> diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 0a590c9c36..91fb064eec 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,38 +20,12 @@ [ {"5.5.2", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + {restart_application, inets} ] }, {"5.5.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_chunk, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, - [httpc_request, http_chunk]} + {restart_application, inets} ] }, {"5.5", @@ -68,38 +42,12 @@ [ {"5.5.2", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + {restart_application, inets} ] }, {"5.5.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_chunk, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, - [httpc_request, http_chunk]} + {restart_application, inets} ] }, {"5.5", diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 81e9c2b230..7607bc9eb6 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -115,7 +115,8 @@ all() -> options, ipv6, headers_as_is, - {group, tickets} + {group, tickets}, + initial_server_connect ]. groups() -> @@ -140,6 +141,7 @@ groups() -> otp_8106_fun, otp_8106_mfa]}]. + init_per_group(_GroupName, Config) -> Config. @@ -199,7 +201,6 @@ end_per_suite(Config) -> application:stop(ssl), ok. - %%-------------------------------------------------------------------- %% Function: init_per_testcase(Case, Config) -> Config %% Case - atom() @@ -214,6 +215,15 @@ end_per_suite(Config) -> %%-------------------------------------------------------------------- init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); + +init_per_testcase(initial_server_connect, Config) -> + inets:start(), + application:start(crypto), + application:start(public_key), + application:start(ssl), + application:start(inets), + Config; + init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). @@ -601,34 +611,35 @@ http_inets_pipe(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. + test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1"), + {ok, RequestId1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + p("test_pipeline -> RequestId1: ~p", [RequestId1]), - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + p("test_pipeline -> issue (async) request 2"), + {ok, RequestId2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), + tsp("RequestId2: ~p", [RequestId2]), + p("test_pipeline -> RequestId2: ~p", [RequestId2]), - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive + receive {http, {RequestId1, {{_, 200, _}, _, _}}} -> p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), receive @@ -646,46 +657,46 @@ test_pipeline(URL) -> ok; {http, Msg2} -> test_server:fail(Msg2) - end; + end; {http, Msg3} -> test_server:fail(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - test_server:fail({error, {timeout, Any1}}) - end + after 60000 -> + receive Any1 -> + tsp("received crap after timeout: ~n ~p", [Any1]), + test_server:fail({error, {timeout, Any1}}) + end end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), + p("test_pipeline -> sleep some"), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + p("test_pipeline -> issue (async) request 4"), + {ok, RequestId3} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - test_server:fail(http_cancel_request_failed) - after 3000 -> - ok - end, + tsp("RequestId3: ~p", [RequestId3]), + p("test_pipeline -> RequestId3: ~p", [RequestId3]), - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> + p("test_pipeline -> issue (async) request 5"), + {ok, RequestId4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId4: ~p~n", [RequestId4]), + p("test_pipeline -> RequestId4: ~p", [RequestId4]), + + p("test_pipeline -> cancel (async) request 4"), + ok = httpc:cancel_request(RequestId3), + + p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request 4"), + Body = + receive + {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> p("test_pipeline -> received reply for (async) request 5"), tsp("Receive : ~p", [Res]), BinBody4; @@ -700,9 +711,9 @@ test_pipeline(URL) -> p("test_pipeline -> check reply for (async) request 5"), inets_test_lib:check_body(binary_to_list(Body)), - + p("test_pipeline -> ensure no unexpected incomming"), - receive + receive {http, Any} -> test_server:fail({unexpected_message, Any}) after 500 -> @@ -712,8 +723,6 @@ test_pipeline(URL) -> p("test_pipeline -> done"), ok. - - %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1675,25 +1684,11 @@ proxy_https_not_supported(suite) -> proxy_https_not_supported(Config) when is_list(Config) -> Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []), case Result of - {error, Reason} -> - %% ok so far - case Reason of - {failed_connecting, Why} -> - %% ok, now check why - case Why of - https_through_proxy_is_not_currently_supported -> - ok; - _ -> - tsf({unexpected_why, Why}) - end; - _ -> - tsf({unexpected_reason, Reason}) - end; + {error, https_through_proxy_is_not_currently_supported} -> + ok; _ -> - tsf({unexpected_result, Result}) - end, - ok. - + tsf({unexpected_reason, Result}) + end. %%------------------------------------------------------------------------- @@ -2446,7 +2441,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> {skip, "Failed to start local http-server"} - end. + end. otp_8106_mfa(doc) -> @@ -2672,7 +2667,7 @@ otp_8739(Config) when is_list(Config) -> Request = {URL, []}, HttpOptions = [{connect_timeout, 500}, {timeout, 1}], Options = [{sync, true}], - case http:request(Method, Request, HttpOptions, Options) of + case httpc:request(Method, Request, HttpOptions, Options) of {error, timeout} -> %% And now we check the size of the handler db Info = httpc:info(), @@ -2729,7 +2724,31 @@ otp_8739_dummy_server_main(_Parent, ListenSocket) -> exit(Error) end. - +%%------------------------------------------------------------------------- + +initial_server_connect(doc) -> + ["If this test cases times out the init of httpc_handler process is" + "blocking the manager/client process (implementation dependent which) but nither" + "should be blocked."]; +initial_server_connect(suite) -> + []; +initial_server_connect(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + ok = httpc:set_options([{ipfamily, inet}]), + + CertFile = filename:join(DataDir, "ssl_server_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + + {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions), + + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html", + + httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]), + + [{session_cookies,[]}] = httpc:which_cookies(), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]). %%-------------------------------------------------------------------- %% Internal functions @@ -3242,11 +3261,9 @@ pick_header(Headers, Name) -> Val end. - not_implemented_yet() -> exit(not_implemented_yet). - p(F) -> p(F, []). @@ -3260,3 +3277,37 @@ tsp(F, A) -> tsf(Reason) -> test_server:fail(Reason). + + +dummy_ssl_server_hang(Caller, IpV, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + ssl:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt); + ipv6 -> + ssl:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt) + end, + {ok, {_,Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + dummy_ssl_server_hang_loop(AcceptSocket). + +dummy_ssl_server_hang_loop(_) -> + %% Do not do ssl:ssl_accept as we + %% want to time out the underlying gen_tcp:connect + receive + stop -> + ok + end. -- cgit v1.2.3 From 416668b76a84d39855b8d78b84838b8a80f40db9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 16:55:21 +0200 Subject: Also updated release notes. --- lib/inets/doc/src/notes.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index b885bcbcdb..edb994a91b 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -104,6 +104,18 @@

*** POTENTIAL INCOMPATIBILITY ***

+ +

[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash.

+

This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions.

+

Own Id: OTP-9246

+
+
-- cgit v1.2.3 From d3d5b4fcf3e07c22e61c2c9a410d365178b12945 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 May 2011 14:06:12 +0200 Subject: Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in an exit with reason badarg. Neither SSL nor INETS catches this, resulting in crashes with incomprehensible reasons. OTP-9289 --- lib/inets/doc/src/notes.xml | 37 +++++++++++++++++++++---------- lib/inets/src/http_lib/http_transport.erl | 30 ++++++++++++++++++++++--- lib/inets/src/inets_app/inets.appup.src | 8 +++---- lib/ssl/src/ssl.erl | 4 +++- lib/ssl/vsn.mk | 2 +- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index edb994a91b..0926df8581 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -104,18 +104,6 @@

*** POTENTIAL INCOMPATIBILITY ***

- -

[httpc] httpc manager crashes. - When a request results in a retry, the request id will be "reused" - in the previous implementation a race condition could occur causing - the manager to crash.

-

This is now avoided by using proc_lib:init_ack and - gen_server:enter_loop to allow mor advanced initialization of - httpc_handlers without blocking the httpc_manger and eliminating - extra processes that can cause race conditions.

-

Own Id: OTP-9246

-
-
@@ -147,6 +135,31 @@

Bernard Duggan

Own Id: OTP-9158

+ + +

[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash.

+

This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions.

+

Own Id: OTP-9246

+
+ + +

[httpc] Issuing a request (httpc:request) to an + host with the ssl option + {ip, {127,0,0,1}} results in an handler crash. + The reason was that the connect call resulted in an exit with + reason badarg + (this was the same for both ssl and gen_tcp).

+

Exits was not catched. This has now been improved.

+

Own Id: OTP-9289

+

Aux Id: seq11845

+
+
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 173911b868..8cabfe3c71 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -110,7 +110,17 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], ?hlrt("connect using gen_tcp", [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]), - gen_tcp:connect(Host, Port, Opts, Timeout); + try gen_tcp:connect(Host, Port, Opts, Timeout) of + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + catch + exit:{badarg, _} -> + {error, {eoptions, Opts}}; + exit:badarg -> + {error, {eoptions, Opts}} + end; %% Wrapper for backaward compatibillity connect({ssl, SslConfig}, Address, Opts, Timeout) -> @@ -123,7 +133,14 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout); + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end; connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, @@ -132,7 +149,14 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout). + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end. %%------------------------------------------------------------------------- diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 91fb064eec..47f3fbba58 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,12 +20,12 @@ [ {"5.5.2", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5.1", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5", @@ -42,12 +42,12 @@ [ {"5.5.2", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5.1", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5", diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7b1fda4cf9..38877ece7e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -612,8 +612,10 @@ do_new_connect(Address, Port, catch exit:{function_clause, _} -> {error, {eoptions, {cb_info, CbInfo}}}; + exit:badarg -> + {error, {eoptions, {inet_options, UserOpts}}}; exit:{badarg, _} -> - {error,{eoptions, {inet_options, UserOpts}}} + {error, {eoptions, {inet_options, UserOpts}}} end. old_connect(Address, Port, Options, Timeout) -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 2f1edfa186..0e80e42637 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 4.1.4 +SSL_VSN = 4.1.5 -- cgit v1.2.3 From 30919cea3c641148389a46e94af20805e55f684c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 May 2011 14:15:37 +0200 Subject: Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in an exit with reason badarg. Neither SSL nor INETS catches this, resulting in crashes with incomprehensible reasons. OTP-9289 --- lib/ssl/doc/src/notes.xml | 84 ++++++++++++++++++++--------------------------- lib/ssl/src/ssl.appup.src | 6 ++-- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 52ee9c086a..b2d17925fd 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -28,59 +28,47 @@ G notes.xml -

This document describes the changes made to the SSL application. -

- -
SSL 4.1.4 - +

This document describes the changes made to the SSL application.

+ +
+ SSL 4.1.5 +
Improvements and New Features - - -

- Reduced memory footprint of an ssl connection.

-

- Handshake hashes, premaster secret and "public_key_info" - does not need to be saved when the connection has been - established. The own certificate is no longer duplicated - in the state.

-

- Own Id: OTP-9021

-
- -

- Add the option {hibernate_after, int()} to ssl:connect - and ssl:listen

-

- Own Id: OTP-9106

-
-
+ + +

Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in + an exit with reason badarg. Neither SSL nor INETS This was not + catched, resulting in crashes with incomprehensible reasons.

+

Own Id: OTP-9289 Aux Id: seq11845

+
+
- -
- -
SSL 4.1.3 - + +
+ +
+ SSL 4.1.3 +
Fixed Bugs and Malfunctions - - -

- Fixed error in cache-handling fix from ssl-4.1.2

-

- Own Id: OTP-9018 Aux Id: seq11739

-
- -

- Verification of a critical extended_key_usage-extension - corrected

-

- Own Id: OTP-9029 Aux Id: seq11541

-
-
+ + +

+ Fixed error in cache-handling fix from ssl-4.1.2

+

+ Own Id: OTP-9018 Aux Id: seq11739

+
+ +

Verification of a critical extended_key_usage-extension + corrected

+

Own Id: OTP-9029 Aux Id: seq11541

+
+
-
+
-
SSL 4.1.2 +
+ SSL 4.1.2
Fixed Bugs and Malfunctions diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index d3e426f254..cf8867245b 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,17 +1,19 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.1.4", [{restart_application, ssl}]}, {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ + {"4.1.4", [{restart_application, ssl}]}, {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. -- cgit v1.2.3 From 8c9edd9c00142d0622beb74ef852c79871a631a6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 May 2011 15:52:03 +0200 Subject: Fixit init per tescase for testcase for initial_server_connect. For this case to work, we need crypto! --- lib/inets/test/httpc_SUITE.erl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 7607bc9eb6..1998bd3950 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -217,12 +217,17 @@ init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); init_per_testcase(initial_server_connect, Config) -> - inets:start(), - application:start(crypto), - application:start(public_key), - application:start(ssl), - application:start(inets), - Config; + %% Try to check if crypto actually exist or not, + %% this test case does not work unless it does + case (catch crypto:start()) of + ok -> + application:start(public_key), + application:start(ssl), + inets:start(), + Config; + _ -> + {skip,"Could not start crypto"} + end; init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). -- cgit v1.2.3