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, { extended: true });
153 const parserMiddleware = {
154 jsonParser: (0, body_parser_1.json)(bodyParserJsonOptions),
155 urlencodedParser: (0, body_parser_1.urlencoded)(bodyParserUrlencodedOptions),
156 };
157 Object.keys(parserMiddleware)
158 .filter(parser => !this.isMiddlewareApplied(parser))
159 .forEach(parserKey => this.use(parserMiddleware[parserKey]));
160 }
161 useBodyParser(type, rawBody, options) {
162 const parserOptions = (0, get_body_parser_options_util_1.getBodyParserOptions)(rawBody, options || {});
163 const parser = bodyparser[type](parserOptions);
164 this.use(parser);
165 return this;
166 }
167 setLocal(key, value) {
168 this.instance.locals[key] = value;
169 return this;
170 }
171 getType() {
172 return 'express';
173 }
174 applyVersionFilter(handler, version, versioningOptions) {
175 const callNextHandler = (req, res, next) => {
176 if (!next) {
177 throw new common_1.InternalServerErrorException('HTTP adapter does not support filtering on version');
178 }
179 return next();
180 };
181 if (version === common_1.VERSION_NEUTRAL ||
182 // URL Versioning is done via the path, so the filter continues forward
183 versioningOptions.type === common_1.VersioningType.URI) {
184 const handlerForNoVersioning = (req, res, next) => handler(req, res, next);
185 return handlerForNoVersioning;
186 }
187 // Custom Extractor Versioning Handler
188 if (versioningOptions.type === common_1.VersioningType.CUSTOM) {
189 const handlerForCustomVersioning = (req, res, next) => {
190 const extractedVersion = versioningOptions.extractor(req);
191 if (Array.isArray(version)) {
192 if (Array.isArray(extractedVersion) &&
193 version.filter(v => extractedVersion.includes(v)).length) {
194 return handler(req, res, next);
195 }
196 if ((0, shared_utils_1.isString)(extractedVersion) &&
197 version.includes(extractedVersion)) {
198 return handler(req, res, next);
199 }
200 }
201 else if ((0, shared_utils_1.isString)(version)) {
202 // Known bug here - if there are multiple versions supported across separate
203 // handlers/controllers, we can't select the highest matching handler.
204 // Since this code is evaluated per-handler, then we can't see if the highest
205 // specified version exists in a different handler.
206 if (Array.isArray(extractedVersion) &&
207 extractedVersion.includes(version)) {
208 return handler(req, res, next);
209 }
210 if ((0, shared_utils_1.isString)(extractedVersion) && version === extractedVersion) {
211 return handler(req, res, next);
212 }
213 }
214 return callNextHandler(req, res, next);
215 };
216 return handlerForCustomVersioning;
217 }
218 // Media Type (Accept Header) Versioning Handler
219 if (versioningOptions.type === common_1.VersioningType.MEDIA_TYPE) {
220 const handlerForMediaTypeVersioning = (req, res, next) => {
221 var _a, _b;
222 const MEDIA_TYPE_HEADER = 'Accept';
223 const acceptHeaderValue = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a[MEDIA_TYPE_HEADER]) ||
224 ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[MEDIA_TYPE_HEADER.toLowerCase()]);
225 const acceptHeaderVersionParameter = acceptHeaderValue
226 ? acceptHeaderValue.split(';')[1]
227 : undefined;
228 // No version was supplied
229 if ((0, shared_utils_1.isUndefined)(acceptHeaderVersionParameter)) {
230 if (Array.isArray(version)) {
231 if (version.includes(common_1.VERSION_NEUTRAL)) {
232 return handler(req, res, next);
233 }
234 }
235 }
236 else {
237 const headerVersion = acceptHeaderVersionParameter.split(versioningOptions.key)[1];
238 if (Array.isArray(version)) {
239 if (version.includes(headerVersion)) {
240 return handler(req, res, next);
241 }
242 }
243 else if ((0, shared_utils_1.isString)(version)) {
244 if (version === headerVersion) {
245 return handler(req, res, next);
246 }
247 }
248 }
249 return callNextHandler(req, res, next);
250 };
251 return handlerForMediaTypeVersioning;
252 }
253 // Header Versioning Handler
254 if (versioningOptions.type === common_1.VersioningType.HEADER) {
255 const handlerForHeaderVersioning = (req, res, next) => {
256 var _a, _b;
257 const customHeaderVersionParameter = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a[versioningOptions.header]) ||
258 ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[versioningOptions.header.toLowerCase()]);
259 // No version was supplied
260 if ((0, shared_utils_1.isUndefined)(customHeaderVersionParameter)) {
261 if (Array.isArray(version)) {
262 if (version.includes(common_1.VERSION_NEUTRAL)) {
263 return handler(req, res, next);
264 }
265 }
266 }
267 else {
268 if (Array.isArray(version)) {
269 if (version.includes(customHeaderVersionParameter)) {
270 return handler(req, res, next);
271 }
272 }
273 else if ((0, shared_utils_1.isString)(version)) {
274 if (version === customHeaderVersionParameter) {
275 return handler(req, res, next);
276 }
277 }
278 }
279 return callNextHandler(req, res, next);
280 };
281 return handlerForHeaderVersioning;
282 }
283 }
284 trackOpenConnections() {
285 this.httpServer.on('connection', (socket) => {
286 this.openConnections.add(socket);
287 socket.on('close', () => this.openConnections.delete(socket));
288 });
289 }
290 closeOpenConnections() {
291 for (const socket of this.openConnections) {
292 socket.destroy();
293 this.openConnections.delete(socket);
294 }
295 }
296 isMiddlewareApplied(name) {
297 const app = this.getInstance();
298 return (!!app._router &&
299 !!app._router.stack &&
300 (0, shared_utils_1.isFunction)(app._router.stack.filter) &&
301 app._router.stack.some((layer) => layer && layer.handle && layer.handle.name === name));
302 }
303}
304exports.ExpressAdapter = ExpressAdapter;