@api-client/core 0.3.1 → 0.3.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/build/browser.d.ts +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.js.map +1 -1
- package/build/src/models/Backend.d.ts +22 -0
- package/build/src/runtime/store/StoreSdk.d.ts +192 -12
- package/build/src/runtime/store/StoreSdk.js +592 -53
- package/build/src/runtime/store/StoreSdk.js.map +1 -1
- package/package.json +3 -1
- package/src/data/DataReader.ts +11 -0
- package/src/data/DataUtils.ts +108 -0
- package/src/data/JmesparthReader.ts +26 -0
- package/src/data/Json2Xml.ts +190 -0
- package/src/data/JsonReader.ts +41 -0
- package/src/data/PayloadPointer.ts +48 -0
- package/src/data/RequestDataExtractor.ts +133 -0
- package/src/data/UrlEncodedReader.ts +20 -0
- package/src/data/XmlReader.ts +103 -0
- package/src/events/BaseEvents.ts +259 -0
- package/src/events/CustomEvent.ts +27 -0
- package/src/events/EventTypes.ts +19 -0
- package/src/events/Events.ts +19 -0
- package/src/events/authorization/AuthorizationEventTypes.ts +22 -0
- package/src/events/authorization/AuthorizationEvents.ts +61 -0
- package/src/events/cookies/CookieEventTypes.ts +13 -0
- package/src/events/cookies/CookieEvents.ts +157 -0
- package/src/events/encryption/EncryptionEventTypes.ts +4 -0
- package/src/events/encryption/EncryptionEvents.ts +51 -0
- package/src/events/environment/EnvironmentEventTypes.ts +3 -0
- package/src/events/environment/EnvironmentEvents.ts +24 -0
- package/src/events/models/ClientCertificateEvents.ts +87 -0
- package/src/events/models/ModelEventTypes.ts +47 -0
- package/src/events/models/ModelEvents.ts +7 -0
- package/src/events/models/ProjectEvents.ts +331 -0
- package/src/events/process/ProcessEventTypes.ts +5 -0
- package/src/events/process/ProcessEvents.ts +76 -0
- package/src/events/readme.md +22 -0
- package/src/events/reporting/ReportingEventTypes.ts +3 -0
- package/src/events/reporting/ReportingEvents.ts +28 -0
- package/src/events/telemetry/TelemetryEventTypes.ts +10 -0
- package/src/events/telemetry/TelemetryEvents.ts +156 -0
- package/src/lib/cookies/Cookie.ts +312 -0
- package/src/lib/cookies/Cookies.ts +326 -0
- package/src/lib/cookies/Utils.ts +168 -0
- package/src/lib/headers/Headers.ts +219 -0
- package/src/lib/logging/DefaultLogger.ts +19 -0
- package/src/lib/logging/DummyLogger.ts +21 -0
- package/src/lib/logging/Logger.ts +16 -0
- package/src/lib/transformers/PayloadSerializer.ts +332 -0
- package/src/lib/transformers/Utils.ts +18 -0
- package/src/lib/uuid.ts +40 -0
- package/src/mocking/LegacyInterfaces.ts +52 -0
- package/src/mocking/LegacyMock.ts +37 -0
- package/src/mocking/legacy/Authorization.ts +39 -0
- package/src/mocking/legacy/Certificates.ts +145 -0
- package/src/mocking/legacy/Cookies.ts +51 -0
- package/src/mocking/legacy/HostRules.ts +43 -0
- package/src/mocking/legacy/Http.ts +236 -0
- package/src/mocking/legacy/HttpResponse.ts +106 -0
- package/src/mocking/legacy/RestApi.ts +68 -0
- package/src/mocking/legacy/Urls.ts +44 -0
- package/src/mocking/legacy/Variables.ts +53 -0
- package/src/models/ArcResponse.ts +166 -0
- package/src/models/Authorization.ts +481 -0
- package/src/models/AuthorizationData.ts +60 -0
- package/src/models/Backend.ts +107 -0
- package/src/models/ClientCertificate.ts +68 -0
- package/src/models/Environment.ts +279 -0
- package/src/models/ErrorResponse.ts +101 -0
- package/src/models/HistoryIndex.ts +76 -0
- package/src/models/HistoryRequest.ts +28 -0
- package/src/models/HostRule.ts +163 -0
- package/src/models/HttpCookie.ts +285 -0
- package/src/models/HttpProject.ts +1294 -0
- package/src/models/HttpProjectListItem.ts +23 -0
- package/src/models/HttpRequest.ts +124 -0
- package/src/models/HttpResponse.ts +143 -0
- package/src/models/License.ts +113 -0
- package/src/models/ProjectDefinitionProperty.ts +40 -0
- package/src/models/ProjectFolder.ts +439 -0
- package/src/models/ProjectItem.ts +135 -0
- package/src/models/ProjectParent.ts +113 -0
- package/src/models/ProjectRequest.ts +277 -0
- package/src/models/ProjectSchema.ts +202 -0
- package/src/models/Property.ts +423 -0
- package/src/models/Provider.ts +98 -0
- package/src/models/README.md +20 -0
- package/src/models/Request.ts +452 -0
- package/src/models/RequestActions.ts +163 -0
- package/src/models/RequestAuthorization.ts +115 -0
- package/src/models/RequestConfig.ts +317 -0
- package/src/models/RequestLog.ts +159 -0
- package/src/models/RequestTime.ts +108 -0
- package/src/models/RequestUiMeta.ts +258 -0
- package/src/models/RequestsSize.ts +65 -0
- package/src/models/ResponseAuthorization.ts +104 -0
- package/src/models/ResponseRedirect.ts +158 -0
- package/src/models/RevisionInfo.ts +37 -0
- package/src/models/SentRequest.ts +125 -0
- package/src/models/SerializablePayload.ts +68 -0
- package/src/models/Server.ts +153 -0
- package/src/models/Thing.ts +110 -0
- package/src/models/Url.ts +90 -0
- package/src/models/User.ts +120 -0
- package/src/models/WebApi.ts +234 -0
- package/src/models/WebApiIndex.ts +122 -0
- package/src/models/Workspace.ts +182 -0
- package/src/models/actions/Action.ts +213 -0
- package/src/models/actions/ActionView.ts +40 -0
- package/src/models/actions/Condition.ts +207 -0
- package/src/models/actions/ConditionView.ts +42 -0
- package/src/models/actions/Enums.ts +29 -0
- package/src/models/actions/RunnableAction.ts +144 -0
- package/src/models/actions/runnable/DeleteCookieAction.ts +113 -0
- package/src/models/actions/runnable/Runnable.ts +9 -0
- package/src/models/actions/runnable/SetCookieAction.ts +216 -0
- package/src/models/actions/runnable/SetVariableAction.ts +81 -0
- package/src/models/legacy/DataExport.ts +172 -0
- package/src/models/legacy/Normalizer.ts +110 -0
- package/src/models/legacy/actions/Actions.ts +269 -0
- package/src/models/legacy/authorization/Authorization.ts +572 -0
- package/src/models/legacy/models/ApiTypes.ts +202 -0
- package/src/models/legacy/models/ArcLegacyProject.ts +39 -0
- package/src/models/legacy/models/AuthData.ts +17 -0
- package/src/models/legacy/models/ClientCertificate.ts +95 -0
- package/src/models/legacy/models/Cookies.ts +52 -0
- package/src/models/legacy/models/HostRule.ts +35 -0
- package/src/models/legacy/models/RestApi.ts +49 -0
- package/src/models/legacy/models/UrlHistory.ts +37 -0
- package/src/models/legacy/models/Variable.ts +43 -0
- package/src/models/legacy/models/base.d.ts +95 -0
- package/src/models/legacy/request/ArcRequest.ts +405 -0
- package/src/models/legacy/request/ArcResponse.ts +177 -0
- package/src/models/legacy/request/HistoryData.ts +47 -0
- package/src/models/legacy/request/Legacy.ts +45 -0
- package/src/models/legacy/request/RequestBody.ts +87 -0
- package/src/models/transformers/ArcDexieTransformer.ts +323 -0
- package/src/models/transformers/ArcLegacyNormalizer.ts +85 -0
- package/src/models/transformers/ArcLegacyTransformer.ts +200 -0
- package/src/models/transformers/ArcPouchTransformer.ts +184 -0
- package/src/models/transformers/BaseTransformer.ts +116 -0
- package/src/models/transformers/ImportUtils.ts +141 -0
- package/src/models/transformers/LegacyDataExportToApiProject.ts +76 -0
- package/src/models/transformers/LegacyExportProcessor.ts +252 -0
- package/src/models/transformers/PostmanBackupTransformer.ts +306 -0
- package/src/models/transformers/PostmanDataTransformer.ts +50 -0
- package/src/models/transformers/PostmanTransformer.ts +106 -0
- package/src/models/transformers/PostmanV21Transformer.ts +311 -0
- package/src/models/transformers/PostmanV2Transformer.ts +308 -0
- package/src/models/transformers/har.ts +865 -0
- package/src/runtime/actions/ActionRunner.ts +83 -0
- package/src/runtime/actions/ConditionRunner.ts +194 -0
- package/src/runtime/actions/RunnableCondition.ts +57 -0
- package/src/runtime/actions/runnable/ActionRunnable.ts +19 -0
- package/src/runtime/actions/runnable/DeleteCookieRunnable.ts +39 -0
- package/src/runtime/actions/runnable/SetCookieRunnable.ts +92 -0
- package/src/runtime/actions/runnable/SetVariableRunnable.ts +53 -0
- package/src/runtime/http-engine/ArcEngine.ts +1064 -0
- package/src/runtime/http-engine/Errors.ts +13 -0
- package/src/runtime/http-engine/FormData.ts +85 -0
- package/src/runtime/http-engine/HttpEngine.ts +874 -0
- package/src/runtime/http-engine/HttpErrorCodes.ts +270 -0
- package/src/runtime/http-engine/NodeEngine.ts +787 -0
- package/src/runtime/http-engine/NodeEngineDirect.ts +476 -0
- package/src/runtime/http-engine/PayloadSupport.ts +84 -0
- package/src/runtime/http-engine/RequestUtils.ts +164 -0
- package/src/runtime/http-engine/ntlm/Des.ts +345 -0
- package/src/runtime/http-engine/ntlm/MD4.ts +135 -0
- package/src/runtime/http-engine/ntlm/NtlmAuth.ts +186 -0
- package/src/runtime/http-engine/ntlm/NtlmMessage.ts +57 -0
- package/src/runtime/modules/BasicAuthCache.ts +133 -0
- package/src/runtime/modules/ExecutionResponse.ts +4 -0
- package/src/runtime/modules/ModulesRegistry.ts +136 -0
- package/src/runtime/modules/RequestAuthorization.ts +110 -0
- package/src/runtime/modules/RequestCookies.ts +145 -0
- package/src/runtime/node/ProjectRunner.ts +275 -0
- package/src/runtime/node/RequestFactory.ts +422 -0
- package/src/runtime/node/VariablesStore.ts +25 -0
- package/src/runtime/store/StoreSdk.ts +838 -0
- package/src/runtime/variables/Cache.ts +53 -0
- package/src/runtime/variables/EvalFunctions.ts +132 -0
- package/src/runtime/variables/ProjectVariables.ts +6 -0
- package/src/runtime/variables/VariablesProcessor.ts +543 -0
- package/src/runtime/variables/VariablesTokenizer.ts +55 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
3
|
+
import { URL, UrlWithStringQuery } from 'url';
|
|
4
|
+
import http from 'http';
|
|
5
|
+
import https from 'https';
|
|
6
|
+
import net from 'net';
|
|
7
|
+
import { HttpEngine, HttpEngineOptions, ResponseErrorInit, HeadersReceivedDetail } from './HttpEngine.js';
|
|
8
|
+
import { IRequestLog } from 'src/models/RequestLog.js';
|
|
9
|
+
import { IHttpRequest } from '../../models/HttpRequest.js';
|
|
10
|
+
import { ArcResponse } from '../../models/ArcResponse.js';
|
|
11
|
+
import { Headers } from '../../lib/headers/Headers.js';
|
|
12
|
+
import { PayloadSupport } from './PayloadSupport.js';
|
|
13
|
+
import { addContentLength, getPort } from './RequestUtils.js';
|
|
14
|
+
|
|
15
|
+
interface IHttpOutputData {
|
|
16
|
+
data: string | Buffer;
|
|
17
|
+
encoding?: string;
|
|
18
|
+
callback?: Function;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A class that makes HTTP requests using Node's HTTP libraries without a proxy.
|
|
23
|
+
*/
|
|
24
|
+
export class NodeEngineDirect extends HttpEngine {
|
|
25
|
+
responseReported = false;
|
|
26
|
+
|
|
27
|
+
_sentHttpMessage?: string | IHttpOutputData[];
|
|
28
|
+
|
|
29
|
+
client?: http.ClientRequest;
|
|
30
|
+
receivingResponse = false;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The agent used to manage the connection.
|
|
34
|
+
* Note, the agent may change, especially when redirecting between protocols.
|
|
35
|
+
*/
|
|
36
|
+
agent: http.Agent | https.Agent;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Prepared to be send payload part of the HTTP message.
|
|
40
|
+
*/
|
|
41
|
+
httpMessage?: Buffer;
|
|
42
|
+
|
|
43
|
+
constructor(request: IHttpRequest, opts: HttpEngineOptions = {}) {
|
|
44
|
+
super(request, opts);
|
|
45
|
+
|
|
46
|
+
const port = getPort(this.uri.port, this.uri.protocol);
|
|
47
|
+
if (port === 443 || this.uri.protocol === 'https:') {
|
|
48
|
+
this.agent = this.httpsAgent(this.uri);
|
|
49
|
+
} else {
|
|
50
|
+
this.agent = this.httpAgent(this.uri);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._connectHandler = this._connectHandler.bind(this);
|
|
54
|
+
this._secureConnectHandler = this._secureConnectHandler.bind(this);
|
|
55
|
+
this._responseHandler = this._responseHandler.bind(this);
|
|
56
|
+
this._timeoutHandler = this._timeoutHandler.bind(this);
|
|
57
|
+
this._errorHandler = this._errorHandler.bind(this);
|
|
58
|
+
this._lookupHandler = this._lookupHandler.bind(this);
|
|
59
|
+
this._closeHandler = this._closeHandler.bind(this);
|
|
60
|
+
this._socketHandler = this._socketHandler.bind(this);
|
|
61
|
+
this._sendEndHandler = this._sendEndHandler.bind(this);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sends the request
|
|
66
|
+
*/
|
|
67
|
+
async send(): Promise<IRequestLog> {
|
|
68
|
+
const promise = this.wrapExecution();
|
|
69
|
+
this.sendRequest();
|
|
70
|
+
return promise;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async sendRequest(): Promise<void> {
|
|
74
|
+
try {
|
|
75
|
+
const headers = new Headers(this.request.headers);
|
|
76
|
+
this.prepareHeaders(headers);
|
|
77
|
+
const message = await this._prepareMessage(headers);
|
|
78
|
+
if (message) {
|
|
79
|
+
this.httpMessage = message;
|
|
80
|
+
}
|
|
81
|
+
this.sentRequest.headers = headers.toString();
|
|
82
|
+
const request = this._connect(message);
|
|
83
|
+
this.client = request;
|
|
84
|
+
const { timeout } = this;
|
|
85
|
+
if (timeout > 0) {
|
|
86
|
+
request.setTimeout(timeout);
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.warn(e);
|
|
90
|
+
this.finalizeRequest(e as Error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Prepares the request body (the payload) and the headers.
|
|
97
|
+
*
|
|
98
|
+
* @return Resolved promise to a `Buffer`. Undefined when no message.
|
|
99
|
+
*/
|
|
100
|
+
async _prepareMessage(headers: Headers): Promise<Buffer|undefined> {
|
|
101
|
+
const { method='GET' } = this.request;
|
|
102
|
+
let { payload } = this.request;
|
|
103
|
+
if (['get', 'head'].includes(method.toLowerCase())) {
|
|
104
|
+
payload = undefined;
|
|
105
|
+
}
|
|
106
|
+
let buffer: Buffer | undefined;
|
|
107
|
+
if (payload) {
|
|
108
|
+
buffer = await PayloadSupport.payloadToBuffer(payload, headers);
|
|
109
|
+
if (buffer) {
|
|
110
|
+
addContentLength(this.request.method || 'GET', buffer, headers);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return buffer;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Connects to the remote machine.
|
|
118
|
+
*/
|
|
119
|
+
_connect(message?: Buffer): http.ClientRequest {
|
|
120
|
+
const uri = new URL(this.request.url);
|
|
121
|
+
const port = getPort(uri.port, uri.protocol);
|
|
122
|
+
if (port === 443 || uri.protocol === 'https:') {
|
|
123
|
+
return this._connectHttps(uri, message);
|
|
124
|
+
}
|
|
125
|
+
return this._connectHttp(uri, message);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Creates a connection using regular transport.
|
|
130
|
+
*/
|
|
131
|
+
_connectHttp(uri: URL, message?: Buffer): http.ClientRequest {
|
|
132
|
+
if (!uri.port) {
|
|
133
|
+
uri.port = '80';
|
|
134
|
+
}
|
|
135
|
+
const options: http.RequestOptions = {
|
|
136
|
+
agent: this.agent,
|
|
137
|
+
protocol: uri.protocol,
|
|
138
|
+
host: uri.hostname,
|
|
139
|
+
method: this.request.method.toUpperCase(),
|
|
140
|
+
path: `${uri.pathname}${uri.search}`,
|
|
141
|
+
headers: {},
|
|
142
|
+
};
|
|
143
|
+
if (uri.port) {
|
|
144
|
+
options.port = uri.port;
|
|
145
|
+
}
|
|
146
|
+
this.appendHeaders(options);
|
|
147
|
+
const startTime = Date.now();
|
|
148
|
+
this.stats.startTime = startTime;
|
|
149
|
+
this.sentRequest.startTime = startTime;
|
|
150
|
+
|
|
151
|
+
const request = http.request(options);
|
|
152
|
+
this._setListeners(request);
|
|
153
|
+
if (message) {
|
|
154
|
+
request.write(message);
|
|
155
|
+
}
|
|
156
|
+
this.stats.messageStart = Date.now();
|
|
157
|
+
request.end();
|
|
158
|
+
// This is a hack to read sent data.
|
|
159
|
+
// In the https://github.com/nodejs/node/blob/0a62026f32d513a8a5d9ed857481df5f5fa18e8b/lib/_http_outgoing.js#L960
|
|
160
|
+
// library it hold the data until it is flushed.
|
|
161
|
+
// @ts-ignore
|
|
162
|
+
const pending = request.outputData as IHttpOutputData[];
|
|
163
|
+
if (Array.isArray(pending)) {
|
|
164
|
+
this._sentHttpMessage = pending;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
this.emit('loadstart');
|
|
168
|
+
} catch (_) {
|
|
169
|
+
//
|
|
170
|
+
}
|
|
171
|
+
return request;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates a connection using SSL transport.
|
|
176
|
+
*/
|
|
177
|
+
_connectHttps(uri: URL, message?: Buffer): http.ClientRequest {
|
|
178
|
+
if (!uri.port) {
|
|
179
|
+
uri.port = '443';
|
|
180
|
+
}
|
|
181
|
+
// const options = this._createGenericOptions(uri);
|
|
182
|
+
// this._addSslOptions(options);
|
|
183
|
+
|
|
184
|
+
const options: https.RequestOptions = {
|
|
185
|
+
agent: this.agent,
|
|
186
|
+
protocol: uri.protocol,
|
|
187
|
+
host: uri.hostname,
|
|
188
|
+
method: this.request.method.toUpperCase(),
|
|
189
|
+
path: `${uri.pathname}${uri.search}`,
|
|
190
|
+
headers: {},
|
|
191
|
+
};
|
|
192
|
+
if (uri.port) {
|
|
193
|
+
options.port = uri.port;
|
|
194
|
+
}
|
|
195
|
+
this.appendHeaders(options);
|
|
196
|
+
|
|
197
|
+
const startTime = Date.now();
|
|
198
|
+
this.stats.startTime = startTime;
|
|
199
|
+
this.sentRequest.startTime = startTime;
|
|
200
|
+
|
|
201
|
+
const request = https.request(options);
|
|
202
|
+
this._setListeners(request);
|
|
203
|
+
if (message) {
|
|
204
|
+
request.write(message);
|
|
205
|
+
}
|
|
206
|
+
this.stats.messageStart = Date.now();
|
|
207
|
+
this.stats.sentTime = this.stats.messageStart + 1;
|
|
208
|
+
request.end();
|
|
209
|
+
// This is a hack to read sent data.
|
|
210
|
+
// In the https://github.com/nodejs/node/blob/0a62026f32d513a8a5d9ed857481df5f5fa18e8b/lib/_http_outgoing.js#L960
|
|
211
|
+
// library it hold the data until it is flushed.
|
|
212
|
+
// @ts-ignore
|
|
213
|
+
const pending = request.outputData as IHttpOutputData[];
|
|
214
|
+
if (Array.isArray(pending)) {
|
|
215
|
+
this._sentHttpMessage = pending;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
this.emit('loadstart');
|
|
219
|
+
} catch (_) {
|
|
220
|
+
//
|
|
221
|
+
}
|
|
222
|
+
return request;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Sets listeners on a socket
|
|
227
|
+
* @param request The request object
|
|
228
|
+
*/
|
|
229
|
+
_setListeners(request: http.ClientRequest): void {
|
|
230
|
+
request.once('socket', this._socketHandler);
|
|
231
|
+
request.once('error', this._errorHandler);
|
|
232
|
+
request.once('response', this._responseHandler);
|
|
233
|
+
request.once('close', this._closeHandler);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Handler for connection error.
|
|
238
|
+
*/
|
|
239
|
+
_errorHandler(e: ResponseErrorInit): void {
|
|
240
|
+
if (this.aborted) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
this._errorRequest({
|
|
244
|
+
code: e.code,
|
|
245
|
+
message: e.message,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Handler for DNS lookup.
|
|
251
|
+
*/
|
|
252
|
+
_lookupHandler(): void {
|
|
253
|
+
this.stats.lookupTime = Date.now();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Handler for connected event.
|
|
258
|
+
*/
|
|
259
|
+
_secureConnectHandler(): void {
|
|
260
|
+
this.stats.secureConnectedTime = Date.now();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Handler for connecting event.
|
|
265
|
+
*/
|
|
266
|
+
_connectHandler(): void {
|
|
267
|
+
this.stats.connectedTime = Date.now();
|
|
268
|
+
this.stats.secureStartTime = Date.now();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Handler for sending finished event
|
|
273
|
+
*/
|
|
274
|
+
_sendEndHandler(): void {
|
|
275
|
+
if (!this.stats.sentTime) {
|
|
276
|
+
this.stats.sentTime = Date.now();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Handler for timeout event
|
|
282
|
+
*/
|
|
283
|
+
_timeoutHandler(): void {
|
|
284
|
+
this._errorRequest({
|
|
285
|
+
code: 7,
|
|
286
|
+
});
|
|
287
|
+
this.abort();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* A handler for response data event
|
|
292
|
+
*/
|
|
293
|
+
_responseHandler(res: http.IncomingMessage): void {
|
|
294
|
+
this.receivingResponse = true;
|
|
295
|
+
|
|
296
|
+
this.emit('firstbyte');
|
|
297
|
+
this.stats.firstReceiveTime = Date.now();
|
|
298
|
+
this.stats.responseTime = Date.now();
|
|
299
|
+
if (this._sentHttpMessage) {
|
|
300
|
+
this.sentRequest.httpMessage = this._readSentMessage(this._sentHttpMessage);
|
|
301
|
+
} else {
|
|
302
|
+
this.sentRequest.httpMessage = '';
|
|
303
|
+
}
|
|
304
|
+
const status = res.statusCode;
|
|
305
|
+
if (!status) {
|
|
306
|
+
this._errorRequest({
|
|
307
|
+
message: 'The response has no status.',
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const headers = this.computeResponseHeaders(res);
|
|
312
|
+
const rawHeaders = headers.toString();
|
|
313
|
+
const response = ArcResponse.fromValues(status, res.statusMessage, rawHeaders);
|
|
314
|
+
this.currentResponse = response;
|
|
315
|
+
this.currentHeaders = headers;
|
|
316
|
+
const detail: HeadersReceivedDetail = {
|
|
317
|
+
returnValue: true,
|
|
318
|
+
value: rawHeaders,
|
|
319
|
+
};
|
|
320
|
+
this.emit('headersreceived', detail);
|
|
321
|
+
if (!detail.returnValue) {
|
|
322
|
+
this.abort();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
res.on('data', (chunk) => {
|
|
326
|
+
if (!this._rawBody) {
|
|
327
|
+
this._rawBody = chunk;
|
|
328
|
+
} else {
|
|
329
|
+
const endTime = Date.now();
|
|
330
|
+
this.stats.lastReceivedTime = endTime;
|
|
331
|
+
this._rawBody = Buffer.concat([this._rawBody, chunk]);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
res.once('end', () => {
|
|
335
|
+
const endTime = Date.now();
|
|
336
|
+
this.sentRequest.endTime = endTime;
|
|
337
|
+
this.stats.receivingTime = endTime;
|
|
338
|
+
this._reportResponse();
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Handler for connection close event
|
|
344
|
+
*/
|
|
345
|
+
_closeHandler(): void {
|
|
346
|
+
if (this.responseReported || this.receivingResponse || this.aborted || this.redirecting) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (!this.currentResponse) {
|
|
350
|
+
const e = new Error('Connection closed unexpectedly.');
|
|
351
|
+
this._errorRequest(e);
|
|
352
|
+
} else {
|
|
353
|
+
// There is an issue with the response. Size mismatch? Anyway,
|
|
354
|
+
// it tries to create a response from current data.
|
|
355
|
+
this.emit('loadend');
|
|
356
|
+
this._publishResponse();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
_socketHandler(socket: net.Socket): void {
|
|
361
|
+
this.socket = socket;
|
|
362
|
+
socket.once('lookup', this._lookupHandler);
|
|
363
|
+
socket.once('connect', this._connectHandler);
|
|
364
|
+
socket.once('timeout', this._timeoutHandler);
|
|
365
|
+
socket.once('end', this._sendEndHandler);
|
|
366
|
+
socket.once('secureConnect', this._secureConnectHandler);
|
|
367
|
+
this.stats.connectionTime = Date.now();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Creates and publishes a response.
|
|
372
|
+
*/
|
|
373
|
+
_reportResponse(): void {
|
|
374
|
+
const { aborted, currentResponse } = this;
|
|
375
|
+
if (aborted || !currentResponse) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const { status } = currentResponse;
|
|
379
|
+
if (status >= 300 && status < 400) {
|
|
380
|
+
if (this.followRedirects !== false && this._reportRedirect(status)) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (this.responseReported) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.responseReported = true;
|
|
388
|
+
this.emit('loadend');
|
|
389
|
+
this._publishResponse();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Transforms a message from the client to a string.
|
|
394
|
+
* It uses `opts.sentMessageLimit` to limit number of data returned
|
|
395
|
+
* by the client.
|
|
396
|
+
*/
|
|
397
|
+
_readSentMessage(messages: string|IHttpOutputData[]): string {
|
|
398
|
+
let result = '';
|
|
399
|
+
if (typeof messages === 'string') {
|
|
400
|
+
result = messages;
|
|
401
|
+
} else {
|
|
402
|
+
for (let i = 0, len = messages.length; i < len; i++) {
|
|
403
|
+
const chunk = messages[i].data;
|
|
404
|
+
if (!chunk) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (typeof chunk === 'string') {
|
|
408
|
+
result += chunk;
|
|
409
|
+
} else if (chunk instanceof Uint8Array) {
|
|
410
|
+
result += chunk.toString();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const limit = this.opts.sentMessageLimit;
|
|
415
|
+
if (limit && limit > 0 && result.length >= limit) {
|
|
416
|
+
result = result.substr(0, limit);
|
|
417
|
+
result += ' ...';
|
|
418
|
+
}
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
httpAgent(uri: URL | UrlWithStringQuery): http.Agent {
|
|
423
|
+
const init: http.AgentOptions = {
|
|
424
|
+
// keepAlive: true,
|
|
425
|
+
host: uri.hostname || undefined,
|
|
426
|
+
};
|
|
427
|
+
if (uri.port) {
|
|
428
|
+
init.port = Number(uri.port);
|
|
429
|
+
}
|
|
430
|
+
const agent = new http.Agent(init);
|
|
431
|
+
return agent;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
httpsAgent(uri: URL | UrlWithStringQuery): https.Agent {
|
|
435
|
+
const init: https.AgentOptions = {
|
|
436
|
+
// keepAlive: true,
|
|
437
|
+
host: uri.hostname || undefined,
|
|
438
|
+
path: `${uri.pathname}${uri.search}`,
|
|
439
|
+
};
|
|
440
|
+
if (uri.port) {
|
|
441
|
+
init.port = Number(uri.port);
|
|
442
|
+
}
|
|
443
|
+
if (this.opts.validateCertificates) {
|
|
444
|
+
init.checkServerIdentity = this._checkServerIdentity.bind(this);
|
|
445
|
+
} else {
|
|
446
|
+
init.rejectUnauthorized = false;
|
|
447
|
+
// init.requestOCSP = false;
|
|
448
|
+
}
|
|
449
|
+
const certs = this.opts.certificates;
|
|
450
|
+
if (Array.isArray(certs)) {
|
|
451
|
+
certs.forEach(cert => this._addClientCertificate(cert, init));
|
|
452
|
+
}
|
|
453
|
+
const agent = new https.Agent(init);
|
|
454
|
+
return agent;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Appends the list of headers on the request options.
|
|
459
|
+
*
|
|
460
|
+
* @param options The request options to alter.
|
|
461
|
+
*/
|
|
462
|
+
appendHeaders(options: http.RequestOptions | https.RequestOptions): void {
|
|
463
|
+
// Note, the final headers are set on the `sentRequest` object.
|
|
464
|
+
// The `request` object is not changed.
|
|
465
|
+
const headers = new Headers(this.sentRequest.headers);
|
|
466
|
+
if (!headers.has('host') && this.hostHeader) {
|
|
467
|
+
headers.set('host', this.hostHeader);
|
|
468
|
+
}
|
|
469
|
+
if (!options.headers) {
|
|
470
|
+
options.headers = {};
|
|
471
|
+
}
|
|
472
|
+
for (const [key, value] of headers.entries()) {
|
|
473
|
+
options.headers[key] = value;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import formDataConverter from './FormData.js';
|
|
2
|
+
import { Headers } from '../../lib/headers/Headers.js';
|
|
3
|
+
import { Payload, PayloadSerializer, IMultipartBody } from '../../lib/transformers/PayloadSerializer.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A class containing static helper methods to deal with Payload
|
|
7
|
+
* transformations
|
|
8
|
+
*/
|
|
9
|
+
export class PayloadSupport {
|
|
10
|
+
/**
|
|
11
|
+
* NormalizeLineEndingsToCRLF
|
|
12
|
+
* https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/
|
|
13
|
+
* platform/text/LineEnding.cpp&rcl=1458041387&l=101
|
|
14
|
+
*
|
|
15
|
+
* @param string A string to be normalized.
|
|
16
|
+
* @return normalized string
|
|
17
|
+
*/
|
|
18
|
+
static normalizeString(string: string): string {
|
|
19
|
+
let result = '';
|
|
20
|
+
for (let i = 0; i < string.length; i++) {
|
|
21
|
+
const c = string[i];
|
|
22
|
+
const p = string[i + 1];
|
|
23
|
+
if (c === '\r') {
|
|
24
|
+
// Safe to look ahead because of trailing '\0'.
|
|
25
|
+
if (p && p !== '\n') {
|
|
26
|
+
// Turn CR into CRLF.
|
|
27
|
+
result += '\r';
|
|
28
|
+
result += '\n';
|
|
29
|
+
}
|
|
30
|
+
} else if (c === '\n') {
|
|
31
|
+
result += '\r';
|
|
32
|
+
result += '\n';
|
|
33
|
+
} else {
|
|
34
|
+
// Leave other characters alone.
|
|
35
|
+
result += c;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Transforms the request payload into a `Buffer`
|
|
43
|
+
*
|
|
44
|
+
* @param payload A payload message
|
|
45
|
+
* @param headers A headers object where to append headers when needed
|
|
46
|
+
* @returns A promise resolved to a `Buffer`.
|
|
47
|
+
*/
|
|
48
|
+
static async payloadToBuffer(payload: Payload, headers: Headers): Promise<Buffer|undefined> {
|
|
49
|
+
if (!payload) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (typeof payload === 'string') {
|
|
53
|
+
payload = PayloadSupport.normalizeString(payload);
|
|
54
|
+
return Buffer.from(payload, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (payload.type === 'string') {
|
|
58
|
+
return PayloadSupport.payloadToBuffer(payload.data as string, headers);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (['blob', 'file'].includes(payload.type)) {
|
|
62
|
+
if (!headers.has('content-type') && payload.mime) {
|
|
63
|
+
headers.set('content-type', payload.mime);
|
|
64
|
+
}
|
|
65
|
+
return PayloadSerializer.deserializeBlobBuffer(payload.data as string);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (payload.type === 'buffer') {
|
|
69
|
+
return PayloadSerializer.deserializeBuffer(payload.data as number[]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (payload.type === 'arraybuffer') {
|
|
73
|
+
return PayloadSerializer.deserializeArrayBufferBuffer(payload.data as number[]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (payload.type === 'formdata') {
|
|
77
|
+
const result = await formDataConverter(payload.data as IMultipartBody[]);
|
|
78
|
+
headers.set('Content-Type', result.type);
|
|
79
|
+
return result.buffer;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw new Error('Unsupported payload message');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Headers } from '../../lib/headers/Headers.js';
|
|
2
|
+
import { ResponseRedirect } from '../../models/ResponseRedirect.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reads a port number for a connection.
|
|
6
|
+
*
|
|
7
|
+
* @param port Existing information about the port.
|
|
8
|
+
* @param protocol Request protocol. Only used if `port` is not set.
|
|
9
|
+
* @return A port number. Default to 80.
|
|
10
|
+
*/
|
|
11
|
+
export function getPort(port: number|string, protocol?: string): number {
|
|
12
|
+
if (port) {
|
|
13
|
+
const typedPort = Number(port);
|
|
14
|
+
if (!Number.isNaN(typedPort)) {
|
|
15
|
+
return typedPort;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (protocol === 'https:') {
|
|
19
|
+
return 443;
|
|
20
|
+
}
|
|
21
|
+
return 80;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a value for host header.
|
|
26
|
+
*
|
|
27
|
+
* @param value An url to get the information from.
|
|
28
|
+
* @return Value of the host header
|
|
29
|
+
*/
|
|
30
|
+
export function getHostHeader(value: string): string | undefined {
|
|
31
|
+
let uri;
|
|
32
|
+
try {
|
|
33
|
+
uri = new URL(value);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let hostValue = uri.hostname;
|
|
38
|
+
const defaultPorts = [80, 443];
|
|
39
|
+
const port = getPort(uri.port, uri.protocol);
|
|
40
|
+
if (!defaultPorts.includes(port)) {
|
|
41
|
+
hostValue += `:${port}`;
|
|
42
|
+
}
|
|
43
|
+
return hostValue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Adds the `content-length` header to current request headers list if
|
|
48
|
+
* it's required.
|
|
49
|
+
* This function will do nothing if the request do not carry a payload or
|
|
50
|
+
* when the content length header is already set.
|
|
51
|
+
*
|
|
52
|
+
* @param method HTTP request method
|
|
53
|
+
* @param buffer Generated message buffer.
|
|
54
|
+
* @param headers A headers object where to append headers when needed
|
|
55
|
+
*/
|
|
56
|
+
export function addContentLength(method: string, buffer: Buffer, headers: Headers): void {
|
|
57
|
+
if (method.toLowerCase() === 'get') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const size = buffer ? buffer.length : 0;
|
|
61
|
+
headers.set('content-length', String(size));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export declare interface RedirectOptions {
|
|
65
|
+
/**
|
|
66
|
+
* true if redirect is required
|
|
67
|
+
*/
|
|
68
|
+
redirect?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* If true the redirected request has to be a GET request.
|
|
71
|
+
*/
|
|
72
|
+
forceGet?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* location of the resource (redirect uri)
|
|
75
|
+
*/
|
|
76
|
+
location?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Checks if redirect is required.
|
|
81
|
+
* @param status Response status code
|
|
82
|
+
* @param method Request HTTP method
|
|
83
|
+
* @param location Location header value, if any
|
|
84
|
+
* @returns The redirect options
|
|
85
|
+
*/
|
|
86
|
+
export function redirectOptions(status: number, method: string, location?: string): RedirectOptions {
|
|
87
|
+
const result: RedirectOptions = {
|
|
88
|
+
redirect: false,
|
|
89
|
+
forceGet: false,
|
|
90
|
+
};
|
|
91
|
+
switch (status) {
|
|
92
|
+
case 300:
|
|
93
|
+
case 304:
|
|
94
|
+
case 305:
|
|
95
|
+
// do nothing;
|
|
96
|
+
break;
|
|
97
|
+
case 301:
|
|
98
|
+
case 302:
|
|
99
|
+
case 307:
|
|
100
|
+
if (['GET', 'HEAD'].indexOf(method) !== -1) {
|
|
101
|
+
result.redirect = true;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case 303:
|
|
105
|
+
result.redirect = true;
|
|
106
|
+
result.forceGet = true;
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
}
|
|
110
|
+
if (!result.redirect) {
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
if (location) {
|
|
114
|
+
result.location = location;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if request is an infinite loop.
|
|
122
|
+
* @param location Redirect location
|
|
123
|
+
* @param redirects List of response objects
|
|
124
|
+
* @return True if redirect is into the same place as already visited.
|
|
125
|
+
*/
|
|
126
|
+
export function isRedirectLoop(location: string, redirects: ResponseRedirect[]): boolean {
|
|
127
|
+
if (redirects) {
|
|
128
|
+
let index = -1;
|
|
129
|
+
let i = 0;
|
|
130
|
+
for (const item of redirects) {
|
|
131
|
+
if (item.url === location) {
|
|
132
|
+
index = i;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
if (index !== -1) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Processes redirection location
|
|
146
|
+
*
|
|
147
|
+
* @param location Redirect location
|
|
148
|
+
* @param requestUrl Request url
|
|
149
|
+
* @return Redirect location
|
|
150
|
+
*/
|
|
151
|
+
export function getRedirectLocation(location: string, requestUrl: string): string | undefined {
|
|
152
|
+
// https://github.com/jarrodek/socket-fetch/issues/5
|
|
153
|
+
try {
|
|
154
|
+
// eslint-disable-next-line no-new
|
|
155
|
+
new URL(location);
|
|
156
|
+
} catch (e) {
|
|
157
|
+
try {
|
|
158
|
+
location = new URL(location, requestUrl).toString();
|
|
159
|
+
} catch (_) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return location;
|
|
164
|
+
}
|