zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

ranch_transport.erl (5679B)


      1 %% Copyright (c) 2012-2018, Loïc Hoguin <essen@ninenines.eu>
      2 %%
      3 %% Permission to use, copy, modify, and/or distribute this software for any
      4 %% purpose with or without fee is hereby granted, provided that the above
      5 %% copyright notice and this permission notice appear in all copies.
      6 %%
      7 %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      8 %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      9 %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     10 %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     11 %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     12 %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     13 %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     14 
     15 -module(ranch_transport).
     16 
     17 -export([sendfile/6]).
     18 
     19 -type socket() :: any().
     20 -export_type([socket/0]).
     21 
     22 -type opts() :: any().
     23 -type stats() :: any().
     24 -type sendfile_opts() :: [{chunk_size, non_neg_integer()}].
     25 -export_type([sendfile_opts/0]).
     26 
     27 -callback name() -> atom().
     28 -callback secure() -> boolean().
     29 -callback messages() -> {OK::atom(), Closed::atom(), Error::atom()}.
     30 -callback listen(opts()) -> {ok, socket()} | {error, atom()}.
     31 -callback accept(socket(), timeout())
     32 	-> {ok, socket()} | {error, closed | timeout | atom()}.
     33 -callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}.
     34 -callback connect(string(), inet:port_number(), opts())
     35 	-> {ok, socket()} | {error, atom()}.
     36 -callback connect(string(), inet:port_number(), opts(), timeout())
     37 	-> {ok, socket()} | {error, atom()}.
     38 -callback recv(socket(), non_neg_integer(), timeout())
     39 	-> {ok, any()} | {error, closed | timeout | atom()}.
     40 -callback recv_proxy_header(socket(), timeout())
     41 	-> {ok, ranch_proxy_header:proxy_info()}
     42 	| {error, closed | atom()}
     43 	| {error, protocol_error, atom()}.
     44 -callback send(socket(), iodata()) -> ok | {error, atom()}.
     45 -callback sendfile(socket(), file:name_all() | file:fd())
     46 	-> {ok, non_neg_integer()} | {error, atom()}.
     47 -callback sendfile(socket(), file:name_all() | file:fd(), non_neg_integer(),
     48 		non_neg_integer()) -> {ok, non_neg_integer()} | {error, atom()}.
     49 -callback sendfile(socket(), file:name_all() | file:fd(), non_neg_integer(),
     50 		non_neg_integer(), sendfile_opts())
     51 	-> {ok, non_neg_integer()} | {error, atom()}.
     52 -callback setopts(socket(), opts()) -> ok | {error, atom()}.
     53 -callback getopts(socket(), [atom()]) -> {ok, opts()} | {error, atom()}.
     54 -callback getstat(socket()) -> {ok, stats()} | {error, atom()}.
     55 -callback getstat(socket(), [atom()]) -> {ok, stats()} | {error, atom()}.
     56 -callback controlling_process(socket(), pid())
     57 	-> ok | {error, closed | not_owner | atom()}.
     58 -callback peername(socket())
     59 	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
     60 -callback sockname(socket())
     61 	-> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
     62 -callback shutdown(socket(), read | write | read_write)
     63 	-> ok | {error, atom()}.
     64 -callback close(socket()) -> ok.
     65 
     66 %% A fallback for transports that don't have a native sendfile implementation.
     67 %% Note that the ordering of arguments is different from file:sendfile/5 and
     68 %% that this function accepts either a raw file or a file name.
     69 -spec sendfile(module(), socket(), file:name_all() | file:fd(),
     70 		non_neg_integer(), non_neg_integer(), sendfile_opts())
     71 	-> {ok, non_neg_integer()} | {error, atom()}.
     72 sendfile(Transport, Socket, Filename, Offset, Bytes, Opts)
     73 		when is_list(Filename) orelse is_atom(Filename)
     74 		orelse is_binary(Filename) ->
     75 	ChunkSize = chunk_size(Opts),
     76 	case file:open(Filename, [read, raw, binary]) of
     77 		{ok, RawFile} ->
     78 			_ = case Offset of
     79 				0 ->
     80 					ok;
     81 				_ ->
     82 					{ok, _} = file:position(RawFile, {bof, Offset})
     83 			end,
     84 			try
     85 				sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize)
     86 			after
     87 				ok = file:close(RawFile)
     88 			end;
     89 		{error, _Reason} = Error ->
     90 			Error
     91 	end;
     92 sendfile(Transport, Socket, RawFile, Offset, Bytes, Opts) ->
     93 	ChunkSize = chunk_size(Opts),
     94 	Initial2 = case file:position(RawFile, {cur, 0}) of
     95 		{ok, Offset} ->
     96 			Offset;
     97 		{ok, Initial} ->
     98 			{ok, _} = file:position(RawFile, {bof, Offset}),
     99 			Initial
    100 		end,
    101 	case sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize) of
    102 		{ok, _Sent} = Result ->
    103 			{ok, _} = file:position(RawFile, {bof, Initial2}),
    104 			Result;
    105 		{error, _Reason} = Error ->
    106 			Error
    107 	end.
    108 
    109 -spec chunk_size(sendfile_opts()) -> pos_integer().
    110 chunk_size(Opts) ->
    111 	case lists:keyfind(chunk_size, 1, Opts) of
    112 		{chunk_size, ChunkSize}
    113 				when is_integer(ChunkSize) andalso ChunkSize > 0 ->
    114 			ChunkSize;
    115 		{chunk_size, 0} ->
    116 			16#1FFF;
    117 		false ->
    118 			16#1FFF
    119 	end.
    120 
    121 -spec sendfile_loop(module(), socket(), file:fd(), non_neg_integer(),
    122 		non_neg_integer(), pos_integer())
    123 	-> {ok, non_neg_integer()} | {error, any()}.
    124 sendfile_loop(_Transport, _Socket, _RawFile, Sent, Sent, _ChunkSize)
    125 		when Sent =/= 0 ->
    126 	%% All requested data has been read and sent, return number of bytes sent.
    127 	{ok, Sent};
    128 sendfile_loop(Transport, Socket, RawFile, Bytes, Sent, ChunkSize) ->
    129 	ReadSize = read_size(Bytes, Sent, ChunkSize),
    130 	case file:read(RawFile, ReadSize) of
    131 		{ok, IoData} ->
    132 			case Transport:send(Socket, IoData) of
    133 				ok ->
    134 					Sent2 = iolist_size(IoData) + Sent,
    135 					sendfile_loop(Transport, Socket, RawFile, Bytes, Sent2,
    136 						ChunkSize);
    137 				{error, _Reason} = Error ->
    138 					Error
    139 			end;
    140 		eof ->
    141 			{ok, Sent};
    142 		{error, _Reason} = Error ->
    143 			Error
    144 	end.
    145 
    146 -spec read_size(non_neg_integer(), non_neg_integer(), non_neg_integer()) ->
    147 	non_neg_integer().
    148 read_size(0, _Sent, ChunkSize) ->
    149 	ChunkSize;
    150 read_size(Bytes, Sent, ChunkSize) ->
    151 	min(Bytes - Sent, ChunkSize).