| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,34 +18,23 @@ npm i undici | |||
| 18 | 18 | ## Benchmarks | |
| 19 | 19 | ||
| 20 | 20 | The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a | |
| 21 | - number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0. | ||
| 22 | - | ||
| 23 | - ### Connections 1 | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | Tests | Samples | Result | Tolerance | Difference with slowest | | ||
| 27 | - |---------------------|---------|---------------|-----------|-------------------------| | ||
| 28 | - | http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - | | ||
| 29 | - | http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % | | ||
| 30 | - | undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % | | ||
| 31 | - | undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % | | ||
| 32 | - | undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % | | ||
| 33 | - | undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % | | ||
| 34 | - | undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % | | ||
| 35 | - | ||
| 36 | - | ||
| 37 | - ### Connections 50 | ||
| 38 | - | ||
| 39 | - | Tests | Samples | Result | Tolerance | Difference with slowest | | ||
| 40 | - |---------------------|---------|------------------|-----------|-------------------------| | ||
| 41 | - | undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - | | ||
| 42 | - | http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % | | ||
| 43 | - | http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % | | ||
| 44 | - | undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % | | ||
| 45 | - | undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % | | ||
| 46 | - | undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % | | ||
| 47 | - | undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % | | ||
| 21 | + 50 TCP connections with a pipelining depth of 10 running on Node 20.10.0. | ||
| 48 | 22 | ||
| 23 | + ``` | ||
| 24 | + │ Tests │ Samples │ Result │ Tolerance │ Difference with slowest │ | ||
| 25 | + |─────────────────────|─────────|─────────────────|───────────|─────────────────────────| | ||
| 26 | + │ got │ 45 │ 1661.71 req/sec │ ± 2.93 % │ - │ | ||
| 27 | + │ node-fetch │ 20 │ 2164.81 req/sec │ ± 2.63 % │ + 30.28 % │ | ||
| 28 | + │ undici - fetch │ 35 │ 2274.27 req/sec │ ± 2.70 % │ + 36.86 % │ | ||
| 29 | + │ http - no keepalive │ 15 │ 2376.04 req/sec │ ± 2.99 % │ + 42.99 % │ | ||
| 30 | + │ axios │ 25 │ 2612.93 req/sec │ ± 2.89 % │ + 57.24 % │ | ||
| 31 | + │ request │ 40 │ 2712.19 req/sec │ ± 2.92 % │ + 63.22 % │ | ||
| 32 | + │ http - keepalive │ 45 │ 4393.25 req/sec │ ± 2.86 % │ + 164.38 % │ | ||
| 33 | + │ undici - pipeline │ 45 │ 5484.69 req/sec │ ± 2.87 % │ + 230.06 % │ | ||
| 34 | + │ undici - request │ 55 │ 7773.98 req/sec │ ± 2.93 % │ + 367.83 % │ | ||
| 35 | + │ undici - stream │ 70 │ 8425.96 req/sec │ ± 2.91 % │ + 407.07 % │ | ||
| 36 | + │ undici - dispatch │ 50 │ 9488.99 req/sec │ ± 2.85 % │ + 471.04 % │ | ||
| 37 | + ``` | ||
| 49 | 38 | ||
| 50 | 39 | ## Quick Start | |
| 51 | 40 | ||
@@ -62,9 +51,7 @@ const { | |||
| 62 | 51 | console.log('response received', statusCode) | |
| 63 | 52 | console.log('headers', headers) | |
| 64 | 53 | ||
| 65 | - for await (const data of body) { | ||
| 66 | - console.log('data', data) | ||
| 67 | - } | ||
| 54 | + for await (const data of body) { console.log('data', data) } | ||
| 68 | 55 | ||
| 69 | 56 | console.log('trailers', trailers) | |
| 70 | 57 | ``` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,10 +53,10 @@ This flag enables debug statements for the `Websocket` API. | |||
| 53 | 53 | > **Note**: statements can overlap with `UNDICI` ones if `undici` or `fetch` flag has been enabled as well. | |
| 54 | 54 | ||
| 55 | 55 | ```sh | |
| 56 | - NODE_DEBUG=fetch node script.js | ||
| 56 | + NODE_DEBUG=websocket node script.js | ||
| 57 | 57 | ||
| 58 | 58 | WEBSOCKET 18309: connecting to echo.websocket.org using https:h1 | |
| 59 | 59 | WEBSOCKET 18309: connected to echo.websocket.org using https:h1 | |
| 60 | 60 | WEBSOCKET 18309: sending request to GET https://echo.websocket.org// | |
| 61 | 61 | WEBSOCKET 18309: connection opened <ip_address> | |
| 62 | - ``` | ||
| 62 | + ``` | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + # EventSource | ||
| 2 | + | ||
| 3 | + Undici exposes a WHATWG spec-compliant implementation of [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) | ||
| 4 | + for [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events). | ||
| 5 | + | ||
| 6 | + ## Instantiating EventSource | ||
| 7 | + | ||
| 8 | + Undici exports a EventSource class. You can instantiate the EventSource as | ||
| 9 | + follows: | ||
| 10 | + | ||
| 11 | + ```mjs | ||
| 12 | + import { EventSource } from 'undici' | ||
| 13 | + | ||
| 14 | + const evenSource = new EventSource('http://localhost:3000') | ||
| 15 | + evenSource.onmessage = (event) => { | ||
| 16 | + console.log(event.data) | ||
| 17 | + } | ||
| 18 | + ``` | ||
| 19 | + | ||
| 20 | + More information about the EventSource API can be found on | ||
| 21 | + [MDN](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,96 @@ | |||
| 1 | + # Class: RedirectHandler | ||
| 2 | + | ||
| 3 | + A class that handles redirection logic for HTTP requests. | ||
| 4 | + | ||
| 5 | + ## `new RedirectHandler(dispatch, maxRedirections, opts, handler, redirectionLimitReached)` | ||
| 6 | + | ||
| 7 | + Arguments: | ||
| 8 | + | ||
| 9 | + - **dispatch** `function` - The dispatch function to be called after every retry. | ||
| 10 | + - **maxRedirections** `number` - Maximum number of redirections allowed. | ||
| 11 | + - **opts** `object` - Options for handling redirection. | ||
| 12 | + - **handler** `object` - An object containing handlers for different stages of the request lifecycle. | ||
| 13 | + - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior. | ||
| 14 | + | ||
| 15 | + Returns: `RedirectHandler` | ||
| 16 | + | ||
| 17 | + ### Parameters | ||
| 18 | + | ||
| 19 | + - **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every redirection. | ||
| 20 | + - **maxRedirections** `number` (required) - Maximum number of redirections allowed. | ||
| 21 | + - **opts** `object` (required) - Options for handling redirection. | ||
| 22 | + - **handler** `object` (required) - Handlers for different stages of the request lifecycle. | ||
| 23 | + - **redirectionLimitReached** `boolean` (default: `false`) - A flag that the implementer can provide to enable or disable the feature. If set to `false`, it indicates that the caller doesn't want to use the feature and prefers the old behavior. | ||
| 24 | + | ||
| 25 | + ### Properties | ||
| 26 | + | ||
| 27 | + - **location** `string` - The current redirection location. | ||
| 28 | + - **abort** `function` - The abort function. | ||
| 29 | + - **opts** `object` - The options for handling redirection. | ||
| 30 | + - **maxRedirections** `number` - Maximum number of redirections allowed. | ||
| 31 | + - **handler** `object` - Handlers for different stages of the request lifecycle. | ||
| 32 | + - **history** `Array` - An array representing the history of URLs during redirection. | ||
| 33 | + - **redirectionLimitReached** `boolean` - Indicates whether the redirection limit has been reached. | ||
| 34 | + | ||
| 35 | + ### Methods | ||
| 36 | + | ||
| 37 | + #### `onConnect(abort)` | ||
| 38 | + | ||
| 39 | + Called when the connection is established. | ||
| 40 | + | ||
| 41 | + Parameters: | ||
| 42 | + | ||
| 43 | + - **abort** `function` - The abort function. | ||
| 44 | + | ||
| 45 | + #### `onUpgrade(statusCode, headers, socket)` | ||
| 46 | + | ||
| 47 | + Called when an upgrade is requested. | ||
| 48 | + | ||
| 49 | + Parameters: | ||
| 50 | + | ||
| 51 | + - **statusCode** `number` - The HTTP status code. | ||
| 52 | + - **headers** `object` - The headers received in the response. | ||
| 53 | + - **socket** `object` - The socket object. | ||
| 54 | + | ||
| 55 | + #### `onError(error)` | ||
| 56 | + | ||
| 57 | + Called when an error occurs. | ||
| 58 | + | ||
| 59 | + Parameters: | ||
| 60 | + | ||
| 61 | + - **error** `Error` - The error that occurred. | ||
| 62 | + | ||
| 63 | + #### `onHeaders(statusCode, headers, resume, statusText)` | ||
| 64 | + | ||
| 65 | + Called when headers are received. | ||
| 66 | + | ||
| 67 | + Parameters: | ||
| 68 | + | ||
| 69 | + - **statusCode** `number` - The HTTP status code. | ||
| 70 | + - **headers** `object` - The headers received in the response. | ||
| 71 | + - **resume** `function` - The resume function. | ||
| 72 | + - **statusText** `string` - The status text. | ||
| 73 | + | ||
| 74 | + #### `onData(chunk)` | ||
| 75 | + | ||
| 76 | + Called when data is received. | ||
| 77 | + | ||
| 78 | + Parameters: | ||
| 79 | + | ||
| 80 | + - **chunk** `Buffer` - The data chunk received. | ||
| 81 | + | ||
| 82 | + #### `onComplete(trailers)` | ||
| 83 | + | ||
| 84 | + Called when the request is complete. | ||
| 85 | + | ||
| 86 | + Parameters: | ||
| 87 | + | ||
| 88 | + - **trailers** `object` - The trailers received. | ||
| 89 | + | ||
| 90 | + #### `onBodySent(chunk)` | ||
| 91 | + | ||
| 92 | + Called when the request body is sent. | ||
| 93 | + | ||
| 94 | + Parameters: | ||
| 95 | + | ||
| 96 | + - **chunk** `Buffer` - The chunk of the request body sent. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,14 +21,6 @@ const DecoratorHandler = require('./lib/handler/DecoratorHandler') | |||
| 21 | 21 | const RedirectHandler = require('./lib/handler/RedirectHandler') | |
| 22 | 22 | const createRedirectInterceptor = require('./lib/interceptor/redirectInterceptor') | |
| 23 | 23 | ||
| 24 | - let hasCrypto | ||
| 25 | - try { | ||
| 26 | - require('crypto') | ||
| 27 | - hasCrypto = true | ||
| 28 | - } catch { | ||
| 29 | - hasCrypto = false | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | 24 | Object.assign(Dispatcher.prototype, api) | |
| 33 | 25 | ||
| 34 | 26 | module.exports.Dispatcher = Dispatcher | |
@@ -102,14 +94,10 @@ function makeDispatcher (fn) { | |||
| 102 | 94 | module.exports.setGlobalDispatcher = setGlobalDispatcher | |
| 103 | 95 | module.exports.getGlobalDispatcher = getGlobalDispatcher | |
| 104 | 96 | ||
| 105 | - let fetchImpl = null | ||
| 106 | - module.exports.fetch = async function fetch (resource) { | ||
| 107 | - if (!fetchImpl) { | ||
| 108 | - fetchImpl = require('./lib/fetch').fetch | ||
| 109 | - } | ||
| 110 | - | ||
| 97 | + const fetchImpl = require('./lib/fetch').fetch | ||
| 98 | + module.exports.fetch = async function fetch (init, options = undefined) { | ||
| 111 | 99 | try { | |
| 112 | - return await fetchImpl(...arguments) | ||
| 100 | + return await fetchImpl(init, options) | ||
| 113 | 101 | } catch (err) { | |
| 114 | 102 | if (typeof err === 'object') { | |
| 115 | 103 | Error.captureStackTrace(err, this) | |
@@ -149,11 +137,7 @@ const { parseMIMEType, serializeAMimeType } = require('./lib/fetch/dataURL') | |||
| 149 | 137 | module.exports.parseMIMEType = parseMIMEType | |
| 150 | 138 | module.exports.serializeAMimeType = serializeAMimeType | |
| 151 | 139 | ||
| 152 | - if (hasCrypto) { | ||
| 153 | - const { WebSocket } = require('./lib/websocket/websocket') | ||
| 154 | - | ||
| 155 | - module.exports.WebSocket = WebSocket | ||
| 156 | - } | ||
| 140 | + module.exports.WebSocket = require('./lib/websocket/websocket').WebSocket | ||
| 157 | 141 | ||
| 158 | 142 | module.exports.request = makeDispatcher(api.request) | |
| 159 | 143 | module.exports.stream = makeDispatcher(api.stream) | |
@@ -165,3 +149,7 @@ module.exports.MockClient = MockClient | |||
| 165 | 149 | module.exports.MockPool = MockPool | |
| 166 | 150 | module.exports.MockAgent = MockAgent | |
| 167 | 151 | module.exports.mockErrors = mockErrors | |
| 152 | + | ||
| 153 | + const { EventSource } = require('./lib/eventsource/eventsource') | ||
| 154 | + | ||
| 155 | + module.exports.EventSource = EventSource | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -94,7 +94,7 @@ module.exports = class BodyReadable extends Readable { | |||
| 94 | 94 | } | |
| 95 | 95 | ||
| 96 | 96 | push (chunk) { | |
| 97 | - if (this[kConsume] && chunk !== null && this.readableLength === 0) { | ||
| 97 | + if (this[kConsume] && chunk !== null) { | ||
| 98 | 98 | consumePush(this[kConsume], chunk) | |
| 99 | 99 | return this[kReading] ? super.push(chunk) : true | |
| 100 | 100 | } | |
@@ -215,26 +215,28 @@ async function consume (stream, type) { | |||
| 215 | 215 | reject(rState.errored ?? new TypeError('unusable')) | |
| 216 | 216 | } | |
| 217 | 217 | } else { | |
| 218 | - stream[kConsume] = { | ||
| 219 | - type, | ||
| 220 | - stream, | ||
| 221 | - resolve, | ||
| 222 | - reject, | ||
| 223 | - length: 0, | ||
| 224 | - body: [] | ||
| 225 | - } | ||
| 218 | + queueMicrotask(() => { | ||
| 219 | + stream[kConsume] = { | ||
| 220 | + type, | ||
| 221 | + stream, | ||
| 222 | + resolve, | ||
| 223 | + reject, | ||
| 224 | + length: 0, | ||
| 225 | + body: [] | ||
| 226 | + } | ||
| 226 | 227 | ||
| 227 | - stream | ||
| 228 | - .on('error', function (err) { | ||
| 229 | - consumeFinish(this[kConsume], err) | ||
| 230 | - }) | ||
| 231 | - .on('close', function () { | ||
| 232 | - if (this[kConsume].body !== null) { | ||
| 233 | - consumeFinish(this[kConsume], new RequestAbortedError()) | ||
| 234 | - } | ||
| 235 | - }) | ||
| 228 | + stream | ||
| 229 | + .on('error', function (err) { | ||
| 230 | + consumeFinish(this[kConsume], err) | ||
| 231 | + }) | ||
| 232 | + .on('close', function () { | ||
| 233 | + if (this[kConsume].body !== null) { | ||
| 234 | + consumeFinish(this[kConsume], new RequestAbortedError()) | ||
| 235 | + } | ||
| 236 | + }) | ||
| 236 | 237 | ||
| 237 | - queueMicrotask(() => consumeStart(stream[kConsume])) | ||
| 238 | + consumeStart(stream[kConsume]) | ||
| 239 | + }) | ||
| 238 | 240 | } | |
| 239 | 241 | }) | |
| 240 | 242 | } | |
@@ -246,8 +248,16 @@ function consumeStart (consume) { | |||
| 246 | 248 | ||
| 247 | 249 | const { _readableState: state } = consume.stream | |
| 248 | 250 | ||
| 249 | - for (const chunk of state.buffer) { | ||
| 250 | - consumePush(consume, chunk) | ||
| 251 | + if (state.bufferIndex) { | ||
| 252 | + const start = state.bufferIndex | ||
| 253 | + const end = state.buffer.length | ||
| 254 | + for (let n = start; n < end; n++) { | ||
| 255 | + consumePush(consume, state.buffer[n]) | ||
| 256 | + } | ||
| 257 | + } else { | ||
| 258 | + for (const chunk of state.buffer) { | ||
| 259 | + consumePush(consume, chunk) | ||
| 260 | + } | ||
| 251 | 261 | } | |
| 252 | 262 | ||
| 253 | 263 | if (state.endEmitted) { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments