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
50 changes: 38 additions & 12 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const assert = require('assert');
const Stream = require('stream');
const http = require('http');
const only = require('only');
const convert = require('koa-convert');
const deprecate = require('depd')('koa');

/**
* Expose `Application` class.
Expand All @@ -42,6 +40,7 @@ module.exports = class Application extends Emitter {

this.proxy = false;
this.middleware = [];
this.wrappers = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
Expand Down Expand Up @@ -95,26 +94,48 @@ module.exports = class Application extends Emitter {
/**
* Use the given middleware `fn`.
*
* Old-style middleware will be converted.
*
* @param {Function} fn
* @return {Application} self
* @api public
*/

use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will been removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/tree/v2.x#old-signature-middleware-v1x---deprecated');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}

/**
* Wrap a middleware with all wrappers
* Also calls the .onUsed hook with useChain
*
* @param {Function} mw the middleware
* @param {Object} useContext the useContext passed to you by onUsed
* @return {Function} the wrapped middleware
* @api public
*/

prepareMiddleware(mw, useContext) {
let oldMiddleware = useContext.currMiddleware;
useContext = {
app: useContext.app,
useChain: Array.from(useContext.useChain),
currMiddleware: mw
};
if (oldMiddleware) {
useContext.useChain.push(oldMiddleware);
}
useContext.prepareMiddleware = mw => useContext.app.prepareMiddleware(mw, useContext);
if (mw.onUsed) {
mw.onUsed(useContext);
}
this.wrappers.forEach(wrapper => mw = wrapper(mw, useContext));
if (typeof mw !== 'function' || isGeneratorFunction(mw)) {
throw new TypeError('middleware must be a non-generator function!');
}
return mw;
}

/**
* Return a request handler callback
* for node's native http server.
Expand All @@ -124,7 +145,12 @@ module.exports = class Application extends Emitter {
*/

callback() {
const fn = compose(this.middleware);
let fn = compose(this.middleware);
fn._name = 'koa';
fn = this.prepareMiddleware(fn, {
app: this,
useChain: []
});

if (!this.listeners('error').length) this.on('error', this.onerror);

Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"http-errors": "^1.2.8",
"is-generator-function": "^1.0.3",
"koa-compose": "^3.0.0",
"koa-convert": "^1.2.0",
"koa-is-json": "^1.0.0",
"mime-types": "^2.0.7",
"on-finished": "^2.1.0",
Expand Down
90 changes: 90 additions & 0 deletions test/application/prepareMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

'use strict';

const request = require('supertest');
const assert = require('assert');
const compose = require('koa-compose');
const Koa = require('../..');

describe('app.prepareMiddleware(mw, useContext, userName)', () => {
it('should prepare and wrap the middleware', () => {
const app = new Koa();
let newUseContext;
let before = false;
let after = false;
app.wrappers.push((fn, shouldBeUseContext) => {
assert.equal(shouldBeUseContext.app, app);
return (ctx, next) => {
before = true;
return Promise.resolve().then(() => fn(ctx, next)).then(() => after = true);
};
});
let useContext = {
app: app,
useChain: [],
currMiddleware: 'oldMiddleware'
};
let mw = ctx => ctx.body = 'body';
mw.onUsed = ctx => newUseContext = ctx;
let newMw = app.prepareMiddleware(mw, useContext);
assert.equal(newUseContext.app, app);
assert.equal(newUseContext.useChain[0], 'oldMiddleware');
assert.equal(newUseContext.currMiddleware, mw);
assert(newUseContext.prepareMiddleware);
let ctx = {};
let ret = newMw(ctx, () => {}).then(() => {
assert.equal(ctx.body, 'body');
assert(after);
});
assert(before);
return ret;
});

it('should work in an actual server', done => {
const app = new Koa();
let useContext;

let mw = ctx => ctx.body = 'body';
mw.onUsed = context => useContext = context;

app.use(mw);

const server = app.listen();

request(server)
.get('/')
.expect('body')
.expect(200)
.end(err => {
if (err) return done(err);
assert(useContext);
assert.equal(useContext.app, app);
assert.equal(useContext.useChain[0]._name, 'koa');
done();
});
});

it('should work support nested middleware', done => {
const app = new Koa();
let useContext;

let mw = ctx => ctx.body = 'body';
mw.onUsed = context => useContext = context;

app.use(compose([mw]));

const server = app.listen();

request(server)
.get('/')
.expect('body')
.expect(200)
.end(err => {
if (err) return done(err);
assert(useContext);
assert.equal(useContext.app, app);
assert.equal(useContext.useChain[0]._name, 'koa');
done();
});
});
});
73 changes: 0 additions & 73 deletions test/application/use.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
'use strict';

const request = require('supertest');
const assert = require('assert');
const Koa = require('../..');

describe('app.use(fn)', () => {
Expand Down Expand Up @@ -43,43 +42,6 @@ describe('app.use(fn)', () => {
});
});

it('should compose mixed middleware', done => {
process.once('deprecation', () => {}); // silence deprecation message
const app = new Koa();
const calls = [];

app.use((ctx, next) => {
calls.push(1);
return next().then(() => {
calls.push(6);
});
});

app.use(function * (next){
calls.push(2);
yield next;
calls.push(5);
});

app.use((ctx, next) => {
calls.push(3);
return next().then(() => {
calls.push(4);
});
});

const server = app.listen();

request(server)
.get('/')
.expect(404)
.end(err => {
if (err) return done(err);
calls.should.eql([1, 2, 3, 4, 5, 6]);
done();
});
});

// https://github.com/koajs/koa/pull/530#issuecomment-148138051
it('should catch thrown errors in non-async functions', done => {
const app = new Koa();
Expand All @@ -91,39 +53,4 @@ describe('app.use(fn)', () => {
.expect(404)
.end(done);
});

it('should accept both generator and function middleware', done => {
process.once('deprecation', () => {}); // silence deprecation message
const app = new Koa();

app.use((ctx, next) => { return next(); });
app.use(function * (next){ this.body = 'generator'; });

request(app.listen())
.get('/')
.expect(200)
.expect('generator', done);
});

it('should throw error for non function', () => {
const app = new Koa();

[null, undefined, 0, false, 'not a function'].forEach(v => (() => app.use(v)).should.throw('middleware must be a function!'));
});

it('should output deprecation message for generator functions', done => {
process.once('deprecation', message => {
assert(/Support for generators will been removed/.test(message));
done();
});

const app = new Koa();
app.use(function * (){});
});

it('should throw error for non function', () => {
const app = new Koa();

(() => app.use('not a function')).should.throw('middleware must be a function!');
});
});