source: frontend/node_modules/webpack-dev-middleware/dist/middleware.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.2 KB
RevLine 
[9af201e]1"use strict";
2
3const path = require("path");
4
5const mime = require("mime-types");
6
7const parseRange = require("range-parser");
8
9const getFilenameFromUrl = require("./utils/getFilenameFromUrl");
10
11const {
12 getHeaderNames,
13 getHeaderFromRequest,
14 getHeaderFromResponse,
15 setHeaderForResponse,
16 setStatusCode,
17 send,
18 sendError
19} = require("./utils/compatibleAPI");
20
21const ready = require("./utils/ready");
22/** @typedef {import("./index.js").NextFunction} NextFunction */
23
24/** @typedef {import("./index.js").IncomingMessage} IncomingMessage */
25
26/** @typedef {import("./index.js").ServerResponse} ServerResponse */
27
28/**
29 * @param {string} type
30 * @param {number} size
31 * @param {import("range-parser").Range} [range]
32 * @returns {string}
33 */
34
35
36function getValueContentRangeHeader(type, size, range) {
37 return `${type} ${range ? `${range.start}-${range.end}` : "*"}/${size}`;
38}
39/**
40 * @param {string | number} title
41 * @param {string} body
42 * @returns {string}
43 */
44
45
46function createHtmlDocument(title, body) {
47 return `${"<!DOCTYPE html>\n" + '<html lang="en">\n' + "<head>\n" + '<meta charset="utf-8">\n' + "<title>"}${title}</title>\n` + `</head>\n` + `<body>\n` + `<pre>${body}</pre>\n` + `</body>\n` + `</html>\n`;
48}
49
50const BYTES_RANGE_REGEXP = /^ *bytes/i;
51/**
52 * @template {IncomingMessage} Request
53 * @template {ServerResponse} Response
54 * @param {import("./index.js").Context<Request, Response>} context
55 * @return {import("./index.js").Middleware<Request, Response>}
56 */
57
58function wrapper(context) {
59 return async function middleware(req, res, next) {
60 const acceptedMethods = context.options.methods || ["GET", "HEAD"]; // fixes #282. credit @cexoso. in certain edge situations res.locals is undefined.
61 // eslint-disable-next-line no-param-reassign
62
63 res.locals = res.locals || {};
64
65 if (req.method && !acceptedMethods.includes(req.method)) {
66 await goNext();
67 return;
68 }
69
70 ready(context, processRequest, req);
71
72 async function goNext() {
73 if (!context.options.serverSideRender) {
74 return next();
75 }
76
77 return new Promise(resolve => {
78 ready(context, () => {
79 /** @type {any} */
80 // eslint-disable-next-line no-param-reassign
81 res.locals.webpack = {
82 devMiddleware: context
83 };
84 resolve(next());
85 }, req);
86 });
87 }
88
89 async function processRequest() {
90 /** @type {import("./utils/getFilenameFromUrl").Extra} */
91 const extra = {};
92 const filename = getFilenameFromUrl(context,
93 /** @type {string} */
94 req.url, extra);
95
96 if (!filename) {
97 await goNext();
98 return;
99 }
100
101 if (extra.errorCode) {
102 if (extra.errorCode === 403) {
103 context.logger.error(`Malicious path "${filename}".`);
104 }
105
106 sendError(req, res, extra.errorCode);
107 return;
108 }
109
110 let {
111 headers
112 } = context.options;
113
114 if (typeof headers === "function") {
115 // @ts-ignore
116 headers = headers(req, res, context);
117 }
118 /**
119 * @type {{key: string, value: string | number}[]}
120 */
121
122
123 const allHeaders = [];
124
125 if (typeof headers !== "undefined") {
126 if (!Array.isArray(headers)) {
127 // eslint-disable-next-line guard-for-in
128 for (const name in headers) {
129 // @ts-ignore
130 allHeaders.push({
131 key: name,
132 value: headers[name]
133 });
134 }
135
136 headers = allHeaders;
137 }
138
139 headers.forEach(
140 /**
141 * @param {{key: string, value: any}} header
142 */
143 header => {
144 setHeaderForResponse(res, header.key, header.value);
145 });
146 }
147
148 if (!getHeaderFromResponse(res, "Content-Type")) {
149 // content-type name(like application/javascript; charset=utf-8) or false
150 const contentType = mime.contentType(path.extname(filename)); // Only set content-type header if media type is known
151 // https://tools.ietf.org/html/rfc7231#section-3.1.1.5
152
153 if (contentType) {
154 setHeaderForResponse(res, "Content-Type", contentType);
155 }
156 }
157
158 if (!getHeaderFromResponse(res, "Accept-Ranges")) {
159 setHeaderForResponse(res, "Accept-Ranges", "bytes");
160 }
161
162 const rangeHeader = getHeaderFromRequest(req, "range");
163 let start;
164 let end;
165
166 if (rangeHeader && BYTES_RANGE_REGEXP.test(rangeHeader)) {
167 const size = await new Promise(resolve => {
168 /** @type {import("fs").lstat} */
169 context.outputFileSystem.lstat(filename, (error, stats) => {
170 if (error) {
171 context.logger.error(error);
172 return;
173 }
174
175 resolve(stats.size);
176 });
177 });
178 const parsedRanges = parseRange(size, rangeHeader, {
179 combine: true
180 });
181
182 if (parsedRanges === -1) {
183 const message = "Unsatisfiable range for 'Range' header.";
184 context.logger.error(message);
185 const existingHeaders = getHeaderNames(res);
186
187 for (let i = 0; i < existingHeaders.length; i++) {
188 res.removeHeader(existingHeaders[i]);
189 }
190
191 setStatusCode(res, 416);
192 setHeaderForResponse(res, "Content-Range", getValueContentRangeHeader("bytes", size));
193 setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");
194 const document = createHtmlDocument(416, `Error: ${message}`);
195 const byteLength = Buffer.byteLength(document);
196 setHeaderForResponse(res, "Content-Length", Buffer.byteLength(document));
197 send(req, res, document, byteLength);
198 return;
199 } else if (parsedRanges === -2) {
200 context.logger.error("A malformed 'Range' header was provided. A regular response will be sent for this request.");
201 } else if (parsedRanges.length > 1) {
202 context.logger.error("A 'Range' header with multiple ranges was provided. Multiple ranges are not supported, so a regular response will be sent for this request.");
203 }
204
205 if (parsedRanges !== -2 && parsedRanges.length === 1) {
206 // Content-Range
207 setStatusCode(res, 206);
208 setHeaderForResponse(res, "Content-Range", getValueContentRangeHeader("bytes", size,
209 /** @type {import("range-parser").Ranges} */
210 parsedRanges[0]));
211 [{
212 start,
213 end
214 }] = parsedRanges;
215 }
216 }
217
218 const isFsSupportsStream = typeof context.outputFileSystem.createReadStream === "function";
219 let bufferOtStream;
220 let byteLength;
221
222 try {
223 if (typeof start !== "undefined" && typeof end !== "undefined" && isFsSupportsStream) {
224 bufferOtStream =
225 /** @type {import("fs").createReadStream} */
226 context.outputFileSystem.createReadStream(filename, {
227 start,
228 end
229 });
230 byteLength = end - start + 1;
231 } else {
232 bufferOtStream =
233 /** @type {import("fs").readFileSync} */
234 context.outputFileSystem.readFileSync(filename);
235 ({
236 byteLength
237 } = bufferOtStream);
238 }
239 } catch (_ignoreError) {
240 await goNext();
241 return;
242 }
243
244 send(req, res, bufferOtStream, byteLength);
245 }
246 };
247}
248
249module.exports = wrapper;
Note: See TracBrowser for help on using the repository browser.