@api-client/core 0.3.3 → 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.
Files changed (176) hide show
  1. package/package.json +1 -1
  2. package/src/data/DataReader.ts +11 -0
  3. package/src/data/DataUtils.ts +108 -0
  4. package/src/data/JmesparthReader.ts +26 -0
  5. package/src/data/Json2Xml.ts +190 -0
  6. package/src/data/JsonReader.ts +41 -0
  7. package/src/data/PayloadPointer.ts +48 -0
  8. package/src/data/RequestDataExtractor.ts +133 -0
  9. package/src/data/UrlEncodedReader.ts +20 -0
  10. package/src/data/XmlReader.ts +103 -0
  11. package/src/events/BaseEvents.ts +259 -0
  12. package/src/events/CustomEvent.ts +27 -0
  13. package/src/events/EventTypes.ts +19 -0
  14. package/src/events/Events.ts +19 -0
  15. package/src/events/authorization/AuthorizationEventTypes.ts +22 -0
  16. package/src/events/authorization/AuthorizationEvents.ts +61 -0
  17. package/src/events/cookies/CookieEventTypes.ts +13 -0
  18. package/src/events/cookies/CookieEvents.ts +157 -0
  19. package/src/events/encryption/EncryptionEventTypes.ts +4 -0
  20. package/src/events/encryption/EncryptionEvents.ts +51 -0
  21. package/src/events/environment/EnvironmentEventTypes.ts +3 -0
  22. package/src/events/environment/EnvironmentEvents.ts +24 -0
  23. package/src/events/models/ClientCertificateEvents.ts +87 -0
  24. package/src/events/models/ModelEventTypes.ts +47 -0
  25. package/src/events/models/ModelEvents.ts +7 -0
  26. package/src/events/models/ProjectEvents.ts +331 -0
  27. package/src/events/process/ProcessEventTypes.ts +5 -0
  28. package/src/events/process/ProcessEvents.ts +76 -0
  29. package/src/events/readme.md +22 -0
  30. package/src/events/reporting/ReportingEventTypes.ts +3 -0
  31. package/src/events/reporting/ReportingEvents.ts +28 -0
  32. package/src/events/telemetry/TelemetryEventTypes.ts +10 -0
  33. package/src/events/telemetry/TelemetryEvents.ts +156 -0
  34. package/src/lib/cookies/Cookie.ts +312 -0
  35. package/src/lib/cookies/Cookies.ts +326 -0
  36. package/src/lib/cookies/Utils.ts +168 -0
  37. package/src/lib/headers/Headers.ts +219 -0
  38. package/src/lib/logging/DefaultLogger.ts +19 -0
  39. package/src/lib/logging/DummyLogger.ts +21 -0
  40. package/src/lib/logging/Logger.ts +16 -0
  41. package/src/lib/transformers/PayloadSerializer.ts +332 -0
  42. package/src/lib/transformers/Utils.ts +18 -0
  43. package/src/lib/uuid.ts +40 -0
  44. package/src/mocking/LegacyInterfaces.ts +52 -0
  45. package/src/mocking/LegacyMock.ts +37 -0
  46. package/src/mocking/legacy/Authorization.ts +39 -0
  47. package/src/mocking/legacy/Certificates.ts +145 -0
  48. package/src/mocking/legacy/Cookies.ts +51 -0
  49. package/src/mocking/legacy/HostRules.ts +43 -0
  50. package/src/mocking/legacy/Http.ts +236 -0
  51. package/src/mocking/legacy/HttpResponse.ts +106 -0
  52. package/src/mocking/legacy/RestApi.ts +68 -0
  53. package/src/mocking/legacy/Urls.ts +44 -0
  54. package/src/mocking/legacy/Variables.ts +53 -0
  55. package/src/models/ArcResponse.ts +166 -0
  56. package/src/models/Authorization.ts +481 -0
  57. package/src/models/AuthorizationData.ts +60 -0
  58. package/src/models/Backend.ts +107 -0
  59. package/src/models/ClientCertificate.ts +68 -0
  60. package/src/models/Environment.ts +279 -0
  61. package/src/models/ErrorResponse.ts +101 -0
  62. package/src/models/HistoryIndex.ts +76 -0
  63. package/src/models/HistoryRequest.ts +28 -0
  64. package/src/models/HostRule.ts +163 -0
  65. package/src/models/HttpCookie.ts +285 -0
  66. package/src/models/HttpProject.ts +1294 -0
  67. package/src/models/HttpProjectListItem.ts +23 -0
  68. package/src/models/HttpRequest.ts +124 -0
  69. package/src/models/HttpResponse.ts +143 -0
  70. package/src/models/License.ts +113 -0
  71. package/src/models/ProjectDefinitionProperty.ts +40 -0
  72. package/src/models/ProjectFolder.ts +439 -0
  73. package/src/models/ProjectItem.ts +135 -0
  74. package/src/models/ProjectParent.ts +113 -0
  75. package/src/models/ProjectRequest.ts +277 -0
  76. package/src/models/ProjectSchema.ts +202 -0
  77. package/src/models/Property.ts +423 -0
  78. package/src/models/Provider.ts +98 -0
  79. package/src/models/README.md +20 -0
  80. package/src/models/Request.ts +452 -0
  81. package/src/models/RequestActions.ts +163 -0
  82. package/src/models/RequestAuthorization.ts +115 -0
  83. package/src/models/RequestConfig.ts +317 -0
  84. package/src/models/RequestLog.ts +159 -0
  85. package/src/models/RequestTime.ts +108 -0
  86. package/src/models/RequestUiMeta.ts +258 -0
  87. package/src/models/RequestsSize.ts +65 -0
  88. package/src/models/ResponseAuthorization.ts +104 -0
  89. package/src/models/ResponseRedirect.ts +158 -0
  90. package/src/models/RevisionInfo.ts +37 -0
  91. package/src/models/SentRequest.ts +125 -0
  92. package/src/models/SerializablePayload.ts +68 -0
  93. package/src/models/Server.ts +153 -0
  94. package/src/models/Thing.ts +110 -0
  95. package/src/models/Url.ts +90 -0
  96. package/src/models/User.ts +120 -0
  97. package/src/models/WebApi.ts +234 -0
  98. package/src/models/WebApiIndex.ts +122 -0
  99. package/src/models/Workspace.ts +182 -0
  100. package/src/models/actions/Action.ts +213 -0
  101. package/src/models/actions/ActionView.ts +40 -0
  102. package/src/models/actions/Condition.ts +207 -0
  103. package/src/models/actions/ConditionView.ts +42 -0
  104. package/src/models/actions/Enums.ts +29 -0
  105. package/src/models/actions/RunnableAction.ts +144 -0
  106. package/src/models/actions/runnable/DeleteCookieAction.ts +113 -0
  107. package/src/models/actions/runnable/Runnable.ts +9 -0
  108. package/src/models/actions/runnable/SetCookieAction.ts +216 -0
  109. package/src/models/actions/runnable/SetVariableAction.ts +81 -0
  110. package/src/models/legacy/DataExport.ts +172 -0
  111. package/src/models/legacy/Normalizer.ts +110 -0
  112. package/src/models/legacy/actions/Actions.ts +269 -0
  113. package/src/models/legacy/authorization/Authorization.ts +572 -0
  114. package/src/models/legacy/models/ApiTypes.ts +202 -0
  115. package/src/models/legacy/models/ArcLegacyProject.ts +39 -0
  116. package/src/models/legacy/models/AuthData.ts +17 -0
  117. package/src/models/legacy/models/ClientCertificate.ts +95 -0
  118. package/src/models/legacy/models/Cookies.ts +52 -0
  119. package/src/models/legacy/models/HostRule.ts +35 -0
  120. package/src/models/legacy/models/RestApi.ts +49 -0
  121. package/src/models/legacy/models/UrlHistory.ts +37 -0
  122. package/src/models/legacy/models/Variable.ts +43 -0
  123. package/src/models/legacy/models/base.d.ts +95 -0
  124. package/src/models/legacy/request/ArcRequest.ts +405 -0
  125. package/src/models/legacy/request/ArcResponse.ts +177 -0
  126. package/src/models/legacy/request/HistoryData.ts +47 -0
  127. package/src/models/legacy/request/Legacy.ts +45 -0
  128. package/src/models/legacy/request/RequestBody.ts +87 -0
  129. package/src/models/transformers/ArcDexieTransformer.ts +323 -0
  130. package/src/models/transformers/ArcLegacyNormalizer.ts +85 -0
  131. package/src/models/transformers/ArcLegacyTransformer.ts +200 -0
  132. package/src/models/transformers/ArcPouchTransformer.ts +184 -0
  133. package/src/models/transformers/BaseTransformer.ts +116 -0
  134. package/src/models/transformers/ImportUtils.ts +141 -0
  135. package/src/models/transformers/LegacyDataExportToApiProject.ts +76 -0
  136. package/src/models/transformers/LegacyExportProcessor.ts +252 -0
  137. package/src/models/transformers/PostmanBackupTransformer.ts +306 -0
  138. package/src/models/transformers/PostmanDataTransformer.ts +50 -0
  139. package/src/models/transformers/PostmanTransformer.ts +106 -0
  140. package/src/models/transformers/PostmanV21Transformer.ts +311 -0
  141. package/src/models/transformers/PostmanV2Transformer.ts +308 -0
  142. package/src/models/transformers/har.ts +865 -0
  143. package/src/runtime/actions/ActionRunner.ts +83 -0
  144. package/src/runtime/actions/ConditionRunner.ts +194 -0
  145. package/src/runtime/actions/RunnableCondition.ts +57 -0
  146. package/src/runtime/actions/runnable/ActionRunnable.ts +19 -0
  147. package/src/runtime/actions/runnable/DeleteCookieRunnable.ts +39 -0
  148. package/src/runtime/actions/runnable/SetCookieRunnable.ts +92 -0
  149. package/src/runtime/actions/runnable/SetVariableRunnable.ts +53 -0
  150. package/src/runtime/http-engine/ArcEngine.ts +1064 -0
  151. package/src/runtime/http-engine/Errors.ts +13 -0
  152. package/src/runtime/http-engine/FormData.ts +85 -0
  153. package/src/runtime/http-engine/HttpEngine.ts +874 -0
  154. package/src/runtime/http-engine/HttpErrorCodes.ts +270 -0
  155. package/src/runtime/http-engine/NodeEngine.ts +787 -0
  156. package/src/runtime/http-engine/NodeEngineDirect.ts +476 -0
  157. package/src/runtime/http-engine/PayloadSupport.ts +84 -0
  158. package/src/runtime/http-engine/RequestUtils.ts +164 -0
  159. package/src/runtime/http-engine/ntlm/Des.ts +345 -0
  160. package/src/runtime/http-engine/ntlm/MD4.ts +135 -0
  161. package/src/runtime/http-engine/ntlm/NtlmAuth.ts +186 -0
  162. package/src/runtime/http-engine/ntlm/NtlmMessage.ts +57 -0
  163. package/src/runtime/modules/BasicAuthCache.ts +133 -0
  164. package/src/runtime/modules/ExecutionResponse.ts +4 -0
  165. package/src/runtime/modules/ModulesRegistry.ts +136 -0
  166. package/src/runtime/modules/RequestAuthorization.ts +110 -0
  167. package/src/runtime/modules/RequestCookies.ts +145 -0
  168. package/src/runtime/node/ProjectRunner.ts +275 -0
  169. package/src/runtime/node/RequestFactory.ts +422 -0
  170. package/src/runtime/node/VariablesStore.ts +25 -0
  171. package/src/runtime/store/StoreSdk.ts +838 -0
  172. package/src/runtime/variables/Cache.ts +53 -0
  173. package/src/runtime/variables/EvalFunctions.ts +132 -0
  174. package/src/runtime/variables/ProjectVariables.ts +6 -0
  175. package/src/runtime/variables/VariablesProcessor.ts +543 -0
  176. package/src/runtime/variables/VariablesTokenizer.ts +55 -0
