Skip to content

Commit 79ed8e1

Browse files
authored
Add keepAlive to node-fetch polyfill (#27376)
Fixes #27109 This PR adds a default `agent` as described in the [`node-fetch` docs](https://github.com/node-fetch/node-fetch#custom-agent). We should see about 2x perf according to some [benchmarks](https://github.com/Ethan-Arrowood/undici-fetch/blob/main/benchmarks.md#fetch).
1 parent c218347 commit 79ed8e1

File tree

5 files changed

+104
-1
lines changed

5 files changed

+104
-1
lines changed

packages/next/server/node-polyfill-fetch.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
import fetch, { Headers, Request, Response } from 'node-fetch'
2+
import { Agent as HttpAgent } from 'http'
3+
import { Agent as HttpsAgent } from 'https'
24

35
// Polyfill fetch() in the Node.js environment
46
if (!global.fetch) {
5-
global.fetch = fetch
7+
const httpAgent = new HttpAgent({ keepAlive: true })
8+
const httpsAgent = new HttpsAgent({ keepAlive: true })
9+
const agent = ({ protocol }) =>
10+
protocol === 'http:' ? httpAgent : httpsAgent
11+
const fetchWithAgent = (url, opts, ...rest) => {
12+
if (!opts) {
13+
opts = { agent }
14+
} else if (!opts.agent) {
15+
opts.agent = agent
16+
}
17+
return fetch(url, opts, ...rest)
18+
}
19+
global.fetch = fetchWithAgent
620
global.Headers = Headers
721
global.Request = Request
822
global.Response = Response
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default async function handler(_req, res) {
2+
const fetchRes = await fetch('http://localhost:44001')
3+
const props = await fetchRes.json()
4+
res.json(props)
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default function SSG(props) {
2+
return <pre id="props">{JSON.stringify(props)}</pre>
3+
}
4+
5+
export async function getStaticProps() {
6+
const res = await fetch('http://localhost:44001')
7+
const props = await res.json()
8+
return { props }
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default function SSR(props) {
2+
return <pre id="props">{JSON.stringify(props)}</pre>
3+
}
4+
5+
export async function getServerSideProps() {
6+
const res = await fetch('http://localhost:44001')
7+
const props = await res.json()
8+
return { props }
9+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint-env jest */
2+
3+
import { join } from 'path'
4+
import { createServer } from 'http'
5+
import {
6+
fetchViaHTTP,
7+
nextBuild,
8+
findPort,
9+
nextStart,
10+
killApp,
11+
} from 'next-test-utils'
12+
import webdriver from 'next-webdriver'
13+
14+
jest.setTimeout(1000 * 60 * 1)
15+
16+
const appDir = join(__dirname, '../')
17+
18+
let appPort
19+
let app
20+
let mockServer
21+
22+
describe('node-fetch-keep-alive', () => {
23+
beforeAll(async () => {
24+
mockServer = createServer((req, res) => {
25+
// we can test request headers by sending them
26+
// back with the response
27+
const { connection } = req.headers
28+
res.end(JSON.stringify({ connection }))
29+
})
30+
mockServer.listen(44001)
31+
const { stdout, stderr } = await nextBuild(appDir, [], {
32+
stdout: true,
33+
stderr: true,
34+
})
35+
if (stdout) console.log(stdout)
36+
if (stderr) console.error(stderr)
37+
appPort = await findPort()
38+
app = await nextStart(appDir, appPort)
39+
})
40+
afterAll(async () => {
41+
await killApp(app)
42+
mockServer.close()
43+
})
44+
45+
it('should send keep-alive for json API', async () => {
46+
const res = await fetchViaHTTP(appPort, '/api/json')
47+
const obj = await res.json()
48+
expect(obj).toEqual({ connection: 'keep-alive' })
49+
})
50+
51+
it('should send keep-alive for getStaticProps', async () => {
52+
const browser = await webdriver(appPort, '/ssg')
53+
const props = await browser.elementById('props').text()
54+
const obj = JSON.parse(props)
55+
expect(obj).toEqual({ connection: 'keep-alive' })
56+
await browser.close()
57+
})
58+
59+
it('should send keep-alive for getServerSideProps', async () => {
60+
const browser = await webdriver(appPort, '/ssr')
61+
const props = await browser.elementById('props').text()
62+
const obj = JSON.parse(props)
63+
expect(obj).toEqual({ connection: 'keep-alive' })
64+
await browser.close()
65+
})
66+
})

0 commit comments

Comments
 (0)