1 | import { HttpResponse } from "@aws-sdk/protocol-http";
|
2 | import { buildQueryString } from "@aws-sdk/querystring-builder";
|
3 | import { requestTimeout } from "./request-timeout";
|
4 | export class FetchHttpHandler {
|
5 | constructor(options) {
|
6 | if (typeof options === "function") {
|
7 | this.configProvider = options().then((opts) => opts || {});
|
8 | }
|
9 | else {
|
10 | this.config = options ?? {};
|
11 | this.configProvider = Promise.resolve(this.config);
|
12 | }
|
13 | }
|
14 | destroy() {
|
15 | }
|
16 | async handle(request, { abortSignal } = {}) {
|
17 | if (!this.config) {
|
18 | this.config = await this.configProvider;
|
19 | }
|
20 | const requestTimeoutInMs = this.config.requestTimeout;
|
21 | if (abortSignal?.aborted) {
|
22 | const abortError = new Error("Request aborted");
|
23 | abortError.name = "AbortError";
|
24 | return Promise.reject(abortError);
|
25 | }
|
26 | let path = request.path;
|
27 | if (request.query) {
|
28 | const queryString = buildQueryString(request.query);
|
29 | if (queryString) {
|
30 | path += `?${queryString}`;
|
31 | }
|
32 | }
|
33 | const { port, method } = request;
|
34 | const url = `${request.protocol}//${request.hostname}${port ? `:${port}` : ""}${path}`;
|
35 | const body = method === "GET" || method === "HEAD" ? undefined : request.body;
|
36 | const requestOptions = {
|
37 | body,
|
38 | headers: new Headers(request.headers),
|
39 | method: method,
|
40 | };
|
41 | if (typeof AbortController !== "undefined") {
|
42 | requestOptions["signal"] = abortSignal;
|
43 | }
|
44 | const fetchRequest = new Request(url, requestOptions);
|
45 | const raceOfPromises = [
|
46 | fetch(fetchRequest).then((response) => {
|
47 | const fetchHeaders = response.headers;
|
48 | const transformedHeaders = {};
|
49 | for (const pair of fetchHeaders.entries()) {
|
50 | transformedHeaders[pair[0]] = pair[1];
|
51 | }
|
52 | const hasReadableStream = response.body != undefined;
|
53 | if (!hasReadableStream) {
|
54 | return response.blob().then((body) => ({
|
55 | response: new HttpResponse({
|
56 | headers: transformedHeaders,
|
57 | statusCode: response.status,
|
58 | body,
|
59 | }),
|
60 | }));
|
61 | }
|
62 | return {
|
63 | response: new HttpResponse({
|
64 | headers: transformedHeaders,
|
65 | statusCode: response.status,
|
66 | body: response.body,
|
67 | }),
|
68 | };
|
69 | }),
|
70 | requestTimeout(requestTimeoutInMs),
|
71 | ];
|
72 | if (abortSignal) {
|
73 | raceOfPromises.push(new Promise((resolve, reject) => {
|
74 | abortSignal.onabort = () => {
|
75 | const abortError = new Error("Request aborted");
|
76 | abortError.name = "AbortError";
|
77 | reject(abortError);
|
78 | };
|
79 | }));
|
80 | }
|
81 | return Promise.race(raceOfPromises);
|
82 | }
|
83 | }
|