ブラウザでTCPを直接送受信できるDirect Sockets APIについて

ブラウザから直接TCPUDPで送受信する「Direct Sockets API」という仕組みが議論されています。

実験段階ですが、Chromeでは起動時にオプションを付けることでこの機能を有効にできます。今回はTCPの方で簡単に動作を見てみます。

Direct Sockets API

Direct Sockets APIは、TCPUDPで直接送受信可能にするAPIです。既存のアプリケーションプロトコル(SSHIRC)、P2Pのような機能を実現可能になります。

もちろんセキュリティ上の問題もあるので、Chromeでは現状デフォルトでは有効になっていない機能です。

セキュリティ面についてはだいぶGithubリポジトリで議論されておりますので目を通すと良いでしょう。ローカルネットワークへの通信やSame-Origin-Policy(CORS)回避の話が上がっていますが、今回は細かくは紹介しません。

その他、意見がある場合はコメントすることもできます。
github.com

試してみる

Chromeでは実験的に実装されています。おそらくまだ実装途中ですので、今後変わる可能性は高いです。

今回は、現在時点のChrome Canaryで動作確認しています。

クライアント側準備

起動時にオプションが必要です。
Chrome 99 では次の起動オプションをつけて、Chromeを起動します (現在の起動コマンドは、chrome:version から確認できます)

  • --enable-blink-features=DirectSockets
  • --enable-features=DirectSockets
  • --restricted-api-origins=https://asnokaze.com
サーバ側準備

有効にするページ(上記 --restricted-api-origins で指定したページ)で、次のレスポンスヘッダをつけます

  • Cross-Origin-Embedder-Policy: require-corp
  • Cross-Origin-Opener-Policy: same-origin
  • Permissions-Policy: direct-sockets=(self)

なお、TCPサーバ特定ポートで接続を受け付けるために、下記のコマンドを実行して123番ポートでリッスンしておきます。(well-knownポートが使用できるか確認するために、123番を使用)

$ while true; do ( echo "Hello from server" ) | sudo nc -l 123; done

つなぐ

Webページを開いたら、今回はデベロッパーツールで以下に示すスクリプトを実行します。このスクリプトでは

  • 指定されたサーバに、123番ポートで接続して、"Hello from chrome" と書き込みます
  • サーバから送信されたデータをデベロッパーツールに出力します

すると、ダイアログが出るので接続先を入力します。(現状は任意のサーバ・任意のポートが指定可能...)
f:id:ASnoKaze:20220104001004p:plain

const options = {
    remoteAddress: 'asnokaze.com',
    remotePort: 123
};
navigator.openTCPSocket(options).then(tcpSocket => {
	readableStream = tcpSocket.readable;
	writableStream = tcpSocket.writable;
	const defaultWriter = writableStream.getWriter();
	defaultWriter.ready.then(() => {
		return defaultWriter.write(str2ab("Hello from chrome"));
	})
	const defaultReader = readableStream.getReader()
	defaultReader.read().then( ({done, value}) => {
		console.log(ab2str(value));
	})
})

function str2ab(str) {
	var buf = new ArrayBuffer(str.length);
	var bufView = new Uint8Array(buf);
	for (var i=0, strLen=str.length; i < strLen; i++) {
		bufView[i] = str.charCodeAt(i);
	}
	return buf;
}

function ab2str(buf) {
	return String.fromCharCode.apply(null, new Uint8Array(buf));
}
サーバ側

クライアントからのメッセージが表示されました

$ while true; do ( echo "Hello from server" ) | sudo nc -l 123; done
Hello from chrome
クライアント側

サーバからのメッセージが表示されました
f:id:ASnoKaze:20220104001351p:plain

標準化に対する姿勢

この仕様は、かなり強力な機能であるため、標準化に対する姿勢はそれぞれです。

そもそもこの仕様はGoogleの方によって提案されています。しかし、Mozillaは反意を示しています(参考URL)。その他、Node.jsやDenoの領域でこの仕様に興味をもっている方も居ます。

なんにせよ、すぐに標準的な仕様になりそうというものではなさそうです。