diff --git a/API.md b/API.md
index 6f48c24..8ca55fd 100644
--- a/API.md
+++ b/API.md
@@ -7,6 +7,7 @@
* [new Router(\[opts\])](#new-routeropts)
* [router.get|put|post|patch|delete|del ⇒ Router
](#routergetputpostpatchdeletedel--coderoutercode)
* [Named routes](#named-routes)
+ * [Match host](#match-host)
* [Multiple middleware](#multiple-middleware)
* [Nested routers](#nested-routers)
* [Router prefixes](#router-prefixes)
@@ -30,11 +31,12 @@
Create a new router.
-| Param | Type | Description |
-| ---------------- | -------------------- | ------------------------------------------------------------------------ |
-| [opts] | Object
| |
-| [opts.prefix] | String
| prefix router paths |
-| [opts.exclusive] | Boolean
| only run last matched route's controller when there are multiple matches |
+| Param | Type | Description |
+| ---------------- | -------------------------- | ------------------------------------------------------------------------ |
+| [opts] | Object
| |
+| [opts.prefix] | String
| prefix router paths |
+| [opts.exclusive] | Boolean
| only run last matched route's controller when there are multiple matches |
+| [opts.host] | String/Regexp
| hostname to match for all routes |
**Example**
Basic usage:
@@ -106,6 +108,28 @@ router.url('user', 3);
// => "/users/3"
```
+### Match host
+
+Routers can match against a specific host by using the `host` property.
+
+```javascript
+const routerA = new Router({
+ host: 'hosta.com' // only match if request host exactly equal `hosta.com`
+});
+
+router.get('/', (ctx, next) => {
+ // Response for hosta.com
+});
+
+const routerB = new Router({
+ host: /^(.*\.)?hostb\.com$/ // match all subdomains of hostb.com, including hostb.com, www.hostb.com, etc.
+});
+
+router.get('/', (ctx, next) => {
+ // Response index for matched hosts
+});
+```
+
### Multiple middleware
Multiple middleware may be given:
diff --git a/README.md b/README.md
index 6656824..0d11075 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
* Express-style routing (`app.get`, `app.put`, `app.post`, etc.)
* Named URL parameters
* Named routes with URL generation
+* Match routes with specific host
* Responds to `OPTIONS` requests with allowed methods
* Support for `405 Method Not Allowed` and `501 Not Implemented`
* Multiple route middleware
diff --git a/lib/router.js b/lib/router.js
index d682c63..2c0ad31 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -48,6 +48,7 @@ module.exports = Router;
* @param {Object=} opts
* @param {Boolean=false} opts.exclusive only run last matched route's controller when there are multiple matches
* @param {String=} opts.prefix prefix router paths
+ * @param {String|RegExp=} opts.host host for router match
* @constructor
*/
@@ -68,6 +69,7 @@ function Router(opts = {}) {
this.params = {};
this.stack = [];
+ this.host = this.opts.host;
}
/**
@@ -183,6 +185,24 @@ function Router(opts = {}) {
* The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is
* used to convert paths to regular expressions.
*
+ *
+ * ### Match host for each router instance
+ *
+ * ```javascript
+ * const router = new Router({
+ * host: 'example.domain' // only match if request host exactly equal `example.domain`
+ * });
+ *
+ * ```
+ *
+ * OR host cloud be a regexp
+ *
+ * ```javascript
+ * const router = new Router({
+ * host: /.*\.?example\.domain$/ // all host end with .example.domain would be matched
+ * });
+ * ```
+ *
* @name get|put|post|patch|delete|del
* @memberof module:koa-router.prototype
* @param {String} path
@@ -358,6 +378,12 @@ Router.prototype.routes = Router.prototype.middleware = function () {
const dispatch = function dispatch(ctx, next) {
debug('%s %s', ctx.method, ctx.path);
+ const hostMatched = router.matchHost(ctx.host);
+
+ if (!hostMatched) {
+ return next();
+ }
+
const path = router.opts.routerPath || ctx.routerPath || ctx.path;
const matched = router.match(path, ctx.method);
let layerChain;
@@ -735,6 +761,32 @@ Router.prototype.match = function (path, method) {
return matched;
};
+/**
+ * Match given `input` to allowed host
+ * @param {String} input
+ * @returns {boolean}
+ */
+
+Router.prototype.matchHost = function (input) {
+ const { host } = this;
+
+ if (!host) {
+ return true;
+ }
+
+ if (!input) {
+ return false;
+ }
+
+ if (typeof host === 'string') {
+ return input === host;
+ }
+
+ if (typeof host === 'object' && host instanceof RegExp) {
+ return host.test(input);
+ }
+};
+
/**
* Run middleware for named route parameters. Useful for auto-loading or
* validation.
diff --git a/test/lib/router.js b/test/lib/router.js
index e599de2..547bfb1 100644
--- a/test/lib/router.js
+++ b/test/lib/router.js
@@ -2410,4 +2410,85 @@ describe('Router', function () {
done();
});
});
+
+ describe('Support host', function () {
+ it('should support host match', function (done) {
+ const app = new Koa();
+ const router = new Router({
+ host: 'test.domain'
+ });
+ router.get('/', (ctx) => {
+ ctx.body = {
+ url: '/'
+ };
+ });
+ app.use(router.routes());
+
+ const server = http.createServer(app.callback());
+
+ request(server)
+ .get('/')
+ .set('Host', 'test.domain')
+ .expect(200)
+ .end(function (err, res) {
+ if (err) return done(err);
+
+ request(server)
+ .get('/')
+ .set('Host', 'a.domain')
+ .expect(404)
+ .end(function (err, res) {
+ if (err) return done(err);
+ done();
+ });
+ });
+ });
+ it('should support host match regexp', function (done) {
+ const app = new Koa();
+ const router = new Router({
+ host: /^(.*\.)?test\.domain/
+ });
+ router.get('/', (ctx) => {
+ ctx.body = {
+ url: '/'
+ };
+ });
+ app.use(router.routes());
+ const server = http.createServer(app.callback());
+
+ request(server)
+ .get('/')
+ .set('Host', 'test.domain')
+ .expect(200)
+ .end(function (err, res) {
+ if (err) return done(err);
+
+ request(server)
+ .get('/')
+ .set('Host', 'www.test.domain')
+ .expect(200)
+ .end(function (err, res) {
+ if (err) return done(err);
+
+ request(server)
+ .get('/')
+ .set('Host', 'any.sub.test.domain')
+ .expect(200)
+ .end(function (err, res) {
+ if (err) return done(err);
+
+ request(server)
+ .get('/')
+ .set('Host', 'sub.anytest.domain')
+ .expect(404)
+ .end(function (err, res) {
+ if (err) return done(err);
+
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
});