Skip to content
Open
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
16 changes: 12 additions & 4 deletions lib/core/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,21 @@ function processHeader (request, key, val) {
} else if (headerName === 'transfer-encoding' || headerName === 'keep-alive' || headerName === 'upgrade') {
throw new InvalidArgumentError(`invalid ${headerName} header`)
} else if (headerName === 'connection') {
const value = typeof val === 'string' ? val.toLowerCase() : null
if (value !== 'close' && value !== 'keep-alive') {
// Per RFC 7230 Section 6.1, Connection header can contain
// a comma-separated list of connection option tokens (header names)
const value = typeof val === 'string' ? val : null
if (value === null) {
throw new InvalidArgumentError('invalid connection header')
}

if (value === 'close') {
request.reset = true
for (const token of value.toLowerCase().split(',')) {
const trimmed = token.trim()
if (!isValidHTTPToken(trimmed)) {
throw new InvalidArgumentError('invalid connection header')
}
if (trimmed === 'close') {
request.reset = true
}
}
} else if (headerName === 'expect') {
throw new NotSupportedError('expect header not supported')
Expand Down
2 changes: 1 addition & 1 deletion test/invalid-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ test('invalid headers', (t) => {
path: '/',
method: 'GET',
headers: {
connection: 'asd'
connection: 'invalid header with spaces'
}
}, (err, data) => {
t.ok(err instanceof errors.InvalidArgumentError)
Expand Down
97 changes: 97 additions & 0 deletions test/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -468,3 +468,100 @@ describe('Should include headers from iterable objects', scope => {
})
})
})

describe('connection header per RFC 7230', () => {
test('should allow close', async (t) => {
t = tspl(t, { plan: 1 })

const server = createServer((req, res) => {
res.end('ok')
})

after(() => server.close())
await new Promise((resolve) => server.listen(0, resolve))

const { statusCode, body } = await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
headers: { connection: 'close' }
})
await body.dump()
t.strictEqual(statusCode, 200)
})

test('should allow keep-alive', async (t) => {
t = tspl(t, { plan: 1 })

const server = createServer((req, res) => {
res.end('ok')
})

after(() => server.close())
await new Promise((resolve) => server.listen(0, resolve))

const { statusCode, body } = await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
headers: { connection: 'keep-alive' }
})
await body.dump()
t.strictEqual(statusCode, 200)
})

test('should allow custom header name as connection option', async (t) => {
t = tspl(t, { plan: 1 })

const server = createServer((req, res) => {
res.end('ok')
})

after(() => server.close())
await new Promise((resolve) => server.listen(0, resolve))

const { statusCode, body } = await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
headers: {
'x-custom-header': 'value',
connection: 'x-custom-header'
}
})
await body.dump()
t.strictEqual(statusCode, 200)
})

test('should allow comma-separated list of connection options', async (t) => {
t = tspl(t, { plan: 1 })

const server = createServer((req, res) => {
res.end('ok')
})

after(() => server.close())
await new Promise((resolve) => server.listen(0, resolve))

const { statusCode, body } = await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
headers: {
'x-custom-header': 'value',
connection: 'close, x-custom-header'
}
})
await body.dump()
t.strictEqual(statusCode, 200)
})

test('should reject invalid tokens in connection header', async (t) => {
t = tspl(t, { plan: 2 })

await request({
method: 'GET',
origin: 'http://localhost:1234',
headers: { connection: 'invalid header with spaces' }
}).catch((err) => {
t.ok(err instanceof errors.InvalidArgumentError)
t.strictEqual(err.message, 'invalid connection header')
})
})
})
Loading