{"id":13527321,"url":"https://github.com/szmarczak/http2-wrapper","last_synced_at":"2025-05-15T13:02:39.237Z","repository":{"id":50495866,"uuid":"144372103","full_name":"szmarczak/http2-wrapper","owner":"szmarczak","description":"Use HTTP/2 the same way like HTTP/1","archived":false,"fork":false,"pushed_at":"2024-05-07T22:05:33.000Z","size":415,"stargazers_count":242,"open_issues_count":13,"forks_count":19,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-07T13:02:45.454Z","etag":null,"topics":["agent","alpn","http2","http2-wrapper","https","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/szmarczak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-11T09:21:51.000Z","updated_at":"2025-03-30T00:44:54.000Z","dependencies_parsed_at":"2024-06-18T12:28:07.566Z","dependency_job_id":"1108c8e6-d8a8-45b7-9ab5-5fe6e2f749fa","html_url":"https://github.com/szmarczak/http2-wrapper","commit_stats":{"total_commits":319,"total_committers":4,"mean_commits":79.75,"dds":0.009404388714733591,"last_synced_commit":"f1a1776d4b1aef1cd47d55b1a950986e86145a09"},"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szmarczak%2Fhttp2-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szmarczak%2Fhttp2-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szmarczak%2Fhttp2-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szmarczak%2Fhttp2-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/szmarczak","download_url":"https://codeload.github.com/szmarczak/http2-wrapper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248565091,"owners_count":21125415,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","alpn","http2","http2-wrapper","https","nodejs"],"created_at":"2024-08-01T06:01:45.732Z","updated_at":"2025-04-14T20:52:39.179Z","avatar_url":"https://github.com/szmarczak.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# http2-wrapper\n\u003e HTTP/2 client, just with the familiar `https` API\n\n[![Node CI](https://github.com/szmarczak/http2-wrapper/workflows/Node%20CI/badge.svg)](https://github.com/szmarczak/http2-wrapper/actions)\n[![codecov](https://codecov.io/gh/szmarczak/http2-wrapper/branch/master/graph/badge.svg)](https://codecov.io/gh/szmarczak/http2-wrapper)\n[![npm](https://img.shields.io/npm/dm/http2-wrapper.svg)](https://www.npmjs.com/package/http2-wrapper)\n[![install size](https://packagephobia.now.sh/badge?p=http2-wrapper)](https://packagephobia.now.sh/result?p=http2-wrapper)\n\nThis package was created to support HTTP/2 without the need to rewrite your code.\u003cbr\u003e\nI 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!\n\n**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.\n\n**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.\n\n## Installation\n\n\u003e `$ npm install http2-wrapper`\u003cbr\u003e\n\u003e `$ yarn add http2-wrapper`\n\n## Usage\n\n```js\nconst http2 = require('http2-wrapper');\n\nconst options = {\n\thostname: 'nghttp2.org',\n\tprotocol: 'https:',\n\tpath: '/httpbin/post',\n\tmethod: 'POST',\n\theaders: {\n\t\t'content-length': 6\n\t}\n};\n\nconst request = http2.request(options, response =\u003e {\n\tconsole.log('statusCode:', response.statusCode);\n\tconsole.log('headers:', response.headers);\n\n\tconst body = [];\n\tresponse.on('data', chunk =\u003e {\n\t\tbody.push(chunk);\n\t});\n\tresponse.on('end', () =\u003e {\n\t\tconsole.log('body:', Buffer.concat(body).toString());\n\t});\n});\n\nrequest.on('error', console.error);\n\nrequest.write('123');\nrequest.end('456');\n\n// statusCode: 200\n// headers: [Object: null prototype] {\n//   ':status': 200,\n//   date: 'Fri, 27 Sep 2019 19:45:46 GMT',\n//   'content-type': 'application/json',\n//   'access-control-allow-origin': '*',\n//   'access-control-allow-credentials': 'true',\n//   'content-length': '239',\n//   'x-backend-header-rtt': '0.002516',\n//   'strict-transport-security': 'max-age=31536000',\n//   server: 'nghttpx',\n//   via: '1.1 nghttpx',\n//   'alt-svc': 'h3-23=\":4433\"; ma=3600',\n//   'x-frame-options': 'SAMEORIGIN',\n//   'x-xss-protection': '1; mode=block',\n//   'x-content-type-options': 'nosniff'\n// }\n// body: {\n//   \"args\": {},\n//   \"data\": \"123456\",\n//   \"files\": {},\n//   \"form\": {},\n//   \"headers\": {\n//     \"Content-Length\": \"6\",\n//     \"Host\": \"nghttp2.org\"\n//   },\n//   \"json\": 123456,\n//   \"origin\": \"xxx.xxx.xxx.xxx\",\n//   \"url\": \"https://nghttp2.org/httpbin/post\"\n// }\n```\n\n## API\n\n**Note:** The `session` option was renamed to `tlsSession` for better readability.\n\n**Note:** The `timeout` option applies to HTTP/2 streams only. In order to set session timeout, pass an Agent with custom `timeout` option set.\n\n### http2.auto(url, options, callback)\n\nPerforms [ALPN](https://nodejs.org/api/tls.html#tls_alpn_and_sni) negotiation.\nReturns a Promise giving proper `ClientRequest` instance (depending on the ALPN).\n\n**Note**: The `agent` option represents an object with `http`, `https` and `http2` properties.\n\n```js\nconst http2 = require('http2-wrapper');\n\nconst options = {\n\thostname: 'httpbin.org',\n\tprotocol: 'http:', // Try changing this to https:\n\tpath: '/post',\n\tmethod: 'POST',\n\theaders: {\n\t\t'content-length': 6\n\t}\n};\n\n(async () =\u003e {\n\ttry {\n\t\tconst request = await http2.auto(options, response =\u003e {\n\t\t\tconsole.log('statusCode:', response.statusCode);\n\t\t\tconsole.log('headers:', response.headers);\n\n\t\t\tconst body = [];\n\t\t\tresponse.on('data', chunk =\u003e body.push(chunk));\n\t\t\tresponse.on('end', () =\u003e {\n\t\t\t\tconsole.log('body:', Buffer.concat(body).toString());\n\t\t\t});\n\t\t});\n\n\t\trequest.on('error', console.error);\n\n\t\trequest.write('123');\n\t\trequest.end('456');\n\t} catch (error) {\n\t\tconsole.error(error);\n\t}\n})();\n\n// statusCode: 200\n// headers: { connection: 'close',\n//   server: 'gunicorn/19.9.0',\n//   date: 'Sat, 15 Dec 2018 18:19:32 GMT',\n//   'content-type': 'application/json',\n//   'content-length': '259',\n//   'access-control-allow-origin': '*',\n//   'access-control-allow-credentials': 'true',\n//   via: '1.1 vegur' }\n// body: {\n//   \"args\": {},\n//   \"data\": \"123456\",\n//   \"files\": {},\n//   \"form\": {},\n//   \"headers\": {\n//     \"Connection\": \"close\",\n//     \"Content-Length\": \"6\",\n//     \"Host\": \"httpbin.org\"\n//   },\n//   \"json\": 123456,\n//   \"origin\": \"xxx.xxx.xxx.xxx\",\n//   \"url\": \"http://httpbin.org/post\"\n// }\n```\n\n### http2.auto.protocolCache\n\nAn instance of [`quick-lru`](https://github.com/sindresorhus/quick-lru) used for ALPN cache.\n\nThere is a maximum of 100 entries. You can modify the limit through `protocolCache.maxSize` - note that the change will be visible globally.\n\n### http2.auto.createResolveProtocol(cache, queue, connect)\n\n#### cache\n\nType: `Map\u003cstring, string\u003e`\n\nThis is the store where cached ALPN protocols are put into.\n\n#### queue\n\nType: `Map\u003cstring, Promise\u003e`\n\nThis is the store that contains pending ALPN negotiation promises.\n\n#### connect\n\nType: `(options, callback) =\u003e TLSSocket | Promise\u003cTLSSocket\u003e`\n\nSee https://github.com/szmarczak/resolve-alpn#connect\n\n### http2.auto.resolveProtocol(options)\n\nReturns a `Promise\u003c{alpnProtocol: string}\u003e`.\n\n### http2.request(url, options, callback)\n\nSame as [`https.request`](https://nodejs.org/api/https.html#https_https_request_options_callback).\n\n##### options.h2session\n\nType: `Http2Session`\u003cbr\u003e\n\nThe session used to make the actual request. If none provided, it will use `options.agent` to get one.\n\n### http2.get(url, options, callback)\n\nSame as [`https.get`](https://nodejs.org/api/https.html#https_https_get_options_callback).\n\n### new http2.ClientRequest(url, options, callback)\n\nSame as [`https.ClientRequest`](https://nodejs.org/api/https.html#https_class_https_clientrequest).\n\n### new http2.IncomingMessage(socket)\n\nSame as [`https.IncomingMessage`](https://nodejs.org/api/https.html#https_class_https_incomingmessage).\n\n### new http2.Agent(options)\n\n**Note:** this is **not** compatible with the classic `http.Agent`.\n\nUsage example:\n\n```js\nconst http2 = require('http2-wrapper');\n\nclass MyAgent extends http2.Agent {\n\tcreateConnection(origin, options) {\n\t\tconsole.log(`Connecting to ${http2.Agent.normalizeOrigin(origin)}`);\n\t\treturn http2.Agent.connect(origin, options);\n\t}\n}\n\nhttp2.get({\n\thostname: 'google.com',\n\tagent: new MyAgent()\n}, response =\u003e {\n\tresponse.on('data', chunk =\u003e console.log(`Received chunk of ${chunk.length} bytes`));\n});\n```\n\n#### options\n\nEach option is an `Agent` property and can be changed later.\n\n##### timeout\n\nType: `number`\u003cbr\u003e\nDefault: `0`\n\nIf there's no activity after `timeout` milliseconds, the session will be closed. If `0`, no timeout is applied.\n\n##### maxSessions\n\nType: `number`\u003cbr\u003e\nDefault: `Infinity`\n\nThe maximum amount of sessions in total.\n\n##### maxEmptySessions\n\nType: `number`\u003cbr\u003e\nDefault: `10`\n\nThe maximum amount of empty sessions in total. An empty session is a session with no pending requests.\n\n##### maxCachedTlsSessions\n\nType: `number`\u003cbr\u003e\nDefault: `100`\n\nThe maximum amount of cached TLS sessions.\n\n#### agent.protocol\n\nType: `string`\u003cbr\u003e\nDefault: `https:`\n\n#### agent.settings\n\nType: `object`\u003cbr\u003e\nDefault: `{enablePush: false}`\n\n[Settings](https://nodejs.org/api/http2.html#http2_settings_object) used by the current agent instance.\n\n#### agent.normalizeOptions([options](https://github.com/szmarczak/http2-wrapper/blob/master/source/agent.js))\n\nReturns a string representing normalized options.\n\n```js\nAgent.normalizeOptions({servername: 'example.com'});\n// =\u003e ':::::::::::::::::::::::::::::::::::::'\n```\n\n#### agent.getSession(origin, options)\n\n##### [origin](https://nodejs.org/api/http2.html#http2_http2_connect_authority_options_listener)\n\nType: `string` `URL` `object`\n\nOrigin used to create new session.\n\n##### [options](https://nodejs.org/api/http2.html#http2_http2_connect_authority_options_listener)\n\nType: `object`\n\nOptions used to create new session.\n\nReturns a Promise giving free `Http2Session`. If no free sessions are found, a new one is created.\n\nA session is considered free when pending streams count is less than max concurrent streams settings.\n\n#### agent.getSession([origin](#origin), [options](options-1), listener)\n\n##### listener\n\nType: `object`\n\n```\n{\n\treject: error =\u003e void,\n\tresolve: session =\u003e void\n}\n```\n\nIf the `listener` argument is present, the Promise will resolve immediately. It will use the `resolve` function to pass the session.\n\n#### agent.request([origin](#origin), [options](#options-1), [headers](https://nodejs.org/api/http2.html#http2_headers_object), [streamOptions](https://nodejs.org/api/http2.html#http2_clienthttp2session_request_headers_options))\n\nReturns a Promise giving `Http2Stream`.\n\n#### agent.createConnection([origin](#origin), [options](#options-1))\n\nReturns a new `TLSSocket`. It defaults to `Agent.connect(origin, options)`.\n\n#### agent.closeEmptySessions(count)\n\n##### count\n\nType: `number`\nDefault: `Number.POSITIVE_INFINITY`\n\nMakes an attempt to close empty sessions. Only sessions with 0 concurrent streams will be closed.\n\n#### agent.destroy(reason)\n\nDestroys **all** sessions.\n\n#### agent.emptySessionCount\n\nType: `number`\n\nA number of empty sessions.\n\n#### agent.pendingSessionCount\n\nType: `number`\n\nA number of pending sessions.\n\n#### agent.sessionCount\n\nType: `number`\n\nA number of all sessions held by the Agent.\n\n#### Event: 'session'\n\n```js\nagent.on('session', session =\u003e {\n\t// A new session has been created by the Agent.\n});\n```\n\n## Proxy support\n\nCurrently `http2-wrapper` provides support for these proxies:\n\n- `HttpOverHttp2`\n- `HttpsOverHttp2`\n- `Http2OverHttp2`\n- `Http2OverHttp`\n- `Http2OverHttps`\n\nAny of the above can be accessed via `http2wrapper.proxies`. Check out the [`examples/proxies`](examples/proxies) directory to learn more.\n\n**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:\n\n```js\nconst resolveAlpnProxy = new URL('https://username:password@localhost:8000');\nconst connect = async (options, callback) =\u003e new Promise((resolve, reject) =\u003e {\n\tconst host = `${options.host}:${options.port}`;\n\n\t(async () =\u003e {\n\t\ttry {\n\t\t\tconst request = await http2.auto(resolveAlpnProxy, {\n\t\t\t\tmethod: 'CONNECT',\n\t\t\t\theaders: {\n\t\t\t\t\thost\n\t\t\t\t},\n\t\t\t\tpath: host,\n\n\t\t\t\t// For demo purposes only!\n\t\t\t\trejectUnauthorized: false,\n\t\t\t});\n\n\t\t\trequest.end();\n\n\t\t\trequest.once('error', reject);\n\n\t\t\trequest.once('connect', (response, socket, head) =\u003e {\n\t\t\t\tif (head.length \u003e 0) {\n\t\t\t\t\treject(new Error(`Unexpected data before CONNECT tunnel: ${head.length} bytes`));\n\n\t\t\t\t\tsocket.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst tlsSocket = tls.connect({\n\t\t\t\t\t...options,\n\t\t\t\t\tsocket\n\t\t\t\t}, callback);\n\n\t\t\t\tresolve(tlsSocket);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treject(error);\n\t\t}\n\t})();\n});\n\n// This is required to prevent leaking real IP address on ALPN negotiation\nconst resolveProtocol = http2.auto.createResolveProtocol(new Map(), new Map(), connect);\n\nconst request = await http2.auto('https://httpbin.org/anything', {\n\tagent: {…},\n\tresolveProtocol\n}, response =\u003e {\n\t// Read the response here\n});\n\nrequest.end();\n```\n\nSee [`unknown-over-unknown.js`](examples/proxies/unknown-over-unknown.js) to learn more.\n\n## Mirroring another server\n\nSee [`examples/proxies/mirror.js`](examples/proxies/mirror.js) for an example.\n\n## [WebSockets over HTTP/2](https://tools.ietf.org/html/rfc8441)\n\nSee [`examples/ws`](examples/ws) for an example.\n\n## Push streams\n\nSee [`examples/push-stream`](examples/push-stream) for an example.\n\n## Related\n\n- [`got`](https://github.com/sindresorhus/got) - Simplified HTTP requests\n- [`http2-proxy`](https://github.com/nxtedition/node-http2-proxy) - A simple http/2 \u0026 http/1.1 spec compliant proxy helper for Node.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszmarczak%2Fhttp2-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszmarczak%2Fhttp2-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszmarczak%2Fhttp2-wrapper/lists"}