Socket
Socket
Sign inDemoInstall

http2-proxy

Package Overview
Dependencies
Maintainers
1
Versions
193
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

http2-proxy - npm Package Compare versions

Comparing version 0.2.20 to 1.0.0

321

index.js
const http2 = require('http2')
const http = require('http')
const net = require('net')
const assert = require('assert')

@@ -26,10 +27,18 @@ const {

ws (req, socket, head, options, callback) {
impl(req, socket, head, options, callback)
proxy(req, socket, head, options, callback)
},
web (req, res, options, callback) {
impl(req, res, null, options, callback)
proxy(req, res, null, options, callback)
}
}
function impl (req, resOrSocket, headOrNil, {
const kReq = Symbol('req')
const kRes = Symbol('res')
const kProxyCallback = Symbol('callback')
const kProxyReq = Symbol('proxyReq')
const kProxyRes = Symbol('proxyRes')
const kProxySocket = Symbol('proxySocket')
const kOnProxyRes = Symbol('onProxyRes')
function proxy (req, res, head, {
hostname,

@@ -43,48 +52,28 @@ port,

}, callback) {
let hasError = false
let proxyReq
req[kRes] = res
function onError (err, statusCode = err.statusCode || 500) {
if (hasError) {
return
}
res[kReq] = req
res[kRes] = res
res[kProxyCallback] = callback
res[kProxyReq] = null
res[kProxyRes] = null
res[kProxySocket] = null
if (proxyReq && !proxyReq.aborted) {
proxyReq.abort()
}
assert(typeof callback === 'function' || callback == null)
hasError = true
let promise
if (!err.code) {
err.code = resOrSocket.code
}
if (resOrSocket.closed === true ||
resOrSocket.headersSent !== false ||
!resOrSocket.writeHead
) {
resOrSocket.destroy()
} else {
resOrSocket.writeHead(statusCode)
resOrSocket.end()
}
if (callback) {
callback(err, req, resOrSocket)
} else {
throw err
}
if (!callback) {
promise = new Promise((resolve, reject) => {
res[kProxyCallback] = err => err ? reject(err) : resolve()
})
}
(req.stream || req).on('error', onError)
resOrSocket.on('error', onError)
if (resOrSocket instanceof net.Socket) {
if (res instanceof net.Socket) {
if (req.method !== 'GET') {
return onError(createError('method not allowed', null, 405))
return onFinish.call(res, createError('method not allowed', null, 405))
}
if (!req.headers[HTTP2_HEADER_UPGRADE] ||
req.headers[HTTP2_HEADER_UPGRADE].toLowerCase() !== 'websocket') {
return onError(createError('bad request', null, 400))
if (sanitize(req.headers[HTTP2_HEADER_UPGRADE]) !== 'websocket') {
return onFinish.call(res, createError('bad request', null, 400))
}

@@ -94,3 +83,3 @@ }

if (req.httpVersion !== '1.1' && req.httpVersion !== '2.0') {
return onError(createError('http version not supported', null, 505))
return onFinish.call(res, createError('http version not supported', null, 505))
}

@@ -101,3 +90,3 @@

if (sanitize(name).endsWith(proxyName.toLowerCase())) {
return onError(createError('loop detected', null, 508))
return onFinish.call(res, createError('loop detected', null, 508))
}

@@ -108,11 +97,11 @@ }

if (timeout) {
req.setTimeout(timeout, () => onError(createError('request timeout', null, 408)))
req.setTimeout(timeout, onRequestTimeout)
}
if (resOrSocket instanceof net.Socket) {
if (headOrNil && headOrNil.length) {
resOrSocket.unshift(headOrNil)
if (res instanceof net.Socket) {
if (head && head.length) {
res.unshift(head)
}
setupSocket(resOrSocket)
setupSocket(res)
}

@@ -143,111 +132,176 @@

proxyReq = http.request(options)
const proxyReq = http.request(options)
const onProxyError = err => {
if (proxyReq.aborted) {
return
}
proxyReq.abort()
proxyReq[kReq] = req
proxyReq[kRes] = res
proxyReq[kOnProxyRes] = onRes
if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
err.statusCode = 503
} else if (/HPE_INVALID/.test(err.code)) {
err.statusCode = 502
} else if (err.code === 'ECONNRESET') {
err.statusCode = 502
} else {
err.statusCode = 500
}
res[kProxyReq] = proxyReq
onError(err)
}
function onFinish () {
if (!proxyReq.aborted) {
proxyReq.abort()
}
}
resOrSocket
res
.on('finish', onFinish)
.on('close', onFinish)
.on('error', onFinish)
.on('close', onFinish)
req
.on('aborted', onFinish)
.on('close', onFinish)
.on('error', onFinish)
.on('close', onFinish)
.pipe(proxyReq)
.on('error', onProxyError)
.on('error', onFinish)
// NOTE http.ClientRequest emits "socket hang up" error when aborted
// before having received a response, i.e. there is no need to listen for
// proxyReq.on('aborted', ...).
.on('timeout', () => onProxyError(createError('gateway timeout', null, 504)))
.on('response', proxyRes => {
proxyRes.on('aborted', () => onProxyError(createError('socket hang up', 'ECONNRESET', 502)))
.on('timeout', onProxyTimeout)
.on('response', onProxyResponse)
.on('upgrade', onProxyUpgrade)
if (resOrSocket instanceof net.Socket) {
if (onRes) {
onRes(req, resOrSocket)
}
return promise
}
if (!proxyRes.upgrade) {
resOrSocket.end()
}
} else {
setupHeaders(proxyRes.headers)
function onFinish (err, statusCode) {
const res = this[kRes]
resOrSocket.statusCode = proxyRes.statusCode
for (const key of Object.keys(proxyRes.headers)) {
resOrSocket.setHeader(key, proxyRes.headers[key])
}
assert(res)
if (onRes) {
onRes(req, resOrSocket)
}
if (!res[kProxyCallback]) {
return
}
resOrSocket.writeHead(resOrSocket.statusCode)
proxyRes
.on('end', () => resOrSocket.addTrailers(proxyRes.trailers))
.on('error', onProxyError)
.pipe(resOrSocket)
}
})
if (res[kProxyReq]) {
res[kProxyReq].abort()
res[kProxyReq] = null
}
if (resOrSocket instanceof net.Socket) {
proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => {
setupSocket(proxySocket)
if (res[kProxySocket]) {
res[kProxySocket].end()
res[kProxySocket] = null
}
if (proxyHead && proxyHead.length) {
proxySocket.unshift(proxyHead)
}
if (res[kProxyRes]) {
res[kProxyRes].destroy()
res[kProxyRes] = null
}
let head = 'HTTP/1.1 101 Switching Protocols'
if (err) {
err.statusCode = statusCode || err.statusCode || 500
err.code = err.code || res.code
for (const key of Object.keys(proxyRes.headers)) {
const value = proxyRes.headers[key]
if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
err.statusCode = 503
} else if (/HPE_INVALID/.test(err.code)) {
err.statusCode = 502
}
}
if (!Array.isArray(value)) {
head += '\r\n' + key + ': ' + value
} else {
for (let i = 0; i < value.length; i++) {
head += '\r\n' + key + ': ' + value[i]
}
}
}
if (res.headersSent !== false) {
res.destroy()
} else {
res.writeHead(statusCode)
res.end()
}
head += '\r\n\r\n'
res[kProxyCallback].call(null, err, res[kReq], res)
res[kProxyCallback] = null
}
resOrSocket.write(head)
function onRequestTimeout () {
onFinish.call(this, createError('request timeout', null, 408))
}
proxyRes.on('error', onProxyError)
function onProxyTimeout () {
onFinish.call(this, createError('gateway timeout', null, 504))
}
proxySocket
.on('error', onProxyError)
.pipe(resOrSocket)
.pipe(proxySocket)
})
function onProxyResponse (proxyRes) {
if (this.aborted) {
return
}
const res = this[kRes]
res[kProxyRes] = proxyRes
proxyRes[kRes] = res
proxyRes.on('aborted', onProxyAborted)
if (res instanceof net.Socket) {
if (!proxyRes.upgrade) {
res.end()
}
} else {
setupHeaders(proxyRes.headers)
res.statusCode = proxyRes.statusCode
for (const key of Object.keys(proxyRes.headers)) {
res.setHeader(key, proxyRes.headers[key])
}
if (this[kOnProxyRes]) {
this[kOnProxyRes](this[kReq], res)
}
res.writeHead(res.statusCode)
proxyRes
.on('end', onProxyTrailers)
.on('error', onFinish)
.pipe(res)
}
}
function onProxyTrailers () {
this[kRes].addTrailers(this.trailers)
}
function onProxyAborted () {
onFinish.call(this, createError('socket hang up', 'ECONNRESET', 502))
}
function onProxyUpgrade (proxyRes, proxySocket, proxyHead) {
if (this.aborted) {
return
}
const res = this[kRes]
res[kProxySocket] = proxySocket
res[kProxyRes] = proxyRes
proxyRes[kRes] = res
proxySocket[kRes] = res
setupSocket(proxySocket)
if (proxyHead && proxyHead.length) {
proxySocket.unshift(proxyHead)
}
let head = 'HTTP/1.1 101 Switching Protocols'
for (const key of Object.keys(proxyRes.headers)) {
const value = proxyRes.headers[key]
if (!Array.isArray(value)) {
head += '\r\n' + key + ': ' + value
} else {
for (let i = 0; i < value.length; i++) {
head += '\r\n' + key + ': ' + value[i]
}
}
}
head += '\r\n\r\n'
this[kRes].write(head)
proxyRes
.on('error', onFinish)
proxySocket
.on('error', onFinish)
.pipe(this[kRes])
.pipe(proxySocket)
}
function getRequestHeaders (req) {

@@ -260,7 +314,9 @@ const host = req.headers[HTTP2_HEADER_AUTHORITY] || req.headers[HTTP2_HEADER_HOST]

// Remove pseudo headers
delete headers[HTTP2_HEADER_AUTHORITY]
delete headers[HTTP2_HEADER_METHOD]
delete headers[HTTP2_HEADER_PATH]
delete headers[HTTP2_HEADER_SCHEME]
if (req.httpVersionMajor === 2) {
// Remove pseudo headers
delete headers[HTTP2_HEADER_AUTHORITY]
delete headers[HTTP2_HEADER_METHOD]
delete headers[HTTP2_HEADER_PATH]
delete headers[HTTP2_HEADER_SCHEME]
}

@@ -306,6 +362,7 @@ if (upgrade) {

for (const name of connection.split(',')) {
delete headers[sanitize(name)]
delete headers[name.trim()]
}
}
// Remove hop by hop headers
delete headers[HTTP2_HEADER_CONNECTION]

@@ -312,0 +369,0 @@ delete headers[HTTP2_HEADER_KEEP_ALIVE]

{
"name": "http2-proxy",
"version": "0.2.20",
"version": "1.0.0",
"scripts": {

@@ -5,0 +5,0 @@ "dev": "nodemon --inspect=9308 --expose-http2 src",

@@ -8,4 +8,4 @@ # node-http2-proxy

- Proxies HTTP 2, HTTP 1.1 and WebSocket
- Simple and easy to follow implementation
- [Hop by hop header handling](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
- Simple and high performance
- [Hop by hop header handling](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).
- [Connection header handling](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection)

@@ -44,3 +44,7 @@ - [Via header handling](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Via)

port: 9000
}, err => console.error(err, 'proxy error'))
}, err => {
if (err) {
console.error('proxy error', err)
}
})
})

@@ -51,3 +55,7 @@ server.on('upgrade', (req, socket, head) => {

port: 9000
}, err => console.error('proxy error', err))
}, err => {
if (err) {
console.error('proxy error', err)
}
})
})

