aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/kernel/src/file.erl142
-rw-r--r--lib/kernel/src/gen_tcp.erl146
-rw-r--r--lib/kernel/test/Makefile3
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl286
-rw-r--r--lib/kernel/test/sendfile_SUITE.erl291
5 files changed, 441 insertions, 427 deletions
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 706c60caaf..ef1d20b53b 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -51,6 +51,9 @@
-export([pid2name/1]).
+%% Sendfile functions
+-export([sendfile/2,sendfile/5]).
+
%%% Obsolete exported functions
-export([raw_read_file_info/1, raw_write_file_info/2]).
@@ -103,7 +106,10 @@
-type date_time() :: calendar:datetime().
-type posix_file_advise() :: 'normal' | 'sequential' | 'random'
| 'no_reuse' | 'will_need' | 'dont_need'.
-
+-type sendfile_option() :: {chunk_size, non_neg_integer()} |
+ {headers, Hdrs :: list(iodata())} |
+ {trailers, Tlrs :: list(iodata())} |
+ sf_nodiskio | sf_mnowait | sf_sync.
%%%-----------------------------------------------------------------
%%% General functions
@@ -1114,6 +1120,140 @@ change_time(Name, Atime, Mtime)
when is_tuple(Atime), is_tuple(Mtime) ->
write_file_info(Name, #file_info{atime=Atime, mtime=Mtime}).
+%%
+%% Send data using sendfile
+%%
+
+-define(MAX_CHUNK_SIZE, (1 bsl 20)*20). %% 20 MB, has to fit in primary memory
+
+-spec sendfile(File, Sock, Offset, Bytes, Opts) ->
+ {'ok', non_neg_integer()} | {'error', inet:posix() | badarg | not_owner} when
+ File :: file:fd(),
+ Sock :: inet:socket(),
+ Offset :: non_neg_integer(),
+ Bytes :: non_neg_integer(),
+ Opts :: [sendfile_option()].
+sendfile(File, _Sock, _Offet, _Bytes, _Opts) when is_pid(File) ->
+ {error, badarg};
+sendfile(File, Sock, Offset, Bytes, []) ->
+ sendfile(File, Sock, Offset, Bytes, ?MAX_CHUNK_SIZE, [], [],
+ false, false, false);
+sendfile(File, Sock, Offset, Bytes, Opts) ->
+ ChunkSize0 = proplists:get_value(chunk_size, Opts, ?MAX_CHUNK_SIZE),
+ ChunkSize = if ChunkSize0 > ?MAX_CHUNK_SIZE ->
+ ?MAX_CHUNK_SIZE;
+ true -> ChunkSize0
+ end,
+ Headers = proplists:get_value(headers, Opts, []),
+ Trailers = proplists:get_value(trailers, Opts, []),
+ sendfile(File, Sock, Offset, Bytes, ChunkSize, Headers, Trailers,
+ lists:member(sf_nodiskio,Opts),lists:member(sf_mnowait,Opts),
+ lists:member(sf_sync,Opts)).
+
+%% sendfile/2
+-spec sendfile(File, Sock) ->
+ {'ok', non_neg_integer()} | {'error', inet:posix() | badarg | not_owner}
+ when File :: file:name(),
+ Sock :: inet:socket().
+sendfile(File, Sock) ->
+ case file:open(File, [read, raw, binary]) of
+ {error, Reason} ->
+ {error, Reason};
+ {ok, Fd} ->
+ Res = sendfile(Fd, Sock, 0, 0, []),
+ file:close(Fd),
+ Res
+ end.
+
+%% Internal sendfile functions
+sendfile(#file_descriptor{ module = Mod } = Fd, Sock, Offset, Bytes,
+ ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync)
+ when is_port(Sock) ->
+ case Mod:sendfile(Fd, Sock, Offset, Bytes, ChunkSize, Headers, Trailers,
+ Nodiskio, MNowait, Sync) of
+ {error, enotsup} ->
+ sendfile_fallback(Fd, Sock, Offset, Bytes, ChunkSize,
+ Headers, Trailers);
+ Else ->
+ Else
+ end;
+sendfile(_,_,_,_,_,_,_,_,_,_) ->
+ {error, badarg}.
+
+%%%
+%% Sendfile Fallback
+%%%
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize,
+ Headers, Trailers)
+ when Headers == []; is_integer(Headers) ->
+ case sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) of
+ {ok, BytesSent} when is_list(Trailers),
+ Trailers =/= [],
+ is_integer(Headers) ->
+ sendfile_send(Sock, Trailers, BytesSent+Headers);
+ {ok, BytesSent} when is_list(Trailers), Trailers =/= [] ->
+ sendfile_send(Sock, Trailers, BytesSent);
+ {ok, BytesSent} when is_integer(Headers) ->
+ {ok, BytesSent + Headers};
+ Else ->
+ Else
+ end;
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, Headers, Trailers) ->
+ case sendfile_send(Sock, Headers, 0) of
+ {ok, BytesSent} ->
+ sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, BytesSent,
+ Trailers);
+ Else ->
+ Else
+ end.
+
+
+sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) ->
+ {ok, CurrPos} = file:position(File, {cur, 0}),
+ {ok, _NewPos} = file:position(File, {bof, Offset}),
+ Res = sendfile_fallback_int(File, Sock, Bytes, ChunkSize, 0),
+ file:position(File, {bof, CurrPos}),
+ Res.
+
+
+sendfile_fallback_int(File, Sock, Bytes, ChunkSize, BytesSent)
+ when Bytes > BytesSent; Bytes == 0 ->
+ Size = if Bytes == 0 ->
+ ChunkSize;
+ (Bytes - BytesSent + ChunkSize) > 0 ->
+ Bytes - BytesSent;
+ true ->
+ ChunkSize
+ end,
+ case file:read(File, Size) of
+ {ok, Data} ->
+ case sendfile_send(Sock, Data, BytesSent) of
+ {ok,NewBytesSent} ->
+ sendfile_fallback_int(
+ File, Sock, Bytes, ChunkSize,
+ NewBytesSent);
+ Error ->
+ Error
+ end;
+ eof ->
+ {ok, BytesSent};
+ Error ->
+ Error
+ end;
+sendfile_fallback_int(_File, _Sock, BytesSent, _ChunkSize, BytesSent) ->
+ {ok, BytesSent}.
+
+sendfile_send(Sock, Data, Old) ->
+ Len = iolist_size(Data),
+ case gen_tcp:send(Sock, Data) of
+ ok ->
+ {ok, Len+Old};
+ Else ->
+ Else
+ end.
+
+
+
%%%-----------------------------------------------------------------
%%% Helpers
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 78e3ab3697..4d6c7f5f1d 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -22,7 +22,7 @@
-export([connect/3, connect/4, listen/2, accept/1, accept/2,
shutdown/2, close/1]).
--export([send/2, recv/2, recv/3, unrecv/2, sendfile/2, sendfile/5]).
+-export([send/2, recv/2, recv/3, unrecv/2]).
-export([controlling_process/2]).
-export([fdopen/2]).
@@ -106,13 +106,8 @@
{tcp_module, module()} |
option().
-type socket() :: port().
--type sendfile_option() :: {chunk_size, non_neg_integer()} |
- {headers, Hdrs :: list(iodata())} |
- {trailers, Tlrs :: list(iodata())} |
- sf_nodiskio | sf_mnowait | sf_sync.
--export_type([option/0, option_name/0, connect_option/0, listen_option/0,
- sendfile_option/0]).
+-export_type([option/0, option_name/0, connect_option/0, listen_option/0]).
%%
%% Connect a socket
@@ -308,52 +303,6 @@ unrecv(S, Data) when is_port(S) ->
Mod:unrecv(S, Data);
Error ->
Error
- end.
-
-%%
-%% Send data using sendfile
-%%
-
--define(MAX_CHUNK_SIZE, (1 bsl 20)*20). %% 20 MB, has to fit in primary memory
-
--spec sendfile(File, Sock, Offset, Bytes, Opts) ->
- {'ok', non_neg_integer()} | {'error', inet:posix() } |
- {'error', not_owner} when
- File :: file:fd(),
- Sock :: socket(),
- Offset :: non_neg_integer(),
- Bytes :: non_neg_integer(),
- Opts :: [sendfile_option()].
-sendfile(File, _Sock, _Offet, _Bytes, _Opts) when is_pid(File) ->
- {error, badarg};
-sendfile(File, Sock, Offset, Bytes, []) ->
- sendfile(File, Sock, Offset, Bytes, ?MAX_CHUNK_SIZE, [], [],
- false, false, false);
-sendfile(File, Sock, Offset, Bytes, Opts) ->
- ChunkSize0 = proplists:get_value(chunk_size, Opts, ?MAX_CHUNK_SIZE),
- ChunkSize = if ChunkSize0 > ?MAX_CHUNK_SIZE ->
- ?MAX_CHUNK_SIZE;
- true -> ChunkSize0
- end,
- Headers = proplists:get_value(headers, Opts, []),
- Trailers = proplists:get_value(trailers, Opts, []),
- sendfile(File, Sock, Offset, Bytes, ChunkSize, Headers, Trailers,
- lists:member(sf_nodiskio,Opts),lists:member(sf_mnowait,Opts),
- lists:member(sf_sync,Opts)).
-
-%% sendfile/2
--spec sendfile(File, Sock) ->
- {'ok', non_neg_integer()} | {'error', inet:posix() | badarg}
- when File :: file:name(),
- Sock :: socket().
-sendfile(File, Sock) ->
- case file:open(File, [read, raw, binary]) of
- {error, Reason} ->
- {error, Reason};
- {ok, Fd} ->
- Res = sendfile(Fd, Sock, 0, 0, []),
- file:close(Fd),
- Res
end.
%%
@@ -407,94 +356,3 @@ mod([_|Opts], Address) ->
mod([], Address) ->
mod(Address).
-
-%% Internal sendfile functions
-sendfile(#file_descriptor{ module = Mod } = Fd, Sock, Offset, Bytes,
- ChunkSize, Headers, Trailers, Nodiskio, MNowait, Sync)
- when is_port(Sock) ->
- ok = inet:lock_socket(Sock,true),
- {ok, SockFd} = prim_inet:getfd(Sock),
- case Mod:sendfile(Fd, SockFd, Offset, Bytes, ChunkSize, Headers, Trailers,
- Nodiskio, MNowait, Sync) of
- {error, enotsup} ->
- ok = inet:lock_socket(Sock,false),
- sendfile_fallback(Fd, Sock, Offset, Bytes, ChunkSize,
- Headers, Trailers);
- Else ->
- ok = inet:lock_socket(Sock,false),
- Else
- end;
-sendfile(_,_,_,_,_,_,_,_,_,_) ->
- {error, badarg}.
-
-%%%
-%% Sendfile Fallback
-%%%
-sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize,
- Headers, Trailers)
- when Headers == []; is_integer(Headers) ->
- case sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) of
- {ok, BytesSent} when is_list(Trailers),
- Trailers =/= [],
- is_integer(Headers) ->
- sendfile_send(Sock, Trailers, BytesSent+Headers);
- {ok, BytesSent} when is_list(Trailers), Trailers =/= [] ->
- sendfile_send(Sock, Trailers, BytesSent);
- {ok, BytesSent} when is_integer(Headers) ->
- {ok, BytesSent + Headers};
- Else ->
- Else
- end;
-sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, Headers, Trailers) ->
- case sendfile_send(Sock, Headers, 0) of
- {ok, BytesSent} ->
- sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize, BytesSent,
- Trailers);
- Else ->
- Else
- end.
-
-
-sendfile_fallback(File, Sock, Offset, Bytes, ChunkSize) ->
- {ok, CurrPos} = file:position(File, {cur, 0}),
- {ok, _NewPos} = file:position(File, {bof, Offset}),
- Res = sendfile_fallback_int(File, Sock, Bytes, ChunkSize, 0),
- file:position(File, {bof, CurrPos}),
- Res.
-
-
-sendfile_fallback_int(File, Sock, Bytes, ChunkSize, BytesSent)
- when Bytes > BytesSent; Bytes == 0 ->
- Size = if Bytes == 0 ->
- ChunkSize;
- (Bytes - BytesSent + ChunkSize) > 0 ->
- Bytes - BytesSent;
- true ->
- ChunkSize
- end,
- case file:read(File, Size) of
- {ok, Data} ->
- case sendfile_send(Sock, Data, BytesSent) of
- {ok,NewBytesSent} ->
- sendfile_fallback_int(
- File, Sock, Bytes, ChunkSize,
- NewBytesSent);
- Error ->
- Error
- end;
- eof ->
- {ok, BytesSent};
- Error ->
- Error
- end;
-sendfile_fallback_int(_File, _Sock, BytesSent, _ChunkSize, BytesSent) ->
- {ok, BytesSent}.
-
-sendfile_send(Sock, Data, Old) ->
- Len = iolist_size(Data),
- case send(Sock, Data) of
- ok ->
- {ok, Len+Old};
- Else ->
- Else
- end.
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 82bc3fc6d1..5dcaad3f5e 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -74,7 +74,8 @@ MODULES= \
wrap_log_reader_SUITE \
cleanup \
zlib_SUITE \
- loose_node
+ loose_node \
+ sendfile_SUITE
APP_FILES = \
appinc.app \
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index d66caad2d8..a7af00c12a 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -24,7 +24,6 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/inet.hrl").
--include_lib("kernel/include/file.hrl").
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
@@ -33,42 +32,21 @@
t_connect_bad/1,
t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
- t_fdopen/1, t_implicit_inet6/1,
- t_sendfile_small/1, t_sendfile_big/1,
- t_sendfile_hdtl/1, t_sendfile_partial/1,
- t_sendfile_offset/1, t_sendfile_sendafter/1,
- t_sendfile_recvafter/1, t_sendfile_sendduring/1,
- t_sendfile_recvduring/1]).
-
--export([sendfile_server/2]).
+ t_fdopen/1, t_implicit_inet6/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_fdopen, t_implicit_inet6,{group,t_sendfile}].
+ t_fdopen, t_implicit_inet6].
groups() ->
[{t_accept, [], [t_accept_timeout]},
{t_connect, [], [t_connect_timeout, t_connect_bad]},
- {t_recv, [], [t_recv_timeout, t_recv_eof]},
- {t_sendfile, [], [{group, sendfile_all()}]}].
-
-sendfile_all() ->
- [
- t_sendfile_small
- ,t_sendfile_big
-% ,t_sendfile_hdtl
- ,t_sendfile_partial
- ,t_sendfile_offset
- ,t_sendfile_sendafter
- ,t_sendfile_recvafter
- ,t_sendfile_sendduring
- ,t_sendfile_recvduring
- ].
-% [t_sendfile_big].
-% [t_sendfile_small].
+ {t_recv, [], [t_recv_timeout, t_recv_eof]}].
+
+
init_per_suite(Config) ->
Config.
@@ -76,31 +54,12 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
-init_per_group(t_sendfile, Config) ->
- Priv = ?config(priv_dir, Config),
- SFilename = filename:join(Priv, "sendfile_small.html"),
- {ok, DS} = file:open(SFilename,[write,raw]),
- file:write(DS,"yo baby yo"),
- file:sync(DS),
- file:close(DS),
- BFilename = filename:join(Priv, "sendfile_big.html"),
- {ok, DB} = file:open(BFilename,[write,raw]),
- [file:write(DB,[<<0:(10*8*1024*1024)>>]) || _I <- lists:seq(1,51)],
- file:sync(DB),
- file:close(DB),
- [{small_file, SFilename},
- {file_opts,[raw,binary]},
- {big_file, BFilename}|Config];
init_per_group(_GroupName, Config) ->
Config.
-end_per_group(t_sendfile, Config) ->
- file:delete(proplists:get_value(big_file, Config));
end_per_group(_,_Config) ->
ok.
-
-
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
[{watchdog, Dog}|Config].
@@ -278,241 +237,6 @@ implicit_inet6(S, Addr) ->
?line ok = gen_tcp:close(S2),
?line ok = gen_tcp:close(S1).
-t_sendfile_small(Config) when is_list(Config) ->
- Filename = proplists:get_value(small_file, Config),
-
- Send = fun(Sock) ->
- {Size, Data} = sendfile_file_info(Filename),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- Data
- end,
-
- ok = sendfile_send(Send).
-
-t_sendfile_big(Config) when is_list(Config) ->
- Filename = proplists:get_value(big_file, Config),
-
- Send = fun(Sock) ->
- {ok, #file_info{size = Size}} =
- file:read_file_info(Filename),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- Size
- end,
-
- ok = sendfile_send("localhost", Send, 0).
-
-t_sendfile_partial(Config) ->
- Filename = proplists:get_value(small_file, Config),
- FileOpts = proplists:get_value(file_opts, Config, []),
-
- SendSingle = fun(Sock) ->
- {_Size, <<Data:5/binary,_/binary>>} =
- sendfile_file_info(Filename),
- {ok,D} = file:open(Filename,[read|FileOpts]),
- {ok,5} = gen_tcp:sendfile(D,Sock,0,5,[]),
- file:close(D),
- Data
- end,
- ok = sendfile_send(SendSingle),
-
- {_Size, <<FData:5/binary,SData:3/binary,_/binary>>} =
- sendfile_file_info(Filename),
- {ok,D} = file:open(Filename,[read|FileOpts]),
- {ok, <<FData/binary>>} = file:read(D,5),
- FSend = fun(Sock) ->
- {ok,5} = gen_tcp:sendfile(D,Sock,0,5,[]),
- FData
- end,
-
- ok = sendfile_send(FSend),
-
- SSend = fun(Sock) ->
- {ok,3} = gen_tcp:sendfile(D,Sock,5,3,[]),
- SData
- end,
-
- ok = sendfile_send(SSend),
-
- {ok, <<SData/binary>>} = file:read(D,3),
-
- file:close(D).
-
-t_sendfile_offset(Config) ->
- Filename = proplists:get_value(small_file, Config),
- FileOpts = proplists:get_value(file_opts, Config, []),
-
- Send = fun(Sock) ->
- {_Size, <<_:5/binary,Data:3/binary,_/binary>> = AllData} =
- sendfile_file_info(Filename),
- {ok,D} = file:open(Filename,[read|FileOpts]),
- {ok,3} = gen_tcp:sendfile(D,Sock,5,3,[]),
- {ok, AllData} = file:read(D,100),
- file:close(D),
- Data
- end,
- ok = sendfile_send(Send).
-
-
-t_sendfile_hdtl(Config) ->
- Filename = proplists:get_value(small_file, Config),
- FileOpts = proplists:get_value(file_opts, Config, []),
-
- Send = fun(Sock, Headers, Trailers, HdtlSize) ->
- {Size, Data} = sendfile_file_info(Filename),
- {ok,D} = file:open(Filename,[read|FileOpts]),
- AllSize = Size+HdtlSize,
- {ok, AllSize} = gen_tcp:sendfile(
- D, Sock,0,0,
- [{headers,Headers},
- {trailers,Trailers}]),
- file:close(D),
- Data
- end,
-
- SendHdTl = fun(Sock) ->
- Headers = [<<"header1">>,<<0:(1024*8)>>,"header2"],
- Trailers = [<<"trailer1">>,"trailer2"],
- D = Send(Sock,Headers,Trailers,
- iolist_size([Headers,Trailers])),
- iolist_to_binary([Headers,D,Trailers])
- end,
- ok = sendfile_send(SendHdTl),
-
- SendHd = fun(Sock) ->
- Headers = [<<"header1">>,"header2"],
- D = Send(Sock,Headers,undefined,
- iolist_size([Headers])),
- iolist_to_binary([Headers,D])
- end,
- ok = sendfile_send(SendHd),
-
- SendTl = fun(Sock) ->
- Trailers = [<<"trailer1">>,"trailer2"],
- D = Send(Sock,undefined,Trailers,
- iolist_size([Trailers])),
- iolist_to_binary([D,Trailers])
- end,
- ok = sendfile_send(SendTl).
-
-t_sendfile_sendafter(Config) ->
- Filename = proplists:get_value(small_file, Config),
-
- Send = fun(Sock) ->
- {Size, Data} = sendfile_file_info(Filename),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- ok = gen_tcp:send(Sock, <<2>>),
- <<Data/binary,2>>
- end,
-
- ok = sendfile_send(Send).
-
-t_sendfile_recvafter(Config) ->
- Filename = proplists:get_value(small_file, Config),
-
- Send = fun(Sock) ->
- {Size, Data} = sendfile_file_info(Filename),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- ok = gen_tcp:send(Sock, <<1>>),
- {ok,<<1>>} = gen_tcp:recv(Sock, 1),
- <<Data/binary,1>>
- end,
-
- ok = sendfile_send(Send).
-
-t_sendfile_sendduring(Config) ->
- Filename = proplists:get_value(big_file, Config),
-
- Send = fun(Sock) ->
- {ok, #file_info{size = Size}} =
- file:read_file_info(Filename),
- spawn_link(fun() ->
- timer:sleep(10),
- ok = gen_tcp:send(Sock, <<2>>)
- end),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- Size+1
- end,
-
- ok = sendfile_send("localhost", Send, 0).
-
-t_sendfile_recvduring(Config) ->
- Filename = proplists:get_value(big_file, Config),
-
- Send = fun(Sock) ->
- {ok, #file_info{size = Size}} =
- file:read_file_info(Filename),
- spawn_link(fun() ->
- timer:sleep(10),
- ok = gen_tcp:send(Sock, <<1>>),
- {ok,<<1>>} = gen_tcp:recv(Sock, 1)
- end),
- {ok, Size} = gen_tcp:sendfile(Filename, Sock),
- timer:sleep(1000),
- Size+1
- end,
-
- ok = sendfile_send("localhost", Send, 0).
-
-%% TODO: consolidate tests and reduce code
-sendfile_send(Send) ->
- sendfile_send("localhost",Send).
-sendfile_send(Host, Send) ->
- sendfile_send(Host, Send, []).
-sendfile_send(Host, Send, Orig) ->
- spawn_link(?MODULE, sendfile_server, [self(), Orig]),
- receive
- {server, Port} ->
- {ok, Sock} = gen_tcp:connect(Host, Port,
- [binary,{packet,0},
- {active,false}]),
- Data = Send(Sock),
- ok = gen_tcp:close(Sock),
- receive
- {ok, Bin} ->
- Data = Bin,
- ok
- end
- end.
-
-sendfile_server(ClientPid, Orig) ->
- {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 0},
- {active, true},
- {reuseaddr, true}]),
- {ok, Port} = inet:port(LSock),
- ClientPid ! {server, Port},
- {ok, Sock} = gen_tcp:accept(LSock),
- {ok, Bin} = sendfile_do_recv(Sock, Orig),
- ClientPid ! {ok, Bin},
- gen_tcp:send(Sock, <<1>>).
-
--define(SENDFILE_TIMEOUT, 10000).
-%% f(),{ok, S} = gen_tcp:connect("localhost",7890,[binary]),gen_tcp:sendfile("/ldisk/lukas/otp/sendfiletest.dat",S).
-sendfile_do_recv(Sock, Bs) ->
- receive
- {tcp, Sock, B} ->
- case binary:match(B,<<1>>) of
- nomatch when is_list(Bs) ->
- sendfile_do_recv(Sock, [B|Bs]);
- nomatch when is_integer(Bs) ->
- sendfile_do_recv(Sock, byte_size(B) + Bs);
- _ when is_list(Bs) ->
- {ok, iolist_to_binary(lists:reverse([B|Bs]))};
- _ when is_integer(Bs) ->
- {ok, byte_size(B) + Bs}
- end;
- {tcp_closed, Sock} when is_list(Bs) ->
- {ok, iolist_to_binary(lists:reverse(Bs))};
- {tcp_closed, Sock} when is_integer(Bs) ->
- {ok, Bs}
- after ?SENDFILE_TIMEOUT ->
- timeout
- end.
-
-sendfile_file_info(File) ->
- {ok, #file_info{size = Size}} = file:read_file_info(File),
- {ok, Data} = file:read_file(File),
- {Size, Data}.
-
%%% Utilities
diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl
new file mode 100644
index 0000000000..cddb783fe7
--- /dev/null
+++ b/lib/kernel/test/sendfile_SUITE.erl
@@ -0,0 +1,291 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(sendfile_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-compile(export_all).
+
+all() ->
+ [t_sendfile_small
+ ,t_sendfile_big
+% ,t_sendfile_hdtl
+ ,t_sendfile_partial
+ ,t_sendfile_offset
+ ,t_sendfile_sendafter
+ ,t_sendfile_recvafter
+ ,t_sendfile_sendduring
+ ,t_sendfile_recvduring
+ ].
+
+init_per_suite(Config) ->
+ Priv = ?config(priv_dir, Config),
+ SFilename = filename:join(Priv, "sendfile_small.html"),
+ {ok, DS} = file:open(SFilename,[write,raw]),
+ file:write(DS,"yo baby yo"),
+ file:sync(DS),
+ file:close(DS),
+ BFilename = filename:join(Priv, "sendfile_big.html"),
+ {ok, DB} = file:open(BFilename,[write,raw]),
+ [file:write(DB,[<<0:(10*8*1024*1024)>>]) || _I <- lists:seq(1,51)],
+ file:sync(DB),
+ file:close(DB),
+ [{small_file, SFilename},
+ {file_opts,[raw,binary]},
+ {big_file, BFilename}|Config].
+
+end_per_suite(Config) ->
+ file:delete(proplists:get_value(big_file, Config)).
+
+t_sendfile_small(Config) when is_list(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Data
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_big(Config) when is_list(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Size
+ end,
+
+ ok = sendfile_send("localhost", Send, 0).
+
+t_sendfile_partial(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ SendSingle = fun(Sock) ->
+ {_Size, <<Data:5/binary,_/binary>>} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok,5} = file:sendfile(D,Sock,0,5,[]),
+ file:close(D),
+ Data
+ end,
+ ok = sendfile_send(SendSingle),
+
+ {_Size, <<FData:5/binary,SData:3/binary,_/binary>>} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok, <<FData/binary>>} = file:read(D,5),
+ FSend = fun(Sock) ->
+ {ok,5} = file:sendfile(D,Sock,0,5,[]),
+ FData
+ end,
+
+ ok = sendfile_send(FSend),
+
+ SSend = fun(Sock) ->
+ {ok,3} = file:sendfile(D,Sock,5,3,[]),
+ SData
+ end,
+
+ ok = sendfile_send(SSend),
+
+ {ok, <<SData/binary>>} = file:read(D,3),
+
+ file:close(D).
+
+t_sendfile_offset(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ Send = fun(Sock) ->
+ {_Size, <<_:5/binary,Data:3/binary,_/binary>> = AllData} =
+ sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ {ok,3} = file:sendfile(D,Sock,5,3,[]),
+ {ok, AllData} = file:read(D,100),
+ file:close(D),
+ Data
+ end,
+ ok = sendfile_send(Send).
+
+
+t_sendfile_hdtl(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+ FileOpts = proplists:get_value(file_opts, Config, []),
+
+ Send = fun(Sock, Headers, Trailers, HdtlSize) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok,D} = file:open(Filename,[read|FileOpts]),
+ AllSize = Size+HdtlSize,
+ {ok, AllSize} = file:sendfile(
+ D, Sock,0,0,
+ [{headers,Headers},
+ {trailers,Trailers}]),
+ file:close(D),
+ Data
+ end,
+
+ SendHdTl = fun(Sock) ->
+ Headers = [<<"header1">>,<<0:(1024*8)>>,"header2"],
+ Trailers = [<<"trailer1">>,"trailer2"],
+ D = Send(Sock,Headers,Trailers,
+ iolist_size([Headers,Trailers])),
+ iolist_to_binary([Headers,D,Trailers])
+ end,
+ ok = sendfile_send(SendHdTl),
+
+ SendHd = fun(Sock) ->
+ Headers = [<<"header1">>,"header2"],
+ D = Send(Sock,Headers,undefined,
+ iolist_size([Headers])),
+ iolist_to_binary([Headers,D])
+ end,
+ ok = sendfile_send(SendHd),
+
+ SendTl = fun(Sock) ->
+ Trailers = [<<"trailer1">>,"trailer2"],
+ D = Send(Sock,undefined,Trailers,
+ iolist_size([Trailers])),
+ iolist_to_binary([D,Trailers])
+ end,
+ ok = sendfile_send(SendTl).
+
+t_sendfile_sendafter(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ ok = gen_tcp:send(Sock, <<2>>),
+ <<Data/binary,2>>
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_recvafter(Config) ->
+ Filename = proplists:get_value(small_file, Config),
+
+ Send = fun(Sock) ->
+ {Size, Data} = sendfile_file_info(Filename),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ ok = gen_tcp:send(Sock, <<1>>),
+ {ok,<<1>>} = gen_tcp:recv(Sock, 1),
+ <<Data/binary,1>>
+ end,
+
+ ok = sendfile_send(Send).
+
+t_sendfile_sendduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ spawn_link(fun() ->
+ timer:sleep(10),
+ ok = gen_tcp:send(Sock, <<2>>)
+ end),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ Size+1
+ end,
+
+ ok = sendfile_send("localhost", Send, 0).
+
+t_sendfile_recvduring(Config) ->
+ Filename = proplists:get_value(big_file, Config),
+
+ Send = fun(Sock) ->
+ {ok, #file_info{size = Size}} =
+ file:read_file_info(Filename),
+ spawn_link(fun() ->
+ timer:sleep(10),
+ ok = gen_tcp:send(Sock, <<1>>),
+ {ok,<<1>>} = gen_tcp:recv(Sock, 1)
+ end),
+ {ok, Size} = file:sendfile(Filename, Sock),
+ timer:sleep(1000),
+ Size+1
+ end,
+
+ ok = sendfile_send("localhost", Send, 0).
+
+%% TODO: consolidate tests and reduce code
+sendfile_send(Send) ->
+ sendfile_send("localhost",Send).
+sendfile_send(Host, Send) ->
+ sendfile_send(Host, Send, []).
+sendfile_send(Host, Send, Orig) ->
+ spawn_link(?MODULE, sendfile_server, [self(), Orig]),
+ receive
+ {server, Port} ->
+ {ok, Sock} = gen_tcp:connect(Host, Port,
+ [binary,{packet,0},
+ {active,false}]),
+ Data = Send(Sock),
+ ok = gen_tcp:close(Sock),
+ receive
+ {ok, Bin} ->
+ Data = Bin,
+ ok
+ end
+ end.
+
+sendfile_server(ClientPid, Orig) ->
+ {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 0},
+ {active, true},
+ {reuseaddr, true}]),
+ {ok, Port} = inet:port(LSock),
+ ClientPid ! {server, Port},
+ {ok, Sock} = gen_tcp:accept(LSock),
+ {ok, Bin} = sendfile_do_recv(Sock, Orig),
+ ClientPid ! {ok, Bin},
+ gen_tcp:send(Sock, <<1>>).
+
+-define(SENDFILE_TIMEOUT, 10000).
+%% f(),{ok, S} = gen_tcp:connect("localhost",7890,[binary]),file:sendfile("/ldisk/lukas/otp/sendfiletest.dat",S).
+sendfile_do_recv(Sock, Bs) ->
+ receive
+ {tcp, Sock, B} ->
+ case binary:match(B,<<1>>) of
+ nomatch when is_list(Bs) ->
+ sendfile_do_recv(Sock, [B|Bs]);
+ nomatch when is_integer(Bs) ->
+ sendfile_do_recv(Sock, byte_size(B) + Bs);
+ _ when is_list(Bs) ->
+ {ok, iolist_to_binary(lists:reverse([B|Bs]))};
+ _ when is_integer(Bs) ->
+ {ok, byte_size(B) + Bs}
+ end;
+ {tcp_closed, Sock} when is_list(Bs) ->
+ {ok, iolist_to_binary(lists:reverse(Bs))};
+ {tcp_closed, Sock} when is_integer(Bs) ->
+ {ok, Bs}
+ after ?SENDFILE_TIMEOUT ->
+ timeout
+ end.
+
+sendfile_file_info(File) ->
+ {ok, #file_info{size = Size}} = file:read_file_info(File),
+ {ok, Data} = file:read_file(File),
+ {Size, Data}.