UNPKG

13 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ExpressAdapter = void 0;
4const common_1 = require("@nestjs/common");
5const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
6const http_adapter_1 = require("@nestjs/core/adapters/http-adapter");
7const router_method_factory_1 = require("@nestjs/core/helpers/router-method-factory");
8const body_parser_1 = require("body-parser");
9const bodyparser = require("body-parser");
10const cors = require("cors");
11const express = require("express");
12const http = require("http");
13const https = require("https");
14const stream_1 = require("stream");
15const get_body_parser_options_util_1 = require("./utils/get-body-parser-options.util");
16/**
17 * @publicApi
18 */
19class ExpressAdapter extends http_adapter_1.AbstractHttpAdapter {
20 constructor(instance) {
21 super(instance || express());
22 this.routerMethodFactory = new router_method_factory_1.RouterMethodFactory();
23 this.logger = new common_1.Logger(ExpressAdapter.name);
24 this.openConnections = new Set();
25 }
26 reply(response, body, statusCode) {
27 if (statusCode) {
28 response.status(statusCode);
29 }
30 if ((0, shared_utils_1.isNil)(body)) {
31 return response.send();
32 }
33 if (body instanceof common_1.StreamableFile) {
34 const streamHeaders = body.getHeaders();
35 if (response.getHeader('Content-Type') === undefined &&
36 streamHeaders.type !== undefined) {
37 response.setHeader('Content-Type', streamHeaders.type);
38 }
39 if (response.getHeader('Content-Disposition') === undefined &&
40 streamHeaders.disposition !== undefined) {
41 response.setHeader('Content-Disposition', streamHeaders.disposition);
42 }
43 if (response.getHeader('Content-Length') === undefined &&
44 streamHeaders.length !== undefined) {
45 response.setHeader('Content-Length', streamHeaders.length);
46 }
47 return (0, stream_1.pipeline)(body.getStream().once('error', (err) => {
48 body.errorHandler(err, response);
49 }), response, (err) => {
50 if (err) {
51 this.logger.error(err.message, err.stack);
52 }
53 });
54 }
55 if (response.getHeader('Content-Type') !== undefined &&
56 response.getHeader('Content-Type') !== 'application/json' &&
57 (body === null || body === void 0 ? void 0 : body.statusCode) >= common_1.HttpStatus.BAD_REQUEST) {
58 this.logger.warn("Content-Type doesn't match Reply body, you might need a custom ExceptionFilter for non-JSON responses");
59 response.setHeader('Content-Type', 'application/json');
60 }
61 return (0, shared_utils_1.isObject)(body) ? response.json(body) : response.send(String(body));
62 }
63 status(response, statusCode) {
64 return response.status(statusCode);
65 }
66 end(response, message) {
67 return response.end(message);
68 }
69 render(response, view, options) {
70 return response.render(view, options);
71 }
72 redirect(response, statusCode, url) {
73 return response.redirect(statusCode, url);
74 }
75 setErrorHandler(handler, prefix) {
76 return this.use(handler);
77 }
78 setNotFoundHandler(handler, prefix) {
79 return this.use(handler);
80 }
81 isHeadersSent(response) {
82 return response.headersSent;
83 }
84 setHeader(response, name, value) {
85 return response.set(name, value);
86 }
87 listen(port, ...args) {
88 return this.httpServer.listen(port, ...args);
89 }
90 close() {
91 this.closeOpenConnections();
92 if (!this.httpServer) {
93 return undefined;
94 }
95 return new Promise(resolve => this.httpServer.close(resolve));
96 }
97 set(...args) {
98 return this.instance.set(...args);
99 }
100 enable(...args) {
101 return this.instance.enable(...args);
102 }
103 disable(...args) {
104 return this.instance.disable(...args);
105 }
106 engine(...args) {
107 return this.instance.engine(...args);
108 }
109 useStaticAssets(path, options) {
110 if (options && options.prefix) {
111 return this.use(options.prefix, express.static(path, options));
112 }
113 return this.use(express.static(path, options));
114 }
115 setBaseViewsDir(path) {
116 return this.set('views', path);
117 }
118 setViewEngine(engine) {
119 return this.set('view engine', engine);
120 }
121 getRequestHostname(request) {
122 return request.hostname;
123 }
124 getRequestMethod(request) {
125 return request.method;
126 }
127 getRequestUrl(request) {
128 return request.originalUrl;
129 }
130 enableCors(options) {
131 return this.use(cors(options));
132 }
133 createMiddlewareFactory(requestMethod) {
134 return this.routerMethodFactory
135 .get(this.instance, requestMethod)
136 .bind(this.instance);
137 }
138 initHttpServer(options) {
139 const isHttpsEnabled = options && options.httpsOptions;
140 if (isHttpsEnabled) {
141 this.httpServer = https.createServer(options.httpsOptions, this.getInstance());
142 }
143 else {
144 this.httpServer = http.createServer(this.getInstance());
145 }
146 if (options === null || options === void 0 ? void 0 : options.forceCloseConnections) {
147 this.trackOpenConnections();
148 }
149 }
150 registerParserMiddleware(prefix, rawBody) {
151 const bodyParserJsonOptions = (0, get_body_parser_options_util_1.getBodyParserOptions)(rawBody);
152 const bodyParserUrlencodedOptions = (0, get_body_parser_options_util_1.getBodyParserOptions)(rawBody, {
153 extended: true,
154 });
155 const parserMiddleware = {
156 jsonParser: (0, body_parser_1.json)(bodyParserJsonOptions),
157 urlencodedParser: (0, body_parser_1.urlencoded)(bodyParserUrlencodedOptions),
158 };
159 Object.keys(parserMiddleware)
160 .filter(parser => !this.isMiddlewareApplied(parser))
161 .forEach(parserKey => this.use(parserMiddleware[parserKey]));
162 }
163 useBodyParser(type, rawBody, options) {
164 const parserOptions = (0, get_body_parser_options_util_1.getBodyParserOptions)(rawBody, options);
165 const parser = bodyparser[type](parserOptions);
166 this.use(parser);
167 return this;
168 }
169 setLocal(key, value) {
170 this.instance.locals[key] = value;
171 return this;
172 }
173 getType() {
174 return 'express';
175 }
176 applyVersionFilter(handler, version, versioningOptions) {
177 const callNextHandler = (req, res, next) => {
178 if (!next) {
179 throw new common_1.InternalServerErrorException('HTTP adapter does not support filtering on version');
180 }
181 return next();
182 };
183 if (version === common_1.VERSION_NEUTRAL ||
184 // URL Versioning is done via the path, so the filter continues forward
185 versioningOptions.type === common_1.VersioningType.URI) {
186 const handlerForNoVersioning = (req, res, next) => handler(req, res, next);
187 return handlerForNoVersioning;
188 }
189 // Custom Extractor Versioning Handler
190 if (versioningOptions.type === common_1.VersioningType.CUSTOM) {
191 const handlerForCustomVersioning = (req, res, next) => {
192 const extractedVersion = versioningOptions.extractor(req);
193 if (Array.isArray(version)) {
194 if (Array.isArray(extractedVersion) &&
195 version.filter(v => extractedVersion.includes(v)).length) {
196 return handler(req, res, next);
197 }
198 if ((0, shared_utils_1.isString)(extractedVersion) &&
199 version.includes(extractedVersion)) {
200 return handler(req, res, next);
201 }
202 }
203 else if ((0, shared_utils_1.isString)(version)) {
204 // Known bug here - if there are multiple versions supported across separate
205 // handlers/controllers, we can't select the highest matching handler.
206 // Since this code is evaluated per-handler, then we can't see if the highest
207 // specified version exists in a different handler.
208 if (Array.isArray(extractedVersion) &&
209 extractedVersion.includes(version)) {
210 return handler(req, res, next);
211 }
212 if ((0, shared_utils_1.isString)(extractedVersion) && version === extractedVersion) {
213 return handler(req, res, next);
214 }
215 }
216 return callNextHandler(req, res, next);
217 };
218 return handlerForCustomVersioning;
219 }
220 // Media Type (Accept Header) Versioning Handler
221 if (versioningOptions.type === common_1.VersioningType.MEDIA_TYPE) {
222 const handlerForMediaTypeVersioning = (req, res, next) => {
223 var _a, _b;
224 const MEDIA_TYPE_HEADER = 'Accept';
225 const acceptHeaderValue = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a[MEDIA_TYPE_HEADER]) ||
226 ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[MEDIA_TYPE_HEADER.toLowerCase()]);
227 const acceptHeaderVersionParameter = acceptHeaderValue
228 ? acceptHeaderValue.split(';')[1]
229 : undefined;
230 // No version was supplied
231 if ((0, shared_utils_1.isUndefined)(acceptHeaderVersionParameter)) {
232 if (Array.isArray(version)) {
233 if (version.includes(common_1.VERSION_NEUTRAL)) {
234 return handler(req, res, next);
235 }
236 }
237 }
238 else {
239 const headerVersion = acceptHeaderVersionParameter.split(versioningOptions.key)[1];
240 if (Array.isArray(version)) {
241 if (version.includes(headerVersion)) {
242 return handler(req, res, next);
243 }
244 }
245 else if ((0, shared_utils_1.isString)(version)) {
246 if (version === headerVersion) {
247 return handler(req, res, next);
248 }
249 }
250 }
251 return callNextHandler(req, res, next);
252 };
253 return handlerForMediaTypeVersioning;
254 }
255 // Header Versioning Handler
256 if (versioningOptions.type === common_1.VersioningType.HEADER) {
257 const handlerForHeaderVersioning = (req, res, next) => {
258 var _a, _b;
259 const customHeaderVersionParameter = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a[versioningOptions.header]) ||
260 ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[versioningOptions.header.toLowerCase()]);
261 // No version was supplied
262 if ((0, shared_utils_1.isUndefined)(customHeaderVersionParameter)) {
263 if (Array.isArray(version)) {
264 if (version.includes(common_1.VERSION_NEUTRAL)) {
265 return handler(req, res, next);
266 }
267 }
268 }
269 else {
270 if (Array.isArray(version)) {
271 if (version.includes(customHeaderVersionParameter)) {
272 return handler(req, res, next);
273 }
274 }
275 else if ((0, shared_utils_1.isString)(version)) {
276 if (version === customHeaderVersionParameter) {
277 return handler(req, res, next);
278 }
279 }
280 }
281 return callNextHandler(req, res, next);
282 };
283 return handlerForHeaderVersioning;
284 }
285 }
286 trackOpenConnections() {
287 this.httpServer.on('connection', (socket) => {
288 this.openConnections.add(socket);
289 socket.on('close', () => this.openConnections.delete(socket));
290 });
291 }
292 closeOpenConnections() {
293 for (const socket of this.openConnections) {
294 socket.destroy();
295 this.openConnections.delete(socket);
296 }
297 }
298 isMiddlewareApplied(name) {
299 const app = this.getInstance();
300 return (!!app._router &&
301 !!app._router.stack &&
302 (0, shared_utils_1.isFunction)(app._router.stack.filter) &&
303 app._router.stack.some((layer) => layer && layer.handle && layer.handle.name === name));
304 }
305}
306exports.ExpressAdapter = ExpressAdapter;