Skip to content

Commit

Permalink
feat: Allow fetch custom dispatcher (nodejs#1411)
Browse files Browse the repository at this point in the history
* Added dispatcher options to fetch()

* fixed types

Signed-off-by: Matteo Collina <hello@matteocollina.com>

* Update index-fetch.js

Co-authored-by: Michaël Zasso <targos@protonmail.com>

* Update index.js

Co-authored-by: Michaël Zasso <targos@protonmail.com>

Co-authored-by: Michaël Zasso <targos@protonmail.com>
  • Loading branch information
mcollina and targos authored May 4, 2022
1 parent c128d13 commit cc65fe5
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 2 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,21 @@ Basic usage example:
}
```

You can pass an optional dispatcher to `fetch` as:

```js
import { fetch, Agent } from 'undici'

const res = await fetch('https://example.com', {
// Mocks are also supported
dispatcher: new Agent({
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10
})
})
const json = await res.json()
console.log(json)
```

#### `request.body`

Expand Down
3 changes: 2 additions & 1 deletion index-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const { getGlobalDispatcher } = require('./lib/global')
const fetchImpl = require('./lib/fetch')

module.exports.fetch = async function fetch (resource) {
return fetchImpl.apply(getGlobalDispatcher(), arguments)
const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher()
return fetchImpl.apply(dispatcher, arguments)
}
module.exports.FormData = require('./lib/fetch/formdata').FormData
module.exports.Headers = require('./lib/fetch/headers').Headers
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 5)) {
if (!fetchImpl) {
fetchImpl = require('./lib/fetch')
}
const dispatcher = getGlobalDispatcher()
const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher()
return fetchImpl.apply(dispatcher, arguments)
}
module.exports.Headers = require('./lib/fetch/headers').Headers
Expand Down
61 changes: 61 additions & 0 deletions test/fetch/client-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ const { createServer } = require('http')
const { ReadableStream } = require('stream/web')
const { Blob } = require('buffer')
const { fetch, Response, Request, FormData, File } = require('../..')
const { Client, setGlobalDispatcher, Agent } = require('../..')
const nodeFetch = require('../../index-fetch')

setGlobalDispatcher(new Agent({
keepAliveTimeout: 1,
keepAliveMaxTimeout: 1
}))

test('function signature', (t) => {
t.plan(2)
Expand Down Expand Up @@ -342,3 +349,57 @@ test('invalid url', async (t) => {
t.match(e.cause.message, 'invalid')
}
})

test('custom agent', (t) => {
t.plan(2)

const obj = { asd: true }
const server = createServer((req, res) => {
res.end(JSON.stringify(obj))
})
t.teardown(server.close.bind(server))

server.listen(0, async () => {
const dispatcher = new Client('http://localhost:' + server.address().port, {
keepAliveTimeout: 1,
keepAliveMaxTimeout: 1
})
const oldDispatch = dispatcher.dispatch
dispatcher.dispatch = function (options, handler) {
t.pass('custom dispatcher')
return oldDispatch.call(this, options, handler)
}
t.teardown(server.close.bind(server))
const body = await fetch(`http://localhost:${server.address().port}`, {
dispatcher
})
t.strictSame(obj, await body.json())
})
})

test('custom agent node fetch', (t) => {
t.plan(2)

const obj = { asd: true }
const server = createServer((req, res) => {
res.end(JSON.stringify(obj))
})
t.teardown(server.close.bind(server))

server.listen(0, async () => {
const dispatcher = new Client('http://localhost:' + server.address().port, {
keepAliveTimeout: 1,
keepAliveMaxTimeout: 1
})
const oldDispatch = dispatcher.dispatch
dispatcher.dispatch = function (options, handler) {
t.pass('custom dispatcher')
return oldDispatch.call(this, options, handler)
}
t.teardown(server.close.bind(server))
const body = await nodeFetch.fetch(`http://localhost:${server.address().port}`, {
dispatcher
})
t.strictSame(obj, await body.json())
})
})
4 changes: 4 additions & 0 deletions test/types/fetch.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Blob } from 'buffer'
import { ReadableStream } from 'stream/web'
import { expectType, expectError } from 'tsd'
import {
Agent,
BodyInit,
fetch,
FormData,
Expand All @@ -23,6 +24,9 @@ import {

const requestInit: RequestInit = {}
const responseInit: ResponseInit = { status: 200, statusText: 'OK' }
const requestInit2: RequestInit = {
dispatcher: new Agent()
}

declare const request: Request
declare const headers: Headers
Expand Down
3 changes: 3 additions & 0 deletions types/fetch.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { URL, URLSearchParams } from 'url'
import { ReadableStream } from 'stream/web'
import { FormData } from './formdata'

import Dispatcher = require('./dispatcher')

export type RequestInfo = string | URL | Request

export declare function fetch (
Expand Down Expand Up @@ -99,6 +101,7 @@ export interface RequestInit {
readonly referrer?: string
readonly referrerPolicy?: ReferrerPolicy
readonly window?: null
readonly dispatcher?: Dispatcher
}

export type ReferrerPolicy =
Expand Down

0 comments on commit cc65fe5

Please sign in to comment.