UNPKG

@loopback/rest

Version:

Expose controllers as REST endpoints and route REST API requests to controller methods

126 lines (112 loc) 3.98 kB
// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved. // Node module: @loopback/rest // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT import {Context} from '@loopback/core'; import { HandlerContext, MiddlewareContext, Request, Response, } from '@loopback/express'; import {RestBindings} from './keys'; import {RestServerResolvedConfig} from './rest.server'; /** * A per-request Context combining an IoC container with handler context * (request, response, etc.). */ export class RequestContext extends MiddlewareContext implements HandlerContext { /** * Get the protocol used by the client to make the request. * Please note this protocol may be different from what we are observing * at HTTP/TCP level, because reverse proxies like nginx or sidecars like * Envoy are switching between protocols. */ get requestedProtocol(): string { return ( ((this.request.get('x-forwarded-proto') ?? '').split(',')[0] || this.request.protocol || this.serverConfig.protocol) ?? 'http' ); } /** * Get the effective base path of the incoming request. This base path * combines `baseUrl` provided by Express when LB4 handler is mounted on * a non-root path, with the `basePath` value configured at LB4 side. */ get basePath(): string { const request = this.request; let basePath = this.serverConfig.basePath ?? ''; if (request.baseUrl && request.baseUrl !== '/') { if (!basePath || request.baseUrl.endsWith(basePath)) { // Express has already applied basePath to baseUrl basePath = request.baseUrl; } else { basePath = request.baseUrl + basePath; } } return basePath; } /** * Get the base URL used by the client to make the request. * This URL contains the protocol, hostname, port and base path. * The path of the invoked route and query string is not included. * * Please note these values may be different from what we are observing * at HTTP/TCP level, because reverse proxies like nginx are rewriting them. */ get requestedBaseUrl(): string { const request = this.request; const config = this.serverConfig; const protocol = this.requestedProtocol; // The host can be in one of the forms // [::1]:3000 // [::1] // 127.0.0.1:3000 // 127.0.0.1 let {host, port} = parseHostAndPort( request.get('x-forwarded-host') ?? request.headers.host, ); const forwardedPort = (request.get('x-forwarded-port') ?? '').split(',')[0]; port = forwardedPort || port; if (!host) { // No host detected from http headers // Use the configured values or the local network address host = config.host ?? request.socket.localAddress ?? ''; port = ((config.port || request.socket.localPort) ?? '').toString(); } // clear default ports port = protocol === 'https' && port === '443' ? '' : port; port = protocol === 'http' && port === '80' ? '' : port; // add port number of present host += port !== '' ? ':' + port : ''; return protocol + '://' + host + this.basePath; } constructor( public readonly request: Request, public readonly response: Response, parent: Context, public readonly serverConfig: RestServerResolvedConfig, name?: string, ) { super(request, response, parent, name); } protected setupBindings() { super.setupBindings(); this.bind(RestBindings.Http.REQUEST).to(this.request).lock(); this.bind(RestBindings.Http.RESPONSE).to(this.response).lock(); this.bind(RestBindings.Http.CONTEXT).to(this).lock(); } } function parseHostAndPort(host: string | undefined) { host = host ?? ''; host = host.split(',')[0]; const portPattern = /:([0-9]+)$/; const port = (host.match(portPattern) ?? [])[1] || ''; host = host.replace(portPattern, ''); return {host, port}; }