From 811f25342e159c4e05d3018a3eaccd2e85cd156f Mon Sep 17 00:00:00 2001 From: Gus Narea Date: Tue, 2 Aug 2022 21:15:44 +0100 Subject: [PATCH] fix: Replace `POHTTP_TLS_REQUIRED` env var with `useTls` option (#219) --- README.md | 10 +- jest.config.js | 2 +- package-lock.json | 280 ++++++++++++++++++++++++++++++++++++----- package.json | 7 +- src/lib/_test_utils.ts | 11 -- src/lib/client.spec.ts | 101 ++++++++------- src/lib/client.ts | 27 ++-- 7 files changed, 333 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index c29c640..038aa9a 100644 --- a/README.md +++ b/README.md @@ -21,25 +21,21 @@ relaynet-pohttp-js is meant to be used with the [core library](https://www.npmjs ```javascript import { deliverParcel } from '@relaycorp/relaynet-pohttp'; -const relayAddress = 'rng+https://relay.relaycorp.tech'; - async function main() { // `parcelSerialized` is the ArrayBuffer representation of the parcel as a RAMF message const parcelSerialized = await yourFunctionToRetrieveTheParcel(); - await deliverParcel('https://ping.relaycorp.tech', parcelSerialized, { - relayAddress, - }); + await deliverParcel('https://ping.relaycorp.tech', parcelSerialized); } ``` -By default, a timeout of 3 seconds will be used and up to 3 consecutive redirects would be followed. `deliverParcel` takes an optional `options` argument to customize these and other options. +By default, a timeout of 3 seconds will be used and up to 3 consecutive redirects would be followed. `deliverParcel()` takes an optional `options` argument to customize these and other options. TypeScript type declarations are included with this library. ## Use in integration or functional tests -Per the Relaynet specs, bindings like PoHTTP must be used over TLS, but this validation can be turned off in test suites that send requests to a mock HTTP server by setting the environment variable `POHTTP_TLS_REQUIRED` to `false`. +Per the Relaynet specs, bindings like PoHTTP must be used over TLS, but this validation can be turned off in test suites that send requests to a mock HTTP server by passing the options `useTls=false` to `deliverParcel()`. ## Support diff --git a/jest.config.js b/jest.config.js index e5c4328..b683449 100644 --- a/jest.config.js +++ b/jest.config.js @@ -125,7 +125,7 @@ module.exports = { setupFiles: ["jest-date-mock"], // A list of paths to modules that run some code to configure or set up the testing framework before each test - setupFilesAfterEnv: ["jest-extended"], + setupFilesAfterEnv: ["jest-extended/all"], // A list of paths to snapshot serializer modules Jest should use for snapshot testing // snapshotSerializers: [], diff --git a/package-lock.json b/package-lock.json index 38a6a89..becb2f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@relaycorp/relaynet-core": "^1.81.11", - "axios": "^0.27.2", - "env-var": "^7.1.1" + "@relaycorp/relaynet-core": "< 2", + "axios": "^0.27.2" }, "devDependencies": { "@relaycorp/shared-config": "^1.9.1", @@ -21,7 +20,7 @@ "buffer-to-arraybuffer": "0.0.6", "jest": "^28.1.3", "jest-date-mock": "^1.0.8", - "jest-extended": "^3.0.1", + "jest-extended": "^2.0.0", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", "rimraf": "^3.0.2", @@ -33,6 +32,9 @@ }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "@relaycorp/relaynet-core": "< 2" } }, "node_modules/@ampproject/remapping": { @@ -2374,14 +2376,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/env-var": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.1.1.tgz", - "integrity": "sha512-4+vvlq+wwGQNwY/nI3/+Ojc1MKHCmITRJ6VWkQzDtMD6fAEb60ACRUCnlIAonMKW9YzqYmYxbyVu9vTb++yNRg==", - "engines": { - "node": ">=10" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3585,21 +3579,156 @@ } }, "node_modules/jest-extended": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.1.tgz", - "integrity": "sha512-OSGbKUhbjy7QikfQyK3ishFrAqLeRodBzeJk7SuuWGACAT7HHcGuJ4aUQ3ueLANx4KSv1Pa7r1LJWGtJ3eI0xA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-2.0.0.tgz", + "integrity": "sha512-6AgjJQVaBEKGSK3FH90kOiRUWJsbzn9NWtW0pjGkAFIdH0oPilfkV/gHPJdVvJeBiqT3jMHw8TUg9pUGC1azDg==", "dev": true, "dependencies": { - "jest-diff": "^28.0.0", - "jest-get-type": "^28.0.0" + "jest-diff": "^27.2.5", + "jest-get-type": "^27.0.6" }, "engines": { - "node": "^14.15.0 || ^16.13.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" }, "peerDependencies": { "jest": ">=27.2.5" } }, + "node_modules/jest-extended/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-extended/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-extended/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-extended/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-extended/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-extended/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-extended/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-extended/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-extended/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-extended/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-extended/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/jest-extended/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-get-type": { "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", @@ -8271,11 +8400,6 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "env-var": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.1.1.tgz", - "integrity": "sha512-4+vvlq+wwGQNwY/nI3/+Ojc1MKHCmITRJ6VWkQzDtMD6fAEb60ACRUCnlIAonMKW9YzqYmYxbyVu9vTb++yNRg==" - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -9134,13 +9258,113 @@ } }, "jest-extended": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.1.tgz", - "integrity": "sha512-OSGbKUhbjy7QikfQyK3ishFrAqLeRodBzeJk7SuuWGACAT7HHcGuJ4aUQ3ueLANx4KSv1Pa7r1LJWGtJ3eI0xA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-2.0.0.tgz", + "integrity": "sha512-6AgjJQVaBEKGSK3FH90kOiRUWJsbzn9NWtW0pjGkAFIdH0oPilfkV/gHPJdVvJeBiqT3jMHw8TUg9pUGC1azDg==", "dev": true, "requires": { - "jest-diff": "^28.0.0", - "jest-get-type": "^28.0.0" + "jest-diff": "^27.2.5", + "jest-get-type": "^27.0.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "jest-get-type": { diff --git a/package.json b/package.json index a6cb071..8aaff1b 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,8 @@ "access": "public" }, "dependencies": { - "@relaycorp/relaynet-core": "^1.81.11", - "axios": "^0.27.2", - "env-var": "^7.1.1" + "@relaycorp/relaynet-core": "< 2", + "axios": "^0.27.2" }, "devDependencies": { "@relaycorp/shared-config": "^1.9.1", @@ -60,7 +59,7 @@ "buffer-to-arraybuffer": "0.0.6", "jest": "^28.1.3", "jest-date-mock": "^1.0.8", - "jest-extended": "^3.0.1", + "jest-extended": "^2.0.0", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", "rimraf": "^3.0.2", diff --git a/src/lib/_test_utils.ts b/src/lib/_test_utils.ts index e3c3528..93fa115 100644 --- a/src/lib/_test_utils.ts +++ b/src/lib/_test_utils.ts @@ -1,5 +1,3 @@ -import envVar from 'env-var'; - export async function expectPromiseToReject( promise: Promise, expectedError: Error, @@ -22,12 +20,3 @@ export function getMockContext(mockedObject: any): jest.MockContext { const mockInstance = getMockInstance(mockedObject); return mockInstance.mock; } - -export function mockEnvVars(envVars: { readonly [key: string]: string | undefined }): void { - jest.spyOn(envVar, 'get').mockImplementation((...args: readonly any[]) => { - const originalEnvVar = jest.requireActual('env-var'); - const env = originalEnvVar.from(envVars); - - return env.get(...args); - }); -} diff --git a/src/lib/client.spec.ts b/src/lib/client.spec.ts index 1eacbb6..ea0e8e4 100644 --- a/src/lib/client.spec.ts +++ b/src/lib/client.spec.ts @@ -2,7 +2,7 @@ import * as relaynet from '@relaycorp/relaynet-core'; import axios from 'axios'; import bufferToArray from 'buffer-to-arraybuffer'; -import { expectPromiseToReject, getMockContext, getMockInstance, mockEnvVars } from './_test_utils'; +import { expectPromiseToReject, getMockContext, getMockInstance } from './_test_utils'; import { deliverParcel, DeliveryOptions } from './client'; import PoHTTPClientBindingError from './PoHTTPClientBindingError'; import PoHTTPError from './PoHTTPError'; @@ -120,8 +120,8 @@ describe('deliverParcel', () => { expect(agent).toHaveProperty('keepAlive', true); }); - describe('TLS enablement option', () => { - test('URL resolution should use HTTPS if option is unspecified', async () => { + describe('TLS', () => { + test('URL resolution should use HTTPS if useTls is unspecified', async () => { await deliverParcel(host, body); expect(stubAxiosPost).toBeCalledWith( @@ -131,8 +131,8 @@ describe('deliverParcel', () => { ); }); - test('URL resolution should use HTTPS if option is enabled', async () => { - await deliverParcel(host, body, { enableTls: true }); + test('URL resolution should use HTTPS if useTls is enabled', async () => { + await deliverParcel(host, body, { useTls: true }); expect(stubAxiosPost).toBeCalledWith( expect.stringMatching(/^https:/), @@ -141,10 +141,8 @@ describe('deliverParcel', () => { ); }); - test('URL resolution should use HTTP if option is disabled', async () => { - mockEnvVars({ POHTTP_TLS_REQUIRED: 'false' }); - - await deliverParcel(host, body, { enableTls: false }); + test('URL resolution should use HTTP if useTls is disabled', async () => { + await deliverParcel(host, body, { useTls: false }); expect(stubAxiosPost).toBeCalledWith( expect.stringMatching(/^http:/), @@ -154,38 +152,6 @@ describe('deliverParcel', () => { }); }); - describe('POHTTP_TLS_REQUIRED', () => { - const nonTlsUrl = 'http://example.com'; - - test('Non-TLS URLs should be refused if POHTTP_TLS_REQUIRED is undefined', async () => { - mockEnvVars({}); - - await expectPromiseToReject( - deliverParcel(nonTlsUrl, body), - new Error(`Can only POST to HTTPS URLs (got ${nonTlsUrl})`), - ); - }); - - test('Non-TLS URLs should be allowed if POHTTP_TLS_REQUIRED=false', async () => { - mockEnvVars({ POHTTP_TLS_REQUIRED: 'false' }); - - await deliverParcel(nonTlsUrl, body); - - expect(stubAxiosPost).toBeCalledTimes(1); - const postCallArgs = getMockContext(stubAxiosPost).calls[0]; - expect(postCallArgs[0]).toEqual(nonTlsUrl); - }); - - test('Non-TLS URLs should be refused if POHTTP_TLS_REQUIRED=true', async () => { - mockEnvVars({ POHTTP_TLS_REQUIRED: 'true' }); - - await expectPromiseToReject( - deliverParcel(nonTlsUrl, body), - new Error(`Can only POST to HTTPS URLs (got ${nonTlsUrl})`), - ); - }); - }); - describe('Timeout', () => { test('Request should time out after 3 seconds by default', async () => { await deliverParcel(url, body); @@ -212,7 +178,6 @@ describe('deliverParcel', () => { }; beforeEach(() => { - // @ts-ignore stubAxiosPost.mockReset(); }); @@ -267,6 +232,58 @@ describe('deliverParcel', () => { }); }); + test('Non-TLS redirects should be refused if TLS is unspecified', async () => { + stubAxiosPost.mockRejectedValueOnce({ + response: { + ...stubRedirectResponse, + headers: { location: `http://${targetHost}/foo` }, + }, + }); + + await expect(deliverParcel(url, body)).rejects.toThrowWithMessage( + PoHTTPError, + /^Can only POST to HTTPS URLs/, + ); + }); + + test('Non-TLS redirects should be refused if TLS is used', async () => { + stubAxiosPost.mockRejectedValueOnce({ + response: { + ...stubRedirectResponse, + headers: { location: `http://${targetHost}/foo` }, + }, + }); + + await expect(deliverParcel(url, body, { useTls: true })).rejects.toThrowWithMessage( + PoHTTPError, + /^Can only POST to HTTPS URLs/, + ); + }); + + test('TLS redirects should be allowed even if TLS is not used', async () => { + stubAxiosPost.mockRejectedValueOnce({ + response: { + ...stubRedirectResponse, + headers: { location: `https://${targetHost}/foo` }, + }, + }); + stubAxiosPost.mockResolvedValueOnce(stubResponse); + + await deliverParcel(url, body, { useTls: false }); + }); + + test('Non-TLS redirects should be allowed if TLS is not used', async () => { + stubAxiosPost.mockRejectedValueOnce({ + response: { + ...stubRedirectResponse, + headers: { location: `http://${targetHost}/foo` }, + }, + }); + stubAxiosPost.mockResolvedValueOnce(stubResponse); + + await deliverParcel(url, body, { useTls: false }); + }); + test('Redirects should be followed up to a maximum of 3 times by default', async () => { stubAxiosPost.mockRejectedValueOnce({ response: stubRedirectResponse }); stubAxiosPost.mockRejectedValueOnce({ response: stubRedirectResponse }); diff --git a/src/lib/client.ts b/src/lib/client.ts index f43657f..925b0e9 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,6 +1,5 @@ import { BindingType, PublicNodeAddress, resolveInternetAddress } from '@relaycorp/relaynet-core'; import axios, { AxiosInstance, AxiosResponse } from 'axios'; -import { get as getEnvVar } from 'env-var'; import * as https from 'https'; import PoHTTPClientBindingError from './PoHTTPClientBindingError'; @@ -8,7 +7,7 @@ import PoHTTPError from './PoHTTPError'; import PoHTTPInvalidParcelError from './PoHTTPInvalidParcelError'; export interface DeliveryOptions { - readonly enableTls: boolean; + readonly useTls: boolean; readonly maxRedirects: number; readonly timeout: number; } @@ -34,8 +33,9 @@ export async function deliverParcel( headers: { 'Content-Type': 'application/vnd.awala.parcel' }, httpsAgent: new https.Agent({ keepAlive: true }), }); - const url = await resolveURL(recipientInternetAddressOrURL, options.enableTls); - const response = await postRequest(url, parcelSerialized, axiosInstance, axiosOptions); + const useTls = options.useTls ?? true; + const url = await resolveURL(recipientInternetAddressOrURL, useTls); + const response = await postRequest(url, parcelSerialized, axiosInstance, axiosOptions, useTls); if (response.status === 307 || response.status === 308) { throw new PoHTTPError(`Reached maximum number of redirects (${axiosOptions.maxRedirects})`); } @@ -47,7 +47,7 @@ interface SupportedAxiosRequestConfig { readonly timeout: number; } -async function resolveURL(targetNodeUrl: string, enableTls?: boolean): Promise { +async function resolveURL(targetNodeUrl: string, useTls: boolean): Promise { if (isURL(targetNodeUrl)) { return targetNodeUrl; } @@ -59,7 +59,7 @@ async function resolveURL(targetNodeUrl: string, enableTls?: boolean): Promise { - const isTlsRequired = getEnvVar('POHTTP_TLS_REQUIRED').default('true').asBool(); - if (isTlsRequired && url.startsWith('http:')) { + if (useTls && url.startsWith('http:')) { throw new PoHTTPError(`Can only POST to HTTPS URLs (got ${url})`); } let response; @@ -118,10 +118,13 @@ async function postRequest( if (isStatusCodeValidRedirect(response.status) && 0 < options.maxRedirects) { // Axios doesn't support 307 or 308 redirects: https://github.com/axios/axios/issues/2429, // so we have to follow the redirect manually. - return postRequest(response.headers.location, body, axiosInstance, { - ...options, - maxRedirects: options.maxRedirects - 1, - }); + return postRequest( + response.headers.location, + body, + axiosInstance, + { ...options, maxRedirects: options.maxRedirects - 1 }, + useTls, + ); } return response;