diff --git a/packages/tendermint-rpc/src/rpcclients/http.spec.ts b/packages/tendermint-rpc/src/rpcclients/http.spec.ts new file mode 100644 index 00000000..fa6b3b0f --- /dev/null +++ b/packages/tendermint-rpc/src/rpcclients/http.spec.ts @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { createJsonRpcRequest } from "../jsonrpc"; +import { defaultInstance, pendingWithoutTendermint } from "../testutil.spec"; +import { http } from "./http"; + +function pendingWithoutHttpServer(): void { + if (!process.env.HTTPSERVER_ENABLED) { + pending("Set HTTPSERVER_ENABLED to enable HTTP tests"); + } +} + +const tendermintUrl = defaultInstance.url; +const echoUrl = "http://localhost:5555/echo_headers"; + +describe("http", () => { + it("can send a health request", async () => { + pendingWithoutTendermint(); + const response = await http("POST", `http://${tendermintUrl}`, undefined, createJsonRpcRequest("health")); + expect(response).toEqual(jasmine.objectContaining({ jsonrpc: "2.0" })); + }); + + it("errors for non-open port", async () => { + await expectAsync( + http("POST", `http://localhost:56745`, undefined, createJsonRpcRequest("health")), + ).toBeRejectedWithError(/(ECONNREFUSED|Failed to fetch)/i); + }); + + it("can send custom headers", async () => { + pendingWithoutHttpServer(); + // Without custom headers + const response1 = await http("POST", echoUrl, undefined, createJsonRpcRequest("health")); + expect(response1).toEqual({ + request_headers: jasmine.objectContaining({ + // Basic headers from http client + Accept: jasmine.any(String), + "Content-Length": jasmine.any(String), + "Content-Type": "application/json", + Host: jasmine.any(String), + "User-Agent": jasmine.any(String), + }), + }); + + // With custom headers + const response2 = await http( + "POST", + echoUrl, + { foo: "bar123", Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz" }, + createJsonRpcRequest("health"), + ); + expect(response2).toEqual({ + request_headers: jasmine.objectContaining({ + // Basic headers from http client + "Content-Length": jasmine.any(String), + "Content-Type": "application/json", + Host: jasmine.any(String), + "User-Agent": jasmine.any(String), + // Custom headers + foo: "bar123", + Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz", + }), + }); + }); +}); diff --git a/packages/tendermint-rpc/src/rpcclients/http.ts b/packages/tendermint-rpc/src/rpcclients/http.ts new file mode 100644 index 00000000..f7ec0b41 --- /dev/null +++ b/packages/tendermint-rpc/src/rpcclients/http.ts @@ -0,0 +1,44 @@ +import axios from "axios"; + +// Global symbols in some environments +// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch +declare const fetch: any | undefined; + +function filterBadStatus(res: any): any { + if (res.status >= 400) { + throw new Error(`Bad status on response: ${res.status}`); + } + return res; +} + +/** + * Helper to work around missing CORS support in Tendermint (https://github.com/tendermint/tendermint/pull/2800) + * + * For some reason, fetch does not complain about missing server-side CORS support. + */ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export async function http( + method: "POST", + url: string, + headers: Record | undefined, + request?: any, +): Promise { + if (typeof fetch !== "undefined") { + const settings = { + method: method, + body: request ? JSON.stringify(request) : undefined, + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + "Content-Type": "application/json", + ...headers, + }, + }; + return fetch(url, settings) + .then(filterBadStatus) + .then((res: any) => res.json()); + } else { + return axios + .request({ url: url, method: method, data: request, headers: headers }) + .then((res) => res.data); + } +} diff --git a/packages/tendermint-rpc/src/rpcclients/httpbatchclient.spec.ts b/packages/tendermint-rpc/src/rpcclients/httpbatchclient.spec.ts index c0362834..d242f84f 100644 --- a/packages/tendermint-rpc/src/rpcclients/httpbatchclient.spec.ts +++ b/packages/tendermint-rpc/src/rpcclients/httpbatchclient.spec.ts @@ -2,7 +2,6 @@ import { createJsonRpcRequest } from "../jsonrpc"; import { defaultInstance } from "../testutil.spec"; import { HttpBatchClient } from "./httpbatchclient"; -import { http } from "./httpclient"; function pendingWithoutTendermint(): void { if (!process.env.TENDERMINT_ENABLED) { @@ -10,64 +9,7 @@ function pendingWithoutTendermint(): void { } } -function pendingWithoutHttpServer(): void { - if (!process.env.HTTPSERVER_ENABLED) { - pending("Set HTTPSERVER_ENABLED to enable HTTP tests"); - } -} - const tendermintUrl = defaultInstance.url; -const echoUrl = "http://localhost:5555/echo_headers"; - -describe("http", () => { - it("can send a health request", async () => { - pendingWithoutTendermint(); - const response = await http("POST", `http://${tendermintUrl}`, undefined, createJsonRpcRequest("health")); - expect(response).toEqual(jasmine.objectContaining({ jsonrpc: "2.0" })); - }); - - it("errors for non-open port", async () => { - await expectAsync( - http("POST", `http://localhost:56745`, undefined, createJsonRpcRequest("health")), - ).toBeRejectedWithError(/(ECONNREFUSED|Failed to fetch)/i); - }); - - it("can send custom headers", async () => { - pendingWithoutHttpServer(); - // Without custom headers - const response1 = await http("POST", echoUrl, undefined, createJsonRpcRequest("health")); - expect(response1).toEqual({ - request_headers: jasmine.objectContaining({ - // Basic headers from http client - Accept: jasmine.any(String), - "Content-Length": jasmine.any(String), - "Content-Type": "application/json", - Host: jasmine.any(String), - "User-Agent": jasmine.any(String), - }), - }); - - // With custom headers - const response2 = await http( - "POST", - echoUrl, - { foo: "bar123", Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz" }, - createJsonRpcRequest("health"), - ); - expect(response2).toEqual({ - request_headers: jasmine.objectContaining({ - // Basic headers from http client - "Content-Length": jasmine.any(String), - "Content-Type": "application/json", - Host: jasmine.any(String), - "User-Agent": jasmine.any(String), - // Custom headers - foo: "bar123", - Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz", - }), - }); - }); -}); describe("HttpBatchClient", () => { it("can make a simple call", async () => { diff --git a/packages/tendermint-rpc/src/rpcclients/httpbatchclient.ts b/packages/tendermint-rpc/src/rpcclients/httpbatchclient.ts index 1b616c3b..f0bafc96 100644 --- a/packages/tendermint-rpc/src/rpcclients/httpbatchclient.ts +++ b/packages/tendermint-rpc/src/rpcclients/httpbatchclient.ts @@ -5,7 +5,8 @@ import { parseJsonRpcResponse, } from "@cosmjs/json-rpc"; -import { http, HttpEndpoint } from "./httpclient"; +import { http } from "./http"; +import { HttpEndpoint } from "./httpclient"; import { hasProtocol, RpcClient } from "./rpcclient"; export interface HttpBatchClientOptions { diff --git a/packages/tendermint-rpc/src/rpcclients/httpclient.spec.ts b/packages/tendermint-rpc/src/rpcclients/httpclient.spec.ts index 1c326508..11cf707e 100644 --- a/packages/tendermint-rpc/src/rpcclients/httpclient.spec.ts +++ b/packages/tendermint-rpc/src/rpcclients/httpclient.spec.ts @@ -1,7 +1,6 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { createJsonRpcRequest } from "../jsonrpc"; import { defaultInstance } from "../testutil.spec"; -import { http, HttpClient } from "./httpclient"; +import { HttpClient } from "./httpclient"; function pendingWithoutTendermint(): void { if (!process.env.TENDERMINT_ENABLED) { @@ -9,64 +8,7 @@ function pendingWithoutTendermint(): void { } } -function pendingWithoutHttpServer(): void { - if (!process.env.HTTPSERVER_ENABLED) { - pending("Set HTTPSERVER_ENABLED to enable HTTP tests"); - } -} - const tendermintUrl = defaultInstance.url; -const echoUrl = "http://localhost:5555/echo_headers"; - -describe("http", () => { - it("can send a health request", async () => { - pendingWithoutTendermint(); - const response = await http("POST", `http://${tendermintUrl}`, undefined, createJsonRpcRequest("health")); - expect(response).toEqual(jasmine.objectContaining({ jsonrpc: "2.0" })); - }); - - it("errors for non-open port", async () => { - await expectAsync( - http("POST", `http://localhost:56745`, undefined, createJsonRpcRequest("health")), - ).toBeRejectedWithError(/(ECONNREFUSED|Failed to fetch)/i); - }); - - it("can send custom headers", async () => { - pendingWithoutHttpServer(); - // Without custom headers - const response1 = await http("POST", echoUrl, undefined, createJsonRpcRequest("health")); - expect(response1).toEqual({ - request_headers: jasmine.objectContaining({ - // Basic headers from http client - Accept: jasmine.any(String), - "Content-Length": jasmine.any(String), - "Content-Type": "application/json", - Host: jasmine.any(String), - "User-Agent": jasmine.any(String), - }), - }); - - // With custom headers - const response2 = await http( - "POST", - echoUrl, - { foo: "bar123", Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz" }, - createJsonRpcRequest("health"), - ); - expect(response2).toEqual({ - request_headers: jasmine.objectContaining({ - // Basic headers from http client - "Content-Length": jasmine.any(String), - "Content-Type": "application/json", - Host: jasmine.any(String), - "User-Agent": jasmine.any(String), - // Custom headers - foo: "bar123", - Authorization: "Basic Z3Vlc3Q6bm9QYXNzMTIz", - }), - }); - }); -}); describe("HttpClient", () => { it("can make a simple call", async () => { diff --git a/packages/tendermint-rpc/src/rpcclients/httpclient.ts b/packages/tendermint-rpc/src/rpcclients/httpclient.ts index 0b7dedac..a17e9699 100644 --- a/packages/tendermint-rpc/src/rpcclients/httpclient.ts +++ b/packages/tendermint-rpc/src/rpcclients/httpclient.ts @@ -4,53 +4,10 @@ import { JsonRpcSuccessResponse, parseJsonRpcResponse, } from "@cosmjs/json-rpc"; -import axios from "axios"; +import { http } from "./http"; import { hasProtocol, RpcClient } from "./rpcclient"; -// Global symbols in some environments -// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch -declare const fetch: any | undefined; - -function filterBadStatus(res: any): any { - if (res.status >= 400) { - throw new Error(`Bad status on response: ${res.status}`); - } - return res; -} - -/** - * Helper to work around missing CORS support in Tendermint (https://github.com/tendermint/tendermint/pull/2800) - * - * For some reason, fetch does not complain about missing server-side CORS support. - */ -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export async function http( - method: "POST", - url: string, - headers: Record | undefined, - request?: any, -): Promise { - if (typeof fetch !== "undefined") { - const settings = { - method: method, - body: request ? JSON.stringify(request) : undefined, - headers: { - // eslint-disable-next-line @typescript-eslint/naming-convention - "Content-Type": "application/json", - ...headers, - }, - }; - return fetch(url, settings) - .then(filterBadStatus) - .then((res: any) => res.json()); - } else { - return axios - .request({ url: url, method: method, data: request, headers: headers }) - .then((res) => res.data); - } -} - export interface HttpEndpoint { /** * The URL of the HTTP endpoint.