@opra/core 1.0.0-beta.2 → 1.0.0-beta.4
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/{http/impl/asset-cache.js → asset-cache.js} +6 -0
- package/cjs/execution-context.js +3 -14
- package/cjs/index.js +3 -25
- package/cjs/platform-adapter.js +3 -3
- package/cjs/{helpers/service-base.js → service-base.js} +9 -4
- package/esm/{http/impl/asset-cache.js → asset-cache.js} +6 -0
- package/esm/execution-context.js +2 -13
- package/esm/index.js +3 -24
- package/esm/platform-adapter.js +2 -2
- package/esm/{helpers/service-base.js → service-base.js} +9 -4
- package/package.json +5 -39
- package/types/{http/impl/asset-cache.d.ts → asset-cache.d.ts} +1 -0
- package/types/execution-context.d.ts +4 -8
- package/types/index.d.cts +3 -23
- package/types/index.d.ts +3 -23
- package/types/interfaces/logger.interface.d.ts +1 -1
- package/types/platform-adapter.d.ts +3 -3
- package/types/service-base.d.ts +11 -0
- package/cjs/augmentation/http-controller.augmentation.js +0 -25
- package/cjs/http/express-adapter.js +0 -155
- package/cjs/http/http-adapter.js +0 -24
- package/cjs/http/http-context.js +0 -104
- package/cjs/http/http-handler.js +0 -609
- package/cjs/http/impl/http-incoming.host.js +0 -112
- package/cjs/http/impl/http-outgoing.host.js +0 -207
- package/cjs/http/impl/multipart-reader.js +0 -196
- package/cjs/http/impl/node-incoming-message.host.js +0 -109
- package/cjs/http/impl/node-outgoing-message.host.js +0 -195
- package/cjs/http/interfaces/http-incoming.interface.js +0 -25
- package/cjs/http/interfaces/http-outgoing.interface.js +0 -22
- package/cjs/http/interfaces/node-incoming-message.interface.js +0 -64
- package/cjs/http/interfaces/node-outgoing-message.interface.js +0 -15
- package/cjs/http/utils/body-reader.js +0 -216
- package/cjs/http/utils/common.js +0 -67
- package/cjs/http/utils/concat-readable.js +0 -19
- package/cjs/http/utils/convert-to-headers.js +0 -64
- package/cjs/http/utils/convert-to-raw-headers.js +0 -23
- package/cjs/http/utils/match-known-fields.js +0 -49
- package/cjs/http/utils/wrap-exception.js +0 -33
- package/cjs/type-guards.js +0 -22
- package/esm/augmentation/http-controller.augmentation.js +0 -23
- package/esm/http/express-adapter.js +0 -150
- package/esm/http/http-adapter.js +0 -20
- package/esm/http/http-context.js +0 -99
- package/esm/http/http-handler.js +0 -604
- package/esm/http/impl/http-incoming.host.js +0 -107
- package/esm/http/impl/http-outgoing.host.js +0 -202
- package/esm/http/impl/multipart-reader.js +0 -191
- package/esm/http/impl/node-incoming-message.host.js +0 -105
- package/esm/http/impl/node-outgoing-message.host.js +0 -191
- package/esm/http/interfaces/http-incoming.interface.js +0 -22
- package/esm/http/interfaces/http-outgoing.interface.js +0 -19
- package/esm/http/interfaces/node-incoming-message.interface.js +0 -61
- package/esm/http/interfaces/node-outgoing-message.interface.js +0 -12
- package/esm/http/utils/body-reader.js +0 -211
- package/esm/http/utils/common.js +0 -61
- package/esm/http/utils/concat-readable.js +0 -16
- package/esm/http/utils/convert-to-headers.js +0 -60
- package/esm/http/utils/convert-to-raw-headers.js +0 -20
- package/esm/http/utils/match-known-fields.js +0 -45
- package/esm/http/utils/wrap-exception.js +0 -30
- package/esm/type-guards.js +0 -16
- package/types/augmentation/http-controller.augmentation.d.ts +0 -20
- package/types/helpers/service-base.d.ts +0 -10
- package/types/http/express-adapter.d.ts +0 -13
- package/types/http/http-adapter.d.ts +0 -54
- package/types/http/http-context.d.ts +0 -44
- package/types/http/http-handler.d.ts +0 -75
- package/types/http/impl/http-incoming.host.d.ts +0 -22
- package/types/http/impl/http-outgoing.host.d.ts +0 -17
- package/types/http/impl/multipart-reader.d.ts +0 -46
- package/types/http/impl/node-incoming-message.host.d.ts +0 -45
- package/types/http/impl/node-outgoing-message.host.d.ts +0 -49
- package/types/http/interfaces/http-incoming.interface.d.ts +0 -192
- package/types/http/interfaces/http-outgoing.interface.d.ts +0 -144
- package/types/http/interfaces/node-incoming-message.interface.d.ts +0 -36
- package/types/http/interfaces/node-outgoing-message.interface.d.ts +0 -27
- package/types/http/utils/body-reader.d.ts +0 -38
- package/types/http/utils/common.d.ts +0 -17
- package/types/http/utils/concat-readable.d.ts +0 -2
- package/types/http/utils/convert-to-headers.d.ts +0 -2
- package/types/http/utils/convert-to-raw-headers.d.ts +0 -2
- package/types/http/utils/match-known-fields.d.ts +0 -6
- package/types/http/utils/wrap-exception.d.ts +0 -2
- package/types/type-guards.d.ts +0 -8
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
This file contains code blocks from open source NodeJs project
|
|
3
|
-
https://github.com/nodejs/
|
|
4
|
-
*/
|
|
5
|
-
import { Duplex } from 'stream';
|
|
6
|
-
import { validateHeaderName, validateHeaderValue, validateString } from '../utils/common.js';
|
|
7
|
-
export const kOutHeaders = Symbol.for('kOutHeaders');
|
|
8
|
-
export const kOutTrailers = Symbol.for('kOutTrailers');
|
|
9
|
-
// noinspection JSUnusedLocalSymbols
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
* @class NodeOutgoingMessageHost
|
|
13
|
-
*/
|
|
14
|
-
export class NodeOutgoingMessageHost extends Duplex {
|
|
15
|
-
constructor(init) {
|
|
16
|
-
super();
|
|
17
|
-
this._headersSent = false;
|
|
18
|
-
this.finished = false;
|
|
19
|
-
if (init) {
|
|
20
|
-
this.req = init.req;
|
|
21
|
-
this.statusCode = init?.statusCode || 0;
|
|
22
|
-
this.statusMessage = init?.statusMessage || '';
|
|
23
|
-
this.chunkedEncoding = !!init?.chunkedEncoding;
|
|
24
|
-
this.sendDate = !!init?.sendDate;
|
|
25
|
-
this.strictContentLength = !!init?.strictContentLength;
|
|
26
|
-
if (init.headers)
|
|
27
|
-
this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
|
|
28
|
-
this.body = init.body;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
get httpVersionMajor() {
|
|
32
|
-
return this.req?.httpVersionMajor || this._httpVersionMajor;
|
|
33
|
-
}
|
|
34
|
-
get httpVersionMinor() {
|
|
35
|
-
return this.req?.httpVersionMinor || this._httpVersionMinor;
|
|
36
|
-
}
|
|
37
|
-
get headersSent() {
|
|
38
|
-
return this._headersSent;
|
|
39
|
-
}
|
|
40
|
-
appendHeader(name, value) {
|
|
41
|
-
if (this.headersSent)
|
|
42
|
-
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
43
|
-
validateHeaderName(name);
|
|
44
|
-
validateHeaderValue(name, value);
|
|
45
|
-
const field = name.toLowerCase();
|
|
46
|
-
const headers = this[kOutHeaders];
|
|
47
|
-
if (headers == null || !headers[field]) {
|
|
48
|
-
return this.setHeader(name, value);
|
|
49
|
-
}
|
|
50
|
-
// Prepare the field for appending, if required
|
|
51
|
-
if (!Array.isArray(headers[field][1])) {
|
|
52
|
-
headers[field][1] = [headers[field][1]];
|
|
53
|
-
}
|
|
54
|
-
const existingValues = headers[field][1];
|
|
55
|
-
if (Array.isArray(value)) {
|
|
56
|
-
for (let i = 0, length = value.length; i < length; i++) {
|
|
57
|
-
existingValues.push(value[i]);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
existingValues.push(value);
|
|
62
|
-
}
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
addTrailers(headers) {
|
|
66
|
-
if (headers && typeof headers === 'object') {
|
|
67
|
-
const entries = typeof headers.entries === 'function' ? headers.entries() : Object.entries(headers);
|
|
68
|
-
let trailers = this[kOutTrailers];
|
|
69
|
-
if (trailers == null)
|
|
70
|
-
this[kOutTrailers] = trailers = { __proto__: null };
|
|
71
|
-
for (const [name, value] of entries) {
|
|
72
|
-
validateHeaderName(name);
|
|
73
|
-
validateHeaderValue(name, value);
|
|
74
|
-
trailers[String(name).toLowerCase()] = [String(name), value];
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
throw new TypeError('Invalid "headers" argument. Value must be an object or raw headers array');
|
|
79
|
-
}
|
|
80
|
-
flushHeaders() {
|
|
81
|
-
// nothing to do
|
|
82
|
-
}
|
|
83
|
-
setHeader(name, value) {
|
|
84
|
-
if (this.headersSent)
|
|
85
|
-
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
86
|
-
validateHeaderName(name);
|
|
87
|
-
validateHeaderValue(name, value);
|
|
88
|
-
let headers = this[kOutHeaders];
|
|
89
|
-
if (headers == null)
|
|
90
|
-
this[kOutHeaders] = headers = { __proto__: null };
|
|
91
|
-
headers[name.toLowerCase()] = [name, value];
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
setHeaders(headers) {
|
|
95
|
-
if (this.headersSent)
|
|
96
|
-
throw new Error(`Cannot set headers after they are sent to the client`);
|
|
97
|
-
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
|
|
98
|
-
const entries = typeof headers.entries === 'function' ? headers.entries() : Object.entries(headers);
|
|
99
|
-
for (const entry of entries) {
|
|
100
|
-
this.setHeader(entry[0], entry[1]);
|
|
101
|
-
}
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
throw new TypeError('Invalid "headers" argument. Value must be an instance of "Headers" or "Map"');
|
|
105
|
-
}
|
|
106
|
-
getHeader(name) {
|
|
107
|
-
validateString(name);
|
|
108
|
-
const headers = this[kOutHeaders];
|
|
109
|
-
if (headers == null)
|
|
110
|
-
return;
|
|
111
|
-
const entry = headers[name.toLowerCase()];
|
|
112
|
-
return entry && entry[1];
|
|
113
|
-
}
|
|
114
|
-
getHeaderNames() {
|
|
115
|
-
return this[kOutHeaders] != null ? Object.keys(this[kOutHeaders]) : [];
|
|
116
|
-
}
|
|
117
|
-
getRawHeaderNames() {
|
|
118
|
-
const headersMap = this[kOutHeaders];
|
|
119
|
-
if (!headersMap)
|
|
120
|
-
return [];
|
|
121
|
-
const values = Object.values(headersMap);
|
|
122
|
-
const headers = Array(values.length);
|
|
123
|
-
for (let i = 0, l = values.length; i < l; i++) {
|
|
124
|
-
headers[i] = values[i][0];
|
|
125
|
-
}
|
|
126
|
-
return headers;
|
|
127
|
-
}
|
|
128
|
-
getHeaders() {
|
|
129
|
-
const headers = this[kOutHeaders];
|
|
130
|
-
// @ts-ignore
|
|
131
|
-
const ret = { __proto__: null };
|
|
132
|
-
if (headers) {
|
|
133
|
-
const keys = Object.keys(headers);
|
|
134
|
-
let key;
|
|
135
|
-
let val;
|
|
136
|
-
for (let i = 0; i < keys.length; ++i) {
|
|
137
|
-
key = keys[i];
|
|
138
|
-
val = headers[key][1];
|
|
139
|
-
ret[key] = val;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return ret;
|
|
143
|
-
}
|
|
144
|
-
hasHeader(name) {
|
|
145
|
-
validateString(name);
|
|
146
|
-
return this[kOutHeaders] != null && !!this[kOutHeaders][name.toLowerCase()];
|
|
147
|
-
}
|
|
148
|
-
removeHeader(name) {
|
|
149
|
-
validateString(name);
|
|
150
|
-
if (this.headersSent)
|
|
151
|
-
throw new Error(`Cannot remove headers after they are sent to the client`);
|
|
152
|
-
const key = name.toLowerCase();
|
|
153
|
-
// switch (key) {
|
|
154
|
-
// case 'connection':
|
|
155
|
-
// this._removedConnection = true;
|
|
156
|
-
// break;
|
|
157
|
-
// case 'content-length':
|
|
158
|
-
// this._removedContLen = true;
|
|
159
|
-
// break;
|
|
160
|
-
// case 'transfer-encoding':
|
|
161
|
-
// this._removedTE = true;
|
|
162
|
-
// break;
|
|
163
|
-
// case 'date':
|
|
164
|
-
// this.sendDate = false;
|
|
165
|
-
// break;
|
|
166
|
-
// }
|
|
167
|
-
if (this[kOutHeaders] != null) {
|
|
168
|
-
delete this[kOutHeaders][key];
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
172
|
-
end(arg0, arg1, arg2) {
|
|
173
|
-
// let cb: (() => void) | undefined;
|
|
174
|
-
// let chunk: any;
|
|
175
|
-
// let encoding: BufferEncoding | undefined;
|
|
176
|
-
//
|
|
177
|
-
// if (typeof arg0 === 'function')
|
|
178
|
-
// cb = arg0;
|
|
179
|
-
// else {
|
|
180
|
-
// chunk = arg0;
|
|
181
|
-
// if (typeof arg1 === 'function')
|
|
182
|
-
// cb = arg1;
|
|
183
|
-
// else {
|
|
184
|
-
// encoding = arg1;
|
|
185
|
-
// cb = arg2;
|
|
186
|
-
// }
|
|
187
|
-
// }
|
|
188
|
-
//
|
|
189
|
-
return this;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mergePrototype } from '@opra/common';
|
|
2
|
-
import { isHttpIncoming, isNodeIncomingMessage } from '../../type-guards.js';
|
|
3
|
-
import { HttpIncomingHost } from '../impl/http-incoming.host.js';
|
|
4
|
-
import { NodeIncomingMessage } from './node-incoming-message.interface.js';
|
|
5
|
-
/**
|
|
6
|
-
* @namespace HttpIncoming
|
|
7
|
-
*/
|
|
8
|
-
export var HttpIncoming;
|
|
9
|
-
(function (HttpIncoming) {
|
|
10
|
-
function from(instance) {
|
|
11
|
-
if (isHttpIncoming(instance))
|
|
12
|
-
return instance;
|
|
13
|
-
if (!isNodeIncomingMessage(instance))
|
|
14
|
-
instance = NodeIncomingMessage.from(instance);
|
|
15
|
-
mergePrototype(instance, HttpIncomingHost.prototype);
|
|
16
|
-
const req = instance;
|
|
17
|
-
req.baseUrl = req.baseUrl || '';
|
|
18
|
-
req.params = req.params || {};
|
|
19
|
-
return req;
|
|
20
|
-
}
|
|
21
|
-
HttpIncoming.from = from;
|
|
22
|
-
})(HttpIncoming || (HttpIncoming = {}));
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { mergePrototype } from '@opra/common';
|
|
2
|
-
import { isHttpOutgoing, isNodeOutgoingMessage } from '../../type-guards.js';
|
|
3
|
-
import { HttpOutgoingHost } from '../impl/http-outgoing.host.js';
|
|
4
|
-
import { NodeOutgoingMessage } from './node-outgoing-message.interface.js';
|
|
5
|
-
/**
|
|
6
|
-
* @namespace HttpIncoming
|
|
7
|
-
*/
|
|
8
|
-
export var HttpOutgoing;
|
|
9
|
-
(function (HttpOutgoing) {
|
|
10
|
-
function from(instance) {
|
|
11
|
-
if (isHttpOutgoing(instance))
|
|
12
|
-
return instance;
|
|
13
|
-
if (!isNodeOutgoingMessage(instance))
|
|
14
|
-
instance = NodeOutgoingMessage.from(instance);
|
|
15
|
-
mergePrototype(instance, HttpOutgoingHost.prototype);
|
|
16
|
-
return instance;
|
|
17
|
-
}
|
|
18
|
-
HttpOutgoing.from = from;
|
|
19
|
-
})(HttpOutgoing || (HttpOutgoing = {}));
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { HTTPParser } from '@browsery/http-parser';
|
|
2
|
-
import { isAsyncIterable, isIterable } from '@opra/common';
|
|
3
|
-
import { Readable } from 'stream';
|
|
4
|
-
import { CRLF, kHttpParser, NodeIncomingMessageHost } from '../impl/node-incoming-message.host.js';
|
|
5
|
-
import { concatReadable } from '../utils/concat-readable.js';
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @namespace NodeIncomingMessage
|
|
9
|
-
*/
|
|
10
|
-
export var NodeIncomingMessage;
|
|
11
|
-
(function (NodeIncomingMessage) {
|
|
12
|
-
/**
|
|
13
|
-
* Creates a new NodeIncomingMessage from given argument
|
|
14
|
-
* @param iterable
|
|
15
|
-
*/
|
|
16
|
-
function from(iterable) {
|
|
17
|
-
if (typeof iterable === 'object' && !(isIterable(iterable) || isAsyncIterable(iterable))) {
|
|
18
|
-
return new NodeIncomingMessageHost(iterable);
|
|
19
|
-
}
|
|
20
|
-
const msg = new NodeIncomingMessageHost();
|
|
21
|
-
const parser = (msg[kHttpParser] = new HTTPParser(HTTPParser.REQUEST));
|
|
22
|
-
let bodyChunks;
|
|
23
|
-
parser[HTTPParser.kOnHeadersComplete] = (info) => {
|
|
24
|
-
msg.httpVersionMajor = info.versionMajor;
|
|
25
|
-
msg.httpVersionMinor = info.versionMinor;
|
|
26
|
-
msg.rawHeaders = info.headers;
|
|
27
|
-
msg.method = HTTPParser.methods[info.method];
|
|
28
|
-
msg.url = info.url;
|
|
29
|
-
msg.emit('headers');
|
|
30
|
-
};
|
|
31
|
-
parser[HTTPParser.kOnHeaders] = (trailers) => {
|
|
32
|
-
msg.rawTrailers = trailers;
|
|
33
|
-
};
|
|
34
|
-
parser[HTTPParser.kOnBody] = (chunk, offset, length) => {
|
|
35
|
-
bodyChunks = bodyChunks || [];
|
|
36
|
-
bodyChunks.push(chunk.subarray(offset, offset + length));
|
|
37
|
-
};
|
|
38
|
-
const readable = concatReadable(Readable.from(iterable), Readable.from(CRLF));
|
|
39
|
-
msg.once('finish', () => {
|
|
40
|
-
parser.finish();
|
|
41
|
-
msg.complete = true;
|
|
42
|
-
if (bodyChunks)
|
|
43
|
-
msg.body = Buffer.concat(bodyChunks);
|
|
44
|
-
});
|
|
45
|
-
readable.pipe(msg);
|
|
46
|
-
return msg;
|
|
47
|
-
}
|
|
48
|
-
NodeIncomingMessage.from = from;
|
|
49
|
-
/**
|
|
50
|
-
* Creates a new NodeIncomingMessage from given argument
|
|
51
|
-
* @param iterable
|
|
52
|
-
*/
|
|
53
|
-
async function fromAsync(iterable) {
|
|
54
|
-
return new Promise((resolve, reject) => {
|
|
55
|
-
const msg = from(iterable);
|
|
56
|
-
msg.once('finish', () => resolve(msg));
|
|
57
|
-
msg.once('error', error => reject(error));
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
NodeIncomingMessage.fromAsync = fromAsync;
|
|
61
|
-
})(NodeIncomingMessage || (NodeIncomingMessage = {}));
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { NodeOutgoingMessageHost } from '../impl/node-outgoing-message.host.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
* @namespace NodeOutgoingMessage
|
|
5
|
-
*/
|
|
6
|
-
export var NodeOutgoingMessage;
|
|
7
|
-
(function (NodeOutgoingMessage) {
|
|
8
|
-
function from(init) {
|
|
9
|
-
return new NodeOutgoingMessageHost(init);
|
|
10
|
-
}
|
|
11
|
-
NodeOutgoingMessage.from = from;
|
|
12
|
-
})(NodeOutgoingMessage || (NodeOutgoingMessage = {}));
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import typeIs from '@browsery/type-is';
|
|
2
|
-
import { BadRequestError, InternalServerError, OpraHttpError } from '@opra/common';
|
|
3
|
-
import { Base64Decode } from 'base64-stream';
|
|
4
|
-
import byteParser from 'bytes';
|
|
5
|
-
import { parse as parseContentType } from 'content-type';
|
|
6
|
-
import { EventEmitter } from 'events';
|
|
7
|
-
import iconv from 'iconv-lite';
|
|
8
|
-
import { Writable } from 'stream';
|
|
9
|
-
import * as zlib from 'zlib';
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
* @class BodyReader
|
|
13
|
-
*/
|
|
14
|
-
export class BodyReader extends EventEmitter {
|
|
15
|
-
constructor(req, options) {
|
|
16
|
-
super();
|
|
17
|
-
this.req = req;
|
|
18
|
-
this._completed = false;
|
|
19
|
-
this._receivedSize = 0;
|
|
20
|
-
this.onAborted = () => this._onAborted();
|
|
21
|
-
this.onData = (chunk) => this._onData(chunk);
|
|
22
|
-
this.onEnd = (err) => this._onEnd(err);
|
|
23
|
-
this.cleanup = () => this._cleanup();
|
|
24
|
-
this.limit = options?.limit
|
|
25
|
-
? typeof options.limit === 'number'
|
|
26
|
-
? options.limit
|
|
27
|
-
: byteParser(options.limit)
|
|
28
|
-
: undefined;
|
|
29
|
-
}
|
|
30
|
-
async read() {
|
|
31
|
-
/* istanbul ignore next */
|
|
32
|
-
if (this._completed) {
|
|
33
|
-
throw new InternalServerError({
|
|
34
|
-
message: 'Stream already read',
|
|
35
|
-
code: 'STREAM_ALREADY_READ',
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
if (!this.req.readable) {
|
|
39
|
-
throw new InternalServerError({
|
|
40
|
-
message: 'Stream is not readable',
|
|
41
|
-
code: 'STREAM_NOT_READABLE',
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
// eslint-disable-next-line prefer-const
|
|
46
|
-
let sizeStream;
|
|
47
|
-
this.once('finish', (error, data) => {
|
|
48
|
-
if (sizeStream)
|
|
49
|
-
this.req.unpipe(sizeStream);
|
|
50
|
-
if (error)
|
|
51
|
-
return reject(error);
|
|
52
|
-
resolve(data);
|
|
53
|
-
});
|
|
54
|
-
/**
|
|
55
|
-
* Check if a request has a request body.
|
|
56
|
-
* A request with a body __must__ either have `transfer-encoding`
|
|
57
|
-
* or `content-length` headers set.
|
|
58
|
-
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
59
|
-
*/
|
|
60
|
-
const contentLength = parseInt(this.req.headers['content-length'] || '0', 10);
|
|
61
|
-
if (this.req.headers['transfer-encoding'] === undefined && !(contentLength && !isNaN(contentLength))) {
|
|
62
|
-
return this.onEnd();
|
|
63
|
-
}
|
|
64
|
-
// check the length and limit options.
|
|
65
|
-
// note: we intentionally leave the stream paused,
|
|
66
|
-
// so users should handle the stream themselves.
|
|
67
|
-
if (this.limit != null && contentLength != null && contentLength > this.limit) {
|
|
68
|
-
return this.onEnd(new OpraHttpError({
|
|
69
|
-
message: 'Content Too Large',
|
|
70
|
-
code: 'HTTP.CONTENT_TOO_LARGE',
|
|
71
|
-
details: {
|
|
72
|
-
length: contentLength,
|
|
73
|
-
limit: this.limit,
|
|
74
|
-
},
|
|
75
|
-
}, 413));
|
|
76
|
-
}
|
|
77
|
-
// Pipe to a Writable stream to count received bytes
|
|
78
|
-
const _this = this;
|
|
79
|
-
sizeStream = new Writable({
|
|
80
|
-
write(chunk, encoding, callback) {
|
|
81
|
-
if (_this._completed)
|
|
82
|
-
return;
|
|
83
|
-
_this._receivedSize += chunk.length;
|
|
84
|
-
if (_this.limit != null && _this._receivedSize > _this.limit) {
|
|
85
|
-
callback(new OpraHttpError({
|
|
86
|
-
message: 'Content Too Large',
|
|
87
|
-
code: 'HTTP.CONTENT_TOO_LARGE',
|
|
88
|
-
details: {
|
|
89
|
-
limit: _this.limit,
|
|
90
|
-
received: _this._receivedSize,
|
|
91
|
-
},
|
|
92
|
-
}, 413));
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
this.req.pipe(sizeStream);
|
|
97
|
-
let stream = BodyReader.encoderPipeline(this.req);
|
|
98
|
-
const mediaType = parseContentType(this.req.headers['content-type'] || '');
|
|
99
|
-
let charset = (mediaType.parameters.charset || '').toLowerCase();
|
|
100
|
-
if (!charset && typeIs.is(mediaType.type, ['json', 'xml', 'txt']))
|
|
101
|
-
charset = 'utf-8';
|
|
102
|
-
if (charset) {
|
|
103
|
-
const newStream = iconv.decodeStream(charset);
|
|
104
|
-
stream.pipe(newStream);
|
|
105
|
-
stream = newStream;
|
|
106
|
-
}
|
|
107
|
-
this._stream = stream;
|
|
108
|
-
// attach listeners
|
|
109
|
-
stream.on('aborted', this.onAborted);
|
|
110
|
-
stream.on('close', this.cleanup);
|
|
111
|
-
stream.on('data', this.onData);
|
|
112
|
-
stream.on('end', this.onEnd);
|
|
113
|
-
stream.on('error', this.onEnd);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
_onEnd(error) {
|
|
117
|
-
if (this._completed)
|
|
118
|
-
return;
|
|
119
|
-
this._completed = true;
|
|
120
|
-
if (error) {
|
|
121
|
-
this._stream?.unpipe();
|
|
122
|
-
this._stream?.pause();
|
|
123
|
-
}
|
|
124
|
-
if (error)
|
|
125
|
-
this.emit('finish', error);
|
|
126
|
-
else if (Array.isArray(this._buffer))
|
|
127
|
-
this.emit('finish', error, Buffer.concat(this._buffer));
|
|
128
|
-
else
|
|
129
|
-
this.emit('finish', error, this._buffer);
|
|
130
|
-
this._cleanup();
|
|
131
|
-
}
|
|
132
|
-
_cleanup() {
|
|
133
|
-
if (this._stream) {
|
|
134
|
-
this._stream.removeListener('aborted', this.onAborted);
|
|
135
|
-
this._stream.removeListener('close', this.cleanup);
|
|
136
|
-
this._stream.removeListener('data', this.onData);
|
|
137
|
-
this._stream.removeListener('end', this.onEnd);
|
|
138
|
-
this._stream.removeListener('error', this.onEnd);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
_onAborted() {
|
|
142
|
-
if (this._completed)
|
|
143
|
-
return;
|
|
144
|
-
this.onEnd(new BadRequestError({
|
|
145
|
-
message: 'request aborted',
|
|
146
|
-
code: 'ECONNABORTED',
|
|
147
|
-
details: {
|
|
148
|
-
received: this._receivedSize,
|
|
149
|
-
},
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
_onData(chunk) {
|
|
153
|
-
if (this._completed)
|
|
154
|
-
return;
|
|
155
|
-
if (typeof chunk === 'string') {
|
|
156
|
-
this._buffer = this._buffer || '';
|
|
157
|
-
this._buffer += chunk;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
this._buffer = this._buffer || [];
|
|
161
|
-
this._buffer.push(chunk);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
static async read(req, options) {
|
|
165
|
-
const bodyReady = new BodyReader(req, options);
|
|
166
|
-
return bodyReady.read();
|
|
167
|
-
}
|
|
168
|
-
static encoderPipeline(req) {
|
|
169
|
-
const contentEncoding = req.headers['content-encoding'] || 'identity';
|
|
170
|
-
const contentEncodings = (Array.isArray(contentEncoding) ? contentEncoding : contentEncoding.split(/\s*,\s*/))
|
|
171
|
-
.map(s => s.toLowerCase())
|
|
172
|
-
.reverse();
|
|
173
|
-
return contentEncodings.reduce((prev, encoding) => {
|
|
174
|
-
switch (encoding) {
|
|
175
|
-
case 'gzip':
|
|
176
|
-
case 'x-gzip': {
|
|
177
|
-
const newStream = zlib.createGunzip();
|
|
178
|
-
prev.pipe(newStream);
|
|
179
|
-
return newStream;
|
|
180
|
-
}
|
|
181
|
-
case 'deflate':
|
|
182
|
-
case 'x-deflate': {
|
|
183
|
-
const newStream = zlib.createInflate();
|
|
184
|
-
prev.pipe(newStream);
|
|
185
|
-
return newStream;
|
|
186
|
-
}
|
|
187
|
-
case 'br': {
|
|
188
|
-
const newStream = zlib.createBrotliDecompress();
|
|
189
|
-
prev.pipe(newStream);
|
|
190
|
-
return newStream;
|
|
191
|
-
}
|
|
192
|
-
case 'base64': {
|
|
193
|
-
const newStream = new Base64Decode();
|
|
194
|
-
prev.pipe(newStream);
|
|
195
|
-
return newStream;
|
|
196
|
-
}
|
|
197
|
-
case 'identity':
|
|
198
|
-
// prev.length = 0;
|
|
199
|
-
return prev;
|
|
200
|
-
default:
|
|
201
|
-
throw new BadRequestError({
|
|
202
|
-
message: 'unsupported content encoding "' + encoding + '"',
|
|
203
|
-
code: '',
|
|
204
|
-
details: {
|
|
205
|
-
encoding,
|
|
206
|
-
},
|
|
207
|
-
}, 415);
|
|
208
|
-
}
|
|
209
|
-
}, req);
|
|
210
|
-
}
|
|
211
|
-
}
|
package/esm/http/utils/common.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
This file contains code blocks from open source NodeJs project
|
|
3
|
-
https://github.com/nodejs/
|
|
4
|
-
*/
|
|
5
|
-
const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
|
|
6
|
-
const nodeInternalPrefix = '__node_internal_';
|
|
7
|
-
/**
|
|
8
|
-
* Verifies that the given val is a valid HTTP token
|
|
9
|
-
* per the rules defined in RFC 7230
|
|
10
|
-
* See https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
11
|
-
*
|
|
12
|
-
* https://github.com/nodejs/node/blob/main/lib/_http_common.js
|
|
13
|
-
*/
|
|
14
|
-
export function checkIsHttpToken(val) {
|
|
15
|
-
return typeof val === 'string' && tokenRegExp.exec(val) !== null;
|
|
16
|
-
}
|
|
17
|
-
const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
|
|
18
|
-
/**
|
|
19
|
-
* True if val contains an invalid field-vchar
|
|
20
|
-
* field-value = *( field-content / obs-fold )
|
|
21
|
-
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
22
|
-
* field-vchar = VCHAR / obs-text
|
|
23
|
-
*
|
|
24
|
-
* https://github.com/nodejs/node/blob/main/lib/_http_common.js
|
|
25
|
-
*/
|
|
26
|
-
function checkInvalidHeaderChar(val) {
|
|
27
|
-
// noinspection SuspiciousTypeOfGuard
|
|
28
|
-
return typeof val === 'string' && headerCharRegex.exec(val) !== null;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* This function removes unnecessary frames from Node.js core errors.
|
|
32
|
-
*
|
|
33
|
-
* https://github.com/nodejs/node/blob/main/lib/internal/errors.js
|
|
34
|
-
*/
|
|
35
|
-
export function hideStackFrames(fn) {
|
|
36
|
-
// We rename the functions that will be hidden to cut off the stacktrace
|
|
37
|
-
// at the outermost one
|
|
38
|
-
const hidden = nodeInternalPrefix + fn.name;
|
|
39
|
-
// @ts-ignore
|
|
40
|
-
Object.defineProperty(fn, 'name', { __proto__: null, value: hidden });
|
|
41
|
-
return fn;
|
|
42
|
-
}
|
|
43
|
-
export const validateHeaderName = hideStackFrames((name, label) => {
|
|
44
|
-
// noinspection SuspiciousTypeOfGuard
|
|
45
|
-
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) {
|
|
46
|
-
throw new TypeError(`${label || 'Header name'} must be a valid HTTP token ["${name}"]`);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
export const validateHeaderValue = hideStackFrames((name, value) => {
|
|
50
|
-
if (value === undefined) {
|
|
51
|
-
throw new TypeError(`Invalid value "${value}" for header "${name}"`);
|
|
52
|
-
}
|
|
53
|
-
if (checkInvalidHeaderChar(value)) {
|
|
54
|
-
throw new TypeError(`Invalid character in header content ["${name}"]`);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
export function validateString(value, name) {
|
|
58
|
-
if (typeof value !== 'string') {
|
|
59
|
-
throw new TypeError(`Invalid ${name ? name + ' ' : ''}argument. Value must be a string`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { PassThrough } from 'stream';
|
|
2
|
-
export function concatReadable(...streams) {
|
|
3
|
-
const out = new PassThrough();
|
|
4
|
-
const pipeNext = () => {
|
|
5
|
-
const nextStream = streams.shift();
|
|
6
|
-
if (nextStream) {
|
|
7
|
-
nextStream.pipe(out, { end: false });
|
|
8
|
-
nextStream.once('end', () => pipeNext());
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
out.end();
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
pipeNext();
|
|
15
|
-
return out;
|
|
16
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { ARRAY_FIELD, COMMA_DELIMITED_FIELD, matchKnownFields, SEMICOLON_DELIMITED_FIELD, } from './match-known-fields.js';
|
|
2
|
-
export function convertToHeaders(src, dst, joinDuplicateHeaders) {
|
|
3
|
-
for (let n = 0; n < src.length; n += 2) {
|
|
4
|
-
addHeaderLine(src[n], src[n + 1], dst, joinDuplicateHeaders);
|
|
5
|
-
}
|
|
6
|
-
return dst;
|
|
7
|
-
}
|
|
8
|
-
export function convertToHeadersDistinct(src, dst) {
|
|
9
|
-
const count = src.length % 2;
|
|
10
|
-
for (let n = 0; n < count; n += 2) {
|
|
11
|
-
addHeaderLineDistinct(src[n], src[n + 1], dst);
|
|
12
|
-
}
|
|
13
|
-
return dst;
|
|
14
|
-
}
|
|
15
|
-
function addHeaderLine(field, value, dest, joinDuplicateHeaders) {
|
|
16
|
-
if (value == null)
|
|
17
|
-
return;
|
|
18
|
-
field = field.toLowerCase();
|
|
19
|
-
const [, flag] = matchKnownFields(field);
|
|
20
|
-
// comma(0) or semicolon(2) delimited field
|
|
21
|
-
if (flag === COMMA_DELIMITED_FIELD || flag === SEMICOLON_DELIMITED_FIELD) {
|
|
22
|
-
// Make a delimited list
|
|
23
|
-
if (typeof dest[field] === 'string') {
|
|
24
|
-
dest[field] += (flag === COMMA_DELIMITED_FIELD ? ', ' : '; ') + value;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
dest[field] = value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
else if (flag === ARRAY_FIELD) {
|
|
31
|
-
// Array header -- only Set-Cookie at the moment
|
|
32
|
-
if (dest['set-cookie'] !== undefined) {
|
|
33
|
-
dest['set-cookie'].push(value);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
dest['set-cookie'] = [value];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else if (joinDuplicateHeaders) {
|
|
40
|
-
if (dest[field] === undefined) {
|
|
41
|
-
dest[field] = value;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
dest[field] += ', ' + value;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
else if (dest[field] === undefined) {
|
|
48
|
-
// Drop duplicates
|
|
49
|
-
dest[field] = value;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function addHeaderLineDistinct(field, value, dest) {
|
|
53
|
-
field = field.toLowerCase();
|
|
54
|
-
if (!dest[field]) {
|
|
55
|
-
dest[field] = [value];
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
dest[field].push(value);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { ARRAY_FIELD, COMMA_DELIMITED_FIELD, matchKnownFields, SEMICOLON_DELIMITED_FIELD, } from './match-known-fields.js';
|
|
2
|
-
export function convertToRawHeaders(src) {
|
|
3
|
-
return Object.entries(src).reduce((a, [field, v]) => {
|
|
4
|
-
const [name, flag] = matchKnownFields(field);
|
|
5
|
-
if (flag === ARRAY_FIELD) {
|
|
6
|
-
if (Array.isArray(v))
|
|
7
|
-
v.forEach(x => a.push(name, String(x)));
|
|
8
|
-
else
|
|
9
|
-
a.push(name, String(v));
|
|
10
|
-
return a;
|
|
11
|
-
}
|
|
12
|
-
if (flag === COMMA_DELIMITED_FIELD || flag === SEMICOLON_DELIMITED_FIELD) {
|
|
13
|
-
v = Array.isArray(v) ? v.join(flag === COMMA_DELIMITED_FIELD ? ', ' : '; ') : String(v);
|
|
14
|
-
}
|
|
15
|
-
else
|
|
16
|
-
v = Array.isArray(v) ? String(v[0]) : String(v);
|
|
17
|
-
a.push(name, v);
|
|
18
|
-
return a;
|
|
19
|
-
}, []);
|
|
20
|
-
}
|