-
Notifications
You must be signed in to change notification settings - Fork 5
Handler
Check out the following example
@impl Requiem
def init(conn, client) do
Logger.debug("new connection is established, connection_id: #{conn.dcid}")
if client.origin == "my-origin.example.com" do
my_state = %{path: client.path}
{:ok, conn, state}
else
{:stop, 400, :forbidden}
end
end
It has a role similar to GenServer.init/2
, and is called after a successful QUIC handshake.
There are two arguments, ConnectionState and ClientIndication.
If necessary, you can check the values of the ClientIndication parameters, origin
and path
, and do whatever you want. In the example above, if the origin
is not correct, the connection is disconnected at this point, and if the origin
is as expected, the connection is maintained.
If WebTransport mode
is turned off, the client indication variable will be passed as nil. See the WebTransport mode page for details.
The handling of origin
is similar to that of CORS in HTTP.
path
is the part of the URL path specified when this server is accessed.
If the URL is quic-transport://my-service.example.org/foobar
, then the /foobar
part will be included.
You may want to look at this value to see if you can branch the service if necessary.
特に問題が無ければ、{:ok, conn, state}
のように値を返してください。自由にstateを作って渡すところは、GenServer.init/1
と同じです。
違うのはConnectionStateの構造体も一緒に引き回すということだけです。
問題があって接続をここで遮断したい場合は{:stop, error_code, reason}
という形のデータを返します。
ここで使われたerror_codeとreasonは、クライアントに対して送信するCLOSE FRAME
に使われます。
@callback init(conn :: Requiem.ConnectionState.t(), client :: Requiem.ClientIndication.t()) ::
{:ok, Requiem.ConnectionState.t(), any}
| {:ok, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
こちらも例を見れば何となく理解できるかと思います。
@impl Requiem
def handle_info({:my_event, my_param}, conn, state) do
do_something(my_param)
{:noreply, conn, state}
end
引数とレスポンスに、ConnectionStateの構造体を引き回す以外は、ほぼGenServer.handle_info/2と同じようなものだと思ってください。 このプロセスがメッセージを受信したときに呼ばれます。
@callback handle_info(
request :: term,
conn :: Requiem.ConnectionState.t(),
state :: any
) ::
{:noreply, Requiem.ConnectionState.t(), any}
| {:noreply, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
こちらも上記のhandle_info/2と同じように、対応するGenServer.handle_cast/2とほぼ同じ役割の関数だと考えてください。
@impl Requiem
def handle_cast({:my_event, my_param}, conn, state) do
do_something(my_param)
{:noreply, conn, state}
end
@callback handle_cast(
request :: term,
conn :: Requiem.ConnectionState.t(),
state :: any
) ::
{:noreply, Requiem.ConnectionState.t(), any}
| {:noreply, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
@impl Requiem
def handle_call({:my_event, my_param}, from, conn, state) do
{:reply, conn, state}
end
こちらも上記のhandle_info/2やhandle_cast/2と同様です。
@callback handle_call(
request :: term,
from :: pid,
conn :: Requiem.ConnectionState.t(),
state :: any
) ::
{:noreply, Requiem.ConnectionState.t(), any}
| {:noreply, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:reply, any, Requiem.ConnectionState.t(), any}
| {:reply, any, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
同様にterminate/3という関数もあります。
@impl Requiem
def terminate(reason, conn, state) do
:ok
end
@type terminate_reason :: :normal | :shutdown | {:shutdown, term} | term
@callback terminate(
reason :: terminate_reason,
conn :: Requiem.ConnectionState.t(),
state :: any
) :: any
ストリームに受信したデータを処理することが出来る関数です
@impl Requiem
def handle_stream(stream_id, data, conn, state) do
case do_something(stream_id, data) do
{:ok, good_result} -> {:ok, conn, state}
{:error, :bad_result} -> {:stop, 400, :bad_request}
end
end
connとstateはもう見慣れたと思います。最初の2つの引数に指定されたstream_idとデータが渡されます。
問題がなければ{:ok, conn, state}
という形でデータを返してください。
問題があり、接続を閉じたければ{:stop, error_code, reason}
という形でデータを返してください。
@callback handle_stream(
stream_id :: non_neg_integer,
data :: binary,
conn :: Requiem.ConnectionState.t(),
state :: any
) ::
{:ok, Requiem.ConnectionState.t(), any}
| {:ok, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
データグラムとして送信されたデータを扱えます。
stream_idのようなものが存在しないだけで、それ以外はhandle_stream/4
と同じです
@impl Requiem
def handle_dgram(data, conn, state) do
case do_something(stream_id, data) do
{:ok, good_result} -> {:ok, conn, state}
{:error, :bad_result} -> {:stop, 400, :bad_request}
end
end
enable_dgramのconfigが指定されている必要があります。
See Configuration for details.
@callback handle_dgram(
data :: binary,
conn :: Requiem.ConnectionState.t(),
state :: any
) ::
{:ok, Requiem.ConnectionState.t(), any}
| {:ok, Requiem.ConnectionState.t(), any, timeout | :hibernate}
| {:stop, non_neg_integer, atom}
ハンドラ内で以下の関数が利用できるようになっています。
各コールバックの戻り値で{:stop, error_code, reason}
を返すことでコネクションの切断は可能ではありますが、
コールバックのタイミングとは別の非同期の処理の中で切断をしたいときがあるでしょう。
そういう場合にはこのclose
を呼んでください。
error_code, reasonを渡せるバージョンのcloseもあります。上のバージョンは正常終了として扱われますが、こちらはアプリケーションエラーを表現できます。
close(error_code, reason)
ストリームを通して、クライアントにデータを送信したいときに使います
stream_send(stream_id, data)
このように使えますが、stream_idの値には注意してください。
See Stream for details.
ストリームではなくデータグラムを送信したいときに使えます。
enable_dgramのconfigが指定されている必要があります。
See Configuration for details.
dgram_send(data)
各コールバックで渡されるconnの実態はRequiem.ConnectionStateモジュールで定義されている構造体です。
以下のようなフィールドを持っています
- conn.scid
- conn.dcid
- conn.odcid
これらはQUICのフレームで利用される接続IDで、クライアントが接続開始の際に指定したものです。 (サーバー側がクライアントに対して指定したものではありません) odcidは、retryフレームを利用したトークンによるバリデーションを行う前に指定されたものです。
また、相手のAddressを持っています
- conn.address
これはRequiem.Addressモジュールの構造体です。
- conn.address.host
- conn.address.port
のように相手の接続に関する情報にアクセスできます。
ConnectionState以外にも知っておかなければならないものが一つあります。
init/2
の第二引数として渡されるClientIndicationです。
@impl Requiem
def init(conn, client) do
my_state = %{}
{:ok, conn, my_state}
end
これは、WebTransportモードのときだけ利用されます。
WebTransportモードではない場合はここにはnil
が渡されます。
(WebTransportモードについて詳しくは、ConfigurationやWebTransportを参照してください。)
WebTransportモードの場合は、クライアントからstream_idが2のstreamを通して受け取ったデータを自動的にパースし、init/2
に渡します。
ここで、origin
やpath
の値をチェックして、問題があれば切断することが出来ます。
@impl Requiem
def init(conn, client) do
if client.origin == "example.org" do
my_state = %{path: client.path}
{:ok, conn, my_state}
else
{:stop, 400, :shutdown}
end
end