diff --git a/src/utils/framework-server.ts b/src/utils/framework-server.ts index 857e51c5863..52d2d49c206 100644 --- a/src/utils/framework-server.ts +++ b/src/utils/framework-server.ts @@ -49,7 +49,9 @@ export const startFrameworkServer = async function ({ let port: { open: boolean; ipVersion?: 4 | 6 } | undefined try { if (settings.skipWaitPort) { - port = { open: true, ipVersion: 6 } + // default ip version based on node version + const ipVersion = parseInt(process.versions.node.split('.')[0]) >= 18 ? 6 : 4 + port = { open: true, ipVersion } } else { port = await waitPort({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/tests/integration/commands/dev/dev.test.ts b/tests/integration/commands/dev/dev.test.ts index 2045014d04e..1d822a343df 100644 --- a/tests/integration/commands/dev/dev.test.ts +++ b/tests/integration/commands/dev/dev.test.ts @@ -357,6 +357,46 @@ describe.concurrent('command/dev', () => { }) }) + test('should follow 301 redirect to an external server', async (t) => { + await withSiteBuilder(t, async (builder) => { + const externalServer = startExternalServer() + const { port } = externalServer.address() as AddressInfo + builder.withRedirectsFile({ + redirects: [{ from: '/api/*', to: `http://localhost:${port}/:splat`, status: 301 }], + }) + + await builder.build() + + await withDevServer({ cwd: builder.directory }, async (server) => { + const [response1, response2] = await Promise.all([ + fetch(`${server.url}/api/ping`, { follow: 0, redirect: 'manual' }), + fetch(`${server.url}/api/ping`).then((res) => res.json()), + ]) + t.expect(response1.headers.get('location')).toEqual(`http://localhost:${port}/ping`) + + t.expect(response2.body).toStrictEqual({}) + t.expect(response2.method).toEqual('GET') + t.expect(response2.url).toEqual('/ping') + }) + + externalServer.close() + }) + }) + + test('should proxy server without waiting for port', async (t) => { + await withSiteBuilder(t, async (builder) => { + const externalServer = startExternalServer() + await builder.build() + + await withDevServer({ cwd: builder.directory, skipWaitPort: true }, async (server) => { + const response = await fetch(`${server.url}/api/test`) + t.expect(response.status).toBe(404) + }) + + externalServer.close() + }) + }) + test('should rewrite POST request if content-type is missing and not crash dev server', async (t) => { await withSiteBuilder(t, async (builder) => { builder.withNetlifyToml({ diff --git a/tests/integration/utils/dev-server.ts b/tests/integration/utils/dev-server.ts index 81eb02e139b..27407c9994b 100644 --- a/tests/integration/utils/dev-server.ts +++ b/tests/integration/utils/dev-server.ts @@ -47,6 +47,7 @@ interface DevServerOptions { offline?: boolean prompt?: $FIXME[] serve?: boolean + skipWaitPort?: boolean } // 240 seconds @@ -62,6 +63,7 @@ const startServer = async ({ offline = true, prompt, serve = false, + skipWaitPort = false, }: DevServerOptions): Promise => { const port = await getPort() const staticPort = await getPort() @@ -69,7 +71,6 @@ const startServer = async ({ const url = `http://${host}:${port}` console.log(`Starting dev server on port: ${port} in directory ${path.basename(cwd)}`) - const baseCommand = serve ? 'serve' : 'dev' const baseArgs = [ baseCommand, @@ -79,6 +80,7 @@ const startServer = async ({ '--staticServerPort', staticPort, debug ? '--debug' : '', + skipWaitPort ? '--skip-wait-port' : '', ] // We use `null` to override the default context and actually omit the flag