@@ -64,3 +72,7 @@ ```

onRes: (req, res) => helmet(req, res, () => {})
}, err => console.error('proxy error', err))
}, err => {
if (err) {
console.error('proxy error', err)
}
})
})

@@ -81,3 +93,7 @@ ```

}
}, err => console.error(err, 'proxy error'))
}, err => {
if (err) {
console.error('proxy error', err)
}
})
})

@@ -88,19 +104,23 @@ ```

#### web (req, res, options, onProxyError)
#### web (req, res, options, [callback])
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest).
- `res`: [`http.ServerResponse`](https://nodejs.org/api/http.html#http_http_request_options_callback) or [`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverresponse).
- `options`: See [Options](#options).
- `onProxyError(err)`: Called on error.
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest)
- `res`: [`http.ServerResponse`](https://nodejs.org/api/http.html#http_http_request_options_callback) or [`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverresponse)
- `options`: See [Options](#options)
- `callback(err)`: Called on completion or error. Optional
Returns a promise if no callback is provided.
See [`request`](https://nodejs.org/api/http.html#http_event_request)
#### ws (req, socket, head, options, onProxyError)
#### ws (req, socket, head, options, [callback])
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage).
- `socket`: [`net.Socket`](https://nodejs.org/api/net.html#net_class_net_socket).
- `head`: [`Buffer`](https://nodejs.org/api/buffer.html#buffer_class_buffer).
- `options`: See [Options](#options).
- `onProxyError(err)`: Called on error.
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
- `socket`: [`net.Socket`](https://nodejs.org/api/net.html#net_class_net_socket)
- `head`: [`Buffer`](https://nodejs.org/api/buffer.html#buffer_class_buffer)
- `options`: See [Options](#options)
- `callback(err)`: Called on completion or error. Optional
Returns a promise if no callback is provided.
See [`upgrade`](https://nodejs.org/api/http.html#http_event_upgrade)

@@ -110,13 +130,13 @@

- `hostname`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) target hostname.
- `port`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) target port.
- `proxyTimeout`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) timeout.
- `proxyName`: Proxy name used for **Via** header.
- `hostname`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) target hostname
- `port`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) target port
- `proxyTimeout`: Proxy [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback) timeout
- `proxyName`: Proxy name used for **Via** header
- `timeout`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest) timeout
- `onReq(req, options)`: Called before proxy request.
- `onReq(req, options)`: Called before proxy request
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest)
- `options`: Options passed to [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback).
- `onRes(req, resOrSocket)`: Called before proxy response.
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest).
- `resOrSocket`: For `web` [`http.ServerResponse`](https://nodejs.org/api/http.html#http_http_request_options_callback) or [`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverresponse) and for `ws` [`net.Socket`](https://nodejs.org/api/net.html#net_class_net_socket).
- `options`: Options passed to [`http.request(options)`](https://nodejs.org/api/http.html#http_http_request_options_callback)
- `onRes(req, resOrSocket)`: Called before proxy response
- `req`: [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) or [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverrequest)
- `resOrSocket`: For `web` [`http.ServerResponse`](https://nodejs.org/api/http.html#http_http_request_options_callback) or [`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#http2_class_http2_http2serverresponse) and for `ws` [`net.Socket`](https://nodejs.org/api/net.html#net_class_net_socket)

@@ -123,0 +143,0 @@ ### License

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