Skip to content

Commit

Permalink
Initial commit: base release
Browse files Browse the repository at this point in the history
  • Loading branch information
naholyr committed Nov 19, 2012
0 parents commit 35bd9e8
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
node_modules
81 changes: 81 additions & 0 deletions README.md
@@ -0,0 +1,81 @@
virtualhost
===========

Make your HTTP server hostname-aware **very simply**.

You define the handler for each server name, and that will return the final handler to be passed to your HTTP server.

Works fine with Express.

Installation
------------

`npm install virtualhost`

Usage
-----

```javascript
var virtualhost = require('virtualhost');
var server = http.createServer(virtualhost(servers, catchAll));
```

* `servers` is a hash of server's configuration, each one having following options:
* `pattern` can be a string (hostnames will simply be compared for equality), or a regular expression (you could use `/^hello\.(fr|com)$/i` for example to use this handler for `hello.fr` and `hello.com`, or `/\.domain.tld$/` to match all subdomains of `domain.tld`). Think about anchors (`^` and `$`) when using regular expression as pattern.
* `handler` is a `function (req, res)`. Request matching pattern will simply be forwarded to this handler.
* `with_port` will include the port in the comparison. Default comparison ignores it, which means `pattern: "domain.tld" will match `domain.tld:8080` and `domain.tld:3000` the same way. If you enable this option, you **have to** include port in your pattern.
* `catchAll` is the default handler used when no server matched hostname. It's not mandatory, and defaults to a simple 404.

### Shorter usage

`servers` can also be a simple hash of the form `pattern: handler`.

For example:

```javascript
virtualhost({
"one.mydomain.tld": function (req, res) {…},
"two.mydomain.tld": function (req, res) {…}
});
```

is strictly equivalent to

```javascript
virtualhost({
"one.mydomain.tld": {
pattern: "one.mydomain.tld",
handler: function (req, res) {…}
},
"two.mydomain.tld": {
pattern: "two.mydomain.tld",
handler: function (req, res) {…}
}
});
```

Of course you can mix both syntax.

Sample usage
------------

```javascript
// Example of standard handler
// This one will simply write "handler1" at "sub.domain.tld/*"
var handler1 = function (req, res) { res.end('handler1') };

// Example of Express 3.x app
// Good guy Express now simply returns standard handler, which makes this directly usable in virtualhost :)
// This one will write "handler2 (www.)" at "www.domain.tld/" and "handler2 (undefined)" at "domain.tld/"
var handler2 = express().get('/', function (req, res) { res.end('handler2 (' + req.virtualhost.match[1] + ')' });

// Example of virtualhost configuration
var apps = {
// Shortcut hostname→handler
sub.domain.tld: handler1,
// Full config with RegExp pattern
express: { pattern: /^(www\.)?domain\.tld$/, handler: handler2 }
};

http.createServer(virtualhost(apps)).listen();
```
18 changes: 18 additions & 0 deletions package.json
@@ -0,0 +1,18 @@
{
"author": "Nicolas Chambrier <naholyr@gmail.com> (http://naholyr.fr)",
"name": "virtualhost",
"description": "Dispatch HTTP request to a handler depending on hostname",
"version": "0.0.1",
"homepage": "https://github.com/lmtm/node-virtualhost",
"repository": {
"type": "git",
"url": "git://github.com/lmtm/node-virtualhost.git"
},
"main": "virtualhost.js",
"dependencies": {},
"devDependencies": {},
"optionalDependencies": {},
"engines": {
"node": "*"
}
}
65 changes: 65 additions & 0 deletions virtualhost.js
@@ -0,0 +1,65 @@

var url = require('url');

module.exports = handler;

function handler (servers, catchAll) {
catchAll = catchAll || _catchAll;

// Check catch-all
if (typeof catchAll !== 'function') throw new Error('[virtualhost] Catch-all should be a valid callback');
// Check servers configuration
for (var server in servers) {
var conf = servers[server];
if (typeof conf === 'function') {
// Direct function assignment
servers[server] = { pattern: server, handler: conf };
} else if (typeof conf === 'object') {
// Check options
if (typeof conf.handler !== 'function') throw new Error('[virtualhost] Invalid configuration for server "' + server + '": "handler" should be a valid callback');
if (!conf.pattern || (typeof conf.pattern !== 'string' && !(conf.pattern instanceof RegExp))) throw new Error('[virtualhost] Invalid configuration for server"' + server + '": "pattern" should be a string or a RegExp');
} else {
// Invalid type
throw new Error('[virtualhost] Invalid configuration for server "' + server + '": object or function expected');
}
}

// Valid options, return meta-handler
return function (req, res) {
// Retrieve hostname
var location = url.parse('http://' + req.headers.host);
// Define "req.virtualhost" (can be used by user)
req.virtualhost = {
hostname: location.hostname || '',
port: location.port,
match: false
};
// Browser available handlers and find the first one matching hostname
for (var server in servers) {
req.virtualhost.match = matchHost(req.virtualhost, servers[server]);
if (req.virtualhost.match) {
req.virtualhost.name = server;
return servers[server].handler.call(this, req, res);
}
}
// None found, fallback to catch-all
req.virtualhost.match = null;
return catchAll.call(this, req, res);
};
}

function matchHost (hostInfo, conf) {
var pattern = conf.pattern;
var host = hostInfo.hostname + (conf.with_port ? (':' + hostInfo.port) : '');
if (conf.pattern instanceof RegExp) {
console.log(host, conf.pattern, host.match(conf.pattern));
return host.match(conf.pattern);
} else {
return host === conf.pattern;
}
}

function _catchAll (req, res) {
res.writeHead(404);
res.end('Invalid host name');
}

0 comments on commit 35bd9e8

Please sign in to comment.