Comparing version 5.1.1 to 5.2.0
@@ -22,3 +22,2 @@ # Errors | ||
| `InformationalError` | `UND_ERR_INFO` | expected error with reason | | ||
| `TrailerMismatchError` | `UND_ERR_TRAILER_MISMATCH` | trailers did not match specification | | ||
@@ -25,0 +24,0 @@ ### `SocketError` |
'use strict' | ||
const Agent = require('./lib/agent') | ||
const { getGlobalDispatcher } = require('./lib/global') | ||
const fetchImpl = require('./lib/fetch') | ||
const globalDispatcher = new Agent() | ||
const fetchImpl = require('./lib/fetch') | ||
module.exports.fetch = async function fetch (resource) { | ||
return fetchImpl.apply(globalDispatcher, arguments) | ||
const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | ||
return fetchImpl.apply(dispatcher, arguments) | ||
} | ||
@@ -11,0 +10,0 @@ module.exports.FormData = require('./lib/fetch/formdata').FormData |
16
index.js
@@ -18,2 +18,3 @@ 'use strict' | ||
const ProxyAgent = require('./lib/proxy-agent') | ||
const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global') | ||
@@ -36,15 +37,2 @@ const nodeVersion = process.versions.node.split('.') | ||
let globalDispatcher = new Agent() | ||
function setGlobalDispatcher (agent) { | ||
if (!agent || typeof agent.dispatch !== 'function') { | ||
throw new InvalidArgumentError('Argument agent must implement Agent') | ||
} | ||
globalDispatcher = agent | ||
} | ||
function getGlobalDispatcher () { | ||
return globalDispatcher | ||
} | ||
function makeDispatcher (fn) { | ||
@@ -103,3 +91,3 @@ return (url, opts, handler) => { | ||
} | ||
const dispatcher = getGlobalDispatcher() | ||
const dispatcher = (arguments[1] && arguments[1].dispatcher) || getGlobalDispatcher() | ||
return fetchImpl.apply(dispatcher, arguments) | ||
@@ -106,0 +94,0 @@ } |
@@ -14,3 +14,2 @@ 'use strict' | ||
ResponseContentLengthMismatchError, | ||
TrailerMismatchError, | ||
InvalidArgumentError, | ||
@@ -429,3 +428,2 @@ RequestAbortedError, | ||
this.trailer = '' | ||
this.keepAlive = '' | ||
@@ -620,4 +618,2 @@ this.contentLength = '' | ||
this.keepAlive += buf.toString() | ||
} else if (key.length === 7 && key.toString().toLowerCase() === 'trailer') { | ||
this.trailer += buf.toString() | ||
} else if (key.length === 14 && key.toString().toLowerCase() === 'content-length') { | ||
@@ -825,3 +821,3 @@ this.contentLength += buf.toString() | ||
onMessageComplete () { | ||
const { client, socket, statusCode, upgrade, trailer, headers, contentLength, bytesRead, shouldKeepAlive } = this | ||
const { client, socket, statusCode, upgrade, headers, contentLength, bytesRead, shouldKeepAlive } = this | ||
@@ -845,3 +841,2 @@ if (socket.destroyed && (!statusCode || shouldKeepAlive)) { | ||
this.contentLength = '' | ||
this.trailer = '' | ||
this.keepAlive = '' | ||
@@ -857,19 +852,2 @@ | ||
const trailers = trailer ? trailer.split(/,\s*/) : [] | ||
for (let i = 0; i < trailers.length; i++) { | ||
const trailer = trailers[i] | ||
let found = false | ||
for (let n = 0; n < headers.length; n += 2) { | ||
const key = headers[n] | ||
if (key.length === trailer.length && key.toString().toLowerCase() === trailer.toLowerCase()) { | ||
found = true | ||
break | ||
} | ||
} | ||
if (!found) { | ||
util.destroy(socket, new TrailerMismatchError()) | ||
return -1 | ||
} | ||
} | ||
/* istanbul ignore next: should be handled by llhttp? */ | ||
@@ -876,0 +854,0 @@ if (request.method !== 'HEAD' && contentLength && bytesRead !== parseInt(contentLength, 10)) { |
@@ -119,12 +119,2 @@ 'use strict' | ||
class TrailerMismatchError extends UndiciError { | ||
constructor (message) { | ||
super(message) | ||
Error.captureStackTrace(this, TrailerMismatchError) | ||
this.name = 'TrailerMismatchError' | ||
this.message = message || 'Trailers does not match trailer header' | ||
this.code = 'UND_ERR_TRAILER_MISMATCH' | ||
} | ||
} | ||
class ClientDestroyedError extends UndiciError { | ||
@@ -200,3 +190,2 @@ constructor (message) { | ||
ConnectTimeoutError, | ||
TrailerMismatchError, | ||
InvalidArgumentError, | ||
@@ -203,0 +192,0 @@ InvalidReturnValueError, |
@@ -80,2 +80,29 @@ // https://github.com/Ethan-Arrowood/undici-fetch | ||
// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object | ||
const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) | ||
// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object | ||
function makeHeadersIterator (iterator) { | ||
const i = { | ||
next () { | ||
if (Object.getPrototypeOf(this) !== i) { | ||
throw new TypeError( | ||
'\'next\' called on an object that does not implement interface Headers Iterator.' | ||
) | ||
} | ||
return iterator.next() | ||
}, | ||
// The class string of an iterator prototype object for a given interface is the | ||
// result of concatenating the identifier of the interface and the string " Iterator". | ||
[Symbol.toStringTag]: 'Headers Iterator' | ||
} | ||
// The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%. | ||
Object.setPrototypeOf(i, esIteratorPrototype) | ||
// esIteratorPrototype needs to be the prototype of i | ||
// which is the prototype of an empty object. Yes, it's confusing. | ||
return Object.setPrototypeOf({}, i) | ||
} | ||
class HeadersList { | ||
@@ -173,10 +200,7 @@ constructor (init) { | ||
get [Symbol.toStringTag] () { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
return this.constructor.name | ||
} | ||
toString () { | ||
// https://fetch.spec.whatwg.org/#dom-headers-append | ||
append (name, value) { | ||
if (!(this instanceof Headers)) { | ||
@@ -186,16 +210,9 @@ throw new TypeError('Illegal invocation') | ||
return Object.prototype.toString.call(this) | ||
} | ||
append (...args) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 2) { | ||
if (arguments.length < 2) { | ||
throw new TypeError( | ||
`Failed to execute 'append' on 'Headers': 2 arguments required, but only ${args.length} present.` | ||
`Failed to execute 'append' on 'Headers': 2 arguments required, but only ${arguments.length} present.` | ||
) | ||
} | ||
const normalizedName = normalizeAndValidateHeaderName(String(args[0])) | ||
const normalizedName = normalizeAndValidateHeaderName(String(name)) | ||
@@ -218,16 +235,18 @@ if (this[kGuard] === 'immutable') { | ||
return this[kHeadersList].append(String(args[0]), String(args[1])) | ||
return this[kHeadersList].append(String(name), String(value)) | ||
} | ||
delete (...args) { | ||
// https://fetch.spec.whatwg.org/#dom-headers-delete | ||
delete (name) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 1) { | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
`Failed to execute 'delete' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
`Failed to execute 'delete' on 'Headers': 1 argument required, but only ${arguments.length} present.` | ||
) | ||
} | ||
const normalizedName = normalizeAndValidateHeaderName(String(args[0])) | ||
const normalizedName = normalizeAndValidateHeaderName(String(name)) | ||
@@ -250,38 +269,44 @@ if (this[kGuard] === 'immutable') { | ||
return this[kHeadersList].delete(String(args[0])) | ||
return this[kHeadersList].delete(String(name)) | ||
} | ||
get (...args) { | ||
// https://fetch.spec.whatwg.org/#dom-headers-get | ||
get (name) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 1) { | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
`Failed to execute 'get' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
`Failed to execute 'get' on 'Headers': 1 argument required, but only ${arguments.length} present.` | ||
) | ||
} | ||
return this[kHeadersList].get(String(args[0])) | ||
return this[kHeadersList].get(String(name)) | ||
} | ||
has (...args) { | ||
// https://fetch.spec.whatwg.org/#dom-headers-has | ||
has (name) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 1) { | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
`Failed to execute 'has' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
`Failed to execute 'has' on 'Headers': 1 argument required, but only ${arguments.length} present.` | ||
) | ||
} | ||
return this[kHeadersList].has(String(args[0])) | ||
return this[kHeadersList].has(String(name)) | ||
} | ||
set (...args) { | ||
// https://fetch.spec.whatwg.org/#dom-headers-set | ||
set (name, value) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 2) { | ||
if (arguments.length < 2) { | ||
throw new TypeError( | ||
`Failed to execute 'set' on 'Headers': 2 arguments required, but only ${args.length} present.` | ||
`Failed to execute 'set' on 'Headers': 2 arguments required, but only ${arguments.length} present.` | ||
) | ||
@@ -294,3 +319,3 @@ } | ||
this[kGuard] === 'request' && | ||
forbiddenHeaderNames.includes(String(args[0]).toLocaleLowerCase()) | ||
forbiddenHeaderNames.includes(String(name).toLocaleLowerCase()) | ||
) { | ||
@@ -302,3 +327,3 @@ return | ||
this[kGuard] === 'response' && | ||
forbiddenResponseHeaderNames.includes(String(args[0]).toLocaleLowerCase()) | ||
forbiddenResponseHeaderNames.includes(String(name).toLocaleLowerCase()) | ||
) { | ||
@@ -308,3 +333,3 @@ return | ||
return this[kHeadersList].set(String(args[0]), String(args[1])) | ||
return this[kHeadersList].set(String(name), String(value)) | ||
} | ||
@@ -322,3 +347,3 @@ | ||
return this[kHeadersSortedMap].keys() | ||
return makeHeadersIterator(this[kHeadersSortedMap].keys()) | ||
} | ||
@@ -331,3 +356,3 @@ | ||
return this[kHeadersSortedMap].values() | ||
return makeHeadersIterator(this[kHeadersSortedMap].values()) | ||
} | ||
@@ -340,6 +365,10 @@ | ||
return this[kHeadersSortedMap].entries() | ||
return makeHeadersIterator(this[kHeadersSortedMap].entries()) | ||
} | ||
[Symbol.iterator] () { | ||
/** | ||
* @param {(value: string, key: string, self: Headers) => void} callbackFn | ||
* @param {unknown} thisArg | ||
*/ | ||
forEach (callbackFn, thisArg = globalThis) { | ||
if (!(this instanceof Headers)) { | ||
@@ -349,15 +378,9 @@ throw new TypeError('Illegal invocation') | ||
return this[kHeadersSortedMap] | ||
} | ||
forEach (...args) { | ||
if (!(this instanceof Headers)) { | ||
throw new TypeError('Illegal invocation') | ||
} | ||
if (args.length < 1) { | ||
if (arguments.length < 1) { | ||
throw new TypeError( | ||
`Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${args.length} present.` | ||
`Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${arguments.length} present.` | ||
) | ||
} | ||
if (typeof args[0] !== 'function') { | ||
if (typeof callbackFn !== 'function') { | ||
throw new TypeError( | ||
@@ -367,8 +390,6 @@ "Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'." | ||
} | ||
const callback = args[0] | ||
const thisArg = args[1] | ||
this[kHeadersSortedMap].forEach((value, index) => { | ||
callback.apply(thisArg, [value, index, this]) | ||
}) | ||
for (const [key, value] of this) { | ||
callbackFn.apply(thisArg, [value, key, this]) | ||
} | ||
} | ||
@@ -375,0 +396,0 @@ |
@@ -422,12 +422,6 @@ /* globals AbortController */ | ||
// list, append header’s name/header’s value to this’s headers. | ||
if (headers instanceof Headers) { | ||
// TODO (fix): Why doesn't this work? | ||
// for (const [key, val] of headers[kHeadersList]) { | ||
// this[kHeaders].append(key, val) | ||
// } | ||
this[kState].headersList = new HeadersList([ | ||
...this[kState].headersList, | ||
...headers[kHeadersList] | ||
]) | ||
if (headers.constructor.name === 'Headers') { | ||
for (const [key, val] of headers[kHeadersList] || headers) { | ||
this[kHeaders].append(key, val) | ||
} | ||
} else { | ||
@@ -472,3 +466,2 @@ // 5. Otherwise, fill this’s headers with headers. | ||
this[kHeaders].append('content-type', contentType) | ||
this[kState].headersList.append('content-type', contentType) | ||
} | ||
@@ -475,0 +468,0 @@ } |
@@ -232,4 +232,3 @@ 'use strict' | ||
case 511: return 'Network Authentication Required' | ||
default: | ||
throw new ReferenceError(`Unknown status code "${statusCode}"!`) | ||
default: return 'unknown' | ||
} | ||
@@ -236,0 +235,0 @@ } |
{ | ||
"name": "undici", | ||
"version": "5.1.1", | ||
"version": "5.2.0", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -76,3 +76,3 @@ "homepage": "https://undici.nodejs.org", | ||
"concurrently": "^7.1.0", | ||
"cronometro": "^0.8.0", | ||
"cronometro": "^1.0.5", | ||
"delay": "^5.0.0", | ||
@@ -83,5 +83,6 @@ "docsify-cli": "^4.4.3", | ||
"husky": "^7.0.2", | ||
"import-fresh": "^3.3.0", | ||
"jest": "^28.0.1", | ||
"jsfuzz": "^1.0.15", | ||
"mocha": "^9.1.1", | ||
"mocha": "^10.0.0", | ||
"p-timeout": "^3.2.0", | ||
@@ -92,3 +93,3 @@ "pre-commit": "^1.2.2", | ||
"semver": "^7.3.5", | ||
"sinon": "^13.0.2", | ||
"sinon": "^14.0.0", | ||
"snazzy": "^9.0.0", | ||
@@ -95,0 +96,0 @@ "standard": "^17.0.0", |
@@ -197,3 +197,18 @@ # undici | ||
You can pass an optional dispatcher to `fetch` as: | ||
```js | ||
import { fetch, Agent } from 'undici' | ||
const res = await fetch('https://example.com', { | ||
// Mocks are also supported | ||
dispatcher: new Agent({ | ||
keepAliveTimeout: 10, | ||
keepAliveMaxTimeout: 10 | ||
}) | ||
}) | ||
const json = await res.json() | ||
console.log(json) | ||
``` | ||
#### `request.body` | ||
@@ -200,0 +215,0 @@ |
@@ -10,2 +10,4 @@ // based on https://github.com/Ethan-Arrowood/undici-fetch/blob/249269714db874351589d2d364a0645d5160ae71/index.d.ts (MIT license) | ||
import Dispatcher = require('./dispatcher') | ||
export type RequestInfo = string | URL | Request | ||
@@ -40,5 +42,17 @@ | ||
export interface HeadersIterator<T, TReturn = any, TNext = undefined> { | ||
next(...args: [] | [TNext]): IteratorResult<T, TReturn>; | ||
} | ||
export interface HeadersIterableIterator<T> extends HeadersIterator<T> { | ||
[Symbol.iterator](): HeadersIterableIterator<T>; | ||
} | ||
export interface HeadersIterable<T> { | ||
[Symbol.iterator](): HeadersIterator<T>; | ||
} | ||
export type HeadersInit = string[][] | Record<string, string | ReadonlyArray<string>> | Headers | ||
export declare class Headers implements Iterable<[string, string]> { | ||
export declare class Headers implements HeadersIterable<[string, string]> { | ||
constructor (init?: HeadersInit) | ||
@@ -55,6 +69,6 @@ readonly append: (name: string, value: string) => void | ||
readonly keys: () => IterableIterator<string> | ||
readonly values: () => IterableIterator<string> | ||
readonly entries: () => IterableIterator<[string, string]> | ||
readonly [Symbol.iterator]: () => Iterator<[string, string]> | ||
readonly keys: () => HeadersIterableIterator<string> | ||
readonly values: () => HeadersIterableIterator<string> | ||
readonly entries: () => HeadersIterableIterator<[string, string]> | ||
readonly [Symbol.iterator]: () => HeadersIterator<[string, string]> | ||
} | ||
@@ -105,2 +119,3 @@ | ||
readonly window?: null | ||
readonly dispatcher?: Dispatcher | ||
} | ||
@@ -107,0 +122,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
817209
102
11885
391
30