Skip to content

Commit

Permalink
feat: allow set router host match (#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeliex authored Jul 4, 2022
1 parent 54a3198 commit d2ad849
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 5 deletions.
34 changes: 29 additions & 5 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [new Router(\[opts\])](#new-routeropts)
* [router.get|put|post|patch|delete|del ⇒ <code>Router</code>](#routergetputpostpatchdeletedel--coderoutercode)
* [Named routes](#named-routes)
* [Match host](#match-host)
* [Multiple middleware](#multiple-middleware)
* [Nested routers](#nested-routers)
* [Router prefixes](#router-prefixes)
Expand All @@ -30,11 +31,12 @@

Create a new router.

| Param | Type | Description |
| ---------------- | -------------------- | ------------------------------------------------------------------------ |
| [opts] | <code>Object</code> | |
| [opts.prefix] | <code>String</code> | prefix router paths |
| [opts.exclusive] | <code>Boolean</code> | only run last matched route's controller when there are multiple matches |
| Param | Type | Description |
| ---------------- | -------------------------- | ------------------------------------------------------------------------ |
| [opts] | <code>Object</code> | |
| [opts.prefix] | <code>String</code> | prefix router paths |
| [opts.exclusive] | <code>Boolean</code> | only run last matched route's controller when there are multiple matches |
| [opts.host] | <code>String/Regexp</code> | hostname to match for all routes |

**Example**
Basic usage:
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

Expand All @@ -68,6 +69,7 @@ function Router(opts = {}) {

this.params = {};
this.stack = [];
this.host = this.opts.host;
}

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
81 changes: 81 additions & 0 deletions test/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
});
});
});
});
});

0 comments on commit d2ad849

Please sign in to comment.