Skip to content

Commit e37b5c9

Browse files
committed
fix: respect charset in response
1 parent 78bfd65 commit e37b5c9

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

packages/nuxt-cli/src/commands/curl.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,28 @@ async function writeResponseBody(response: Response, pretty: boolean): Promise<v
277277
return
278278
}
279279

280-
const text = buffer.toString('utf-8')
280+
const text = decodeBody(buffer, contentType)
281281
const body = renderer?.(text) ?? text
282282
process.stdout.write(body)
283283
if (!body.endsWith('\n')) {
284284
process.stdout.write('\n')
285285
}
286286
}
287287

288+
/** Decode with the charset the response declares, falling back to UTF-8 when it is absent or unknown. */
289+
function decodeBody(buffer: Buffer, contentType: string): string {
290+
const charset = /;\s*charset=["']?([\w-]+)/i.exec(contentType)?.[1]
291+
if (charset) {
292+
try {
293+
return new TextDecoder(charset).decode(buffer)
294+
}
295+
catch {
296+
// fall through to UTF-8
297+
}
298+
}
299+
return buffer.toString('utf-8')
300+
}
301+
288302
/**
289303
* A content type we can render, or one matching `TEXT_CONTENT_TYPE_RE`, is
290304
* trusted outright; anything else is sniffed for a NUL byte, which no valid

packages/nuxt-cli/test/unit/commands/curl.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ const server = createServer(async (req, res) => {
6464
return
6565
}
6666

67+
if (req.url === '/latin1') {
68+
res.setHeader('content-type', 'text/plain; charset=iso-8859-1')
69+
res.end(Buffer.from('café', 'latin1'))
70+
return
71+
}
72+
6773
if (req.url === '/binary') {
6874
res.setHeader('content-type', 'application/octet-stream')
6975
res.end(BINARY_BODY)
@@ -305,6 +311,12 @@ describe('curl', () => {
305311
expect(stripVTControlCharacters(stdout)).toBe('<urlset>\n <url>\n <loc>/</loc>\n </url>\n</urlset>\n')
306312
})
307313

314+
it('decodes the body with the charset the response declares', async () => {
315+
process.stdout.isTTY = true
316+
expect(await run([`${origin}/latin1`])).toBe(0)
317+
expect(stdout).toBe('café\n')
318+
})
319+
308320
it('takes a text/plain body at its word, even when it looks like json', async () => {
309321
process.stdout.isTTY = true
310322
expect(await run([`${origin}/untyped`])).toBe(0)

0 commit comments

Comments
 (0)