h2o2 Proxy Handler
repository·master·Indexed 41 days ago
https://github.com/hapijs/h2o2A proxy handler plugin for hapi.js (version 10.0.4) that enables proxying capabilities for web applications. It allows routing requests to upstream endpoints using host, port, and protocol, absolute URIs with template variables, or dynamic mapping via mapUri and onResponse. While designed for the hapi ecosystem, it is a standalone module compatible with other web frameworks.
What's inside h2o2
- @hapi/h2o2 is a proxy handler designed for the hapi.js web framework. While it is part of the hapi ecosystem and integrates seamlessly with hapi and its other components, it is a standalone module that can also be used with other web frameworks.
Use template values in the uri option
masterWhen using the
urioption, you can inject values from the incoming request using the following template variables:{protocol}{host}{port}{path}{query}
You can also capture
request.paramsandqueryvalues by using a similar replacement strategy. Note that the default variables listed above take precedence; avoid using them as names for your ownrequest.params.// Example using default template values server.route({ method: 'GET', path: '/foo', handler: { proxy: { uri: '{protocol}://{host}:{port}/go/to/{path}' } } }); // Example using request.params server.route({ method: 'GET', path: '/foo/{bar}', handler: { proxy: { uri: 'https://some.upstream.service.com/some/path/to/{bar}{query}' } } });Install and register @hapi/h2o2
masterTo add proxying functionality to your hapi server, register the
@hapi/h2o2plugin. This allows you to use theproxyhandler in your route configurations.const Hapi = require('@hapi/hapi'); const H2o2 = require('@hapi/h2o2'); const start = async function() { const server = Hapi.server(); await server.register(H2o2); await server.start(); console.log(`Server started at: ${server.info.uri}`); }; start();Configure URI mapping with templates
masterWhen defining the
urioption, you can use placeholders that h2o2 will automatically replace using the current request's information:{protocol}: The current server's protocol.{host}: The current server's host.{port}: The current server's port.{path}: The current request's path.{query}: The current request's query string.{key}: Any parameter defined in the route (e.g.,{id}).
Example:
uri: 'https://api.example.com/{path}'will map/userstohttps://api.example.com/users.// If the route is /user/{id} // And the request is GET /user/123?name=foo // The following uri will map to: https://upstream.com/api/123?name=foo h.proxy({ uri: 'https://upstream.com/api/{id}{query}' });Register the @hapi/h2o2 plugin
masterTo use h2o2 as a proxy in your hapi server, register the plugin. You can provide global configuration options during registration which will be applied to all proxy requests unless overridden at the route level.
Global options include:
host,port,protocol,uri: Define the upstream target.passThrough: Iftrue, headers from the downstream request are passed to the upstream (excludinghostandcontent-length).localStatePassThrough: Iftrue, allows cookies defined by the upstream server to be passed back to the client.redirects: Number of redirects to follow (orfalse).timeout: Request timeout in milliseconds (default is 3 minutes).maxSockets: Limit on the number of sockets per agent.xforward: Iftrue, addsx-forwarded-*headers to the upstream request.ttl: If set to'upstream', the proxy will use themax-agefrom the upstream'sCache-Controlheader to set the response TTL.
const Hapi = require('@hapi/hapi'); const H2o2 = require('@hapi/h2o2'); const init = async () => { const server = Hapi.server(); await server.register({ plugin: H2o2, options: { host: 'upstream-server.com', protocol: 'https' } }); await server.start(); };Use a custom HTTP client
masterBy default,
h2o2useswreck. You can provide a custom client by passing an object to thehttpClientoption. The client must implement thewreckinterface, specifically therequest()andparseCacheControl()methods.server.route({ method: 'GET', path: '/', handler: { proxy: { httpClient: { request(method, uri, options) { return axios({ method, url: 'https://some.upstream.service.com/' }) } } } } });Proxy to an absolute URI
masterUse the
urioption to send requests to a specific absolute URI. This overrides the incoming host, port, protocol, path, and query. This configuration cannot be used simultaneously withhost,port,protocol, ormapUri.server.route({ method: 'GET', path: '/', handler: { proxy: { uri: 'https://some.upstream.service.com/that/has?what=you&want=todo' } } });Configure proxy using host, port, and protocol
masterYou can direct requests to a specific upstream service by providing the
host,port, andprotocol(either'http'or'https'). The request will be sent to the upstream service using the same path as the original client request. This configuration cannot be used simultaneously withuriormapUri.server.route({ method: 'GET', path: '/', handler: { proxy: { host: '10.33.33.1', port: '443', protocol: 'https' } } });Map URIs and process responses with mapUri and onResponse
masterFor advanced proxying, use
mapUriandonResponsetogether. This combination allows you to dynamically determine the upstream URI and manipulate the response before it reaches the client. This cannot be used withhost,port,protocol, oruri.mapUri
Signature:
function (request)Returns an object:uri: The absolute proxy URI.headers: (Optional) An object of HTTP request headers to send.
onResponse
Signature:
function (err, res, request, h, settings, ttl)err: Internal or upstream error.res: The Node response object (a readable stream). You are responsible for closing this stream.request: The incoming request object.h: The response toolkit.settings: The proxy handler configuration.ttl: The upstream TTL in milliseconds (ifttlis set to'upstream').
server.route({ method: 'GET', path: '/', handler: { proxy: { mapUri: function (request) { console.log('doing some additional stuff before redirecting'); return { uri: 'https://some.upstream.service.com/' }; }, onResponse: async function (err, res, request, h, settings, ttl) { console.log('receiving the response from the upstream.'); const payload = await Wreck.read(res, { json: true }); console.log('some payload manipulation if you want to.'); const response = h.response(payload); response.headers = res.headers; return response; } } } });Proxy requests using h.proxy(options)
masterYou can proxy a request to an upstream endpoint within a route handler using
h.proxy(options). Theoptionsobject accepts the same keys as the route configurationproxyhandler. Note that response flow control rules do not apply when using this method.const handler = function (request, h) { return h.proxy({ host: 'example.com', port: 80, protocol: 'http' }); };Configure proxy handler options
masterThe following options can be provided when registering the plugin or via
h.proxy(options)to configure the proxy behavior:Option Description hostUpstream service host. Path is appended from client request. portUpstream service port. protocol'http'or'https'.uriAbsolute URI. Cannot be used with host,port,protocol, ormapUri.httpClientAn HTTP client abiding by the Wreck interface. Defaults to wreck.passThroughIf true, forwards client headers to upstream and upstream headers to client. Defaults tofalse.localStatePassThroughIf false, removes locally defined state from incoming requests. Defaults tofalse.acceptEncodingIf false, does not pass-through 'Accept-Encoding' header. Useful foronResponseprocessing. Defaults totrue.rejectUnauthorizedSets rejectUnauthorizedon the HTTPS agent. Defaults totrue.xforwardIf true, setsX-Forwarded-For,X-Forwarded-Port,X-Forwarded-Proto, andX-Forwarded-Hostheaders. Defaults tofalse.redirectsMax number of HTTP redirections to follow. falseor0disables them. Defaults tofalse.timeoutMilliseconds before aborting upstream request. Defaults to 180000.mapUriFunction (request) => { uri, headers }to map request to proxy URI.onRequestCustom function (req)passed the Wreck request.onResponseCustom function (err, res, request, h, settings, ttl)for response processing.ttlIf 'upstream', applies upstream caching policy.agentNode http(s) agent for connections. maxSocketsMax sockets per outgoing host. Defaults to Infinity.secureProtocolTLS flag for SSL method (e.g. SSLv3_method).ciphersTLS ciphers list to override defaults. downstreamResponseTimeIf true, logs time spent processing downstream request. Defaults tofalse.Handle upstream responses with onResponse
masterYou can provide an
onResponsecallback in your proxy options to intercept the upstream response. This is useful for custom error handling or transforming the response before it is sent to the downstream client.The callback receives:
(err, res, request, h, settings, ttl).err: The error object if the request failed.res: The upstream response object.request: The downstream request object.h: The hapi toolkit.settings: The effective proxy settings.ttl: The calculated TTL (ifttl: 'upstream'was used).
h.proxy({ uri: 'https://api.example.com', onResponse: (err, res, request, h, settings, ttl) => { if (err) { return h.response({ error: 'Upstream unavailable' }).code(502); } // Custom logic for successful responses return h.response(res).ttl(ttl); } });