UNPKG

tencentcloud-sdk-nodejs-common

Version:
233 lines (214 loc) 5.52 kB
import * as QueryString from "querystring" import { URL } from "url" import * as isStream from "is-stream" import * as getStream from "get-stream" import * as FormData from "form-data" import Sign from "../sign" import fetch, { FetchOptions } from "./fetch" import { Response, RequestInit } from "node-fetch" import { Agent } from "http" import * as JSONBigInt from "json-bigint" const JSONbigNative = JSONBigInt({ useNativeBigInt: true }) /** * @inner */ export class HttpConnection { static async doRequest({ method, url, data, timeout, headers = {}, agent, proxy, signal, }: { method: string url: string data: any timeout: number headers?: Record<string, string> agent?: Agent proxy?: string signal?: AbortSignal }): Promise<Response> { const config: FetchOptions = { method: method, headers: Object.assign({}, headers), timeout, agent, proxy, signal, } if (method === "GET") { url += "?" + QueryString.stringify(data) } else { config.headers["Content-Type"] = "application/x-www-form-urlencoded" config.body = QueryString.stringify(data) } return await fetch(url, config) } static async doRequestWithSign3({ method, url, data, service, action, region, version, secretId, secretKey, multipart = false, timeout = 60000, token, requestClient, language, headers = {}, agent, proxy, signal, }: { method: string url: string data: any service: string action: string region: string version: string secretId: string secretKey: string multipart?: boolean timeout?: number token: string requestClient: string language: string headers?: Record<string, string> agent?: Agent proxy?: string signal?: AbortSignal }): Promise<Response> { // data 中可能带有 readStream,由于需要计算整个 body 的 hash, // 所以这里把 readStream 转为 Buffer // eslint-disable-next-line @typescript-eslint/no-use-before-define await convertReadStreamToBuffer(data) // eslint-disable-next-line @typescript-eslint/no-use-before-define data = deepRemoveNull(data) const timestamp = parseInt(String(new Date().getTime() / 1000)) method = method.toUpperCase() let payload = "" if (method === "GET") { // eslint-disable-next-line @typescript-eslint/no-use-before-define data = mergeData(data) url += "?" + QueryString.stringify(data) } if (method === "POST") { payload = data } const config: FetchOptions = { method, timeout, headers: Object.assign({}, headers, { Host: new URL(url).host, "X-TC-Action": action, "X-TC-Region": region, "X-TC-Timestamp": timestamp, "X-TC-Version": version, "X-TC-Token": token, "X-TC-RequestClient": requestClient, }), agent, proxy, signal, } if (token === null || token === undefined) { delete config.headers["X-TC-Token"] } if (region === null || region === undefined) { delete config.headers["X-TC-Region"] } if (language) { config.headers["X-TC-Language"] = language } let form if (method === "GET") { config.headers["Content-Type"] = "application/x-www-form-urlencoded" } if (method === "POST" && !multipart) { config.body = data const contentType = config.headers["Content-Type"] || "application/json" if (!isBuffer(data)) config.body = JSONbigNative.stringify(data) config.headers["Content-Type"] = contentType } if (method === "POST" && multipart) { form = new FormData() for (const key in data) { form.append(key, data[key]) } config.body = form config.headers = Object.assign({}, config.headers, form.getHeaders()) } const signature = Sign.sign3({ method, url, payload, timestamp, service, secretId, secretKey, multipart, boundary: form ? form.getBoundary() : undefined, headers: config.headers, }) config.headers["Authorization"] = signature return await fetch(url, config) } } async function convertReadStreamToBuffer(data: any): Promise<void> { for (const key in data) { if (isStream(data[key])) { data[key] = await getStream.buffer(data[key]) } } } function mergeData(data: any, prefix = "") { const ret: any = {} for (const k in data) { if (data[k] === null) { continue } if (data[k] instanceof Array || data[k] instanceof Object) { Object.assign(ret, mergeData(data[k], prefix + k + ".")) } else { ret[prefix + k] = data[k] } } return ret } function deepRemoveNull(obj: any) { if (isArray(obj)) { return obj.map(deepRemoveNull) } else if (isObject(obj)) { const result: any = {} for (const key in obj) { const value = obj[key] if (!isNull(value)) { result[key] = deepRemoveNull(value) } } return result } else { return obj } } function isBuffer(x: any): boolean { return Buffer.isBuffer(x) } function isArray(x: any): boolean { return Array.isArray(x) } function isObject(x: any): boolean { return typeof x === "object" && !isArray(x) && !isStream(x) && !isBuffer(x) && x !== null } function isNull(x: any): boolean { return x === null }