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
59 changes: 59 additions & 0 deletions packages/standard-server-node/src/body.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ beforeEach(() => {
vi.clearAllMocks()
})

function createChunkedRequest(contentType: string, chunks: Buffer[]): IncomingMessage {
const request = Readable.from(chunks) as IncomingMessage
request.headers = {
'content-type': contentType,
}
return request
}

function splitBufferInsideCharacter(text: string, splitCharacter: string): Buffer[] {
const buffer = Buffer.from(text)
const splitBytes = Buffer.from(splitCharacter)
const splitIndex = buffer.indexOf(splitBytes)

if (splitIndex === -1) {
throw new Error(`split character not found: ${splitCharacter}`)
}

return [
buffer.subarray(0, splitIndex + 1),
buffer.subarray(splitIndex + 1),
]
}

describe('toStandardBody', () => {
it('undefined', async () => {
let standardBody: StandardBody = {} as any
Expand Down Expand Up @@ -51,6 +74,42 @@ describe('toStandardBody', () => {
expect(standardBody).toEqual({ foo: 'bar' })
})

it('json with utf-8 characters split across chunk boundaries', async () => {
const original = {
json: {
text: '滚滚长江东逝水',
},
}

const chunks = splitBufferInsideCharacter(JSON.stringify(original), '江')
const request = createChunkedRequest('application/json', chunks)

const standardBody = await toStandardBody(request)

expect(standardBody).toEqual(original)
})

it('text with utf-8 characters split across chunk boundaries', async () => {
const original = '海内存知己,天涯若比邻'
const chunks = splitBufferInsideCharacter(original, '存')
const request = createChunkedRequest('text/plain', chunks)

const standardBody = await toStandardBody(request)

expect(standardBody).toBe(original)
})

it('text with utf-8 characters split across chunk boundaries end with incomplete utf8', async () => {
const original = '海内存知己,天涯若比邻'
const chunks = splitBufferInsideCharacter(original, '存')
const incompleteUtf8 = Buffer.from([230, 181])
const request = createChunkedRequest('text/plain', [...chunks, incompleteUtf8])

const standardBody = await toStandardBody(request)

expect(standardBody).toBe(`${original}�`)
})

it('json but empty body', async () => {
let standardBody: StandardBody = {} as any

Expand Down
11 changes: 7 additions & 4 deletions packages/standard-server-node/src/body.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { StandardBody, StandardHeaders } from '@orpc/standard-server'
import type { Buffer } from 'node:buffer'
import type { ToEventIteratorOptions, ToEventStreamOptions } from './event-iterator'
import type { NodeHttpRequest } from './types'
import { Buffer } from 'node:buffer'
import { Readable } from 'node:stream'
import { isAsyncIteratorObject, parseEmptyableJSON, runWithSpan, stringifyJSON } from '@orpc/shared'
import { flattenHeader, generateContentDisposition, getFilenameFromContentDisposition } from '@orpc/standard-server'
Expand Down Expand Up @@ -114,13 +114,16 @@ function _streamToFormData(stream: Readable, contentType: string): Promise<FormD
}

async function _streamToString(stream: Readable): Promise<string> {
let string = ''
const decoder = new TextDecoder()
let text = ''

for await (const chunk of stream) {
string += chunk.toString()
text += decoder.decode(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk), { stream: true })
}

return string
text += decoder.decode()

return text
}

async function _streamToFile(stream: Readable, fileName: string, contentType: string): Promise<File> {
Expand Down
Loading