Skip to content

Commit f334be1

Browse files
committed
fix: support for redirects with 200 status. Fix crashing dev server when have socket error. Follow redirects from proxyRes
1 parent a87fd59 commit f334be1

File tree

10 files changed

+143
-17
lines changed

10 files changed

+143
-17
lines changed

src/utils/proxy.ts

+20-12
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,14 @@ const formatEdgeFunctionError = (errorBuffer, acceptsHtml) => {
122122
})
123123
}
124124

125-
function isInternal(url?: string): boolean {
125+
function isInternalFunctions(url?: string): boolean {
126126
return url?.startsWith('/.netlify/') ?? false
127127
}
128128

129+
function isInternal(url?: string): boolean {
130+
return url?.startsWith('/') ?? false
131+
}
132+
129133
function isFunction(functionsPort: boolean | number | undefined, url: string) {
130134
return functionsPort && url.match(DEFAULT_FUNCTION_URL_EXPRESSION)
131135
}
@@ -202,7 +206,7 @@ const handleAddonUrl = function ({ addonUrl, req, res }) {
202206

203207
// @ts-expect-error TS(7006) FIXME: Parameter 'match' implicitly has an 'any' type.
204208
const isRedirect = function (match) {
205-
return match.status && match.status >= 300 && match.status <= 400
209+
return match.status && match.status >= 300 && match.status < 400
206210
}
207211

208212
// @ts-expect-error TS(7006) FIXME: Parameter 'publicFolder' implicitly has an 'any' t... Remove this comment to see the full error message
@@ -417,8 +421,8 @@ const serveRedirect = async function ({
417421
const ct = req.headers['content-type'] ? contentType.parse(req).type : ''
418422
if (
419423
req.method === 'POST' &&
420-
!isInternal(req.url) &&
421-
!isInternal(destURL) &&
424+
!isInternalFunctions(req.url) &&
425+
!isInternalFunctions(destURL) &&
422426
(ct.endsWith('/x-www-form-urlencoded') || ct === 'multipart/form-data')
423427
) {
424428
return proxy.web(req, res, { target: options.functionsServer })
@@ -452,9 +456,11 @@ const serveRedirect = async function ({
452456

453457
return proxy.web(req, res, { headers: functionHeaders, target: options.functionsServer })
454458
}
459+
455460
if (isImageRequest(req)) {
456461
return imageProxy(req, res)
457462
}
463+
458464
const addonUrl = getAddonUrl(options.addonsUrls, req)
459465
if (addonUrl) {
460466
return handleAddonUrl({ req, res, addonUrl })
@@ -519,11 +525,14 @@ const initializeProxy = async function ({
519525
}
520526
})
521527

522-
proxy.on('error', (_, req, res) => {
523-
// @ts-expect-error TS(2339) FIXME: Property 'writeHead' does not exist on type 'Socke... Remove this comment to see the full error message
524-
res.writeHead(500, {
525-
'Content-Type': 'text/plain',
526-
})
528+
proxy.on('error', (err, req, res) => {
529+
console.error('Got error from proxy', err);
530+
531+
if (res instanceof http.ServerResponse) {
532+
res.writeHead(500, {
533+
'Content-Type': 'text/plain',
534+
})
535+
}
527536

528537
const message = isEdgeFunctionsRequest(req)
529538
? 'There was an error with an Edge Function. Please check the terminal for more details.'
@@ -596,8 +605,7 @@ const initializeProxy = async function ({
596605
}
597606
}
598607

599-
// @ts-expect-error TS(2339) FIXME: Property 'proxyOptions' does not exist on type 'In... Remove this comment to see the full error message
600-
if (req.proxyOptions.staticFile && isRedirect({ status: proxyRes.statusCode }) && proxyRes.headers.location) {
608+
if (isRedirect({ status: proxyRes.statusCode }) && proxyRes.headers.location) {
601609
req.url = proxyRes.headers.location
602610
return serveRedirect({
603611
// We don't want to match functions at this point because any redirects
@@ -838,7 +846,7 @@ const onRequest = async (
838846
hasFormSubmissionHandler &&
839847
functionsServer &&
840848
req.method === 'POST' &&
841-
!isInternal(req.url) &&
849+
!isInternalFunctions(req.url) &&
842850
(ct.endsWith('/x-www-form-urlencoded') || ct === 'multipart/form-data')
843851
) {
844852
return proxy.web(req, res, { target: functionsServer })

tests/integration/__fixtures__/hugo-site/netlify.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
command = "npm run serve"
33
framework = "#custom"
44
functions = "functions/"
5-
port = 7000
5+
port = 7001
66
publish = "out"
77
targetPort = 1313

tests/integration/__fixtures__/next-app/app/page.js

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ export default function Home() {
55
return (
66
<main className={styles.main}>
77
<div className={styles.description}>
8+
<p>
9+
Local site Dev Server
10+
</p>
811
<p>
912
Get started by editing&nbsp;
1013
<code className={styles.code}>app/page.js</code>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function ExistsPage() {
2+
return (
3+
<p>
4+
Exists page
5+
</p>
6+
);
7+
}

tests/integration/__fixtures__/next-app/netlify.toml

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ publish = "public"
44
[dev]
55
targetPort = 6123
66

7+
[[redirects]]
8+
from = "/local/*"
9+
to = "/:splat"
10+
status = 200
11+
12+
[[redirects]]
13+
from = "/test/*"
14+
to = "https://www.netlify.app/:splat"
15+
status = 200
16+
717
[[redirects]]
818
from = "*"
919
to = "https://www.netlify.app/:splat"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.netlify
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const http = require('http')
2+
3+
const server = http.createServer((req, res) => {
4+
const pathname = new URL(req.url, 'http://localhost').pathname
5+
6+
console.log(`Got ${pathname}`)
7+
8+
if (pathname === '/') {
9+
res.write('Root page')
10+
res.end()
11+
} else if (pathname === '/test/exists') {
12+
res.writeHead(302, undefined, { location: '/test/exists/' })
13+
res.end()
14+
} else if (pathname === '/test/exists/') {
15+
res.write('Test exists page')
16+
res.end()
17+
} else {
18+
res.writeHead(404).write('Page is not found')
19+
res.end()
20+
}
21+
})
22+
23+
server.listen(6124, () => {
24+
console.log('Server is Running')
25+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[dev]
2+
targetPort = 6124
3+
command = "npm run dev"
4+
5+
[[redirects]]
6+
from = "/local/*"
7+
to = "/:splat"
8+
status = 200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "site-with-redirect",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "node index.js"
7+
},
8+
"dependencies": {
9+
}
10+
}

tests/integration/commands/dev/redirects.test.ts

+58-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import fetch from 'node-fetch'
66
describe('redirects', () => {
77
setupFixtureTests('dev-server-with-functions', { devServer: true }, () => {
88
test<FixtureTestContext>('should send original query params to functions', async ({ devServer }) => {
9-
const response = await fetch(`http://localhost:${devServer.port}/with-params?param2=world&other=1`, {})
9+
const response = await fetch(`http://localhost:${devServer.port}/with-params?param2=world&other=1`)
1010

1111
expect(response.status).toBe(200)
1212

@@ -19,7 +19,7 @@ describe('redirects', () => {
1919
test<FixtureTestContext>('should send original query params to functions when using duplicate parameters', async ({
2020
devServer,
2121
}) => {
22-
const response = await fetch(`http://localhost:${devServer.port}/api/echo?param=hello&param=world`, {})
22+
const response = await fetch(`http://localhost:${devServer.port}/api/echo?param=hello&param=world`)
2323

2424
expect(response.status).toBe(200)
2525

@@ -31,23 +31,77 @@ describe('redirects', () => {
3131

3232
setupFixtureTests('next-app', { devServer: { env: { NETLIFY_DEV_SERVER_CHECK_SSG_ENDPOINTS: 1 } } }, () => {
3333
test<FixtureTestContext>('should prefer local files instead of redirect when not forced', async ({ devServer }) => {
34-
const response = await fetch(`http://localhost:${devServer.port}/test.txt`, {})
34+
const response = await fetch(`http://localhost:${devServer.port}/test.txt`)
3535

3636
expect(response.status).toBe(200)
3737

3838
const result = await response.text()
3939
expect(result.trim()).toEqual('hello world')
40+
expect(devServer?.output).not.toContain('Proxying to https://www.netlify.app');
4041
})
4142

4243
test<FixtureTestContext>('should check for the dynamic page existence before doing redirect', async ({
4344
devServer,
4445
}) => {
45-
const response = await fetch(`http://localhost:${devServer.port}/`, {})
46+
const response = await fetch(`http://localhost:${devServer.port}/`)
4647

4748
expect(response.status).toBe(200)
4849

4950
const result = await response.text()
51+
expect(result.toLowerCase()).toContain('local site dev server')
5052
expect(result.toLowerCase()).not.toContain('netlify')
53+
expect(devServer?.output).not.toContain('Proxying to https://www.netlify.app');
54+
})
55+
56+
test<FixtureTestContext>('nested route redirect check for the page existence', async ({
57+
devServer,
58+
}) => {
59+
let response = await fetch(`http://localhost:${devServer.port}/test/exists`)
60+
expect(response.status).toBe(200)
61+
62+
let result = await response.text()
63+
expect(result.toLowerCase()).toContain('exists page')
64+
expect(devServer?.output).not.toContain('Proxying to https://www.netlify.app/exists');
65+
66+
response = await fetch(`http://localhost:${devServer.port}/test/about`)
67+
expect(response.status).toBe(200)
68+
69+
result = await response.text()
70+
expect(result.toLowerCase()).toContain('netlify')
71+
72+
expect(devServer?.output).toContain('Proxying to https://www.netlify.app/about');
73+
})
74+
75+
test<FixtureTestContext>('should do local redirect', async ({
76+
devServer,
77+
}) => {
78+
const response = await fetch(`http://localhost:${devServer.port}/local/test/exists`)
79+
80+
expect(response.status).toBe(200)
81+
82+
const result = await response.text()
83+
expect(response.headers.get('location')).toBeNull()
84+
expect(response.status).toBe(200)
85+
expect(result.toLowerCase()).toContain('exists page')
86+
expect(result.toLowerCase()).not.toContain('netlify')
87+
expect(devServer?.output).not.toContain('Proxying to https://www.netlify.app/test');
88+
})
89+
})
90+
91+
setupFixtureTests('site-with-redirect', { devServer: true }, () => {
92+
test<FixtureTestContext>('should do local redirect', async ({
93+
devServer,
94+
}) => {
95+
const response = await fetch(`http://localhost:${devServer.port}/local/test/exists`)
96+
97+
expect(response.status).toBe(200)
98+
99+
const result = await response.text()
100+
expect(response.url).toEqual(`http://localhost:${devServer.port}/local/test/exists`)
101+
expect(response.status).toBe(200)
102+
expect(result.toLowerCase()).toContain('exists page')
103+
expect(result.toLowerCase()).not.toContain('netlify')
104+
expect(devServer?.output).not.toContain('Proxying to https://www.netlify.app');
51105
})
52106
})
53107
})

0 commit comments

Comments
 (0)