Skip to content

Commit

Permalink
Add support for passing iterable objects as headers (nodejs#2708)
Browse files Browse the repository at this point in the history
* Update tests with iterable object cases

* Add support for iterable object headers

* Update tests with cases of malformed headers

* Add check for malformed headers

* Update lib/core/request.js

Co-authored-by: Mert Can Altın <mertgold60@gmail.com>

* Update lib/core/request.js

Co-authored-by: Mert Can Altın <mertgold60@gmail.com>

* Fix code after unverified improvement broke functionality

---------

Co-authored-by: Mert Can Altın <mertgold60@gmail.com>
  • Loading branch information
2 people authored and crysmags committed Feb 27, 2024
1 parent bcd3ede commit 010924b
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 5 deletions.
17 changes: 13 additions & 4 deletions lib/core/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,19 @@ class Request {
processHeader(this, headers[i], headers[i + 1])
}
} else if (headers && typeof headers === 'object') {
const keys = Object.keys(headers)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
processHeader(this, key, headers[key])
if (headers[Symbol.iterator]) {
for (const header of headers) {
if (!Array.isArray(header) || header.length !== 2) {
throw new InvalidArgumentError('headers must be in key-value pair format')
}
const [key, value] = header
processHeader(this, key, value)
}
} else {
const keys = Object.keys(headers)
for (const key of keys) {
processHeader(this, key, headers[key])
}
}
} else if (headers != null) {
throw new InvalidArgumentError('headers must be an object or an array')
Expand Down
135 changes: 134 additions & 1 deletion test/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const { createServer } = require('node:http')
const { test } = require('tap')
const { request } = require('..')
const { request, errors } = require('..')

test('no-slash/one-slash pathname should be included in req.path', async (t) => {
const pathServer = createServer((req, res) => {
Expand Down Expand Up @@ -246,3 +246,136 @@ test('DispatchOptions#reset', scope => {
})
})
})

test('Should include headers from iterable objects', scope => {
scope.plan(4)

scope.test('Should include headers built with Headers global object', async t => {
const server = createServer((req, res) => {
t.equal('GET', req.method)
t.equal(`localhost:${server.address().port}`, req.headers.host)
t.equal(req.headers.hello, 'world')
res.statusCode = 200
res.end('hello')
})

const headers = new Headers()
headers.set('hello', 'world')

t.plan(3)

t.teardown(server.close.bind(server))

await new Promise((resolve, reject) => {
server.listen(0, (err) => {
if (err != null) reject(err)
else resolve()
})
})

await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
reset: true,
headers
})
})

scope.test('Should include headers built with Map', async t => {
const server = createServer((req, res) => {
t.equal('GET', req.method)
t.equal(`localhost:${server.address().port}`, req.headers.host)
t.equal(req.headers.hello, 'world')
res.statusCode = 200
res.end('hello')
})

const headers = new Map()
headers.set('hello', 'world')

t.plan(3)

t.teardown(server.close.bind(server))

await new Promise((resolve, reject) => {
server.listen(0, (err) => {
if (err != null) reject(err)
else resolve()
})
})

await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
reset: true,
headers
})
})

scope.test('Should include headers built with custom iterable object', async t => {
const server = createServer((req, res) => {
t.equal('GET', req.method)
t.equal(`localhost:${server.address().port}`, req.headers.host)
t.equal(req.headers.hello, 'world')
res.statusCode = 200
res.end('hello')
})

const headers = {
* [Symbol.iterator] () {
yield ['hello', 'world']
}
}

t.plan(3)

t.teardown(server.close.bind(server))

await new Promise((resolve, reject) => {
server.listen(0, (err) => {
if (err != null) reject(err)
else resolve()
})
})

await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
reset: true,
headers
})
})

scope.test('Should throw error if headers iterable object does not yield key-value pairs', async t => {
const server = createServer((req, res) => {
res.end('hello')
})

const headers = {
* [Symbol.iterator] () {
yield 'Bad formatted header'
}
}

t.plan(2)

t.teardown(server.close.bind(server))

await new Promise((resolve, reject) => {
server.listen(0, (err) => {
if (err != null) reject(err)
else resolve()
})
})

await request({
method: 'GET',
origin: `http://localhost:${server.address().port}`,
reset: true,
headers
}).catch((err) => {
t.type(err, errors.InvalidArgumentError)
t.equal(err.message, 'headers must be in key-value pair format')
})
})
})

0 comments on commit 010924b

Please sign in to comment.