@api-client/core 0.3.2 → 0.3.5

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 (205) hide show
  1. package/build/browser.d.ts +1 -0
  2. package/build/browser.js +1 -0
  3. package/build/browser.js.map +1 -1
  4. package/build/index.d.ts +1 -1
  5. package/build/index.js +1 -1
  6. package/build/index.js.map +1 -1
  7. package/build/src/models/ErrorResponse.d.ts +5 -4
  8. package/build/src/models/ErrorResponse.js +18 -5
  9. package/build/src/models/ErrorResponse.js.map +1 -1
  10. package/build/src/models/SerializableError.d.ts +30 -0
  11. package/build/src/models/SerializableError.js +63 -0
  12. package/build/src/models/SerializableError.js.map +1 -0
  13. package/build/src/runtime/http-engine/ArcEngine.js +8 -4
  14. package/build/src/runtime/http-engine/ArcEngine.js.map +1 -1
  15. package/build/src/runtime/http-engine/HttpEngine.d.ts +3 -3
  16. package/build/src/runtime/http-engine/HttpEngine.js +3 -3
  17. package/build/src/runtime/http-engine/HttpEngine.js.map +1 -1
  18. package/build/src/runtime/http-engine/NodeEngine.js +9 -4
  19. package/build/src/runtime/http-engine/NodeEngine.js.map +1 -1
  20. package/build/src/runtime/http-engine/NodeEngineDirect.js +8 -2
  21. package/build/src/runtime/http-engine/NodeEngineDirect.js.map +1 -1
  22. package/build/src/runtime/node/ProjectRunner.d.ts +2 -2
  23. package/build/src/runtime/node/ProjectRunner.js +9 -2
  24. package/build/src/runtime/node/ProjectRunner.js.map +1 -1
  25. package/build/src/runtime/store/StoreSdk.js +10 -15
  26. package/build/src/runtime/store/StoreSdk.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/data/DataReader.ts +11 -0
  29. package/src/data/DataUtils.ts +108 -0
  30. package/src/data/JmesparthReader.ts +26 -0
  31. package/src/data/Json2Xml.ts +190 -0
  32. package/src/data/JsonReader.ts +41 -0
  33. package/src/data/PayloadPointer.ts +48 -0
  34. package/src/data/RequestDataExtractor.ts +133 -0
  35. package/src/data/UrlEncodedReader.ts +20 -0
  36. package/src/data/XmlReader.ts +103 -0
  37. package/src/events/BaseEvents.ts +259 -0
  38. package/src/events/CustomEvent.ts +27 -0
  39. package/src/events/EventTypes.ts +19 -0
  40. package/src/events/Events.ts +19 -0
  41. package/src/events/authorization/AuthorizationEventTypes.ts +22 -0
  42. package/src/events/authorization/AuthorizationEvents.ts +61 -0
  43. package/src/events/cookies/CookieEventTypes.ts +13 -0
  44. package/src/events/cookies/CookieEvents.ts +157 -0
  45. package/src/events/encryption/EncryptionEventTypes.ts +4 -0
  46. package/src/events/encryption/EncryptionEvents.ts +51 -0
  47. package/src/events/environment/EnvironmentEventTypes.ts +3 -0
  48. package/src/events/environment/EnvironmentEvents.ts +24 -0
  49. package/src/events/models/ClientCertificateEvents.ts +87 -0
  50. package/src/events/models/ModelEventTypes.ts +47 -0
  51. package/src/events/models/ModelEvents.ts +7 -0
  52. package/src/events/models/ProjectEvents.ts +331 -0
  53. package/src/events/process/ProcessEventTypes.ts +5 -0
  54. package/src/events/process/ProcessEvents.ts +76 -0
  55. package/src/events/readme.md +22 -0
  56. package/src/events/reporting/ReportingEventTypes.ts +3 -0
  57. package/src/events/reporting/ReportingEvents.ts +28 -0
  58. package/src/events/telemetry/TelemetryEventTypes.ts +10 -0
  59. package/src/events/telemetry/TelemetryEvents.ts +156 -0
  60. package/src/lib/cookies/Cookie.ts +312 -0
  61. package/src/lib/cookies/Cookies.ts +326 -0
  62. package/src/lib/cookies/Utils.ts +168 -0
  63. package/src/lib/headers/Headers.ts +219 -0
  64. package/src/lib/logging/DefaultLogger.ts +19 -0
  65. package/src/lib/logging/DummyLogger.ts +21 -0
  66. package/src/lib/logging/Logger.ts +16 -0
  67. package/src/lib/transformers/PayloadSerializer.ts +332 -0
  68. package/src/lib/transformers/Utils.ts +18 -0
  69. package/src/lib/uuid.ts +40 -0
  70. package/src/mocking/LegacyInterfaces.ts +52 -0
  71. package/src/mocking/LegacyMock.ts +37 -0
  72. package/src/mocking/legacy/Authorization.ts +39 -0
  73. package/src/mocking/legacy/Certificates.ts +145 -0
  74. package/src/mocking/legacy/Cookies.ts +51 -0
  75. package/src/mocking/legacy/HostRules.ts +43 -0
  76. package/src/mocking/legacy/Http.ts +236 -0
  77. package/src/mocking/legacy/HttpResponse.ts +106 -0
  78. package/src/mocking/legacy/RestApi.ts +68 -0
  79. package/src/mocking/legacy/Urls.ts +44 -0
  80. package/src/mocking/legacy/Variables.ts +53 -0
  81. package/src/models/ArcResponse.ts +166 -0
  82. package/src/models/Authorization.ts +481 -0
  83. package/src/models/AuthorizationData.ts +60 -0
  84. package/src/models/Backend.ts +107 -0
  85. package/src/models/ClientCertificate.ts +68 -0
  86. package/src/models/Environment.ts +279 -0
  87. package/src/models/ErrorResponse.ts +113 -0
  88. package/src/models/HistoryIndex.ts +76 -0
  89. package/src/models/HistoryRequest.ts +28 -0
  90. package/src/models/HostRule.ts +163 -0
  91. package/src/models/HttpCookie.ts +285 -0
  92. package/src/models/HttpProject.ts +1294 -0
  93. package/src/models/HttpProjectListItem.ts +23 -0
  94. package/src/models/HttpRequest.ts +124 -0
  95. package/src/models/HttpResponse.ts +143 -0
  96. package/src/models/License.ts +113 -0
  97. package/src/models/ProjectDefinitionProperty.ts +40 -0
  98. package/src/models/ProjectFolder.ts +439 -0
  99. package/src/models/ProjectItem.ts +135 -0
  100. package/src/models/ProjectParent.ts +113 -0
  101. package/src/models/ProjectRequest.ts +277 -0
  102. package/src/models/ProjectSchema.ts +202 -0
  103. package/src/models/Property.ts +423 -0
  104. package/src/models/Provider.ts +98 -0
  105. package/src/models/README.md +20 -0
  106. package/src/models/Request.ts +452 -0
  107. package/src/models/RequestActions.ts +163 -0
  108. package/src/models/RequestAuthorization.ts +115 -0
  109. package/src/models/RequestConfig.ts +317 -0
  110. package/src/models/RequestLog.ts +159 -0
  111. package/src/models/RequestTime.ts +108 -0
  112. package/src/models/RequestUiMeta.ts +258 -0
  113. package/src/models/RequestsSize.ts +65 -0
  114. package/src/models/ResponseAuthorization.ts +104 -0
  115. package/src/models/ResponseRedirect.ts +158 -0
  116. package/src/models/RevisionInfo.ts +37 -0
  117. package/src/models/SentRequest.ts +125 -0
  118. package/src/models/SerializableError.ts +80 -0
  119. package/src/models/SerializablePayload.ts +68 -0
  120. package/src/models/Server.ts +153 -0
  121. package/src/models/Thing.ts +110 -0
  122. package/src/models/Url.ts +90 -0
  123. package/src/models/User.ts +120 -0
  124. package/src/models/WebApi.ts +234 -0
  125. package/src/models/WebApiIndex.ts +122 -0
  126. package/src/models/Workspace.ts +182 -0
  127. package/src/models/actions/Action.ts +213 -0
  128. package/src/models/actions/ActionView.ts +40 -0
  129. package/src/models/actions/Condition.ts +207 -0
  130. package/src/models/actions/ConditionView.ts +42 -0
  131. package/src/models/actions/Enums.ts +29 -0
  132. package/src/models/actions/RunnableAction.ts +144 -0
  133. package/src/models/actions/runnable/DeleteCookieAction.ts +113 -0
  134. package/src/models/actions/runnable/Runnable.ts +9 -0
  135. package/src/models/actions/runnable/SetCookieAction.ts +216 -0
  136. package/src/models/actions/runnable/SetVariableAction.ts +81 -0
  137. package/src/models/legacy/DataExport.ts +172 -0
  138. package/src/models/legacy/Normalizer.ts +110 -0
  139. package/src/models/legacy/actions/Actions.ts +269 -0
  140. package/src/models/legacy/authorization/Authorization.ts +572 -0
  141. package/src/models/legacy/models/ApiTypes.ts +202 -0
  142. package/src/models/legacy/models/ArcLegacyProject.ts +39 -0
  143. package/src/models/legacy/models/AuthData.ts +17 -0
  144. package/src/models/legacy/models/ClientCertificate.ts +95 -0
  145. package/src/models/legacy/models/Cookies.ts +52 -0
  146. package/src/models/legacy/models/HostRule.ts +35 -0
  147. package/src/models/legacy/models/RestApi.ts +49 -0
  148. package/src/models/legacy/models/UrlHistory.ts +37 -0
  149. package/src/models/legacy/models/Variable.ts +43 -0
  150. package/src/models/legacy/models/base.d.ts +95 -0
  151. package/src/models/legacy/request/ArcRequest.ts +405 -0
  152. package/src/models/legacy/request/ArcResponse.ts +177 -0
  153. package/src/models/legacy/request/HistoryData.ts +47 -0
  154. package/src/models/legacy/request/Legacy.ts +45 -0
  155. package/src/models/legacy/request/RequestBody.ts +87 -0
  156. package/src/models/transformers/ArcDexieTransformer.ts +323 -0
  157. package/src/models/transformers/ArcLegacyNormalizer.ts +85 -0
  158. package/src/models/transformers/ArcLegacyTransformer.ts +200 -0
  159. package/src/models/transformers/ArcPouchTransformer.ts +184 -0
  160. package/src/models/transformers/BaseTransformer.ts +116 -0
  161. package/src/models/transformers/ImportUtils.ts +141 -0
  162. package/src/models/transformers/LegacyDataExportToApiProject.ts +76 -0
  163. package/src/models/transformers/LegacyExportProcessor.ts +252 -0
  164. package/src/models/transformers/PostmanBackupTransformer.ts +306 -0
  165. package/src/models/transformers/PostmanDataTransformer.ts +50 -0
  166. package/src/models/transformers/PostmanTransformer.ts +106 -0
  167. package/src/models/transformers/PostmanV21Transformer.ts +311 -0
  168. package/src/models/transformers/PostmanV2Transformer.ts +308 -0
  169. package/src/models/transformers/har.ts +865 -0
  170. package/src/runtime/actions/ActionRunner.ts +83 -0
  171. package/src/runtime/actions/ConditionRunner.ts +194 -0
  172. package/src/runtime/actions/RunnableCondition.ts +57 -0
  173. package/src/runtime/actions/runnable/ActionRunnable.ts +19 -0
  174. package/src/runtime/actions/runnable/DeleteCookieRunnable.ts +39 -0
  175. package/src/runtime/actions/runnable/SetCookieRunnable.ts +92 -0
  176. package/src/runtime/actions/runnable/SetVariableRunnable.ts +53 -0
  177. package/src/runtime/http-engine/ArcEngine.ts +1068 -0
  178. package/src/runtime/http-engine/FormData.ts +85 -0
  179. package/src/runtime/http-engine/HttpEngine.ts +874 -0
  180. package/src/runtime/http-engine/HttpErrorCodes.ts +270 -0
  181. package/src/runtime/http-engine/NodeEngine.ts +792 -0
  182. package/src/runtime/http-engine/NodeEngineDirect.ts +482 -0
  183. package/src/runtime/http-engine/PayloadSupport.ts +84 -0
  184. package/src/runtime/http-engine/RequestUtils.ts +164 -0
  185. package/src/runtime/http-engine/ntlm/Des.ts +345 -0
  186. package/src/runtime/http-engine/ntlm/MD4.ts +135 -0
  187. package/src/runtime/http-engine/ntlm/NtlmAuth.ts +186 -0
  188. package/src/runtime/http-engine/ntlm/NtlmMessage.ts +57 -0
  189. package/src/runtime/modules/BasicAuthCache.ts +133 -0
  190. package/src/runtime/modules/ExecutionResponse.ts +4 -0
  191. package/src/runtime/modules/ModulesRegistry.ts +136 -0
  192. package/src/runtime/modules/RequestAuthorization.ts +110 -0
  193. package/src/runtime/modules/RequestCookies.ts +145 -0
  194. package/src/runtime/node/ProjectRunner.ts +281 -0
  195. package/src/runtime/node/RequestFactory.ts +422 -0
  196. package/src/runtime/node/VariablesStore.ts +25 -0
  197. package/src/runtime/store/StoreSdk.ts +838 -0
  198. package/src/runtime/variables/Cache.ts +53 -0
  199. package/src/runtime/variables/EvalFunctions.ts +132 -0
  200. package/src/runtime/variables/ProjectVariables.ts +6 -0
  201. package/src/runtime/variables/VariablesProcessor.ts +543 -0
  202. package/src/runtime/variables/VariablesTokenizer.ts +55 -0
  203. package/build/src/runtime/http-engine/Errors.d.ts +0 -10
  204. package/build/src/runtime/http-engine/Errors.js +0 -14
  205. package/build/src/runtime/http-engine/Errors.js.map +0 -1