@@ -0,0 +1,787 @@
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 { IHttpRequest } from '../../models/HttpRequest.js';
9
+ import { ArcResponse } from '../../models/ArcResponse.js';
10
+ import { IRequestLog } from '../../models/RequestLog.js';
11
+ import { Headers } from '../../lib/headers/Headers.js';
12
+ import { PayloadSupport } from './PayloadSupport.js';
13
+ import { addContentLength, getPort } from './RequestUtils.js';
14
+ import { NetError } from './Errors.js';
15
+ import { INtlmAuthorization } from '../../models/Authorization.js';
16
+ import { NtlmAuth, INtlmAuthConfig } from './ntlm/NtlmAuth.js';
17
+
18
+ export class NodeEngine extends HttpEngine {
19
+ responseReported = false;
20
+
21
+ _sentHttpMessage: any;
22
+
23
+ client?: http.ClientRequest;
24
+ receivingResponse = false;
25
+
26
+ constructor(request: IHttpRequest, opts: HttpEngineOptions = {}) {
27
+ super(request, opts);
28
+
29
+ this._connectHandler = this._connectHandler.bind(this);
30
+ this._secureConnectHandler = this._secureConnectHandler.bind(this);
31
+ this._responseHandler = this._responseHandler.bind(this);
32
+ this._timeoutHandler = this._timeoutHandler.bind(this);
33
+ this._errorHandler = this._errorHandler.bind(this);
34
+ this._lookupHandler = this._lookupHandler.bind(this);
35
+ this._closeHandler = this._closeHandler.bind(this);
36
+ this._socketHandler = this._socketHandler.bind(this);
37
+ this._sendEndHandler = this._sendEndHandler.bind(this);
38
+ }
39
+
40
+ /**
41
+ * Sends the request
42
+ */
43
+ async send(): Promise<IRequestLog> {
44
+ this.abort();
45
+ this.aborted = false;
46
+ const promise = this.wrapExecution();
47
+ this.sendRequest();
48
+ return promise;
49
+ }
50
+
51
+ private async sendRequest(): Promise<void> {
52
+ try {
53
+ const message = await this._prepareMessage();
54
+ const request = this.opts.proxy ? await this._connectProxy(this.opts.proxy, message) : this._connect(message);
55
+ if (request) {
56
+ this.client = request;
57
+ const { timeout } = this;
58
+ if (timeout > 0) {
59
+ request.setTimeout(timeout);
60
+ }
61
+ }
62
+ } catch (e) {
63
+ console.warn(e);
64
+ this.finalizeRequest(e as Error);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Prepares the request body (the payload) and the headers.
70
+ *
71
+ * @return Resolved promise to a `Buffer`. Undefined when no message.
72
+ */
73
+ async _prepareMessage(): Promise<Buffer|undefined> {
74
+ const { method='GET', headers } = this.request;
75
+ let { payload } = this.request;
76
+ if (['get', 'head'].includes(method.toLowerCase())) {
77
+ payload = undefined;
78
+ }
79
+ const engineHeaders = new Headers(headers);
80
+ this.prepareHeaders(engineHeaders);
81
+ let buffer: Buffer | undefined;
82
+ if (payload) {
83
+ buffer = await PayloadSupport.payloadToBuffer(payload, engineHeaders);
84
+ if (buffer) {
85
+ addContentLength(method, buffer, engineHeaders);
86
+ }
87
+ }
88
+ this._handleAuthorization(engineHeaders);
89
+ this.sentRequest.headers = engineHeaders.toString();
90
+ // if (this.auth) {
91
+ // // This restores altered by the authorization original headers
92
+ // // so it can be safe to use when redirecting
93
+ // if (this.auth.headers) {
94
+ // this.request.headers = this.auth.headers;
95
+ // delete this.auth.headers;
96
+ // }
97
+ // }
98
+ return buffer;
99
+ }
100
+
101
+ /**
102
+ * Connects to the remote machine.
103
+ */
104
+ _connect(message?: Buffer): http.ClientRequest {
105
+ const uri = new URL(this.request.url);
106
+ const port = getPort(uri.port, uri.protocol);
107
+ if (port === 443 || uri.protocol === 'https:') {
108
+ return this._connectHttps(uri, message);
109
+ }
110
+ return this._connectHttp(uri, message);
111
+ }
112
+
113
+ /**
114
+ * Creates a connection using regular transport.
115
+ */
116
+ _connectHttp(uri: URL, message?: Buffer): http.ClientRequest {
117
+ if (!uri.port) {
118
+ uri.port = '80';
119
+ }
120
+ const options = this._createGenericOptions(uri);
121
+ options.agent = new http.Agent({ keepAlive: true });
122
+ const startTime = Date.now();
123
+ this.stats.startTime = startTime;
124
+ this.sentRequest.startTime = startTime;
125
+
126
+ const request = http.request(options);
127
+ this._setCommonListeners(request);
128
+ if (message) {
129
+ request.write(message);
130
+ }
131
+ this.stats.messageStart = Date.now();
132
+ request.end();
133
+ // This is a hack to read sent data.
134
+ // In the https://github.com/nodejs/node/blob/0a62026f32d513a8a5d9ed857481df5f5fa18e8b/lib/_http_outgoing.js#L960
135
+ // library it hold the data until it is flushed.
136
+ // @ts-ignore
137
+ const pending = request.outputData;
138
+ if (Array.isArray(pending)) {
139
+ this._sentHttpMessage = pending;
140
+ }
141
+ try {
142
+ this.emit('loadstart');
143
+ } catch (_) {
144
+ //
145
+ }
146
+ return request;
147
+ }
148
+
149
+ /**
150
+ * Creates a connection using SSL transport.
151
+ */
152
+ _connectHttps(uri: URL, message?: Buffer): http.ClientRequest {
153
+ if (!uri.port) {
154
+ uri.port = '443';
155
+ }
156
+ const options = this._createGenericOptions(uri);
157
+ this._addSslOptions(options);
158
+ const startTime = Date.now();
159
+ this.stats.startTime = startTime;
160
+ this.sentRequest.startTime = startTime;
161
+
162
+ const request = https.request(options);
163
+ this._setCommonListeners(request);
164
+ if (message) {
165
+ request.write(message);
166
+ }
167
+ this.stats.messageStart = Date.now();
168
+ this.stats.sentTime = this.stats.messageStart + 1;
169
+ request.end();
170
+ // This is a hack to read sent data.
171
+ // In the https://github.com/nodejs/node/blob/0a62026f32d513a8a5d9ed857481df5f5fa18e8b/lib/_http_outgoing.js#L960
172
+ // library it hold the data until it is flushed.
173
+ // @ts-ignore
174
+ const pending = request.outputData;
175
+ if (Array.isArray(pending)) {
176
+ this._sentHttpMessage = pending;
177
+ }
178
+ try {
179
+ this.emit('loadstart');
180
+ } catch (_) {
181
+ //
182
+ }
183
+ return request;
184
+ }
185
+
186
+ /**
187
+ * Sets listeners on a socket
188
+ * @param request The request object
189
+ */
190
+ _setCommonListeners(request: http.ClientRequest): void {
191
+ // request.shouldKeepAlive = false;
192
+ request.once('socket', this._socketHandler);
193
+ request.once('error', this._errorHandler);
194
+ request.once('response', this._responseHandler);
195
+ request.once('close', this._closeHandler);
196
+ }
197
+
198
+ /**
199
+ * Handler for connection error.
200
+ */
201
+ _errorHandler(e: ResponseErrorInit): void {
202
+ if (this.aborted) {
203
+ return;
204
+ }
205
+ this._errorRequest({
206
+ code: e.code,
207
+ message: e.message,
208
+ });
209
+ }
210
+
211
+ /**
212
+ * Handler for DNS lookup.
213
+ */
214
+ _lookupHandler(): void {
215
+ this.stats.lookupTime = Date.now();
216
+ }
217
+
218
+ /**
219
+ * Handler for connected event.
220
+ */
221
+ _secureConnectHandler(): void {
222
+ this.stats.secureConnectedTime = Date.now();
223
+ }
224
+
225
+ /**
226
+ * Handler for connecting event.
227
+ */
228
+ _connectHandler(): void {
229
+ this.stats.connectedTime = Date.now();
230
+ this.stats.secureStartTime = Date.now();
231
+ }
232
+
233
+ /**
234
+ * Handler for sending finished event
235
+ */
236
+ _sendEndHandler(): void {
237
+ if (!this.stats.sentTime) {
238
+ this.stats.sentTime = Date.now();
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Handler for timeout event
244
+ */
245
+ _timeoutHandler(): void {
246
+ this._errorRequest({
247
+ code: 7,
248
+ });
249
+ this.abort();
250
+ }
251
+
252
+ /**
253
+ * A handler for response data event
254
+ */
255
+ _responseHandler(res: http.IncomingMessage): void {
256
+ this.receivingResponse = true;
257
+
258
+ this.emit('firstbyte');
259
+ this.stats.firstReceiveTime = Date.now();
260
+ this.stats.responseTime = Date.now();
261
+ if (this._sentHttpMessage) {
262
+ this.sentRequest.httpMessage = this._readSentMessage(this._sentHttpMessage);
263
+ } else {
264
+ this.sentRequest.httpMessage = '';
265
+ }
266
+ const status = res.statusCode;
267
+ if (!status) {
268
+ this._errorRequest({
269
+ message: 'The response has no status.',
270
+ });
271
+ return;
272
+ }
273
+ const headers = this.computeResponseHeaders(res);
274
+ const rawHeaders = headers.toString();
275
+ const response = ArcResponse.fromValues(status, res.statusMessage, rawHeaders);
276
+ this.currentResponse = response;
277
+ this.currentHeaders = headers;
278
+ const detail: HeadersReceivedDetail = {
279
+ returnValue: true,
280
+ value: rawHeaders,
281
+ };
282
+ this.emit('headersreceived', detail);
283
+ if (!detail.returnValue) {
284
+ this.abort();
285
+ return;
286
+ }
287
+ // if (status === 401 && this.auth) {
288
+ // switch (this.auth.method) {
289
+ // case 'ntlm': this._handleNtlmResponse(); break;
290
+ // }
291
+ // }
292
+ res.on('data', (chunk) => {
293
+ if (!this._rawBody) {
294
+ this._rawBody = chunk;
295
+ } else {
296
+ const endTime = Date.now();
297
+ this.stats.lastReceivedTime = endTime;
298
+ this._rawBody = Buffer.concat([this._rawBody, chunk]);
299
+ }
300
+ });
301
+ res.once('end', () => {
302
+ const endTime = Date.now();
303
+ this.sentRequest.endTime = endTime;
304
+ this.stats.receivingTime = endTime;
305
+ this._reportResponse();
306
+ });
307
+ }
308
+
309
+ /**
310
+ * Handler for connection close event
311
+ */
312
+ _closeHandler(): void {
313
+ if (this.responseReported || this.receivingResponse || this.aborted || this.redirecting) {
314
+ return;
315
+ }
316
+ if (!this.currentResponse) {
317
+ const e = new Error('Connection closed unexpectedly.');
318
+ // console.log(e.stack);
319
+ // console.log(this.sentRequest);
320
+ this._errorRequest(e);
321
+ } else {
322
+ // There is an issue with the response. Size mismatch? Anyway,
323
+ // it tries to create a response from current data.
324
+ this.emit('loadend');
325
+ this._publishResponse();
326
+ }
327
+ }
328
+
329
+ _socketHandler(socket: net.Socket): void {
330
+ this.socket = socket;
331
+ socket.once('lookup', this._lookupHandler);
332
+ socket.once('connect', this._connectHandler);
333
+ socket.once('timeout', this._timeoutHandler);
334
+ socket.once('end', this._sendEndHandler);
335
+ socket.once('secureConnect', this._secureConnectHandler);
336
+ this.stats.connectionTime = Date.now();
337
+ }
338
+
339
+ /**
340
+ * Creates and publishes a response.
341
+ */
342
+ _reportResponse(): void {
343
+ const { aborted, currentResponse } = this;
344
+ if (aborted || !currentResponse) {
345
+ return;
346
+ }
347
+ const { status } = currentResponse;
348
+ if (status >= 300 && status < 400) {
349
+ if (this.followRedirects !== false && this._reportRedirect(status)) {
350
+ return;
351
+ }
352
+ }
353
+ if (this.responseReported) {
354
+ return;
355
+ }
356
+ this.responseReported = true;
357
+ this.emit('loadend');
358
+ this._publishResponse();
359
+ }
360
+
361
+ /**
362
+ * Transforms a message from the client to a string.
363
+ * It uses `opts.sentMessageLimit` to limit number of data returned
364
+ * by the client.
365
+ */
366
+ _readSentMessage(messages: string|any[]): string {
367
+ let result = '';
368
+ if (typeof messages === 'string') {
369
+ result = messages;
370
+ } else {
371
+ for (let i = 0, len = messages.length; i < len; i++) {
372
+ const chunk = messages[i].data;
373
+ if (!chunk) {
374
+ continue;
375
+ }
376
+ if (typeof chunk === 'string') {
377
+ result += chunk;
378
+ } else if (chunk instanceof Uint8Array) {
379
+ result += chunk.toString();
380
+ }
381
+ }
382
+ }
383
+ const limit = this.opts.sentMessageLimit;
384
+ if (limit && limit > 0 && result.length >= limit) {
385
+ result = result.substr(0, limit);
386
+ result += ' ...';
387
+ }
388
+ return result;
389
+ }
390
+
391
+ /**
392
+ * Connects to the remote machine via a proxy.
393
+ */
394
+ async _connectProxy(proxy: string, message?: Buffer): Promise<http.ClientRequest | undefined> {
395
+ const { url } = this.request;
396
+ const isTargetSsl = url.startsWith('https:');
397
+ const isProxySsl = proxy.startsWith('https:');
398
+ const uri = new URL(url);
399
+
400
+ if (!isProxySsl && !isTargetSsl) {
401
+ return this._proxyHttpOverHttp(uri, proxy, message);
402
+ }
403
+ if (!isProxySsl && isTargetSsl) {
404
+ return this._proxyHttpsOverHttp(uri, proxy, message);
405
+ }
406
+ if (isProxySsl && !isTargetSsl) {
407
+ return this._proxyHttpOverHttps(uri, proxy, message);
408
+ }
409
+ return this._proxyHttpsOverHttps(uri, proxy, message);
410
+ }
411
+
412
+ /**
413
+ * Creates a default options for a request.
414
+ * @param uri Instance of URL class for current URL.
415
+ */
416
+ _createGenericOptions(uri: URL | UrlWithStringQuery): http.RequestOptions {
417
+ const result: http.RequestOptions = {
418
+ protocol: uri.protocol,
419
+ host: uri.hostname,
420
+ // hash: uri.hash,
421
+ method: this.request.method.toUpperCase(),
422
+ };
423
+ result.headers = {};
424
+ if (uri.port) {
425
+ result.port = uri.port;
426
+ }
427
+ result.path = `${uri.pathname}${uri.search}`;
428
+ // Note, the final headers are set on the `sentRequest` object.
429
+ // The `request` object is not changed.
430
+ const headers = new Headers(this.sentRequest.headers);
431
+ for (const [key, value] of headers.entries()) {
432
+ result.headers[key] = value;
433
+ }
434
+ return result;
435
+ }
436
+
437
+ /**
438
+ * Adds SSL options to the request.
439
+ */
440
+ _addSslOptions(options: any): void {
441
+ if (this.opts.validateCertificates) {
442
+ options.checkServerIdentity = this._checkServerIdentity.bind(this);
443
+ } else {
444
+ options.rejectUnauthorized = false;
445
+ options.requestOCSP = false;
446
+ }
447
+ const certs = this.opts.certificates;
448
+ if (Array.isArray(certs)) {
449
+ certs.forEach(cert => this._addClientCertificate(cert, options));
450
+ }
451
+ options.agent = new https.Agent(options);
452
+ }
453
+
454
+ /**
455
+ * Creates options to be set on the proxy request.
456
+ * It replaces the original `host` and `port` values with the ones defined
457
+ * for the proxy server.
458
+ *
459
+ * @param proxy The proxy URI. (e.g. 10.0.0.12:8118)
460
+ * @param requestUri The original request URI.
461
+ * @param requestOptions The original request options
462
+ */
463
+ _createProxyOptions(proxy: string, requestUri: URL, requestOptions: http.RequestOptions): http.RequestOptions {
464
+ let proxyUrl = proxy;
465
+ const options = requestOptions;
466
+ const isSsl = proxyUrl.startsWith('https:');
467
+ const isHttp = proxyUrl.startsWith('http:');
468
+ if (!isSsl && !isHttp) {
469
+ proxyUrl = `http://${proxyUrl}`;
470
+ }
471
+ const proxyUri = new URL(proxyUrl);
472
+ if (!options.headers) {
473
+ options.headers = {};
474
+ }
475
+ const auth = this._proxyAuthHeader();
476
+ if (auth) {
477
+ if (!options.headers['proxy-authorization']) {
478
+ options.headers['proxy-authorization'] = auth;
479
+ }
480
+ }
481
+ options.headers.host = `${requestUri.hostname}:${requestUri.port || 80}`;
482
+ delete options.headers.Host;
483
+ return {
484
+ ...options,
485
+ protocol: proxyUri.protocol,
486
+ host: proxyUri.hostname,
487
+ hostname: proxyUri.hostname,
488
+ port: proxyUri.port || 80,
489
+ path: requestUri.toString(),
490
+ agent: false,
491
+ };
492
+ }
493
+
494
+ /**
495
+ * Creates a connection to non-ssl target via a non-ssl proxy.
496
+ *
497
+ * @param message The message to send
498
+ * @param uri The target URI
499
+ * @param proxy The proxy URI
500
+ */
501
+ _proxyHttpOverHttp(uri: URL, proxy: string, message?: Buffer): http.ClientRequest {
502
+ const targetOptions = this._createGenericOptions(uri);
503
+ const options = this._createProxyOptions(proxy, uri, targetOptions);
504
+ const startTime = Date.now();
505
+ this.stats.startTime = startTime;
506
+ this.sentRequest.startTime = startTime;
507
+ const request = http.request(options);
508
+ this._setCommonListeners(request);
509
+ if (message) {
510
+ request.write(message);
511
+ }
512
+ this.stats.messageStart = Date.now();
513
+ this.stats.sentTime = this.stats.messageStart + 1;
514
+ request.end();
515
+ try {
516
+ this.emit('loadstart');
517
+ } catch (_) {
518
+ //
519
+ }
520
+ return request;
521
+ }
522
+
523
+ /**
524
+ * Creates a connection to non-ssl target via an ssl proxy.
525
+ *
526
+ * @param message The message to send
527
+ * @param uri The target URI
528
+ * @param proxy The proxy URI
529
+ */
530
+ async _proxyHttpsOverHttp(uri: URL, proxy: string, message?: Buffer): Promise<http.ClientRequest|undefined> {
531
+ let proxyUrl = proxy;
532
+ if (!proxyUrl.startsWith('http:')) {
533
+ proxyUrl = `http://${proxyUrl}`;
534
+ }
535
+ const proxyUri = new URL(proxyUrl);
536
+ const proxyPort = proxyUri.port || 80;
537
+ const targetPort = uri.port || 443; // target is always SSL so 443.
538
+ const authority = `${uri.hostname}:${targetPort}`;
539
+ const connectOptions: http.RequestOptions = {
540
+ host: proxyUri.hostname,
541
+ port: proxyPort,
542
+ method: 'CONNECT',
543
+ path: authority,
544
+ headers: {
545
+ host: authority,
546
+ },
547
+ };
548
+ const auth = this._proxyAuthHeader();
549
+ if (auth) {
550
+ connectOptions.headers = {
551
+ 'proxy-authorization': auth,
552
+ };
553
+ }
554
+ return new Promise((resolve, reject) => {
555
+ const connectRequest = http.request(connectOptions);
556
+ connectRequest.on('connect', (res, socket, head) => {
557
+ if (res.statusCode === 200) {
558
+ const options = this._createGenericOptions(uri);
559
+ this._addSslOptions(options);
560
+ delete options.agent;
561
+ const startTime = Date.now();
562
+ this.stats.startTime = startTime;
563
+ this.sentRequest.startTime = startTime;
564
+ const agent = new https.Agent({
565
+ socket,
566
+ });
567
+ const request = https.request({ ...options, agent });
568
+ this._connectHandler();
569
+ this._setCommonListeners(request);
570
+ if (message) {
571
+ request.write(message);
572
+ }
573
+ request.end();
574
+ this.stats.messageStart = Date.now();
575
+ this.stats.sentTime = this.stats.messageStart + 1;
576
+ resolve(request);
577
+ } else if (res.statusCode === 401) {
578
+ this.currentHeaders = this.computeResponseHeaders(res);
579
+ const response = ArcResponse.fromValues(res.statusCode, res.statusMessage, this.currentHeaders.toString());
580
+ this.currentResponse = response;
581
+ if (head.length) {
582
+ this._rawBody = head;
583
+ this.currentResponse.payload = {
584
+ type: 'buffer',
585
+ data: [...head],
586
+ };
587
+ }
588
+ connectRequest.destroy();
589
+ resolve(undefined);
590
+ setTimeout(() => {
591
+ // const e = new NetError('The proxy requires authentication.', 127);
592
+ this._publishResponse();
593
+ });
594
+ } else {
595
+ connectRequest.destroy();
596
+ const e = new NetError('A tunnel connection through the proxy could not be established.', 111);
597
+ reject(e);
598
+ }
599
+ });
600
+ connectRequest.once('error', (err) => reject(err));
601
+ try {
602
+ this.emit('loadstart');
603
+ } catch (_) {
604
+ //
605
+ }
606
+ connectRequest.end();
607
+ });
608
+ }
609
+
610
+ /**
611
+ * Creates a connection to a non-ssl target using SSL proxy.
612
+ *
613
+ * @param proxy The proxy URI
614
+ */
615
+ _proxyHttpOverHttps(uri: URL, proxy: string, message?: Buffer): http.ClientRequest {
616
+ const targetOptions = this._createGenericOptions(uri);
617
+ const options: https.RequestOptions = this._createProxyOptions(proxy, uri, targetOptions);
618
+ options.rejectUnauthorized = false;
619
+ // @ts-ignore
620
+ options.requestOCSP = false;
621
+ const startTime = Date.now();
622
+ this.stats.startTime = startTime;
623
+ this.sentRequest.startTime = startTime;
624
+ const request = https.request(options);
625
+ this._setCommonListeners(request);
626
+ if (message) {
627
+ request.write(message);
628
+ }
629
+ this.stats.messageStart = Date.now();
630
+ this.stats.sentTime = this.stats.messageStart + 1;
631
+ request.end();
632
+ // @ts-ignore
633
+ const pending = request.outputData;
634
+ if (Array.isArray(pending)) {
635
+ this._sentHttpMessage = pending;
636
+ }
637
+ try {
638
+ this.emit('loadstart');
639
+ } catch (_) {
640
+ //
641
+ }
642
+ return request;
643
+ }
644
+
645
+ /**
646
+ * Creates a connection to a non-ssl target using SSL proxy.
647
+ *
648
+ * @param proxy The proxy URI
649
+ */
650
+ _proxyHttpsOverHttps(uri: URL, proxy: string, message?: Buffer): http.ClientRequest {
651
+ let proxyUrl = proxy;
652
+ if (!proxyUrl.startsWith('https:')) {
653
+ proxyUrl = `https://${proxyUrl}`;
654
+ }
655
+ const proxyUri = new URL(proxyUrl);
656
+ const connectOptions: https.RequestOptions = {
657
+ host: proxyUri.hostname, // IP address of proxy server
658
+ port: proxyUri.port || 443, // port of proxy server
659
+ method: 'CONNECT',
660
+ path: `${uri.hostname}:${uri.port || 443}`,
661
+ headers: {
662
+ host: `${uri.hostname}:${uri.port || 443}`,
663
+ },
664
+ rejectUnauthorized: false,
665
+ // @ts-ignore
666
+ requestOCSP: false,
667
+ };
668
+ const auth = this._proxyAuthHeader();
669
+ if (auth) {
670
+ connectOptions.headers = {
671
+ 'proxy-authorization': auth,
672
+ };
673
+ }
674
+ const connectRequest = https.request(connectOptions);
675
+ connectRequest.on('connect', (res, socket) => {
676
+ if (res.statusCode === 200) {
677
+ const agent = new https.Agent({ socket });
678
+ const options = this._createGenericOptions(uri);
679
+ this._addSslOptions(options);
680
+ const startTime = Date.now();
681
+ this.stats.startTime = startTime;
682
+ this.sentRequest.startTime = startTime;
683
+ const sslRequest = https.request({ ...options, agent, protocol: 'https:' });
684
+ sslRequest.on('error', () => {
685
+ console.log('sslRequest error');
686
+ });
687
+ this._connectHandler();
688
+ this._setCommonListeners(sslRequest);
689
+ if (message) {
690
+ sslRequest.write(message);
691
+ }
692
+ this.stats.messageStart = Date.now();
693
+ this.stats.sentTime = this.stats.messageStart + 1;
694
+ sslRequest.end();
695
+ // @ts-ignore
696
+ const pending = sslRequest.outputData;
697
+ if (Array.isArray(pending)) {
698
+ this._sentHttpMessage = pending;
699
+ }
700
+ } else {
701
+ this._errorRequest({
702
+ code: 111,
703
+ message: 'A tunnel connection through the proxy could not be established.',
704
+ });
705
+ connectRequest.destroy();
706
+ }
707
+ });
708
+ connectRequest.once('error', this._errorHandler);
709
+ try {
710
+ this.emit('loadstart');
711
+ } catch (_) {
712
+ //
713
+ }
714
+ connectRequest.end();
715
+ return connectRequest;
716
+ }
717
+
718
+ /**
719
+ * Alters authorization header depending on the `auth` object
720
+ * @param {ArcHeaders} headers A headers object where to append headers if
721
+ * needed
722
+ */
723
+ _handleAuthorization(headers: Headers): void {
724
+ const { authorization } = this.opts;
725
+ const enabled = Array.isArray(authorization) ? authorization.filter((i) => i.enabled) : [];
726
+ if (!enabled.length) {
727
+ return;
728
+ }
729
+ const ntlm = enabled.find((i) => i.type === 'ntlm');
730
+ if (ntlm) {
731
+ this._authorizeNtlm(ntlm.config as INtlmAuthorization, headers);
732
+ }
733
+ }
734
+
735
+ /**
736
+ * Authorize the request with NTLM
737
+ * @param authData The credentials to use
738
+ * @param headers A headers object where to append headers if needed
739
+ */
740
+ _authorizeNtlm(authData: INtlmAuthorization, headers: Headers): void {
741
+ const init = { ...authData, url: this.request.url } as INtlmAuthConfig;
742
+ const auth = new NtlmAuth(init);
743
+ if (!this.auth) {
744
+ this.auth = {
745
+ method: 'ntlm',
746
+ state: 0,
747
+ headers: headers.toString(),
748
+ };
749
+ const msg = auth.createMessage1(this.uri.host);
750
+ headers.set('Authorization', `NTLM ${msg.toBase64()}`);
751
+ headers.set('Connection', 'keep-alive');
752
+ } else if (this.auth && this.auth.state === 1) {
753
+ const msg = auth.createMessage3(this.auth.challengeHeader!, this.uri.host);
754
+ this.auth.state = 2;
755
+ headers.set('Authorization', `NTLM ${msg.toBase64()}`);
756
+ }
757
+ }
758
+
759
+ /**
760
+ * Handles the response with NTLM authorization
761
+ */
762
+ async _handleNtlmResponse(res: http.IncomingMessage): Promise<void> {
763
+ const headers = this.computeResponseHeaders(res);
764
+ const { auth } = this;
765
+ if (auth && auth.state === 0) {
766
+ console.log('Handling 401 2');
767
+ if (headers.has('www-authenticate')) {
768
+ auth.state = 1;
769
+ auth.challengeHeader = headers.get('www-authenticate');
770
+ this._cleanUpRedirect();
771
+ console.log('Preparing message');
772
+
773
+ const message = await this._prepareMessage();
774
+ console.log('The message', message);
775
+
776
+ if (message) {
777
+ console.log('writing the message');
778
+ this.socket?.write(message);
779
+ }
780
+ return;
781
+ }
782
+ }
783
+ delete this.auth;
784
+ this.emit('loadend');
785
+ this._publishResponse();
786
+ }
787
+ }