# セッションウェブフック

> **注意**
>
> セッションウェブフックは実験的機能のため、正式版では仕様が変更される可能性があります

## 概要

セッションウェブフックは、チャネルに誰も接続していない状態で新しく接続されたタイミングと、
誰も接続しない状態が一定時間続いたタイミングで、ウェブフックを利用してセッションの状態を HTTP リクエストで送信する機能です。

## 目的

セッションは特定の期間にチャネルに接続していたクライアントを時間でグルーピングする仕組みです。

もともと Sora ではチャネルを作るという概念がありません。
誰もそのチャネルに接続していない状態で新しく接続があれば、新規チャネルが自動的に作られます。

そのため、チャネルが利用されているかされていないかの判断は HTTP API を利用したり、
イベントウェブフックリクエストの送信を利用したりして、アプリケーション側で判断する必要がありました。

このセッションウェブフックは新しくチャネルに状態を持たせ、
その状態をウェブフックリクエストで送信することで開発や運用のコストを減らすことができるようになります。

## セッション ID

特定の期間チャネルに接続していたクライアントをグルーピングするために、 Sora はセッション ID という値を払い出します。
セッション ID は Sora が指定するため、書き換えることはできません。

セッション ID は UUIDv4 を Base32 でエンコードした 26 バイトの文字列です。

### HTTP ヘッダー

> **注釈**
>
> JSON のパース時の判断などに利用してください。

イベントウェブフックの HTTP ヘッダー に `x-sora-session-webhook-type` というヘッダー名でイベントウェブフックのタイプが入ってきます。

`type` が `session.created` の場合は `x-sora-session-webhook-type: session.created` のように値が入ってきます。

## 設定

### ウェブフックリクエスト送信先のサーバーがベーシック認証を利用している場合

もしウェブフックリクエスト送信先のサーバーがベーシック認証を利用している場合は `sora.conf` にて [webhook_basic_authn](SORA_CONF.html#081a9f) に `true` を設定することでベーシック認証を利用できます。

[webhook_basic_authn_user_id](SORA_CONF.html#a107fd) と [webhook_basic_authn_password](SORA_CONF.html#f70fe9) に使用するユーザー ID とパスワードを設定して下さい。

### ウェブフックリクエスト送信先のサーバーが自己署名証明書などを利用している場合

**この設定はおすすめしません**

デフォルトでは自己署名証明書などの正規の認証局から発行されていない証明書を利用した場合には、信頼できないと判断しエラーになります。

もし自己署名証明書を利用したサーバーがウェブフックリクエストの送信先になる場合は、
`sora.conf` にて [webhook_insecure](SORA_CONF.html#676769) に `true` を設定することで証明書のチェックを行わないようになります。

### session_webhook_url

セッションウェブフックリクエストの送信先を指定してください。

```ini
session_webhook_url = https://example.com/sora/session/webhook
```

### session_created_timeout

セッションが存在しない状態で、新しくセッションを生成する際の制限時間です。
これはセッションウェブフック処理も含めた時間で、この時間を超えるとタイムアウトとなり、切断されます。

デフォルトでは 5 秒です。

```ini
session_created_timeout = 5 s
```

### session_destroyed_timeout

そのチャネルの接続数が 0 になってから、何秒間新しい接続が無い場合に `session.destoryed` のイベントウェブフックリクエストを送信するかどうかを指定します。

デフォルトでは 15 秒です。

```ini
session_destroyed_timeout = 15 s
```


## ログ

セッションウェブフックのログは `log/session_webhook.log` に出力されます。
これは `session_webhook_url` を指定していなくても出力されます。

## `multistream` と `spotlight` が既存セッションと異なる場合の挙動

セッションが存在するタイミングでシグナリングのパラメータが異なる接続が来た場合、接続数によって挙動が変わります。

### 既存セッションの接続数が 0 の場合

セッションの接続数が 0 のタイミングで `multistream` と `spotlight` がセッションと異なる新規接続が来た場合は既存のセッションを破棄し `session.destroyed` ウェブフックリクエストを送信します。その後に、新規でセッションを作成し `session.created` ウェブフックリクエストを送信します。

### 既存セッションの接続数が 0 ではない場合

セッションの接続数が 0 ではないタイミングで `multistream` と `spotlight` がセッションと異なる新規接続が来た場合は `INVALID-SIGNALING-PARAMS` エラーを返し切断します。


## session.created ウェブフック

**セッション生成**

同時接続数が 0 のチャネル ID に新しい接続の認証が成功したタイミングで `session.created` ウェブフックリクエストを送信します。

チャネルの同時接続数が 0 の場合でも `multistream` および `spotlight` の値が一致するセッションが過去に生成されており、
まだ破棄されていない場合には、それが使用されるため `session.created` ウェブフックは送信されません。

* - キー
  - 型
  - 内容
* - type
  - string
  - "session.created"
* - id
  - string
  - ウェブフック ID (ユニーク)
* - label
  - string
  - sora.conf の label にて指定した値
* - node_name
  - string
  - Sora ノード名
* - version
  - string
  - Sora のバージョン
* - channel_id
  - string
  - チャネル ID
* - session_id
  - string
  - セッション ID
* - timestamp
  - string (RFC3339 UTC マイクロ秒)
  - ウェブフック作成時間
* - created_time
  - integer (UNIX 時間)
  - セッション作成時間
* - created_timestamp
  - integer (RFC3339 UTC マイクロ秒)
  - セッション作成時間
* - multistream
  - boolean
  - マルチストリームが有効かどうか
* - spotlight
  - boolean
  - スポットライトが有効かどうか

```javascript
{
  "channel_id": "sora",
  "created_time": 1638337454,
  "created_timestamp": "2021-12-01T05:44:14.523736Z",
  "id": "2YN2DQE5KD3GSFSVPAYZ7VTAV4",
  "label": "WebRTC SFU Sora",
  "multistream": true,
  "spotlight": false,
  "node_name": "sora@127.0.0.1",
  "session_id": "NPR769YPQ914K10FW42PGH4TKW",
  "timestamp": "2021-12-01T05:44:14.523912Z",
  "type": "session.created",
  "version": "2022.1.0"
}
```

### 戻り値

通常のイベントウェブフックとは異なり、セッションウェブフックは `"type": "session.created"` の戻り値を指定することができます。

#### session_metadata

`session_metadata` を指定した場合、セッション終了時の `"type": "session.destroyed"` に `session_metadata` が含まれます。

```javascript
{
  "session_metadata": "<JSON>"
}
```


## session.destoryed ウェブフック

**セッション破棄**

同時接続数が 0 になったチャネル ID が一定時間経過したタイミングで `session.destroyed` ウェブフックリクエストを送信します。

* - キー
  - 型
  - 内容
* - type
  - string
  - "session.destoryed"
* - id
  - string
  - ウェブフック ID (ユニーク)
* - label
  - string
  - sora.conf の label にて指定した値
* - node_name
  - string
  - Sora ノード名
* - version
  - string
  - Sora のバージョン
* - channel_id
  - string
  - チャネル ID
* - session_id
  - string
  - セッション ID
* - timestamp
  - string (RFC3339 UTC マイクロ秒)
  - ウェブフック作成時間
* - created_time
  - integer (UNIX 時間)
  - セッション作成時間
* - created_timestamp
  - integer (RFC3339 UTC マイクロ秒)
  - セッション作成時間
* - destroyed_time
  - integer (UNIX 時間)
  - セッション破棄時間
* - destroyed_timestamp
  - integer (RFC3339 UTC マイクロ秒)
  - セッション破棄時間
* - multistream
  - boolean
  - マルチストリームが有効かどうか
* - spotlight
  - boolean
  - スポットライトが有効かどうか
* - total_connections
  - integer
  - 延べ接続数
* - max_connections
  - integer
  - 最大同時接続数
* - connections
  - array (object)
  - セッションに参加した接続情報

```javascript
{
  "channel_id": "sora",
  "connections": [
    {
      "audio": {
        "codec_type": "OPUS"
      },
      "client_id": "W9QE86Z0BS1QFFPZ2QB5DRZ2HC",
      "bundle_id": "W9QE86Z0BS1QFFPZ2QB5DRZ2HC",
      "connection_id": "W9QE86Z0BS1QFFPZ2QB5DRZ2HC",
      "created_timestamp": "2021-12-01T05:44:23.051704Z",
      "destroyed_timestamp": "2021-12-01T05:44:57.083215Z",
      "role": "sendrecv",
      "simulcast": false,
      "video": {
        "bit_rate": 1000,
        "codec_type": "VP9"
      }
    },
    {
      "audio": {
        "codec_type": "OPUS"
      },
      "client_id": "JXMYW6GPX54EH0HGA5X4130FBM",
      "bundle_id": "JXMYW6GPX54EH0HGA5X4130FBM",
      "connection_id": "JXMYW6GPX54EH0HGA5X4130FBM",
      "created_timestamp": "2021-12-01T05:44:21.500272Z",
      "destroyed_timestamp": "2021-12-01T05:44:56.878019Z",
      "role": "sendrecv",
      "simulcast": false,
      "video": {
        "bit_rate": 1000,
        "codec_type": "VP9"
      }
    },
    {
      "audio": {
        "codec_type": "OPUS"
      },
      "client_id": "F8VK9R71BN5S5EDE737C8XAA3C",
      "bundle_id": "F8VK9R71BN5S5EDE737C8XAA3C",
      "connection_id": "F8VK9R71BN5S5EDE737C8XAA3C",
      "created_timestamp": "2021-12-01T05:44:19.811385Z",
      "destroyed_timestamp": "2021-12-01T05:44:55.897819Z",
      "role": "sendrecv",
      "simulcast": false,
      "video": {
        "bit_rate": 1000,
        "codec_type": "VP9"
      }
    },
    {
      "audio": {
        "codec_type": "OPUS"
      },
      "client_id": "VBYS5TV5S510F98GS2VK015AKG",
      "bundle_id": "VBYS5TV5S510F98GS2VK015AKG",
      "connection_id": "VBYS5TV5S510F98GS2VK015AKG",
      "created_timestamp": "2021-12-01T05:44:14.716974Z",
      "destroyed_timestamp": "2021-12-01T05:44:57.197372Z",
      "role": "sendrecv",
      "simulcast": false,
      "video": {
        "bit_rate": 1000,
        "codec_type": "VP9"
      }
    }
  ],
  "created_time": 1638337454,
  "created_timestamp": "2021-12-01T05:44:14.523736Z",
  "destroyed_time": 1638337502,
  "destroyed_timestamp": "2021-12-01T05:45:02.199179Z",
  "id": "M7K1AVS9KS34V9XFJJH04TB400",
  "label": "WebRTC SFU Sora",
  "max_connections": 4,
  "multistream": true,
  "node_name": "sora@127.0.0.1",
  "session_id": "NPR769YPQ914K10FW42PGH4TKW",
  "spotlight": false,
  "timestamp": "2021-12-01T05:45:02.199419Z",
  "total_connections": 4,
  "type": "session.destroyed",
  "version": "2022.1.0"
}
```


## session.vanished ウェブフック

Sora のモードが `block_new_session` または `block_new_connection` の時に、
全てのセッションがなくなったタイミングでウェブフックリクエストを送信します。

このウェブフックリクエストを受信するには `sora.conf` にて [ignore_session_vanished_webhook](SORA_CONF.html#85718d) を `false` に設定してください。

```javascript
{
  "id": "047MK20PWD14QBPBA2JAK585JM",
  "label": "WebRTC SFU Sora",
  "node_name": "sora@127.0.0.1",
  "timestamp": "2021-10-05T01:51:57.977800Z",
  "type": "session.vanished",
  "version": "2022.1.0"
}
```

## シーケンス図

seqdiag {
    default_fontsize = 12;
    edge_length = 200;

    クライアント1; クライアント2; Sora; アプリケーションサーバー

    クライアント1 ->> Sora [label = '"type": "type"'];

        Sora -> アプリケーションサーバー [label = 'セッションウェブフック\n"type": "session.created"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント1 <<- Sora [label = '"type": "offer"'];
    クライアント1 ->> Sora [label = '"type": "answer"', noactivate];

    === クライアント1 WebRTC 確立 ===


        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント2 ->> Sora [label = '"type": "type"'];
    クライアント2 <<- Sora [label = '"type": "offer"'];
    クライアント2 ->> Sora [label = '"type": "answer"', noactivate];

    === クライアント2 WebRTC 確立 ===

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント1 ->> Sora [label = '"type": "disconnect"', noactivate];

            Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.destroyed"'];
            Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント1 <<- Sora [label = 'WebSocket Close'];

    === クライアント2 WebRTC 切断 ===

        クライアント2 ->> Sora [label = '"type": "disconnect"', noactivate];

            Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.destroyed"'];
            Sora <-- アプリケーションサーバー [label = '200 OK'];

        クライアント2 <<- Sora [label = 'WebSocket Close'];

    === クライアント2 WebRTC 切断 ===

        ... 接続数 0 から 15 秒経過 ...

        Sora -> アプリケーションサーバー [label = 'セッションウェブフック\n"type": "session.destoryed"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

        ... セッションがすべて破棄された ...

        Sora -> アプリケーションサーバー [label = 'セッションウェブフック\n"type": "session.vanished"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];
}
