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

> **注意**
>
> この機能を利用する場合は事前にサポートまでご連絡ください

> **警告**
>
> この機能は [実験的機能](EXPERIMENTAL.html) のため、正式版では仕様が変更される可能性があります

## 概要

セッションウェブフックは、セッション単位で扱う機能のウェブフックです。

例えばチャネルに誰も接続していない状態で新しく接続されたタイミング、
誰も接続しない状態が一定時間続いたタイミングで、ウェブフックを利用してセッションの状態などを 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.destroyed` のセッションウェブフックリクエストを送信するまでの時間を指定します。

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

```ini
session_destroyed_timeout = 15 s
```


## ログ

セッションウェブフックのログは `log/session_webhook.jsonl` または `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
  - スポットライトが有効かどうか
* - external_signaling_url
  - string
  - (クラスター機能が有効の場合) 接続先の external_signaling_url

```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",
  "external_signaling_url": "wss://node-01.example.com/signaling",
  "type": "session.created",
  "version": "2022.1.0"
}
```

### 戻り値

セッションウェブフックは `"type": "session.created"` の戻り値を指定できます。

#### session_metadata

`session_metadata` を指定した場合、セッション終了時の `"type": "session.destroyed"` のウェブフックリクエストに `session_metadata` が含まれます。

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

#### forwarding_filter

`forwarding_filter` を指定した場合、転送フィルターをセッションに対して反映することができます。

転送フィルタールールの詳細については [転送フィルター機能](FORWARDING_FILTER.html) をご確認ください。

```javascript
{
  "forwarding_filter": {
    "action": "allow",
    "rules": [
      // チャネルに接続しているすべてのコネクションに音声のみ転送する
      [
       {"field": "kind", "operator": "is_in", "values": ["audio"]}
      ]
    ]
  }
}
```

#### audio_streaming

`audio_streaming` を `true` に指定した場合、そのセッションで音声ストリーミングが有効になります。

音声ストリーミング機能の詳細については [音声ストリーミング機能](AUDIO_STREAMING.html) をご確認ください。

```javascript
{
  "audio_streaming": true
}
```

#### audio_streaming_auto

`audio_streaming_auto` を `true` に指定した場合、そのセッションで音声ストリーミングが有効になり、
かつ自動開始、自動停止機能が有効になります。
この設定を `true` にするときには `audio_streaming` も `true` に設定する必要があります。

詳細は [audio_streaming 関連の払い出し設定の組み合わせによる動作](AUDIO_STREAMING.html#e28478) をご確認ください。

```javascript
{
  "audio_streaming": true,
  "audio_streaming_auto": true
}
```


## session.destroyed ウェブフック

**セッション破棄**

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

* - キー
  - 型
  - 内容
* - type
  - string
  - "session.destroyed"
* - 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)
  - セッションに参加した接続情報
* - session_metadata
  - json
  - (値がある場合) session.created の戻り値で指定した値
* - external_signaling_url
  - string
  - (クラスター機能が有効の場合) 接続先の external_signaling_url

```javascript
{
  "channel_id": "sora",
  "connections": [
    {
      "audio": true,
      "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": true,
      "video_bit_rate": 1000,
      "vieo_codec_type": "VP9",
      "video_vp9_params": { "profile_id": 0 },
    },
    {
      "audio": true,
      "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": true,
      "video_bit_rate": 1000,
      "vieo_codec_type": "VP9",
      "video_vp9_params": { "profile_id": 0 },
    },
    {
      "audio": true,
      "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": true,
      "video_bit_rate": 1000,
      "vieo_codec_type": "VP9",
      "video_vp9_params": { "profile_id": 0 },
    },
    {
      "audio": true,
      "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": true,
      "video_bit_rate": 1000,
      "vieo_codec_type": "VP9",
      "video_vp9_params": { "profile_id": 0 },
    }
  ],
  "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,
  "session_metadata": {"spam": "egg"},
  "external_signaling_url": "wss://node-01.example.com/signaling",
  "type": "session.destroyed",
  "version": "2022.1.0"
}
```


## session.vanished ウェブフック

