Просмотр исходного кода

Add Transport:sendfile/2 support

Uses file:sendfile/2 for TCP, a fallback function for SSL.
Loïc Hoguin 12 лет назад
Родитель
Сommit
fb7ed38076
2 измененных файлов с 42 добавлено и 0 удалено
  1. 29 0
      src/ranch_ssl.erl
  2. 13 0
      src/ranch_tcp.erl

+ 29 - 0
src/ranch_ssl.erl

@@ -32,6 +32,7 @@
 -export([connect/3]).
 -export([recv/3]).
 -export([send/2]).
+-export([sendfile/2]).
 -export([setopts/2]).
 -export([controlling_process/2]).
 -export([peername/1]).
@@ -138,6 +139,34 @@ recv(Socket, Length, Timeout) ->
 send(Socket, Packet) ->
 	ssl:send(Socket, Packet).
 
+%% @doc Send a file on a socket.
+%%
+%% Unlike with TCP, no syscall can be used here, so sending files
+%% through SSL will be much slower in comparison.
+%%
+%% @see file:sendfile/2
+-spec sendfile(ssl:sslsocket(), file:name())
+	-> {ok, non_neg_integer()} | {error, atom()}.
+sendfile(Socket, Filepath) ->
+	{ok, IoDevice} = file:open(Filepath, [read, binary, raw]),
+	sendfile(Socket, IoDevice, 0).
+
+-spec sendfile(ssl:sslsocket(), file:io_device(), non_neg_integer())
+	-> {ok, non_neg_integer()} | {error, atom()}.
+sendfile(Socket, IoDevice, Sent) ->
+	case file:read(IoDevice, 16#1FFF) of
+		eof ->
+			ok = file:close(IoDevice),
+			{ok, Sent};
+		{ok, Bin} ->
+			case send(Socket, Bin) of
+				ok ->
+					sendfile(Socket, IoDevice, Sent + byte_size(Bin));
+				{error, Reason} ->
+					{error, Reason}
+			end
+	end.
+
 %% @doc Set options on the given socket.
 %% @see ssl:setopts/2
 %% @todo Probably filter Opts?

+ 13 - 0
src/ranch_tcp.erl

@@ -27,6 +27,7 @@
 -export([connect/3]).
 -export([recv/3]).
 -export([send/2]).
+-export([sendfile/2]).
 -export([setopts/2]).
 -export([controlling_process/2]).
 -export([peername/1]).
@@ -103,6 +104,18 @@ recv(Socket, Length, Timeout) ->
 send(Socket, Packet) ->
 	gen_tcp:send(Socket, Packet).
 
+%% @doc Send a file on a socket.
+%%
+%% This is the optimal way to send files using TCP. It uses a syscall
+%% which means there is no context switch between opening the file
+%% and writing its contents on the socket.
+%%
+%% @see file:sendfile/2
+-spec sendfile(inet:socket(), file:name())
+	-> {ok, non_neg_integer()} | {error, atom()}.
+sendfile(Socket, Filename) ->
+	file:sendfile(Filename, Socket).
+
 %% @doc Set options on the given socket.
 %% @see inet:setopts/2
 %% @todo Probably filter Opts?