@goa/goa
Version:
The Goa Source Code For Compilation Into @Goa/Koa That Includes Modules, Tests, Types And Dependencies.
362 lines (331 loc) • 8.62 kB
JavaScript
import { format } from 'util'
import Cookies from '@goa/cookies'
import Accepts from '@goa/accepts' // eslint-disable-line
import createError from '@goa/http-errors'
import Delegate from '../modules/delegates'
import httpAssert from '../modules/http-assert'
import statuses from '@goa/statuses'
import { inspect } from 'util'
import Request from './request' // eslint-disable-line
import Response from './response' // eslint-disable-line
const COOKIES = Symbol('context#cookies')
/**
* Context prototype.
* @implements {_goa.Context}
*/
export default class Context {
constructor() {
// suppress types here so that can access
// the properties without casting later
/**
* @suppress {checkTypes}
* @type {?_goa.Application}
*/
this.app = null
/**
* @suppress {checkTypes}
* @type {Request}
*/
this.request = null
/**
* @suppress {checkTypes}
* @type {Response}
*/
this.response = null
/**
* @suppress {checkTypes}
* @type {?http.IncomingMessage}
*/
this.req = null
/**
* @suppress {checkTypes}
* @type {?http.ServerResponse}
*/
this.res = null
/**
* @suppress {checkTypes}
* @type {?string}
*/
this.originalUrl = null
/** @type {Object} */ this.state = null
/** @type {_goa.Cookies} */ this[COOKIES] = null
/**
* Set to false to bypass Koa's response.
* @type {boolean}
*/
this.respond = true
// REQUEST delegates
// method
/** @type {?} **/
this.acceptsLanguages = undefined
/** @type {?} **/
this.acceptsEncodings = undefined
/** @type {?} **/
this.acceptsCharsets = undefined
/** @type {?} **/
this.accepts = undefined
/** @type {?} **/
this.get = undefined
/** @type {?} **/
this.is = undefined
// access
/** @type {?} **/
this.querystring = undefined
/** @type {?} **/
this.idempotent = undefined
/** @type {?} **/
this.socket = undefined
/** @type {?} **/
this.search = undefined
/** @type {?} **/
this.method = undefined
/** @type {?} **/
this.query = undefined
/** @type {?} **/
this.path = undefined
/** @type {?} **/
this.url = undefined
/** @type {_goa.Accepts} **/
this.accept = null
// getters
/** @type {?} **/
this.origin = undefined
/** @type {?} **/
this.href = undefined
/** @type {?} **/
this.subdomains = undefined
/** @type {?} **/
this.protocol = undefined
/** @type {?} **/
this.host = undefined
/** @type {?} **/
this.hostname = undefined
/** @type {?} **/
this.URL = undefined
/** @type {?} **/
this.header = undefined
/** @type {?} **/
this.headers = undefined
/** @type {?} **/
this.secure = undefined
/** @type {?} **/
this.stale = undefined
/** @type {?} **/
this.fresh = undefined
/** @type {?} **/
this.ips = undefined
/** @type {?} **/
this.ip = undefined
// RESPONSE delegates
// method
/** @type {?} **/ this.attachment = undefined
/** @type {?} **/ this.redirect = undefined
/** @type {?} **/ this.remove = undefined
/** @type {?} **/ this.vary = undefined
/** @type {?} **/ this.set = undefined
/** @type {?} **/ this.append = undefined
/** @type {?} **/ this.flushHeaders = undefined
// access
/** @type {?} **/ this.status = undefined
/** @type {?} **/ this.message = undefined
/** @type {?} **/ this.body = undefined
/** @type {?} **/ this.length = undefined
/** @type {?} **/ this.type = undefined
/** @type {?} **/ this.lastModified = undefined
/** @type {?} **/ this.etag = undefined
// getter
/** @type {boolean} **/ this.headerSent = false
/** @type {boolean} **/ this.writable = false
}
/**
* util.inspect() implementation, which
* just returns the JSON output.
*/
inspect() {
return this.toJSON()
}
/**
* Return JSON representation.
*
* Here we explicitly invoke .toJSON() on each
* object, as iteration will otherwise fail due
* to the getters and cause utilities such as
* clone() to fail.
* @return {!Object}
*/
toJSON() {
return {
'request': this.request.toJSON(),
'response': this.response.toJSON(),
'app': this.app.toJSON(),
'originalUrl': this.originalUrl,
'req': '<original node req>',
'res': '<original node res>',
'socket': '<original node socket>',
}
}
/**
* Similar to .throw(), adds assertion.
*
* this.assert(this.user, 401, 'Please login!');
*
* See: https://github.com/jshttp/http-assert
*/
get assert() {
return httpAssert
}
/**
* Throw an error with `status` (default 500) and
* `msg`. Note that these are user-level
* errors, and the message may be exposed to the client.
*
* See: https://github.com/idiocc/http-errors
*
* Note: `status` should only be passed as the first parameter.
*
* @param {...(string|number|!Error)} [args]
*/
throw(...args) {
throw createError(...args)
}
/**
* Default error handling.
*
* @param {Error} err
* @api private
*/
onerror(err) {
// don't do anything if there is no error.
// this allows you to pass `this.onerror`
// to node-style callbacks.
if (null == err) return
if (!(err instanceof Error)) err = new Error(format('non-error thrown: %j', err))
let headerSent = false
if (this.headerSent || !this.writable) {
headerSent = err['headerSent'] = true
}
// delegate
this.app.emit('error', err, this)
// nothing we can do here other
// than delegate to the app-level
// handler and log.
if (headerSent) {
return
}
const { res } = this
// first unset all headers
res.getHeaderNames().forEach(name => res.removeHeader(name))
// then set those specified
this.set(err.headers)
// force text/plain
this.type = 'text'
// ENOENT support
if ('ENOENT' == err.code) err.status = 404
// default to 500
if ('number' != typeof err.status || !statuses[err.status]) err.status = 500
// respond
const code = statuses[err.status]
const msg = err.expose ? err.message : code
this.status = err.status
this.length = Buffer.byteLength(msg)
res.end(msg)
}
get cookies() {
if (!this[COOKIES]) {
this[COOKIES] = new Cookies(
/** @type {!http.IncomingMessage} */ (this.req),
/** @type {!http.ServerResponse} */ (this.res), {
keys: this.app.keys, // change @goaCookies
secure: this.request.secure,
})
}
return this[COOKIES]
}
set cookies(_cookies) {
this[COOKIES] = _cookies
}
/**
* @suppress {checkTypes}
*/
[inspect.custom]() {
return this.inspect()
}
}
/**
* Custom inspection implementation for newer Node.js versions.
*
* @return {Object}
*/
// /* istanbul ignore else */
// if (util.inspect.custom) {
// module.exports[util.inspect.custom] = module.exports.inspect
// }
/**
* Response delegation.
*/
new Delegate(Context.prototype, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable')
/**
* Request delegation.
*/
new Delegate(Context.prototype, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.method('acceptsCharsets')
.method('accepts')
.method('get')
.method('is')
.access('querystring')
.access('idempotent')
.access('socket')
.access('search')
.access('method')
.access('query')
.access('path')
.access('url')
.access('accept')
.getter('origin')
.getter('href')
.getter('subdomains')
.getter('protocol')
.getter('host')
.getter('hostname')
.getter('URL')
.getter('header')
.getter('headers')
.getter('secure')
.getter('stale')
.getter('fresh')
.getter('ips')
.getter('ip')
/**
* @suppress {nonStandardJsDocs}
* @typedef {import('../types').Application} _goa.Application
*/
/**
* @suppress {nonStandardJsDocs}
* @typedef {import('http').IncomingMessage} http.IncomingMessage
*/
/**
* @suppress {nonStandardJsDocs}
* @typedef {import('http').ServerResponse} http.ServerResponse
*/
/**
* @suppress {nonStandardJsDocs}
* @typedef {import('@goa/accepts/types').Accepts} _goa.Accepts
*/