From eab4f0cc57f05ff34b694357d06adb3962d3f5fb Mon Sep 17 00:00:00 2001 From: jongleberry Date: Fri, 30 Aug 2024 21:39:43 -0700 Subject: [PATCH 1/4] github actions: test node@22 --- .github/workflows/node.js.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 8daf816f3..f9d61d8c2 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [18.x, 20.x] + node-version: [18.x, 20.x, 22.x] steps: - uses: actions/checkout@v4 From 056ae99c7520698488f239bd07a9938fbe8534d6 Mon Sep 17 00:00:00 2001 From: jongleberry Date: Fri, 30 Aug 2024 21:40:19 -0700 Subject: [PATCH 2/4] github actions: continue on error for codecov --- .github/workflows/node.js.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index f9d61d8c2..a06e4fd8a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -25,3 +25,4 @@ jobs: - run: npm run lint - run: npm test -- --coverage --maxWorkers 2 - run: npx codecov + continue-on-error: true From d0f854382445749309740a54c54c963410d2a68b Mon Sep 17 00:00:00 2001 From: jongleberry Date: Fri, 30 Aug 2024 21:45:06 -0700 Subject: [PATCH 3/4] docs: add example of using an HTTP2, closes #1833 --- docs/guide.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/guide.md b/docs/guide.md index c3304764d..4ca0cd4d8 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -15,6 +15,7 @@ - [Response Middleware](#response-middleware) - [Async operations](#async-operations) - [Debugging Koa](#debugging-koa) + - [HTTP2](#http2) ## Writing Middleware @@ -246,3 +247,23 @@ app.use(publicFiles); ``` koa:application use static /public +0ms ``` + +## HTTP2 + +Example of setting up an HTTP2 server with Koa using the HTTP compatibility layer: + +```js +import Koa from 'koa' +import http2 from 'node:http2' +import fs from 'node:fs' + +const app = new Koa(); + +const onRequestHandler = app.callback(); +const serverOptions = { + key: fs.readFileSync('key.pem'), + cert: fs.readFileSync('cert.pem') +} + +const server = http2.createSecureServer(serverOptions, onRequestHandler); +``` From 7cf1462d346b97d009a9fa1726e1d066bb45c88f Mon Sep 17 00:00:00 2001 From: Matthieu Tabarin Date: Sat, 31 Aug 2024 05:50:02 +0100 Subject: [PATCH 4/4] Feat: Replace node querystring with URLSearchParams (#1828) Fix #1570 Replace node querystring with URLSearchParams it mimics the way that querystring works ## Checklist - [x] I have ensured my pull request is not behind the main or master branch of the original repository. - [x] I have rebased all commits where necessary so that reviewing this pull request can be done without having to merge it first. - [x] I have written a commit message that passes commitlint linting. - [x] I have ensured that my code changes pass linting tests. - [x] I have ensured that my code changes pass unit tests. - [x] I have described my pull request and the reasons for code changes along with context if necessary. --- __tests__/lib/search-params.js | 40 ++++++++++++++++++++++++++++++++++ lib/request.js | 8 +++---- lib/search-params.js | 33 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 __tests__/lib/search-params.js create mode 100644 lib/search-params.js diff --git a/__tests__/lib/search-params.js b/__tests__/lib/search-params.js new file mode 100644 index 000000000..6d797717b --- /dev/null +++ b/__tests__/lib/search-params.js @@ -0,0 +1,40 @@ +const sp = require('../../lib/search-params') +const assert = require('assert') + +describe('search-params', () => { + describe('stringify', () => { + it('Should stringify a simple object', () => { + assert.deepStrictEqual(sp.stringify({ a: 1, b: 'b' }), 'a=1&b=b') + }) + + it('Should stringify an object with an array', () => { + assert.deepStrictEqual(sp.stringify({ a: [1, 2] }), 'a=1&a=2') + }) + + it('Should stringify an object with an array with a single value', () => { + assert.deepStrictEqual(sp.stringify({ a: [1] }), 'a=1') + }) + + it('Stringify an object with an array with a single empty value', () => { + assert.deepStrictEqual(sp.stringify({ a: [''] }), 'a=') + }) + + it('Should not stringify an object with a nested object', () => { + assert.deepStrictEqual(sp.stringify({ a: { b: 1 } }), 'a=') + }) + }) + + describe('parse', () => { + it('Should parse a simple query string', () => { + assert.deepStrictEqual(sp.parse('a=1&b=2'), { a: '1', b: '2' }) + }) + + it('Should parse a query string with same key and multiple values', () => { + assert.deepEqual(sp.parse('a=1&a=2'), { a: ['1', '2'] }) + }) + + it('Should parse a query string with an array with a single empty value', () => { + assert.deepStrictEqual(sp.parse('a='), { a: '' }) + }) + }) +}) diff --git a/lib/request.js b/lib/request.js index b756a7a06..7136c0d0b 100644 --- a/lib/request.js +++ b/lib/request.js @@ -10,7 +10,8 @@ const accepts = require('accepts') const contentType = require('content-type') const stringify = require('url').format const parse = require('parseurl') -const qs = require('querystring') +const sp = require('./search-params.js') + const typeis = require('type-is') const fresh = require('fresh') const only = require('./only.js') @@ -171,7 +172,7 @@ module.exports = { get query () { const str = this.querystring const c = this._querycache = this._querycache || {} - return c[str] || (c[str] = qs.parse(str)) + return c[str] || (c[str] = sp.parse(str)) }, /** @@ -182,7 +183,7 @@ module.exports = { */ set query (obj) { - this.querystring = qs.stringify(obj) + this.querystring = sp.stringify(obj) }, /** @@ -210,7 +211,6 @@ module.exports = { url.search = str url.path = null - this.url = stringify(url) }, diff --git a/lib/search-params.js b/lib/search-params.js new file mode 100644 index 000000000..cf824d7e0 --- /dev/null +++ b/lib/search-params.js @@ -0,0 +1,33 @@ +const URLSearchParams = require('url').URLSearchParams + +module.exports = { + stringify: (obj) => { + const searchParams = new URLSearchParams() + const addKey = (k, v, params) => { + const val = typeof v === 'string' || typeof v === 'number' ? v : '' + params.append(k, val) + } + + for (const [key, value] of Object.entries(obj)) { + if (Array.isArray(value)) { + const lgth = value.length + for (let i = 0; i < lgth; i++) { + addKey(key, value[i], searchParams) + } + } else { + addKey(key, value, searchParams) + } + } + return searchParams.toString() + }, + + parse: (str) => { + const searchParams = new URLSearchParams(str) + const obj = {} + for (const key of searchParams.keys()) { + const values = searchParams.getAll(key) + obj[key] = values.length <= 1 ? values[0] : values + } + return obj + } +}