From d2cc86f54a6f7cbc045174eec19cc89cb6873a09 Mon Sep 17 00:00:00 2001 From: Garth Kidd Date: Tue, 18 Dec 2018 12:24:57 +1100 Subject: [PATCH] h2o2: update to hapi@17 compatible h2o2@8 --- types/h2o2/h2o2-tests.ts | 240 ++++++++++++++++++--------------------- types/h2o2/index.d.ts | 73 ++++++------ types/h2o2/tsconfig.json | 17 +-- 3 files changed, 152 insertions(+), 178 deletions(-) diff --git a/types/h2o2/h2o2-tests.ts b/types/h2o2/h2o2-tests.ts index 48b05b8aa3..9d0a590fa8 100644 --- a/types/h2o2/h2o2-tests.ts +++ b/types/h2o2/h2o2-tests.ts @@ -1,106 +1,114 @@ -import http = require("http"); -import * as Boom from 'boom'; -import * as Hapi from 'hapi'; -import * as Wreck from 'wreck'; +import wreck = require('wreck'); import h2o2 = require('h2o2'); +import hapi = require('hapi'); -// https://github.com/hapijs/h2o2#manual-loading +async function main() { + const server = new hapi.Server({}); + await server.register(h2o2); -const server = new Hapi.Server({}); - -server.register({ - register: h2o2 -}, function (err) { - - if (err) { - console.log('Failed to load h2o2'); - } - - server.start(function (err) { - - console.log('Server started at: ' + server.info!.uri); + server.route({ + method: 'GET', + path: '/hproxyoptions', + async handler(request, h) { + // ResponseToolkit augmentation + // https://github.com/hapijs/h2o2#hproxyoptions + return h.proxy({ host: 'example.com', port: 80, protocol: 'http' }); + } }); -}); -// https://github.com/hapijs/h2o2#replyproxyoptions - -const handler: Hapi.RouteHandler = function (request, reply) { - - return reply.proxy({ host: 'example.com', port: 80, protocol: 'http' }); -}; - -// https://github.com/hapijs/h2o2#using-the-host-port-protocol-options - -server.route({ - method: 'GET', - path: '/', - handler: { - proxy: { - host: '10.33.33.1', - port: '443', - protocol: 'https' - } - } -}); - -// https://github.com/hapijs/h2o2#using-the-uri-option - -server.route({ - method: 'GET', - path: '/', - handler: { - proxy: { - uri: 'https://some.upstream.service.com/that/has?what=you&want=todo' - } - } -}); - -// https://github.com/hapijs/h2o2#custom-uri-template-values - -server.route({ - method: 'GET', - path: '/foo', - handler: { - proxy: { - uri: '{protocol}://{host}:{port}/go/to/{path}' - } - } -}); - -server.route({ - method: 'GET', - path: '/foo/{bar}', - handler: { - proxy: { - uri: 'https://some.upstream.service.com/some/path/to/{bar}' - } - } -}); - -// https://github.com/hapijs/h2o2#using-the-mapuri-and-onresponse-options - -server.route({ - method: 'GET', - path: '/', - handler: { - proxy: { - mapUri: function (request, callback) { - - console.log('doing some aditional stuff before redirecting'); - callback(null, 'https://some.upstream.service.com/'); - }, - onResponse: function (err, res, request, reply, settings, ttl) { - - console.log('receiving the response from the upstream.'); - Wreck.read(res, { json: true }, function (err, payload) { - - console.log('some payload manipulation if you want to.') - reply(payload).headers = res.headers as any; - }); + server.route({ + method: 'GET', + path: '/using-the-host-port-protocol-options', + handler: { + // HandlerDecorations augmentation + // https://github.com/hapijs/h2o2#using-the-host-port-protocol-options + proxy: { + host: '10.33.33.1', + port: '443', + protocol: 'https' } } - } -}); + }); + + server.route({ + method: 'GET', + path: '/using-the-uri-option', + handler: { + // HandlerDecorations augmentation + // https://github.com/hapijs/h2o2#using-the-uri-option + proxy: { + uri: 'https://some.upstream.service.com/that/has?what=you&want=todo' + } + } + }); + + + server.route({ + method: 'GET', + path: '/custom-uri-template-values', + handler: { + // HandlerDecorations augmentation + // https://github.com/hapijs/h2o2#custom-uri-template-values + proxy: { + uri: '{protocol}://{host}:{port}/go/to/{path}' + } + } + }); + + server.route({ + method: 'GET', + path: '/custom-uri-template-values/{bar}', + handler: { + // HandlerDecorations augmentation + // https://github.com/hapijs/h2o2#custom-uri-template-values + proxy: { + uri: 'https://some.upstream.service.com/some/path/to/{bar}' + } + } + }); + + server.route({ + method: 'GET', + path: '/', + handler: { + // HandlerDecorations augmentation + // https://github.com/hapijs/h2o2#using-the-mapuri-and-onresponse-options + proxy: { + async mapUri(request) { + console.log('doing some additional stuff before redirecting'); + return { + uri: 'https://some.upstream.service.com/' + }; + }, + + async onResponse(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.') + let response = h.response(payload); + + // TODO find a quicker way to do this + for (let header in res.headers) { + let value = res.headers[header]; + if (value) { + if (typeof value === 'string') { + value = [value]; + } + for (let v of value) { + response = response.header(header, v); + } + } + } + return response; + } + } + } + }); + + await server.start(); + await server.stop(); +} /** * test code added in additional to code in docs. Demonstrates that for the moment @@ -119,7 +127,7 @@ var proxyOptions: h2o2.ProxyHandlerOptions = { protocol: 'https' // errors correctly if misspelt } -server.route({ +const badProtocolDemo: hapi.ServerRoute = { method: 'GET', path: '/', handler: { @@ -127,39 +135,11 @@ server.route({ host: '10.33.33.1', port: '443', BAD_protocol: 'https' // TODO change typings to fix this / submit bug report + // port: null // detected as incompatible } } -}) +}; -server.route({ - method: 'GET', - path: '/', - handler: { - proxy: { - uri: 'https://some.upstream.service.com/that/has?what=you&want=todo' - } - } -}) - -server.route({ - method: 'GET', - path: '/', - handler: { - proxy: { - mapUri: function (request: Hapi.Request, callback: (err: null | Boom.BoomError, value: string) => void) { - - console.log('doing some aditional stuff before redirecting'); - callback(null, 'https://some.upstream.service.com/'); - }, - onResponse: function (err: null | Boom.BoomError, res: http.IncomingMessage, request: Hapi.Request, reply: Hapi.ReplyWithContinue, settings: h2o2.ProxyHandlerOptions, ttl: number) { - - console.log('receiving the response from the upstream.'); - Wreck.read(res, { json: true }, function (err: null | Boom.BoomError, payload: any) { - - console.log('some payload manipulation if you want to.') - reply(payload).headers = res.headers as any; - }); - } - } - } -}); +if (!module.parent) { + main().then(() => console.log('done'), err => console.error(err.stack)); +} diff --git a/types/h2o2/index.d.ts b/types/h2o2/index.d.ts index 3e47a084e8..b6e5145eb6 100644 --- a/types/h2o2/index.d.ts +++ b/types/h2o2/index.d.ts @@ -1,16 +1,33 @@ -// Type definitions for h2o2 5.4 -// Project: https://github.com/hapijs/catbox +// Type definitions for h2o2 8.1 +// Project: https://github.com/hapijs/h2o2 // Definitions by: Jason Swearingen , AJP // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 /// -import http = require("http"); -import * as hapi from 'hapi'; -import * as Boom from 'boom'; +import http = require('http'); +import Boom = require('boom'); + +import { + Plugin, + Request, + ResponseObject, + ResponseToolkit, + Lifecycle, + RouteOptions, +} from 'hapi'; + +declare namespace h2o2 { + /** `mapURI` return value */ + export interface ProxyTarget { + /** The URI to request */ + uri: string; + + /** The headers with which to request `uri` */ + headers?: { [key: string]: string }; + } -declare namespace H2o2 { /** Proxy handler options */ export interface ProxyHandlerOptions { /** host - upstream service host to proxy requests to. It will have the same path as the client request. */ @@ -35,29 +52,26 @@ declare namespace H2o2 { redirects?: number | false; /** timeout - number of milliseconds before aborting the upstream request. Defaults to 180000 (3 minutes). */ timeout?: number; - /** mapUri - a function used to map the request URI to the proxied URI. Cannot be used together with host, port, protocol, or uri. The function signature is function (request, callback) where: + /** mapUri - a function used to map the request URI to the target `uri` and optional `headers` with which to make that request. Cannot be used together with `host`, `port`, `protocol`, or `uri`. * @param request - is the incoming request object. - * @param callback - is function (err, uri, headers) where: - * @param err - internal error condition. TODO: check this is of type BoomError or just Error. - * @param uri - the absolute proxy URI. - * @param headers - optional object where each key is an HTTP request header and the value is the header content. */ - mapUri?: (request: hapi.Request, callback: (err: null | Boom.BoomError, uri: string, headers?: { [key: string]: string }) => void) => void; + mapUri?: (this: ProxyHandlerOptions, request: Request) => Promise; /** - * onResponse - a custom function for processing the response from the upstream service before sending to the client. Useful for custom error handling of responses from the proxied endpoint or other payload manipulation. Function signature is function (err, res, request, reply, settings, ttl) where: + * onResponse - a custom function for processing the response from the upstream service before sending to the client. Useful for custom error handling of responses from the proxied endpoint or other payload manipulation. * @param err - internal or upstream error returned from attempting to contact the upstream proxy. TODO: check this is of type BoomError or just Error. * @param res - the node response object received from the upstream service. res is a readable stream (use the wreck module read method to easily convert it to a Buffer or string). * @param request - is the incoming request object. - * @param reply - the reply interface function. + * @param h - Hapi's response toolkit. * @param settings - the proxy handler configuration. * @param ttl - the upstream TTL in milliseconds if proxy.ttl it set to 'upstream' and the upstream response included a valid 'Cache-Control' header with 'max-age'. */ - onResponse?: (err: null | Boom.BoomError, + onResponse?: (this: RouteOptions, + err: null | Boom, res: http.IncomingMessage, - req: hapi.Request, - reply: hapi.ReplyWithContinue, // TODO, check it has continue + req: Request, + h: ResponseToolkit, settings: ProxyHandlerOptions, - ttl: number) => void; + ttl: number) => Lifecycle.ReturnValue; /** ttl - if set to 'upstream', applies the upstream response caching policy to the response using the response.ttl() method (or passed as an argument to the onResponse method if provided). */ ttl?: 'upstream'; /** agent - a node http(s) agent to be used for connections to upstream server. @see {@link https://nodejs.org/api/http.html#http_class_http_agent} */ @@ -68,24 +82,17 @@ declare namespace H2o2 { } declare module 'hapi' { - /** - * As one of the handlers for hapi, it is used through the route configuration object. - * [see docs](https://github.com/hapijs/h2o2#usage) - */ - interface RouteHandlerPlugins { - proxy?: H2o2.ProxyHandlerOptions; + interface HandlerDecorations { + /** Proxies the request to an upstream endpoint */ + proxy?: h2o2.ProxyHandlerOptions; } - interface Base_Reply { - /** - * Proxies the request to an upstream endpoint - * @param options an object including the same keys and restrictions defined by the [route proxy handler options](https://github.com/hapijs/h2o2#options). - * [see docs](https://github.com/hapijs/h2o2#replyproxyoptions) - */ - proxy(options: H2o2.ProxyHandlerOptions): void; + interface ResponseToolkit { + /** Proxies the request to an upstream endpoint */ + proxy(options: h2o2.ProxyHandlerOptions): void; } } -declare var H2o2: hapi.PluginFunction<{}>; +declare const h2o2: Plugin<{}>; -export = H2o2; +export = h2o2; diff --git a/types/h2o2/tsconfig.json b/types/h2o2/tsconfig.json index d8e49426e5..16279498a6 100644 --- a/types/h2o2/tsconfig.json +++ b/types/h2o2/tsconfig.json @@ -13,22 +13,9 @@ "../" ], "types": [], - "paths": { - "boom": [ - "boom/v4" - ], - "catbox": [ - "catbox/v7" - ], - "hapi": [ - "hapi/v16" - ], - "wreck": [ - "wreck/v7" - ] - }, "noEmit": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true }, "files": [ "index.d.ts",