From d44e7a16f7d2823cc658e39b5d953ba0850e47ba Mon Sep 17 00:00:00 2001 From: juhlig Date: Tue, 17 Sep 2019 17:55:25 +0200 Subject: Enable multiple steps handshake Also fix some Protocol:start_link/4 into start_link/3 left over in the documentation. --- doc/src/guide/listeners.asciidoc | 4 +- doc/src/guide/protocols.asciidoc | 2 +- doc/src/manual/ranch.asciidoc | 2 + doc/src/manual/ranch.handshake.asciidoc | 22 +++++-- doc/src/manual/ranch.handshake_cancel.asciidoc | 55 ++++++++++++++++++ doc/src/manual/ranch.handshake_continue.asciidoc | 67 +++++++++++++++++++++ doc/src/manual/ranch.recv_proxy_header.asciidoc | 2 +- doc/src/manual/ranch_ssl.asciidoc | 10 ++++ doc/src/manual/ranch_transport.asciidoc | 52 ++++++++++++++++- src/ranch.erl | 74 ++++++++++++++++++------ src/ranch_ssl.erl | 37 +++++++++++- src/ranch_tcp.erl | 20 +++++++ src/ranch_transport.erl | 6 +- test/acceptor_SUITE.erl | 30 ++++++++++ test/handshake_protocol.erl | 32 ++++++++++ 15 files changed, 382 insertions(+), 33 deletions(-) create mode 100644 doc/src/manual/ranch.handshake_cancel.asciidoc create mode 100644 doc/src/manual/ranch.handshake_continue.asciidoc create mode 100644 test/handshake_protocol.erl diff --git a/doc/src/guide/listeners.asciidoc b/doc/src/guide/listeners.asciidoc index fd988f1..6e2dd19 100644 --- a/doc/src/guide/listeners.asciidoc +++ b/doc/src/guide/listeners.asciidoc @@ -239,8 +239,8 @@ with the name of the listener as the only argument. [source,erlang] ranch:remove_connection(Ref). -As seen in the chapter covering protocols, this pid is received as the -first argument of the protocol's `start_link/4` callback. +As seen in the chapter covering protocols, this reference is received +as the first argument of the protocol's `start_link/3` callback. You can modify the `max_connections` value on a running listener by using the `ranch:set_max_connections/2` function, with the name of the diff --git a/doc/src/guide/protocols.asciidoc b/doc/src/guide/protocols.asciidoc index 89360ef..b455143 100644 --- a/doc/src/guide/protocols.asciidoc +++ b/doc/src/guide/protocols.asciidoc @@ -6,7 +6,7 @@ protocol logic executed in this process. === Writing a protocol handler All protocol handlers must implement the `ranch_protocol` behavior -which defines a single callback, `start_link/4`. This callback is +which defines a single callback, `start_link/3`. This callback is responsible for spawning a new process for handling the connection. It receives four arguments: the name of the listener, the socket, the transport handler being used and the protocol options defined in diff --git a/doc/src/manual/ranch.asciidoc b/doc/src/manual/ranch.asciidoc index 065ded3..ed57236 100644 --- a/doc/src/manual/ranch.asciidoc +++ b/doc/src/manual/ranch.asciidoc @@ -26,6 +26,8 @@ Suspend/resume: Connections: * link:man:ranch:handshake(3)[ranch:handshake(3)] - Perform the transport handshake +* link:man:ranch:handshake_continue(3)[ranch:handshake_continue(3)] - Resume the paused transport handshake +* link:man:ranch:handshake_cancel(3)[ranch:handshake_cancel(3)] - Cancel the paused transport handshake * link:man:ranch:recv_proxy_header(3)[ranch:recv_proxy_header(3)] - Receive the PROXY protocol header * link:man:ranch:remove_connection(3)[ranch:remove_connection(3)] - Remove connection from the count diff --git a/doc/src/manual/ranch.handshake.asciidoc b/doc/src/manual/ranch.handshake.asciidoc index 5d2694c..508abe5 100644 --- a/doc/src/manual/ranch.handshake.asciidoc +++ b/doc/src/manual/ranch.handshake.asciidoc @@ -8,12 +8,13 @@ ranch:handshake - Perform the transport handshake [source,erlang] ---- -handshake(Ref) -> handshake(Ref, []) -handshake(Ref, Opts) -> {ok, Socket} +handshake(Ref) -> {ok, Socket} | {continue, Info} +handshake(Ref, Opts) -> {ok, Socket} | {continue, Info} Ref :: ranch:ref() Opts :: any() Socket :: any() +Info :: any() ---- Perform the transport handshake. @@ -38,12 +39,23 @@ Allowed options depend on the transport module. == Return value -An `ok` tuple is returned containing the socket for the connection. +An `ok` tuple is returned containing the socket for the connection +by default. + +Depending on configuration, a `continue` tuple can otherwise +be returned when the handshake operation is paused. It contains +data provided by the transport that can be used to inform further +decisions before resuming the handshake, for example to provide +new transport options. The handshake can be resumed using +link:man:ranch:handshake_continue(3)[ranch:handshake_continue(3)] +or canceled using +link:man:ranch:handshake_cancel(3)[ranch:handshake_cancel(3)]. This function will trigger an exception when an error occurs. == Changelog +* *2.0*: The `continue` tuple can now be returned. * *1.6*: Function introduced. Replaces `ranch:accept_ack/1`. == Examples @@ -51,7 +63,7 @@ This function will trigger an exception when an error occurs. .Initialize the connection process [source,erlang] ---- -start_link(Ref, _, Transport, Opts) -> +start_link(Ref, Transport, Opts) -> Pid = proc_lib:spawn_link(?MODULE, init, [Ref, Transport, Opts]), {ok, Pid}. @@ -65,6 +77,8 @@ init(Ref, Transport, Opts) -> == See also link:man:ranch:start_listener(3)[ranch:start_listener(3)], +link:man:ranch:handshake_continue(3)[ranch:handshake_continue(3)], +link:man:ranch:handshake_cancel(3)[ranch:handshake_cancel(3)], link:man:ranch:recv_proxy_header(3)[ranch:recv_proxy_header(3)], link:man:ranch:remove_connection(3)[ranch:remove_connection(3)], link:man:ranch(3)[ranch(3)] diff --git a/doc/src/manual/ranch.handshake_cancel.asciidoc b/doc/src/manual/ranch.handshake_cancel.asciidoc new file mode 100644 index 0000000..6f742f5 --- /dev/null +++ b/doc/src/manual/ranch.handshake_cancel.asciidoc @@ -0,0 +1,55 @@ += ranch:handshake_cancel(3) + +== Name + +ranch:handshake_cancel - Cancel the paused transport handshake + +== Description + +[source,erlang] +---- +handshake_cancel(Ref :: ranch:ref()) -> ok +---- + +Cancel the paused transport handshake. + +This function may be called by the protocol process +to cancel a paused handshake. + +== Arguments + +Ref:: + +The listener name. ++ +Allowed options depend on the transport module. + +== Return value + +The return value depends on the transport module. + +== Changelog + +* *2.0*: Function introduced. + +== Examples + +.Cancel a paused transport handshake +[source,erlang] +---- +start_link(Ref, Transport, Opts) -> + Pid = proc_lib:spawn_link(?MODULE, init, + [Ref, Transport, Opts]), + {ok, Pid}. + +init(Ref, Transport, Opts) -> + {continue, _Info} = ranch:handshake(Ref), + ranch:handshake_cancel(Ref), + exit(handshake_cancelled). +---- + +== See also + +link:man:ranch:handshake(3)[ranch:handshake(3)], +link:man:ranch:handshake_continue(3)[ranch:handshake_continue(3)], +link:man:ranch(3)[ranch(3)] diff --git a/doc/src/manual/ranch.handshake_continue.asciidoc b/doc/src/manual/ranch.handshake_continue.asciidoc new file mode 100644 index 0000000..4fb08bf --- /dev/null +++ b/doc/src/manual/ranch.handshake_continue.asciidoc @@ -0,0 +1,67 @@ += ranch:handshake_continue(3) + +== Name + +ranch:handshake_continue - Resume the paused transport handshake + +== Description + +[source,erlang] +---- +handshake_continue(Ref) -> {ok, Socket} +handshake_continue(Ref, Opts) -> {ok, Socket} + +Ref :: ranch:ref() +Opts :: any() +Socket :: any() +---- + +Resume the paused transport handshake. + +This function must be called by the protocol process in order +to resume a paused handshake. + +== Arguments + +Ref:: + +The listener name. + +Opts:: + +Transport handshake options. ++ +Allowed options depend on the transport module. + +== Return value + +An `ok` tuple is returned containing the socket for the connection. + +This function will trigger an exception when an error occurs. + +== Changelog + +* *2.0*: Function introduced. + +== Examples + +.Continue a paused transport handshake +[source,erlang] +---- +start_link(Ref, Transport, Opts) -> + Pid = proc_lib:spawn_link(?MODULE, init, + [Ref, Transport, Opts]), + {ok, Pid}. + +init(Ref, Transport, Opts) -> + {continue, _Info} = ranch:handshake(Ref), + {ok, Socket} = ranch:handshake_continue(Ref), + loop(#state{ref=Ref, socket=Socket, + transport=Transport, opts=Opts}). +---- + +== See also + +link:man:ranch:handshake(3)[ranch:handshake(3)], +link:man:ranch:handshake_cancel(3)[ranch:handshake_cancel(3)], +link:man:ranch(3)[ranch(3)] diff --git a/doc/src/manual/ranch.recv_proxy_header.asciidoc b/doc/src/manual/ranch.recv_proxy_header.asciidoc index 9f23bde..0536e3d 100644 --- a/doc/src/manual/ranch.recv_proxy_header.asciidoc +++ b/doc/src/manual/ranch.recv_proxy_header.asciidoc @@ -51,7 +51,7 @@ the error. .Receive the PROXY protocol header [source,erlang] ---- -start_link(Ref, _, Transport, Opts) -> +start_link(Ref, Transport, Opts) -> Pid = proc_lib:spawn_link(?MODULE, init, [Ref, Transport, Opts]), {ok, Pid}. diff --git a/doc/src/manual/ranch_ssl.asciidoc b/doc/src/manual/ranch_ssl.asciidoc index 00f6fad..66f91b1 100644 --- a/doc/src/manual/ranch_ssl.asciidoc +++ b/doc/src/manual/ranch_ssl.asciidoc @@ -54,6 +54,7 @@ ssl_opt() = {alpn_preferred_protocols, [binary()]} | {dhfile, file:filename()} | {eccs, [atom()]} | {fail_if_no_peer_cert, boolean()} + | {handshake, hello | full} | {hibernate_after, timeout()} | {honor_cipher_order, boolean()} | {honor_ecc_order, boolean()} @@ -156,6 +157,15 @@ fail_if_no_peer_cert (false):: Whether to refuse the connection if the client sends an empty certificate. +handshake (full):: + +If `hello` is specified for this option, the handshake is +paused after receiving the client hello message. The handshake +can then be resumed via `handshake_continue/3`, or cancelled +via `handshake_cancel/1`. ++ +This option cannot be given to `ranch:handshake/1,2`. + hibernate_after (undefined):: Time in ms after which SSL socket processes go into diff --git a/doc/src/manual/ranch_transport.asciidoc b/doc/src/manual/ranch_transport.asciidoc index 78468b3..c26d91c 100644 --- a/doc/src/manual/ranch_transport.asciidoc +++ b/doc/src/manual/ranch_transport.asciidoc @@ -80,9 +80,17 @@ Get one or more statistic options for the socket. [source,erlang] ---- handshake(Socket0 :: socket(), - SockOpts :: any(), Timeout :: timeout()) - -> {ok, Socket} + -> {ok, Socket :: socket()} + | {ok, Socket :: socket(), Info :: any()} + | {error, any()} + +handshake(Socket0 :: socket(), + SockOpts :: opts(), + Timeout :: timeout()) + -> {ok, Socket :: socket()} + | {ok, Socket :: socket(), Info :: any()} + | {error, any()} ---- Perform the transport-level handshake. @@ -92,11 +100,51 @@ before performing any socket operation. It allows transports that require extra initialization to perform their task and return a socket that is ready to use. +If the handshake is completed by this call, the function will +return `{ok, Socket}`. However, some transports (notably, +`ranch_ssl` if `{handshake, hello}` is specified in the socket +options) may pause the handshake at a certain point and return +`{ok, Socket, Info}` instead, in order to allow for +additional decisions to be made before resuming the handshake +with `handshake_continue/3` or cancelling it with +`handshake_cancel/1`. + This function may also be used to upgrade a connection from a transport to another depending on the capabilities of the transports. For example a `ranch_tcp` socket may be upgraded to a `ranch_ssl` one using this function. +=== handshake_continue + +[source,erlang] +---- +handshake_continue(Socket0 :: socket(), + Timeout :: timeout()) + -> {ok, Socket :: socket()} + | {error, any()} + +handshake_continue(Socket0 :: socket(), + SockOpts :: opts(), + Timeout :: timeout()) + -> {ok, Socket :: socket()} + | {error, any()} +---- + +Resume the paused transport-level handshake and return a socket +that is ready to use. + +This function will be called by connection processes +to resume a paused handshake. + +=== handshake_cancel + +[source,erlang] +---- +handshake_cancel(Socket :: socket()) -> ok +---- + +Cancel the paused transport-level handshake. + === listen [source,erlang] diff --git a/src/ranch.erl b/src/ranch.erl index 08eebfc..89ca21e 100644 --- a/src/ranch.erl +++ b/src/ranch.erl @@ -22,6 +22,9 @@ -export([child_spec/5]). -export([handshake/1]). -export([handshake/2]). +-export([handshake_continue/1]). +-export([handshake_continue/2]). +-export([handshake_cancel/1]). -export([recv_proxy_header/2]). -export([remove_connection/1]). -export([get_status/1]). @@ -197,28 +200,61 @@ child_spec(Ref, Transport, TransOpts0, Protocol, ProtoOpts) -> Ref, Transport, TransOpts, Protocol, ProtoOpts ]}, type => supervisor}. --spec handshake(ref()) -> {ok, ranch_transport:socket()}. +-spec handshake(ref()) -> {ok, ranch_transport:socket()} | {continue, any()}. handshake(Ref) -> - handshake(Ref, []). + handshake1(Ref, undefined). --spec handshake(ref(), any()) -> {ok, ranch_transport:socket()}. +-spec handshake(ref(), any()) -> {ok, ranch_transport:socket()} | {continue, any()}. handshake(Ref, Opts) -> - receive {handshake, Ref, Transport, CSocket, HandshakeTimeout} -> - case Transport:handshake(CSocket, Opts, HandshakeTimeout) of - OK = {ok, _} -> - OK; - %% Garbage was most likely sent to the socket, don't error out. - {error, {tls_alert, _}} -> - ok = Transport:close(CSocket), - exit(normal); - %% Socket most likely stopped responding, don't error out. - {error, Reason} when Reason =:= timeout; Reason =:= closed -> - ok = Transport:close(CSocket), - exit(normal); - {error, Reason} -> - ok = Transport:close(CSocket), - error(Reason) - end + handshake1(Ref, {opts, Opts}). + +handshake1(Ref, Opts) -> + receive {handshake, Ref, Transport, CSocket, Timeout} -> + Handshake = handshake_transport(Transport, handshake, CSocket, Opts, Timeout), + handshake_result(Handshake, Ref, Transport, CSocket, Timeout) + end. + +-spec handshake_continue(ref()) -> {ok, ranch_transport:socket()}. +handshake_continue(Ref) -> + handshake_continue1(Ref, undefined). + +-spec handshake_continue(ref(), any()) -> {ok, ranch_transport:socket()}. +handshake_continue(Ref, Opts) -> + handshake_continue1(Ref, {opts, Opts}). + +handshake_continue1(Ref, Opts) -> + receive {handshake_continue, Ref, Transport, CSocket, Timeout} -> + Handshake = handshake_transport(Transport, handshake_continue, CSocket, Opts, Timeout), + handshake_result(Handshake, Ref, Transport, CSocket, Timeout) + end. + +handshake_transport(Transport, Fun, CSocket, undefined, Timeout) -> + Transport:Fun(CSocket, Timeout); +handshake_transport(Transport, Fun, CSocket, {opts, Opts}, Timeout) -> + Transport:Fun(CSocket, Opts, Timeout). + +handshake_result(Result, Ref, Transport, CSocket, Timeout) -> + case Result of + OK = {ok, _} -> + OK; + {ok, CSocket2, Info} -> + self() ! {handshake_continue, Ref, Transport, CSocket2, Timeout}, + {continue, Info}; + {error, {tls_alert, _}} -> + ok = Transport:close(CSocket), + exit(normal); + {error, Reason} when Reason =:= timeout; Reason =:= closed -> + ok = Transport:close(CSocket), + exit(normal); + {error, Reason} -> + ok = Transport:close(CSocket), + error(Reason) + end. + +-spec handshake_cancel(ref()) -> ok. +handshake_cancel(Ref) -> + receive {handshake_continue, Ref, Transport, CSocket, _} -> + Transport:handshake_cancel(CSocket) end. %% Unlike handshake/2 this function always return errors because diff --git a/src/ranch_ssl.erl b/src/ranch_ssl.erl index b6ca50b..b86a35f 100644 --- a/src/ranch_ssl.erl +++ b/src/ranch_ssl.erl @@ -21,7 +21,11 @@ -export([listen/1]). -export([disallowed_listen_options/0]). -export([accept/2]). +-export([handshake/2]). -export([handshake/3]). +-export([handshake_continue/2]). +-export([handshake_continue/3]). +-export([handshake_cancel/1]). -export([connect/3]). -export([connect/4]). -export([recv/3]). @@ -56,6 +60,7 @@ %% @todo Update when ssl exports named_curve(). | {eccs, [atom()]} | {fail_if_no_peer_cert, boolean()} + | {handshake, hello | full} | {hibernate_after, timeout()} | {honor_cipher_order, boolean()} | {honor_ecc_order, boolean()} @@ -138,16 +143,42 @@ disallowed_listen_options() -> accept(LSocket, Timeout) -> ssl:transport_accept(LSocket, Timeout). +-spec handshake(inet:socket() | ssl:sslsocket(), timeout()) + -> {ok, ssl:sslsocket()} | {ok, ssl:sslsocket(), ssl:protocol_extensions()} | {error, any()}. +handshake(CSocket, Timeout) -> + handshake(CSocket, [], Timeout). + -spec handshake(inet:socket() | ssl:sslsocket(), opts(), timeout()) - -> {ok, ssl:sslsocket()} | {error, any()}. + -> {ok, ssl:sslsocket()} | {ok, ssl:sslsocket(), ssl:protocol_extensions()} | {error, any()}. handshake(CSocket, Opts, Timeout) -> case ssl:handshake(CSocket, Opts, Timeout) of - {ok, NewSocket} -> - {ok, NewSocket}; + OK = {ok, _} -> + OK; + OK = {ok, _, _} -> + OK; + Error = {error, _} -> + Error + end. + +-spec handshake_continue(ssl:sslsocket(), timeout()) + -> {ok, ssl:sslsocket()} | {error, any()}. +handshake_continue(CSocket, Timeout) -> + handshake_continue(CSocket, [], Timeout). + +-spec handshake_continue(ssl:sslsocket(), [ssl:tls_server_option()], timeout()) + -> {ok, ssl:sslsocket()} | {error, any()}. +handshake_continue(CSocket, Opts, Timeout) -> + case ssl:handshake_continue(CSocket, Opts, Timeout) of + OK = {ok, _} -> + OK; Error = {error, _} -> Error end. +-spec handshake_cancel(ssl:sslsocket()) -> ok. +handshake_cancel(CSocket) -> + ok = ssl:handshake_cancel(CSocket). + %% @todo Probably filter Opts? -spec connect(inet:ip_address() | inet:hostname(), inet:port_number(), any()) diff --git a/src/ranch_tcp.erl b/src/ranch_tcp.erl index 3b4e4c3..0d8da2e 100644 --- a/src/ranch_tcp.erl +++ b/src/ranch_tcp.erl @@ -21,7 +21,11 @@ -export([listen/1]). -export([disallowed_listen_options/0]). -export([accept/2]). +-export([handshake/2]). -export([handshake/3]). +-export([handshake_continue/2]). +-export([handshake_continue/3]). +-export([handshake_cancel/1]). -export([connect/3]). -export([connect/4]). -export([recv/3]). @@ -105,10 +109,26 @@ disallowed_listen_options() -> accept(LSocket, Timeout) -> gen_tcp:accept(LSocket, Timeout). +-spec handshake(inet:socket(), timeout()) -> {ok, inet:socket()}. +handshake(CSocket, Timeout) -> + handshake(CSocket, [], Timeout). + -spec handshake(inet:socket(), opts(), timeout()) -> {ok, inet:socket()}. handshake(CSocket, _, _) -> {ok, CSocket}. +-spec handshake_continue(inet:socket(), timeout()) -> no_return(). +handshake_continue(CSocket, Timeout) -> + handshake_continue(CSocket, [], Timeout). + +-spec handshake_continue(inet:socket(), opts(), timeout()) -> no_return(). +handshake_continue(_, _, _) -> + error(not_supported). + +-spec handshake_cancel(inet:socket()) -> no_return(). +handshake_cancel(_) -> + error(not_supported). + %% @todo Probably filter Opts? -spec connect(inet:ip_address() | inet:hostname(), inet:port_number(), any()) diff --git a/src/ranch_transport.erl b/src/ranch_transport.erl index 6790977..5fd6242 100644 --- a/src/ranch_transport.erl +++ b/src/ranch_transport.erl @@ -30,7 +30,11 @@ -callback listen(ranch:transport_opts(any())) -> {ok, socket()} | {error, atom()}. -callback accept(socket(), timeout()) -> {ok, socket()} | {error, closed | timeout | atom()}. --callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake(socket(), timeout()) -> {ok, socket()} | {ok, socket(), any()} | {error, any()}. +-callback handshake(socket(), opts(), timeout()) -> {ok, socket()} | {ok, socket(), any()} | {error, any()}. +-callback handshake_continue(socket(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake_continue(socket(), opts(), timeout()) -> {ok, socket()} | {error, any()}. +-callback handshake_cancel(socket()) -> ok. -callback connect(string(), inet:port_number(), opts()) -> {ok, socket()} | {error, atom()}. -callback connect(string(), inet:port_number(), opts(), timeout()) diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl index 2c8b9e2..7fc26f4 100644 --- a/test/acceptor_SUITE.erl +++ b/test/acceptor_SUITE.erl @@ -57,6 +57,7 @@ groups() -> ssl_echo, ssl_local_echo, ssl_graceful, + ssl_handshake, ssl_sni_echo, ssl_sni_fail, ssl_upgrade_from_tcp, @@ -533,6 +534,35 @@ ssl_echo(_) -> {'EXIT', _} = begin catch ranch:get_port(Name) end, ok. +ssl_handshake(_) -> + doc("Ensure that multiple steps handshake works with SSL transport."), + Name = name(), + {CaCert1, Cert1, Key1} = ct_helper:make_certs(), + {CaCert2, Cert2, Key2} = ct_helper:make_certs(), + Opts1 = [{cert, Cert1}, {key, Key1}, {cacerts, [CaCert1]}, {verify, verify_peer}], + Opts2 = [{cert, Cert2}, {key, Key2}, {cacerts, [CaCert2]}, {verify, verify_peer}], + DefaultOpts = ct_helper:get_certs_from_ets(), + {ok, _} = ranch:start_listener(Name, + ranch_ssl, [{handshake, hello}|DefaultOpts], + handshake_protocol, #{"ranch1" => Opts1, "ranch2" => Opts2}), + Port = ranch:get_port(Name), + {ok, Socket1} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}, + {server_name_indication, "ranch1"}], 5000), + {ok, Cert1} = ssl:peercert(Socket1), + ok = ssl:send(Socket1, <<"SSL Ranch is working!">>), + {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket1, 21, 1000), + {ok, Socket2} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}, + {server_name_indication, "ranch2"}], 5000), + {ok, Cert2} = ssl:peercert(Socket2), + ok = ssl:send(Socket2, <<"SSL Ranch is working!">>), + {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket2, 21, 1000), + ok = ranch:stop_listener(Name), + {error, closed} = ssl:recv(Socket1, 0, 1000), + {error, closed} = ssl:recv(Socket2, 0, 1000), + %% Make sure the listener stopped. + {'EXIT', _} = begin catch ranch:get_port(Name) end, + ok. + ssl_local_echo(_) -> case do_os_supports_local_sockets() of true -> diff --git a/test/handshake_protocol.erl b/test/handshake_protocol.erl new file mode 100644 index 0000000..cedbe2d --- /dev/null +++ b/test/handshake_protocol.erl @@ -0,0 +1,32 @@ +-module(handshake_protocol). +-behaviour(ranch_protocol). + +-export([start_link/3]). +-export([init/3]). + +start_link(Ref, Transport, Opts) -> + Pid = spawn_link(?MODULE, init, [Ref, Transport, Opts]), + {ok, Pid}. + +init(Ref, Transport, Opts) -> + SniHost = case ranch:handshake(Ref) of + %% Due to a bug in ssl (https://bugs.erlang.org/browse/ERL-951, + %% fixed in OTP 22.0.3) the value for sni may be {sni, Hostname} + %% instead of Hostname. + {continue, #{sni := {sni, Hostname}}} -> + Hostname; + {continue, #{sni := Hostname}} -> + Hostname + end, + SniHostOpts = maps:get(SniHost, Opts), + {ok, Socket} = ranch:handshake_continue(Ref, SniHostOpts), + loop(Socket, Transport). + +loop(Socket, Transport) -> + case Transport:recv(Socket, 0, 5000) of + {ok, Data} -> + Transport:send(Socket, Data), + loop(Socket, Transport); + _ -> + ok = Transport:close(Socket) + end. -- cgit v1.2.3