[javascript HTML5] Q. Server-Sent Eventsとは?

A. Server-Sent Eventsは、サーバーにコネクションを張り続け
サーバーからの応答を待つ一連の動作を簡単に行えるAPIです。
var source = new EventSource(endpoint);
でサーバにアクセスを行い、イベントリスナーで応答を待ちます。
endpointは、オリジン(scheme, domain, portが同じ)でなければいけません。

実際にEventSourceをつかったサンプルをみてみます。

HTMLファイル index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ServerSentEvent</title>
<script type="text/javascript">

var source = new EventSource('/events');

// EventListener Style
source.addEventListener('message', function(e) {
  console.log("MESSAGE");
  console.log(e);
  document.body.innerHTML += e.data + '<br>';
}, false);

// On Style
/*
source.onmessage = function (e) {
  console.log(e.data);
  document.body.innerHTML += e.data + '<br>';
};
*/

source.addEventListener('open', function(e) {
  console.log("OPEN");
  console.log(e);
}, false);

source.addEventListener('error', function(e) {
  // CONNECTING = 0;
  // OPEN = 1;
  // CLOSED = 2;
  if (e.eventPhase == EventSource.CONNECTING) {
    console.log("CONNECTING");
    console.log(e);
  }
  if (e.eventPhase == EventSource.CLOSED) {
    console.log("ERROR");
    console.log(e);
  }
}, false);

</script>
</head>
<body>
</body>
</html>
イベントはaddEventListenerかonスタイル両方で記述する事が可能です。

サーバファイル app.js
var http = require('http');
var fs = require('fs');

http.createServer(function(req, res) {
    console.log(req);
    if (req.headers.accept && req.headers.accept == 'text/event-stream') {
        if (req.url == '/events') {
            res.writeHead(200, {
                'Content-Type': 'text/event-stream',
                'Cache-Control': 'no-cache'
            });
            res.write('data: Server time is: ' + (new Date()) + '\n\n');
            res.end();
        }
    } else {
        // 同じオリジンでないと通信できないので                                           
        // index.htmlもnode.js経由で返す                                            
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(fs.readFileSync('index.html'));
        // 次にコネクションを張る時間
        // res.write("retry: 5000");
        res.end();
    }
} ).listen(10001);
サーバー側はnode.jsで記述してみます。
ここではポート番号10001でサーバーを起動するので
オリジンの同一の為にindex.htmlのリソースもnode.js経由で
応答する事にします。

node.jsでapp.jsを起動する
$ node app.js
node.jsを起動し、ブラウザからindex.htmlにアクセスすると
EventSource('/events')で設定した /event に以下のような要求が来ます

ヘッダー情報(cookieも送信される)
   { host: 'dev.yoshimax.net:10001'
   , 'user-agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4'
   , accept: 'text/event-stream'
   , referer: 'http://dev.yoshimax.net:10001/'
   , 'cache-control': 'max-age=0'
   , 'accept-language': 'ja-jp'
   , 'accept-encoding': 'gzip, deflate'
   , cookie: '_redmine_session=BAh7BjoPc2VzcXXXXXXXXX'
   , connection: 'keep-alive'
   }

実行結果は以下のようになります。
js_html5_seversentevent_3.png

ここで応答までの時間や再接続の時間を変更して試してみます。

サーバファイル リトライ5秒 app.js
  res.writeHead(200, {
                    'Content-Type': 'text/event-stream',
                    'Cache-Control': 'no-cache'
  });
  res.write('data: Server time is: ' + (new Date()) + '\n\n');
  // 次にコネクションを張りに来るまでの時間
  res.write("retry: 5000");
  res.end();
}
js_html5_seversentevent_2.png


サーバファイルレスポンス3秒、リトライ1秒 app.js
if (req.url == '/events') {
  // レスポンスまでの時間
  setTimeout(function () {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache'
    });
    res.write('data: Server time is: ' + (new Date()) + '\n\n');
    // 次にコネクションを張りに来るまでの時間
    res.write("retry: 1000");
    res.end();
  }, 3000);
}
js_html5_seversentevent_1.png

参考サイト:
Stream Updates with Server-Sent Events
W3C Server-Sent Events