A simple http/2 & http/1.1 spec compliant proxy helper for Node.

Features

Installation

$ npm install http2-proxy

Notes

http2-proxy requires at least node v10.0.0.

Fully async/await compatible and all callback based usage is optional and discouraged.

During 503 it is safe to assume that nothing was read or written. This makes it safe to retry request (including non idempotent methods).

Use a final and/or error handler since errored responses won't be cleaned up automatically. This makes it possible to perform retries.

const finalhandler = require ( ' finalhandler ' ) const defaultWebHandler = ( err , req , res ) => { if ( err ) { console . error ( ' proxy error ' , err ) finalhandler ( req , res ) ( err ) } } const defaultWSHandler = ( err , req , socket , head ) => { if ( err ) { console . error ( ' proxy error ' , err ) socket . destroy ( ) } }

HTTP/1 API

You must pass allowHTTP1: true to the http2.createServer or http2.createSecureServer factory methods.

import http2 from ' http2 ' import proxy from ' http2-proxy ' const server = http2 . createServer ( { allowHTTP1 : true } ) server . listen ( 8000 )

You can also use http-proxy2 with the old http && https API's.

import http from ' http ' const server = http . createServer ( ) server . listen ( 8000 )

API

Proxy HTTP/2, HTTP/1 and WebSocket

server . on ( ' request ' , ( req , res ) => { proxy . web ( req , res , { hostname : ' localhost ' port : 9000 } , defaultWebHandler ) } ) server . on ( ' upgrade ' , ( req , socket , head ) => { proxy . ws ( req , socket , head , { hostname : ' localhost ' port : 9000 } , defaultWsHandler ) } )

const app = connect ( ) app . use ( helmet ( ) ) app . use ( ( req , res , next ) => proxy . web ( req , res , { hostname : ' localhost ' port : 9000 } , err => { if ( err ) { next ( err ) } } ) ) server . on ( ' request ' , app )

Add x-forwarded Headers

server . on ( ' request ' , ( req , res ) => { proxy . web ( req , res , { hostname : ' localhost ' port : 9000 , onReq : ( req , { headers } ) => { headers [ ' x-forwarded-for ' ] = req . socket . remoteAddress headers [ ' x-forwarded-proto ' ] = req . socket . encrypted ? ' https ' : ' http ' headers [ ' x-forwarded-host ' ] = req . headers [ ' host ' ] } } , defaultWebHandler ) } )

Follow Redirects

const http = require ( ' follow-redirects ' ) . http server . on ( ' request ' , ( req , res ) => { proxy . web ( req , res , { hostname : ' localhost ' port : 9000 , onReq : ( req , options ) => http . request ( options ) } , defaultWebHandler ) } )

Add Response Header

server . on ( ' request ' , ( req , res ) => { proxy . web ( req , res , { hostname : ' localhost ' port : 9000 , onReq : ( req , options ) => http . request ( options ) , onRes : ( req , res , proxyRes ) => { res . setHeader ( ' x-powered-by ' , ' http2-proxy ' ) res . writeHead ( proxyRes . statusCode , proxyRes . headers ) proxyRes . pipe ( res ) } } , defaultWebHandler ) } )

Proxy HTTP2

HTTP proxying can be achieved using http2 client compat libraries such as:

https://github.com/hisco/http2-client https://github.com/spdy-http2/node-spdy https://github.com/grantila/fetch-h2 https://github.com/szmarczak/http2-wrapper

const http = require ( ' http2-wrapper ' ) server . on ( ' request ' , ( req , res ) => { proxy . web ( req , res , { hostname : ' localhost ' port : 9000 , onReq : ( req , options ) => http . request ( options ) } , defaultWebHandler ) } )

Try Multiple Upstream Servers (Advanced)

const http = require ( ' http ' ) const proxy = require ( ' http2-proxy ' ) const createError = require ( ' http-errors ' ) server . on ( ' request ' , async ( req , res ) => { try { res . statusCode = null for await ( const { port , timeout , hostname } of upstream ) { if ( req . aborted || res . readableEnded ) { return } let error = null let bytesWritten = 0 try { return await proxy . web ( req , res , { port , timeout , hostname , onRes : async ( req , res , proxyRes ) => { if ( proxyRes . statusCode >= 500 ) { throw createError ( proxyRes . statusCode , proxyRes . message ) } function setHeaders ( ) { if ( ! bytesWritten ) { res . statusCode = proxyRes . statusCode for ( const [ key , value ] of Object . entries ( headers ) ) { res . setHeader ( key , value ) } } } proxyRes . on ( ' data ' , buf => { setHeaders ( ) bytesWritten += buf . length if ( ! res . write ( buf ) ) { proxyRes . pause ( ) } } ) . on ( ' end ' , ( ) => { setHeaders ( ) res . addTrailers ( proxyRes . trailers ) res . end ( ) } ) . on ( ' close ' , ( ) => { res . off ( ' drain ' , onDrain ) } ) ) res . on ( ' drain ' , onDrain ) function onDrain ( ) { proxyRes . resume ( ) } } } ) } catch ( err ) { if ( ! err . statusCode ) { throw err } error = err if ( err . statusCode === 503 ) { continue } if ( req . method === ' HEAD ' || req . method === ' GET ' ) { if ( ! bytesWritten ) { continue } } throw err } } throw error || new createError . ServiceUnavailable ( ) } catch ( err ) { defaultWebHandler ( err ) } }

[async] web (req, res, options[, callback])

req : http.IncomingMessage or http2.Http2ServerRequest .

: or . res : http.ServerResponse or http2.Http2ServerResponse .

: or . options : See Options

: See Options callback(err, req, res) : Called on completion or error.

See request

[async] ws (req, socket, head, options[, callback])

req : http.IncomingMessage .

: . socket : net.Socket .

: . head : Buffer .

: . options : See Options.

: See Options. callback(err, req, socket, head) : Called on completion or error.

See upgrade

options

hostname : Proxy http.request(options) target hostname.

: Proxy target hostname. port : Proxy http.request(options) target port.

: Proxy target port. protocol : Agent protocol ( 'http' or 'https' ). Defaults to 'http' .

: Agent protocol ( or ). Defaults to . path : Target pathname. Defaults to req.originalUrl || req.url .

: Target pathname. Defaults to . proxyTimeout : Proxy http.request(options) timeout.

: Proxy timeout. proxyName : Proxy name used for Via header.

: Proxy name used for header. [async] onReq(req, options[, callback]) : Called before proxy request. If returning a truthy value it will be used as the request. req : http.IncomingMessage or http2.Http2ServerRequest options : Options passed to http.request(options) . callback(err) : Called on completion or error.

: Called before proxy request. If returning a truthy value it will be used as the request. [async] onRes(req, resOrSocket, proxyRes[, callback]) : Called on proxy response. Writing of response must be done inside this method if provided. req : http.IncomingMessage or http2.Http2ServerRequest . resOrSocket : For web http.ServerResponse or http2.Http2ServerResponse and for ws net.Socket . proxyRes : http.ServerResponse . callback(err) : Called on completion or error.

: Called on proxy response. Writing of response must be done inside this method if provided.

License

MIT