ranch_transport.erl 5.6 KB

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