Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

http2-wrapper

Package Overview
Dependencies
Maintainers
1
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

http2-wrapper - npm Package Compare versions

Comparing version 0.5.0 to 0.5.1

2

package.json
{
"name": "http2-wrapper",
"version": "0.5.0",
"version": "0.5.1",
"description": "HTTP2 client, just with the familiar `https` API",

@@ -5,0 +5,0 @@ "main": "source",

@@ -156,2 +156,12 @@ # http2-wrapper

### http2.auto.prepareRequest(url, options)
Performs [ALPN](https://nodejs.org/api/tls.html#tls_alpn_and_sni) negotiation.
Returns a Promise giving an object with `options` and `request`.
Depending on the ALPN protocol, `request` is either `http2.request` or `http.request`.<br>
Options are normalized.
**Note**: the `agent` option also accepts an object with `http`, `https` and `http2` properties.
### http2.auto.resolveALPN(options)

@@ -161,2 +171,14 @@

### http2.auto.protocolCache
An object storing cache for detecting ALPN. It looks like:
```js
{
'hostname:port:alpn1,alpn2': 'alpn1'
}
```
There are maximum 100 entries.
### http2.request(url, options, callback)

@@ -234,4 +256,3 @@

Returns a Promise giving free `Http2Session`. If no free sessions are found, a new one is created.<br>
You can cancel the session query by calling `.cancel()` on the promise.
Returns a Promise giving free `Http2Session`. If no free sessions are found, a new one is created.

@@ -252,4 +273,3 @@ ##### authority

Returns a Promise giving `Http2Stream`.<br>
You can cancel the request by calling `.cancel()` on the promise, as well as using `.abort()` on the `ClientRequest` instance.
Returns a Promise giving `Http2Stream`.

@@ -278,11 +298,11 @@ #### agent.createConnection(authority, options)

Node: v12.6.0<br>
Commit: latest<br>
Version: master<br>
```
http2-wrapper x 9,453 ops/sec ±6.98% (75 runs sampled)
http2-wrapper - preconfigured session x 11,955 ops/sec ±1.89% (84 runs sampled)
http2 x 18,478 ops/sec ±1.36% (84 runs sampled)
http2 - using PassThrough x 16,099 ops/sec ±1.60% (84 runs sampled)
https x 1,547 ops/sec ±3.93% (74 runs sampled)
http x 6,138 ops/sec ±5.15% (74 runs sampled)
http2-wrapper x 10,839 ops/sec ±2.19% (84 runs sampled)
http2-wrapper - preconfigured session x 13,005 ops/sec ±3.65% (84 runs sampled)
http2 x 17,100 ops/sec ±3.40% (81 runs sampled)
http2 - using PassThrough proxies x 14,831 ops/sec ±2.22% (84 runs sampled)
https x 1,623 ops/sec ±3.40% (76 runs sampled)
http x 6,761 ops/sec ±3.46% (76 runs sampled)
Fastest is http2

@@ -293,13 +313,13 @@ ```

- It's `1.95x` slower than `http2`.
- It's `1.70x` slower than `http2` with `PassThrough`.
- It's `6.11x` faster than `https`.
- It's `1.54x` faster than `http`.
- It's `1.58x` slower than `http2`.
- It's `1.37x` slower than `http2` with `PassThrough`.
- It's `6.68x` faster than `https`.
- It's `1.60x` faster than `http`.
`http2-wrapper - preconfigured session`:
- It's `1.55x` slower than `http2`.
- It's `1.35x` slower than `http2` with `PassThrough`.
- It's `7.73x` faster than `https`.
- It's `1.95x` faster than `http`.
- It's `1.31x` slower than `http2`.
- It's `1.14x` slower than `http2` with `PassThrough`.
- It's `8.01x` faster than `https`.
- It's `1.92x` faster than `http`.

@@ -306,0 +326,0 @@ ## Related

@@ -66,3 +66,3 @@ 'use strict';

class Agent extends EventEmitter {
constructor({timeout = 30000, maxSessions = Infinity, maxFreeSessions = 1} = {}) {
constructor({timeout = 60000, maxSessions = Infinity, maxFreeSessions = 1} = {}) {
super();

@@ -106,13 +106,11 @@

if (busyLength < this.maxSessions && this.queue[name]) {
if (busyLength < this.maxSessions && this.queue[name] && !this.queue[name].completed) {
this.queue[name]();
delete this.queue[name];
this.queue[name].completed = true;
}
}
getSession(authority, options) {
let onCancel = () => {};
const promise = new Promise((resolve, reject) => {
async getSession(authority, options, preconnectOnly = false) {
return new Promise((resolve, reject) => {
const name = this.getName(authority, options);

@@ -123,126 +121,129 @@ const detached = {resolve, reject};

resolve(this.freeSessions[name][0]);
} else if (this.queue[name]) {
this.queue[name].listeners.push(detached);
} else {
const listeners = [detached];
this.queue[name] = () => {
try {
const session = http2.connect(authority, {
createConnection: this.createConnection,
...options
});
session[kCurrentStreamsCount] = 0;
return;
}
session.setTimeout(this.timeout, () => {
session.close();
});
if (this.queue[name]) {
let listenersLength = this.queue[name].listeners.length;
session.once('error', error => {
session.destroy();
if (this.queue[name].preconnectOnly) {
listenersLength--;
}
reject(error);
});
if (listenersLength < this.maxFreeSessions) {
this.queue[name].listeners.push(detached);
session.once('close', () => {
removeSession(this.freeSessions, name, session);
this._processQueue(name);
});
session.once('remoteSettings', () => {
this.freeSessions[name] = [session];
for (const listener of listeners) {
listener.resolve(session);
}
});
session[kRequest] = session.request;
session.request = () => {
throw new Error('Invalid usage. Use `agent.request(authority, options, headers)` instead.');
};
} catch (error) {
for (const listener of listeners) {
listener.reject(error);
}
if (this.queue[name].listeners.length === this.maxFreeSessions) {
// All seats are taken, remove entry from the queue.
delete this.queue[name];
}
};
this.queue[name].listeners = listeners;
this._processQueue(name);
return;
}
}
onCancel = () => {
if (this.queue[name]) {
const {listeners} = this.queue[name];
this.queue[name].splice(listeners.indexOf(detached), 1);
const listeners = [detached];
if (listeners.length === 0) {
delete this.queue[name];
}
const free = () => {
// If our entry is replaced,`completed` will be `false`.
// Or the entry will be `undefined` if all seats are taken.
if (this.queue[name] && this.queue[name].completed) {
delete this.queue[name];
}
};
});
promise.cancel = () => onCancel();
this.queue[name] = () => {
try {
const session = http2.connect(authority, {
createConnection: this.createConnection,
...options
});
session[kCurrentStreamsCount] = 0;
return promise;
}
session.setTimeout(this.timeout, () => {
session.close();
});
request(authority, options, headers) {
let onCancel = () => {};
session.once('error', error => {
session.destroy();
const promise = (async () => {
const name = this.getName(authority, options);
const sessionPromise = this.getSession(authority, options);
for (const listener of listeners) {
listener.reject(error);
}
});
onCancel = () => {
sessionPromise.cancel();
};
session.once('close', () => {
free();
const session = await sessionPromise;
const stream = session[kRequest](headers, {
endStream: false
});
removeSession(this.freeSessions, name, session);
this._processQueue(name);
});
session.ref();
session.once('remoteSettings', () => {
free();
if (++session[kCurrentStreamsCount] >= session.remoteSettings.maxConcurrentStreams) {
removeSession(this.freeSessions, name, session);
this.freeSessions[name] = [session];
if (this.busySessions[name]) {
this.busySessions[name].push(session);
} else {
this.busySessions[name] = [session];
}
}
for (const listener of listeners) {
listener.resolve(session);
}
});
stream.once('close', () => {
if (--session[kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams) {
session.unref();
session[kRequest] = session.request;
session.request = headers => {
const stream = session[kRequest](headers, {
endStream: false
});
if (removeSession(this.busySessions, name, session) && !session.destroyed) {
if ((this.freeSessions[name] || []).length < this.maxFreeSessions) {
if (this.freeSessions[name]) {
this.freeSessions[name].push(session);
session.ref();
if (++session[kCurrentStreamsCount] >= session.remoteSettings.maxConcurrentStreams) {
removeSession(this.freeSessions, name, session);
if (this.busySessions[name]) {
this.busySessions[name].push(session);
} else {
this.freeSessions[name] = [session];
this.busySessions[name] = [session];
}
} else {
session.close();
}
stream.once('close', () => {
if (--session[kCurrentStreamsCount] < session.remoteSettings.maxConcurrentStreams) {
session.unref();
if (removeSession(this.busySessions, name, session) && !session.destroyed) {
if ((this.freeSessions[name] || []).length < this.maxFreeSessions) {
if (this.freeSessions[name]) {
this.freeSessions[name].push(session);
} else {
this.freeSessions[name] = [session];
}
} else {
session.close();
}
}
}
});
return stream;
};
} catch (error) {
for (const listener of listeners) {
listener.reject(error);
}
}
});
onCancel = () => {
stream.close(http2.constants.NGHTTP2_CANCEL);
};
return stream;
})();
this.queue[name].listeners = listeners;
this.queue[name].preconnectOnly = preconnectOnly;
this.queue[name].completed = false;
this._processQueue(name);
});
}
promise.cancel = () => onCancel();
async request(authority, options, headers) {
const session = await this.getSession(authority, options);
const stream = session.request(headers);
return promise;
return stream;
}

@@ -249,0 +250,0 @@

@@ -10,3 +10,3 @@ 'use strict';

module.exports = async (input, options, callback) => {
const prepareRequest = async (input, options) => {
if (typeof input === 'string' || input instanceof URL) {

@@ -35,4 +35,4 @@ input = urlToOptions(new URL(input));

const keys = Object.keys(cache);
/* istanbul ignore next */
if (keys.length > 100) {
while (keys.length > 100) {
delete cache[keys.pop()];

@@ -47,3 +47,6 @@ }

return new Http2ClientRequest(options, callback);
return {
options,
request: Http2ClientRequest.request
};
}

@@ -61,6 +64,17 @@

options.session = options.socketSession;
return new http.ClientRequest(options, callback);
return {
options,
request: http.request
};
};
module.exports = async (input, options, callback) => {
const {options: preparedOptions, request} = await prepareRequest(input, options);
return request(preparedOptions, callback);
};
module.exports.resolveALPN = httpResolveALPN;
module.exports.prepareRequest = prepareRequest;
module.exports.protocolCache = cache;

@@ -5,3 +5,3 @@ 'use strict';

const {Agent, globalAgent} = require('./agent');
const HTTP2IncomingMessage = require('./incoming-message');
const IncomingMessage = require('./incoming-message');
const urlToOptions = require('./utils/url-to-options');

@@ -29,6 +29,5 @@ const proxyEvents = require('./utils/proxy-events');

const kFlushedHeaders = Symbol('flushedHeaders');
const kPendingPromise = Symbol('pendingPromise');
class ClientRequest extends Writable {
constructor(input, options, cb) {
constructor(input, options, callback) {
super();

@@ -41,7 +40,7 @@

if (typeof options === 'function') {
// (options, cb)
cb = options;
// (options, callback)
callback = options;
options = input;
} else {
// (input, options, cb)
// (input, options, callback)
options = {...input, ...options};

@@ -72,4 +71,4 @@ }

this.method = options.method ? options.method.toUpperCase() : 'GET';
this.path = options.path || '/';
this.method = options.method;
this.path = options.path;

@@ -111,4 +110,3 @@ this.res = null;

if (this[kAgent] && options.preconnect) {
this[kPendingPromise] = this[kAgent].getSession(this[kAuthority], options);
this[kPendingPromise].catch(() => {});
this[kAgent].getSession(this[kAuthority], options, true).catch(() => {});
}

@@ -120,4 +118,4 @@

if (cb) {
this.once('response', cb);
if (callback) {
this.once('response', callback);
}

@@ -128,2 +126,22 @@

set method(value) {
if (value) {
this[kHeaders][HTTP2_HEADER_METHOD] = value.toUpperCase();
}
}
get method() {
return this[kHeaders][HTTP2_HEADER_METHOD];
}
set path(value) {
if (value) {
this[kHeaders][HTTP2_HEADER_PATH] = value;
}
}
get path() {
return this[kHeaders][HTTP2_HEADER_PATH];
}
_write(chunk, encoding, callback) {

@@ -163,4 +181,2 @@ this.flushHeaders();

this._request.close(NGHTTP2_CANCEL);
} else if (this[kPendingPromise]) {
this[kPendingPromise].cancel();
}

@@ -186,12 +202,2 @@ }

const headers = {
...this[kHeaders],
[HTTP2_HEADER_METHOD]: this.method
};
// Must not specify the `:path` and `:scheme` headers for `CONNECT` requests.
if (!isConnectMethod) {
headers[HTTP2_HEADER_PATH] = this.path;
}
// The real magic is here

@@ -209,7 +215,7 @@ const onStream = stream => {

this._request.once('response', (headers, flags, rawHeaders) => {
this.res = new HTTP2IncomingMessage(this.socket);
this.res = new IncomingMessage(this.socket);
this.res.req = this;
this.res.statusCode = headers[HTTP2_HEADER_STATUS];
this.res.headers = {...headers};
this.res.rawHeaders = [...rawHeaders];
this.res.headers = headers;
this.res.rawHeaders = rawHeaders;

@@ -252,4 +258,4 @@ this.res.once('end', () => {

// Assigns trailers to the response object.
this.res.trailers = {...trailers};
this.res.rawTrailers = [...rawTrailers];
this.res.trailers = trailers;
this.res.rawTrailers = rawTrailers;
});

@@ -271,3 +277,3 @@

try {
onStream(this[kSession].request(headers, {
onStream(this[kSession].request(this[kHeaders], {
endStream: false

@@ -279,6 +285,4 @@ }));

} else {
this[kPendingPromise] = this[kAgent].request(this[kAuthority], this[kOptions], headers);
// eslint-disable-next-line promise/prefer-await-to-then
this[kPendingPromise].then(onStream, error => {
this[kAgent].request(this[kAuthority], this[kOptions], this[kHeaders]).then(onStream, error => {
this.emit('error', error);

@@ -321,8 +325,8 @@ });

setTimeout(ms, cb) {
setTimeout(ms, callback) {
if (this._request) {
this._request.setTimeout(ms, cb);
this._request.setTimeout(ms, callback);
} else {
this.once('socket', () => {
this._request.setTimeout(ms, cb);
this._request.setTimeout(ms, callback);
});

@@ -347,2 +351,7 @@ }

const request = (url, options, callback) => {
return new ClientRequest(url, options, callback);
};
module.exports = ClientRequest;
module.exports.request = request;

@@ -8,8 +8,4 @@ 'use strict';

const request = (url, options, cb) => {
return new ClientRequest(url, options, cb);
};
const get = (url, options, cb) => {
const req = request(url, options, cb);
const get = (url, options, callback) => {
const req = ClientRequest.request(url, options, callback);
req.end();

@@ -24,3 +20,3 @@

auto,
request,
request: ClientRequest.request,
get,

@@ -27,0 +23,0 @@ ClientRequest,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc