aboutsummaryrefslogtreecommitdiffstats
path: root/lib/inets/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/inets/src')
-rw-r--r--lib/inets/src/ftp/ftp.erl127
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl18
-rw-r--r--lib/inets/src/http_server/httpd_example.erl8
-rw-r--r--lib/inets/src/http_server/httpd_file.erl3
-rw-r--r--lib/inets/src/http_server/httpd_response.erl8
-rw-r--r--lib/inets/src/http_server/mod_esi.erl15
6 files changed, 118 insertions, 61 deletions
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index e0430654eb..ffcccdefea 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -94,6 +94,9 @@
%% data needed further on.
caller = undefined, % term()
ipfamily, % inet | inet6 | inet6fb4
+ sockopts_ctrl = [],
+ sockopts_data_passive = [],
+ sockopts_data_active = [],
progress = ignore, % ignore | pid()
dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity
tls_upgrading_data_connection = false,
@@ -138,9 +141,10 @@ open({option_list, Options}) when is_list(Options) ->
try
{ok, StartOptions} = start_options(Options),
{ok, OpenOptions} = open_options(Options),
+ {ok, SockOpts} = socket_options(Options),
case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
{ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
+ call(Pid, {open, ip_comm, OpenOptions, SockOpts}, plain);
Error1 ->
Error1
end
@@ -168,9 +172,11 @@ open(Host, Opts) when is_list(Opts) ->
?fcrt("open", [{start_options, StartOptions}]),
{ok, OpenOptions} = open_options([{host, Host}|Opts]),
?fcrt("open", [{open_options, OpenOptions}]),
+ {ok, SocketOptions} = socket_options(Opts),
+ ?fcrt("open", [{socket_options, SocketOptions}]),
case start_link(StartOptions, []) of
{ok, Pid} ->
- do_open(Pid, OpenOptions, tls_options(Opts));
+ do_open(Pid, OpenOptions, SocketOptions, tls_options(Opts));
Error1 ->
?fcrt("open - error", [{error1, Error1}]),
Error1
@@ -181,8 +187,8 @@ open(Host, Opts) when is_list(Opts) ->
Error2
end.
-do_open(Pid, OpenOptions, TLSOpts) ->
- case call(Pid, {open, ip_comm, OpenOptions}, plain) of
+do_open(Pid, OpenOptions, SocketOptions, TLSOpts) ->
+ case call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain) of
{ok, Pid} ->
maybe_tls_upgrade(Pid, TLSOpts);
Error ->
@@ -879,9 +885,10 @@ start_standalone(Options) ->
try
{ok, StartOptions} = start_options(Options),
{ok, OpenOptions} = open_options(Options),
+ {ok, SocketOptions} = socket_options(Options),
case start_link(StartOptions, []) of
{ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
+ call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain);
Error1 ->
Error1
end
@@ -893,10 +900,11 @@ start_standalone(Options) ->
start_service(Options) ->
try
{ok, StartOptions} = start_options(Options),
- {ok, OpenOptions} = open_options(Options),
+ {ok, OpenOptions} = open_options(Options),
+ {ok, SocketOptions} = socket_options(Options),
case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of
{ok, Pid} ->
- call(Pid, {open, ip_comm, OpenOptions}, plain);
+ call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain);
Error1 ->
Error1
end
@@ -1041,6 +1049,34 @@ open_options(Options) ->
{ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
validate_options(Options, ValidOptions, []).
+
+
+socket_options(Options) ->
+ CtrlOpts = proplists:get_value(sock_ctrl, Options, []),
+ DataActOpts = proplists:get_value(sock_data_act, Options, CtrlOpts),
+ DataPassOpts = proplists:get_value(sock_data_pass, Options, CtrlOpts),
+ case [O || O <- lists:usort(CtrlOpts++DataPassOpts++DataActOpts),
+ not valid_socket_option(O)] of
+ [] ->
+ {ok, {CtrlOpts, DataPassOpts, DataActOpts}};
+ Invalid ->
+ throw({error,{sock_opts,Invalid}})
+ end.
+
+
+valid_socket_option(inet ) -> false;
+valid_socket_option(inet6 ) -> false;
+valid_socket_option({ipv6_v6only, _}) -> false;
+valid_socket_option({active,_} ) -> false;
+valid_socket_option({packet,_} ) -> false;
+valid_socket_option({mode,_} ) -> false;
+valid_socket_option(binary ) -> false;
+valid_socket_option(list ) -> false;
+valid_socket_option({header,_} ) -> false;
+valid_socket_option({packet_size,_} ) -> false;
+valid_socket_option(_) -> true.
+
+
tls_options(Options) ->
%% Options will be validated by ssl application
proplists:get_value(tls, Options, undefined).
@@ -1182,7 +1218,7 @@ handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = Sta
handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid ->
{reply, {error, not_connection_owner}, State};
-handle_call({_, {open, ip_comm, Opts}}, From, State) ->
+handle_call({_, {open, ip_comm, Opts, {CtrlOpts, DataPassOpts, DataActOpts}}}, From, State) ->
?fcrd("handle_call(open)", [{opts, Opts}]),
case key_search(host, Opts, undefined) of
undefined ->
@@ -1200,6 +1236,9 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
mode = Mode,
progress = progress(Progress),
ipfamily = IpFamily,
+ sockopts_ctrl = CtrlOpts,
+ sockopts_data_passive = DataPassOpts,
+ sockopts_data_active = DataActOpts,
dtimeout = DTimeout,
ftp_extension = FtpExt},
@@ -1218,28 +1257,6 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
end
end;
-handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
- Mode = key_search(mode, Opts, ?DEFAULT_MODE),
- Port = key_search(port, Opts, ?FTP_PORT),
- Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
- DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
- Progress = key_search(progress, Opts, ignore),
- FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
-
- State2 = State#state{client = From,
- mode = Mode,
- progress = progress(Progress),
- dtimeout = DTimeout,
- ftp_extension = FtpExt},
-
- case setup_ctrl_connection(Host, Port, Timeout, State2) of
- {ok, State3, WaitTimeout} ->
- {noreply, State3, WaitTimeout};
- {error, _Reason} ->
- gen_server:reply(From, {error, ehost}),
- {stop, normal, State2#state{client = undefined}}
- end;
-
handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) ->
send_ctrl_message(State, mk_cmd("AUTH TLS", [])),
activate_ctrl_connection(State),
@@ -1822,11 +1839,12 @@ handle_ctrl_result({pos_compl, Lines},
client = From,
caller = {setup_data_connection, Caller},
csock = CSock,
+ sockopts_data_passive = SockOpts,
timeout = Timeout}
= State) ->
[_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
{ok, {IP, _}} = peername(CSock),
- case connect(IP, list_to_integer(PortStr), Timeout, State) of
+ case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of
{ok, _, Socket} ->
handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
{error, _Reason} = Error ->
@@ -1839,7 +1857,8 @@ handle_ctrl_result({pos_compl, Lines},
ipfamily = inet,
client = From,
caller = {setup_data_connection, Caller},
- timeout = Timeout,
+ timeout = Timeout,
+ sockopts_data_passive = SockOpts,
ftp_extension = false} = State) ->
{_, [?LEFT_PAREN | Rest]} =
@@ -1853,7 +1872,7 @@ handle_ctrl_result({pos_compl, Lines},
Port = (P1 * 256) + P2,
?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]),
- case connect(IP, Port, Timeout, State) of
+ case connect(IP, Port, SockOpts, Timeout, State) of
{ok, _, Socket} ->
handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}});
{error, _Reason} = Error ->
@@ -1868,13 +1887,14 @@ handle_ctrl_result({pos_compl, Lines},
caller = {setup_data_connection, Caller},
csock = CSock,
timeout = Timeout,
+ sockopts_data_passive = SockOpts,
ftp_extension = true} = State) ->
[_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
{ok, {IP, _}} = peername(CSock),
?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]),
- case connect(IP, list_to_integer(PortStr), Timeout, State) of
+ case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of
{ok, _, Socket} ->
handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
{error, _Reason} = Error ->
@@ -2238,9 +2258,9 @@ handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} =
%% Connect to FTP server at Host (default is TCP port 21)
%% in order to establish a control connection.
-setup_ctrl_connection(Host, Port, Timeout, State) ->
+setup_ctrl_connection(Host, Port, Timeout, #state{sockopts_ctrl = SockOpts} = State) ->
MsTime = erlang:monotonic_time(),
- case connect(Host, Port, Timeout, State) of
+ case connect(Host, Port, SockOpts, Timeout, State) of
{ok, IpFam, CSock} ->
NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam},
activate_ctrl_connection(NewState),
@@ -2258,12 +2278,15 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
setup_data_connection(#state{mode = active,
caller = Caller,
csock = CSock,
+ sockopts_data_active = SockOpts,
ftp_extension = FtpExt} = State) ->
case (catch sockname(CSock)) of
- {ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->
+ {ok, {{_, _, _, _, _, _, _, _} = IP0, _}} ->
+ IP = proplists:get_value(ip, SockOpts, IP0),
{ok, LSock} =
gen_tcp:listen(0, [{ip, IP}, {active, false},
- inet6, binary, {packet, 0}]),
+ inet6, binary, {packet, 0} |
+ lists:keydelete(ip,1,SockOpts)]),
{ok, {_, Port}} = sockname({tcp,LSock}),
IpAddress = inet_parse:ntoa(IP),
Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]),
@@ -2271,9 +2294,11 @@ setup_data_connection(#state{mode = active,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}};
- {ok, {{_,_,_,_} = IP, _}} ->
+ {ok, {{_,_,_,_} = IP0, _}} ->
+ IP = proplists:get_value(ip, SockOpts, IP0),
{ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},
- binary, {packet, 0}]),
+ binary, {packet, 0} |
+ lists:keydelete(ip,1,SockOpts)]),
{ok, Port} = inet:port(LSock),
case FtpExt of
false ->
@@ -2312,41 +2337,41 @@ setup_data_connection(#state{mode = passive, ipfamily = inet,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}}.
-connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) ->
- connect2(Host, Port, IpFam, Timeout);
+connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet = IpFam}) ->
+ connect2(Host, Port, IpFam, SockOpts, Timeout);
-connect(Host, Port, Timeout, #state{ipfamily = inet6 = IpFam}) ->
- connect2(Host, Port, IpFam, Timeout);
+connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6 = IpFam}) ->
+ connect2(Host, Port, IpFam, SockOpts, Timeout);
-connect(Host, Port, Timeout, #state{ipfamily = inet6fb4}) ->
+connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6fb4}) ->
case inet:getaddr(Host, inet6) of
{ok, {0, 0, 0, 0, 0, 16#ffff, _, _} = IPv6} ->
case inet:getaddr(Host, inet) of
{ok, IPv4} ->
IpFam = inet,
- connect2(IPv4, Port, IpFam, Timeout);
+ connect2(IPv4, Port, IpFam, SockOpts, Timeout);
_ ->
IpFam = inet6,
- connect2(IPv6, Port, IpFam, Timeout)
+ connect2(IPv6, Port, IpFam, SockOpts, Timeout)
end;
{ok, IPv6} ->
IpFam = inet6,
- connect2(IPv6, Port, IpFam, Timeout);
+ connect2(IPv6, Port, IpFam, SockOpts, Timeout);
_ ->
case inet:getaddr(Host, inet) of
{ok, IPv4} ->
IpFam = inet,
- connect2(IPv4, Port, IpFam, Timeout);
+ connect2(IPv4, Port, IpFam, SockOpts, Timeout);
Error ->
Error
end
end.
-connect2(Host, Port, IpFam, Timeout) ->
- Opts = [IpFam, binary, {packet, 0}, {active, false}],
+connect2(Host, Port, IpFam, SockOpts, Timeout) ->
+ Opts = [IpFam, binary, {packet, 0}, {active, false} | SockOpts],
case gen_tcp:connect(Host, Port, Opts, Timeout) of
{ok, Sock} ->
{ok, IpFam, Sock};
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 26e4f4e699..1a2ce277bf 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -980,13 +980,23 @@ handle_http_body(_, #state{status = {ssl_tunnel, Request},
NewState = answer_request(Request, ClientErrMsg, State),
{stop, normal, NewState};
-handle_http_body(<<>>, #state{status_line = {_,304, _}} = State) ->
+%% All 1xx (informational), 204 (no content), and 304 (not modified)
+%% responses MUST NOT include a message-body, and thus are always
+%% terminated by the first empty line after the header fields.
+%% This implies that chunked encoding MUST NOT be used for these
+%% status codes.
+handle_http_body(<<>>, #state{headers = Headers,
+ status_line = {_,StatusCode, _}} = State)
+ when Headers#http_response_h.'transfer-encoding' =/= "chunked" andalso
+ (StatusCode =:= 204 orelse %% No Content
+ StatusCode =:= 304 orelse %% Not Modified
+ 100 =< StatusCode andalso StatusCode =< 199) -> %% Informational
handle_response(State#state{body = <<>>});
-handle_http_body(<<>>, #state{status_line = {_,204, _}} = State) ->
- handle_response(State#state{body = <<>>});
-handle_http_body(<<>>, #state{request = #request{method = head}} = State) ->
+handle_http_body(<<>>, #state{headers = Headers,
+ request = #request{method = head}} = State)
+ when Headers#http_response_h.'transfer-encoding' =/= "chunked" ->
handle_response(State#state{body = <<>>});
handle_http_body(Body, #state{headers = Headers,
diff --git a/lib/inets/src/http_server/httpd_example.erl b/lib/inets/src/http_server/httpd_example.erl
index 47a8c48d01..dbf7c44ad3 100644
--- a/lib/inets/src/http_server/httpd_example.erl
+++ b/lib/inets/src/http_server/httpd_example.erl
@@ -22,7 +22,7 @@
-export([print/1]).
-export([get/2, put/2, post/2, yahoo/2, test1/2, get_bin/2, peer/2,new_status_and_location/2]).
--export([newformat/3, post_chunked/3]).
+-export([newformat/3, post_chunked/3, post_204/3]).
%% These are used by the inets test-suite
-export([delay/1, chunk_timeout/3]).
@@ -151,6 +151,12 @@ post_chunked(SessionID, _Env, {last, _Body, undefined} = _Bodychunk) ->
post_chunked(_, _, _Body) ->
exit(body_not_chunked).
+post_204(SessionID, _Env, _Input) ->
+ mod_esi:deliver(SessionID,
+ ["Status: 204 No Content" ++ "\r\n\r\n"]),
+ mod_esi:deliver(SessionID, []).
+
+
newformat(SessionID,_,_) ->
mod_esi:deliver(SessionID, "Content-Type:text/html\r\n\r\n"),
mod_esi:deliver(SessionID, top("new esi format test")),
diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl
index 4d419172d0..fb71834e95 100644
--- a/lib/inets/src/http_server/httpd_file.erl
+++ b/lib/inets/src/http_server/httpd_file.erl
@@ -33,6 +33,9 @@ handle_error(enoent, Op, ModData, Path) ->
handle_error(enotdir, Op, ModData, Path) ->
handle_error(404, Op, ModData, Path,
": A component of the file name is not a directory");
+handle_error(eisdir, Op, ModData, Path) ->
+ handle_error(403, Op, ModData, Path,
+ ":Ilegal operation expected a file not a directory");
handle_error(emfile, Op, _ModData, Path) ->
handle_error(500, Op, none, Path, ": Too many open files");
handle_error({enfile,_}, Op, _ModData, Path) ->
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 6b9053fda6..0bcdee1e16 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -61,8 +61,12 @@ generate_and_send_response(#mod{config_db = ConfigDB} = ModData) ->
{StatusCode, Response} -> %% Old way
send_response_old(ModData, StatusCode, Response),
ok;
- undefined ->
- send_status(ModData, 500, none),
+ undefined ->
+ %% Happens when no mod_*
+ %% handles the request
+ send_status(ModData, 501, {ModData#mod.method,
+ ModData#mod.request_uri,
+ ModData#mod.http_version}),
ok
end
end
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 3206d957d9..9fbedce6aa 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -394,7 +394,16 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
Continue;
{Headers, Body} ->
{ok, NewHeaders, StatusCode} = httpd_esi:handle_headers(Headers),
- IsDisableChunkedSend = httpd_response:is_disable_chunked_send(Db),
+ %% All 1xx (informational), 204 (no content), and 304 (not modified)
+ %% responses MUST NOT include a message-body, and thus are always
+ %% terminated by the first empty line after the header fields.
+ %% This implies that chunked encoding MUST NOT be used for these
+ %% status codes.
+ IsDisableChunkedSend =
+ httpd_response:is_disable_chunked_send(Db) orelse
+ StatusCode =:= 204 orelse %% No Content
+ StatusCode =:= 304 orelse %% Not Modified
+ (100 =< StatusCode andalso StatusCode =< 199), %% Informational
case (ModData#mod.http_version =/= "HTTP/1.1") or
(IsDisableChunkedSend) of
true ->
@@ -405,8 +414,8 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
send_headers(ModData, StatusCode,
[{"transfer-encoding",
"chunked"} | NewHeaders])
- end,
- handle_body(Pid, ModData, Body, Timeout, length(Body),
+ end,
+ handle_body(Pid, ModData, Body, Timeout, length(Body),
IsDisableChunkedSend);
timeout ->
send_headers(ModData, 504, [{"connection", "close"}]),