Skip to content

Instantly share code, notes, and snippets.

@lightsofapollo
Created April 25, 2012 16:57
Show Gist options
  • Save lightsofapollo/2491286 to your computer and use it in GitHub Desktop.
Save lightsofapollo/2491286 to your computer and use it in GitHub Desktop.
httpd
const CC = Components.Constructor;
const LocalFile = CC('@mozilla.org/file/local;1',
'nsILocalFile',
'initWithPath');
const ScriptableInputStream = CC(
"@mozilla.org/scriptableinputstream;1",
"nsIScriptableInputStream");
Components.utils.import('resource:///modules/devtools/dbg-client.jsm');
function startupHttpd(baseDir, port) {
const httpdURL = 'chrome://httpd.js/content/httpd.js';
let httpd = {};
Services.scriptloader.loadSubScript(httpdURL, httpd);
let server = new httpd.nsHttpServer();
server.registerDirectory('/', new LocalFile(baseDir));
server.registerContentType('appcache', 'text/cache-manifest');
server.start(port);
// Add {appname}.domain.tld:port to the list of domains handle by httpd
let identity = server.identity;
let scheme = 'http';
let host = 'gaiamobile.org';
identity.add(scheme, host, port);
let directories = getDirectories(baseDir);
directories.forEach(function appendDir(name) {
identity.add(scheme, name + '.' + host, port);
});
try {
Components.utils.import('resource:///modules/devtools/dbg-server.jsm');
// DebuggerServer.addBrowserActors();
DebuggerServer.addActors('chrome://httpd.js/content/marionette-actors.js');
DebuggerServer.init();
DebuggerServer.openListener(2828, true);
} catch (e) {
// already open if running in b2g desktop
}
server.registerPathHandler('/marionette', handleMarionette);
}
function getDirectories(dir) {
let appsDir = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
appsDir.initWithPath(dir);
appsDir.append('apps');
let dirs = [];
let files = appsDir.directoryEntries;
while (files.hasMoreElements()) {
let file = files.getNext().QueryInterface(Ci.nsILocalFile);
if (file.isDirectory()) {
dirs.push(file.leafName);
}
}
return dirs;
}
function LongPoll(connid, transport) {
this.transport = transport;
this.connid = connid;
this.buffer = [];
this.outstanding_push = null;
}
LongPoll.prototype = {
cast: function cast(msg) {
this.buffer.push(msg);
dump("cast "+JSON.stringify(msg)+ " " + this.outstanding_push + "\n");
if (this.outstanding_push !== null) {
this.drain_queue(this.outstanding_push);
this.outstanding_push.finish();
this.outstanding_push = null;
}
},
drain_queue: function drain_queue(res) {
let queue = this.buffer,
response = { messages: [] };
this.buffer = [];
queue.forEach(function(chunk) {
response.messages.push({
id: this.connid,
response: chunk
})
}.bind(this));
},
send: function send(obj) {
this.transport.send(obj);
}
}
let connections = {},
connection_num = 0,
scriptable = new ScriptableInputStream();
function handleMarionette(req, res) {
try {
if (req.method === "GET") {
if (req.queryString.length === 0) {
res.setStatusLine("1.1", 404, "Not Found");
res.write(JSON.stringify({
error: 'connection not found'
}));
} else {
// render event stream
let connid = req.queryString.split('=')[0],
me = connections[connid];
res.setStatusLine("1.1", 200, "OK");
res.setHeader("Content-Type", "application/javascript");
if (me.buffer.length === 0) {
dump("waiting /marionette?" + req.queryString + "\n");
res.processAsync();
me.outstanding_push = res;
} else {
dump("messages /marionette?" + req.queryString + "\n");
me.drain_queue(res);
}
}
} else if (req.method === "POST") {
// parse login screen
// render connection stream
let connid = connection_num++;
res.setStatusLine("1.1", 200, "OK");
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify({ id: connid}));
req.bodyInputStream.asyncWait(function (stream) {
scriptable.init(stream);
let bytes = scriptable.read(scriptable.available()),
parsed = JSON.parse(bytes);
dump("BYTES "+bytes+"\n");
let transport = debuggerSocketConnect(parsed.server, parseInt(parsed.port)),
me = new LongPoll(connid, transport);
connections[connid] = me;
transport.hooks = {
onPacket: function onPacket(pack) {
me.cast(pack);
dump("ONPACKET"+JSON.stringify(pack)+"\n");
},
onClosed: function onClosed(status) {
dump("CLOSED"+JSON.stringify(status)+"\n");
}
}
transport.ready();
dump("transport\n");
}, 0, 0, Services.tm.currentThread);
} else if (req.method === "PUT") {
let connid = req.queryString.split('=')[0],
me = connections[connid];
dump("put\n");
res.setStatusLine("1.1", 201, "Accepted");
res.setHeader("Content-Type", "text/plain", false);
res.write('');
req.bodyInputStream.asyncWait(function (stream) {
scriptable.init(stream);
let bytes = scriptable.read(scriptable.available()),
parsed = null;
try {
parsed = JSON.parse(bytes);
dump("PARSED!\n");
} catch (e) {
}
dump("stream " + bytes+"\n");
me.send(parsed);
}, 0, 0, Services.tm.currentThread);
} else {
res.setStatusLine("1.1", 405, "Method Not Allowed");
res.write('');
}
} catch (e) {
res.setStatusLine("1.1", 500, "Internal Server Error");
if(e.stack === undefined) {
res.write(e.toString());
} else {
res.write(e.toString() + " " + e.stack);
}
}
}
startupHttpd('/Users/jlal/workspace/marionette_js_client', 8080);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment