| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -185,12 +185,12 @@ Help us improve the test coverage by following instructions at [nodejs/undici/#9 | |||
| 185 | 185 | Basic usage example: | |
| 186 | 186 | ||
| 187 | 187 | ```js | |
| 188 | - import { fetch } from 'undici'; | ||
| 188 | + import { fetch } from 'undici' | ||
| 189 | 189 | ||
| 190 | 190 | ||
| 191 | 191 | const res = await fetch('https://example.com') | |
| 192 | 192 | const json = await res.json() | |
| 193 | - console.log(json); | ||
| 193 | + console.log(json) | ||
| 194 | 194 | ``` | |
| 195 | 195 | ||
| 196 | 196 | You can pass an optional dispatcher to `fetch` as: | |
@@ -225,29 +225,29 @@ A body can be of the following types: | |||
| 225 | 225 | In this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard.](https://fetch.spec.whatwg.org) | |
| 226 | 226 | ||
| 227 | 227 | ```js | |
| 228 | - import { fetch } from "undici"; | ||
| 228 | + import { fetch } from 'undici' | ||
| 229 | 229 | ||
| 230 | 230 | const data = { | |
| 231 | 231 | async *[Symbol.asyncIterator]() { | |
| 232 | - yield "hello"; | ||
| 233 | - yield "world"; | ||
| 232 | + yield 'hello' | ||
| 233 | + yield 'world' | ||
| 234 | 234 | }, | |
| 235 | - }; | ||
| 235 | + } | ||
| 236 | 236 | ||
| 237 | - await fetch("https://example.com", { body: data, method: 'POST' }); | ||
| 237 | + await fetch('https://example.com', { body: data, method: 'POST' }) | ||
| 238 | 238 | ``` | |
| 239 | 239 | ||
| 240 | 240 | #### `response.body` | |
| 241 | 241 | ||
| 242 | 242 | Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`. | |
| 243 | 243 | ||
| 244 | 244 | ```js | |
| 245 | - import { fetch } from 'undici'; | ||
| 246 | - import { Readable } from 'node:stream'; | ||
| 245 | + import { fetch } from 'undici' | ||
| 246 | + import { Readable } from 'node:stream' | ||
| 247 | 247 | ||
| 248 | 248 | const response = await fetch('https://example.com') | |
| 249 | - const readableWebStream = response.body; | ||
| 250 | - const readableNodeStream = Readable.fromWeb(readableWebStream); | ||
| 249 | + const readableWebStream = response.body | ||
| 250 | + const readableNodeStream = Readable.fromWeb(readableWebStream) | ||
| 251 | 251 | ``` | |
| 252 | 252 | ||
| 253 | 253 | #### Specification Compliance | |
@@ -329,6 +329,28 @@ Gets the global dispatcher used by Common API Methods. | |||
| 329 | 329 | ||
| 330 | 330 | Returns: `Dispatcher` | |
| 331 | 331 | ||
| 332 | + ### `undici.setGlobalOrigin(origin)` | ||
| 333 | + | ||
| 334 | + * origin `string | URL | undefined` | ||
| 335 | + | ||
| 336 | + Sets the global origin used in `fetch`. | ||
| 337 | + | ||
| 338 | + If `undefined` is passed, the global origin will be reset. This will cause `Response.redirect`, `new Request()`, and `fetch` to throw an error when a relative path is passed. | ||
| 339 | + | ||
| 340 | + ```js | ||
| 341 | + setGlobalOrigin('http://localhost:3000') | ||
| 342 | + | ||
| 343 | + const response = await fetch('/api/ping') | ||
| 344 | + | ||
| 345 | + console.log(response.url) // http://localhost:3000/api/ping | ||
| 346 | + ``` | ||
| 347 | + | ||
| 348 | + ### `undici.getGlobalOrigin()` | ||
| 349 | + | ||
| 350 | + Gets the global origin used in `fetch`. | ||
| 351 | + | ||
| 352 | + Returns: `URL` | ||
| 353 | + | ||
| 332 | 354 | ### `UrlObject` | |
| 333 | 355 | ||
| 334 | 356 | * **port** `string | number` (optional) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,10 +16,11 @@ Returns: `Agent` | |||
| 16 | 16 | ||
| 17 | 17 | ### Parameter: `AgentOptions` | |
| 18 | 18 | ||
| 19 | - Extends: [`ClientOptions`](Pool.md#parameter-pooloptions) | ||
| 19 | + Extends: [`PoolOptions`](Pool.md#parameter-pooloptions) | ||
| 20 | 20 | ||
| 21 | 21 | * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)` | |
| 22 | 22 | * **maxRedirections** `Integer` - Default: `0`. The number of HTTP redirection to follow unless otherwise specified in `DispatchOptions`. | |
| 23 | + * **interceptors** `{ Agent: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time. | ||
| 23 | 24 | ||
| 24 | 25 | ## Instance Properties | |
| 25 | 26 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -26,6 +26,7 @@ Returns: `Client` | |||
| 26 | 26 | * **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections. | |
| 27 | 27 | * **connect** `ConnectOptions | Function | null` (optional) - Default: `null`. | |
| 28 | 28 | * **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. | |
| 29 | + * **interceptors** `{ Client: DispatchInterceptor[] }` - Default: `[RedirectInterceptor]` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). Note that the behavior of interceptors is Experimental and might change at any given time. | ||
| 29 | 30 | ||
| 30 | 31 | #### Parameter: `ConnectOptions` | |
| 31 | 32 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,60 @@ | |||
| 1 | + #Interface: DispatchInterceptor | ||
| 2 | + | ||
| 3 | + Extends: `Function` | ||
| 4 | + | ||
| 5 | + A function that can be applied to the `Dispatcher.Dispatch` function before it is invoked with a dispatch request. | ||
| 6 | + | ||
| 7 | + This allows one to write logic to intercept both the outgoing request, and the incoming response. | ||
| 8 | + | ||
| 9 | + ### Parameter: `Dispatcher.Dispatch` | ||
| 10 | + | ||
| 11 | + The base dispatch function you are decorating. | ||
| 12 | + | ||
| 13 | + ### ReturnType: `Dispatcher.Dispatch` | ||
| 14 | + | ||
| 15 | + A dispatch function that has been altered to provide additional logic | ||
| 16 | + | ||
| 17 | + ### Basic Example | ||
| 18 | + | ||
| 19 | + Here is an example of an interceptor being used to provide a JWT bearer token | ||
| 20 | + | ||
| 21 | + ```js | ||
| 22 | + 'use strict' | ||
| 23 | + | ||
| 24 | + const insertHeaderInterceptor = dispatch => { | ||
| 25 | + return function InterceptedDispatch(opts, handler){ | ||
| 26 | + opts.headers.push('Authorization', 'Bearer [Some token]') | ||
| 27 | + return dispatch(opts, handler) | ||
| 28 | + } | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + const client = new Client('https://localhost:3000', { | ||
| 32 | + interceptors: { Client: [insertHeaderInterceptor] } | ||
| 33 | + }) | ||
| 34 | + | ||
| 35 | + ``` | ||
| 36 | + | ||
| 37 | + ### Basic Example 2 | ||
| 38 | + | ||
| 39 | + Here is a contrived example of an interceptor stripping the headers from a response. | ||
| 40 | + | ||
| 41 | + ```js | ||
| 42 | + 'use strict' | ||
| 43 | + | ||
| 44 | + const clearHeadersInterceptor = dispatch => { | ||
| 45 | + const { DecoratorHandler } = require('undici') | ||
| 46 | + class ResultInterceptor extends DecoratorHandler { | ||
| 47 | + onHeaders (statusCode, headers, resume) { | ||
| 48 | + return super.onHeaders(statusCode, [], resume) | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + return function InterceptedDispatch(opts, handler){ | ||
| 52 | + return dispatch(opts, new ResultInterceptor(handler)) | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + const client = new Client('https://localhost:3000', { | ||
| 57 | + interceptors: { Client: [clearHeadersInterceptor] } | ||
| 58 | + }) | ||
| 59 | + | ||
| 60 | + ``` | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,7 +54,7 @@ Returns: `MockInterceptor` corresponding to the input options. | |||
| 54 | 54 | ### Parameter: `MockPoolInterceptOptions` | |
| 55 | 55 | ||
| 56 | 56 | * **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path. | |
| 57 | - * **method** `string | RegExp | (method: string) => boolean` - a matcher for the HTTP request method. | ||
| 57 | + * **method** `string | RegExp | (method: string) => boolean` - (optional) - a matcher for the HTTP request method. Defaults to `GET`. | ||
| 58 | 58 | * **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body. | |
| 59 | 59 | * **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way. | |
| 60 | 60 | * **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ Extends: [`ClientOptions`](Client.md#parameter-clientoptions) | |||
| 19 | 19 | ||
| 20 | 20 | * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)` | |
| 21 | 21 | * **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances. | |
| 22 | + * **interceptors** `{ Pool: DispatchInterceptor[] } }` - Default: `{ Pool: [] }` - A list of interceptors that are applied to the dispatch method. Additional logic can be applied (such as, but not limited to: 302 status code handling, authentication, cookies, compression and caching). | ||
| 22 | 23 | ||
| 23 | 24 | ## Instance Properties | |
| 24 | 25 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,11 +1,16 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | 3 | const { getGlobalDispatcher } = require('./lib/global') | |
| 4 | - const fetchImpl = require('./lib/fetch') | ||
| 4 | + const fetchImpl = require('./lib/fetch').fetch | ||
| 5 | 5 | ||
| 6 | 6 | module.exports.fetch = async function fetch (resource) { | |
| 7 | 7 | const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | |
| 8 | - return fetchImpl.apply(dispatcher, arguments) | ||
| 8 | + try { | ||
| 9 | + return await fetchImpl.apply(dispatcher, arguments) | ||
| 10 | + } catch (err) { | ||
| 11 | + Error.captureStackTrace(err, this) | ||
| 12 | + throw err | ||
| 13 | + } | ||
| 9 | 14 | } | |
| 10 | 15 | module.exports.FormData = require('./lib/fetch/formdata').FormData | |
| 11 | 16 | module.exports.Headers = require('./lib/fetch/headers').Headers | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,9 @@ | |||
| 1 | 1 | import Dispatcher = require('./types/dispatcher') | |
| 2 | 2 | import { setGlobalDispatcher, getGlobalDispatcher } from './types/global-dispatcher' | |
| 3 | + import { setGlobalOrigin, getGlobalOrigin } from './types/global-origin' | ||
| 3 | 4 | import Pool = require('./types/pool') | |
| 5 | + import { RedirectHandler, DecoratorHandler } from './types/handlers' | ||
| 6 | + | ||
| 4 | 7 | import BalancedPool = require('./types/balanced-pool') | |
| 5 | 8 | import Client = require('./types/client') | |
| 6 | 9 | import buildConnector = require('./types/connector') | |
@@ -19,14 +22,15 @@ export * from './types/formdata' | |||
| 19 | 22 | export * from './types/diagnostics-channel' | |
| 20 | 23 | export { Interceptable } from './types/mock-interceptor' | |
| 21 | 24 | ||
| 22 | - export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent } | ||
| 25 | + export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent, RedirectHandler, DecoratorHandler } | ||
| 23 | 26 | export default Undici | |
| 24 | 27 | ||
| 25 | - declare function Undici(url: string, opts: Pool.Options): Pool | ||
| 26 | - | ||
| 27 | 28 | declare namespace Undici { | |
| 28 | 29 | var Dispatcher: typeof import('./types/dispatcher') | |
| 29 | 30 | var Pool: typeof import('./types/pool'); | |
| 31 | + var RedirectHandler: typeof import ('./types/handlers').RedirectHandler | ||
| 32 | + var DecoratorHandler: typeof import ('./types/handlers').DecoratorHandler | ||
| 33 | + var createRedirectInterceptor: typeof import ('./types/interceptors').createRedirectInterceptor | ||
| 30 | 34 | var BalancedPool: typeof import('./types/balanced-pool'); | |
| 31 | 35 | var Client: typeof import('./types/client'); | |
| 32 | 36 | var buildConnector: typeof import('./types/connector'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,9 @@ const MockPool = require('./lib/mock/mock-pool') | |||
| 16 | 16 | const mockErrors = require('./lib/mock/mock-errors') | |
| 17 | 17 | const ProxyAgent = require('./lib/proxy-agent') | |
| 18 | 18 | const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global') | |
| 19 | + const DecoratorHandler = require('./lib/handler/DecoratorHandler') | ||
| 20 | + const RedirectHandler = require('./lib/handler/RedirectHandler') | ||
| 21 | + const createRedirectInterceptor = require('./lib/interceptor/redirectInterceptor') | ||
| 19 | 22 | ||
| 20 | 23 | const nodeVersion = process.versions.node.split('.') | |
| 21 | 24 | const nodeMajor = Number(nodeVersion[0]) | |
@@ -30,6 +33,10 @@ module.exports.BalancedPool = BalancedPool | |||
| 30 | 33 | module.exports.Agent = Agent | |
| 31 | 34 | module.exports.ProxyAgent = ProxyAgent | |
| 32 | 35 | ||
| 36 | + module.exports.DecoratorHandler = DecoratorHandler | ||
| 37 | + module.exports.RedirectHandler = RedirectHandler | ||
| 38 | + module.exports.createRedirectInterceptor = createRedirectInterceptor | ||
| 39 | + | ||
| 33 | 40 | module.exports.buildConnector = buildConnector | |
| 34 | 41 | module.exports.errors = errors | |
| 35 | 42 | ||
@@ -89,16 +96,26 @@ if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 8)) { | |||
| 89 | 96 | let fetchImpl = null | |
| 90 | 97 | module.exports.fetch = async function fetch (resource) { | |
| 91 | 98 | if (!fetchImpl) { | |
| 92 | - fetchImpl = require('./lib/fetch') | ||
| 99 | + fetchImpl = require('./lib/fetch').fetch | ||
| 93 | 100 | } | |
| 94 | 101 | const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | |
| 95 | - return fetchImpl.apply(dispatcher, arguments) | ||
| 102 | + try { | ||
| 103 | + return await fetchImpl.apply(dispatcher, arguments) | ||
| 104 | + } catch (err) { | ||
| 105 | + Error.captureStackTrace(err, this) | ||
| 106 | + throw err | ||
| 107 | + } | ||
| 96 | 108 | } | |
| 97 | 109 | module.exports.Headers = require('./lib/fetch/headers').Headers | |
| 98 | 110 | module.exports.Response = require('./lib/fetch/response').Response | |
| 99 | 111 | module.exports.Request = require('./lib/fetch/request').Request | |
| 100 | 112 | module.exports.FormData = require('./lib/fetch/formdata').FormData | |
| 101 | 113 | module.exports.File = require('./lib/fetch/file').File | |
| 114 | + | ||
| 115 | + const { setGlobalOrigin, getGlobalOrigin } = require('./lib/fetch/global') | ||
| 116 | + | ||
| 117 | + module.exports.setGlobalOrigin = setGlobalOrigin | ||
| 118 | + module.exports.getGlobalOrigin = getGlobalOrigin | ||
| 102 | 119 | } | |
| 103 | 120 | ||
| 104 | 121 | module.exports.request = makeDispatcher(api.request) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,12 +1,12 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | 3 | const { InvalidArgumentError } = require('./core/errors') | |
| 4 | - const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('./core/symbols') | ||
| 4 | + const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('./core/symbols') | ||
| 5 | 5 | const DispatcherBase = require('./dispatcher-base') | |
| 6 | 6 | const Pool = require('./pool') | |
| 7 | 7 | const Client = require('./client') | |
| 8 | 8 | const util = require('./core/util') | |
| 9 | - const RedirectHandler = require('./handler/redirect') | ||
| 9 | + const createRedirectInterceptor = require('./interceptor/redirectInterceptor') | ||
| 10 | 10 | const { WeakRef, FinalizationRegistry } = require('./compat/dispatcher-weakref')() | |
| 11 | 11 | ||
| 12 | 12 | const kOnConnect = Symbol('onConnect') | |
@@ -44,7 +44,14 @@ class Agent extends DispatcherBase { | |||
| 44 | 44 | connect = { ...connect } | |
| 45 | 45 | } | |
| 46 | 46 | ||
| 47 | + this[kInterceptors] = options.interceptors && options.interceptors.Agent && Array.isArray(options.interceptors.Agent) | ||
| 48 | + ? options.interceptors.Agent | ||
| 49 | + : [createRedirectInterceptor({ maxRedirections })] | ||
| 50 | + | ||
| 47 | 51 | this[kOptions] = { ...util.deepClone(options), connect } | |
| 52 | + this[kOptions].interceptors = options.interceptors | ||
| 53 | + ? { ...options.interceptors } | ||
| 54 | + : undefined | ||
| 48 | 55 | this[kMaxRedirections] = maxRedirections | |
| 49 | 56 | this[kFactory] = factory | |
| 50 | 57 | this[kClients] = new Map() | |
@@ -108,12 +115,6 @@ class Agent extends DispatcherBase { | |||
| 108 | 115 | this[kFinalizer].register(dispatcher, key) | |
| 109 | 116 | } | |
| 110 | 117 | ||
| 111 | - const { maxRedirections = this[kMaxRedirections] } = opts | ||
| 112 | - if (maxRedirections != null && maxRedirections !== 0) { | ||
| 113 | - opts = { ...opts, maxRedirections: 0 } // Stop sub dispatcher from also redirecting. | ||
| 114 | - handler = new RedirectHandler(this, maxRedirections, opts, handler) | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | 118 | return dispatcher.dispatch(opts, handler) | |
| 118 | 119 | } | |
| 119 | 120 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments