From fb7ed3807620f7534c617789e7347192838a419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sat, 1 Dec 2012 10:24:07 +0100 Subject: Add Transport:sendfile/2 support Uses file:sendfile/2 for TCP, a fallback function for SSL. --- src/ranch_ssl.erl | 29 +++++++++++++++++++++++++++++ src/ranch_tcp.erl | 13 +++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/ranch_ssl.erl b/src/ranch_ssl.erl index 4bbc3ce..734de21 100644 --- a/src/ranch_ssl.erl +++ b/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? diff --git a/src/ranch_tcp.erl b/src/ranch_tcp.erl index d68972d..b113186 100644 --- a/src/ranch_tcp.erl +++ b/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? -- cgit v1.2.3