Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/script/src/runtime/server/proxy-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ export default defineEventHandler(async (event) => {

// Resolve the fetch body: passthrough streams the raw request, otherwise serialize
let fetchBody: BodyInit | undefined
if (passthroughBody) {
if (passthroughBody && originalHeaders['content-length'] !== '0') {
fetchBody = getRequestWebStream(event) as BodyInit | undefined
}
else if (body !== undefined) {
Expand All @@ -538,7 +538,7 @@ export default defineEventHandler(async (event) => {
credentials: 'omit', // Don't send cookies to third parties
signal: controller.signal,
redirect: 'manual',
duplex: passthroughBody ? 'half' : undefined,
duplex: fetchBody instanceof ReadableStream ? 'half' : undefined,
}
response = await network.fetch(targetUrl, requestInit)
clearTimeout(timeoutId)
Expand Down
24 changes: 24 additions & 0 deletions test/unit/proxy-handler-body.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ describe('proxy handler request bodies (#836)', () => {
let capturedBody = Buffer.alloc(0)
let capturedContentLength: string | undefined
let capturedContentType: string | undefined
let capturedFetchBody: BodyInit | null | undefined
let capturedFetchDuplex: 'half' | undefined
let capturedUrl = ''
let releaseStream: (() => void) | undefined
const realFetch = globalThis.fetch
Expand Down Expand Up @@ -81,6 +83,8 @@ describe('proxy handler request bodies (#836)', () => {
const requestUrl = input instanceof Request ? input.url : String(input)
const url = new URL(requestUrl)
if (url.hostname === 'upstream.test') {
capturedFetchBody = init?.body
capturedFetchDuplex = (init as RequestInit & { duplex?: 'half' } | undefined)?.duplex
const redirected = `http://127.0.0.1:${upstreamPort}${url.pathname}${url.search}`
return realFetch(redirected, init)
}
Expand All @@ -98,6 +102,8 @@ describe('proxy handler request bodies (#836)', () => {
capturedBody = Buffer.alloc(0)
capturedContentLength = undefined
capturedContentType = undefined
capturedFetchBody = undefined
capturedFetchDuplex = undefined
capturedUrl = ''
releaseStream = undefined
})
Expand All @@ -120,10 +126,28 @@ describe('proxy handler request bodies (#836)', () => {
})

expect(response.status).toBe(200)
expect(capturedFetchBody).toBeInstanceOf(ReadableStream)
expect(capturedFetchDuplex).toBe('half')
expect(capturedBody.equals(compressed)).toBe(true)
expect(capturedContentType).toBe('text/plain')
})

it('forwards an explicitly empty opaque POST without a body stream (#853)', async () => {
const response = await realFetch(`http://127.0.0.1:${proxyPort}/_scripts/p/upstream.test/measurement/conversion`, {
method: 'POST',
headers: {
'content-length': '0',
'content-type': 'text/plain;charset=UTF-8',
},
})

expect(response.status).toBe(200)
expect(capturedFetchBody).toBeUndefined()
expect(capturedFetchDuplex).toBeUndefined()
expect(capturedBody).toHaveLength(0)
expect(capturedContentLength).toBe('0')
})

it('rejects an allowlisted local network target before the upstream fetch', async () => {
const response = await realFetch(`http://127.0.0.1:${proxyPort}/_scripts/p/127.0.0.1/private`)

Expand Down
Loading