Skip to content

Commit

Permalink
fix: content-disposition header parsing (nodejs#1911)
Browse files Browse the repository at this point in the history
  • Loading branch information
climba03003 authored and anonrig committed Apr 4, 2023
1 parent ca0423a commit 4bf8f8d
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 4 deletions.
23 changes: 20 additions & 3 deletions lib/core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,42 @@ function parseHeaders (headers, obj = {}) {
for (let i = 0; i < headers.length; i += 2) {
const key = headers[i].toString().toLowerCase()
let val = obj[key]

const encoding = key.length === 19 && key === 'content-disposition'
? 'latin1'
: 'utf8'

if (!val) {
if (Array.isArray(headers[i + 1])) {
obj[key] = headers[i + 1]
} else {
obj[key] = headers[i + 1].toString()
obj[key] = headers[i + 1].toString(encoding)
}
} else {
if (!Array.isArray(val)) {
val = [val]
obj[key] = val
}
val.push(headers[i + 1].toString())
val.push(headers[i + 1].toString(encoding))
}
}
return obj
}

function parseRawHeaders (headers) {
return headers.map(header => header.toString())
const ret = []
for (let n = 0; n < headers.length; n += 2) {
const key = headers[n + 0].toString()

const encoding = key.length === 19 && key.toLowerCase() === 'content-disposition'
? 'latin1'
: 'utf8'

const val = headers[n + 1].toString(encoding)

ret.push(key, val)
}
return ret
}

function isBuffer (buffer) {
Expand Down
6 changes: 5 additions & 1 deletion lib/mock/mock-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,11 @@ function buildKey (opts) {
}

function generateKeyValues (data) {
return Object.entries(data).reduce((keyValuePairs, [key, value]) => [...keyValuePairs, key, value], [])
return Object.entries(data).reduce((keyValuePairs, [key, value]) => [
...keyValuePairs,
Buffer.from(`${key}`),
Array.isArray(value) ? value.map(x => Buffer.from(`${x}`)) : Buffer.from(`${value}`)
], [])
}

/**
Expand Down
77 changes: 77 additions & 0 deletions test/issue-1903.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict'

const { createServer } = require('http')
const { test } = require('tap')
const { request } = require('..')

function createPromise () {
const result = {}
result.promise = new Promise((resolve) => {
result.resolve = resolve
})
return result
}

test('should parse content-disposition consistencely', async (t) => {
t.plan(5)

// create promise to allow server spinup in parallel
const spinup1 = createPromise()
const spinup2 = createPromise()
const spinup3 = createPromise()

// variables to store content-disposition header
const header = []

const server = createServer((req, res) => {
res.writeHead(200, {
'content-length': 2,
'content-disposition': "attachment; filename='år.pdf'"
})
header.push("attachment; filename='år.pdf'")
res.end('OK', spinup1.resolve)
})
t.teardown(server.close.bind(server))
server.listen(0, spinup1.resolve)

const proxy1 = createServer(async (req, res) => {
const { statusCode, headers, body } = await request(`http://localhost:${server.address().port}`, {
method: 'GET'
})
header.push(headers['content-disposition'])
delete headers['transfer-encoding']
res.writeHead(statusCode, headers)
body.pipe(res)
})
t.teardown(proxy1.close.bind(proxy1))
proxy1.listen(0, spinup2.resolve)

const proxy2 = createServer(async (req, res) => {
const { statusCode, headers, body } = await request(`http://localhost:${proxy1.address().port}`, {
method: 'GET'
})
header.push(headers['content-disposition'])
delete headers['transfer-encoding']
res.writeHead(statusCode, headers)
body.pipe(res)
})
t.teardown(proxy2.close.bind(proxy2))
proxy2.listen(0, spinup3.resolve)

// wait until all server spinup
await Promise.all([spinup1.promise, spinup2.promise, spinup3.promise])

const { statusCode, headers, body } = await request(`http://localhost:${proxy2.address().port}`, {
method: 'GET'
})
header.push(headers['content-disposition'])
t.equal(statusCode, 200)
t.equal(await body.text(), 'OK')

// we check header
// must not be the same in first proxy
t.notSame(header[0], header[1])
// chaining always the same value
t.equal(header[1], header[2])
t.equal(header[2], header[3])
})

0 comments on commit 4bf8f8d

Please sign in to comment.