@opra/core 0.20.3 → 0.22.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/cjs/adapter/adapter.js +2 -2
- package/cjs/adapter/http/express-adapter.js +29 -7
- package/cjs/adapter/http/helpers/common.js +66 -0
- package/cjs/adapter/http/helpers/convert-to-headers.js +65 -0
- package/cjs/adapter/http/helpers/convert-to-raw-headers.js +25 -0
- package/cjs/adapter/http/helpers/match-known-fields.js +47 -0
- package/cjs/adapter/http/http-adapter.js +82 -440
- package/cjs/adapter/http/impl/http-incoming-message-host.js +127 -0
- package/cjs/adapter/http/impl/http-outgoing-message-host.js +210 -0
- package/cjs/adapter/http/impl/http-server-request.js +125 -0
- package/cjs/adapter/http/impl/http-server-response.js +226 -0
- package/cjs/adapter/http/request-parsers/batch-request-parser.js +169 -0
- package/cjs/adapter/http/request-parsers/parse-collection-request.js +165 -0
- package/cjs/adapter/http/request-parsers/parse-request.js +24 -0
- package/cjs/adapter/http/request-parsers/parse-singleton-request.js +96 -0
- package/cjs/adapter/request-context.host.js +17 -3
- package/cjs/adapter/request.host.js +6 -3
- package/cjs/adapter/response.host.js +5 -3
- package/cjs/index.js +4 -2
- package/esm/adapter/adapter.js +2 -2
- package/esm/adapter/http/express-adapter.js +6 -6
- package/esm/adapter/http/helpers/common.js +60 -0
- package/esm/adapter/http/helpers/convert-to-headers.js +60 -0
- package/esm/adapter/http/helpers/convert-to-raw-headers.js +21 -0
- package/esm/adapter/http/helpers/match-known-fields.js +43 -0
- package/esm/adapter/http/http-adapter.js +83 -441
- package/esm/adapter/http/impl/http-incoming-message-host.js +122 -0
- package/esm/adapter/http/impl/http-outgoing-message-host.js +205 -0
- package/esm/adapter/http/impl/http-server-request.js +121 -0
- package/esm/adapter/http/impl/http-server-response.js +222 -0
- package/esm/adapter/http/request-parsers/batch-request-parser.js +169 -0
- package/esm/adapter/http/request-parsers/parse-collection-request.js +161 -0
- package/esm/adapter/http/request-parsers/parse-request.js +20 -0
- package/esm/adapter/http/request-parsers/parse-singleton-request.js +92 -0
- package/esm/adapter/request-context.host.js +17 -3
- package/esm/adapter/request.host.js +6 -3
- package/esm/adapter/response.host.js +5 -3
- package/esm/index.js +4 -2
- package/package.json +19 -15
- package/types/adapter/adapter.d.ts +1 -1
- package/types/adapter/http/helpers/common.d.ts +17 -0
- package/types/adapter/http/helpers/convert-to-headers.d.ts +2 -0
- package/types/adapter/http/helpers/convert-to-raw-headers.d.ts +3 -0
- package/types/adapter/http/helpers/match-known-fields.d.ts +6 -0
- package/types/adapter/http/http-adapter.d.ts +7 -12
- package/types/adapter/http/impl/http-incoming-message-host.d.ts +58 -0
- package/types/adapter/http/impl/http-outgoing-message-host.d.ts +72 -0
- package/types/adapter/http/{http-request-message.d.ts → impl/http-server-request.d.ts} +52 -85
- package/types/adapter/http/impl/http-server-response.d.ts +137 -0
- package/types/adapter/http/request-parsers/batch-request-parser.d.ts +0 -0
- package/types/adapter/http/request-parsers/parse-collection-request.d.ts +4 -0
- package/types/adapter/http/request-parsers/parse-request.d.ts +4 -0
- package/types/adapter/http/request-parsers/parse-singleton-request.d.ts +4 -0
- package/types/adapter/interfaces/request-context.interface.d.ts +14 -10
- package/types/adapter/interfaces/request.interface.d.ts +3 -2
- package/types/adapter/interfaces/response.interface.d.ts +2 -2
- package/types/adapter/request-context.host.d.ts +9 -6
- package/types/adapter/request.host.d.ts +8 -4
- package/types/adapter/response.host.d.ts +6 -4
- package/types/index.d.ts +4 -2
- package/cjs/adapter/http/http-message.host.js +0 -251
- package/cjs/adapter/http/http-request-context.host.js +0 -28
- package/cjs/adapter/http/http-request-message.js +0 -152
- package/cjs/adapter/http/http-request.host.js +0 -14
- package/cjs/adapter/http/http-response-message.js +0 -238
- package/cjs/adapter/http/http-response.host.js +0 -14
- package/esm/adapter/http/http-message.host.js +0 -246
- package/esm/adapter/http/http-request-context.host.js +0 -24
- package/esm/adapter/http/http-request-message.js +0 -148
- package/esm/adapter/http/http-request.host.js +0 -10
- package/esm/adapter/http/http-response-message.js +0 -233
- package/esm/adapter/http/http-response.host.js +0 -10
- package/types/adapter/http/http-message.host.d.ts +0 -122
- package/types/adapter/http/http-request-context.host.d.ts +0 -16
- package/types/adapter/http/http-request.host.d.ts +0 -7
- package/types/adapter/http/http-response-message.d.ts +0 -321
- package/types/adapter/http/http-response.host.d.ts +0 -7
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file contains code blocks from open source NodeJs project
|
|
3
|
+
https://github.com/nodejs/
|
|
4
|
+
*/
|
|
5
|
+
var _a;
|
|
6
|
+
import * as stream from 'stream';
|
|
7
|
+
import { validateHeaderName, validateHeaderValue, validateString } from '../helpers/common.js';
|
|
8
|
+
export const kOutHeaders = Symbol('kOutHeaders');
|
|
9
|
+
export const kOutTrailers = Symbol('kOutTrailers');
|
|
10
|
+
export const kErrored = Symbol('kErrored');
|
|
11
|
+
// noinspection JSUnusedLocalSymbols
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @class HttpOutgoingMessageHost
|
|
15
|
+
*/
|
|
16
|
+
export class HttpOutgoingMessageHost {
|
|
17
|
+
constructor() {
|
|
18
|
+
this._headersSent = false;
|
|
19
|
+
this._closed = false;
|
|
20
|
+
this[_a] = null;
|
|
21
|
+
this.finished = false;
|
|
22
|
+
stream.Stream.apply(this);
|
|
23
|
+
}
|
|
24
|
+
get headersSent() {
|
|
25
|
+
return this._headersSent;
|
|
26
|
+
}
|
|
27
|
+
get closed() {
|
|
28
|
+
return this._closed;
|
|
29
|
+
}
|
|
30
|
+
get errored() {
|
|
31
|
+
return this[kErrored];
|
|
32
|
+
}
|
|
33
|
+
appendHeader(name, value) {
|
|
34
|
+
if (this.headersSent)
|
|
35
|
+
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
36
|
+
validateHeaderName(name);
|
|
37
|
+
validateHeaderValue(name, value);
|
|
38
|
+
const field = name.toLowerCase();
|
|
39
|
+
const headers = this[kOutHeaders];
|
|
40
|
+
if (headers == null || !headers[field]) {
|
|
41
|
+
return this.setHeader(name, value);
|
|
42
|
+
}
|
|
43
|
+
// Prepare the field for appending, if required
|
|
44
|
+
if (!Array.isArray(headers[field][1])) {
|
|
45
|
+
headers[field][1] = [headers[field][1]];
|
|
46
|
+
}
|
|
47
|
+
const existingValues = headers[field][1];
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
for (let i = 0, length = value.length; i < length; i++) {
|
|
50
|
+
existingValues.push(value[i]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
existingValues.push(value);
|
|
55
|
+
}
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
addTrailers(headers) {
|
|
59
|
+
if (headers && typeof headers === 'object') {
|
|
60
|
+
const entries = typeof headers.entries === 'function'
|
|
61
|
+
? headers.entries() : Object.entries(headers);
|
|
62
|
+
let trailers = this[kOutTrailers];
|
|
63
|
+
if (trailers == null)
|
|
64
|
+
this[kOutTrailers] = trailers = { __proto__: null };
|
|
65
|
+
for (const [name, value] of entries) {
|
|
66
|
+
validateHeaderName(name);
|
|
67
|
+
validateHeaderValue(name, value);
|
|
68
|
+
trailers[String(name).toLowerCase()] = [String(name), value];
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
throw new TypeError('Invalid "headers" argument. Value must be an object or raw headers array');
|
|
73
|
+
}
|
|
74
|
+
setHeader(name, value) {
|
|
75
|
+
if (this.headersSent)
|
|
76
|
+
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
77
|
+
validateHeaderName(name);
|
|
78
|
+
validateHeaderValue(name, value);
|
|
79
|
+
let headers = this[kOutHeaders];
|
|
80
|
+
if (headers == null)
|
|
81
|
+
this[kOutHeaders] = headers = { __proto__: null };
|
|
82
|
+
headers[name.toLowerCase()] = [name, value];
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
setHeaders(headers) {
|
|
86
|
+
if (this.headersSent)
|
|
87
|
+
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
88
|
+
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
|
|
89
|
+
const entries = typeof headers.entries === 'function'
|
|
90
|
+
? headers.entries() : Object.entries(headers);
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
this.setHeader(entry[0], entry[1]);
|
|
93
|
+
}
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
throw new TypeError('Invalid "headers" argument. Value must be an instance of "Headers" or "Map"');
|
|
97
|
+
}
|
|
98
|
+
getHeader(name) {
|
|
99
|
+
validateString(name);
|
|
100
|
+
const headers = this[kOutHeaders];
|
|
101
|
+
if (headers == null)
|
|
102
|
+
return;
|
|
103
|
+
const entry = headers[name.toLowerCase()];
|
|
104
|
+
return entry && entry[1];
|
|
105
|
+
}
|
|
106
|
+
getHeaderNames() {
|
|
107
|
+
return this[kOutHeaders] != null ? Object.keys(this[kOutHeaders]) : [];
|
|
108
|
+
}
|
|
109
|
+
getRawHeaderNames() {
|
|
110
|
+
const headersMap = this[kOutHeaders];
|
|
111
|
+
if (!headersMap)
|
|
112
|
+
return [];
|
|
113
|
+
const values = Object.values(headersMap);
|
|
114
|
+
const headers = Array(values.length);
|
|
115
|
+
for (let i = 0, l = values.length; i < l; i++) {
|
|
116
|
+
headers[i] = values[i][0];
|
|
117
|
+
}
|
|
118
|
+
return headers;
|
|
119
|
+
}
|
|
120
|
+
getHeaders() {
|
|
121
|
+
const headers = this[kOutHeaders];
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
const ret = { __proto__: null };
|
|
124
|
+
if (headers) {
|
|
125
|
+
const keys = Object.keys(headers);
|
|
126
|
+
let key;
|
|
127
|
+
let val;
|
|
128
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
129
|
+
key = keys[i];
|
|
130
|
+
val = headers[key][1];
|
|
131
|
+
ret[key] = val;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return ret;
|
|
135
|
+
}
|
|
136
|
+
hasHeader(name) {
|
|
137
|
+
validateString(name);
|
|
138
|
+
return this[kOutHeaders] != null &&
|
|
139
|
+
!!this[kOutHeaders][name.toLowerCase()];
|
|
140
|
+
}
|
|
141
|
+
removeHeader(name) {
|
|
142
|
+
validateString(name);
|
|
143
|
+
if (this.headersSent)
|
|
144
|
+
throw new Error(`Cannot remove headers after they are sent to the client`);
|
|
145
|
+
const key = name.toLowerCase();
|
|
146
|
+
// switch (key) {
|
|
147
|
+
// case 'connection':
|
|
148
|
+
// this._removedConnection = true;
|
|
149
|
+
// break;
|
|
150
|
+
// case 'content-length':
|
|
151
|
+
// this._removedContLen = true;
|
|
152
|
+
// break;
|
|
153
|
+
// case 'transfer-encoding':
|
|
154
|
+
// this._removedTE = true;
|
|
155
|
+
// break;
|
|
156
|
+
// case 'date':
|
|
157
|
+
// this.sendDate = false;
|
|
158
|
+
// break;
|
|
159
|
+
// }
|
|
160
|
+
if (this[kOutHeaders] != null) {
|
|
161
|
+
delete this[kOutHeaders][key];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
165
|
+
end(arg0, arg1, arg2) {
|
|
166
|
+
// let cb: (() => void) | undefined;
|
|
167
|
+
// let chunk: any;
|
|
168
|
+
// let encoding: BufferEncoding | undefined;
|
|
169
|
+
//
|
|
170
|
+
// if (typeof arg0 === 'function')
|
|
171
|
+
// cb = arg0;
|
|
172
|
+
// else {
|
|
173
|
+
// chunk = arg0;
|
|
174
|
+
// if (typeof arg1 === 'function')
|
|
175
|
+
// cb = arg1;
|
|
176
|
+
// else {
|
|
177
|
+
// encoding = arg1;
|
|
178
|
+
// cb = arg2;
|
|
179
|
+
// }
|
|
180
|
+
// }
|
|
181
|
+
//
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
_readConfig(init) {
|
|
185
|
+
this.req = init.req;
|
|
186
|
+
this.statusCode = init?.statusCode || 0;
|
|
187
|
+
this.statusMessage = init?.statusMessage || '';
|
|
188
|
+
this.chunkedEncoding = !!init?.chunkedEncoding;
|
|
189
|
+
this.sendDate = !!init?.sendDate;
|
|
190
|
+
this.strictContentLength = !!init?.strictContentLength;
|
|
191
|
+
if (init.headers) {
|
|
192
|
+
this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
|
|
193
|
+
}
|
|
194
|
+
this.body = init.body;
|
|
195
|
+
}
|
|
196
|
+
static create(init) {
|
|
197
|
+
const msg = new HttpOutgoingMessageHost();
|
|
198
|
+
if (init)
|
|
199
|
+
msg._readConfig(init);
|
|
200
|
+
return msg;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
_a = kErrored;
|
|
204
|
+
// Apply mixins
|
|
205
|
+
Object.assign(HttpOutgoingMessageHost.prototype, stream.Stream.prototype);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Some parts of this file contains codes from open source express library
|
|
3
|
+
https://github.com/expressjs
|
|
4
|
+
*/
|
|
5
|
+
import accepts from 'accepts';
|
|
6
|
+
import fresh from 'fresh';
|
|
7
|
+
import parseRange from 'range-parser';
|
|
8
|
+
import typeIs from 'type-is';
|
|
9
|
+
import { isReadable, mergePrototype } from '@opra/common';
|
|
10
|
+
import { HttpIncomingMessageHost } from './http-incoming-message-host.js';
|
|
11
|
+
function isHttpIncomingMessage(v) {
|
|
12
|
+
return v && Array.isArray(v.rawHeaders) && isReadable(v);
|
|
13
|
+
}
|
|
14
|
+
export var HttpServerRequest;
|
|
15
|
+
(function (HttpServerRequest) {
|
|
16
|
+
function create(instance) {
|
|
17
|
+
if (!isHttpIncomingMessage(instance))
|
|
18
|
+
instance = HttpIncomingMessageHost.create(instance);
|
|
19
|
+
mergePrototype(instance, HttpServerRequestHost.prototype);
|
|
20
|
+
return instance;
|
|
21
|
+
}
|
|
22
|
+
HttpServerRequest.create = create;
|
|
23
|
+
})(HttpServerRequest || (HttpServerRequest = {}));
|
|
24
|
+
class HttpServerRequestHost {
|
|
25
|
+
get protocol() {
|
|
26
|
+
const proto = this.header('X-Forwarded-Proto') || 'http';
|
|
27
|
+
const index = proto.indexOf(',');
|
|
28
|
+
return index !== -1
|
|
29
|
+
? proto.substring(0, index).trim()
|
|
30
|
+
: proto.trim();
|
|
31
|
+
}
|
|
32
|
+
get secure() {
|
|
33
|
+
return this.protocol === 'https';
|
|
34
|
+
}
|
|
35
|
+
get hostname() {
|
|
36
|
+
let host = this.get('X-Forwarded-Host');
|
|
37
|
+
if (!host) {
|
|
38
|
+
host = this.get('Host');
|
|
39
|
+
}
|
|
40
|
+
else if (host.indexOf(',') !== -1) {
|
|
41
|
+
// Note: X-Forwarded-Host is normally only ever a
|
|
42
|
+
// single value, but this is to be safe.
|
|
43
|
+
host = host.substring(0, host.indexOf(',')).trim();
|
|
44
|
+
}
|
|
45
|
+
if (!host)
|
|
46
|
+
return;
|
|
47
|
+
// IPv6 literal support
|
|
48
|
+
const offset = host[0] === '['
|
|
49
|
+
? host.indexOf(']') + 1
|
|
50
|
+
: 0;
|
|
51
|
+
const index = host.indexOf(':', offset);
|
|
52
|
+
return index !== -1
|
|
53
|
+
? host.substring(0, index)
|
|
54
|
+
: host;
|
|
55
|
+
}
|
|
56
|
+
get fresh() {
|
|
57
|
+
const method = this.method;
|
|
58
|
+
// GET or HEAD for weak freshness validation only
|
|
59
|
+
if ('GET' !== method && 'HEAD' !== method)
|
|
60
|
+
return false;
|
|
61
|
+
const status = this.res?.statusCode;
|
|
62
|
+
// 2xx or 304 as per rfc2616 14.26
|
|
63
|
+
if ((status >= 200 && status < 300) || 304 === status) {
|
|
64
|
+
return fresh(this.headers, {
|
|
65
|
+
'etag': this.res.getHeader('ETag'),
|
|
66
|
+
'last-modified': this.res.getHeader('Last-Modified')
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
get xhr() {
|
|
72
|
+
const val = this.get('X-Requested-With') || '';
|
|
73
|
+
return val.toLowerCase() === 'xmlhttprequest';
|
|
74
|
+
}
|
|
75
|
+
header(name) {
|
|
76
|
+
name = name.toLowerCase();
|
|
77
|
+
const headers = this.headers;
|
|
78
|
+
switch (name) {
|
|
79
|
+
case 'referer':
|
|
80
|
+
return headers.referer || headers.referrer;
|
|
81
|
+
case 'referrer':
|
|
82
|
+
return headers.referrer || headers.referer;
|
|
83
|
+
default:
|
|
84
|
+
return headers[name];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
get(name) {
|
|
88
|
+
return this.header(name);
|
|
89
|
+
}
|
|
90
|
+
accepts(...types) {
|
|
91
|
+
const accept = accepts(this);
|
|
92
|
+
return accept.types.call(accept, ...types);
|
|
93
|
+
}
|
|
94
|
+
acceptsCharsets(...charsets) {
|
|
95
|
+
const accept = accepts(this);
|
|
96
|
+
return accept.charsets.call(accept, ...charsets);
|
|
97
|
+
}
|
|
98
|
+
acceptsEncodings(...encoding) {
|
|
99
|
+
const accept = accepts(this);
|
|
100
|
+
// eslint-disable-next-line prefer-spread
|
|
101
|
+
return accept.encodings.apply(accept, encoding);
|
|
102
|
+
}
|
|
103
|
+
acceptsLanguages(...lang) {
|
|
104
|
+
const accept = accepts(this);
|
|
105
|
+
// eslint-disable-next-line prefer-spread
|
|
106
|
+
return accept.languages.apply(accept, lang);
|
|
107
|
+
}
|
|
108
|
+
is(type, ...otherTypes) {
|
|
109
|
+
const types = Array.isArray(type) ? type : [type];
|
|
110
|
+
if (otherTypes.length)
|
|
111
|
+
types.push(...otherTypes);
|
|
112
|
+
const contentType = this.header('content-type');
|
|
113
|
+
return contentType ? typeIs.is(contentType, types) : null;
|
|
114
|
+
}
|
|
115
|
+
range(size, options) {
|
|
116
|
+
const range = this.header('range');
|
|
117
|
+
if (!range)
|
|
118
|
+
return;
|
|
119
|
+
return parseRange(size, range, options);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Some parts of this file contains codes from open source express library
|
|
3
|
+
https://github.com/expressjs
|
|
4
|
+
*/
|
|
5
|
+
import contentDisposition from 'content-disposition';
|
|
6
|
+
import contentType from 'content-type';
|
|
7
|
+
import cookie from 'cookie';
|
|
8
|
+
import cookieSignature from 'cookie-signature';
|
|
9
|
+
import encodeUrl from 'encodeurl';
|
|
10
|
+
import mime from 'mime-types';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { toString } from 'putil-varhelpers';
|
|
13
|
+
import vary from 'vary';
|
|
14
|
+
import { HttpStatusCodes, isStream, mergePrototype } from '@opra/common';
|
|
15
|
+
import { HttpOutgoingMessageHost } from './http-outgoing-message-host.js';
|
|
16
|
+
const charsetRegExp = /;\s*charset\s*=/;
|
|
17
|
+
function isHttpIncomingMessage(v) {
|
|
18
|
+
return v && typeof v.getHeaders === 'function' && isStream(v);
|
|
19
|
+
}
|
|
20
|
+
export var HttpServerResponse;
|
|
21
|
+
(function (HttpServerResponse) {
|
|
22
|
+
function create(instance) {
|
|
23
|
+
if (!isHttpIncomingMessage(instance))
|
|
24
|
+
instance = HttpOutgoingMessageHost.create(instance);
|
|
25
|
+
mergePrototype(instance, HttpServerResponseHost.prototype);
|
|
26
|
+
return instance;
|
|
27
|
+
}
|
|
28
|
+
HttpServerResponse.create = create;
|
|
29
|
+
})(HttpServerResponse || (HttpServerResponse = {}));
|
|
30
|
+
class HttpServerResponseHost {
|
|
31
|
+
attachment(filename) {
|
|
32
|
+
if (filename)
|
|
33
|
+
this.contentType(path.extname(filename));
|
|
34
|
+
this.setHeader('Content-Disposition', contentDisposition(filename));
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
contentType(type) {
|
|
38
|
+
const ct = type.indexOf('/') === -1
|
|
39
|
+
? mime.lookup(type)
|
|
40
|
+
: type;
|
|
41
|
+
this.setHeader('Content-Type', ct);
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
setHeader(field, val) {
|
|
45
|
+
const setHeader = Object.getPrototypeOf(this).setHeader;
|
|
46
|
+
if (typeof field === 'object') {
|
|
47
|
+
for (const [k, v] of Object.entries(field)) {
|
|
48
|
+
this.setHeader(k, v);
|
|
49
|
+
}
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
const fieldLower = field.toLowerCase();
|
|
53
|
+
let value = Array.isArray(val)
|
|
54
|
+
? val.map(String)
|
|
55
|
+
: (val ? String(val) : '');
|
|
56
|
+
// add charset to content-type
|
|
57
|
+
if (fieldLower === 'content-type') {
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
throw new TypeError('Content-Type cannot be set to an Array');
|
|
60
|
+
}
|
|
61
|
+
if (!charsetRegExp.test(value)) {
|
|
62
|
+
const charset = mime.charsets.lookup(value.split(';')[0]);
|
|
63
|
+
if (charset)
|
|
64
|
+
value += '; charset=' + charset.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
setHeader.call(this, field, value);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
clearCookie(name, options) {
|
|
71
|
+
const opts = {
|
|
72
|
+
expires: new Date(1),
|
|
73
|
+
path: '/',
|
|
74
|
+
...options
|
|
75
|
+
};
|
|
76
|
+
return this.cookie(name, '', opts);
|
|
77
|
+
}
|
|
78
|
+
cookie(name, value, options) {
|
|
79
|
+
const opts = { ...options };
|
|
80
|
+
let val = typeof value === 'object'
|
|
81
|
+
? 'j:' + JSON.stringify(value)
|
|
82
|
+
: String(value);
|
|
83
|
+
if (opts.signed) {
|
|
84
|
+
const secret = opts.secret || this.req?.secret;
|
|
85
|
+
if (!secret)
|
|
86
|
+
throw new Error('"secret" required for signed cookies');
|
|
87
|
+
val = 's:' + cookieSignature.sign(val, secret);
|
|
88
|
+
}
|
|
89
|
+
if (opts.maxAge != null) {
|
|
90
|
+
const maxAge = opts.maxAge - 0;
|
|
91
|
+
if (!isNaN(maxAge)) {
|
|
92
|
+
opts.expires = new Date(Date.now() + maxAge);
|
|
93
|
+
opts.maxAge = Math.floor(maxAge / 1000);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (opts.path == null)
|
|
97
|
+
opts.path = '/';
|
|
98
|
+
this.appendHeader('Set-Cookie', cookie.serialize(name, String(val), opts));
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
status(code) {
|
|
102
|
+
this.statusCode = code;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
sendStatus(statusCode) {
|
|
106
|
+
const body = HttpStatusCodes[statusCode] || String(statusCode);
|
|
107
|
+
this.statusCode = statusCode;
|
|
108
|
+
this.contentType('txt');
|
|
109
|
+
return this.send(body);
|
|
110
|
+
}
|
|
111
|
+
links(links) {
|
|
112
|
+
let link = this.getHeader('Link') || '';
|
|
113
|
+
if (link)
|
|
114
|
+
link += ', ';
|
|
115
|
+
this.setHeader('Link', link +
|
|
116
|
+
Object.keys(links)
|
|
117
|
+
.map(rel => '<' + links[rel] + '>; rel="' + rel + '"')
|
|
118
|
+
.join(', '));
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
location(url) {
|
|
122
|
+
let loc = url;
|
|
123
|
+
// "back" is an alias for the referrer
|
|
124
|
+
if (url === 'back')
|
|
125
|
+
loc = this.req?.get('Referrer') || '/';
|
|
126
|
+
// set location
|
|
127
|
+
return this.setHeader('Location', encodeUrl(loc));
|
|
128
|
+
}
|
|
129
|
+
redirect(arg0, arg1) {
|
|
130
|
+
const address = String(arg1 || arg0);
|
|
131
|
+
const status = typeof arg0 === 'number' ? arg0 : 302;
|
|
132
|
+
// Set location header
|
|
133
|
+
this.location(address);
|
|
134
|
+
// Respond
|
|
135
|
+
this.statusCode = status;
|
|
136
|
+
this.end();
|
|
137
|
+
}
|
|
138
|
+
send(body) {
|
|
139
|
+
let chunk = body;
|
|
140
|
+
let encoding;
|
|
141
|
+
const req = this.req;
|
|
142
|
+
let ctype = toString(this.getHeader('Content-Type'));
|
|
143
|
+
if (typeof chunk !== 'string') {
|
|
144
|
+
if (chunk === null)
|
|
145
|
+
chunk = '';
|
|
146
|
+
else if (Buffer.isBuffer(chunk)) {
|
|
147
|
+
if (!ctype)
|
|
148
|
+
this.contentType('bin');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
ctype = 'json';
|
|
152
|
+
chunk = JSON.stringify(chunk);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// write strings in utf-8
|
|
156
|
+
if (typeof chunk === 'string') {
|
|
157
|
+
encoding = 'utf-8';
|
|
158
|
+
this.setHeader('Content-Type', setCharset(ctype || 'txt', encoding));
|
|
159
|
+
}
|
|
160
|
+
// populate Content-Length
|
|
161
|
+
let len = 0;
|
|
162
|
+
if (chunk !== undefined) {
|
|
163
|
+
if (Buffer.isBuffer(chunk)) {
|
|
164
|
+
// get length of Buffer
|
|
165
|
+
len = chunk.length;
|
|
166
|
+
}
|
|
167
|
+
else if (chunk.length < 1000) {
|
|
168
|
+
// just calculate length when small chunk
|
|
169
|
+
len = Buffer.byteLength(chunk, encoding);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// convert chunk to Buffer and calculate
|
|
173
|
+
chunk = Buffer.from(chunk, encoding);
|
|
174
|
+
encoding = undefined;
|
|
175
|
+
len = chunk.length;
|
|
176
|
+
}
|
|
177
|
+
this.setHeader('Content-Length', len);
|
|
178
|
+
}
|
|
179
|
+
// freshness
|
|
180
|
+
if (req?.fresh)
|
|
181
|
+
this.statusCode = 304;
|
|
182
|
+
// strip irrelevant headers
|
|
183
|
+
if (204 === this.statusCode || 304 === this.statusCode) {
|
|
184
|
+
this.removeHeader('Content-Type');
|
|
185
|
+
this.removeHeader('Content-Length');
|
|
186
|
+
this.removeHeader('Transfer-Encoding');
|
|
187
|
+
chunk = '';
|
|
188
|
+
}
|
|
189
|
+
// alter headers for 205
|
|
190
|
+
if (this.statusCode === 205) {
|
|
191
|
+
this.setHeader('Content-Length', '0');
|
|
192
|
+
this.removeHeader('Transfer-Encoding');
|
|
193
|
+
chunk = '';
|
|
194
|
+
}
|
|
195
|
+
if (req?.method === 'HEAD') {
|
|
196
|
+
// skip body for HEAD
|
|
197
|
+
this.end();
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// respond
|
|
201
|
+
if (encoding)
|
|
202
|
+
this.end(chunk, encoding);
|
|
203
|
+
else
|
|
204
|
+
this.end(chunk);
|
|
205
|
+
}
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
vary(field) {
|
|
209
|
+
vary(this, field);
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function setCharset(type, charset) {
|
|
214
|
+
if (!(type && charset))
|
|
215
|
+
return type;
|
|
216
|
+
// parse type
|
|
217
|
+
const parsed = contentType.parse(type);
|
|
218
|
+
// set charset
|
|
219
|
+
parsed.parameters.charset = charset;
|
|
220
|
+
// format type
|
|
221
|
+
return contentType.format(parsed);
|
|
222
|
+
}
|