tl;dr
server.listen(, )
. E.g: server.listen(80, '::1')
http://[::1]:80/
OK, my last few posts on node.js may have seemed a little negative. While there are some things in node.js that seem a little more complicated than necessary, there are some things that are nice and simple, such as getting your server to run on both IPv4 and IPv6. This post is a little late for World IPv6 Day, but better late than never!
So this post isn’t about configuring IPv6 on your machine in
general. I’m going to assume that your local network interface has an
IPv6 address. You can probably check this with the output of
ifconfig
. On my Darwin box it looks something like:
benno@ff:~% ifconfig lo0 lo0: flags=8049mtu 16384 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 inet 127.0.0.1 netmask 0xff000000
You should see the local interface bound to the IPv6 localhost
address ::1
as well the IPv4 localhost address
127.0.0.1
. So lets get started with a simple little
IPv4 server.
var http = require('http') var server function onRequest(req, res) { console.log(req.method, req.url) res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello World\n') } function onListening() { console.log('Listening at http://' + this.address().address + ':' + this.address().port + '/') } server = http.createServer() server.on('request', onRequest) server.on('listening', onListening) server.listen(1337, '127.0.0.1')
This is a slight variation on the canonical node.js Hello World example. A few things worth noting:
on
method rather than the verbose addListener
So, apart from my stylistic quirks, the above should be fairly straight forward. The only new thing functionality wise compared to the normal node.js example is the addition of some trivial logging in the request handler.
So our quest is going to be to add support for IPv6. Before we do that though, I’m going to improve our logging a bit. Just because we are supporting IPv6, doesn’t mean we want to stop our server running on IPv4, so we are going to end up with multiple servers running at once. Once this happens, our logging might get a bit confusing. So we’re going to give our servers a name, and include that in the logging.
var http = require('http') var server function onRequest(req, res) { console.log('[' + this.name + ']', req.method, req.url) res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello World\n') } function onListening() { console.log('[' + this.name + '] Listening at http://' + this.address().address + ':' + this.address().port + '/') } server = http.createServer() server.name = 'ipv4server' server.on('request', onRequest) server.on('listening', onListening) server.listen(1337, '127.0.0.1')
Because Javascript objects are open we can trivially add a
name
field to our objects, and then use this when
logging. In general I avoid messing with objects created by other
modules, but it is the quick and easy approach in this case.
OK, so on to IPv6. As a first stab at it, we get something like this:
var http = require('http') var server function onRequest(req, res) { console.log('[' + this.name + ']', req.method, req.url) res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello World\n') } function onListening() { console.log('[' + this.name + '] Listening at http://' + this.address().address + ':' + this.address().port + '/') } ipv4server = http.createServer() ipv6server = http.createServer() ipv4server.name = 'ipv4server' ipv6server.name = 'ipv6server' ipv4server.on('request', onRequest) ipv6server.on('request', onRequest) ipv4server.on('listening', onListening) ipv6server.on('listening', onListening) ipv4server.listen(1337, '127.0.0.1') ipv6server.listen(1337, '::1')
Basically, creating an IPv6 server is exactly the same as creating
an IPv4 server, except you use an IPv6 address literal (i.e:
::1
) to specify the local address to bind to, rather than
an IPv4 address literal. You can see that there is absolutely no problem
sharing the event handlers between the two different servers. The this
variable in each event handler function refers to the server itself, so you can
handle cases that are server specific if necessary.
When you run this you should get some output like:
[ipv4server] Listening at http://127.0.0.1:1337/ [ipv6server] Listening at http://::1:1337/
Which looks pretty good. You can try going to the IPv4 server URL in your browser. If you
try the IPv6 URL, you will probably run in to some problems. This is because you need some
escaping of the IPv6 literal address in the URL, or it can’t be parsed correctly (what with there
being all those colons which are usually used for separating the port number). So the correct
URL should be: http://[::1]:1337/
. We better fix
this bug in the code:
function onListening() { var hostname = this.type === 'tcp4' ? this.address().address : '[' + this.address().address + ']' console.log('[' + this.name + '] Listening at http://' + hostname + ':' + this.address().port + '/') }
OK, that’s looking pretty good now, if you start hitting those URLs on the different address you should get some useful output such as:
[ipv4server] Listening at http://127.0.0.1:1337/ [ipv6server] Listening at http://[::1]:1337/ [ipv4server] GET / [ipv4server] GET /favicon.ico [ipv6server] GET / [ipv6server] GET /favicon.ico
Now, I mentioned earlier I don’t like duplicating data. I also don’t like duplicating code either, so let’s refactor this a little:
function startServer(name, address, port) { var server = http.createServer() server.name = name server.on('request', onRequest) server.on('listening', onListening) server.listen(port, address) return server } startServer('ipv4server', '127.0.0.1', 1337) startServer('ipv6server', '::1', 1337)
So, in conclusion it is easy-peasy to run your web application on IPv6, and even on IPv4 and IPv6 using the exact same script.