-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
SendJsonMiddleware.js
111 lines (102 loc) · 3.48 KB
/
SendJsonMiddleware.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/** @typedef {import('../types').IMiddleware} IMiddleware */
/** @typedef {import('../types').MiddlewareFunction} MiddlewareFunction */
/** @typedef {import('../types/index.js').ResponseFinalizer} ResponseFinalizer */
/**
* @typedef {Object} SendJsonMiddlewareOptions
* @prop {string} [defaultCharset='utf-8']
* @prop {boolean} [setCharset=true]
* Automatically applies charset in `Content-Type`
* @prop {boolean} [setMediaType=true]
* Automatically applies `application/json` mediatype in `Content-Type`
*/
export default class SendJsonMiddleware {
/** @param {SendJsonMiddlewareOptions} [options] */
constructor(options = {}) {
this.options = {
defaultCharset: options.defaultCharset || 'utf-8',
setCharset: options.setCharset !== false,
setMediaType: options.setMediaType !== false,
};
// Single shared function instead of one per response
this.finalizeResponse = this.finalizeResponse.bind(this);
}
/**
* @param {string} charset
* @return {BufferEncoding}
*/
static charsetAsBufferEncoding(charset) {
switch (charset) {
case 'iso-8859-1':
case 'ascii':
case 'binary':
case 'latin1':
return 'latin1';
case 'utf-16le':
case 'ucs-2':
case 'ucs2':
case 'utf16le':
return 'utf16le';
case 'base64':
case 'hex':
return /** @type {BufferEncoding} */ (charset);
case 'utf-8':
case 'utf8':
default:
return 'utf-8';
}
}
/** @type {ResponseFinalizer} */
finalizeResponse(response) {
if (response.isStreaming
|| response.body == null
|| typeof response.body !== 'object'
|| Buffer.isBuffer(response.body)) return;
// TODO: Check response.request.headers.accepts
let charset;
const contentType = /** @type {string} */ (response.headers['content-type']);
if (contentType) {
contentType.split(';').some((directive) => {
const parameters = directive.split('=');
if (parameters[0].trim().toLowerCase() !== 'charset') {
return false;
}
charset = parameters[1]?.trim().toLowerCase();
const firstQuote = charset.indexOf('"');
const lastQuote = charset.lastIndexOf('"');
if (firstQuote !== -1 && lastQuote !== -1) {
charset = charset.slice(firstQuote + 1, lastQuote);
}
return true;
});
} else if (this.options.setMediaType) {
charset = this.options.defaultCharset;
response.headers['content-type'] = `application/json;charset=${charset}`;
}
if (!charset) {
charset = this.options.defaultCharset;
if (this.options.setCharset && !response.headersSent) {
response.headers['content-type'] = `${contentType || ''};charset=${charset}`;
}
}
const stringData = JSON.stringify(response.body);
const bufferEncoding = SendJsonMiddleware.charsetAsBufferEncoding(charset);
response.body = Buffer.from(stringData, bufferEncoding);
}
/** @type {MiddlewareFunction} */
execute({ response }) {
response.finalizers.push(this.finalizeResponse);
}
/** @type {SendJsonMiddleware} */
static #defaultInstance;
/** @type {MiddlewareFunction} */
static Execute({ response }) {
if (!SendJsonMiddleware.#defaultInstance) {
SendJsonMiddleware.#defaultInstance = new SendJsonMiddleware({
defaultCharset: 'utf-8',
setCharset: true,
setMediaType: true,
});
}
response.finalizers.push(SendJsonMiddleware.#defaultInstance.finalizeResponse);
}
}