@e22m4u/js-http-static-router 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +2 -1
- package/README.md +265 -17
- package/build-cjs.js +1 -1
- package/dist/cjs/index.cjs +111 -73
- package/example/server.js +23 -13
- package/mocha.setup.js +4 -0
- package/package.json +4 -2
- package/src/http-static-router.d.ts +7 -2
- package/src/http-static-router.js +70 -58
- package/src/http-static-router.spec.js +899 -0
- package/src/static-route.js +18 -8
- package/src/types.d.ts +7 -0
- package/src/utils/create-cookie-string.d.ts +6 -0
- package/src/utils/create-cookie-string.js +28 -0
- package/src/utils/create-cookie-string.spec.js +32 -0
- package/src/utils/create-error.d.ts +14 -0
- package/src/utils/create-error.js +29 -0
- package/src/utils/create-error.spec.js +42 -0
- package/src/utils/create-request-mock.d.ts +35 -0
- package/src/utils/create-request-mock.js +483 -0
- package/src/utils/create-request-mock.spec.js +646 -0
- package/src/utils/create-response-mock.d.ts +18 -0
- package/src/utils/create-response-mock.js +131 -0
- package/src/utils/create-response-mock.spec.js +150 -0
- package/src/utils/fetch-request-body.d.ts +26 -0
- package/src/utils/fetch-request-body.js +148 -0
- package/src/utils/fetch-request-body.spec.js +209 -0
- package/src/utils/get-pathname-from-url.d.ts +1 -1
- package/src/utils/{get-request-pathname.spec.js → get-pathname-from-url.spec.js} +1 -1
- package/src/utils/index.d.ts +8 -0
- package/src/utils/index.js +8 -0
- package/src/utils/is-readable-stream.d.ts +9 -0
- package/src/utils/is-readable-stream.js +12 -0
- package/src/utils/is-readable-stream.spec.js +23 -0
- package/src/utils/parse-content-type.d.ts +15 -0
- package/src/utils/parse-content-type.js +34 -0
- package/src/utils/parse-content-type.spec.js +79 -0
- package/src/utils/parse-cookie-string.d.ts +19 -0
- package/src/utils/parse-cookie-string.js +36 -0
- package/src/utils/parse-cookie-string.spec.js +45 -0
- package/{example/static → static}/index.html +2 -2
- /package/{example/static/nested/file.txt → static/assets/nested/heart.txt} +0 -0
- /package/{example/static/file.txt → static/assets/rabbit.txt} +0 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import {Socket} from 'net';
|
|
2
|
+
import {TLSSocket} from 'tls';
|
|
3
|
+
import queryString from 'querystring';
|
|
4
|
+
import {IncomingMessage} from 'http';
|
|
5
|
+
import {InvalidArgumentError} from '@e22m4u/js-format';
|
|
6
|
+
import {isReadableStream} from './is-readable-stream.js';
|
|
7
|
+
import {parseCookieString} from './parse-cookie-string.js';
|
|
8
|
+
import {createCookieString} from './create-cookie-string.js';
|
|
9
|
+
import {CHARACTER_ENCODING_LIST} from './fetch-request-body.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Supported options.
|
|
13
|
+
*/
|
|
14
|
+
const SUPPORTED_OPTIONS = [
|
|
15
|
+
'host',
|
|
16
|
+
'method',
|
|
17
|
+
'secure',
|
|
18
|
+
'url',
|
|
19
|
+
'path',
|
|
20
|
+
'query',
|
|
21
|
+
'cookies',
|
|
22
|
+
'headers',
|
|
23
|
+
'body',
|
|
24
|
+
'stream',
|
|
25
|
+
'encoding',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create request mock.
|
|
30
|
+
*
|
|
31
|
+
* @param {import('./create-request-mock.js').RequestOptions} [options]
|
|
32
|
+
* @returns {import('http').IncomingMessage}
|
|
33
|
+
*/
|
|
34
|
+
export function createRequestMock(options) {
|
|
35
|
+
if (options !== undefined) {
|
|
36
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
37
|
+
throw new InvalidArgumentError(
|
|
38
|
+
'Parameter "options" must be an Object, but %v was given.',
|
|
39
|
+
options,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
Object.keys(options).forEach(optionName => {
|
|
43
|
+
if (!SUPPORTED_OPTIONS.includes(optionName)) {
|
|
44
|
+
throw new InvalidArgumentError(
|
|
45
|
+
'Option %v is not supported.',
|
|
46
|
+
optionName,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// options.host
|
|
51
|
+
if (options.host !== undefined && typeof options.host !== 'string') {
|
|
52
|
+
throw new InvalidArgumentError(
|
|
53
|
+
'Option "host" must be a String, but %v was given.',
|
|
54
|
+
options.host,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
// options.method
|
|
58
|
+
if (options.method !== undefined && typeof options.method !== 'string') {
|
|
59
|
+
throw new InvalidArgumentError(
|
|
60
|
+
'Option "method" must be a String, but %v was given.',
|
|
61
|
+
options.method,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
// options.secure
|
|
65
|
+
if (options.secure !== undefined && typeof options.secure !== 'boolean') {
|
|
66
|
+
throw new InvalidArgumentError(
|
|
67
|
+
'Option "secure" must be a Boolean, but %v was given.',
|
|
68
|
+
options.secure,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
// option.url
|
|
72
|
+
if (options.url !== undefined) {
|
|
73
|
+
if (typeof options.url !== 'string') {
|
|
74
|
+
throw new InvalidArgumentError(
|
|
75
|
+
'Option "url" must be a String, but %v was given.',
|
|
76
|
+
options.url,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (options.url.indexOf('#') !== -1) {
|
|
80
|
+
throw new InvalidArgumentError(
|
|
81
|
+
'Option "url" must not contain "#", but %v was given.',
|
|
82
|
+
options.url,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// options.path
|
|
87
|
+
if (options.path !== undefined) {
|
|
88
|
+
if (typeof options.path !== 'string') {
|
|
89
|
+
throw new InvalidArgumentError(
|
|
90
|
+
'Option "path" must be a String, but %v was given.',
|
|
91
|
+
options.path,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
// contain #
|
|
95
|
+
if (options.path.indexOf('#') !== -1) {
|
|
96
|
+
throw new InvalidArgumentError(
|
|
97
|
+
'Option "path" must not contain "#", but %v was given.',
|
|
98
|
+
options.path,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
// contain ?
|
|
102
|
+
if (options.path.indexOf('?') !== -1) {
|
|
103
|
+
throw new InvalidArgumentError(
|
|
104
|
+
'Option "path" must not contain "?", but %v was given.',
|
|
105
|
+
options.path,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
// not starting with /
|
|
109
|
+
if (!options.path.startsWith('/')) {
|
|
110
|
+
throw new InvalidArgumentError(
|
|
111
|
+
'Option "path" must start with "/", but %v was given.',
|
|
112
|
+
options.path,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// options.query
|
|
117
|
+
if (options.query !== undefined) {
|
|
118
|
+
if (
|
|
119
|
+
options.query === null ||
|
|
120
|
+
(typeof options.query !== 'string' &&
|
|
121
|
+
typeof options.query !== 'object') ||
|
|
122
|
+
Array.isArray(options.query)
|
|
123
|
+
) {
|
|
124
|
+
throw new InvalidArgumentError(
|
|
125
|
+
'Option "query" must be a String or an Object, but %v was given.',
|
|
126
|
+
options.query,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// options.cookies
|
|
131
|
+
if (options.cookies !== undefined) {
|
|
132
|
+
if (
|
|
133
|
+
!options.cookies ||
|
|
134
|
+
typeof options.cookies !== 'object' ||
|
|
135
|
+
Array.isArray(options.cookies)
|
|
136
|
+
) {
|
|
137
|
+
throw new InvalidArgumentError(
|
|
138
|
+
'Option "cookies" must be an Object, but %v was given.',
|
|
139
|
+
options.cookies,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
// options.cookies[k]
|
|
143
|
+
Object.keys(options.cookies).forEach(cookieName => {
|
|
144
|
+
const cookieValue = options.cookies[cookieName];
|
|
145
|
+
if (cookieValue !== undefined && typeof cookieValue !== 'string') {
|
|
146
|
+
throw new InvalidArgumentError(
|
|
147
|
+
'Cookie %v must be a String, but %v was given.',
|
|
148
|
+
cookieName,
|
|
149
|
+
cookieValue,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// options.headers
|
|
155
|
+
if (options.headers !== undefined) {
|
|
156
|
+
if (
|
|
157
|
+
!options.headers ||
|
|
158
|
+
typeof options.headers !== 'object' ||
|
|
159
|
+
Array.isArray(options.headers)
|
|
160
|
+
) {
|
|
161
|
+
throw new InvalidArgumentError(
|
|
162
|
+
'Option "headers" must be an Object, but %v was given.',
|
|
163
|
+
options.headers,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
// options.headers[k]
|
|
167
|
+
Object.keys(options.headers).forEach(headerName => {
|
|
168
|
+
const headerValue = options.headers[headerName];
|
|
169
|
+
if (headerValue !== undefined) {
|
|
170
|
+
if (typeof headerValue !== 'string' && !Array.isArray(headerValue)) {
|
|
171
|
+
throw new InvalidArgumentError(
|
|
172
|
+
'Header %v must be a String or an Array, but %v was given.',
|
|
173
|
+
headerName,
|
|
174
|
+
headerValue,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
// options.headers[k][n]
|
|
178
|
+
if (Array.isArray(headerValue)) {
|
|
179
|
+
headerValue.forEach((headerEl, index) => {
|
|
180
|
+
if (typeof headerEl !== 'string') {
|
|
181
|
+
throw new InvalidArgumentError(
|
|
182
|
+
'Element %d of the header %v must be a String, ' +
|
|
183
|
+
'but %v was given.',
|
|
184
|
+
index,
|
|
185
|
+
headerName,
|
|
186
|
+
headerEl,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// options.stream
|
|
195
|
+
if (options.stream !== undefined && !isReadableStream(options.stream)) {
|
|
196
|
+
throw new InvalidArgumentError(
|
|
197
|
+
'Option "stream" must be a Stream, but %v was given.',
|
|
198
|
+
options.stream,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
// options.encoding
|
|
202
|
+
if (options.encoding !== undefined) {
|
|
203
|
+
if (typeof options.encoding !== 'string') {
|
|
204
|
+
throw new InvalidArgumentError(
|
|
205
|
+
'Option "encoding" must be a String, but %v was given.',
|
|
206
|
+
options.encoding,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (!CHARACTER_ENCODING_LIST.includes(options.encoding)) {
|
|
210
|
+
throw new InvalidArgumentError(
|
|
211
|
+
'Character encoding %v is not supported.',
|
|
212
|
+
options.encoding,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// если определен url, выполняется
|
|
217
|
+
// проверка на несовместимые опции
|
|
218
|
+
if (options.url !== undefined) {
|
|
219
|
+
if (options.path !== undefined) {
|
|
220
|
+
throw new InvalidArgumentError(
|
|
221
|
+
'The "url" and "path" options cannot be used together.',
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (options.query !== undefined) {
|
|
225
|
+
throw new InvalidArgumentError(
|
|
226
|
+
'The "url" and "query" options cannot be used together.',
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// если передан поток, выполняется
|
|
231
|
+
// проверка на несовместимые опции
|
|
232
|
+
if (options.stream !== undefined) {
|
|
233
|
+
if (options.secure !== undefined) {
|
|
234
|
+
throw new InvalidArgumentError(
|
|
235
|
+
'The "stream" and "secure" options cannot be used together.',
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (options.body !== undefined) {
|
|
239
|
+
throw new InvalidArgumentError(
|
|
240
|
+
'The "stream" and "body" options cannot be used together.',
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (options.encoding !== undefined) {
|
|
244
|
+
throw new InvalidArgumentError(
|
|
245
|
+
'The "stream" and "encoding" options cannot be used together.',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
options = options || {};
|
|
251
|
+
let request;
|
|
252
|
+
if (options.stream) {
|
|
253
|
+
// перенаправление данных из переданного потока
|
|
254
|
+
// в новый IncomingMessage, чтобы сохранить
|
|
255
|
+
// работу проверки instanceof
|
|
256
|
+
const socket = new Socket();
|
|
257
|
+
request = new IncomingMessage(socket);
|
|
258
|
+
options.stream.on('data', chunk => request.push(chunk));
|
|
259
|
+
options.stream.on('end', () => request.push(null));
|
|
260
|
+
options.stream.on('error', err => request.emit('error', err));
|
|
261
|
+
} else {
|
|
262
|
+
request = createRequestStream(
|
|
263
|
+
options.secure,
|
|
264
|
+
options.body,
|
|
265
|
+
options.encoding,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
// добавление свойств сокета
|
|
269
|
+
// для определения IP адреса
|
|
270
|
+
Object.defineProperty(request.socket, 'remoteAddress', {value: '127.0.0.1'});
|
|
271
|
+
Object.defineProperty(request.socket, 'localAddress', {value: '127.0.0.1'});
|
|
272
|
+
// определение остальных свойств
|
|
273
|
+
// экземпляра IncomingMessage
|
|
274
|
+
request.httpVersion = '1.1';
|
|
275
|
+
request.url = '/';
|
|
276
|
+
if (options.url !== undefined) {
|
|
277
|
+
request.url = options.url;
|
|
278
|
+
} else if (options.path !== undefined || options.query !== undefined) {
|
|
279
|
+
request.url = createRequestUrl(options.path, options.query);
|
|
280
|
+
}
|
|
281
|
+
request.headers = createRequestHeaders(
|
|
282
|
+
options.host,
|
|
283
|
+
options.secure,
|
|
284
|
+
options.body,
|
|
285
|
+
options.cookies,
|
|
286
|
+
options.encoding,
|
|
287
|
+
options.headers,
|
|
288
|
+
);
|
|
289
|
+
request.method = (options.method || 'get').toUpperCase();
|
|
290
|
+
return request;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Create request stream.
|
|
295
|
+
*
|
|
296
|
+
* @param {boolean|undefined} secure
|
|
297
|
+
* @param {*} body
|
|
298
|
+
* @param {string|undefined} encoding
|
|
299
|
+
* @returns {import('http').IncomingMessage}
|
|
300
|
+
*/
|
|
301
|
+
function createRequestStream(secure, body, encoding) {
|
|
302
|
+
if (encoding !== undefined && typeof encoding !== 'string') {
|
|
303
|
+
throw new InvalidArgumentError(
|
|
304
|
+
'Parameter "encoding" must be a String, but %v was given.',
|
|
305
|
+
encoding,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
encoding = encoding || 'utf-8';
|
|
309
|
+
// для безопасного подключения
|
|
310
|
+
// использует обертка TLSSocket
|
|
311
|
+
let socket = new Socket();
|
|
312
|
+
// при использовании опции "secure"
|
|
313
|
+
// создается новый экземпляр TLSSocket
|
|
314
|
+
if (secure) {
|
|
315
|
+
socket = new TLSSocket(socket);
|
|
316
|
+
}
|
|
317
|
+
const request = new IncomingMessage(socket);
|
|
318
|
+
// если тело определено, то данные
|
|
319
|
+
// передаются в текущий запрос
|
|
320
|
+
if (body != null) {
|
|
321
|
+
if (typeof body === 'string') {
|
|
322
|
+
request.push(body, encoding);
|
|
323
|
+
} else if (Buffer.isBuffer(body)) {
|
|
324
|
+
request.push(body);
|
|
325
|
+
} else {
|
|
326
|
+
request.push(JSON.stringify(body), encoding);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// передача "null" определяет
|
|
330
|
+
// конец данных
|
|
331
|
+
request.push(null);
|
|
332
|
+
return request;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Create request url.
|
|
337
|
+
*
|
|
338
|
+
* @param {string|undefined} path
|
|
339
|
+
* @param {string|object|undefined} query
|
|
340
|
+
* @returns {string}
|
|
341
|
+
*/
|
|
342
|
+
function createRequestUrl(path, query) {
|
|
343
|
+
if (path !== undefined && typeof path !== 'string') {
|
|
344
|
+
throw new InvalidArgumentError(
|
|
345
|
+
'Parameter "path" must be a String, but %v was given.',
|
|
346
|
+
path,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (query !== undefined) {
|
|
350
|
+
if (
|
|
351
|
+
query === null ||
|
|
352
|
+
(typeof query !== 'string' && typeof query !== 'object') ||
|
|
353
|
+
Array.isArray(query)
|
|
354
|
+
) {
|
|
355
|
+
throw new InvalidArgumentError(
|
|
356
|
+
'Parameter "query" must be a String or an Object, but %v was given.',
|
|
357
|
+
query,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
let res = path !== undefined ? path : '/';
|
|
362
|
+
if (typeof query === 'object') {
|
|
363
|
+
const qs = queryString.stringify(query);
|
|
364
|
+
if (qs) {
|
|
365
|
+
res += `?${qs}`;
|
|
366
|
+
}
|
|
367
|
+
} else if (typeof query === 'string' && query !== '' && query !== '?') {
|
|
368
|
+
res += `?${query.replace(/^\?/, '')}`;
|
|
369
|
+
}
|
|
370
|
+
return res;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Create request headers.
|
|
375
|
+
*
|
|
376
|
+
* @param {string|undefined} host
|
|
377
|
+
* @param {boolean|undefined} secure
|
|
378
|
+
* @param {*} body
|
|
379
|
+
* @param {object|undefined} cookies
|
|
380
|
+
* @param {string|undefined} encoding
|
|
381
|
+
* @param {object|undefined} headers
|
|
382
|
+
* @returns {object}
|
|
383
|
+
*/
|
|
384
|
+
function createRequestHeaders(host, secure, body, cookies, encoding, headers) {
|
|
385
|
+
if (host !== undefined && typeof host !== 'string') {
|
|
386
|
+
throw new InvalidArgumentError(
|
|
387
|
+
'Parameter "host" must be a non-empty String, but %v was given.',
|
|
388
|
+
host,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
host = host || 'localhost';
|
|
392
|
+
if (secure !== undefined && typeof secure !== 'boolean') {
|
|
393
|
+
throw new InvalidArgumentError(
|
|
394
|
+
'Parameter "secure" must be a Boolean, but %v was given.',
|
|
395
|
+
secure,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
secure = Boolean(secure);
|
|
399
|
+
if (cookies !== undefined) {
|
|
400
|
+
if (!cookies || typeof cookies !== 'object' || Array.isArray(cookies)) {
|
|
401
|
+
throw new InvalidArgumentError(
|
|
402
|
+
'Parameter "cookies" must be an Object, but %v was given.',
|
|
403
|
+
cookies,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (headers !== undefined) {
|
|
408
|
+
if (!headers || typeof headers !== 'object' || Array.isArray(headers)) {
|
|
409
|
+
throw new InvalidArgumentError(
|
|
410
|
+
'Parameter "headers" must be an Object, but %v was given.',
|
|
411
|
+
headers,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
headers = headers || {};
|
|
416
|
+
if (encoding !== undefined && typeof encoding !== 'string') {
|
|
417
|
+
throw new InvalidArgumentError(
|
|
418
|
+
'Parameter "encoding" must be a String, but %v was given.',
|
|
419
|
+
encoding,
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
encoding = encoding || 'utf-8';
|
|
423
|
+
const res = {};
|
|
424
|
+
Object.keys(headers).forEach(headerName => {
|
|
425
|
+
res[headerName.toLowerCase()] = headers[headerName];
|
|
426
|
+
});
|
|
427
|
+
if (res.host === undefined) {
|
|
428
|
+
res['host'] = host;
|
|
429
|
+
}
|
|
430
|
+
if (secure) {
|
|
431
|
+
res['x-forwarded-proto'] = 'https';
|
|
432
|
+
}
|
|
433
|
+
// формирование заголовка Cookie используя
|
|
434
|
+
// существующие данные заголовка и объекта,
|
|
435
|
+
// переданного в параметр данной функции
|
|
436
|
+
if (typeof cookies === 'object' && Object.keys(cookies).length) {
|
|
437
|
+
if (res['cookie']) {
|
|
438
|
+
const existedCookies = parseCookieString(res['cookie']);
|
|
439
|
+
res['cookie'] = createCookieString({...existedCookies, ...cookies});
|
|
440
|
+
} else {
|
|
441
|
+
res['cookie'] = createCookieString(cookies);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// установка заголовка "content-type"
|
|
445
|
+
// в зависимости от тела запроса
|
|
446
|
+
if (body != null && !('content-type' in res)) {
|
|
447
|
+
if (typeof body === 'string') {
|
|
448
|
+
res['content-type'] = 'text/plain';
|
|
449
|
+
} else if (Buffer.isBuffer(body)) {
|
|
450
|
+
res['content-type'] = 'application/octet-stream';
|
|
451
|
+
} else if (
|
|
452
|
+
typeof body === 'object' ||
|
|
453
|
+
typeof body === 'boolean' ||
|
|
454
|
+
typeof body === 'number'
|
|
455
|
+
) {
|
|
456
|
+
res['content-type'] = 'application/json';
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// подсчет количества байт тела
|
|
460
|
+
// для заголовка "content-length"
|
|
461
|
+
if (
|
|
462
|
+
body != null &&
|
|
463
|
+
res['transfer-encoding'] == null &&
|
|
464
|
+
res['content-length'] == null
|
|
465
|
+
) {
|
|
466
|
+
if (typeof body === 'string') {
|
|
467
|
+
const length = Buffer.byteLength(body, encoding);
|
|
468
|
+
res['content-length'] = String(length);
|
|
469
|
+
} else if (Buffer.isBuffer(body)) {
|
|
470
|
+
const length = Buffer.byteLength(body);
|
|
471
|
+
res['content-length'] = String(length);
|
|
472
|
+
} else if (
|
|
473
|
+
typeof body === 'object' ||
|
|
474
|
+
typeof body === 'boolean' ||
|
|
475
|
+
typeof body === 'number'
|
|
476
|
+
) {
|
|
477
|
+
const json = JSON.stringify(body);
|
|
478
|
+
const length = Buffer.byteLength(json, encoding);
|
|
479
|
+
res['content-length'] = String(length);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return res;
|
|
483
|
+
}
|