nest: ユースケースの整理と実装計画

冷静に考えると、Web Pushのsubscriptionにはそもそもpushを送る先のエンドポイントが入ってるんだから、わざわざFCMを経由する必要性は無い気がする(本当?) 実際 web-push https://www.npmjs.com/package/web-push とか見る限りFCMトークンはオプショナルなんだよなぁ。。
やりたい事
P2PでWebアプリを提供したい。また、デバイスとのLAN上直接通信も実現したい。つまりユースケースとしては:
- Webブラウザ ← (WebRTC) → Webブラウザ: 大トラフィックはWebRTCを経由する
- Webブラウザ ← (pipe) → Webブラウザ: Web Socketの双方向pipeをCloudflare(や、Caddy)の向うでやりたい(可能?) -- WebRTCのシグナリング通信もこれで行う
- Webブラウザ → (notification) → Webブラウザ: 接続開始イベント
シグナリングもWebPushで済ませたいが流石にちょっとパフォーマンス的にダメそうなのでpipe serverを用意する。

通信に使用する要素
通信に使用する要素は4つ:
- (イベント通知) Web Push: https://w3c.github.io/push-api/
- (イベント通知) HTTP サーバー: デバイスのみ
- (ストリーミングchat、データ転送) WebSocket pipe service
- (データ転送) WebRTC
Web Pushはブラウザベンダが運営しているブローカーにhttpリクエストすることで通知を送信する。これは対デバイスではどうしようもないので、別途HTTPシグナリングを別にプロトコルを定めて実装することになる。
WebSocket pipe serviceは https://qiita.com/nwtgck/items/78309fc529da7776cba0 のWebSocket版を想定している。たぶんこれが一番ユニークだから真面目に設計する必要がありそうだな。。P2PのWebRTCが使えないケースでTURNの代りに使えると便利そうだけど設計が面倒になる(上限を静的に入れづらい)のでダメかな。

ユースケース1: LinuxホストにWebブラウザからログインする
いやまぁコレいくらでもソリューションあるけどsshを外部サーバーでproxyするみたいな実装が多く、E2Eソリューションになっていることが殆どない気がする。
前提: ホストとブラウザは同じネットワークに居てWebRTCが疎通できる。 LinuxホストはTCP listenできない 。
- ホストはpipe serviceに接続し、接続リクエストを待ち受ける(Websocket上で)。
- ブラウザはpipe serviceの対向pipeに接続し、接続リクエストをする。
- ホスト ←→ ブラウザでシグナリングし、Datachannelを確立する
- Datachannel上で通信する
そもそもどのくらいCloudflareのWebsocket proxyが無traffic期間を許してくれるのかが謎。いわゆるhttpのlong pollingはそれなりの期間(1分とか)でも大丈夫だったので、1分ごとに接続をrenewするとして一日2000コネクションくらいならまぁ許容範囲か。

ユースケース2: Webブラウザ同士で通信する
これは普通のWebRTC。
前提: ステートレスのpush brokerサービスが存在する。
- それぞれブラウザはWeb Pushを事前にsubscribeしておき、subscriptionとVAPIDをbrokerの鍵で暗号化して交換しておく。
- 接続要求をbrokerに送信する
- brokerはリクエストを復号し、対向するブラウザにpushする
- リクエストに含まれるURLを元にpipeに接続する
- ブラウザ ←→ ブラウザでシグナリングし、Datachannelを確立する
- Datachannel上で通信する
ブラウザ単体ではブラウザベンダのPush serviceのURLには書き込めないため、インターネット上にbrokerが必要となる。
FCMのようなサービスはpush brokerになれるが、少くともFCMは配信用のAPIキーをクライアントに配る設計でない(と思う)ので自前のbrokerの設計と実装が必要。

ユースケース3: ローカルデバイスを制御する
前提: ローカルデバイスはTCP listenができる。 ローカルデバイスはインターネットに出ていけない 。
これは前の記事で書いた通りなので略。

2つのステートレスサービス
可能な限り運営コストを圧縮するためには、インターネット上のサービスはステートレスにしたい。まぁコネクションとかは仕方ないとしてpersistentな状態を可能な限り排除して設計したいところ。
Push broker
Web pushのsubscriptionはシリアライズ可能で、これを適当に暗号化してみんなに配ってしまうという方向が考えられる。subscription自体は匿名(ただし使っているブラウザベンダはバレる)であり、万一悪いactorに渡してしまったとしても大きな問題ではない。
VAPID origin != Web origin なケースが許されているのかは規格書からは読み取れない。ただ、そもそもサービスワーカーの登録はoriginとなるURL群につき1つなので、基本的にはp2pで使うべきものだろう。
Pipe server
Pipeはマジで設計が難しい。可能な限り機能を減らして、Cloudflare側でアクセス制限掛けるくらいしかアイデアがない。... そもそもCloudflareで求めている機能が実現できるかわからないから、一旦簡単なサーバーを書いてみるのが良いかな。