diff --git a/docs/api/DispatchInterceptor.md b/docs/api/DispatchInterceptor.md index 652b2e86bf9..7dfc260e32a 100644 --- a/docs/api/DispatchInterceptor.md +++ b/docs/api/DispatchInterceptor.md @@ -1,4 +1,4 @@ -#Interface: DispatchInterceptor +# Interface: DispatchInterceptor Extends: `Function` diff --git a/lib/api/api-stream.js b/lib/api/api-stream.js index 3a8e71a5730..c571a6f79a7 100644 --- a/lib/api/api-stream.js +++ b/lib/api/api-stream.js @@ -104,6 +104,10 @@ class StreamHandler extends AsyncResource { { callback, body: res, contentType, statusCode, statusMessage, headers } ) } else { + if (factory === null) { + return + } + res = this.runInAsyncScope(factory, null, { statusCode, headers, @@ -152,7 +156,7 @@ class StreamHandler extends AsyncResource { onData (chunk) { const { res } = this - return res.write(chunk) + return res ? res.write(chunk) : true } onComplete (trailers) { @@ -160,6 +164,10 @@ class StreamHandler extends AsyncResource { removeSignal(this) + if (!res) { + return + } + this.trailers = util.parseHeaders(trailers) res.end() diff --git a/lib/api/readable.js b/lib/api/readable.js index 508fbdef928..d106568cd4b 100644 --- a/lib/api/readable.js +++ b/lib/api/readable.js @@ -268,7 +268,7 @@ function consumeEnd (consume) { pos += buf.byteLength } - resolve(dst) + resolve(dst.buffer) } else if (type === 'blob') { if (!Blob) { Blob = require('buffer').Blob diff --git a/lib/client.js b/lib/client.js index 065fb563380..00f4467cc8b 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1070,7 +1070,9 @@ function onParserTimeout (parser) { function onSocketReadable () { const { [kParser]: parser } = this - parser.readMore() + if (parser) { + parser.readMore() + } } function onSocketError (err) { diff --git a/package.json b/package.json index 67046ad68a3..31e157de002 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "5.26.3", + "version": "5.26.4", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { @@ -67,11 +67,10 @@ "index-fetch.js", "lib", "types", - "docs", - "scripts/esbuild-build.mjs" + "docs" ], "scripts": { - "build:node": "node scripts/esbuild-build.mjs", + "build:node": "npx esbuild@0.19.4 index-fetch.js --bundle --platform=node --outfile=undici-fetch.js --define:esbuildDetection=1 --keep-names", "prebuild:wasm": "node build/wasm.js --prebuild", "build:wasm": "node build/wasm.js --docker", "lint": "standard | snazzy", @@ -85,7 +84,7 @@ "test:tdd": "tap test/*.js test/diagnostics-channel/*.js -w", "test:typescript": "node scripts/verifyVersion.js 14 || tsd && tsc --skipLibCheck test/imports/undici-import.ts", "test:websocket": "node scripts/verifyVersion.js 18 || tap test/websocket/*.js", - "test:wpt": "node scripts/verifyVersion 18 || (node test/wpt/start-fetch.mjs && node test/wpt/start-FileAPI.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node --no-warnings test/wpt/start-websockets.mjs)", + "test:wpt": "node scripts/verifyVersion 18 || (node test/wpt/start-fetch.mjs && node test/wpt/start-FileAPI.mjs && node test/wpt/start-mimesniff.mjs && node test/wpt/start-xhr.mjs && node --no-warnings --expose-internals test/wpt/start-websockets.mjs)", "coverage": "nyc --reporter=text --reporter=html npm run test", "coverage:ci": "nyc --reporter=lcov npm run test", "bench": "PORT=3042 concurrently -k -s first npm:bench:server npm:bench:run", @@ -110,7 +109,6 @@ "delay": "^5.0.0", "dns-packet": "^5.4.0", "docsify-cli": "^4.4.3", - "esbuild": "^0.19.4", "form-data": "^4.0.0", "formdata-node": "^4.3.1", "https-pem": "^3.0.0", diff --git a/scripts/esbuild-build.mjs b/scripts/esbuild-build.mjs deleted file mode 100644 index ca5886c1a2b..00000000000 --- a/scripts/esbuild-build.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import * as esbuild from 'esbuild' -import fs from 'node:fs' - -const bundle = { - name: 'bundle', - setup (build) { - build.onLoad({ filter: /lib(\/|\\)fetch(\/|\\)index.js/ }, async (args) => { - const text = await fs.promises.readFile(args.path, 'utf8') - - return { - contents: `var esbuildDetection = 1;${text}`, - loader: 'js' - } - }) - } -} - -await esbuild.build({ - entryPoints: ['index-fetch.js'], - bundle: true, - outfile: 'undici-fetch.js', - plugins: [bundle], - platform: 'node' -}) diff --git a/test/client-request.js b/test/client-request.js index 703cedf6803..3e6670523b8 100644 --- a/test/client-request.js +++ b/test/client-request.js @@ -413,7 +413,7 @@ test('request blob', { skip: nodeMajor < 16 }, (t) => { }) test('request arrayBuffer', (t) => { - t.plan(1) + t.plan(2) const obj = { asd: true } const server = createServer((req, res) => { @@ -429,7 +429,10 @@ test('request arrayBuffer', (t) => { path: '/', method: 'GET' }) - t.strictSame(Buffer.from(JSON.stringify(obj)), Buffer.from(await body.arrayBuffer())) + const ab = await body.arrayBuffer() + + t.strictSame(Buffer.from(JSON.stringify(obj)), Buffer.from(ab)) + t.ok(ab instanceof ArrayBuffer) }) }) diff --git a/test/fetch/bundle.js b/test/fetch/bundle.js new file mode 100644 index 00000000000..3b35cfc04aa --- /dev/null +++ b/test/fetch/bundle.js @@ -0,0 +1,20 @@ +'use strict' + +const { test } = require('tap') +const { Response, Request, FormData, Headers } = require('../../undici-fetch') + +test('bundle sets constructor.name and .name properly', (t) => { + t.equal(new Response().constructor.name, 'Response') + t.equal(Response.name, 'Response') + + t.equal(new Request('http://a').constructor.name, 'Request') + t.equal(Request.name, 'Request') + + t.equal(new Headers().constructor.name, 'Headers') + t.equal(Headers.name, 'Headers') + + t.equal(new FormData().constructor.name, 'FormData') + t.equal(FormData.name, 'FormData') + + t.end() +}) diff --git a/test/fetch/http2.js b/test/fetch/http2.js index b426de3cbe5..32fb33e14eb 100644 --- a/test/fetch/http2.js +++ b/test/fetch/http2.js @@ -193,11 +193,11 @@ test( ':status': 200 }) - stream.end('hello h2!') - for await (const chunk of stream) { requestChunks.push(chunk) } + + stream.end('hello h2!') }) t.plan(8) diff --git a/test/issue-2349.js b/test/issue-2349.js new file mode 100644 index 00000000000..a82bb74a261 --- /dev/null +++ b/test/issue-2349.js @@ -0,0 +1,53 @@ +'use strict' + +const { test, skip } = require('tap') +const { nodeMajor } = require('../lib/core/util') +const { Writable } = require('stream') +const { MockAgent, errors, stream } = require('..') + +if (nodeMajor < 16) { + skip('only for node 16') + process.exit(0) +} + +test('stream() does not fail after request has been aborted', async (t) => { + t.plan(1) + + const mockAgent = new MockAgent() + + mockAgent.disableNetConnect() + mockAgent + .get('http://localhost:3333') + .intercept({ + path: '/' + }) + .reply(200, 'ok') + .delay(10) + + const parts = [] + const ac = new AbortController() + + setTimeout(() => ac.abort('nevermind'), 5) + + try { + await stream( + 'http://localhost:3333/', + { + opaque: { parts }, + signal: ac.signal, + dispatcher: mockAgent + }, + ({ opaque: { parts } }) => { + return new Writable({ + write (chunk, _encoding, callback) { + parts.push(chunk) + callback() + } + }) + } + ) + } catch (error) { + console.log(error) + t.equal(error instanceof errors.RequestAbortedError, true) + } +}) diff --git a/test/wpt/start-websockets.mjs b/test/wpt/start-websockets.mjs index 73fd945c4d6..29c9e676d3f 100644 --- a/test/wpt/start-websockets.mjs +++ b/test/wpt/start-websockets.mjs @@ -3,9 +3,18 @@ import { join } from 'path' import { fileURLToPath } from 'url' import { fork } from 'child_process' import { on } from 'events' +import options from 'internal/options' const { WPT_REPORT } = process.env +function isGlobalAvailable () { + if (typeof WebSocket !== 'undefined') { + return true + } + + return typeof options.getOptionValue('--experimental-websocket') === 'boolean' +} + if (process.env.CI) { // TODO(@KhafraDev): figure out *why* these tests are flaky in the CI. // process.exit(0) @@ -22,7 +31,7 @@ child.on('exit', (code) => process.exit(code)) for await (const [message] of on(child, 'message')) { if (message.server) { const runner = new WPTRunner('websockets', message.server, { - appendReport: !!WPT_REPORT, + appendReport: !!WPT_REPORT && isGlobalAvailable(), reportPath: WPT_REPORT }) runner.run()