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.4.2 to 0.5.0

source/agent.js

22

package.json
{
"name": "http2-wrapper",
"version": "0.4.2",
"description": "HTTP2 client, but with the HTTP1 API",
"version": "0.5.0",
"description": "HTTP2 client, just with the familiar `https` API",
"main": "source",

@@ -17,3 +17,6 @@ "engines": {

"keywords": [
"http2"
"http2",
"https",
"http",
"request"
],

@@ -30,7 +33,11 @@ "repository": {

"homepage": "https://github.com/szmarczak/http2-wrapper#readme",
"dependencies": {
"resolve-alpn": "^1.0.0"
},
"devDependencies": {
"@sindresorhus/is": "^0.14.0",
"ava": "^1.0.1",
"coveralls": "^3.0.2",
"delay": "^4.1.0",
"benchmark": "^2.1.4",
"coveralls": "^3.0.4",
"delay": "^4.3.0",
"get-stream": "^4.0.0",

@@ -40,7 +47,10 @@ "nyc": "^13.1.0",

"pem": "^1.12.5",
"resolve-alpn": "^1.0.0",
"tempy": "^0.2.1",
"timekeeper": "^2.2.0",
"to-readable-stream": "^1.0.0",
"xo": "^0.23.0"
},
"ava": {
"timeout": "2m"
}
}
# http2-wrapper
> HTTP2 client, but with the HTTP1 API
> HTTP2 client, just with the familiar `https` API

@@ -9,3 +9,5 @@ [![Build Status](https://travis-ci.org/szmarczak/http2-wrapper.svg?branch=master)](https://travis-ci.org/szmarczak/http2-wrapper)

This package was created for the purpose of supporting HTTP2 without the need to rewrite your code.<br>
**This package is under heavy development. It may contain bugs. Don't forget to report them if you notice any.**
This package was created to support HTTP2 without the need to rewrite your code.<br>
I recommend adapting to the [`http2`](https://nodejs.org/api/http2.html) module if possible - it's much simpler to use and has many cool features!

@@ -15,2 +17,7 @@

## Installation
> `$ npm install http2-wrapper`<br>
> `$ yarn add http2-wrapper`
## Usage

@@ -31,11 +38,11 @@ ```js

const req = http2.request(options, res => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
const request = http2.request(options, response => {
console.log('statusCode:', response.statusCode);
console.log('headers:', response.headers);
const body = [];
res.on('data', chunk => {
response.on('data', chunk => {
body.push(chunk);
});
res.on('end', () => {
response.on('end', () => {
console.log('body:', Buffer.concat(body).toString());

@@ -45,6 +52,6 @@ });

req.on('error', e => console.error(e));
request.on('error', e => console.error(e));
req.write('123');
req.end('456');
request.write('123');
request.end('456');

@@ -83,10 +90,11 @@ // statusCode: 200

### http2.auto(url, options)
**Note**: the `session` option accepts an instance of [`Http2Session`](https://nodejs.org/api/http2.html#http2_class_http2session). To pass a SSL session, use `socketSession` instead.
**Note**: [`resolve-alpn`](https://github.com/szmarczak/resolve-alpn) package required.
### http2.auto(url, options, callback)
Performs [ALPN](https://nodejs.org/api/tls.html#tls_alpn_and_sni) negotiation.
Returns a Promise giving [`HTTP2ClientRequest`](https://github.com/szmarczak/http2-wrapper#http2http2clientrequest) instance or [`ClientRequest`](https://nodejs.org/api/https.html#https_class_https_clientrequest) instance (depending on the ALPN).<br>
Usage example:
Returns a Promise giving proper `ClientRequest` instance (depending on the ALPN).
**Note**: the `agent` option also accepts an object with `http`, `https` and `http2` properties.
```js

@@ -108,10 +116,9 @@ 'use strict';

try {
const req = await http2.auto(options);
req.on('response', res => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
const request = await http2.auto(options, response => {
console.log('statusCode:', response.statusCode);
console.log('headers:', response.headers);
const body = [];
res.on('data', chunk => body.push(chunk));
res.on('end', () => {
response.on('data', chunk => body.push(chunk));
response.on('end', () => {
console.log('body:', Buffer.concat(body).toString());

@@ -121,6 +128,6 @@ });

req.on('error', console.error);
request.on('error', console.error);
req.write('123');
req.end('456');
request.write('123');
request.end('456');
} catch (error) {

@@ -156,74 +163,152 @@ console.error(error);

#### url
### http2.auto.resolveALPN(options)
Type: `string` `URL`
Resolves ALPN using HTTP options.
### http2.request(url, options, callback)
Same as [`https.request`](https://nodejs.org/api/https.html#https_https_request_options_callback).
### http2.get(url, options, callback)
Same as [`https.get`](https://nodejs.org/api/https.html#https_https_get_options_callback).
### new http2.ClientRequest(url, options, callback)
Same as [`https.ClientRequest`](https://nodejs.org/api/https.html#https_class_https_clientrequest).
### new http2.IncomingMessage(socket)
Same as [`https.IncomingMessage`](https://nodejs.org/api/https.html#https_class_https_incomingmessage).
### new http2.Agent(options)
**Note**: this is **not** compatible with the classic `http.Agent`.
Usage example:
```js
'use strict';
const http2 = require('http2-wrapper');
class MyAgent extends http2.Agent {
createConnection(authority, options) {
console.log(`Connecting to ${authority}`);
return http2.Agent.connect(authority, options);
}
}
http2.get({
hostname: 'google.com',
agent: new MyAgent()
}, res => {
res.on('data', chunk => console.log(`Received chunk of ${chunk.length} bytes`));
});
```
#### options
Type: `object`
Each option is assigned to each `Agent` instance and can be changed later.
#### callback(error, clientRequestInstance)
##### timeout
Type: `Function`
Type: `number`<br>
Default: `30000`
### http2.auto.resolveALPN(options)
If there's no activity in given time (milliseconds), the session is closed.
Resolves ALPN using HTTP options.
##### maxSessions
### http2.request(url, options, callback)
Type: `number`<br>
Default: `Infinity`
Same as [https.request](https://nodejs.org/api/https.html#https_https_request_options_callback).
Max sessions per origin.
**Note**: `session` accepts [HTTP2 sessions](https://nodejs.org/api/http2.html#http2_http2_connect_authority_options_listener). To pass TLS session, you need to use `socketSession` instead.
##### maxFreeSessions
### http2.get(url, options, callback)
Type: `number`<br>
Default: `1`
Same as [https.get](https://nodejs.org/api/https.html#https_https_get_options_callback).
Max free sessions per origin.
### http2.HTTP2ClientRequest
#### agent.getName(authority, options)
Same as [https.ClientRequest](https://nodejs.org/api/https.html#https_class_https_clientrequest).
Returns a `string` containing a proper name for sessions created with these options.
### http2.HTTP2IncomingMessage
#### agent.getSession(authority, options)
Same as [https.IncomingMessage](https://nodejs.org/api/https.html#https_class_https_incomingmessage).
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.
## Tips
##### authority
### Reusing sessions
Type: `string`
Due to the lack of HTTP2 session pools, you have to manage them by yourself. To reuse a session, you need to pass it through the `session` option:
Authority used to create a new session.
```js
const http2 = require('http2-wrapper');
const session = http2.connect('https://google.com');
session.unref(); // The session will destroy automatically when the process exits
##### options
const options = {
hostname: 'google.com',
session
};
Type: `Object`
// Reuses the same session
const makeRequest = () => {
http2.request(options, res => {
res.on('data', chunk => {
console.log(`Received ${chunk.length} bytes of data`);
});
}).end();
};
Options used to create a new session.
makeRequest(); // Received 220 bytes of data
makeRequest(); // Received 220 bytes of data
```
#### agent.request(authority, options, headers)
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.
#### agent.createConnection(authority, options)
Returns a new TLS `Socket`. It defaults to `Agent.connect(authority, options)`.
#### agent.closeFreeSessions()
Makes an attempt to close free sessions. Only sessions with no concurrent streams are closed.
#### agent.destroy()
Destroys **all** sessions.
## Notes
- [WebSockets over HTTP2 is not supported](https://github.com/nodejs/node/issues/15230).
- [HTTP2 sockets cannot be malformed](https://github.com/nodejs/node/blob/cc8250fab86486632fdeb63892be735d7628cd13/lib/internal/http2/core.js#L725).
- HTTP2IncomingMessage has no [`close` event](https://nodejs.org/api/http.html#http_event_close_2) to prevent [memory leaks](https://github.com/szmarczak/http2-wrapper/blob/945f8b140541d2e8f0dfcb946a2ead3b4d3233ae/source/client-request.js#L72-L74).
- There's no `pool` (`agent` equivalent) option [yet](https://github.com/nodejs/node/issues/17746).
- [WebSockets over HTTP2 is not supported yet](https://github.com/nodejs/node/issues/15230), although there is [a proposal](https://tools.ietf.org/html/rfc8441) already.
- [HTTP2 sockets cannot be malformed](https://github.com/nodejs/node/blob/cc8250fab86486632fdeb63892be735d7628cd13/lib/internal/http2/core.js#L725), therefore modifying the socket will have no effect.
## Benchmarks
CPU: Intel i7-7700k<br>
Server: H2O 2.2.5 [`h2o.conf`](h2o.conf)<br>
Node: v12.6.0<br>
Commit: latest<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)
Fastest is http2
```
`http2-wrapper`:
- 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`.
`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`.
## Related
- [`got`](https://github.com/sindresorhus/got) - Simplified HTTP requests
## License
MIT
'use strict';
const http = require('http');
const https = require('https');
const Http2ClientRequest = require('./client-request');
const httpResolveALPN = require('./utils/http-resolve-alpn');
const urlToOptions = require('./utils/url-to-options');
const HTTP2ClientRequest = require('./client-request');
module.exports = async (input, options) => {
const cache = {};
module.exports = async (input, options, callback) => {
if (typeof input === 'string' || input instanceof URL) {

@@ -13,27 +15,49 @@ input = urlToOptions(new URL(input));

options = {...input, ...options};
options.protocol = options.protocol || 'https:';
options.port = options.port || (options.protocol === 'http:' ? 80 : 443);
options = {
protocol: 'https:',
...input,
...options
};
if (options.protocol === 'https:') {
const {alpnProtocol, socket} = await httpResolveALPN({
...options,
resolveSocket: !options.createConnection
});
const host = options.hostname || options.host || 'localhost';
const port = options.port || 443;
const ALPNProtocols = options.ALPNProtocols || ['h2', 'http/1.1'];
const name = `${host}:${port}:${ALPNProtocols.sort()}`;
if (socket) {
options.createConnection = () => socket;
let alpnProtocol = cache[name];
if (typeof alpnProtocol === 'undefined') {
alpnProtocol = (await httpResolveALPN(options)).alpnProtocol;
cache[name] = alpnProtocol;
const keys = Object.keys(cache);
/* istanbul ignore next */
if (keys.length > 100) {
delete cache[keys.pop()];
}
}
if (alpnProtocol === 'h2') {
return new HTTP2ClientRequest(options);
if (options.agent && options.agent.http2) {
options.agent = options.agent.http2;
}
return new Http2ClientRequest(options, callback);
}
if (options.agent && options.agent.https) {
options.agent = options.agent.https;
}
options._defaultAgent = https.globalAgent;
} else if (options.agent && options.agent.http) {
options.agent = options.agent.http;
}
options.session = options.socketSession;
return new http.ClientRequest(options);
return new http.ClientRequest(options, callback);
};
module.exports.resolveALPN = httpResolveALPN;
module.exports.protocolCache = cache;
'use strict';
const http2 = require('http2');
const {Writable} = require('stream');
const {Agent, globalAgent} = require('./agent');
const HTTP2IncomingMessage = require('./incoming-message');

@@ -8,10 +9,9 @@ const urlToOptions = require('./utils/url-to-options');

const {
ERR_HTTP_HEADERS_SENT,
ERR_INVALID_HTTP_TOKEN,
ERR_HTTP_INVALID_HEADER_VALUE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_PROTOCOL
ERR_INVALID_PROTOCOL,
ERR_HTTP_HEADERS_SENT
} = require('./utils/errors');
const {
NGHTTP2_CANCEL,
HTTP2_HEADER_STATUS,

@@ -24,8 +24,10 @@ HTTP2_HEADER_METHOD,

const kHeaders = Symbol('headers');
const kCustomSession = Symbol('customSession');
const kErrorHandler = Symbol('errorHandler');
const kAuthority = Symbol('authority');
const kAgent = Symbol('agent');
const kSession = Symbol('session');
const kOptions = Symbol('options');
const kFlushedHeaders = Symbol('flushedHeaders');
const kPendingPromise = Symbol('pendingPromise');
const noop = () => {};
class HTTP2ClientRequest extends Writable {
class ClientRequest extends Writable {
constructor(input, options, cb) {

@@ -35,3 +37,2 @@ super();

if (typeof input === 'string' || input instanceof URL) {
// (input, options, cb)
input = urlToOptions(new URL(input));

@@ -45,8 +46,16 @@ }

} else {
// (input, options, cb)
options = {...input, ...options};
}
options.protocol = options.protocol || 'https:';
options.port = options.port || 443;
const defaultPort = options.defaultPort || (this.agent && this.agent.defaultPort);
options = {
protocol: 'https:',
port: defaultPort || 443,
preconnect: true,
...options,
host: options.hostname || options.host || 'localhost'
};
if (options.protocol !== 'https:') {

@@ -56,63 +65,61 @@ throw new ERR_INVALID_PROTOCOL(options.protocol, 'https:');

const {timeout} = options;
delete options.timeout;
this[kHeaders] = Object.create(null);
this[kErrorHandler] = error => this.emit('error', error);
this.socket = null;
this.connection = null;
// Don't confuse with the `net` module.
this.method = options.method ? options.method.toUpperCase() : 'GET';
this.path = options.path || '/';
this.session = options.session;
options.path = options.socketPath;
options.session = options.socketSession;
if (this.session) {
this[kCustomSession] = true;
} else {
this[kCustomSession] = false;
this.session = http2.connect(options.authority || options, options);
this.res = null;
this.aborted = false;
if (options.headers) {
for (const [header, value] of Object.entries(options.headers)) {
this[kHeaders][header.toLowerCase()] = value;
}
}
// Forwards errors to this instance.
this.session.once('error', this[kErrorHandler]);
if (options.auth && !Reflect.has(this[kHeaders], 'authorization')) {
this[kHeaders].authorization = 'Basic ' + Buffer.from(options.auth).toString('base64');
}
// Note: response `close` event is not supported,
// because if you keep the session open till the process exits
// there will be a memory leak.
if (options.agent) {
this[kAgent] = options.agent;
} else if (options.session) {
this[kSession] = options.session;
} else if (options.agent === false) {
this[kAgent] = new Agent({maxFreeSessions: 0});
} else if (options.agent === null || typeof options.agent === 'undefined') {
if (typeof options.createConnection !== 'function') {
this[kAgent] = globalAgent;
}
} else if (typeof options.agent.request !== 'function') {
throw new ERR_INVALID_ARG_TYPE('options.agent', ['Agent-like Object', 'undefined', 'false'], options.agent);
}
this.socket = this.session.socket;
this.connection = this.socket;
options.ALPNProtocols = ['h2'];
options.session = options.socketSession;
options.path = options.socketPath;
this.method = options.method || 'GET';
this[kOptions] = options;
this[kAuthority] = options.authority || `https://${options.hostname || options.host}:${options.port}`;
if (cb) {
this.once('response', cb);
if (this[kAgent] && options.preconnect) {
this[kPendingPromise] = this[kAgent].getSession(this[kAuthority], options);
this[kPendingPromise].catch(() => {});
}
this.res = null;
this.aborted = false;
try {
if (options.headers) {
for (const [header, value] of Object.entries(options.headers)) {
this.setHeader(header, value);
}
}
if (options.auth && !Reflect.has(this[kHeaders], 'authorization')) {
this.setHeader('authorization', 'Basic ' + Buffer.from(options.auth).toString('base64'));
}
} catch (error) {
if (!this[kCustomSession]) {
this.session.close();
}
throw error;
if (timeout) {
this.setTimeout(timeout);
}
if (options.timeout) {
this.setTimeout(options.timeout);
if (cb) {
this.once('response', cb);
}
// Move the `socket` event to next tick, so the user can
// attach the listener before the event has been emitted.
process.nextTick(() => this.emit('socket', this.socket));
this[kFlushedHeaders] = false;
}

@@ -122,12 +129,20 @@

this.flushHeaders();
this._request.write(chunk, encoding, callback);
const callWrite = () => this._request.write(chunk, encoding, callback);
if (this._request) {
callWrite();
} else {
this.once('socket', callWrite);
}
}
_final(callback) {
if (this.session.destroyed) {
return;
this.flushHeaders();
const callEnd = () => this._request.end(callback);
if (this._request) {
callEnd();
} else {
this.once('socket', callEnd);
}
this.flushHeaders();
this._request.end(callback);
}

@@ -137,3 +152,3 @@

if (!this.aborted) {
process.nextTick(self => self.emit('abort'), this);
process.nextTick(() => this.emit('abort'));
}

@@ -147,5 +162,5 @@ this.aborted = true;

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

@@ -157,10 +172,4 @@ }

this._request.destroy(error);
} else {
if (!this[kCustomSession]) {
this.session.close();
}
if (error) {
process.nextTick(() => this.emit('error', error));
}
} else if (error) {
process.nextTick(() => this.emit('error', error));
}

@@ -170,89 +179,105 @@ }

flushHeaders() {
if (!this._request && !this.destroyed) {
// Makes the real request.
const headers = {
[HTTP2_HEADER_METHOD]: this.method,
...this[kHeaders]
};
if (this[kFlushedHeaders]) {
return;
}
// Update the `method` property in case user changed it.
this.method = headers[HTTP2_HEADER_METHOD].toUpperCase();
const isConnectMethod = this.method === HTTP2_METHOD_CONNECT;
this[kFlushedHeaders] = true;
// Must not specify the `:path` and `:scheme` headers for `CONNECT` requests.
if (!isConnectMethod) {
headers[HTTP2_HEADER_PATH] = this.path;
}
const isConnectMethod = this.method === HTTP2_METHOD_CONNECT;
this._request = this.session.request(headers, {
endStream: false
});
const headers = {
...this[kHeaders],
[HTTP2_HEADER_METHOD]: this.method
};
// Forwards `timeout`, `continue`, `close` and `error` events to this instance.
if (!isConnectMethod) {
proxyEvents(this._request, this, ['timeout', 'continue', 'close']);
this._request.once('error', error => this.emit('error', Reflect.has(error, 'cause') ? error.cause : error));
}
// Must not specify the `:path` and `:scheme` headers for `CONNECT` requests.
if (!isConnectMethod) {
headers[HTTP2_HEADER_PATH] = this.path;
}
// Detaches error handling (the `http2` module will pass them to the request instance).
this.session.removeListener('error', this[kErrorHandler]);
this.session.once('error', noop);
// The real magic is here
const onStream = stream => {
this._request = stream;
// Clean up error handling. We're done.
this._request.once('end', () => this.session.removeListener('error', noop));
if (!this.destroyed && !this.aborted) {
// Forwards `timeout`, `continue`, `close` and `error` events to this instance.
if (!isConnectMethod) {
proxyEvents(this._request, this, ['timeout', 'continue', 'close', 'error']);
}
// This event tells we are ready to listen for the data.
this._request.once('response', (headers, flags, rawHeaders) => {
this.res = new HTTP2IncomingMessage(this.socket);
this.res.req = this;
this.res.statusCode = headers[HTTP2_HEADER_STATUS];
this.res.headers = {...headers};
this.res.rawHeaders = [...rawHeaders];
// This event tells we are ready to listen for the data.
this._request.once('response', (headers, flags, rawHeaders) => {
this.res = new HTTP2IncomingMessage(this.socket);
this.res.req = this;
this.res.statusCode = headers[HTTP2_HEADER_STATUS];
this.res.headers = {...headers};
this.res.rawHeaders = [...rawHeaders];
this.res.once('end', () => {
if (this.aborted) {
this.res.aborted = true;
this.res.emit('aborted');
} else {
this.res.complete = true;
}
});
this.res.once('end', () => {
if (this.aborted) {
this.res.aborted = true;
this.res.emit('aborted');
} else {
this.res.complete = true;
}
});
if (isConnectMethod) {
this.res.upgrade = true;
if (isConnectMethod) {
this.res.upgrade = true;
// The HTTP1 API says the socket is detached here,
// but we can't do that so we pass the original HTTP2 request.
if (this.emit('connect', this.res, this._request, Buffer.alloc(0))) {
this.emit('close');
// The HTTP1 API says the socket is detached here,
// but we can't do that so we pass the original HTTP2 request.
if (this.emit('connect', this.res, this._request, Buffer.alloc(0))) {
this.emit('close');
} else {
// No listeners attached, destroy the original request.
this._request.destroy();
}
} else {
// No listeners attached, destroy the original request.
this._request.destroy();
}
} else {
// Forwards data
this._request.pipe(this.res);
// Forwards data
this._request.pipe(this.res);
if (!this.emit('response', this.res)) {
// No listeners attached, dump the response.
this.res._dump();
if (!this.emit('response', this.res)) {
// No listeners attached, dump the response.
this.res._dump();
}
}
}
});
});
// Emits `information` event
this._request.once('headers', headers => this.emit('information', {statusCode: headers[HTTP2_HEADER_STATUS]}));
// Emits `information` event
this._request.once('headers', headers => this.emit('information', {statusCode: headers[HTTP2_HEADER_STATUS]}));
this._request.once('trailers', (trailers, flags, rawTrailers) => {
// Assigns trailers to the response object.
this.res.trailers = {...trailers};
this.res.rawTrailers = [...rawTrailers];
});
this._request.once('trailers', (trailers, flags, rawTrailers) => {
// Assigns trailers to the response object.
this.res.trailers = {...trailers};
this.res.rawTrailers = [...rawTrailers];
});
if (!this[kCustomSession]) {
// The session is ours, we won't reuse it.
this._request.once('close', () => this.session.close());
this.socket = this._request.session.socket;
this.connection = this._request.session.socket;
process.nextTick(() => {
this.emit('socket', this._request.session.socket);
});
} else {
this._request.close(NGHTTP2_CANCEL);
}
};
this.emit('flushedHeaders');
// Makes a HTTP2 request
if (this[kSession]) {
try {
onStream(this[kSession].request(headers, {
endStream: false
}));
} catch (error) {
this.emit('error', error);
}
} 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.emit('error', error);
});
}

@@ -262,6 +287,2 @@ }

getHeader(name) {
if (typeof name !== 'string') {
throw new ERR_INVALID_ARG_TYPE('name', 'string', name);
}
return this[kHeaders][name.toLowerCase()];

@@ -271,7 +292,3 @@ }

get headersSent() {
if (this._request) {
return Boolean(this._request.sentHeaders);
}
return false;
return this[kFlushedHeaders];
}

@@ -283,5 +300,2 @@

}
if (name === '' || typeof name !== 'string') {
throw new ERR_INVALID_HTTP_TOKEN('Header name', name);
}

@@ -295,8 +309,2 @@ delete this[kHeaders][name.toLowerCase()];

}
if (name === '' || typeof name !== 'string') {
throw new ERR_INVALID_HTTP_TOKEN('Header name', name);
}
if (value === undefined) {
throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name);
}

@@ -318,3 +326,3 @@ this[kHeaders][name.toLowerCase()] = value;

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

@@ -326,4 +334,16 @@ });

}
get maxHeadersCount() {
if (this._request) {
return this._request.session.localSettings.maxHeaderListSize;
}
return undefined;
}
set maxHeadersCount(_value) {
// Updating HTTP2 settings would affect all requests, do nothing.
}
}
module.exports = HTTP2ClientRequest;
module.exports = ClientRequest;
'use strict';
const {PassThrough} = require('stream');
class HTTP2IncomingMessage extends PassThrough {
class IncomingMessage extends PassThrough {
constructor(socket) {

@@ -49,2 +49,2 @@ super();

module.exports = HTTP2IncomingMessage;
module.exports = IncomingMessage;
'use strict';
const http2 = require('http2');
const HTTP2ClientRequest = require('./client-request');
const HTTP2IncomingMessage = require('./incoming-message');
const agent = require('./agent');
const ClientRequest = require('./client-request');
const IncomingMessage = require('./incoming-message');
const auto = require('./auto');
const request = (url, options, cb) => {
return new HTTP2ClientRequest(url, options, cb);
return new ClientRequest(url, options, cb);
};

@@ -20,7 +21,8 @@

...http2,
...agent,
auto,
request,
get,
HTTP2ClientRequest,
HTTP2IncomingMessage
ClientRequest,
IncomingMessage
};
'use strict';
/* istanbul ignore file: https://github.com/nodejs/node/blob/master/lib/internal/errors.js */
module.exports = {};
const makeError = (Base, key, getMessage) => {

@@ -10,44 +8,8 @@ module.exports[key] = class NodeError extends Base {

super(typeof getMessage === 'string' ? getMessage : getMessage(args));
this.name = `${super.name} [${key}]`;
this.code = key;
}
get name() {
return `${super.name} [${key}]`;
}
set name(value) {
Object.defineProperty(this, 'name', {
configurable: true,
enumerable: true,
value,
writable: true
});
}
get code() {
return key;
}
set code(value) {
Object.defineProperty(this, 'code', {
configurable: true,
enumerable: true,
value,
writable: true
});
}
};
};
makeError(Error, 'ERR_HTTP_HEADERS_SENT', args => {
return `Cannot ${args[0]} headers after they are sent to the client`;
});
makeError(TypeError, 'ERR_INVALID_HTTP_TOKEN', args => {
return `${args[0]} must be a valid HTTP token ["${args[1]}"]`;
});
makeError(TypeError, 'ERR_HTTP_INVALID_HEADER_VALUE', args => {
return `Invalid value "${args[0]}" for header "${args[1]}"`;
});
makeError(TypeError, 'ERR_INVALID_ARG_TYPE', args => {

@@ -61,1 +23,5 @@ const type = args[0].includes('.') ? 'property' : 'argument';

});
makeError(Error, 'ERR_HTTP_HEADERS_SENT', args => {
return `Cannot ${args[0]} headers after they are sent to the client`;
});

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

port: options.port || 443,
host: options.hostname || options.host,
host: options.hostname || options.host || 'localhost',
path: options.socketPath,

@@ -14,0 +14,0 @@ session: options.socketSession,

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