UNPKG

12 kBJavaScriptView Raw
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License. See License.txt in the project root for license information.
3import { __assign, __awaiter, __generator } from "tslib";
4import AbortController from "abort-controller";
5import FormData from "form-data";
6import { HttpHeaders } from "./httpHeaders";
7import { RestError } from "./restError";
8import { Transform } from "stream";
9var FetchHttpClient = /** @class */ (function () {
10 function FetchHttpClient() {
11 }
12 FetchHttpClient.prototype.sendRequest = function (httpRequest) {
13 return __awaiter(this, void 0, void 0, function () {
14 var abortController, abortListener, formData, requestForm_1, appendFormValue, _i, _a, formKey, formValue, j, contentType, body, loadedBytes_1, uploadReportStream, platformSpecificRequestInit, requestInit, operationResponse, response, headers, _b, _c, onDownloadProgress_1, responseBody, loadedBytes_2, downloadReportStream, length_1, error_1, fetchError, uploadStreamDone, downloadStreamDone;
15 return __generator(this, function (_d) {
16 switch (_d.label) {
17 case 0:
18 if (!httpRequest && typeof httpRequest !== "object") {
19 throw new Error("'httpRequest' (WebResource) cannot be null or undefined and must be of type object.");
20 }
21 abortController = new AbortController();
22 if (httpRequest.abortSignal) {
23 if (httpRequest.abortSignal.aborted) {
24 throw new RestError("The request was aborted", RestError.REQUEST_ABORTED_ERROR, undefined, httpRequest);
25 }
26 abortListener = function (event) {
27 if (event.type === "abort") {
28 abortController.abort();
29 }
30 };
31 httpRequest.abortSignal.addEventListener("abort", abortListener);
32 }
33 if (httpRequest.timeout) {
34 setTimeout(function () {
35 abortController.abort();
36 }, httpRequest.timeout);
37 }
38 if (httpRequest.formData) {
39 formData = httpRequest.formData;
40 requestForm_1 = new FormData();
41 appendFormValue = function (key, value) {
42 // value function probably returns a stream so we can provide a fresh stream on each retry
43 if (typeof value === "function") {
44 value = value();
45 }
46 if (value && value.hasOwnProperty("value") && value.hasOwnProperty("options")) {
47 requestForm_1.append(key, value.value, value.options);
48 }
49 else {
50 requestForm_1.append(key, value);
51 }
52 };
53 for (_i = 0, _a = Object.keys(formData); _i < _a.length; _i++) {
54 formKey = _a[_i];
55 formValue = formData[formKey];
56 if (Array.isArray(formValue)) {
57 for (j = 0; j < formValue.length; j++) {
58 appendFormValue(formKey, formValue[j]);
59 }
60 }
61 else {
62 appendFormValue(formKey, formValue);
63 }
64 }
65 httpRequest.body = requestForm_1;
66 httpRequest.formData = undefined;
67 contentType = httpRequest.headers.get("Content-Type");
68 if (contentType && contentType.indexOf("multipart/form-data") !== -1) {
69 if (typeof requestForm_1.getBoundary === "function") {
70 httpRequest.headers.set("Content-Type", "multipart/form-data; boundary=" + requestForm_1.getBoundary());
71 }
72 else {
73 // browser will automatically apply a suitable content-type header
74 httpRequest.headers.remove("Content-Type");
75 }
76 }
77 }
78 body = httpRequest.body
79 ? typeof httpRequest.body === "function"
80 ? httpRequest.body()
81 : httpRequest.body
82 : undefined;
83 if (httpRequest.onUploadProgress && httpRequest.body) {
84 loadedBytes_1 = 0;
85 uploadReportStream = new Transform({
86 transform: function (chunk, _encoding, callback) {
87 loadedBytes_1 += chunk.length;
88 httpRequest.onUploadProgress({ loadedBytes: loadedBytes_1 });
89 callback(undefined, chunk);
90 },
91 });
92 if (isReadableStream(body)) {
93 body.pipe(uploadReportStream);
94 }
95 else {
96 uploadReportStream.end(body);
97 }
98 body = uploadReportStream;
99 }
100 return [4 /*yield*/, this.prepareRequest(httpRequest)];
101 case 1:
102 platformSpecificRequestInit = _d.sent();
103 requestInit = __assign({ body: body, headers: httpRequest.headers.rawHeaders(), method: httpRequest.method, signal: abortController.signal, redirect: "manual" }, platformSpecificRequestInit);
104 _d.label = 2;
105 case 2:
106 _d.trys.push([2, 8, 9, 10]);
107 return [4 /*yield*/, this.fetch(httpRequest.url, requestInit)];
108 case 3:
109 response = _d.sent();
110 headers = parseHeaders(response.headers);
111 _b = {
112 headers: headers,
113 request: httpRequest,
114 status: response.status,
115 readableStreamBody: httpRequest.streamResponseBody
116 ? response.body
117 : undefined
118 };
119 if (!!httpRequest.streamResponseBody) return [3 /*break*/, 5];
120 return [4 /*yield*/, response.text()];
121 case 4:
122 _c = _d.sent();
123 return [3 /*break*/, 6];
124 case 5:
125 _c = undefined;
126 _d.label = 6;
127 case 6:
128 operationResponse = (_b.bodyAsText = _c,
129 _b.redirected = response.redirected,
130 _b.url = response.url,
131 _b);
132 onDownloadProgress_1 = httpRequest.onDownloadProgress;
133 if (onDownloadProgress_1) {
134 responseBody = response.body || undefined;
135 if (isReadableStream(responseBody)) {
136 loadedBytes_2 = 0;
137 downloadReportStream = new Transform({
138 transform: function (chunk, _encoding, callback) {
139 loadedBytes_2 += chunk.length;
140 onDownloadProgress_1({ loadedBytes: loadedBytes_2 });
141 callback(undefined, chunk);
142 },
143 });
144 responseBody.pipe(downloadReportStream);
145 operationResponse.readableStreamBody = downloadReportStream;
146 }
147 else {
148 length_1 = parseInt(headers.get("Content-Length")) || undefined;
149 if (length_1) {
150 // Calling callback for non-stream response for consistency with browser
151 onDownloadProgress_1({ loadedBytes: length_1 });
152 }
153 }
154 }
155 return [4 /*yield*/, this.processRequest(operationResponse)];
156 case 7:
157 _d.sent();
158 return [2 /*return*/, operationResponse];
159 case 8:
160 error_1 = _d.sent();
161 fetchError = error_1;
162 if (fetchError.code === "ENOTFOUND") {
163 throw new RestError(fetchError.message, RestError.REQUEST_SEND_ERROR, undefined, httpRequest);
164 }
165 else if (fetchError.type === "aborted") {
166 throw new RestError("The request was aborted", RestError.REQUEST_ABORTED_ERROR, undefined, httpRequest);
167 }
168 throw fetchError;
169 case 9:
170 // clean up event listener
171 if (httpRequest.abortSignal && abortListener) {
172 uploadStreamDone = Promise.resolve();
173 if (isReadableStream(body)) {
174 uploadStreamDone = isStreamComplete(body);
175 }
176 downloadStreamDone = Promise.resolve();
177 if (isReadableStream(operationResponse === null || operationResponse === void 0 ? void 0 : operationResponse.readableStreamBody)) {
178 downloadStreamDone = isStreamComplete(operationResponse.readableStreamBody);
179 }
180 Promise.all([uploadStreamDone, downloadStreamDone])
181 .then(function () {
182 var _a;
183 (_a = httpRequest.abortSignal) === null || _a === void 0 ? void 0 : _a.removeEventListener("abort", abortListener);
184 return;
185 })
186 .catch(function (_e) { });
187 }
188 return [7 /*endfinally*/];
189 case 10: return [2 /*return*/];
190 }
191 });
192 });
193 };
194 return FetchHttpClient;
195}());
196export { FetchHttpClient };
197function isReadableStream(body) {
198 return body && typeof body.pipe === "function";
199}
200function isStreamComplete(stream) {
201 return new Promise(function (resolve) {
202 stream.on("close", resolve);
203 stream.on("end", resolve);
204 stream.on("error", resolve);
205 });
206}
207export function parseHeaders(headers) {
208 var httpHeaders = new HttpHeaders();
209 headers.forEach(function (value, key) {
210 httpHeaders.set(key, value);
211 });
212 return httpHeaders;
213}
214//# sourceMappingURL=fetchHttpClient.js.map
\No newline at end of file