Skip to content

Handler

Lyo Kato edited this page Jan 4, 2021 · 42 revisions

Callbacks

init/2

以下の例を確認してください

@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のハンドシェイクに成功した後に呼ばれます。

Arguments

引数が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の部分が入ります。

必要によっては、この値を見て、サービスを分岐させて見ると良いでしょう。

Response

特に問題が無ければ、{: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}

handle_info/3

こちらも例を見れば何となく理解できるかと思います。

@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_cast/3

@impl Requiem
def handle_cast({:my_event, my_param}, conn, state) do
   do_something(my_param)
  {:noreply, conn, state}
end

こちらも上記のhandle_info/2と同じように、対応するGenServer.handle_cast/2とほぼ同じ役割の関数だと考えてくささい。

  @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}

handle_call/4

@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}
              | {:stop, non_neg_integer, atom}

terminate/3

  @type terminate_reason :: :normal | :shutdown | {:shutdown, term} | term

  @callback terminate(
              reason :: terminate_reason,
              conn :: Requiem.ConnectionState.t(),
              state :: any
            ) :: :ok

handle_stream/4


handle_dgram/3


Methods

close/0

close/2

stream_send/2

dgram_send/2


ConnectionState

各コールバックで渡されるconnの実態はRequiem.ConnectionStateモジュールで定義されている構造体です。

以下のようなフィールドを持っています

  • conn.scid
  • conn.dcid
  • conn.odcid

これらはQUICのフレームで利用される接続IDで、クライアントが接続開始の際に指定したものです。 (サーバー側がクライアントに対して指定したものではありません) odcidは、retryフレームを利用したトークンによるバリデーションを行う前に指定されたものです。

また、相手のAddressを持っています

  • conn.address

これはRequiem.Addressモジュールの構造体です。

  • conn.address.host
  • conn.address.port

のように相手の接続に関する情報にアクセスできます。


ClientIndication

ConnectionState以外にも知っておかなければならないものが一つあります。 init/2の第二引数として渡されるClientIndicationです。

@impl Requiem
def init(conn, client) do
  my_state = %{}
  {:ok, conn, my_state}
end

これは、WebTransportモードのときだけ利用されます。 WebTransportモードではない場合はここにはnilが渡されます。 (WebTransportモードについて詳しくは、ConfigurationWebTransportを参照してください。)

WebTransportモードの場合は、クライアントからstream_idが2のstreamを通して受け取ったデータを自動的にパースし、init/2に渡します。

ここで、originpathの値をチェックして、問題があれば切断することが出来ます。

@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
Clone this wiki locally