| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,162 @@ | |||
| 1 | + # Class: EnvHttpProxyAgent | ||
| 2 | + | ||
| 3 | + Stability: Experimental. | ||
| 4 | + | ||
| 5 | + Extends: `undici.Dispatcher` | ||
| 6 | + | ||
| 7 | + EnvHttpProxyAgent automatically reads the proxy configuration from the environment variables `http_proxy`, `https_proxy`, and `no_proxy` and sets up the proxy agents accordingly. When `http_proxy` and `https_proxy` are set, `http_proxy` is used for HTTP requests and `https_proxy` is used for HTTPS requests. If only `http_proxy` is set, `http_proxy` is used for both HTTP and HTTPS requests. If only `https_proxy` is set, it is only used for HTTPS requests. | ||
| 8 | + | ||
| 9 | + `no_proxy` is a comma or space-separated list of hostnames that should not be proxied. The list may contain leading wildcard characters (`*`). If `no_proxy` is set, the EnvHttpProxyAgent will bypass the proxy for requests to hosts that match the list. If `no_proxy` is set to `"*"`, the EnvHttpProxyAgent will bypass the proxy for all requests. | ||
| 10 | + | ||
| 11 | + Uppercase environment variables are also supported: `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. However, if both the lowercase and uppercase environment variables are set, the uppercase environment variables will be ignored. | ||
| 12 | + | ||
| 13 | + ## `new EnvHttpProxyAgent([options])` | ||
| 14 | + | ||
| 15 | + Arguments: | ||
| 16 | + | ||
| 17 | + * **options** `EnvHttpProxyAgentOptions` (optional) - extends the `Agent` options. | ||
| 18 | + | ||
| 19 | + Returns: `EnvHttpProxyAgent` | ||
| 20 | + | ||
| 21 | + ### Parameter: `EnvHttpProxyAgentOptions` | ||
| 22 | + | ||
| 23 | + Extends: [`AgentOptions`](Agent.md#parameter-agentoptions) | ||
| 24 | + | ||
| 25 | + * **httpProxy** `string` (optional) - When set, it will override the `HTTP_PROXY` environment variable. | ||
| 26 | + * **httpsProxy** `string` (optional) - When set, it will override the `HTTPS_PROXY` environment variable. | ||
| 27 | + * **noProxy** `string` (optional) - When set, it will override the `NO_PROXY` environment variable. | ||
| 28 | + | ||
| 29 | + Examples: | ||
| 30 | + | ||
| 31 | + ```js | ||
| 32 | + import { EnvHttpProxyAgent } from 'undici' | ||
| 33 | + | ||
| 34 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 35 | + // or | ||
| 36 | + const envHttpProxyAgent = new EnvHttpProxyAgent({ httpProxy: 'my.proxy.server:8080', httpsProxy: 'my.proxy.server:8443', noProxy: 'localhost' }) | ||
| 37 | + ``` | ||
| 38 | + | ||
| 39 | + #### Example - EnvHttpProxyAgent instantiation | ||
| 40 | + | ||
| 41 | + This will instantiate the EnvHttpProxyAgent. It will not do anything until registered as the agent to use with requests. | ||
| 42 | + | ||
| 43 | + ```js | ||
| 44 | + import { EnvHttpProxyAgent } from 'undici' | ||
| 45 | + | ||
| 46 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 47 | + ``` | ||
| 48 | + | ||
| 49 | + #### Example - Basic Proxy Fetch with global agent dispatcher | ||
| 50 | + | ||
| 51 | + ```js | ||
| 52 | + import { setGlobalDispatcher, fetch, EnvHttpProxyAgent } from 'undici' | ||
| 53 | + | ||
| 54 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 55 | + setGlobalDispatcher(envHttpProxyAgent) | ||
| 56 | + | ||
| 57 | + const { status, json } = await fetch('http://localhost:3000/foo') | ||
| 58 | + | ||
| 59 | + console.log('response received', status) // response received 200 | ||
| 60 | + | ||
| 61 | + const data = await json() // data { foo: "bar" } | ||
| 62 | + ``` | ||
| 63 | + | ||
| 64 | + #### Example - Basic Proxy Request with global agent dispatcher | ||
| 65 | + | ||
| 66 | + ```js | ||
| 67 | + import { setGlobalDispatcher, request, EnvHttpProxyAgent } from 'undici' | ||
| 68 | + | ||
| 69 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 70 | + setGlobalDispatcher(envHttpProxyAgent) | ||
| 71 | + | ||
| 72 | + const { statusCode, body } = await request('http://localhost:3000/foo') | ||
| 73 | + | ||
| 74 | + console.log('response received', statusCode) // response received 200 | ||
| 75 | + | ||
| 76 | + for await (const data of body) { | ||
| 77 | + console.log('data', data.toString('utf8')) // data foo | ||
| 78 | + } | ||
| 79 | + ``` | ||
| 80 | + | ||
| 81 | + #### Example - Basic Proxy Request with local agent dispatcher | ||
| 82 | + | ||
| 83 | + ```js | ||
| 84 | + import { EnvHttpProxyAgent, request } from 'undici' | ||
| 85 | + | ||
| 86 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 87 | + | ||
| 88 | + const { | ||
| 89 | + statusCode, | ||
| 90 | + body | ||
| 91 | + } = await request('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent }) | ||
| 92 | + | ||
| 93 | + console.log('response received', statusCode) // response received 200 | ||
| 94 | + | ||
| 95 | + for await (const data of body) { | ||
| 96 | + console.log('data', data.toString('utf8')) // data foo | ||
| 97 | + } | ||
| 98 | + ``` | ||
| 99 | + | ||
| 100 | + #### Example - Basic Proxy Fetch with local agent dispatcher | ||
| 101 | + | ||
| 102 | + ```js | ||
| 103 | + import { EnvHttpProxyAgent, fetch } from 'undici' | ||
| 104 | + | ||
| 105 | + const envHttpProxyAgent = new EnvHttpProxyAgent() | ||
| 106 | + | ||
| 107 | + const { | ||
| 108 | + status, | ||
| 109 | + json | ||
| 110 | + } = await fetch('http://localhost:3000/foo', { dispatcher: envHttpProxyAgent }) | ||
| 111 | + | ||
| 112 | + console.log('response received', status) // response received 200 | ||
| 113 | + | ||
| 114 | + const data = await json() // data { foo: "bar" } | ||
| 115 | + ``` | ||
| 116 | + | ||
| 117 | + ## Instance Methods | ||
| 118 | + | ||
| 119 | + ### `EnvHttpProxyAgent.close([callback])` | ||
| 120 | + | ||
| 121 | + Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise). | ||
| 122 | + | ||
| 123 | + ### `EnvHttpProxyAgent.destroy([error, callback])` | ||
| 124 | + | ||
| 125 | + Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise). | ||
| 126 | + | ||
| 127 | + ### `EnvHttpProxyAgent.dispatch(options, handler: AgentDispatchOptions)` | ||
| 128 | + | ||
| 129 | + Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler). | ||
| 130 | + | ||
| 131 | + #### Parameter: `AgentDispatchOptions` | ||
| 132 | + | ||
| 133 | + Extends: [`DispatchOptions`](Dispatcher.md#parameter-dispatchoptions) | ||
| 134 | + | ||
| 135 | + * **origin** `string | URL` | ||
| 136 | + * **maxRedirections** `Integer`. | ||
| 137 | + | ||
| 138 | + Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise). | ||
| 139 | + | ||
| 140 | + ### `EnvHttpProxyAgent.connect(options[, callback])` | ||
| 141 | + | ||
| 142 | + See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback). | ||
| 143 | + | ||
| 144 | + ### `EnvHttpProxyAgent.dispatch(options, handler)` | ||
| 145 | + | ||
| 146 | + Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler). | ||
| 147 | + | ||
| 148 | + ### `EnvHttpProxyAgent.pipeline(options, handler)` | ||
| 149 | + | ||
| 150 | + See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler). | ||
| 151 | + | ||
| 152 | + ### `EnvHttpProxyAgent.request(options[, callback])` | ||
| 153 | + | ||
| 154 | + See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback). | ||
| 155 | + | ||
| 156 | + ### `EnvHttpProxyAgent.stream(options, factory[, callback])` | ||
| 157 | + | ||
| 158 | + See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback). | ||
| 159 | + | ||
| 160 | + ### `EnvHttpProxyAgent.upgrade(options[, callback])` | ||
| 161 | + | ||
| 162 | + See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,11 +8,11 @@ Receives a header object and returns the parsed value. | |||
| 8 | 8 | ||
| 9 | 9 | Arguments: | |
| 10 | 10 | ||
| 11 | - - **headers** `Record<string, string | string[]> | (Buffer | string | (Buffer | string)[])[]` (required) - Header object. | ||
| 11 | + - **headers** `(Buffer | string | (Buffer | string)[])[]` (required) - Header object. | ||
| 12 | 12 | ||
| 13 | 13 | - **obj** `Record<string, string | string[]>` (optional) - Object to specify a proxy object. The parsed value is assigned to this object. But, if **headers** is an object, it is not used. | |
| 14 | 14 | ||
| 15 | - Returns: `Record<string, string | string[]>` If **headers** is an object, it is **headers**. Otherwise, if **obj** is specified, it is equivalent to **obj**. | ||
| 15 | + Returns: `Record<string, string | string[]>` If **obj** is specified, it is equivalent to **obj**. | ||
| 16 | 16 | ||
| 17 | 17 | ## `headerNameToString(value)` | |
| 18 | 18 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | Connecting through a proxy is possible by: | |
| 4 | 4 | ||
| 5 | - - Using [AgentProxy](../api/ProxyAgent.md). | ||
| 5 | + - Using [ProxyAgent](../api/ProxyAgent.md). | ||
| 6 | 6 | - Configuring `Client` or `Pool` constructor. | |
| 7 | 7 | ||
| 8 | 8 | The proxy url should be passed to the `Client` or `Pool` constructor, while the upstream server url | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,11 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | 3 | const assert = require('node:assert') | |
| 4 | 4 | const { Readable } = require('./readable') | |
| 5 | - const { InvalidArgumentError } = require('../core/errors') | ||
| 5 | + const { InvalidArgumentError, RequestAbortedError } = require('../core/errors') | ||
| 6 | 6 | const util = require('../core/util') | |
| 7 | 7 | const { getResolveErrorBodyCallback } = require('./util') | |
| 8 | 8 | const { AsyncResource } = require('node:async_hooks') | |
| 9 | - const { addSignal, removeSignal } = require('./abort-signal') | ||
| 10 | 9 | ||
| 11 | 10 | class RequestHandler extends AsyncResource { | |
| 12 | 11 | constructor (opts, callback) { | |
@@ -45,6 +44,7 @@ class RequestHandler extends AsyncResource { | |||
| 45 | 44 | throw err | |
| 46 | 45 | } | |
| 47 | 46 | ||
| 47 | + this.method = method | ||
| 48 | 48 | this.responseHeaders = responseHeaders || null | |
| 49 | 49 | this.opaque = opaque || null | |
| 50 | 50 | this.callback = callback | |
@@ -56,14 +56,36 @@ class RequestHandler extends AsyncResource { | |||
| 56 | 56 | this.onInfo = onInfo || null | |
| 57 | 57 | this.throwOnError = throwOnError | |
| 58 | 58 | this.highWaterMark = highWaterMark | |
| 59 | + this.signal = signal | ||
| 60 | + this.reason = null | ||
| 61 | + this.removeAbortListener = null | ||
| 59 | 62 | ||
| 60 | 63 | if (util.isStream(body)) { | |
| 61 | 64 | body.on('error', (err) => { | |
| 62 | 65 | this.onError(err) | |
| 63 | 66 | }) | |
| 64 | 67 | } | |
| 65 | 68 | ||
| 66 | - addSignal(this, signal) | ||
| 69 | + if (this.signal) { | ||
| 70 | + if (this.signal.aborted) { | ||
| 71 | + this.reason = this.signal.reason ?? new RequestAbortedError() | ||
| 72 | + } else { | ||
| 73 | + this.removeAbortListener = util.addAbortListener(this.signal, () => { | ||
| 74 | + this.reason = this.signal.reason ?? new RequestAbortedError() | ||
| 75 | + if (this.res) { | ||
| 76 | + util.destroy(this.res, this.reason) | ||
| 77 | + } else if (this.abort) { | ||
| 78 | + this.abort(this.reason) | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + if (this.removeAbortListener) { | ||
| 82 | + this.res?.off('close', this.removeAbortListener) | ||
| 83 | + this.removeAbortListener() | ||
| 84 | + this.removeAbortListener = null | ||
| 85 | + } | ||
| 86 | + }) | ||
| 87 | + } | ||
| 88 | + } | ||
| 67 | 89 | } | |
| 68 | 90 | ||
| 69 | 91 | onConnect (abort, context) { | |
@@ -93,48 +115,52 @@ class RequestHandler extends AsyncResource { | |||
| 93 | 115 | const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers | |
| 94 | 116 | const contentType = parsedHeaders['content-type'] | |
| 95 | 117 | const contentLength = parsedHeaders['content-length'] | |
| 96 | - const body = new Readable({ resume, abort, contentType, contentLength, highWaterMark }) | ||
| 118 | + const res = new Readable({ | ||
| 119 | + resume, | ||
| 120 | + abort, | ||
| 121 | + contentType, | ||
| 122 | + contentLength: this.method !== 'HEAD' && contentLength | ||
| 123 | + ? Number(contentLength) | ||
| 124 | + : null, | ||
| 125 | + highWaterMark | ||
| 126 | + }) | ||
| 127 | + | ||
| 128 | + if (this.removeAbortListener) { | ||
| 129 | + res.on('close', this.removeAbortListener) | ||
| 130 | + } | ||
| 97 | 131 | ||
| 98 | 132 | this.callback = null | |
| 99 | - this.res = body | ||
| 133 | + this.res = res | ||
| 100 | 134 | if (callback !== null) { | |
| 101 | 135 | if (this.throwOnError && statusCode >= 400) { | |
| 102 | 136 | this.runInAsyncScope(getResolveErrorBodyCallback, null, | |
| 103 | - { callback, body, contentType, statusCode, statusMessage, headers } | ||
| 137 | + { callback, body: res, contentType, statusCode, statusMessage, headers } | ||
| 104 | 138 | ) | |
| 105 | 139 | } else { | |
| 106 | 140 | this.runInAsyncScope(callback, null, null, { | |
| 107 | 141 | statusCode, | |
| 108 | 142 | headers, | |
| 109 | 143 | trailers: this.trailers, | |
| 110 | 144 | opaque, | |
| 111 | - body, | ||
| 145 | + body: res, | ||
| 112 | 146 | context | |
| 113 | 147 | }) | |
| 114 | 148 | } | |
| 115 | 149 | } | |
| 116 | 150 | } | |
| 117 | 151 | ||
| 118 | 152 | onData (chunk) { | |
| 119 | - const { res } = this | ||
| 120 | - return res.push(chunk) | ||
| 153 | + return this.res.push(chunk) | ||
| 121 | 154 | } | |
| 122 | 155 | ||
| 123 | 156 | onComplete (trailers) { | |
| 124 | - const { res } = this | ||
| 125 | - | ||
| 126 | - removeSignal(this) | ||
| 127 | - | ||
| 128 | 157 | util.parseHeaders(trailers, this.trailers) | |
| 129 | - | ||
| 130 | - res.push(null) | ||
| 158 | + this.res.push(null) | ||
| 131 | 159 | } | |
| 132 | 160 | ||
| 133 | 161 | onError (err) { | |
| 134 | 162 | const { res, callback, body, opaque } = this | |
| 135 | 163 | ||
| 136 | - removeSignal(this) | ||
| 137 | - | ||
| 138 | 164 | if (callback) { | |
| 139 | 165 | // TODO: Does this need queueMicrotask? | |
| 140 | 166 | this.callback = null | |
@@ -155,6 +181,12 @@ class RequestHandler extends AsyncResource { | |||
| 155 | 181 | this.body = null | |
| 156 | 182 | util.destroy(body, err) | |
| 157 | 183 | } | |
| 184 | + | ||
| 185 | + if (this.removeAbortListener) { | ||
| 186 | + res?.off('close', this.removeAbortListener) | ||
| 187 | + this.removeAbortListener() | ||
| 188 | + this.removeAbortListener = null | ||
| 189 | + } | ||
| 158 | 190 | } | |
| 159 | 191 | } | |
| 160 | 192 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,9 +63,13 @@ class BodyReadable extends Readable { | |||
| 63 | 63 | // tick as it is created, then a user who is waiting for a | |
| 64 | 64 | // promise (i.e micro tick) for installing a 'error' listener will | |
| 65 | 65 | // never get a chance and will always encounter an unhandled exception. | |
| 66 | - setImmediate(() => { | ||
| 66 | + if (!this[kReading]) { | ||
| 67 | + setImmediate(() => { | ||
| 68 | + callback(err) | ||
| 69 | + }) | ||
| 70 | + } else { | ||
| 67 | 71 | callback(err) | |
| 68 | - }) | ||
| 72 | + } | ||
| 69 | 73 | } | |
| 70 | 74 | ||
| 71 | 75 | on (ev, ...args) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -165,7 +165,7 @@ function setupTimeout (onConnectTimeout, timeout) { | |||
| 165 | 165 | let s1 = null | |
| 166 | 166 | let s2 = null | |
| 167 | 167 | const timeoutId = setTimeout(() => { | |
| 168 | - // setImmediate is added to make sure that we priotorise socket error events over timeouts | ||
| 168 | + // setImmediate is added to make sure that we prioritize socket error events over timeouts | ||
| 169 | 169 | s1 = setImmediate(() => { | |
| 170 | 170 | if (process.platform === 'win32') { | |
| 171 | 171 | // Windows needs an extra setImmediate probably due to implementation differences in the socket logic | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -524,6 +524,7 @@ function writeH2 (client, request) { | |||
| 524 | 524 | } | |
| 525 | 525 | } else if (util.isStream(body)) { | |
| 526 | 526 | writeStream({ | |
| 527 | + abort, | ||
| 527 | 528 | body, | |
| 528 | 529 | client, | |
| 529 | 530 | request, | |
@@ -535,6 +536,7 @@ function writeH2 (client, request) { | |||
| 535 | 536 | }) | |
| 536 | 537 | } else if (util.isIterable(body)) { | |
| 537 | 538 | writeIterable({ | |
| 539 | + abort, | ||
| 538 | 540 | body, | |
| 539 | 541 | client, | |
| 540 | 542 | request, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments