-
Notifications
You must be signed in to change notification settings - Fork 5
Handler
以下の例を確認してください
@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
GenServer.init/2
に似ている役割を持っています。QUICのハンドシェイクに成功した後に呼ばれます。
引数が2つあり、ConnectionStateと、ClientIndicationです。
必要ならClientIndicationのパラメータであるoriginとpathの値を確認し、任意の処理を行うことが出来ます。 上の例ではoriginが正しくなければここで切断し、originが期待通りであれば接続を維持するような処理になっています。
WebTransportモードをoffにした場合は、client indicationの部分はnilが渡されます。詳しくはWebTransportのページを参照してください。
originの扱いは、HTTPにおけるCORS と同じような話だと捉えてもらってよいです。
pathはこのサーバーにアクセスされるときに指定されたURLのパスの部分が入ります。
quic-transport://my-service.example.org/foobar
というURLでアクセスされたとしたら、/foobar
の部分が入ります。
必要によっては、この値を見て、サービスを分岐させて見ると良いでしょう。
特に問題が無ければ、{: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
@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}
各コールバックで渡される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