Skip to content

Commit 9a388d0

Browse files
Romain Prietorprieto
authored andcommitted
Pre-process origin matchers for better performance (regexes created once only)
1 parent 97a0158 commit 9a388d0

File tree

5 files changed

+48
-29
lines changed

5 files changed

+48
-29
lines changed

src/actual.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
var origin = require('./origin.js')
1+
var originMatcher = require('./origin-matcher.js')
22
var constants = require('./constants.js')
33

44
exports.handler = function (options) {
5+
var matcher = originMatcher.create(options.origins)
56
return function (req, res, next) {
67
var originHeader = req.headers['origin']
78

89
// If either no origin was set, or the origin isn't supported, continue
910
// without setting any headers
10-
if (!originHeader || !origin.allowed(options.origins || [], originHeader)) {
11+
if (!originHeader || !matcher(originHeader)) {
1112
return next()
1213
}
1314

src/origin-matcher.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
exports.create = function (allowedOrigins) {
3+
// pre-compile list of matchers, so regexes are only built once
4+
var matchers = allowedOrigins.map(createMatcher)
5+
// does a given request Origin match the list?
6+
return function (requestOrigin) {
7+
if (requestOrigin) {
8+
return matchers.some(matcher => matcher(requestOrigin))
9+
} else {
10+
return false
11+
}
12+
}
13+
}
14+
15+
function createMatcher (allowedOrigin) {
16+
if (allowedOrigin.indexOf('*') === -1) {
17+
// simple string comparison
18+
return requestOrigin => requestOrigin === allowedOrigin
19+
} else {
20+
// need to build a regex
21+
var regex = '^' + allowedOrigin.replace('.', '\\.').replace('*', '.*') + '$'
22+
return requestOrigin => requestOrigin.match(regex)
23+
}
24+
}

src/origin.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/preflight.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
var origin = require('./origin')
1+
var originMatcher = require('./origin-matcher')
22
var constants = require('./constants.js')
33

44
exports.handler = function (options) {
5+
var matcher = originMatcher.create(options.origins)
56
return function (req, res, next) {
67
if (req.method !== 'OPTIONS') return next()
78

89
// 6.2.1 and 6.2.2
910
var originHeader = req.headers['origin']
10-
if (origin.allowed(options.origins, originHeader) === false) return next()
11+
if (!matcher(originHeader)) return next()
1112

1213
// 6.2.3
1314
var requestedMethod = req.headers[constants['AC_REQ_METHOD']]

test/origin.spec.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,55 @@
11
/* eslint-env mocha */
22
require('should')
3-
var origin = require('../src/origin')
3+
var originMatcher = require('../src/origin-matcher')
44

55
describe('Origin list', function () {
66
it('returns false if the request has no origin', function () {
77
var list = ['http://api.myapp.com', 'http://www.myapp.com']
8-
origin.allowed(list, null).should.eql(false)
8+
var matcher = originMatcher.create(list)
9+
matcher(null).should.eql(false)
10+
matcher('').should.eql(false)
911
})
1012

1113
it('returns false if the origin is not in the list', function () {
1214
var list = ['http://api.myapp.com', 'http://www.myapp.com']
13-
origin.allowed(list, 'http://random-website.com').should.eql(false)
15+
var matcher = originMatcher.create(list)
16+
matcher('http://random-website.com').should.eql(false)
1417
})
1518

1619
it('returns true if the origin matched', function () {
1720
var list = ['http://api.myapp.com', 'http://www.myapp.com']
18-
origin.allowed(list, 'http://api.myapp.com').should.eql(true)
21+
var matcher = originMatcher.create(list)
22+
matcher('http://api.myapp.com').should.eql(true)
1923
})
2024

2125
it('does not do partial matches by default', function () {
2226
var list = ['http://api.myapp.com', 'http://www.myapp.com']
23-
origin.allowed(list, 'api.myapp.com').should.eql(false)
27+
var matcher = originMatcher.create(list)
28+
matcher('api.myapp.com').should.eql(false)
2429
})
2530

2631
it('always matches if the list contains *', function () {
2732
var list = ['*']
28-
origin.allowed(list, 'http://random-website.com').should.eql(true)
33+
var matcher = originMatcher.create(list)
34+
matcher('http://random-website.com').should.eql(true)
2935
})
3036

3137
it('supports * for partial matches', function () {
3238
var list = ['http://*.myapp.com', 'http://other-website.com']
33-
origin.allowed(list, 'http://api.myapp.com').should.eql(true)
39+
var matcher = originMatcher.create(list)
40+
matcher('http://api.myapp.com').should.eql(true)
3441
})
3542

3643
it('escapes the partial regex properly', function () {
3744
// the "." should be a real dot, not mean "[any character]myapp"
3845
var list = ['http://*.myapp.com', 'http://other-website.com']
39-
origin.allowed(list, 'http://xmyapp.com').should.eql(false)
46+
var matcher = originMatcher.create(list)
47+
matcher('http://xmyapp.com').should.eql(false)
4048
})
4149

4250
it('returns false if there was no partial match', function () {
4351
var list = ['http://*.myapp.com']
44-
origin.allowed(list, 'http://random-website.com').should.eql(false)
52+
var matcher = originMatcher.create(list)
53+
matcher('http://random-website.com').should.eql(false)
4554
})
4655
})

0 commit comments

Comments
 (0)