@@ -0,0 +1,874 @@
1
+ /* eslint-disable no-redeclare */
2
+ /* eslint-disable no-unused-vars */
3
+ import { URL } from 'url';
4
+ import zlib from 'zlib';
5
+ import tls from 'tls';
6
+ import net from 'net';
7
+ import http from 'http';
8
+ import { EventEmitter } from 'events';
9
+ import { IHttpRequest, HttpRequest } from '../../models/HttpRequest.js';
10
+ import { IRequestBaseConfig } from '../../models/RequestConfig.js';
11
+ import { IRequestAuthorization } from '../../models/RequestAuthorization.js';
12
+ import { HostRule } from '../../models/HostRule.js';
13
+ import { IRequestCertificate } from '../../models/ClientCertificate.js';
14
+ import { SentRequest } from '../../models/SentRequest.js';
15
+ import { ArcResponse } from '../../models/ArcResponse.js';
16
+ import { ErrorResponse } from '../../models/ErrorResponse.js';
17
+ import { RequestsSize } from '../../models/RequestsSize.js';
18
+ import { HttpResponse } from '../../models/HttpResponse.js';
19
+ import { ResponseRedirect } from '../../models/ResponseRedirect.js';
20
+ import { RequestLog, IRequestLog } from '../../models/RequestLog.js';
21
+ import { RequestTime } from '../../models/RequestTime.js';
22
+ import { SerializableError } from '../../models/SerializableError.js';
23
+ import { ResponseAuthorization } from '../../models/ResponseAuthorization.js';
24
+ import { DefaultLogger } from '../../lib/logging/DefaultLogger.js';
25
+ import { ILogger, Logger } from '../../lib/logging/Logger.js';
26
+ import { Headers } from '../../lib/headers/Headers.js';
27
+ import * as RequestUtils from './RequestUtils.js';
28
+ import { Cookies } from '../../lib/cookies/Cookies.js';
29
+ import { HttpErrorCodes } from './HttpErrorCodes.js';
30
+
31
+ export interface HttpEngineOptions extends IRequestBaseConfig {
32
+ /**
33
+ * The authorization configuration to apply to the request.
34
+ */
35
+ authorization?: IRequestAuthorization[];
36
+ /**
37
+ * Logger object.
38
+ */
39
+ logger?: ILogger;
40
+ /**
41
+ * A certificate to use with the request.
42
+ */
43
+ certificates?: IRequestCertificate[];
44
+ }
45
+
46
+ export interface RequestStats {
47
+ firstReceiveTime?: number;
48
+ lastReceivedTime?: number;
49
+ messageStart?: number;
50
+ sentTime?: number;
51
+ connectionTime?: number;
52
+ lookupTime?: number;
53
+ connectedTime?: number;
54
+ secureStartTime?: number;
55
+ secureConnectedTime?: number;
56
+ startTime?: number;
57
+ responseTime?: number;
58
+ receivingTime?: number;
59
+ }
60
+
61
+ export interface ResponseErrorInit {
62
+ code?: number | string;
63
+ message?: string;
64
+ }
65
+
66
+ export interface BeforeRedirectDetail {
67
+ location: string;
68
+ returnValue: boolean;
69
+ }
70
+ export interface HeadersReceivedDetail {
71
+ value: string;
72
+ returnValue: boolean;
73
+ }
74
+
75
+ export interface IRequestAuthState {
76
+ method: 'ntlm';
77
+ state: number;
78
+ headers?: string;
79
+ challengeHeader?: string;
80
+ }
81
+
82
+ export declare interface HttpEngine {
83
+ /**
84
+ * Dispatched before a redirect occurs.
85
+ * The detail object on the listener's only argument has the `location` property
86
+ * with the new location for the request.
87
+ * When the `returnValue` is set to `false` the request is cancelled.
88
+ */
89
+ on(event: 'beforeredirect', listener: (detail: BeforeRedirectDetail) => void): this;
90
+ on(event: 'loadstart', listener: () => void): this;
91
+ on(event: 'firstbyte', listener: () => void): this;
92
+ on(event: 'headersreceived', listener: (detail: HeadersReceivedDetail) => void): this;
93
+ on(event: 'loadend', listener: () => void): this;
94
+ /**
95
+ * Dispatched before a redirect occurs.
96
+ * The detail object on the listener's only argument has the `location` property
97
+ * with the new location for the request.
98
+ * When the `returnValue` is set to `false` the request is cancelled.
99
+ */
100
+ once(event: 'beforeredirect', listener: (detail: BeforeRedirectDetail) => void): this;
101
+ once(event: 'loadstart', listener: () => void): this;
102
+ once(event: 'firstbyte', listener: () => void): this;
103
+ once(event: 'headersreceived', listener: (detail: HeadersReceivedDetail) => void): this;
104
+ once(event: 'loadend', listener: () => void): this;
105
+ }
106
+
107
+ export const mainPromiseSymbol = Symbol('mainPromise');
108
+
109
+ /**
110
+ * The base class to make HTTP requests in API Client.
111
+ * This can be extended to support particular implementations.
112
+ */
113
+ export abstract class HttpEngine extends EventEmitter {
114
+ request: HttpRequest;
115
+ opts: HttpEngineOptions;
116
+ logger: Logger;
117
+
118
+ /**
119
+ * The current sent request
120
+ */
121
+ sentRequest: SentRequest;
122
+ redirects: ResponseRedirect[] = [];
123
+ /**
124
+ * When true the request has been aborted.
125
+ */
126
+ aborted = false;
127
+ /**
128
+ * Parsed value of the request URL.
129
+ */
130
+ uri: URL;
131
+
132
+ socket?: net.Socket;
133
+ /**
134
+ * Host header can be different than registered URL because of
135
+ * `hosts` rules.
136
+ * If a rule changes host value of the URL the original URL host value
137
+ * is used when generating the request and not overwritten one.
138
+ * This way virtual hosts can be tested using hosts.
139
+ */
140
+ hostHeader: string | undefined;
141
+
142
+ protected hostTestReg = /^\s*host\s*:/im;
143
+ /**
144
+ * Set when the request is redirected.
145
+ */
146
+ redirecting = false;
147
+
148
+ /**
149
+ * The response headers.
150
+ * The object may be empty when the response is not set.
151
+ */
152
+ currentHeaders = new Headers();
153
+
154
+ /**
155
+ * The response object build during the execution.
156
+ */
157
+ currentResponse?: ArcResponse;
158
+
159
+ /**
160
+ * @return True if following redirects is allowed.
161
+ */
162
+ get followRedirects(): boolean {
163
+ const { opts } = this;
164
+ if (typeof opts.followRedirects === 'boolean') {
165
+ return opts.followRedirects;
166
+ }
167
+ return true;
168
+ }
169
+
170
+ /**
171
+ * The request timeout.
172
+ */
173
+ get timeout(): number {
174
+ const { opts } = this;
175
+ if (typeof opts.timeout === 'number') {
176
+ return opts.timeout;
177
+ }
178
+ return 0;
179
+ }
180
+
181
+ /**
182
+ * Keeps the raw body in a temporary buffer while processing the response.
183
+ */
184
+ _rawBody?: Buffer;
185
+
186
+ stats: RequestStats = {};
187
+
188
+ auth?: IRequestAuthState;
189
+
190
+ protected mainResolver?: (log: IRequestLog) => void;
191
+ protected mainRejecter?: (err: SerializableError) => void;
192
+ [mainPromiseSymbol]?: Promise<IRequestLog>;
193
+
194
+ constructor(request: IHttpRequest, opts: HttpEngineOptions = {}) {
195
+ super();
196
+ this.request = new HttpRequest({ ...request });
197
+ this.opts = opts;
198
+ this.logger = this.setupLogger(opts);
199
+ this.sentRequest = new SentRequest({ ...request, startTime: Date.now() });
200
+ this.uri = this.readUrl(request.url);
201
+ this.hostHeader = RequestUtils.getHostHeader(request.url);
202
+ }
203
+
204
+ /**
205
+ * Creates a logger object to log debug output.
206
+ */
207
+ setupLogger(opts: HttpEngineOptions = {}): Logger {
208
+ if (opts.logger) {
209
+ return opts.logger;
210
+ }
211
+ return new DefaultLogger();
212
+ }
213
+
214
+ /**
215
+ * Updates the `uri` property from current request URL
216
+ * @param value The request URL
217
+ */
218
+ readUrl(value: string): URL {
219
+ const { hosts = [] } = this.opts;
220
+ const instances = hosts.map(i => new HostRule(i));
221
+ value = HostRule.applyHosts(value, instances);
222
+ try {
223
+ return new URL(value);
224
+ } catch (e) {
225
+ throw new Error(`Unable to parse the URL: ${value}`);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Aborts current request.
231
+ * It emits `error` event
232
+ */
233
+ abort(): void {
234
+ this.aborted = true;
235
+ if (!this.socket) {
236
+ return;
237
+ }
238
+ this.socket.removeAllListeners();
239
+ if (this.socket.destroyed) {
240
+ this.socket = undefined;
241
+ return;
242
+ }
243
+ this.socket.pause();
244
+ this.socket.destroy();
245
+ this.socket = undefined;
246
+ }
247
+
248
+ /**
249
+ * Sends the request.
250
+ */
251
+ abstract send(): Promise<IRequestLog>;
252
+
253
+ /**
254
+ * Decompresses received body if `content-encoding` header is set.
255
+ *
256
+ * @param body A body buffer to decompress.
257
+ * @return Promise resolved to parsed body
258
+ */
259
+ async decompress(body?: Buffer): Promise<Buffer | undefined> {
260
+ const { aborted, currentHeaders } = this;
261
+ if (aborted || !currentHeaders || !body) {
262
+ return;
263
+ }
264
+ const ce = currentHeaders.get('content-encoding');
265
+ if (!ce) {
266
+ return body;
267
+ }
268
+ if (ce.indexOf('deflate') !== -1) {
269
+ return this.inflate(body);
270
+ }
271
+ if (ce.indexOf('gzip') !== -1) {
272
+ return this.gunzip(body);
273
+ }
274
+ if (ce.indexOf('br') !== -1) {
275
+ return this.brotli(body);
276
+ }
277
+ return body;
278
+ }
279
+
280
+ /**
281
+ * Decompress body with Inflate.
282
+ * @param body Received response payload
283
+ * @return Promise resolved to decompressed buffer.
284
+ */
285
+ inflate(body: Buffer): Promise<Buffer> {
286
+ body = Buffer.from(body);
287
+ return new Promise((resolve, reject) => {
288
+ zlib.inflate(body, (err, buffer) => {
289
+ if (err) {
290
+ reject(new Error(err.message || String(err)));
291
+ } else {
292
+ resolve(buffer);
293
+ }
294
+ });
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Decompress body with ZLib.
300
+ * @param body Received response payload
301
+ * @return Promise resolved to decompressed buffer.
302
+ */
303
+ gunzip(body: Buffer): Promise<Buffer> {
304
+ body = Buffer.from(body);
305
+ return new Promise((resolve, reject) => {
306
+ zlib.gunzip(body, (err, buffer) => {
307
+ if (err) {
308
+ reject(new Error(err.message || String(err)));
309
+ } else {
310
+ resolve(buffer);
311
+ }
312
+ });
313
+ });
314
+ }
315
+
316
+ /**
317
+ * Decompress Brotli.
318
+ * @param body Received response payload
319
+ * @return Promise resolved to decompressed buffer.
320
+ */
321
+ brotli(body: Buffer): Promise<Buffer> {
322
+ body = Buffer.from(body);
323
+ return new Promise((resolve, reject) => {
324
+ zlib.brotliDecompress(body, (err, buffer) => {
325
+ if (err) {
326
+ reject(err);
327
+ } else {
328
+ resolve(buffer);
329
+ }
330
+ });
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Prepares headers list to be send to the remote machine.
336
+ * If `defaultHeaders` option is set then it adds `user-agent` and `accept`
337
+ * headers.
338
+ * @param headers Parsed headers
339
+ */
340
+ prepareHeaders(headers: Headers): void {
341
+ if (!this.opts.defaultHeaders) {
342
+ return;
343
+ }
344
+ if (!headers.has('user-agent')) {
345
+ if (this.opts.defaultUserAgent) {
346
+ headers.set('user-agent', this.opts.defaultUserAgent);
347
+ } else {
348
+ headers.set('user-agent', 'api client');
349
+ }
350
+ }
351
+ if (!headers.has('accept')) {
352
+ if (this.opts.defaultAccept) {
353
+ headers.set('accept', this.opts.defaultAccept);
354
+ } else {
355
+ headers.set('accept', '*/*');
356
+ }
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Cleans the state after finished.
362
+ */
363
+ _cleanUp(): void {
364
+ this.redirects = [];
365
+ this.currentHeaders = new Headers();
366
+ this.currentResponse = undefined;
367
+ this._rawBody = undefined;
368
+ this.redirecting = false;
369
+ this.stats = {};
370
+ this.mainRejecter = undefined;
371
+ this.mainResolver = undefined;
372
+ this._clearSocketEventListeners();
373
+ }
374
+
375
+ /**
376
+ * Cleans up the state for redirect.
377
+ */
378
+ _cleanUpRedirect(): void {
379
+ this.currentHeaders = new Headers();
380
+ this.currentResponse = undefined;
381
+ this._rawBody = undefined;
382
+ this.stats = {};
383
+ this._clearSocketEventListeners();
384
+ }
385
+
386
+ /**
387
+ * Reports response when redirected.
388
+ * @param Received status code
389
+ * @return True if the request has been redirected.
390
+ */
391
+ _reportRedirect(status: number): boolean {
392
+ const { request, currentHeaders } = this;
393
+ if (!currentHeaders) {
394
+ return false;
395
+ }
396
+ const rerUrl = currentHeaders.get('location');
397
+ // https://github.com/jarrodek/socket-fetch/issues/13
398
+ const redirectOptions = RequestUtils.redirectOptions(status, request.method, rerUrl);
399
+ if (!redirectOptions.redirect) {
400
+ return false;
401
+ }
402
+ this.redirecting = true;
403
+ setTimeout(() => this._redirectRequest(redirectOptions));
404
+ return true;
405
+ }
406
+
407
+ /**
408
+ * Creates a response and adds it to the redirects list and redirects
409
+ * the request to the new location.
410
+ */
411
+ async _redirectRequest(options: RequestUtils.RedirectOptions): Promise<void> {
412
+ if (this.followRedirects === false) {
413
+ this._publishResponse();
414
+ return;
415
+ }
416
+ const location = options.location && RequestUtils.getRedirectLocation(options.location, this.request.url);
417
+ if (!location) {
418
+ this._errorRequest({ code: 302 });
419
+ return;
420
+ }
421
+
422
+ // check if this is infinite loop
423
+ if (RequestUtils.isRedirectLoop(location, this.redirects)) {
424
+ this._errorRequest({ code: 310 });
425
+ return;
426
+ }
427
+
428
+ const detail: BeforeRedirectDetail = {
429
+ location,
430
+ returnValue: true,
431
+ };
432
+ this.emit('beforeredirect', detail);
433
+ if (!detail.returnValue) {
434
+ this._publishResponse();
435
+ return;
436
+ }
437
+ let responseCookies;
438
+ if (this.currentHeaders.has('set-cookie')) {
439
+ responseCookies = this.currentHeaders.get('set-cookie');
440
+ }
441
+ try {
442
+ const response = await this._createRedirectResponse(location);
443
+ this.redirects.push(response);
444
+ this._cleanUpRedirect();
445
+ if (responseCookies) {
446
+ this._processRedirectCookies(responseCookies, location);
447
+ }
448
+ this.redirecting = false;
449
+
450
+ this.request.url = location;
451
+ this.sentRequest.url = location;
452
+ if (options.forceGet) {
453
+ this.request.method = 'GET';
454
+ }
455
+ this.uri = this.readUrl(location);
456
+ this.hostHeader = RequestUtils.getHostHeader(location);
457
+ // No idea why but without setTimeout the program loses it's
458
+ // scope after calling the function.
459
+ setTimeout(() => this.send());
460
+ } catch (e) {
461
+ const error = e as Error;
462
+ this._errorRequest({
463
+ message: (error && error.message) || 'Unknown error occurred',
464
+ });
465
+ }
466
+ }
467
+
468
+ /**
469
+ * @param location The redirect location.
470
+ * @return Redirect response object
471
+ */
472
+ async _createRedirectResponse(location: string): Promise<ResponseRedirect> {
473
+ const { currentResponse = new ArcResponse() } = this;
474
+
475
+ const response = HttpResponse.fromValues(
476
+ currentResponse.status,
477
+ currentResponse.statusText,
478
+ currentResponse.headers,
479
+ );
480
+ if (currentResponse.payload) {
481
+ response.payload = currentResponse.payload;
482
+ }
483
+
484
+ const body = await this.decompress(this._rawBody);
485
+ if (body) {
486
+ await response.writePayload(body);
487
+ currentResponse.payload = response.payload;
488
+ }
489
+
490
+ const redirect = ResponseRedirect.fromValues(location, response.toJSON(), this.stats.startTime, this.stats.responseTime);
491
+ redirect.timings = this._computeStats(this.stats);
492
+
493
+ return redirect;
494
+ }
495
+
496
+ /**
497
+ * Creates a response object
498
+ *
499
+ * @return A response object.
500
+ */
501
+ async _createResponse(): Promise<ArcResponse | undefined> {
502
+ if (this.aborted) {
503
+ return;
504
+ }
505
+ const { currentResponse } = this;
506
+ if (!currentResponse) {
507
+ throw new Error(`Tried to create a response but no response data is set.`);
508
+ }
509
+ const { status } = currentResponse;
510
+ if (status === undefined) {
511
+ throw new Error(`The response status is empty.
512
+ It means that the successful connection wasn't made.
513
+ Check your request parameters.`);
514
+ }
515
+ const body = await this.decompress(this._rawBody);
516
+ const response = ArcResponse.fromValues(
517
+ status,
518
+ currentResponse.statusText,
519
+ currentResponse.headers,
520
+ );
521
+ response.timings = this._computeStats(this.stats);
522
+ response.loadingTime = response.timings.total();
523
+
524
+ // const response = /** @type ArcResponse */ ({
525
+ // size: {
526
+ // request: 0,
527
+ // response: 0,
528
+ // },
529
+ // });
530
+
531
+ if (body) {
532
+ await response.writePayload(body);
533
+ currentResponse.payload = response.payload;
534
+ }
535
+ // if (body) {
536
+ // response.payload = body;
537
+ // currentResponse.payload = body;
538
+ // response.size.response = body.length;
539
+ // }
540
+ // if (this.sentRequest.httpMessage) {
541
+ // response.size.request = Buffer.from(this.sentRequest.httpMessage).length;
542
+ // }
543
+ // if (opts.includeRedirects && this.redirects.length) {
544
+ // response.redirects = Array.from(this.redirects);
545
+ // }
546
+ if (status === 401) {
547
+ response.auth = this._getAuth();
548
+ }
549
+ return response;
550
+ }
551
+
552
+ /**
553
+ * Finishes the response with error message.
554
+ */
555
+ _errorRequest(opts: ResponseErrorInit): void {
556
+ const { currentResponse } = this;
557
+ this.aborted = true;
558
+ let message;
559
+ if (opts.code && !opts.message) {
560
+ message = HttpErrorCodes.getCodeMessage(opts.code);
561
+ } else if (opts.message) {
562
+ message = opts.message;
563
+ }
564
+ message = message || 'Unknown error occurred';
565
+ const error = new SerializableError(message, opts.code);
566
+ const log = RequestLog.fromRequest(this.sentRequest);
567
+ const response = ErrorResponse.fromError(error);
568
+ log.response = response;
569
+ if (currentResponse && currentResponse.status) {
570
+ response.status = currentResponse.status;
571
+ response.statusText = currentResponse.statusText;
572
+ response.headers = currentResponse.headers;
573
+ response.payload = currentResponse.payload;
574
+ }
575
+ this.finalizeRequest(log);
576
+ this._cleanUp();
577
+ }
578
+
579
+ /**
580
+ * Generates authorization info object from response.
581
+ */
582
+ _getAuth(): ResponseAuthorization {
583
+ let auth = this.currentHeaders.get('www-authenticate');
584
+ const result = new ResponseAuthorization();
585
+ if (auth) {
586
+ auth = auth.toLowerCase();
587
+ if (auth.indexOf('ntlm') !== -1) {
588
+ result.method = 'ntlm';
589
+ } else if (auth.indexOf('basic') !== -1) {
590
+ result.method = 'basic';
591
+ } else if (auth.indexOf('digest') !== -1) {
592
+ result.method = 'digest';
593
+ }
594
+ }
595
+ return result;
596
+ }
597
+
598
+ /**
599
+ * Generate response object and publish it to the listeners.
600
+ */
601
+ async _publishResponse(): Promise<void> {
602
+ if (this.aborted) {
603
+ return;
604
+ }
605
+ try {
606
+ const response = await this._createResponse();
607
+ if (!response) {
608
+ return;
609
+ }
610
+ const result = RequestLog.fromRequestResponse(this.sentRequest, response.toJSON());
611
+ if (this.redirects.length) {
612
+ result.redirects = this.redirects;
613
+ }
614
+ result.size = new RequestsSize();
615
+ if (this.sentRequest.httpMessage) {
616
+ result.size.request = Buffer.from(this.sentRequest.httpMessage).length;
617
+ }
618
+ if (response.payload) {
619
+ if (typeof response.payload === 'string') {
620
+ result.size.response = response.payload.length;
621
+ } else {
622
+ result.size.response = response.payload.data.length;
623
+ }
624
+ }
625
+ this.finalizeRequest(result);
626
+ } catch (e) {
627
+ const error = e as Error;
628
+ console.error(error);
629
+ this._errorRequest({
630
+ message: (error && error.message) || 'Unknown error occurred',
631
+ });
632
+ }
633
+ this.abort();
634
+ this._cleanUp();
635
+ }
636
+
637
+ /**
638
+ * Creates HAR 1.2 timings object from stats.
639
+ * @param stats Timings object
640
+ */
641
+ _computeStats(stats: RequestStats): RequestTime {
642
+ const {
643
+ sentTime,
644
+ messageStart,
645
+ connectionTime=0,
646
+ lookupTime=0,
647
+ connectedTime,
648
+ secureStartTime,
649
+ secureConnectedTime,
650
+ lastReceivedTime,
651
+ firstReceiveTime,
652
+ receivingTime,
653
+ } = stats;
654
+ // in case the `send` event was not handled we use the `messageStart` as this is set when the request is created.
655
+ const adjustedSentTime = sentTime || messageStart;
656
+ // when there was no body we check when the end time.
657
+ const adjustedLastReceivedTime = lastReceivedTime || receivingTime;
658
+ const adjustedLookupTime = lookupTime || messageStart;
659
+ let send = adjustedSentTime && messageStart ? adjustedSentTime - messageStart : -1;
660
+ if (send < 0) {
661
+ send = 0;
662
+ }
663
+ const dns = lookupTime ? lookupTime - connectionTime : -1;
664
+ const connect = connectedTime && adjustedLookupTime ? connectedTime - adjustedLookupTime : -1;
665
+ let receive = adjustedLastReceivedTime && firstReceiveTime ? adjustedLastReceivedTime - firstReceiveTime : -1;
666
+ if (receive < 0) {
667
+ receive = 0;
668
+ }
669
+ let wait = firstReceiveTime && adjustedSentTime ? firstReceiveTime - adjustedSentTime : -1;
670
+ if (wait < 0) {
671
+ wait = 0;
672
+ }
673
+ let ssl = -1;
674
+ if (typeof secureStartTime === 'number' && typeof secureConnectedTime === 'number') {
675
+ ssl = secureConnectedTime - secureStartTime;
676
+ }
677
+ const result = new RequestTime();
678
+ result.blocked = 0;
679
+ result.connect = connect;
680
+ result.receive = receive;
681
+ result.send = send;
682
+ result.wait = wait;
683
+ result.dns = dns;
684
+ result.ssl = ssl;
685
+ return result;
686
+ }
687
+
688
+ /**
689
+ * Handles cookie exchange when redirecting the request.
690
+ * @param responseCookies Cookies received in the response
691
+ * @param location Redirect destination
692
+ */
693
+ _processRedirectCookies(responseCookies: string, location: string): void {
694
+ let newParser = new Cookies(responseCookies, location);
695
+ newParser.filter();
696
+ const expired = newParser.clearExpired();
697
+ const headers = new Headers(this.request.headers);
698
+ const hasCookie = headers.has('cookie');
699
+ if (hasCookie) {
700
+ const oldCookies = headers.get('cookie');
701
+ const oldParser = new Cookies(oldCookies, location);
702
+ oldParser.filter();
703
+ oldParser.clearExpired();
704
+ oldParser.merge(newParser);
705
+ newParser = oldParser;
706
+ // remove expired from the new response.
707
+ newParser.cookies = newParser.cookies.filter((c) => {
708
+ for (let i = 0, len = expired.length; i < len; i++) {
709
+ if (expired[i].name === c.name) {
710
+ return false;
711
+ }
712
+ }
713
+ return true;
714
+ });
715
+ }
716
+ const str = newParser.toString(true);
717
+ if (str) {
718
+ headers.set('cookie', str);
719
+ } else if (hasCookie) {
720
+ headers.delete('cookie');
721
+ }
722
+ this.request.headers = headers.toString();
723
+ }
724
+
725
+ /**
726
+ * Checks certificate identity using TLS api.
727
+ *
728
+ * @param host Request host name
729
+ * @param cert TLS certificate info object
730
+ */
731
+ _checkServerIdentity(host: string, cert: tls.PeerCertificate): Error|undefined {
732
+ const err = tls.checkServerIdentity(host, cert);
733
+ if (err) {
734
+ return err;
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Clears event listeners of the socket object,
740
+ */
741
+ _clearSocketEventListeners(): void {
742
+ if (!this.socket) {
743
+ return;
744
+ }
745
+ this.socket.removeAllListeners('error');
746
+ this.socket.removeAllListeners('timeout');
747
+ this.socket.removeAllListeners('end');
748
+ }
749
+
750
+ /**
751
+ * Adds client certificate to the request configuration options.
752
+ *
753
+ * @param certificate List of certificate configurations.
754
+ * @param options Request options. Cert agent options are added to this object.
755
+ */
756
+ _addClientCertificate(certificate: IRequestCertificate, options: tls.ConnectionOptions): void {
757
+ if (!certificate) {
758
+ return;
759
+ }
760
+ const cert = { ...certificate };
761
+ if (!Array.isArray(cert.cert)) {
762
+ cert.cert = [cert.cert];
763
+ }
764
+ if (cert.type === 'p12') {
765
+ if (!options.pfx) {
766
+ options.pfx = [];
767
+ }
768
+ const added = cert.cert.map((item) => {
769
+ const struct: tls.PxfObject = {
770
+ buf: Buffer.from(item.data as string),
771
+ };
772
+ if (item.passphrase) {
773
+ struct.passphrase = item.passphrase;
774
+ }
775
+ return struct;
776
+ });
777
+ options.pfx = (options.pfx as tls.PxfObject[]).concat(added);
778
+ } else if (cert.type === 'pem') {
779
+ if (!options.cert) {
780
+ options.cert = [];
781
+ }
782
+ const added = cert.cert.map((item) => Buffer.from(item.data as string));
783
+ options.cert = (options.cert as Buffer[]).concat(added);
784
+ if (cert.key) {
785
+ if (!Array.isArray(cert.key)) {
786
+ cert.key = [cert.key];
787
+ }
788
+ if (!options.key) {
789
+ options.key = [];
790
+ }
791
+ const keys = cert.key.map((item) => {
792
+ const struct: tls.KeyObject = {
793
+ pem: Buffer.from(item.data as string),
794
+ };
795
+ if (item.passphrase) {
796
+ struct.passphrase = item.passphrase;
797
+ }
798
+ return struct;
799
+ });
800
+ options.key = (options.key as tls.KeyObject[]).concat(keys);
801
+ }
802
+ }
803
+ }
804
+
805
+ /**
806
+ * @return Proxy authorization header value, when defined.
807
+ */
808
+ _proxyAuthHeader(): string|undefined {
809
+ const { proxyUsername, proxyPassword='' } = this.opts;
810
+ if (!proxyUsername) {
811
+ return undefined;
812
+ }
813
+ const auth = `${proxyUsername}:${proxyPassword}`;
814
+ const hash = Buffer.from(auth).toString('base64');
815
+ return `Basic ${hash}`;
816
+ }
817
+
818
+ /**
819
+ * Reads the raw headers from the node response and transforms them into the internal headers.
820
+ */
821
+ computeResponseHeaders(res: http.IncomingMessage): Headers {
822
+ const headers: Record<string, string> = {};
823
+ for (let i = 0, len = res.rawHeaders.length; i < len; i += 2) {
824
+ const name = res.rawHeaders[i];
825
+ const value = res.rawHeaders[i + 1];
826
+ if (headers[name]) {
827
+ headers[name] += `; ${value}`;
828
+ } else {
829
+ headers[name] = value;
830
+ }
831
+ }
832
+ return new Headers(headers);
833
+ }
834
+
835
+ /**
836
+ * Called with the `send()` function to initialize the main promise returned by the send function.
837
+ * The send function returns a promise that is resolved when the request finish.
838
+ */
839
+ protected wrapExecution(): Promise<IRequestLog> {
840
+ let promise: Promise<IRequestLog>;
841
+ if (this[mainPromiseSymbol]) {
842
+ promise = this[mainPromiseSymbol] as Promise<IRequestLog>;
843
+ } else {
844
+ promise = new Promise((resolve, reject) => {
845
+ this.mainResolver = resolve;
846
+ this.mainRejecter = reject;
847
+ });
848
+ this[mainPromiseSymbol] = promise;
849
+ }
850
+ return promise;
851
+ }
852
+
853
+ /**
854
+ * Called by the request finalizer or error finalized to report the response.
855
+ *
856
+ * @param log Either the request execution log or an error.
857
+ */
858
+ protected finalizeRequest(log: RequestLog | SerializableError): void {
859
+ const { mainRejecter, mainResolver } = this;
860
+ if (!mainRejecter || !mainResolver) {
861
+ // console.error(`Trying to finalize the request but the main resolver is not set.`);
862
+ return;
863
+ }
864
+
865
+ if (log instanceof SerializableError) {
866
+ mainRejecter(log);
867
+ } else {
868
+ mainResolver(log.toJSON());
869
+ }
870
+ this.mainRejecter = undefined;
871
+ this.mainResolver = undefined;
872
+ this[mainPromiseSymbol] = undefined;
873
+ }
874
+ }