npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

http2wrap

v2.2.1

Published

HTTP2 client, just with the familiar `https` API

Downloads

5

Readme

http2-wrapper

HTTP/2 client, just with the familiar https API

Node CI codecov npm install size

This package was created to support HTTP/2 without the need to rewrite your code. I recommend adapting to the http2 module if possible - it's much simpler to use and has many cool features!

Tip: http2-wrapper is very useful when you rely on other modules that use the HTTP/1 API and you want to support HTTP/2.

Pro Tip: While the native http2 doesn't have agents yet, you can use http2-wrapper Agents and still operate on the native HTTP/2 streams.

Installation

$ npm install http2-wrapper $ yarn add http2-wrapper

Usage

const http2 = require('http2-wrapper');

const options = {
	hostname: 'nghttp2.org',
	protocol: 'https:',
	path: '/httpbin/post',
	method: 'POST',
	headers: {
		'content-length': 6
	}
};

const request = http2.request(options, response => {
	console.log('statusCode:', response.statusCode);
	console.log('headers:', response.headers);

	const body = [];
	response.on('data', chunk => {
		body.push(chunk);
	});
	response.on('end', () => {
		console.log('body:', Buffer.concat(body).toString());
	});
});

request.on('error', console.error);

request.write('123');
request.end('456');

// statusCode: 200
// headers: [Object: null prototype] {
//   ':status': 200,
//   date: 'Fri, 27 Sep 2019 19:45:46 GMT',
//   'content-type': 'application/json',
//   'access-control-allow-origin': '*',
//   'access-control-allow-credentials': 'true',
//   'content-length': '239',
//   'x-backend-header-rtt': '0.002516',
//   'strict-transport-security': 'max-age=31536000',
//   server: 'nghttpx',
//   via: '1.1 nghttpx',
//   'alt-svc': 'h3-23=":4433"; ma=3600',
//   'x-frame-options': 'SAMEORIGIN',
//   'x-xss-protection': '1; mode=block',
//   'x-content-type-options': 'nosniff'
// }
// body: {
//   "args": {},
//   "data": "123456",
//   "files": {},
//   "form": {},
//   "headers": {
//     "Content-Length": "6",
//     "Host": "nghttp2.org"
//   },
//   "json": 123456,
//   "origin": "xxx.xxx.xxx.xxx",
//   "url": "https://nghttp2.org/httpbin/post"
// }

API

Note: The session option was renamed to tlsSession for better readability.

Note: The timeout option applies to HTTP/2 streams only. In order to set session timeout, pass an Agent with custom timeout option set.

http2.auto(url, options, callback)

Performs ALPN negotiation. Returns a Promise giving proper ClientRequest instance (depending on the ALPN).

Note: The agent option represents an object with http, https and http2 properties.

const http2 = require('http2-wrapper');

const options = {
	hostname: 'httpbin.org',
	protocol: 'http:', // Try changing this to https:
	path: '/post',
	method: 'POST',
	headers: {
		'content-length': 6
	}
};

