summaryrefslogtreecommitdiff
path: root/src/headless/NodeHttpLib.ts
blob: 118fb9e962e1718d9fb2faffa4e6fa7ad4211f78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
 This file is part of GNU Taler
 (C) 2019 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

 SPDX-License-Identifier: AGPL3.0-or-later
*/

/**
 * Imports.
 */
import {
  Headers,
  HttpRequestLibrary,
  HttpRequestOptions,
  HttpResponse,
} from "../util/http";
import { RequestThrottler } from "../util/RequestThrottler";
import Axios from "axios";

/**
 * Implementation of the HTTP request library interface for node.
 */
export class NodeHttpLib implements HttpRequestLibrary {
  private throttle = new RequestThrottler();
  private throttlingEnabled = true;

  /**
   * Set whether requests should be throttled.
   */
  setThrottling(enabled: boolean): void {
    this.throttlingEnabled = enabled;
  }

  private async req(
    method: "post" | "get",
    url: string,
    body: any,
    opt?: HttpRequestOptions,
  ): Promise<HttpResponse> {
    if (this.throttlingEnabled && this.throttle.applyThrottle(url)) {
      throw Error("request throttled");
    }
    const resp = await Axios({
      method,
      url: url,
      responseType: "text",
      headers: opt?.headers,
      validateStatus: () => true,
      transformResponse: (x) => x,
      data: body,
    });

    const respText = resp.data;
    if (typeof respText !== "string") {
      throw Error("unexpected response type");
    }
    const makeJson = async (): Promise<any> => {
      let responseJson;
      try {
        responseJson = JSON.parse(respText);
      } catch (e) {
        throw Error("Invalid JSON from HTTP response");
      }
      if (responseJson === null || typeof responseJson !== "object") {
        throw Error("Invalid JSON from HTTP response");
      }
      return responseJson;
    };
    const headers = new Headers();
    for (const hn of Object.keys(resp.headers)) {
      headers.set(hn, resp.headers[hn]);
    }
    return {
      headers,
      status: resp.status,
      text: async () => resp.data,
      json: makeJson,
    };
  }

  async get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
    return this.req("get", url, undefined, opt);
  }

  async postJson(
    url: string,
    body: any,
    opt?: HttpRequestOptions,
  ): Promise<HttpResponse> {
    return this.req("post", url, body, opt);
  }
}