Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions packages/polka/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ function mutate(str, req) {
req.path = req.path.substring(str.length) || '/';
}

// Finds the longest registered `key` (from `use(base, ...)`) that is a
// full-segment prefix of `path`, so bases with more than one "folder"
// (eg. `/foo/bar`) can be matched instead of only ever comparing the
// first segment of `path`.
function matchBase(path, keys) {
let base, blen=0, i=0, k, klen;
for (; i < keys.length; i++) {
k = keys[i]; klen = k.length;
if (klen > blen && path.length >= klen && path.substring(0, klen) === k && (path.length === klen || path.charCodeAt(klen) === 47)) {
base = k; blen = klen;
}
}
return base;
}

function onError(err, req, res, next) {
let code = (res.statusCode = err.code || err.status || 500);
if (typeof err === 'string' || Buffer.isBuffer(err)) res.end(err);
Expand Down Expand Up @@ -72,16 +87,20 @@ class Polka extends Router {
info = info || this.parse(req);
let fns=[], arr=this.wares, obj=this.find(req.method, info.pathname);
req.originalUrl = req.originalUrl || req.url;
let base = value(req.path = info.pathname);
if (this.bwares[base] !== void 0) {
arr = arr.concat(this.bwares[base]);
req.path = info.pathname;
let wbase = matchBase(req.path, Object.keys(this.bwares));
if (wbase !== void 0) {
arr = arr.concat(this.bwares[wbase]);
}
if (obj) {
fns = obj.handlers;
req.params = obj.params;
} else if (this.apps[base] !== void 0) {
mutate(base, req); info.pathname=req.path; //=> updates
fns.push(this.apps[base].handler.bind(null, req, res, info));
} else {
let abase = matchBase(req.path, Object.keys(this.apps));
if (abase !== void 0) {
mutate(abase, req); info.pathname=req.path; //=> updates
fns.push(this.apps[abase].handler.bind(null, req, res, info));
}
}
fns.push(this.onNoMatch);
// Grab addl values from `info`
Expand Down
57 changes: 57 additions & 0 deletions tests/polka.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,63 @@ test('polka::usage::middleware (basenames)', async t => {
app.server.close();
});

test('polka::usage::middleware (multi-segment basenames)', async t => {
t.plan(11);

let app = polka()
.use('/foo/bar', (req, res) => {
t.pass('runs the base middleware for: /foo/bar');
t.false(req.url.includes('/foo/bar'), '~> strips "/foo/bar" base from `req.url`');
t.false(req.path.includes('/foo/bar'), '~> strips "/foo/bar" base from `req.path`');
t.ok(req.originalUrl.includes('/foo/bar'), '~> keeps "/foo/bar" base within `req.originalUrl`');
res.end('hello from foo/bar');
})
.get('/', (req, res) => {
t.pass('runs the MAIN app handler for GET /');
res.end('hello from main');
});

let uri = listen(app);

// A single-segment base under the same first "folder" should be unaffected.
await axios.get(uri).catch(() => {});
let r1 = await axios.get(uri);
t.is(r1.status, 200, '~> received 200 status');
t.is(r1.data, 'hello from main', '~> received "hello from main" response');

// Test (GET /foo/bar/baz), a base with more than one "folder"
let r2 = await axios.get(`${uri}/foo/bar/baz`);
t.is(r2.status, 200, '~> received 200 status');
t.is(r2.data, 'hello from foo/bar', '~> received "hello from foo/bar" response');

// A path that only shares the FIRST segment ("/foo") must not match.
await axios.get(`${uri}/foo/baz`).catch(err => {
let r = err.response;
t.is(r.status, 404, '~> "/foo/baz" does not match the "/foo/bar" base');
});

app.server.close();
});

test('polka::usage::sub-application (multi-segment base)', async t => {
t.plan(4);

let sub = polka().get('/', (req, res) => {
t.pass('runs the sub-application route');
t.is(req.url, '/', '~> trims the multi-segment base from `req.url`');
res.end('hello from sub');
});

let app = polka().use('/api/v2', sub);

let uri = listen(app);
let r = await axios.get(`${uri}/api/v2`);
t.is(r.status, 200, '~> received 200 status');
t.is(r.data, 'hello from sub', '~> received "hello from sub" response');

app.server.close();
});

test('polka::usage::middleware (wildcard)', async t => {
t.plan(29);

Expand Down