ranch_transport.erl 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. -module(ranch_transport).
  15. -export([sendfile/6]).
  16. -type socket() :: any().
  17. -export_type([socket/0]).
  18. -type opts() :: any().
  19. -type stats() :: any().
  20. -type sendfile_opts() :: [{chunk_size, non_neg_integer()}].
  21. -export_type([sendfile_opts/0]).
  22. -callback name() -> atom().
  23. -callback secure() -> boolean().
  24. -callback messages() -> {OK::atom(), Closed::atom(), Error::atom()}.
  25. -callback listen(opts()) -> {ok, socket()} | {error, atom()}.
  26. -callback accept(socket(), timeout())
  27. -> {ok, socket()} | {error, closed | timeout | atom()}.
  28. -callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}.
  29. -callback connect(string(), inet:port_number(), opts())
  30. -> {ok, socket()} | {error, atom()}.
  31. -callback connect(string(), inet:port_number(), opts(), timeout())
  32. -> {ok, socket()} | {error, atom()}.
  33. -callback recv(socket(), non_neg_integer(), timeout())
  34. -> {ok, any()} | {error, closed | timeout | atom()}.
  35. -callback send(socket(), iodata()) -> ok | {error, atom()}.
  36. -callback sendfile(socket(), file:name() | file:fd())
  37. -> {ok, non_neg_integer()} | {error, atom()}.
  38. -callback sendfile(socket(), file:name() | file:fd(), non_neg_integer(),
  39. non_neg_integer()) -> {ok, non_neg_integer()} | {error, atom()}.
  40. -callback sendfile(socket(), file:name() | file:fd(), non_neg_integer(),
  41. non_neg_integer(), sendfile_opts())
  42. -> {ok, non_neg_integer()} | {error, atom()}.
  43. -callback setopts(socket(), opts()) -> ok | {error, atom()}.
  44. -callback getopts(socket(), [atom()]) -> {ok, opts()} | {error, atom()}.
  45. -callback getstat(socket()) -> {ok, stats()} | {error, atom()}.
  46. -callback getstat(socket(), [atom()]) -> {ok, stats()} | {error, atom()}.
  47. -callback controlling_process(socket(), pid())
  48. -> ok | {error, closed | not_owner | atom()}.
  49. -callback peername(socket())
  50. -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
  51. -callback sockname(socket())
  52. -> {ok, {inet:ip_address(), inet:port_number()}} | {error, atom()}.
  53. -callback shutdown(socket(), read | write | read_write)
  54. -> ok | {error, atom()}.
  55. -callback close(socket()) -> ok.
  56. %% A fallback for transports that don't have a native sendfile implementation.
  57. %% Note that the ordering of arguments is different from file:sendfile/5 and
  58. %% that this function accepts either a raw file or a file name.
  59. -spec sendfile(module(), socket(), file:filename_all() | file:fd(),
  60. non_neg_integer(), non_neg_integer(), sendfile_opts())
  61. -> {ok, non_neg_integer()} | {error, atom()}.
  62. sendfile(Transport, Socket, Filename, Offset, Bytes, Opts)
  63. when is_list(Filename) orelse is_atom(Filename)
  64. orelse is_binary(Filename) ->
  65. ChunkSize = chunk_size(Opts),
  66. case file:open(Filename, [read, raw, binary]) of
  67. {ok, RawFile} ->
  68. _ = case Offset of
  69. 0 ->
  70. ok;
  71. _ ->
  72. {ok, _} = file:position(RawFile, {bof, Offset})
  73. end,
  74. try
  75. sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize)
  76. after
  77. ok = file:close(RawFile)
  78. end;
  79. {error, _Reason} = Error ->
  80. Error
  81. end;
  82. sendfile(Transport, Socket, RawFile, Offset, Bytes, Opts) ->
  83. ChunkSize = chunk_size(Opts),
  84. Initial2 = case file:position(RawFile, {cur, 0}) of
  85. {ok, Offset} ->
  86. Offset;
  87. {ok, Initial} ->
  88. {ok, _} = file:position(RawFile, {bof, Offset}),
  89. Initial
  90. end,
  91. case sendfile_loop(Transport, Socket, RawFile, Bytes, 0, ChunkSize) of
  92. {ok, _Sent} = Result ->
  93. {ok, _} = file:position(RawFile, {bof, Initial2}),
  94. Result;
  95. {error, _Reason} = Error ->
  96. Error
  97. end.
  98. -spec chunk_size(sendfile_opts()) -> pos_integer().
  99. chunk_size(Opts) ->
  100. case lists:keyfind(chunk_size, 1, Opts) of
  101. {chunk_size, ChunkSize}
  102. when is_integer(ChunkSize) andalso ChunkSize > 0 ->
  103. ChunkSize;
  104. {chunk_size, 0} ->
  105. 16#1FFF;
  106. false ->
  107. 16#1FFF
  108. end.
  109. -spec sendfile_loop(module(), socket(), file:fd(), non_neg_integer(),
  110. non_neg_integer(), pos_integer())
  111. -> {ok, non_neg_integer()} | {error, any()}.
  112. sendfile_loop(_Transport, _Socket, _RawFile, Sent, Sent, _ChunkSize)
  113. when Sent =/= 0 ->
  114. %% All requested data has been read and sent, return number of bytes sent.
  115. {ok, Sent};
  116. sendfile_loop(Transport, Socket, RawFile, Bytes, Sent, ChunkSize) ->
  117. ReadSize = read_size(Bytes, Sent, ChunkSize),
  118. case file:read(RawFile, ReadSize) of
  119. {ok, IoData} ->
  120. case Transport:send(Socket, IoData) of
  121. ok ->
  122. Sent2 = iolist_size(IoData) + Sent,
  123. sendfile_loop(Transport, Socket, RawFile, Bytes, Sent2,
  124. ChunkSize);
  125. {error, _Reason} = Error ->
  126. Error
  127. end;
  128. eof ->
  129. {ok, Sent};
  130. {error, _Reason} = Error ->
  131. Error
  132. end.
  133. -spec read_size(non_neg_integer(), non_neg_integer(), non_neg_integer()) ->
  134. non_neg_integer().
  135. read_size(0, _Sent, ChunkSize) ->
  136. ChunkSize;
  137. read_size(Bytes, Sent, ChunkSize) ->
  138. min(Bytes - Sent, ChunkSize).