http2-wrapper
Advanced tools
Comparing version 1.0.0-beta.1 to 1.0.0-beta.2
{ | ||
"name": "http2-wrapper", | ||
"version": "1.0.0-beta.1", | ||
"version": "1.0.0-beta.2", | ||
"description": "HTTP2 client, just with the familiar `https` API", | ||
@@ -5,0 +5,0 @@ "main": "source", |
@@ -87,3 +87,3 @@ # http2-wrapper | ||
**Note:** the `session` option accepts an instance of [`Http2Session`](https://nodejs.org/api/http2.html#http2_class_http2session). To pass a TLS session, use `tlsSession` instead. | ||
**Note:** the `session` option was renamed to `tlsSession` for better readability. | ||
@@ -157,15 +157,2 @@ ### http2.auto(url, options, callback) | ||
### http2.auto.prepareRequest(options) | ||
Performs [ALPN](https://nodejs.org/api/tls.html#tls_alpn_and_sni) negotiation. | ||
Returns a Promise giving proper request function depending on the ALPN protocol. | ||
**Note:** the request function takes only two arguments: `options` and `callback`. | ||
**Tip:** the `agent` option also accepts an object with `http`, `https` and `http2` properties. | ||
### http2.auto.resolveALPN(options) | ||
Returns a Promise giving the best ALPN protocol possible. It can be either `h2` or `http/1.1`. | ||
### http2.auto.protocolCache | ||
@@ -188,2 +175,8 @@ | ||
##### options.h2session | ||
Type: `Http2Session`<br> | ||
The session used to make the actual request. If none provided, it will use `options.agent`. | ||
### http2.get(url, options, callback) | ||
@@ -329,2 +322,10 @@ | ||
#### Event: 'session' | ||
```js | ||
agent.on('session', session => { | ||
// A new session has been created by the Agent. | ||
}); | ||
``` | ||
## Notes | ||
@@ -340,11 +341,12 @@ | ||
Server: H2O 2.2.5 [`h2o.conf`](h2o.conf)<br> | ||
Node: v12.10.0 | ||
Node: 13.0.1 | ||
``` | ||
http2-wrapper x 9,954 ops/sec ±3.72% (81 runs sampled) | ||
http2-wrapper - preconfigured session x 12,309 ops/sec ±1.48% (87 runs sampled) | ||
http2 x 14,664 ops/sec ±1.63% (78 runs sampled) | ||
http2 - using PassThrough proxies x 11,884 ops/sec ±2.43% (82 runs sampled) | ||
https x 1,586 ops/sec ±4.05% (79 runs sampled) | ||
http x 5,886 ops/sec ±2.73% (76 runs sampled) | ||
http2-wrapper x 10,943 ops/sec ±4.19% (80 runs sampled) | ||
http2-wrapper - preconfigured session x 13,600 ops/sec ±1.78% (85 runs sampled) | ||
http2-wrapper - auto x 10,080 ops/sec ±4.22% (80 runs sampled) | ||
http2 x 16,078 ops/sec ±1.67% (85 runs sampled) | ||
http2 - using PassThrough proxies x 13,090 ops/sec ±6.36% (85 runs sampled) | ||
https x 1,471 ops/sec ±4.05% (74 runs sampled) | ||
http x 6,100 ops/sec ±4.89% (72 runs sampled) | ||
Fastest is http2 | ||
@@ -355,14 +357,21 @@ ``` | ||
- It's `1.473x` slower than `http2`. | ||
- It's `1.194x` slower than `http2` with `2xPassThrough`. | ||
- It's `6.276x` faster than `https`. | ||
- It's `1.691x` faster than `http`. | ||
- It's `1.4692x` slower than `http2`. | ||
- It's `1.1962x` slower than `http2` with `2xPassThrough`. | ||
- It's `7.4392x` faster than `https`. | ||
- It's `1.7939x` faster than `http`. | ||
`http2-wrapper - preconfigured session`: | ||
- It's `1.191x` slower than `http2`. | ||
- It's `1.036x` faster than `http2` with `2xPassThrough`. | ||
- It's `7.761x` faster than `https`. | ||
- It's `2.091x` faster than `http`. | ||
- It's `1.1822x` slower than `http2`. | ||
- It's almost the same as `http2` with `2xPassThrough`. | ||
- It's `9.2454x` faster than `https`. | ||
- It's `2.2295x` faster than `http`. | ||
`http2-wrapper - auto`: | ||
- It's `1.5950x` slower than `http2`. | ||
- It's `1.2986x` slower than `http2` with `2xPassThrough`. | ||
- It's `6.8525x` faster than `https`. | ||
- It's `1.6525x` faster than `http`. | ||
## Related | ||
@@ -369,0 +378,0 @@ |
@@ -265,3 +265,3 @@ 'use strict'; | ||
// The entry must be removed from the queue IMMEDIATELY when: | ||
// 1. a session connects successfully, | ||
// 1. the session connects successfully, | ||
// 2. an error occurs. | ||
@@ -268,0 +268,0 @@ const removeFromQueue = () => { |
'use strict'; | ||
const http = require('http'); | ||
const https = require('https'); | ||
const resolveALPN = require('resolve-alpn'); | ||
const QuickLRU = require('quick-lru'); | ||
const Http2ClientRequest = require('./client-request'); | ||
const httpResolveALPN = require('./utils/http-resolve-alpn'); | ||
const calculateServerName = require('./utils/calculate-server-name'); | ||
const urlToOptions = require('./utils/url-to-options'); | ||
@@ -11,37 +12,55 @@ | ||
const prepareRequest = async options => { | ||
const resolveProtocol = async options => { | ||
const name = `${options.host}:${options.port}:${options.ALPNProtocols.sort()}`; | ||
if (!cache.has(name)) { | ||
const result = (await resolveALPN(options)).alpnProtocol; | ||
cache.set(name, result); | ||
return result; | ||
} | ||
return cache.get(name); | ||
}; | ||
module.exports = async (input, options, callback) => { | ||
if (typeof input === 'string' || input instanceof URL) { | ||
input = urlToOptions(new URL(input)); | ||
} | ||
if (typeof options === 'function') { | ||
callback = options; | ||
} | ||
options = { | ||
ALPNProtocols: ['h2', 'http/1.1'], | ||
protocol: 'https:', | ||
...input, | ||
...options, | ||
resolveSocket: false | ||
}; | ||
options.host = options.hostname || options.host || 'localhost'; | ||
options.session = options.tlsSession; | ||
options.servername = options.servername || calculateServerName(options); | ||
if (options.protocol === 'https:') { | ||
const host = options.hostname || options.host || 'localhost'; | ||
const port = options.port || 443; | ||
options.port = options.port || 443; | ||
const ALPNProtocols = options.ALPNProtocols || ['h2', 'http/1.1']; | ||
const name = `${host}:${port}:${ALPNProtocols.sort()}`; | ||
const {path} = options; | ||
options.path = options.socketPath; | ||
let alpnProtocol = cache.get(name); | ||
const protocol = await resolveProtocol(options); | ||
if (typeof alpnProtocol === 'undefined') { | ||
alpnProtocol = (await httpResolveALPN(options)).alpnProtocol; | ||
cache.set(name, alpnProtocol); | ||
} | ||
options.path = path; | ||
if (alpnProtocol === 'h2') { | ||
return (options, callback) => { | ||
if (options.agent && options.agent.http2) { | ||
options = { | ||
...options, | ||
agent: options.agent.http2 | ||
}; | ||
} | ||
if (protocol === 'h2') { | ||
if (options.agent && options.agent.http2) { | ||
options.agent = options.agent.http2; | ||
} | ||
return Http2ClientRequest.request(options, callback); | ||
}; | ||
return new Http2ClientRequest(options, callback); | ||
} | ||
return (options, callback) => { | ||
options = { | ||
...options, | ||
_defaultAgent: https.globalAgent, | ||
session: options.tlsSession | ||
}; | ||
if (protocol === 'http/1.1') { | ||
if (options.agent && options.agent.https) { | ||
@@ -51,38 +70,21 @@ options.agent = options.agent.https; | ||
options._defaultAgent = https.globalAgent; | ||
return http.request(options, callback); | ||
}; | ||
} | ||
throw new Error('Unknown ALPN protocol'); | ||
} | ||
return (options, callback) => { | ||
options = { | ||
...options, | ||
_defaultAgent: http.globalAgent | ||
}; | ||
options.port = options.port || 80; | ||
if (options.agent && options.agent.http) { | ||
options.agent = options.agent.http; | ||
} | ||
return http.request(options, callback); | ||
}; | ||
}; | ||
module.exports = async (input, options, callback) => { | ||
if (typeof input === 'string' || input instanceof URL) { | ||
input = urlToOptions(new URL(input)); | ||
if (options.agent && options.agent.http) { | ||
options.agent = options.agent.http; | ||
} | ||
options = { | ||
protocol: 'https:', | ||
...input, | ||
...options | ||
}; | ||
options._defaultAgent = http.globalAgent; | ||
const request = await prepareRequest(options); | ||
return request(options, callback); | ||
return http.request(options, callback); | ||
}; | ||
module.exports.resolveALPN = httpResolveALPN; | ||
module.exports.prepareRequest = prepareRequest; | ||
module.exports.protocolCache = cache; |
@@ -52,4 +52,4 @@ 'use strict'; | ||
if (options.session) { | ||
this[kSession] = options.session; | ||
if (options.h2session) { | ||
this[kSession] = options.h2session; | ||
} else if (options.agent === false) { | ||
@@ -94,2 +94,3 @@ this.agent = new Agent({maxFreeSessions: 0}); | ||
this.aborted = false; | ||
this.reusedSocket = false; | ||
@@ -226,3 +227,5 @@ if (options.headers) { | ||
this._request.once('response', (headers, flags, rawHeaders) => { | ||
this.res = new IncomingMessage(this.connection); | ||
// If we were to emit raw request stream, it would be as fast as the native approach. | ||
// Note that wrapping the raw stream in a Proxy instance won't improve the performance (already tested it). | ||
this.res = new IncomingMessage(this.socket, this._request.readableHighWaterMark); | ||
this.res.req = this; | ||
@@ -277,3 +280,3 @@ this.res.statusCode = headers[HTTP2_HEADER_STATUS]; | ||
process.nextTick(() => { | ||
this.emit('socket', this.connection); | ||
this.emit('socket', this.socket); | ||
}); | ||
@@ -292,2 +295,4 @@ }; | ||
} else { | ||
this.reusedSocket = true; | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
@@ -377,7 +382,2 @@ this.agent.request(this[kAuthority], this[kOptions], this[kHeaders]).then(onStream, error => { | ||
const request = (url, options, callback) => { | ||
return new ClientRequest(url, options, callback); | ||
}; | ||
module.exports = ClientRequest; | ||
module.exports.request = request; |
@@ -5,4 +5,4 @@ 'use strict'; | ||
class IncomingMessage extends PassThrough { | ||
constructor(socket) { | ||
super(); | ||
constructor(socket, highWaterMark) { | ||
super({highWaterMark}); | ||
@@ -9,0 +9,0 @@ this.statusCode = null; |
@@ -8,4 +8,8 @@ 'use strict'; | ||
const request = (url, options, callback) => { | ||
return new ClientRequest(url, options, callback); | ||
}; | ||
const get = (url, options, callback) => { | ||
const req = ClientRequest.request(url, options, callback); | ||
const req = new ClientRequest(url, options, callback); | ||
req.end(); | ||
@@ -18,8 +22,8 @@ | ||
...http2, | ||
ClientRequest, | ||
IncomingMessage, | ||
...agent, | ||
auto, | ||
request: ClientRequest.request, | ||
request, | ||
get, | ||
ClientRequest, | ||
IncomingMessage | ||
auto | ||
}; |
'use strict'; | ||
const net = require('net'); | ||
/* istanbul ignore file: https://github.com/nodejs/node/blob/v12.10.0/lib/_http_agent.js */ | ||
/* istanbul ignore file: https://github.com/nodejs/node/blob/v13.0.1/lib/_http_agent.js */ | ||
module.exports = (options, headers) => { | ||
module.exports = options => { | ||
let servername = options.host; | ||
const hostHeader = headers.host; | ||
const hostHeader = options.headers && options.headers.host; | ||
@@ -15,3 +15,3 @@ if (hostHeader) { | ||
} else { | ||
servername = hostHeader.substr(1, index - 1); | ||
servername = hostHeader.slice(1, -1); | ||
} | ||
@@ -18,0 +18,0 @@ } else { |
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
378
46140
12
1006