Comparing version 4.0.0-alpha.3 to 4.0.0-alpha.4
41
index.js
@@ -38,7 +38,32 @@ 'use strict' | ||
function makeDispatcher (fn) { | ||
return (url, { agent, dispatcher = getGlobalDispatcher(), method = 'GET', ...opts } = {}, ...additionalArgs) => { | ||
if (opts.path != null) { | ||
throw new InvalidArgumentError('unsupported opts.path') | ||
return (url, opts, handler) => { | ||
if (typeof opts === 'function') { | ||
handler = opts | ||
opts = null | ||
} | ||
if (!url || (typeof url !== 'string' && typeof url !== 'object' && !(url instanceof URL))) { | ||
throw new InvalidArgumentError('invalid url') | ||
} | ||
if (opts != null && typeof opts !== 'object') { | ||
throw new InvalidArgumentError('invalid opts') | ||
} | ||
if (opts && opts.path != null) { | ||
if (typeof opts.path !== 'string') { | ||
throw new InvalidArgumentError('invalid opts.path') | ||
} | ||
url = new URL(opts.path, util.parseOrigin(url)) | ||
} else { | ||
if (!opts) { | ||
opts = typeof url === 'object' ? url : {} | ||
} | ||
url = util.parseURL(url) | ||
} | ||
const { agent, dispatcher = getGlobalDispatcher() } = opts | ||
if (agent) { | ||
@@ -48,6 +73,8 @@ throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?') | ||
const { origin, pathname, search } = util.parseURL(url) | ||
const path = search ? `${pathname}${search}` : pathname | ||
return fn.call(dispatcher, { ...opts, origin, method, path }, ...additionalArgs) | ||
return fn.call(dispatcher, { | ||
...opts, | ||
origin: url.origin, | ||
path: url.search ? `${url.pathname}${url.search}` : url.pathname, | ||
method: opts.method ? opts.method : opts.body ? 'PUT' : 'GET' | ||
}, handler) | ||
} | ||
@@ -54,0 +81,0 @@ } |
@@ -49,3 +49,3 @@ 'use strict' | ||
this[kClients] = new Map() | ||
this[kFinalizer] = new FinalizationRegistry(key => /* istanbul ignore next: gc is undeterministic */{ | ||
this[kFinalizer] = new FinalizationRegistry(/* istanbul ignore next: gc is undeterministic */ key => { | ||
const ref = this[kClients].get(key) | ||
@@ -52,0 +52,0 @@ if (ref !== undefined && ref.deref() === undefined) { |
@@ -389,3 +389,3 @@ 'use strict' | ||
class HTTPParserError extends Error { | ||
constructor (message, code) { | ||
constructor (message, code, data) { | ||
super(message) | ||
@@ -395,2 +395,3 @@ Error.captureStackTrace(this, HTTPParserError) | ||
this.code = code ? `HPE_${code}` : undefined | ||
this.data = data.toString() | ||
} | ||
@@ -404,12 +405,4 @@ } | ||
wasm_on_message_begin: p => { | ||
return 0 | ||
}, | ||
wasm_on_url: /* istanbul ignore next: not used */ (p, at, length) => { | ||
return 0 | ||
}, | ||
wasm_on_status: (p, at, length) => { | ||
return 0 | ||
}, | ||
wasm_on_header_field: (p, at, len) => { | ||
assert.strictEqual(currentParser.ptr, p) | ||
const start = at - currentParser.bufferPtr | ||
@@ -420,2 +413,3 @@ const end = start + len | ||
wasm_on_header_value: (p, at, len) => { | ||
assert.strictEqual(currentParser.ptr, p) | ||
const start = at - currentParser.bufferPtr | ||
@@ -425,9 +419,8 @@ const end = start + len | ||
}, | ||
wasm_on_headers_complete: p => { | ||
const statusCode = llhttp.exports.llhttp_get_status_code(p) | ||
const upgrade = Boolean(llhttp.exports.llhttp_get_upgrade(p)) | ||
const shouldKeepAlive = Boolean(llhttp.exports.llhttp_should_keep_alive(p)) | ||
return currentParser.onHeadersComplete(statusCode, upgrade, shouldKeepAlive) || 0 | ||
wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => { | ||
assert.strictEqual(currentParser.ptr, p) | ||
return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0 | ||
}, | ||
wasm_on_body: (p, at, len) => { | ||
assert.strictEqual(currentParser.ptr, p) | ||
const start = at - currentParser.bufferPtr | ||
@@ -438,2 +431,3 @@ const end = start + len | ||
wasm_on_message_complete: (p) => { | ||
assert.strictEqual(currentParser.ptr, p) | ||
return currentParser.onMessageComplete() || 0 | ||
@@ -456,4 +450,4 @@ } | ||
this.ptr = llhttp.exports.llhttp_alloc(constants.TYPE.RESPONSE) | ||
this.bufferSize = 0 | ||
this.bufferPtr = null | ||
this.bufferSize = 16384 | ||
this.bufferPtr = llhttp.exports.malloc(this.bufferSize) | ||
this.bufferRef = null | ||
@@ -472,7 +466,3 @@ this.client = client | ||
this.paused = false | ||
this.resume = this.resume.bind(this) | ||
this.execute = this.execute.bind(this) | ||
socket.on('data', this.execute) | ||
} | ||
@@ -514,6 +504,3 @@ | ||
this.socket.resume() | ||
if (!this.socket.readableLength) { | ||
this.execute(EMPTY_BUF) // Flush parser. | ||
} | ||
this.execute(EMPTY_BUF) // Flush parser. | ||
} | ||
@@ -524,2 +511,3 @@ | ||
assert(currentParser == null) | ||
assert(!this.paused) | ||
@@ -530,5 +518,3 @@ const { socket } = this | ||
if (data.length > this.bufferSize) { | ||
if (this.bufferPtr) { | ||
llhttp.exports.free(this.bufferPtr) | ||
} | ||
llhttp.exports.free(this.bufferPtr) | ||
this.bufferSize = Math.ceil(data.length / 4096) * 4096 | ||
@@ -538,9 +524,6 @@ this.bufferPtr = llhttp.exports.malloc(this.bufferSize) | ||
if (data.length) { | ||
assert(this.bufferPtr != null) | ||
assert(this.bufferSize >= data.length) | ||
assert(this.bufferSize >= data.length) | ||
// TODO (perf): Can we avoid this copy somehow? | ||
new Uint8Array(llhttp.exports.memory.buffer, this.bufferPtr, this.bufferSize).set(data) | ||
} | ||
// TODO (perf): Can we avoid this copy somehow? | ||
new Uint8Array(llhttp.exports.memory.buffer, this.bufferPtr, this.bufferSize).set(data) | ||
@@ -557,28 +540,40 @@ // Call `execute` on the wasm parser. | ||
if (ret === constants.ERROR.OK) { | ||
return | ||
} | ||
const offset = llhttp.exports.llhttp_get_error_pos(this.ptr) - this.bufferPtr | ||
if (ret === constants.ERROR.PAUSED_UPGRADE) { | ||
const offset = llhttp.exports.llhttp_get_error_pos(this.ptr) - this.bufferPtr | ||
this.onUpgrade(data.slice(offset)) | ||
} else if (ret === constants.ERROR.PAUSED) { | ||
const offset = llhttp.exports.llhttp_get_error_pos(this.ptr) - this.bufferPtr | ||
this.paused = true | ||
socket.pause() | ||
socket.unshift(data.slice(offset)) | ||
} else { | ||
} else if (ret !== constants.ERROR.OK) { | ||
const ptr = llhttp.exports.llhttp_get_error_reason(this.ptr) | ||
let message | ||
let message = '' | ||
if (ptr) { | ||
const len = new Uint8Array(llhttp.exports.memory.buffer).indexOf(0, ptr) - ptr | ||
const len = new Uint8Array(llhttp.exports.memory.buffer, ptr).indexOf(0) | ||
message = Buffer.from(llhttp.exports.memory.buffer, ptr, len).toString() | ||
} else { | ||
message = '' | ||
} | ||
const code = constants.ERROR[ret] | ||
util.destroy(socket, new HTTPParserError(message, code)) | ||
util.destroy(socket, new HTTPParserError(message, constants.ERROR[ret], data)) | ||
} | ||
} | ||
destroy () { | ||
assert(this.ptr != null) | ||
assert(this.bufferPtr != null) | ||
assert(currentParser == null) | ||
llhttp.exports.llhttp_free(this.ptr) | ||
this.ptr = null | ||
llhttp.exports.free(this.bufferPtr) | ||
this.bufferPtr = null | ||
clearTimeout(this.timeout) | ||
this.timeout = null | ||
this.timeoutValue = null | ||
this.timeoutType = null | ||
this.paused = false | ||
} | ||
onHeaderField (buf) { | ||
@@ -732,9 +727,9 @@ const len = this.headers.length | ||
if (!keepAlive && key.length === 10 && key.toString().toLowerCase() === 'keep-alive') { | ||
keepAlive = val.toString() | ||
keepAlive = val | ||
} else if (!trailers && key.length === 7 && key.toString().toLowerCase() === 'trailer') { | ||
trailers = val.toString() | ||
trailers = val | ||
} | ||
} | ||
this.trailers = trailers ? trailers.toLowerCase().split(/,\s*/) : [] | ||
this.trailers = trailers ? trailers.toString().toLowerCase().split(/,\s*/) : [] | ||
@@ -878,24 +873,2 @@ if (shouldKeepAlive && client[kPipelining]) { | ||
} | ||
destroy () { | ||
assert(this.ptr != null) | ||
assert(currentParser == null) | ||
llhttp.exports.llhttp_free(this.ptr) | ||
this.ptr = null | ||
if (this.bufferPtr != null) { | ||
llhttp.exports.free(this.bufferPtr) | ||
this.bufferPtr = null | ||
} | ||
clearTimeout(this.timeout) | ||
this.timeout = null | ||
this.timeoutValue = null | ||
this.timeoutType = null | ||
this.paused = false | ||
this.socket.removeListener('data', this.execute) | ||
} | ||
} | ||
@@ -906,2 +879,3 @@ | ||
/* istanbul ignore else */ | ||
if (timeoutType === TIMEOUT_HEADERS) { | ||
@@ -913,16 +887,19 @@ if (!socket[kWriting]) { | ||
} else if (timeoutType === TIMEOUT_BODY) { | ||
if (!socket.isPaused()) { | ||
if (!parser.paused) { | ||
util.destroy(socket, new BodyTimeoutError()) | ||
} | ||
} else if (timeoutType === TIMEOUT_IDLE) { | ||
if (client[kRunning] === 0 && client[kKeepAliveTimeoutValue]) { | ||
util.destroy(socket, new InformationalError('socket idle timeout')) | ||
} | ||
assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]) | ||
util.destroy(socket, new InformationalError('socket idle timeout')) | ||
} else if (timeoutType === TIMEOUT_CONNECT) { | ||
if (!client[kConnected]) { | ||
util.destroy(socket, new ConnectTimeoutError()) | ||
} | ||
assert(!client[kConnected]) | ||
util.destroy(socket, new ConnectTimeoutError()) | ||
} | ||
} | ||
function onSocketData (data) { | ||
const { [kParser]: parser } = this | ||
parser.execute(data) | ||
} | ||
function onSocketConnect () { | ||
@@ -976,2 +953,3 @@ const { [kClient]: client } = this | ||
.removeListener('error', onSocketError) | ||
.removeListener('data', onSocketData) | ||
.removeListener('end', onSocketEnd) | ||
@@ -1073,2 +1051,3 @@ .removeListener('close', onSocketClose) | ||
.on('error', onSocketError) | ||
.on('data', onSocketData) | ||
.on('end', onSocketEnd) | ||
@@ -1075,0 +1054,0 @@ .on('close', onSocketClose) |
@@ -38,2 +38,10 @@ 'use strict' | ||
if (url.path != null && typeof url.path !== 'string') { | ||
throw new InvalidArgumentError('invalid path') | ||
} | ||
if (url.pathname != null && typeof url.pathname !== 'string') { | ||
throw new InvalidArgumentError('invalid pathname') | ||
} | ||
if (url.hostname != null && typeof url.hostname !== 'string') { | ||
@@ -43,3 +51,7 @@ throw new InvalidArgumentError('invalid hostname') | ||
if (!/https?/.test(url.protocol)) { | ||
if (url.origin != null && typeof url.origin !== 'string') { | ||
throw new InvalidArgumentError('invalid origin') | ||
} | ||
if (!/^https?:/.test(url.origin || url.protocol)) { | ||
throw new InvalidArgumentError('invalid protocol') | ||
@@ -49,9 +61,13 @@ } | ||
if (!(url instanceof URL)) { | ||
const port = url.port || { | ||
'http:': 80, | ||
'https:': 443 | ||
}[url.protocol] | ||
assert(port != null) | ||
const path = url.path || `${url.pathname || '/'}${url.search || ''}` | ||
url = new URL(`${url.protocol}//${url.hostname}:${port}${path}`) | ||
const port = url.port != null | ||
? url.port | ||
: { 'http:': 80, 'https:': 443 }[url.protocol] | ||
const origin = url.origin != null | ||
? url.origin | ||
: `${url.protocol}//${url.hostname}:${port}` | ||
const path = url.path != null | ||
? url.path | ||
: `${url.pathname || ''}${url.search || ''}` | ||
url = new URL(path, origin) | ||
} | ||
@@ -134,3 +150,3 @@ | ||
function parseKeepAliveTimeout (val) { | ||
const m = val.match(KEEPALIVE_TIMEOUT_EXPR) | ||
const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR) | ||
return m ? parseInt(m[1]) * 1000 : null | ||
@@ -137,0 +153,0 @@ } |
{ | ||
"name": "undici", | ||
"version": "4.0.0-alpha.3", | ||
"version": "4.0.0-alpha.4", | ||
"description": "An HTTP/1.1 client, written from scratch for Node.js", | ||
@@ -46,7 +46,7 @@ "homepage": "https://undici.nodejs.org", | ||
"devDependencies": { | ||
"@sinonjs/fake-timers": "^6.0.1", | ||
"@types/node": "^14.6.2", | ||
"@sinonjs/fake-timers": "^7.0.5", | ||
"@types/node": "^14.14.39", | ||
"abort-controller": "^3.0.0", | ||
"benchmark": "^2.1.4", | ||
"concurrently": "^6.0.0", | ||
"concurrently": "^6.0.2", | ||
"docsify-cli": "^4.4.2", | ||
@@ -53,0 +53,0 @@ "https-pem": "^2.0.0", |
@@ -60,3 +60,3 @@ # undici | ||
### `undici.request(url[, options]): Promise` | ||
### `undici.request([url, options]): Promise` | ||
@@ -68,3 +68,3 @@ Arguments: | ||
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher) | ||
* **method** `String` - Default: `GET` | ||
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` | ||
* **maxRedirections** `Integer` - Default: `0` | ||
@@ -80,3 +80,3 @@ | ||
### `undici.stream(url, options, factory): Promise` | ||
### `undici.stream([url, options, ]factory): Promise` | ||
@@ -88,3 +88,3 @@ Arguments: | ||
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher) | ||
* **method** `String` - Default: `GET` | ||
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` | ||
* **factory** `Dispatcher.stream.factory` | ||
@@ -94,4 +94,2 @@ | ||
`url` may contain pathname. `options` may not contain path. | ||
Calls `options.dispatcher.stream(options, factory)`. | ||
@@ -101,3 +99,3 @@ | ||
### `undici.pipeline(url, options, handler): Duplex` | ||
### `undici.pipeline([url, options, ]handler): Duplex` | ||
@@ -109,3 +107,3 @@ Arguments: | ||
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher) | ||
* **method** `String` - Default: `GET` | ||
* **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` | ||
* **handler** `Dispatcher.pipeline.handler` | ||
@@ -115,4 +113,2 @@ | ||
`url` may contain pathname. `options` may not contain path. | ||
Calls `options.dispatch.pipeline(options, handler)`. | ||
@@ -122,3 +118,3 @@ | ||
### `undici.connect(options[, callback])` | ||
### `undici.connect([url, options]): Promise` | ||
@@ -129,5 +125,5 @@ Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT). | ||
* **url** `string | URL | object` | ||
* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions) | ||
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher) | ||
* **method** `String` - Default: `GET` | ||
* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional) | ||
@@ -137,4 +133,2 @@ | ||
`url` may contain pathname. `options` may not contain path. | ||
Calls `options.dispatch.connect(options)`. | ||
@@ -144,3 +138,3 @@ | ||
### `undici.upgrade(options[, callback])` | ||
### `undici.upgrade([url, options]): Promise` | ||
@@ -151,5 +145,5 @@ Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details. | ||
* **url** `string | URL | object` | ||
* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions) | ||
* **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher) | ||
* **method** `String` - Default: `GET` | ||
* **callback** `(error: Error | null, data: UpgradeData) => void` (optional) | ||
@@ -159,4 +153,2 @@ | ||
`url` may contain pathname. `options` may not contain path. | ||
Calls `options.dispatcher.upgrade(options)`. | ||
@@ -215,7 +207,13 @@ | ||
* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup> | ||
* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor> | ||
* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina> | ||
* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag> | ||
### Releasers | ||
* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina> | ||
* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag> | ||
## License | ||
MIT |
Sorry, the diff of this file is not supported yet
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
363295
4660
205