| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0040030 commit 0d24163
40 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,7 @@ Returns: `Client` | |||
| 23 | 23 | * **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds. | |
| 24 | 24 | * **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `1e3` - A number subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 1 second. | |
| 25 | 25 | * **maxHeaderSize** `number | null` (optional) - Default: `16384` - The maximum length of request headers in bytes. Defaults to 16KiB. | |
| 26 | + * **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable. | ||
| 26 | 27 | * **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 | 28 | * **connect** `ConnectOptions | Function | null` (optional) - Default: `null`. | |
| 28 | 29 | * **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. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ import { errors } from 'undici' | |||
| 19 | 19 | | `RequestContentLengthMismatchError` | `UND_ERR_REQ_CONTENT_LENGTH_MISMATCH` | request body does not match content-length header | | |
| 20 | 20 | | `ResponseContentLengthMismatchError` | `UND_ERR_RES_CONTENT_LENGTH_MISMATCH` | response body does not match content-length header | | |
| 21 | 21 | | `InformationalError` | `UND_ERR_INFO` | expected error with reason | | |
| 22 | + | `ResponseExceededMaxSizeError` | `UND_ERR_RES_EXCEEDED_MAX_SIZE` | response body exceed the max size allowed | | ||
| 22 | 23 | ||
| 23 | 24 | ### `SocketError` | |
| 24 | 25 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,6 +17,8 @@ Returns: `ProxyAgent` | |||
| 17 | 17 | Extends: [`AgentOptions`](Agent.md#parameter-agentoptions) | |
| 18 | 18 | ||
| 19 | 19 | * **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string. | |
| 20 | + * **token** `string` (optional) - It can be passed by a string of token for authentication. | ||
| 21 | + * **auth** `string` (**deprecated**) - Use token. | ||
| 20 | 22 | ||
| 21 | 23 | Examples: | |
| 22 | 24 | ||
@@ -74,6 +76,26 @@ for await (const data of body) { | |||
| 74 | 76 | } | |
| 75 | 77 | ``` | |
| 76 | 78 | ||
| 79 | + #### Example - Basic Proxy Request with authentication | ||
| 80 | + | ||
| 81 | + ```js | ||
| 82 | + import { setGlobalDispatcher, request, ProxyAgent } from 'undici'; | ||
| 83 | + | ||
| 84 | + const proxyAgent = new ProxyAgent({ | ||
| 85 | + uri: 'my.proxy.server', | ||
| 86 | + token: 'Bearer xxxx' | ||
| 87 | + }); | ||
| 88 | + setGlobalDispatcher(proxyAgent); | ||
| 89 | + | ||
| 90 | + const { statusCode, body } = await request('http://localhost:3000/foo'); | ||
| 91 | + | ||
| 92 | + console.log('response received', statusCode); // response received 200 | ||
| 93 | + | ||
| 94 | + for await (const data of body) { | ||
| 95 | + console.log('data', data.toString('utf8')); // data foo | ||
| 96 | + } | ||
| 97 | + ``` | ||
| 98 | + | ||
| 77 | 99 | ### `ProxyAgent.close()` | |
| 78 | 100 | ||
| 79 | 101 | Closes the proxy agent and waits for registered pools and clients to also close before resolving. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,12 +1,10 @@ | |||
| 1 | 1 | 'use strict' | |
| 2 | 2 | ||
| 3 | - const { getGlobalDispatcher } = require('./lib/global') | ||
| 4 | 3 | const fetchImpl = require('./lib/fetch').fetch | |
| 5 | 4 | ||
| 6 | 5 | module.exports.fetch = async function fetch (resource) { | |
| 7 | - const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | ||
| 8 | 6 | try { | |
| 9 | - return await fetchImpl.apply(dispatcher, arguments) | ||
| 7 | + return await fetchImpl(...arguments) | ||
| 10 | 8 | } catch (err) { | |
| 11 | 9 | Error.captureStackTrace(err, this) | |
| 12 | 10 | throw err | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,7 @@ import { request, pipeline, stream, connect, upgrade } from './types/api' | |||
| 18 | 18 | ||
| 19 | 19 | export * from './types/fetch' | |
| 20 | 20 | export * from './types/file' | |
| 21 | + export * from './types/filereader' | ||
| 21 | 22 | export * from './types/formdata' | |
| 22 | 23 | export * from './types/diagnostics-channel' | |
| 23 | 24 | export { Interceptable } from './types/mock-interceptor' | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,9 +98,9 @@ if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 8)) { | |||
| 98 | 98 | if (!fetchImpl) { | |
| 99 | 99 | fetchImpl = require('./lib/fetch').fetch | |
| 100 | 100 | } | |
| 101 | - const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | ||
| 101 | + | ||
| 102 | 102 | try { | |
| 103 | - return await fetchImpl.apply(dispatcher, arguments) | ||
| 103 | + return await fetchImpl(...arguments) | ||
| 104 | 104 | } catch (err) { | |
| 105 | 105 | Error.captureStackTrace(err, this) | |
| 106 | 106 | throw err | |
@@ -111,6 +111,7 @@ if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 8)) { | |||
| 111 | 111 | module.exports.Request = require('./lib/fetch/request').Request | |
| 112 | 112 | module.exports.FormData = require('./lib/fetch/formdata').FormData | |
| 113 | 113 | module.exports.File = require('./lib/fetch/file').File | |
| 114 | + module.exports.FileReader = require('./lib/fileapi/filereader').FileReader | ||
| 114 | 115 | ||
| 115 | 116 | const { setGlobalOrigin, getGlobalOrigin } = require('./lib/fetch/global') | |
| 116 | 117 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -106,7 +106,7 @@ class StreamHandler extends AsyncResource { | |||
| 106 | 106 | } | |
| 107 | 107 | ||
| 108 | 108 | res.on('drain', resume) | |
| 109 | - // TODO: Avoid finished. It registers an unecessary amount of listeners. | ||
| 109 | + // TODO: Avoid finished. It registers an unnecessary amount of listeners. | ||
| 110 | 110 | finished(res, { readable: false }, (err) => { | |
| 111 | 111 | const { callback, res, opaque, trailers, abort } = this | |
| 112 | 112 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,7 +17,8 @@ const { | |||
| 17 | 17 | SocketError, | |
| 18 | 18 | InformationalError, | |
| 19 | 19 | BodyTimeoutError, | |
| 20 | - HTTPParserError | ||
| 20 | + HTTPParserError, | ||
| 21 | + ResponseExceededMaxSizeError | ||
| 21 | 22 | } = require('./core/errors') | |
| 22 | 23 | const buildConnector = require('./core/connect') | |
| 23 | 24 | const { | |
@@ -60,7 +61,9 @@ const { | |||
| 60 | 61 | kClose, | |
| 61 | 62 | kDestroy, | |
| 62 | 63 | kDispatch, | |
| 63 | - kInterceptors | ||
| 64 | + kInterceptors, | ||
| 65 | + kLocalAddress, | ||
| 66 | + kMaxResponseSize | ||
| 64 | 67 | } = require('./core/symbols') | |
| 65 | 68 | ||
| 66 | 69 | const kClosedResolve = Symbol('kClosedResolve') | |
@@ -102,7 +105,9 @@ class Client extends DispatcherBase { | |||
| 102 | 105 | maxCachedSessions, | |
| 103 | 106 | maxRedirections, | |
| 104 | 107 | connect, | |
| 105 | - maxRequestsPerClient | ||
| 108 | + maxRequestsPerClient, | ||
| 109 | + localAddress, | ||
| 110 | + maxResponseSize | ||
| 106 | 111 | } = {}) { | |
| 107 | 112 | super() | |
| 108 | 113 | ||
@@ -170,6 +175,14 @@ class Client extends DispatcherBase { | |||
| 170 | 175 | throw new InvalidArgumentError('maxRequestsPerClient must be a positive number') | |
| 171 | 176 | } | |
| 172 | 177 | ||
| 178 | + if (localAddress != null && (typeof localAddress !== 'string' || net.isIP(localAddress) === 0)) { | ||
| 179 | + throw new InvalidArgumentError('localAddress must be valid string IP address') | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) { | ||
| 183 | + throw new InvalidArgumentError('maxResponseSize must be a positive number') | ||
| 184 | + } | ||
| 185 | + | ||
| 173 | 186 | if (typeof connect !== 'function') { | |
| 174 | 187 | connect = buildConnector({ | |
| 175 | 188 | ...tls, | |
@@ -193,6 +206,7 @@ class Client extends DispatcherBase { | |||
| 193 | 206 | this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold | |
| 194 | 207 | this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout] | |
| 195 | 208 | this[kServerName] = null | |
| 209 | + this[kLocalAddress] = localAddress != null ? localAddress : null | ||
| 196 | 210 | this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming | |
| 197 | 211 | this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming | |
| 198 | 212 | this[kHostHeader] = `host: ${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}\r\n` | |
@@ -202,6 +216,7 @@ class Client extends DispatcherBase { | |||
| 202 | 216 | this[kMaxRedirections] = maxRedirections | |
| 203 | 217 | this[kMaxRequests] = maxRequestsPerClient | |
| 204 | 218 | this[kClosedResolve] = null | |
| 219 | + this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1 | ||
| 205 | 220 | ||
| 206 | 221 | // kQueue is built up of 3 sections separated by | |
| 207 | 222 | // the kRunningIdx and kPendingIdx indices. | |
@@ -426,6 +441,7 @@ class Parser { | |||
| 426 | 441 | ||
| 427 | 442 | this.keepAlive = '' | |
| 428 | 443 | this.contentLength = '' | |
| 444 | + this.maxResponseSize = client[kMaxResponseSize] | ||
| 429 | 445 | } | |
| 430 | 446 | ||
| 431 | 447 | setTimeout (value, type) { | |
@@ -542,19 +558,6 @@ class Parser { | |||
| 542 | 558 | } | |
| 543 | 559 | } | |
| 544 | 560 | ||
| 545 | - finish () { | ||
| 546 | - try { | ||
| 547 | - try { | ||
| 548 | - currentParser = this | ||
| 549 | - } finally { | ||
| 550 | - currentParser = null | ||
| 551 | - } | ||
| 552 | - } catch (err) { | ||
| 553 | - /* istanbul ignore next: difficult to make a test case for */ | ||
| 554 | - util.destroy(this.socket, err) | ||
| 555 | - } | ||
| 556 | - } | ||
| 557 | - | ||
| 558 | 561 | destroy () { | |
| 559 | 562 | assert(this.ptr != null) | |
| 560 | 563 | assert(currentParser == null) | |
@@ -783,7 +786,7 @@ class Parser { | |||
| 783 | 786 | } | |
| 784 | 787 | ||
| 785 | 788 | onBody (buf) { | |
| 786 | - const { client, socket, statusCode } = this | ||
| 789 | + const { client, socket, statusCode, maxResponseSize } = this | ||
| 787 | 790 | ||
| 788 | 791 | if (socket.destroyed) { | |
| 789 | 792 | return -1 | |
@@ -802,6 +805,11 @@ class Parser { | |||
| 802 | 805 | ||
| 803 | 806 | assert(statusCode >= 200) | |
| 804 | 807 | ||
| 808 | + if (maxResponseSize > -1 && this.bytesRead + buf.length > maxResponseSize) { | ||
| 809 | + util.destroy(socket, new ResponseExceededMaxSizeError()) | ||
| 810 | + return -1 | ||
| 811 | + } | ||
| 812 | + | ||
| 805 | 813 | this.bytesRead += buf.length | |
| 806 | 814 | ||
| 807 | 815 | try { | |
@@ -917,7 +925,7 @@ function onSocketError (err) { | |||
| 917 | 925 | // to the user. | |
| 918 | 926 | if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { | |
| 919 | 927 | // We treat all incoming data so for as a valid response. | |
| 920 | - parser.finish() | ||
| 928 | + parser.onMessageComplete() | ||
| 921 | 929 | return | |
| 922 | 930 | } | |
| 923 | 931 | ||
@@ -951,7 +959,7 @@ function onSocketEnd () { | |||
| 951 | 959 | ||
| 952 | 960 | if (parser.statusCode && !parser.shouldKeepAlive) { | |
| 953 | 961 | // We treat all incoming data so far as a valid response. | |
| 954 | - parser.finish() | ||
| 962 | + parser.onMessageComplete() | ||
| 955 | 963 | return | |
| 956 | 964 | } | |
| 957 | 965 | ||
@@ -961,6 +969,11 @@ function onSocketEnd () { | |||
| 961 | 969 | function onSocketClose () { | |
| 962 | 970 | const { [kClient]: client } = this | |
| 963 | 971 | ||
| 972 | + if (!this[kError] && this[kParser].statusCode && !this[kParser].shouldKeepAlive) { | ||
| 973 | + // We treat all incoming data so far as a valid response. | ||
| 974 | + this[kParser].onMessageComplete() | ||
| 975 | + } | ||
| 976 | + | ||
| 964 | 977 | this[kParser].destroy() | |
| 965 | 978 | this[kParser] = null | |
| 966 | 979 | ||
@@ -1020,7 +1033,8 @@ async function connect (client) { | |||
| 1020 | 1033 | hostname, | |
| 1021 | 1034 | protocol, | |
| 1022 | 1035 | port, | |
| 1023 | - servername: client[kServerName] | ||
| 1036 | + servername: client[kServerName], | ||
| 1037 | + localAddress: client[kLocalAddress] | ||
| 1024 | 1038 | }, | |
| 1025 | 1039 | connector: client[kConnector] | |
| 1026 | 1040 | }) | |
@@ -1033,7 +1047,8 @@ async function connect (client) { | |||
| 1033 | 1047 | hostname, | |
| 1034 | 1048 | protocol, | |
| 1035 | 1049 | port, | |
| 1036 | - servername: client[kServerName] | ||
| 1050 | + servername: client[kServerName], | ||
| 1051 | + localAddress: client[kLocalAddress] | ||
| 1037 | 1052 | }, (err, socket) => { | |
| 1038 | 1053 | if (err) { | |
| 1039 | 1054 | reject(err) | |
@@ -1076,7 +1091,8 @@ async function connect (client) { | |||
| 1076 | 1091 | hostname, | |
| 1077 | 1092 | protocol, | |
| 1078 | 1093 | port, | |
| 1079 | - servername: client[kServerName] | ||
| 1094 | + servername: client[kServerName], | ||
| 1095 | + localAddress: client[kLocalAddress] | ||
| 1080 | 1096 | }, | |
| 1081 | 1097 | connector: client[kConnector], | |
| 1082 | 1098 | socket | |
@@ -1093,7 +1109,8 @@ async function connect (client) { | |||
| 1093 | 1109 | hostname, | |
| 1094 | 1110 | protocol, | |
| 1095 | 1111 | port, | |
| 1096 | - servername: client[kServerName] | ||
| 1112 | + servername: client[kServerName], | ||
| 1113 | + localAddress: client[kLocalAddress] | ||
| 1097 | 1114 | }, | |
| 1098 | 1115 | connector: client[kConnector], | |
| 1099 | 1116 | error: err | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) { | |||
| 21 | 21 | timeout = timeout == null ? 10e3 : timeout | |
| 22 | 22 | maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions | |
| 23 | 23 | ||
| 24 | - return function connect ({ hostname, host, protocol, port, servername, httpSocket }, callback) { | ||
| 24 | + return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) { | ||
| 25 | 25 | let socket | |
| 26 | 26 | if (protocol === 'https:') { | |
| 27 | 27 | if (!tls) { | |
@@ -39,6 +39,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) { | |||
| 39 | 39 | ...options, | |
| 40 | 40 | servername, | |
| 41 | 41 | session, | |
| 42 | + localAddress, | ||
| 42 | 43 | socket: httpSocket, // upgrade socket connection | |
| 43 | 44 | port: port || 443, | |
| 44 | 45 | host: hostname | |
@@ -70,6 +71,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) { | |||
| 70 | 71 | socket = net.connect({ | |
| 71 | 72 | highWaterMark: 64 * 1024, // Same as nodejs fs streams. | |
| 72 | 73 | ...options, | |
| 74 | + localAddress, | ||
| 73 | 75 | port: port || 80, | |
| 74 | 76 | host: hostname | |
| 75 | 77 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -183,6 +183,16 @@ class HTTPParserError extends Error { | |||
| 183 | 183 | } | |
| 184 | 184 | } | |
| 185 | 185 | ||
| 186 | + class ResponseExceededMaxSizeError extends UndiciError { | ||
| 187 | + constructor (message) { | ||
| 188 | + super(message) | ||
| 189 | + Error.captureStackTrace(this, ResponseExceededMaxSizeError) | ||
| 190 | + this.name = 'ResponseExceededMaxSizeError' | ||
| 191 | + this.message = message || 'Response content exceeded max size' | ||
| 192 | + this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE' | ||
| 193 | + } | ||
| 194 | + } | ||
| 195 | + | ||
| 186 | 196 | module.exports = { | |
| 187 | 197 | HTTPParserError, | |
| 188 | 198 | UndiciError, | |
@@ -201,5 +211,6 @@ module.exports = { | |||
| 201 | 211 | SocketError, | |
| 202 | 212 | NotSupportedError, | |
| 203 | 213 | ResponseContentLengthMismatchError, | |
| 204 | - BalancedPoolMissingUpstreamError | ||
| 214 | + BalancedPoolMissingUpstreamError, | ||
| 215 | + ResponseExceededMaxSizeError | ||
| 205 | 216 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments