Current section
Files
Jump to
Current section
Files
src/ranch_erltls.erl
-module(ranch_erltls).
-behaviour(ranch_transport).
-export([
name/0,
secure/0,
messages/0,
listen/1,
handshake/3,
accept/2,
accept_ack/2,
connect/3,
connect/4,
recv/3,
recv_proxy_header/2,
send/2,
sendfile/2,
sendfile/4,
sendfile/5,
setopts/2,
getopts/2,
getstat/1,
getstat/2,
controlling_process/2,
peername/1,
sockname/1,
shutdown/2,
close/1
]).
-define(DEFAULT_LISTEN_OPTS, [
{backlog, 1024},
{nodelay, true},
{send_timeout, 30000},
{send_timeout_close, true},
{reuseaddr, true},
{mode, binary},
{active, false},
{packet, raw}
]).
-define(DEFAULT_CONNECT_OPTS, [
{mode, binary},
{active, false},
{packet, raw}
]).
name() ->
erltls.
-spec secure() ->
boolean().
secure() ->
true.
messages() ->
{ssl, ssl_closed, ssl_error}.
-spec listen(erltls:listen_option()) ->
{ok, erltls:tlssocket()} | {error, erltls:reason()}.
listen(Opts0) ->
case erltls_utils:lookup(port, Opts0) of
null ->
{error, no_port};
Port ->
Opts = erltls_options:normalize_options(Opts0),
erltls:listen(Port, apply_default_options(?DEFAULT_LISTEN_OPTS, Opts))
end.
-spec accept(erltls:tlssocket(), timeout()) ->
{ok, erltls:tlssocket()} | {error, erltls:reason()}.
accept(LSocket, Timeout) ->
erltls:transport_accept(LSocket, Timeout).
-spec handshake(erltls:tlssocket(), any(), timeout()) ->
{ok, erltls:tlssocket()} | {error, erltls:reason()}.
handshake(CSocket, _, Timeout) ->
ok = accept_ack(CSocket, Timeout),
{ok, CSocket}.
-spec accept_ack(erltls:tlssocket(), timeout()) ->
ok | {error, erltls:reason()}.
accept_ack(CSocket, Timeout) ->
case erltls:ssl_accept(CSocket, Timeout) of
ok ->
ok;
%garbage data
{error, invalid_tls_frame} ->
ok = close(CSocket),
exit(normal);
%% Socket most likely stopped responding, don't error out.
{error, Reason} when Reason =:= timeout; Reason =:= closed ->
ok = close(CSocket),
exit(normal);
{error, Reason} ->
ok = close(CSocket),
error(Reason)
end.
-spec connect(erltls:host(), inet:port_number(), list()) ->
{ok, erltls:tlssocket()} | {error, erltls:reason()}.
connect(Host, Port, Opts0) ->
Opts = erltls_options:normalize_options(Opts0),
erltls:connect(Host, Port, apply_default_options(?DEFAULT_CONNECT_OPTS, Opts)).
-spec connect(erltls:host(), inet:port_number(), [erltls:connect_option()], timeout()) ->
{ok, erltls:tlssocket()} | {error, erltls:reason()}.
connect(Host, Port, Options, Timeout) ->
erltls:connect(Host, Port, Options, Timeout).
-spec recv(erltls:tlssocket(), non_neg_integer(), timeout()) ->
{ok, binary()| list()} | {error, erltls:reason()}.
recv(Socket, Length, Timeout) ->
erltls:recv(Socket, Length, Timeout).
-spec recv_proxy_header(ssl:sslsocket(), timeout())
-> {ok, ranch_proxy_header:proxy_info()}
| {error, closed | atom()}
| {error, protocol_error, atom()}.
recv_proxy_header(TLSSocket, Timeout) ->
%% This follows the implementation in
%% https://github.com/ninenines/ranch/blob/1.7.1/src/ranch_ssl.erl
{tlssocket, TCPSocket, _Pid} = TLSSocket,
ranch_tcp:recv_proxy_header(TCPSocket, Timeout).
-spec send(erltls:tlssocket(), iodata()) ->
ok | {error, erltls:reason()}.
send(Socket, Packet) ->
erltls:send(Socket, Packet).
-spec sendfile(erltls:tlssocket(), file:name_all() | file:fd()) ->
{ok, non_neg_integer()} | {error, term()}.
sendfile(Socket, Filename) ->
sendfile(Socket, Filename, 0, 0, []).
-spec sendfile(erltls:tlssocket(), file:name_all() | file:fd(), non_neg_integer(), non_neg_integer()) ->
{ok, non_neg_integer()} | {error, term()}.
sendfile(Socket, File, Offset, Bytes) ->
sendfile(Socket, File, Offset, Bytes, []).
-spec sendfile(erltls:tlssocket(), file:name_all() | file:fd(), non_neg_integer(), non_neg_integer(), ranch_transport:sendfile_opts()) ->
{ok, non_neg_integer()} | {error, atom()}.
sendfile(Socket, File, Offset, Bytes, Opts) ->
ranch_transport:sendfile(?MODULE, Socket, File, Offset, Bytes, Opts).
-spec setopts(erltls:tlssocket(), [gen_tcp:option()]) ->
ok | {error, erltls:reason()}.
setopts(Socket, Opts) ->
erltls:setopts(Socket, Opts).
-spec getopts(ssl:sslsocket(), [atom()]) -> {ok, list()} | {error, atom()}.
getopts(Socket, Opts) ->
erltls:getopts(Socket, Opts).
-spec getstat(ssl:sslsocket()) -> {ok, list()} | {error, atom()}.
getstat(Socket) ->
erltls:getstat(Socket).
-spec getstat(ssl:sslsocket(), [atom()]) -> {ok, list()} | {error, atom()}.
getstat(Socket, OptionNames) ->
erltls:getstat(Socket, OptionNames).
-spec controlling_process(erltls:tlssocket(), pid()) ->
ok | {error, erltls:reason()}.
controlling_process(Socket, Pid) ->
erltls:controlling_process(Socket, Pid).
-spec peername(erltls:tlssocket()) ->
{ok, {inet:ip_address(), inet:port_number()}} | {error, erltls:reason()}.
peername(Socket) ->
erltls:peername(Socket).
-spec sockname(erltls:tlssocket()) ->
{ok, {inet:ip_address(), inet:port_number()}} | {error, erltls:reason()}.
sockname(Socket) ->
erltls:sockname(Socket).
-spec shutdown(erltls:tlssocket(), read | write | read_write) ->
ok | {error, erltls:reason()}.
shutdown(Socket, How) ->
erltls:shutdown(Socket, How).
-spec close(erltls:tlssocket()) ->
term().
close(Socket) ->
erltls:close(Socket).
% Internal stuffs
apply_default_options([{K, _V} = H|T], Opts) ->
case lists:keymember(K, 1, Opts) of
true ->
apply_default_options(T, Opts);
false ->
apply_default_options(T, [H|Opts])
end;
apply_default_options([], Opts) ->
Opts.