
Upgrade Your Drupal Skills
We trained 1,000+ Drupal Developers over the last decade.
See Advanced Courses NAH, I know EnoughHTTP all the things
Recently, I've heard about the browserver project, that turns your browser in a pseudo web-server by using web-sockets. While the form is surely new, I found the idea quite familiar. Indeed, there has been quite a few attempts at embedding a web server in every user's browser: for example this add-on for the (discontinued) Opera Unite promised to revolutionize the way you shared files with friends by embedding a web server in your browser.
Furthermore, my recent efforts to control Rdio's web
app from the command-line have led me to explore Firefox's add-on SDK in
order to… have an extension start its own web server. The good thing
is that this is done from the comfort of JavaScript, by abusing the
httpd module normally available to run unit tests. Sure, I'm not
really writing unit tests here, but it couldn't just sit here unused,
could it?
Abusing Mozilla's add-on SDK for fun and profit
While Firefox's documentation on how to create an add-on and get it running is
excellent (so this won't be covered here), it is quite sparse when it comes to
abusing its unit-testing modules. The httpd module is available for us add-on
writers: it “provides an HTTP server written in JavaScript for the Mozilla
platform, which can be used in unit tests.” Let's use that for more creative
things…
The first example from Mozilla, pasted here, is about serving files from a directory:
var {startServerAsync} = require("httpd");
var srv = startServerAsync(port, basePath);
require("unload").when(function cleanup() {
srv.stop(function() { // you should continue execution from this point.
})
});
OK, that was sort of instructive. But don't despair yet, you can also do your own thing:
var {nsHttpServer} = require("httpd");
var srv = new nsHttpServer();
And that's about it for the add-on's documentation.
Of course, the required module's source is available and quite
interesting if you care to have a look at it: the httpd.js file is
bundled with the add-on SDK, and also available on Github. If
you have even more time on your hands you can also read the
lower-level interface definitions. While these are a
little arid, and not entirely reproduced in JavaScript, it is a very
instructive read on which behavior you should expect from the underlying
implementation.
Getting to know your browser's httpd
By following the latest example, you should have created an instance of
nsHttpServer, which you can now start and stop with the following:
// I won't repeat these two lines anymore:
var {nsHttpServer} = require("httpd");
var srv = new nsHttpServer();
// Start listening on port 8000
srv.start(8000);
// Stop it.
srv.stop(function() { console.log("Stopped."); });
There are a handful of other (public) methods, but I'll only cover a couple here that can be used to actually serve content.
registerPathHandler
Match a path (a String) to a handler:
srv.registerPathHandler("/", function(request, response) {
response.write("Hello world!\n");
});
From there you can already query your awesome server with whatever HTTP verb, it does not care:
$ curl localhost:8000
Hello world!
$ curl -XPUT localhost:8000
Hello world!
registerErrorHandler
This method allows you to catch generic HTTP errors by matching a status-code.
For example: throwing a generic exception in a path-handler will trigger the
callback registered with the code 500.
Note that setting the appropriate HTTP headers to indicate the status in the response is up to you.
srv.registerPathHandler("/bim", function(request, response) {
throw "error";
});
srv.registerPrefixHandler(500, function(request, response) {
// Set response headers
response.setStatusLine(request.httpVersion, 500, "KO");
// Complete response body
response.write("BIM!\n");
});
Requests and responses
When registering a path — or error — handler, the callback receives two
objects, namely Request and Response, in that order.
- With no big surprises,
Requestprovides information on the incoming request:host,port,queryString,httpVersion, … Responseallows you to write a response to the world, with two main methods:writeaccepts "data", e.g.:write("whatever");setStatusLinesets the HTTP status-line header, e.g.:setStatusLine("1.1", 200, "OK")
Wrapping up
I'm sure not every Firefox user wants to have add-ons start web servers in their back, but this little hack is an example of the powerful interfaces that Mozilla provides to add-on developers, directly in JavaScript. Hopefully this gave you some ideas about what you could do within a browser when you're done making the next coolest Backbone app ever. :)
As a side-note, if you're interested in hacking around this, maybe you'd
like to have a nicer interface than registerPathHandler. I started a
very simple wrapper that provides a somewhat nicer
interface. Maybe reading about this will inspire
people with some talent for JavaScript to write a cleaner one than I did.
With enough spare time, I'm sure one could port existing Javascript web frameworks to the add-on SDK…
About Drupal Sun
Drupal Sun is an Evolving Web project. It allows you to:
- Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
- Facet based on tags, author, or feed
- Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
- View the entire article text inline, or in the context of the site where it was created
See the blog post at Evolving Web