> **重要**
>
> このウェブフックリクエストが送信されるには
> `sora.conf` の [ignore_session_vanished_webhook](SORA_CONF.html#85718d) が `false` である必要があります。

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

```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"
}
```


## audio-streaming.started ウェブフック

> **重要**
>
> このウェブフックリクエストが送信されるには
> `sora.conf` の [ignore_audio_streaming_webhook](SORA_CONF.html#8f086b) が `false` である必要があります。

以下の 2 つの条件で音声ストリーミングが開始した時にウェブフックリクエストを送信します。

- [StartAudioStreaming](EXPERIMENTAL_API_AUDIO_STREAMING.html#0f1087) API が実行されるか、またはセッションウェブフックで `audio_streaming: true` 払い出されたタイミングでウェブフックリクエストを送信します。
- 音声ストリーミングの [ウェブフック](AUDIO_STREAMING.html#a62cea) を利用時に[SubscribeAudioStreamingResultPush](EXPERIMENTAL_API_AUDIO_STREAMING.html#d26262) API が呼び出される、もしくはクライアントが接続されて、そのチャネルで音声ストリーミングの結果をサブスクライブしているクライアント数が 1 以上になった場合

[session.created ウェブフック](SESSION_WEBHOOK.html#3cec61) よりも必ず **後に** ウェブフックが送信されます。

* - キー
  - 型
  - 内容
* - type
  - string
  - "audio-streaming.started"
* - 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
  - string (RFC3339 UTC マイクロ秒)
  - セッション作成時間
* - multistream
  - boolean
  - マルチストリームが有効かどうか
* - spotlight
  - boolean
  - スポットライトが有効かどうか
* - total_connections
  - integer
  - 延べ接続数
* - max_connections
  - integer
  - 最大同時接続数
* - session_metadata
  - json
  - (値がある場合) session.created の戻り値で指定した値
* - external_signaling_url
  - string
  - (クラスター機能が有効の場合) 接続先の external_signaling_url
* - data
  - object
  - 音声ストリーミング固有

data には以下が含まれます。

* - キー
  - 型
  - 内容
* - audio_streaming_auto
  - boolean
  - 自動開始/停止が有効かどうか
* - audio_streaming_started_timestamp
  - string (RFC3339 UTC マイクロ秒)
  - 音声ストリーミングを開始した時間

```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": "2022-12-01T05:44:14.523912Z",
  "type": "audio-streaming.started",
  "version": "2023.1.0",
  "max_connections": 4,
  "total_connections": 4,
  "session_metadata": {"spam": "egg"},
  "external_signaling_url": "wss://node-01.example.com/signaling",
  "data": {
    "audio_streaming_auto": true,
    "audio_streaming_started_timestamp": "2021-12-01T05:44:16.523912Z"
  }
}
```


## audio-streaming.stopped ウェブフック

> **重要**
>
> このウェブフックリクエストが送信されるには
> `sora.conf` の [ignore_audio_streaming_webhook](SORA_CONF.html#8f086b) が `false` である必要があります。

以下の 3 つの条件で音声ストリーミングが停止した時にウェブフックリクエストを送信します。

- セッションが終了した場合
- [StopAudioStreaming](EXPERIMENTAL_API_AUDIO_STREAMING.html#bad1bb) API が呼び出す場合
- 音声ストリーミングの [ウェブフック](AUDIO_STREAMING.html#a62cea) を利用時に[UnsubscribeAudioStreamingResultPush](EXPERIMENTAL_API_AUDIO_STREAMING.html#b63656) API が呼び出される、もしくはクライアントが切断されて、そのチャネルで音声ストリーミングの結果をサブスクライブしているクライアント数が 0 になった場合

[session.destroyed ウェブフック](SESSION_WEBHOOK.html#fdf2da) よりも必ず **前に** ウェブフックが送信されます。

* - キー
  - 型
  - 内容
* - type
  - string
  - "audio-streaming.started"
* - 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
  - string (RFC3339 UTC マイクロ秒)
  - セッション作成時間
* - multistream
  - boolean
  - マルチストリームが有効かどうか
* - spotlight
  - boolean
  - スポットライトが有効かどうか
* - total_connections
  - integer
  - 延べ接続数
* - max_connections
  - integer
  - 最大同時接続数
* - session_metadata
  - json
  - (値がある場合) session.created の戻り値で指定した値
* - external_signaling_url
  - string
  - (クラスター機能が有効の場合) 接続先の external_signaling_url
* - data
  - object
  - 音声ストリーミング固有

data には以下が含まれます。

* - キー
  - 型
  - 内容
* - audio_streaming_auto
  - boolean
  - 自動開始/停止が有効かどうか
* - audio_streaming_started_timestamp
  - string (RFC3339 UTC マイクロ秒)
  - 音声ストリーミングを開始した時間
* - audio_streaming_stopped_timestamp
  - string (RFC3339 UTC マイクロ秒)
  - 音声ストリーミングを停止した時間

```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": "2022-12-01T05:44:14.523912Z",
  "type": "audio-streaming.stopped",
  "version": "2023.1.0",
  "max_connections": 4,
  "total_connections": 4,
  "session_metadata": {"spam": "egg"},
  "external_signaling_url": "wss://node-01.example.com/signaling",
  "data": {
    "audio_streaming_auto": true,
    "audio_streaming_started_timestamp": "2021-12-01T05:44:16.523912Z",
    "audio_streaming_stopped_timestamp": "2021-12-01T05:44:16.523912Z"
  }
}
```

## API

### TerminateSession API

セッションを強制的に終了させる API です。
`channel_id` を指定して、セッションを終了させます。
`session_id` を追加で指定することができますが、 `session_id` が見つからない場合はエラーになります。

API 実行中に新規の接続が来た場合、その接続はいったん保留して、セッション終了後に新規セッションでの接続として扱います。

## シーケンス図

```mermaid
sequenceDiagram
    autonumber
    participant クライアント1
    participant クライアント2
    クライアント1->>Sora: type: connect
    Sora->>+アプリケーションサーバー: 認証ウェブフック
    アプリケーションサーバー-->>-Sora: 200 OK
    Sora->>+アプリケーションサーバー: セッションウェブフック<br/>type: session.created
    アプリケーションサーバー-->>-Sora: 200 OK
    Sora->>クライアント1: type: offer
    クライアント1->>Sora: type: answer
    Note over クライアント1,アプリケーションサーバー: クライアント1 WebRTC 確立
    Sora->>+アプリケーションサーバー: イベントウェブフック<br/>type: connection.created
    アプリケーションサーバー-->>-Sora: 200 OK
    クライアント2->>Sora: type: connect
    Sora->>+アプリケーションサーバー: 認証ウェブフック
    アプリケーションサーバー-->>-Sora: 200 OK
    Sora->>クライアント2: type: offer
    クライアント2->>Sora: type: answer
    Note over クライアント1,アプリケーションサーバー: クライアント2 WebRTC 確立
    Sora->>+アプリケーションサーバー: イベントウェブフック<br/>type: connection.created
    アプリケーションサーバー-->>-Sora: 200 OK
    クライアント1->>Sora: "type": "disconnect"
    Sora->>+アプリケーションサーバー: イベントウェブフック<br/>type: connection.destroyed
    アプリケーションサーバー-->>-Sora: 200 OK
    Sora->>クライアント1: WebSocket Close
    Note over クライアント1,アプリケーションサーバー: クライアント1 WebRTC 切断
    クライアント2->>Sora: "type": "disconnect"
    Sora->>+アプリケーションサーバー: イベントウェブフック<br/>type: connection.destroyed
    アプリケーションサーバー-->>-Sora: 200 OK
    Sora->>クライアント2: WebSocket Close
    Note over クライアント1,アプリケーションサーバー: クライアント2 WebRTC 切断
    note right of Sora: 接続数 0 から 15 秒経過
    Sora->>+アプリケーションサーバー: セッションウェブフック<br/>type: session.destroyed
    アプリケーションサーバー-->>-Sora: 200 OK
    note right of Sora: セッションがすべて破棄された
    Sora->>+アプリケーションサーバー: セッションウェブフック<br/>type: session.vanished
    アプリケーションサーバー-->>-Sora: 200 OK
```
