Skip to content

Commit 2331d7a

Browse files
authored
fix(proxy): skip empty passthrough body streams (#854)
1 parent 2db3380 commit 2331d7a

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎packages/script/src/runtime/server/proxy-handler.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ export default defineEventHandler(async (event) => {
520520

521521
// Resolve the fetch body: passthrough streams the raw request, otherwise serialize
522522
letfetchBody: BodyInit|undefined
523-
if(passthroughBody){
523+
if(passthroughBody&&originalHeaders['content-length']!=='0'){
524524
fetchBody=getRequestWebStream(event)asBodyInit|undefined
525525
}
526526
elseif(body!==undefined){
@@ -538,7 +538,7 @@ export default defineEventHandler(async (event) => {
538538
credentials: 'omit',// Don't send cookies to third parties
539539
signal: controller.signal,
540540
redirect: 'manual',
541-
duplex: passthroughBody ? 'half' : undefined,
541+
duplex: fetchBodyinstanceofReadableStream ? 'half' : undefined,
542542
}
543543
response=awaitnetwork.fetch(targetUrl,requestInit)
544544
clearTimeout(timeoutId)

‎test/unit/proxy-handler-body.test.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ describe('proxy handler request bodies (#836)', () => {
4040
letcapturedBody=Buffer.alloc(0)
4141
letcapturedContentLength: string|undefined
4242
letcapturedContentType: string|undefined
43+
letcapturedFetchBody: BodyInit|null|undefined
44+
letcapturedFetchDuplex: 'half'|undefined
4345
letcapturedUrl=''
4446
letreleaseStream: (()=>void)|undefined
4547
constrealFetch=globalThis.fetch
@@ -81,6 +83,8 @@ describe('proxy handler request bodies (#836)', () => {
8183
constrequestUrl=inputinstanceofRequest ? input.url : String(input)
8284
consturl=newURL(requestUrl)
8385
if(url.hostname==='upstream.test'){
86+
capturedFetchBody=init?.body
87+
capturedFetchDuplex=(initasRequestInit&{duplex?: 'half'}|undefined)?.duplex
8488
constredirected=`http://127.0.0.1:${upstreamPort}${url.pathname}${url.search}`
8589
returnrealFetch(redirected,init)
8690
}
@@ -98,6 +102,8 @@ describe('proxy handler request bodies (#836)', () => {
98102
capturedBody=Buffer.alloc(0)
99103
capturedContentLength=undefined
100104
capturedContentType=undefined
105+
capturedFetchBody=undefined
106+
capturedFetchDuplex=undefined
101107
capturedUrl=''
102108
releaseStream=undefined
103109
})
@@ -120,10 +126,28 @@ describe('proxy handler request bodies (#836)', () => {
120126
})
121127

122128
expect(response.status).toBe(200)
129+
expect(capturedFetchBody).toBeInstanceOf(ReadableStream)
130+
expect(capturedFetchDuplex).toBe('half')
123131
expect(capturedBody.equals(compressed)).toBe(true)
124132
expect(capturedContentType).toBe('text/plain')
125133
})
126134

135+
it('forwards an explicitly empty opaque POST without a body stream (#853)',async()=>{
136+
constresponse=awaitrealFetch(`http://127.0.0.1:${proxyPort}/_scripts/p/upstream.test/measurement/conversion`,{
137+
method: 'POST',
138+
headers: {
139+
'content-length': '0',
140+
'content-type': 'text/plain;charset=UTF-8',
141+
},
142+
})
143+
144+
expect(response.status).toBe(200)
145+
expect(capturedFetchBody).toBeUndefined()
146+
expect(capturedFetchDuplex).toBeUndefined()
147+
expect(capturedBody).toHaveLength(0)
148+
expect(capturedContentLength).toBe('0')
149+
})
150+
127151
it('rejects an allowlisted local network target before the upstream fetch',async()=>{
128152
constresponse=awaitrealFetch(`http://127.0.0.1:${proxyPort}/_scripts/p/127.0.0.1/private`)
129153

0 commit comments

Comments
 (0)