この記事はDeno Advent Calendar 2021とは全く関係ない記事です。
Denoの標準ライブラリを使って、Server-Sent Eventsを送信するサーバーを書いていきたいと思います。
Server-Sent Eventsとは?
Server-Sent Events(SSE)とは、サーバーからクライアントへイベントを受け渡しできる接続方法のことです。WebSocketと似ていますが、WeSocketが双方向通信なのに対し、SSEはデータがサーバーからクライアントへの1方向しか送信できないという違いがあります。
Deno標準ライブラリのサーバーでServer-Sent Eventsを送信する
SSEを送信するにはReadableStream
を使用します。
import { serve } from "https://deno.land/std@0.117.0/http/mod.ts";
const encoder = new TextEncoder();
console.log("http://localhost:8000");
serve(() => { // デフォルトではポート8000番を使用
let timeoutId: number | undefined;
const stream = new ReadableStream({
start(controller) {
timeoutId = setInterval(() => {
// utf8でエンコード
const data = encoder.encode(`data: ${new Date()}\n\n`);
// 送信
controller.enqueue(data);
}, 1000); // 1秒おきに送信
},
cancel() {
// 接続切断時のクリーンアップ
clearInterval(timeoutId);
},
});
return new Response(stream, {
headers: { "Content-Type": "text/event-stream; charset=utf-8" },
});
});
上記のコードを実行して、ブラウザからアクセスすると、1秒おきにデータが送信されているのが確認できると思います。
SSEで送信するメッセージは一定のプロトコルに従う必要があります。
- メッセージ「フィールド名、コロン、テキストデータ」の形式で送らなければならない
- フィールド名は
event
、data
、id
、retry
のいずれか - メッセージ同士は2つの改行文字で区切られる
このプロトコルに従っていないと、ブラウザでデータを受け取ることができません。
詳しくはMDNや仕様書を参照してください。
https://developer.mozilla.org/ja/docs/Web/API/Server-sent_events/Using_server-sent_events#examples
DenoでServer-Sent Eventsを受信する
SSEの受信には、EventSource APIを使います。しかし、このAPIはまだDenoには実装されていません。
※#12350によると、現在実装中のようです。
そのため、SSEの受信には、fetch APIを使用してReadableStreamを取得する必要があります。
そのため、SSEの受信にはpolyfillをimportする必要があります。
import { EventSource } from "https://deno.land/x/eventsource@v0.0.2/mod.ts"
const source = new EventSource("http://localhost:8000/");
source.addEventListener("message", event=>console.log(event.data));
fetch APIを使用してReadableStreamを取得する方法もあります。
const decoder = new TextDecoder();
const response = await fetch("http://localhost:8000/");
if (!response.body) {
throw new Error("response.body is null.");
}
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
console.log(decoder.decode(value));
}
(おまけ)ブラウザでServer-Sent Eventsを受信する
const source = new EventSource("http://localhost:8000/");
source.addEventListener("message", event=>console.log(event.data));
まとめ
Server-Sent Eventsの送信にはReadableStreamを使います。
Server-Sent Eventsの受信にはEventSourceを使います。