Skip to content

Commit

Permalink
feat(router-proxy): added support for proxy endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
carloseustaquio committed Oct 8, 2021
1 parent e4c40db commit 45b6937
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ export class HttpServer {
},
});
}

for (const proxy of router.getProxies()) {
this.server.route(proxy);
}
}

await this.server.start();
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/http/router/router.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ function create({ routerPath = '' }: { routerPath?: string } = {}): RouterMock {
patch: jest.fn(),
getRoutes: jest.fn(),
handleLegacyErrors: jest.fn().mockImplementation((handler) => handler),
proxy: jest.fn(),
getProxies: jest.fn(),
};
}

Expand Down
94 changes: 93 additions & 1 deletion src/core/server/http/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
* GitHub history for details.
*/

import { Request, ResponseObject, ResponseToolkit } from 'hapi';
import { Request, ResponseObject, ResponseToolkit, ServerRoute } from 'hapi';
import Boom from 'boom';

import { isConfigSchema } from '@osd/config-schema';
import { IncomingMessage } from 'http';
import { Logger } from '../../logging';
import { LegacyOpenSearchErrorHelpers } from '../../opensearch/legacy/errors';
import {
Expand All @@ -60,6 +61,25 @@ interface RouterRoute {
handler: (req: Request, responseToolkit: ResponseToolkit) => Promise<ResponseObject | Boom<any>>;
}

interface ProxyRouterExtraOptions {
proxyPath: string;
additionalHeaders?:
| Record<string, string>
| ((request: Request) => Promise<Record<string, string>>);
onResponse?: (err: null | Boom, res: IncomingMessage, req: Request) => IncomingMessage;
}

type RouterProxy = ServerRoute;

/**
* Proxy handler common definition
*
* @public
*/
export type ProxyRegistrar = <P, Q, B>(
route: RouteConfig<P, Q, B, any> & ProxyRouterExtraOptions
) => void;

/**
* Route handler common definition
*
Expand All @@ -82,6 +102,12 @@ export interface IRouter {
*/
routerPath: string;

/**
* Register a proxy handler for requests.
* @param route {@link RouteConfig} - a route configuration.
*/
proxy: ProxyRegistrar;

/**
* Register a route handler for `GET` request.
* @param route {@link RouteConfig} - a route configuration.
Expand Down Expand Up @@ -129,6 +155,13 @@ export interface IRouter {
* @internal
*/
getRoutes: () => RouterRoute[];

/**
* Returns all proxies registered with this router.
* @returns List of registered proxies.
* @internal
*/
getProxies: () => RouterProxy[];
}

export type ContextEnhancer<P, Q, B, Method extends RouteMethod> = (
Expand Down Expand Up @@ -218,11 +251,13 @@ function validOptions(
*/
export class Router implements IRouter {
public routes: Array<Readonly<RouterRoute>> = [];
public proxies: Array<Readonly<RouterProxy>> = [];
public get: IRouter['get'];
public post: IRouter['post'];
public delete: IRouter['delete'];
public put: IRouter['put'];
public patch: IRouter['patch'];
public proxy: IRouter['proxy'];

constructor(
public readonly routerPath: string,
Expand All @@ -249,17 +284,74 @@ export class Router implements IRouter {
});
};

const buildProxy = () => <P, Q, B>(
proxyConfig: RouteConfig<P, Q, B, any> & ProxyRouterExtraOptions
) => {
const methods: RouteMethod[] = ['get', 'post', 'delete', 'put', 'patch', 'options'];
const proxyPath =
proxyConfig.proxyPath +
(proxyConfig.proxyPath.charAt(proxyConfig.proxyPath.length - 1) === '/' ? '' : '/');

methods.forEach((method) => {
this.proxies.push({
path: `${proxyConfig.path}/{path*}`,
method,
options:
method === 'get'
? undefined
: {
payload: { parse: false },
validate: { payload: true },
},
handler: {
proxy: {
onResponse: (err, res, req) => {
if (proxyConfig.onResponse) {
return proxyConfig.onResponse(err, res, req);
}
return res;
},
mapUri: async (request) => {
let additionalHeaders;

if (typeof proxyConfig.additionalHeaders === 'function') {
additionalHeaders = await proxyConfig.additionalHeaders(request);
} else {
additionalHeaders = proxyConfig.additionalHeaders;
}

return {
uri: proxyPath + request.params.path,
headers: {
...request.headers,
...additionalHeaders,
},
};
},
passThrough: true,
xforward: true,
},
},
});
});
};

this.get = buildMethod('get');
this.post = buildMethod('post');
this.delete = buildMethod('delete');
this.put = buildMethod('put');
this.patch = buildMethod('patch');
this.proxy = buildProxy();
}

public getRoutes() {
return [...this.routes];
}

public getProxies() {
return [...this.proxies];
}

public handleLegacyErrors = wrapErrors;

private async handle<P, Q, B>({
Expand Down

0 comments on commit 45b6937

Please sign in to comment.