-
Notifications
You must be signed in to change notification settings - Fork 748
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow devs to supply a dispatcher to the Node implementation of `fetc…
…h` (#2178) # Summary The current version of web3.js allows folks to supply what's called an ‘HTTP Agent.’ This is a class that mediates how network requests should be fetched. One example is to establish a connection pool for a given URL, and keep connections open for a time so that TLS/SSL negotiation doesn't have to be done on every request. The new web3.js up until this point has punted on how to reintroduce this concept. In this PR we bring it back in the form of allowing callers to create transports with custom Undici `Dispatchers`. It only works in Node. If you supply the `dispatcher_NODE_ONLY` property in non-Node environments you'll get a console warning that your config has been ignored. # Alternate designs considered I desperately wanted to avoid the situation in this PR, which was to create a config property on transports that's only usable in Node. Alternate approaches considered: 1. Create a special transport for use in Node. Callers would have to `createDefaultNodeRpcTransport()` and/or `solanaRpcFactoryForNode()` which I thought was a bridge too far. 2. Encourage people to [inject a global dispatcher](https://stackoverflow.com/a/77526783/802047). This is a terrible idea for all of the reasons that global state is terrible: * Applies to all connections in the process * Your global dispatcher can be unset by someone _else_ * There might already be a global dispatcher installed (eg. a `ProxyAgent`) and you might unknowingly replace it 3. Export a `Dispatcher` setter that lets you install a dispatcher accessible _only_ to `@solana/fetch-impl`. Besides having most of the bad features of #​2, this would not work. We inline `@solana/fetch-impl` in the build, meaning there's no longer anywhere to export such a method from. # Test Plan ```shell cd packages/fetch-impl/ pnpm test:unit:node cd ../rpc-transport-http/ pnpm test:unit:node ``` See benchmark tests in next PR. Closes #2126.
- Loading branch information
1 parent
16f8db8
commit a2fc5a3
Showing
11 changed files
with
138 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Dispatcher, fetch as fetchImpl } from 'undici'; | ||
|
||
import fetch from '../index.node'; | ||
|
||
jest.mock('undici'); | ||
|
||
describe('fetch', () => { | ||
it('should call the underlying `fetch` with the `dispatcher` supplied in `requestInit`', () => { | ||
const explicitDispatcher = Symbol('explicitDispatcher') as unknown as Dispatcher; | ||
fetch('http://solana.com', { dispatcher: explicitDispatcher }); | ||
expect(fetchImpl).toHaveBeenCalledWith('http://solana.com', { | ||
dispatcher: explicitDispatcher, | ||
}); | ||
}); | ||
it('should call the underlying `fetch` with an undefined `dispatcher` when an undefined is explicitly supplied in `requestInit`', () => { | ||
fetch('http://solana.com', { dispatcher: undefined }); | ||
expect(fetchImpl).toHaveBeenCalledWith('http://solana.com', { | ||
dispatcher: undefined, | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
// TODO(https://github.com/solana-labs/solana-web3.js/issues/1787) Write HTTP/2 implementation. | ||
export default globalThis.fetch; // The Fetch API is supported natively in Node 18+. | ||
export { fetch as default } from 'undici'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
packages/rpc-transport-http/src/__tests__/http-transport-dispatcher-test.browser.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import type { Dispatcher } from 'undici'; | ||
|
||
const WARNING_MESSAGE = | ||
'You have supplied a `Dispatcher` to `createHttpTransport()`. It has been ignored because ' + | ||
'Undici dispatchers only work in Node environments. To eliminate this warning, omit the ' + | ||
'`dispatcher_NODE_ONLY` property from your config when running in a non-Node environment.'; | ||
|
||
describe('createHttpTransport()', () => { | ||
let createHttpTransport: typeof import('../http-transport').createHttpTransport; | ||
beforeEach(async () => { | ||
jest.spyOn(console, 'warn').mockImplementation(); | ||
await jest.isolateModulesAsync(async () => { | ||
createHttpTransport = | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
(await import('../http-transport')).createHttpTransport; | ||
}); | ||
}); | ||
describe('in development mode', () => { | ||
beforeEach(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(globalThis as any).__DEV__ = true; | ||
}); | ||
it('warns when configured with a dispatcher', () => { | ||
createHttpTransport({ dispatcher_NODE_ONLY: {} as Dispatcher, url: 'fake://url' }); | ||
expect(console.warn).toHaveBeenCalledWith(WARNING_MESSAGE); | ||
}); | ||
it('warns when configured with an undefined dispatcher', () => { | ||
createHttpTransport({ dispatcher_NODE_ONLY: undefined, url: 'fake://url' }); | ||
expect(console.warn).toHaveBeenCalledWith(WARNING_MESSAGE); | ||
}); | ||
it('only warns once no matter how many times it is configured with a dispatcher', () => { | ||
createHttpTransport({ dispatcher_NODE_ONLY: undefined, url: 'fake://url' }); | ||
createHttpTransport({ dispatcher_NODE_ONLY: {} as Dispatcher, url: 'fake://url' }); | ||
createHttpTransport({ dispatcher_NODE_ONLY: null as unknown as Dispatcher, url: 'fake://url' }); | ||
expect(console.warn).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
describe('in production mode', () => { | ||
beforeEach(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(globalThis as any).__DEV__ = false; | ||
}); | ||
it('does not warn when configured with a dispatcher', () => { | ||
createHttpTransport({ dispatcher_NODE_ONLY: {} as Dispatcher, url: 'fake://url' }); | ||
expect(console.warn).not.toHaveBeenCalledWith(WARNING_MESSAGE); | ||
}); | ||
it('does not warn when configured with an undefined dispatcher', () => { | ||
createHttpTransport({ dispatcher_NODE_ONLY: undefined, url: 'fake://url' }); | ||
expect(console.warn).not.toHaveBeenCalledWith(WARNING_MESSAGE); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.