From bd481ebd4450f04c9cbfc62cab8b1a7c628d057f Mon Sep 17 00:00:00 2001 From: uzlopak <aras.abbasi@googlemail.com> Date: Mon, 15 Apr 2024 05:40:37 +0200 Subject: [PATCH 1/2] test: increase coverage --- lib/core/request.js | 6 +- lib/core/util.js | 4 +- test/client-request.js | 159 +++++++++++++++++++++++++++++++++++------ test/request.js | 14 ++++ 4 files changed, 156 insertions(+), 27 deletions(-) diff --git a/lib/core/request.js b/lib/core/request.js index 37839d3c949..d77d07670fa 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -7,7 +7,7 @@ const { const assert = require('node:assert') const { isValidHTTPToken, - isValidHeaderChar, + isValidHeaderValue, isStream, destroy, isBuffer, @@ -336,7 +336,7 @@ function processHeader (request, key, val) { const arr = [] for (let i = 0; i < val.length; i++) { if (typeof val[i] === 'string') { - if (!isValidHeaderChar(val[i])) { + if (!isValidHeaderValue(val[i])) { throw new InvalidArgumentError(`invalid ${key} header`) } arr.push(val[i]) @@ -350,7 +350,7 @@ function processHeader (request, key, val) { } val = arr } else if (typeof val === 'string') { - if (!isValidHeaderChar(val)) { + if (!isValidHeaderValue(val)) { throw new InvalidArgumentError(`invalid ${key} header`) } } else if (val === null) { diff --git a/lib/core/util.js b/lib/core/util.js index 2051f5fad2d..47ad0485202 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -547,7 +547,7 @@ const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/ /** * @param {string} characters */ -function isValidHeaderChar (characters) { +function isValidHeaderValue (characters) { return !headerCharRegex.test(characters) } @@ -627,7 +627,7 @@ module.exports = { buildURL, addAbortListener, isValidHTTPToken, - isValidHeaderChar, + isValidHeaderValue, isTokenCharCode, parseRangeHeader, isValidPort, diff --git a/test/client-request.js b/test/client-request.js index 8e0111de09c..96694748501 100644 --- a/test/client-request.js +++ b/test/client-request.js @@ -3,7 +3,7 @@ 'use strict' const { tspl } = require('@matteo.collina/tspl') -const { test, after } = require('node:test') +const { test, after, describe, before } = require('node:test') const { Client, errors } = require('..') const { createServer } = require('node:http') const EE = require('node:events') @@ -11,7 +11,7 @@ const { kConnect } = require('../lib/core/symbols') const { Readable } = require('node:stream') const net = require('node:net') const { promisify } = require('node:util') -const { NotSupportedError } = require('../lib/core/errors') +const { NotSupportedError, InvalidArgumentError } = require('../lib/core/errors') const { parseFormDataString } = require('./utils/formdata') test('request dump big', async (t) => { @@ -53,6 +53,7 @@ test('request dump', async (t) => { t = tspl(t, { plan: 3 }) const server = createServer((req, res) => { + res.setHeader('content-length', 5) res.end('hello') }) after(() => server.close()) @@ -391,34 +392,148 @@ test('request text', async (t) => { await t.completed }) -test('empty host header', async (t) => { - t = tspl(t, { plan: 3 }) +describe('headers', () => { + describe('invalid headers', () => { + test('invalid header value - array with string with invalid character', async (t) => { + t = tspl(t, { plan: 1 }) - const server = createServer((req, res) => { - res.end(req.headers.host) - }) - after(() => server.close()) + const client = new Client('http://localhost:8080') + after(() => client.destroy()) - server.listen(0, async () => { - const serverAddress = `localhost:${server.address().port}` - const client = new Client(`http://${serverAddress}`) - after(() => client.destroy()) + t.rejects(client.request({ + path: '/', + method: 'GET', + headers: { name: ['test\0'] } + }), new InvalidArgumentError('invalid name header')) + + await t.completed + }) + test('invalid header value - array with POJO', async (t) => { + t = tspl(t, { plan: 1 }) + + const client = new Client('http://localhost:8080') + after(() => client.destroy()) - const getWithHost = async (host, wanted) => { - const { body } = await client.request({ + t.rejects(client.request({ path: '/', method: 'GET', - headers: { host } - }) - t.strictEqual(await body.text(), wanted) - } + headers: { name: [{}] } + }), new InvalidArgumentError('invalid name header')) + + await t.completed + }) + + test('invalid header value - string with invalid character', async (t) => { + t = tspl(t, { plan: 1 }) - await getWithHost('test', 'test') - await getWithHost(undefined, serverAddress) - await getWithHost('', '') + const client = new Client('http://localhost:8080') + after(() => client.destroy()) + + t.rejects(client.request({ + path: '/', + method: 'GET', + headers: { name: 'test\0' } + }), new InvalidArgumentError('invalid name header')) + + await t.completed + }) }) - await t.completed + describe('array', () => { + let serverAddress + const server = createServer((req, res) => { + res.end(JSON.stringify(req.headers)) + }) + + before(async () => { + server.listen(0) + await EE.once(server, 'listening') + serverAddress = `localhost:${server.address().port}` + }) + + after(() => server.close()) + + test('empty host header', async (t) => { + t = tspl(t, { plan: 4 }) + + const client = new Client(`http://${serverAddress}`) + after(() => client.destroy()) + + const testCase = async (expected, actual) => { + const { body } = await client.request({ + path: '/', + method: 'GET', + headers: expected + }) + + const result = await body.json() + t.deepStrictEqual(result, { ...result, ...actual }) + } + + await testCase({ key: [null] }, { key: '' }) + await testCase({ key: ['test'] }, { key: 'test' }) + await testCase({ key: ['test', 'true'] }, { key: 'test, true' }) + await testCase({ key: ['test', true] }, { key: 'test, true' }) + + await t.completed + }) + }) + + describe('host', () => { + let serverAddress + const server = createServer((req, res) => { + res.end(req.headers.host) + }) + + before(async () => { + server.listen(0) + await EE.once(server, 'listening') + serverAddress = `localhost:${server.address().port}` + }) + + after(() => server.close()) + + test('invalid host header', async (t) => { + t = tspl(t, { plan: 1 }) + + const client = new Client(`http://${serverAddress}`) + after(() => client.destroy()) + + t.rejects(client.request({ + path: '/', + method: 'GET', + headers: { + host: [ + 'www.example.com' + ] + } + }), new InvalidArgumentError('invalid host header')) + + await t.completed + }) + + test('empty host header', async (t) => { + t = tspl(t, { plan: 3 }) + + const client = new Client(`http://${serverAddress}`) + after(() => client.destroy()) + + const getWithHost = async (host, wanted) => { + const { body } = await client.request({ + path: '/', + method: 'GET', + headers: { host } + }) + t.strictEqual(await body.text(), wanted) + } + + await getWithHost('test', 'test') + await getWithHost(undefined, serverAddress) + await getWithHost('', '') + + await t.completed + }) + }) }) test('request long multibyte text', async (t) => { diff --git a/test/request.js b/test/request.js index 628bed0d415..d515133e0a4 100644 --- a/test/request.js +++ b/test/request.js @@ -163,6 +163,20 @@ test('Absolute URL as pathname should be included in req.path', async (t) => { t.end() }) +describe('DispatchOptions#expectContinue', () => { + test('Should throw if invalid expectContinue option', async t => { + t = tspl(t, { plan: 1 }) + + await t.rejects(request({ + method: 'GET', + origin: 'http://somehost.xyz', + expectContinue: 0 + }), /invalid expectContinue/) + + await t.completed + }) +}) + describe('DispatchOptions#reset', () => { test('Should throw if invalid reset option', async t => { t = tspl(t, { plan: 1 }) From 4c59f4dffb341dc51df71832051aa1eb79578105 Mon Sep 17 00:00:00 2001 From: uzlopak <aras.abbasi@googlemail.com> Date: Mon, 15 Apr 2024 05:58:39 +0200 Subject: [PATCH 2/2] remove redundant line --- lib/core/request.js | 2 -- test/client-request.js | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/core/request.js b/lib/core/request.js index d77d07670fa..c44cd0891f5 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -355,8 +355,6 @@ function processHeader (request, key, val) { } } else if (val === null) { val = '' - } else if (typeof val === 'object') { - throw new InvalidArgumentError(`invalid ${key} header`) } else { val = `${val}` } diff --git a/test/client-request.js b/test/client-request.js index 96694748501..e71a034408d 100644 --- a/test/client-request.js +++ b/test/client-request.js @@ -437,6 +437,21 @@ describe('headers', () => { await t.completed }) + + test('invalid header value - object', async (t) => { + t = tspl(t, { plan: 1 }) + + const client = new Client('http://localhost:8080') + after(() => client.destroy()) + + t.rejects(client.request({ + path: '/', + method: 'GET', + headers: { name: new Date() } + }), new InvalidArgumentError('invalid name header')) + + await t.completed + }) }) describe('array', () => {