Skip to content

Instantly share code, notes, and snippets.

@kyo-ago
Created August 3, 2012 01:46
Show Gist options
  • Save kyo-ago/3243320 to your computer and use it in GitHub Desktop.
Save kyo-ago/3243320 to your computer and use it in GitHub Desktop.
Android2系でXHRのLong Pollingができない問題の検証コード(ざっくり内容)
<html>
<head>
<title></title>
<script type="text/javascript">
/*
// mxhr.js
// BSD license
var mxhr = new MXHR;
mxhr.listen(mime, function(body){ process(body) });
mxhr.listen('complete', function(status_code){ ... }); // 2xx response
mxhr.listen('error', function(status_code){ ... }); // other case
mxhr.open("GET", url, true); // or mxhr.open("POST", url, true);
mxhr.send("");
*/
function MXHR() {
this.listeners = {};
this.watcher_interval = 15;
this.parsed = 0;
this.aborted = 0;
this.boundary = '';
this._watcher_id = null;
}
(function(p){
function open(method, path, async){
var self = this;
this.aborted = 0;
var req = new XMLHttpRequest();
['progresss', 'loadstart', 'abort', 'error', 'load', 'timeout', 'loadend', 'readystatechange'].forEach(function (name) {
req.addEventListener(name, (function (name) {
return function () {
log.innerHTML += req.readyState + name + '\n';
}
})(name), false);
});
req.onloadstart = function () {
setTimeout(function () {
self.abort();
log.innerHTML += req.responseText.length + 'length\n';
}, 2000);
};
var res = req.open(method, path, async);
this.req = req;
return res;
}
function send(query){
var req = this.req;
return req.send(query);
}
function abort(){
this.aborted = 1;
this.finish_stream("ABORTED");
var req = this.req;
return req.abort();
}
function init_stream(){
var self = this;
var contentTypeHeader = this.req.getResponseHeader("Content-Type");
if (contentTypeHeader.indexOf("multipart/mixed") === -1) {
this.req.onreadystatechange = function() {
self.req.onreadystatechange = function() {};
self.invoke_callback('error', self.req.status);
};
} else {
this.boundary = '--' + contentTypeHeader.split('"')[1];
this.start_watcher();
}
}
function finish_stream(status){
this.stop_watcher();
this.process_chunk();
if (status >= 200 && status < 300) {
this.invoke_callback('complete', status);
} else {
this.invoke_callback('error', status);
}
}
function start_watcher() {
var self = this;
this._watcher_id = window.setInterval(function(){
self.process_chunk();
}, this.watcher_interval);
}
function stop_watcher() {
window.clearInterval(this._watcher_id);
this._watcher_id = null;
}
function listen(mime, callback){
if(typeof this.listeners[mime] === 'undefined') {
this.listeners[mime] = [];
}
if(typeof callback !== 'undefined' && callback.constructor === Function) {
this.listeners[mime].push(callback);
}
}
function process_chunk(){
var length = this.req.responseText.length;
var rbuf = this.req.responseText.substring(this.parsed, length);
// [parsed_length, header_and_body]
var res = this.incr_parse(rbuf);
if (res[0] > 0) {
this.process_part(res[1]);
this.parsed += res[0];
if (length > this.parsed) { this.process_chunk(); }
}
}
function process_part(part) {
var self = this;
part = part.replace(this.boundary + "\n", '');
var lines = part.split("\n");
var mime = lines.shift().split('Content-Type:', 2)[1].split(";", 1)[0].replace(' ', '');
mime = mime ? mime : null;
var body = lines.join("\n");
this.invoke_callback(mime, body);
}
function invoke_callback(mime, body) {
var i;
var self = this;
if(typeof this.listeners[mime] !== 'undefined') {
for (i = this.listeners[mime].length; i--;) {
this.listeners[mime][i].call(self, body);
}
}
}
function incr_parse(buf) {
if (buf.length < 1) { return [-1]; }
var start = buf.indexOf(this.boundary);
if (start === -1) { return [-1]; }
var end = buf.indexOf(this.boundary, start + this.boundary.length);
// SUCCESS
if (start > -1 && end > -1) {
var part = buf.substring(start, end);
// end != part.length in wrong response, ignore it
return [end, part];
}
// INCOMPLETE
return [-1];
}
var i;
var methods = ("open,send,abort,init_stream,finish_stream,start_watcher,stop_watcher,listen," +
"process_chunk,process_part,invoke_callback,incr_parse").split(",");
var code = "";
for (i = 0; i < methods.length; i++) {
code += ('p.' + methods[i] + '=' + methods[i] + ';');
}
eval(code);
}(MXHR.prototype));
</script>
<script type="text/javascript">
var qs = function (obj) {
var k, q = [];
for (k in obj) {
if (obj.hasOwnProperty(k)) {
q.push(k + "=" + obj[k]);
}
}
return (q.length > 0) ? q.join("&") : null;
};
var handler = {
PING: function (data, source, origin) {
var message = JSON.stringify({
id: data.id,
rid: data.rid,
body: "PONG"
});
source.postMessage(message, origin);
},
STREAM: function (data, source, origin) {
var api = data.path;
data.params.prelude = 1;
var query = qs(data.params);
if (query) {
api += "?" + query;
}
var mxhr;
var connect = function () {
if (mxhr) {
mxhr.abort();
}
mxhr = new MXHR();
mxhr.listen("application/json", function (body) {
var message = JSON.stringify({
id: data.id,
rid: data.rid,
body: body
});
source.postMessage(message, origin);
});
mxhr.listen("complete", connect);
mxhr.listen("error", function (status) {
setTimeout(connect, 5000);
});
mxhr.open("GET", api, true);
mxhr.send();
};
connect();
},
REST: function (data, source, origin) {
var xhr = new XMLHttpRequest();
var path = data.path;
var query = qs(data.params);
if (data.method === "GET" && query) {
path += "?" + query;
}
xhr.open(data.method, path);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var message = JSON.stringify({
id: data.id,
rid: data.rid,
body: xhr.responseText
});
source.postMessage(message, origin);
}
};
if (data.method === "POST" && query) {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(query);
} else {
xhr.send(null);
}
}
};
var listener = function (e) {
var source = e.source;
var origin = e.origin;
var data = JSON.parse(e.data);
handler[data.type](data, source, origin);
};
if (window.attachEvent) {
window.attachEvent("onmessage", listener);
} else {
window.addEventListener("message", listener, false);
}
</script>
</head>
<body>
<div id="log"></div>
</body>
</html>
var http = require('http');
var fs = require('fs');
var url = require('url');
http.createServer(function (request, response) {
var req = url.parse(request.url, true);
var path = '../' + req.pathname.replace(/^\//g, '');
console.log(path);
if (fs.existsSync(path) && fs.statSync(path).isFile()) {
var ext = (path.match(/\.(\w+)$/) || [''])[1];
var mime = ({
'html' : 'text/html',
'css' : 'text/css',
'xml' : 'text/xml',
'js' : 'application/x-javascript'
})[ext] || 'text/plain';
console.log(ext, mime);
response.writeHead(200, {'Content-Type': mime});
response.end(fs.readFileSync(path));
return;
};
response.writeHead(200, {
'Content-Type': 'multipart/mixed; boundary="boundary_str"'
});
var count = 5;
var interval = setInterval(function () {
response.write('--boundary_str\nContent-Type: application/json\n\n');
response.write('res' + count);
console.log(count);
if (!count--) {
response.end('');
clearInterval(interval);
console.log('end');
}
}, 2000);
}).listen(8125);
console.log('Server running at http://127.0.0.1:8125/');
<html><head></head><body>
<iframe src="hoge.html"></iframe>
<div id="log"></div>
<script type="text/javascript">
window.onload = function () {
var iframe = document.getElementsByTagName('iframe')[0];
iframe.contentWindow.postMessage(JSON.stringify({
'type' : 'STREAM',
'method' : 'GET',
'path' : '/hoge' + Date.now(),
'params' : {}
}), '*');
window.addEventListener('message', function () {
log.innerHTML = arguments[0].data;
}, false);
}
</script>
</body></html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment