Skip to content

Commit

Permalink
URL with a path and credentials is not handled correctly (#103)
Browse files Browse the repository at this point in the history
This is a fix for a regression introduced by 1766e70.

If a URL with a path is passed to `JsonRpcProvider`, the path is dropped when the provider makes a query because `const url = new URL(route, finalRpcUrl);` does drop the path in `finalRpcUrl`.

The proposed fix to create a URL first, and appends a path via string concat.  This patch also adds a testcase for that logic.
  • Loading branch information
msmania authored Mar 11, 2024
1 parent 2d1a815 commit b73415d
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 9 deletions.
34 changes: 26 additions & 8 deletions packages/provider/src/json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ import {

const DEFAULT_TIMEOUT = 10000

/**
* extractBasicAuth extracts basic authentication credentials from the given
* url into a separate base64-encoded string so that we can pass it to Fetch API.
**/
export function extractBasicAuth(urlStr: string) {
const url = new URL(urlStr);
if (!url.username && !url.password) {
return {
urlStr,
};
}

const credential =
Buffer.from(`${url.username}:${url.password}`).toString('base64');
url.username = '';
url.password = '';
return {
urlStr: url.toString(),
basicAuth: `Basic ${credential}`,
};
}

/**
* A JSONRPCProvider lets you query data from the chain and send relays to the network.
* NodeJS only, not Isomorphic or Browser compatible.
Expand Down Expand Up @@ -98,16 +120,12 @@ export class JsonRpcProvider implements AbstractProvider {
'Content-Type': 'application/json',
};

const url = new URL(route, finalRpcUrl);
if (url.username || url.password) {
const credential =
Buffer.from(`${url.username}:${url.password}`).toString('base64');
headers['Authorization'] = `Basic ${credential}`;
url.username = '';
url.password = '';
const {urlStr, basicAuth} = extractBasicAuth(finalRpcUrl);
if (basicAuth) {
headers['Authorization'] = basicAuth;
}

const routedRpcUrl = url.toString();
const routedRpcUrl = urlStr + route;

const rpcResponse = await fetch(routedRpcUrl, {
method: 'POST',
Expand Down
21 changes: 20 additions & 1 deletion packages/provider/tests/json-rpc-provider.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MockAgent, setGlobalDispatcher } from 'undici'
import { DEFAULT_URL } from './test-utils'
import { JsonRpcProvider } from '../src/json-rpc-provider'
import { JsonRpcProvider, extractBasicAuth } from '../src/json-rpc-provider'
import { V1RpcRoutes } from '@pokt-foundation/pocketjs-abstract-provider'
import { responseSamples } from './response-samples'
import { RawTxRequest } from '@pokt-foundation/pocketjs-types'
Expand Down Expand Up @@ -160,4 +160,23 @@ describe('JsonRpcProvider tests', () => {
// @ts-ignore
expect(ans).toBe(59133)
})

it('extractBasicAuth test', () => {
let {urlStr, basicAuth} =
extractBasicAuth('https://localhost:8082');
expect(urlStr).toBe('https://localhost:8082');
expect(basicAuth).toBeUndefined();

// Url with a path
({urlStr, basicAuth} =
extractBasicAuth('https://mainnet.rpc.grove.city/v1/12345678'));
expect(urlStr).toBe('https://mainnet.rpc.grove.city/v1/12345678');
expect(basicAuth).toBeUndefined();

// Url with a path and a credential
({urlStr, basicAuth} =
extractBasicAuth('https://scott:tiger@mainnet.rpc.grove.city/v1/12345678'));
expect(urlStr).toBe('https://mainnet.rpc.grove.city/v1/12345678');
expect(basicAuth).toBe('Basic c2NvdHQ6dGlnZXI=');
})
})

0 comments on commit b73415d

Please sign in to comment.