const res = ctx.res let body = ctx.body const code = ctx.status // statuses 是一个状态的枚举包 https://www.npmjs.com/package/statuses // ignore body if (statuses.empty[code]) { // strip headers ctx.body = null return res.end() }
// HEAD 请求 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/HEAD if (ctx.method === 'HEAD') { if (!res.headersSent && !ctx.response.has('Content-Length')) { const { length } = ctx.response if (Number.isInteger(length)) ctx.length = length } return res.end() }
// status body if (body == null) { if (ctx.response._explicitNullBody) { ctx.response.remove('Content-Type') ctx.response.remove('Transfer-Encoding') ctx.length = 0 return res.end() } if (ctx.req.httpVersionMajor >= 2) { body = String(code) } else { body = ctx.message || String(code) } if (!res.headersSent) { ctx.type = 'text' ctx.length = Buffer.byteLength(body) } return res.end(body) }
// responses if (Buffer.isBuffer(body)) return res.end(body) if (typeof body === 'string') return res.end(body) if (body instanceofStream) return body.pipe(res)
// body: json body = JSON.stringify(body) if (!res.headersSent) { ctx.length = Buffer.byteLength(body) } res.end(body) }
use (fn) { if (typeof fn !== 'function') thrownewTypeError('middleware must be a function!') debug('use %s', fn._name || fn.name || '-') this.middleware.push(fn) returnthis }
functioncompose (middleware) { // 判断是不是数组 if (!Array.isArray(middleware)) thrownewTypeError('Middleware stack must be an array!') // 使用for of 可以遍历可迭代对象 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols // 判断其中的每一项是否都为函数, 如果不是那么抛出异常 for (const fn of middleware) { if (typeof fn !== 'function') thrownewTypeError('Middleware must be composed of functions!') }
returnfunction (context, next) { // last called middleware # let index = -1 returndispatch(0) functiondispatch (i) { if (i <= index) returnPromise.reject(newError('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) returnPromise.resolve() try { returnPromise.resolve(fn(context, dispatch.bind(null, i + 1))) } catch (err) { returnPromise.reject(err) } } } }
returnasyncfunctioncors(ctx, next) { // If the Origin header is not present terminate this set of steps. // The request is outside the scope of this specification. const requestOrigin = ctx.get('Origin');
// Always set Vary header // https://github.com/rs/cors/issues/10 ctx.vary('Origin');
if (!requestOrigin) returnawaitnext();
let origin; if (typeof options.origin === 'function') { origin = options.origin(ctx); if (origin instanceofPromise) origin = await origin; if (!origin) returnawaitnext(); } else { origin = options.origin || requestOrigin; }
let credentials; if (typeof options.credentials === 'function') { credentials = options.credentials(ctx); if (credentials instanceofPromise) credentials = await credentials; } else { credentials = !!options.credentials; }
// If there is no Access-Control-Request-Method header or if parsing failed, // do not set any additional headers and terminate this set of steps. // The request is outside the scope of this specification. if (!ctx.get('Access-Control-Request-Method')) { // this not preflight request, ignore it returnawaitnext(); }
ctx.set('Access-Control-Allow-Origin', origin);
if (credentials === true) { ctx.set('Access-Control-Allow-Credentials', 'true'); }
if (options.maxAge) { ctx.set('Access-Control-Max-Age', options.maxAge); }
if (options.privateNetworkAccess && ctx.get('Access-Control-Request-Private-Network')) { ctx.set('Access-Control-Allow-Private-Network', 'true'); }
if (options.allowMethods) { ctx.set('Access-Control-Allow-Methods', options.allowMethods); }
if (options.secureContext) { set('Cross-Origin-Opener-Policy', 'same-origin'); set('Cross-Origin-Embedder-Policy', 'require-corp'); }
let allowHeaders = options.allowHeaders; if (!allowHeaders) { allowHeaders = ctx.get('Access-Control-Request-Headers'); } if (allowHeaders) { ctx.set('Access-Control-Allow-Headers', allowHeaders); }