code for querying cross-domain

This commit is contained in:
ansuz 2016-08-01 15:22:04 +02:00
parent 75170353fe
commit f2c97d8913
3 changed files with 305 additions and 0 deletions

View File

@ -0,0 +1,151 @@
(function () {
var Frame = {};
var uid = function () {
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
.toString(32).replace(/\./g, '');
};
// create an invisible iframe with a given source
// append it to a parent element
// execute a callback when it has loaded
var create = Frame.create = function (parent, src, onload, timeout) {
var iframe = document.createElement('iframe');
timeout = timeout || 10000;
var to = window.setTimeout(function () {
onload('[timeoutError] could not load iframe at ' + src);
}, timeout);
iframe.onload = function (e) {
onload(void 0, iframe, e);
window.clearTimeout(to);
};
iframe.setAttribute('src', src);
iframe.style.display = 'none';
parent.appendChild(iframe);
};
/* given an iframe with an rpc script loaded, create a frame object
with an asynchronous 'send' method */
var open = Frame.open = function (e, A, timeout) {
var win = e.contentWindow;
var frame = {};
var listeners = {};
var timeouts = {};
timeout = timeout || 5000;
var accepts = frame.accepts = function (o) {
return A.some(function (e) {
switch (typeof(e)) {
case 'string': return e === o;
case 'object': return e.test(o);
}
});
};
var _listener = function (e) {
if (!frame.accepts(e.origin)) {
console.log("message from %s rejected!", e.origin);
return;
}
var message = JSON.parse(e.data);
var uid = message._uid;
var error = message.error;
var data = message.data;
if (!uid) {
console.log("No uid!");
return;
}
if (timeouts[uid]) {
window.clearTimeout(timeouts[uid]);
}
if (listeners[uid]) {
listeners[uid](error, data, e);
delete listeners[uid];
}
};
window.addEventListener('message', _listener);
var close = frame.close = function () {
window.removeEventListener('message', _listener);
};
/* method (string): (set|get|remove)
key (string)
data (string)
cb (function) */
var send = frame.send = function (method, key, data, cb) {
var req = {
method: method,
key: key,
data: data,
};
var id = req._uid = uid();
if (typeof(cb) === 'function') {
//console.log("setting callback!");
listeners[id] = cb;
//console.log("setting timeout of %sms", timeout);
timeouts[id] = window.setTimeout(function () {
// when the callback is executed it will clear this timeout
cb('[TimeoutError] request timed out after ' + timeout + 'ms');
}, timeout);
} else {
console.log(typeof(cb));
}
win.postMessage(JSON.stringify(req), '*');
};
var set = frame.set = function (key, val, cb) {
send('set', key, val, cb);
};
var batchset = frame.batchset = function (map, cb) {
send('batchset', void 0, map, cb);
};
var get = frame.get = function (key, cb) {
send('get', key, void 0, cb);
};
var batchget = frame.batchget = function (keys, cb) {
send('batchget', void 0, keys, cb);
};
var remove = frame.remove = function (key, cb) {
send('remove', key, void 0, cb);
};
var batchremove = frame.batchremove = function (keys, cb) {
send('batchremove', void 0, keys, cb);
};
var keys = frame.keys = function (cb) {
send('keys', void 0, void 0, cb);
};
return frame;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = Frame;
}
else if ((typeof(define) !== 'undefined' && define !== null) &&
(define.amd !== null)) {
define([
'/bower_components/jquery/dist/jquery.min.js',
], function () {
return Frame;
});
} else {
window.Frame = Frame;
}
}());

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<script data-main="test" src="/bower_components/requirejs/require.js"></script>
<style>
#status { border: 5px solid red; }
#status.working { border: 5px solid green; }
</style>
</head>
<body>
<p>This page opens up an iframe which connects to beta.cryptpad.fr</p>
<p>the idea is that some other subdomain of cryptpad.fr should be able to use this technique to open up a connection
to beta.cryptpad.fr and read from its local storage, so that we can make a seamless transition from beta
to www. or *.cryptpad.fr.</p>
<div id="status">
<p>If this box turns green, you've configured your scripts correctly.</p>
<p>If your scripts are not working correctly, check that:
<ol>
<li>/customize/share/respond.js is configured to respond to requests from your domain (see the validDomains variable)</li>
<li>the script attempting to connect to this endpoint is configured to listen for responses from it (in this case main.js)</li>
</ol>
</div>

View File

@ -0,0 +1,126 @@
define([
'/customize/share/frame.js',
'/bower_components/jquery/dist/jquery.min.js',
], function (Frame) {
var $ = window.jQuery;
var domain = 'https://beta.cryptpad.fr';
var path = '/customize/share/frame.html';
var acceptResponseFrom = [
/cryptpad.fr$/
];
var lock = 0;
var unlock = function (i) {
lock--;
console.log("Test #%s passed", i + 1);
if (!lock) { $('#status').addClass('working'); }
};
var runTest = function (test, i) {
lock++;
test(i);
};
var randInt = function () {
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
};
var handleErr = function (err) {
if (err) {
console.error(err);
return true;
}
};
var areNull = function (keys, data) {
return !keys.some(function (k) { return data[k] !== null; });
};
Frame.create(document.body, domain + path, function (err, iframe, loadEvent) {
if (handleErr(err)) { return; }
console.log("Created iframe");
// open a channel into the frame, accept messages from (sub)domain(s)
var frame = window.Beta = Frame.open(iframe, acceptResponseFrom);
/* Run your actual tests */
[function (i) { // test #1
var pew = randInt();
frame.set('pew', pew, function (err, data) {
if (handleErr(err)) { return; }
frame.get('pew', function (err, num) {
if (handleErr(err)) { return; }
if (pew === num) {
frame.remove('pew', function (err) {
if (handleErr(err)) { return; }
frame.get('pew', function (err, data) {
if (handleErr(err)) { return; }
if (data !== null) { return; }
unlock(i);
});
});
}
});
});
}, function (i) { // test #2
var map = {
bang: randInt(),
pow: randInt(),
lol: randInt(),
};
var keys = Object.keys(map);
frame.batchset(map, function (err, data) {
if (handleErr(err)) { return; }
frame.batchget(keys, function (err, data) {
if (handleErr(err)) { return; }
frame.batchremove(Object.keys(map), function (err) {
if (handleErr(err)) { return; }
frame.batchget(keys, function (err, data) {
if (areNull(keys, data)) { unlock(i); }
});
});
});
});
}, function (i) { // test #3
var map = {
bang2: true,
pow2: true,
lol2: true,
};
var keys = Object.keys(map);
// set some keys to arbitrary values
frame.batchset(map, function (err) {
if (handleErr(err)) { return; }
// remove those values
frame.batchremove(keys, function (err) {
if (handleErr(err)) { return; }
// check that they were actually removed
frame.batchget(keys, function (err, data) {
if (handleErr(err)) { return; }
// all keys should be null after you've removed them
if (areNull(keys, data)) {
unlock(i);
return;
}
console.log("Expected all keys to return null");
});
});
});
}].forEach(runTest);
});
});