long pollingって結局
- HTTPでつなぎに行って、レスポンスが帰ってきたら何かして再度つなぐ(Client Side)
- HTTPリクエストをキープしといて、イベントに応じてレスポンスを好きな時に返す(Server Side)
の2つだろう、と思って、勉強を兼ねて「とりあえず上記の条件だけは満たすっぽいもの」を書いてみた。実際はこんな風にはかかないだろうし、エラーハンドリングとかをもっとちゃんとする必要があると思われる。(特にtimeout処理とか)
Server side ( longpoll.js )
node v0.3.1-pre にて動作確認
var server = require('http').createServer(handle), util = require('util'), fs = require('fs'), port = 8000, listeners = [], pub = "/pub", sub = "/sub", script = "/script/longpollclient.js"; function grub(request, response) { request.pause(); listeners.push( { "req": request, "res": response } ); } function publish(request, response) { var listener; request.on("data", function(data) { while( listener = listeners.pop() ) { listener.req.resume(); listener.res.writeHead(200, { 'Content-Type': 'application/json' }); listener.res.end(data); } }); response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end('sent'); } function serveStatic(filename, ctype, response) { fs.readFile(filename, function(err, data) { if (err) { console.log(err); } else { response.writeHead(200, { 'Content-Type': ctype }); response.end(data); } }); } function serveClientJS(response) { serveStatic('./longpollclient.js', 'text/javascript', response); } function serveHtml(response) { serveStatic('./index.html', 'text/html', response); } function handle(request, response) { // http://hostname:port/sub if ( request.url.match(sub) ) { grub(request, response); } // http://hostname:port/pub else if ( request.url.match(pub) ) { publish(request, response); } // http://hostname:port/script/longpollclient.js else if ( request.url.match(script) ) { serveClientJS(response); } else { serveHtml(response); } } server.listen(port); console.log("server listening " + port + "...");
Client Side ( longpollclient.js, index.html )
Firefox 3.6.12 にて動作確認(他では試してないよ。xhrがクロスブラウザじゃない!とか言われても困るよ)
var longPoll; (function(XHR) { // public interfaces function LongPoll(base) { this.basepath = base; } LongPoll.prototype.subscribe = subscribe; LongPoll.prototype.publish = publish; longPoll = LongPoll; function subscribe(path, onmessage) { var url = this.basepath + path; sub(url, onmessage); } function publish(path, json) { var url = this.basepath + path; pub(url, json); } // implementations function pub(url, data) { var xhr = new XHR(); xhr.open("POST", url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); } function sub(url, onmessage) { var xhr = new XHR(); xhr.open("GET", url, true); xhr.onreadystatechange = function handleXhr() { if ( xhr.readyState === 4 && xhr.status === 200 ) { onmessage(JSON.parse(xhr.responseText)); sub(url, onmessage); } }; xhr.send(); } })(XMLHttpRequest);
<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <title>long polling test</title> </head> <body> <script type="text/javascript" src="script/longpollclient.js"></script> <script type="text/javascript"> var poll = new longPoll("http://" + location.host); poll.subscribe('/sub', function(message) { console.dir(message); }); setInterval(function() { poll.publish('/pub', {"foo": "bar" }); }, 5000); </script> </body> </html>
一応、それっぽい動きはする。