Skip to content

Commit 5a89db9

Browse files
authored
fix(standard-server): unify empty body parsing behavior across envs (#533)
Empty body parsing can be `undefined` in some environments (e.g., Bun), but now it always follows the headers. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved handling of empty or null input streams, ensuring consistent behavior and preventing errors. - Adjusted body processing logic to handle all HTTP methods, including GET and HEAD, for more robust request handling. - **Tests** - Added a new test to verify correct behavior when processing empty streams. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 0da8ff2 commit 5a89db9

File tree

4 files changed

+15
-18
lines changed

4 files changed

+15
-18
lines changed

packages/standard-server-fetch/src/body.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ import { generateContentDisposition, getFilenameFromContentDisposition } from '@
55
import { toEventIterator, toEventStream } from './event-iterator'
66

77
export async function toStandardBody(re: Request | Response): Promise<StandardBody> {
8-
/**
9-
* In native environments like React Native, the body may be `undefined` due to lack of streaming support.
10-
* Therefore, we explicitly check for `null` to indicate an intentionally empty body.
11-
*/
12-
if (re.body === null) {
13-
return undefined
14-
}
15-
168
const contentDisposition = re.headers.get('content-disposition')
179

1810
if (typeof contentDisposition === 'string') {

packages/standard-server-fetch/src/event-iterator.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ describe('toEventIterator', () => {
8282
await expect(stream.getReader().closed).resolves.toBe(undefined)
8383
})
8484

85+
it('with empty stream', async () => {
86+
const generator = toEventIterator(null)
87+
expect(generator).toSatisfy(isAsyncIteratorObject)
88+
expect(await generator.next()).toEqual({ done: true, value: undefined })
89+
expect(await generator.next()).toEqual({ done: true, value: undefined })
90+
})
91+
8592
it('with error event', async () => {
8693
const stream = new ReadableStream<string>({
8794
async pull(controller) {

packages/standard-server-fetch/src/event-iterator.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@ import {
88
} from '@orpc/standard-server'
99

1010
export function toEventIterator(
11-
stream: ReadableStream<Uint8Array>,
11+
stream: ReadableStream<Uint8Array> | null,
1212
): AsyncIteratorObject<unknown | void, unknown | void, void> & AsyncGenerator<unknown | void, unknown | void, void> {
1313
const eventStream = stream
14-
.pipeThrough(new TextDecoderStream())
14+
?.pipeThrough(new TextDecoderStream())
1515
.pipeThrough(new EventDecoderStream())
1616

17-
const reader = eventStream.getReader()
17+
const reader = eventStream?.getReader()
1818

1919
return createAsyncIteratorObject(async () => {
2020
while (true) {
21+
if (reader === undefined) {
22+
return { done: true, value: undefined }
23+
}
24+
2125
const { done, value } = await reader.read()
2226

2327
if (done) {
@@ -57,7 +61,7 @@ export function toEventIterator(
5761
}
5862
}
5963
}, async () => {
60-
await reader.cancel()
64+
await reader?.cancel()
6165
})
6266
}
6367

packages/standard-server-node/src/body.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ import { flattenHeader, generateContentDisposition, getFilenameFromContentDispos
88
import { toEventIterator, toEventStream } from './event-iterator'
99

1010
export async function toStandardBody(req: NodeHttpRequest): Promise<StandardBody> {
11-
const method = req.method ?? 'GET'
12-
13-
if (method === 'GET' || method === 'HEAD') {
14-
return undefined
15-
}
16-
1711
const contentDisposition = req.headers['content-disposition']
1812
const contentType = req.headers['content-type']
1913

0 commit comments

Comments
 (0)