(async () => {
	try {
		const request = await http2.auto(options, response => {
			console.log('statusCode:', response.statusCode);
			console.log('headers:', response.headers);

			const body = [];
			response.on('data', chunk => body.push(chunk));
			response.on('end', () => {
				console.log('body:', Buffer.concat(body).toString());
			});
		});

		request.on('error', console.error);

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

// statusCode: 200
// headers: { connection: 'close',
//   server: 'gunicorn/19.9.0',
//   date: 'Sat, 15 Dec 2018 18:19:32 GMT',
//   'content-type': 'application/json',
//   'content-length': '259',
//   'access-control-allow-origin': '*',
//   'access-control-allow-credentials': 'true',
//   via: '1.1 vegur' }
// body: {
//   "args": {},
//   "data": "123456",
//   "files": {},
//   "form": {},
//   "headers": {
//     "Connection": "close",
//     "Content-Length": "6",
//     "Host": "httpbin.org"
//   },
//   "json": 123456,
//   "origin": "xxx.xxx.xxx.xxx",
//   "url": "http://httpbin.org/post"
// }

http2.auto.protocolCache

An instance of quick-lru used for ALPN cache.

There is a maximum of 100 entries. You can modify the limit through protocolCache.maxSize - note that the change will be visible globally.

http2.auto.createResolveProtocol(cache, queue, connect)

cache

Type: Map<string, string>

This is the store where cached ALPN protocols are put into.

queue

Type: Map<string, Promise>

This is the store that contains pending ALPN negotiation promises.

connect

Type: (options, callback) => TLSSocket | Promise<TLSSocket>

See https://github.com/szmarczak/resolve-alpn#connect

http2.auto.resolveProtocol(options)

Returns a Promise<{alpnProtocol: string}>.

http2.request(url, options, callback)

Same as https.request.

options.h2session

Type: Http2Session

The session used to make the actual request. If none provided, it will use options.agent to get one.

http2.get(url, options, callback)

Same as https.get.

new http2.ClientRequest(url, options, callback)

Same as https.ClientRequest.

new http2.IncomingMessage(socket)

Same as https.IncomingMessage.

new http2.Agent(options)

Note: this is not compatible with the classic http.Agent.

Usage example:

const http2 = require('http2-wrapper');

class MyAgent extends http2.Agent {
	createConnection(origin, options) {
		console.log(`Connecting to ${http2.Agent.normalizeOrigin(origin)}`);
		return http2.Agent.connect(origin, options);
	}
}

http2.get({
	hostname: 'google.com',
	agent: new MyAgent()
}, response => {
	response.on('data', chunk => console.log(`Received chunk of ${chunk.length} bytes`));
});

options

Each option is an Agent property and can be changed later.

timeout

Type: number Default: 0

If there's no activity after timeout milliseconds, the session will be closed. If 0, no timeout is applied.

maxSessions

Type: number Default: Infinity

The maximum amount of sessions in total.

maxEmptySessions

Type: number Default: 10

The maximum amount of empty sessions in total. An empty session is a session with no pending requests.

maxCachedTlsSessions

Type: number Default: 100

The maximum amount of cached TLS sessions.

agent.protocol

Type: string Default: https:

agent.settings

Type: object Default: {enablePush: false}

Settings used by the current agent instance.

agent.normalizeOptions(options)

Returns a string representing normalized options.

Agent.normalizeOptions({servername: 'example.com'});
// => ':::::::::::::::::::::::::::::::::::::'

agent.getSession(origin, options)

origin

Type: string URL object

Origin used to create new session.

options

Type: object

Options used to create new session.

Returns a Promise giving free Http2Session. If no free sessions are found, a new one is created.

A session is considered free when pending streams count is less than max concurrent streams settings.

agent.getSession(origin, options, listener)

listener

Type: object

{
	reject: error => void,
	resolve: session => void
}

If the listener argument is present, the Promise will resolve immediately. It will use the resolve function to pass the session.

agent.request(origin, options, headers, streamOptions)

Returns a Promise giving Http2Stream.

agent.createConnection(origin, options)

Returns a new TLSSocket. It defaults to Agent.connect(origin, options).

agent.closeEmptySessions(count)

count

Type: number Default: Number.POSITIVE_INFINITY

Makes an attempt to close empty sessions. Only sessions with 0 concurrent streams will be closed.

agent.destroy(reason)

Destroys all sessions.

agent.emptySessionCount

Type: number

A number of empty sessions.

agent.pendingSessionCount

Type: number

A number of pending sessions.

agent.sessionCount

Type: number

A number of all sessions held by the Agent.

Event: 'session'

agent.on('session', session => {
	// A new session has been created by the Agent.
});

Proxy support

Currently http2-wrapper provides support for these proxies:

  • HttpOverHttp2
  • HttpsOverHttp2
  • Http2OverHttp2
  • Http2OverHttp
  • Http2OverHttps

Any of the above can be accessed via http2wrapper.proxies. Check out the examples/proxies directory to learn more.

Note: If you use the http2.auto function, the real IP address will leak. http2wrapper is not aware of the context. It will create a connection to the end server using your real IP address to get the ALPN protocol. Then it will create another connection using proxy. To migitate this, you need to pass a custom resolveProtocol function as an option:

const resolveAlpnProxy = new URL('https://username:password@localhost:8000');
const connect = async (options, callback) => new Promise((resolve, reject) => {
	const host = `${options.host}:${options.port}`;

	(async () => {
		try {
			const request = await http2.auto(resolveAlpnProxy, {
				method: 'CONNECT',
				headers: {
					host
				},
				path: host,

				// For demo purposes only!
				rejectUnauthorized: false,
			});

			request.end();

			request.once('error', reject);

			request.once('connect', (response, socket, head) => {
				if (head.length > 0) {
					reject(new Error(`Unexpected data before CONNECT tunnel: ${head.length} bytes`));

					socket.destroy();
					return;
				}

				const tlsSocket = tls.connect({
					...options,
					socket
				}, callback);

				resolve(tlsSocket);
			});
		} catch (error) {
			reject(error);
		}
	})();
});

// This is required to prevent leaking real IP address on ALPN negotiation
const resolveProtocol = http2.auto.createResolveProtocol(new Map(), new Map(), connect);

const request = await http2.auto('https://httpbin.org/anything', {
	agent: {…},
	resolveProtocol
}, response => {
	// Read the response here
});

request.end();

See unknown-over-unknown.js to learn more.

Mirroring another server

See examples/proxies/mirror.js for an example.

WebSockets over HTTP/2

See examples/ws for an example.

Push streams

See examples/push-stream for an example.

Related

  • got - Simplified HTTP requests
  • http2-proxy - A simple http/2 & http/1.1 spec compliant proxy helper for Node.

License

MIT