Skip to content

Commit

Permalink
feat: add optional proxy hook configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-tkachenko committed Nov 17, 2023
1 parent c351814 commit 21bab59
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 9 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ yarn add prxi

```typescript
import { Prxi, HttpMethod, ProxyRequest} from 'prxi';
import { OutgoingHttpHeaders, RequestOptions, ServerResponse } from 'http';

// Instantiate new Prxi, requires a src/Configuration.ts configuration object
const proxy = new Prxi({
Expand Down Expand Up @@ -139,6 +140,25 @@ const requestHandlers = [
'X-ADD_TO_RESPONSE': 'value',
'X-REMOVE_FROM_RESPONSE': null,
},

/**
* Optional handler before making the proxy request
* @param options request options
* @returns
*/
onBeforeProxyRequest: (options: RequestOptions) => {

}

/**
* Optional handler before sending a response
* @param res
* @param outgoingHeaders
* @returns
*/
onBeforeResponse: (res: ServerResponse, outgoingHeaders: OutgoingHttpHeaders) => {

}
});
}
}
Expand Down Expand Up @@ -189,6 +209,15 @@ const webSocketHandlers = [
'X-ADD_TO_RESPONSE': 'value',
'X-REMOVE_FROM_RESPONSE': null,
},

/**
* Optional handler before making the proxy request
* @param options request options
* @returns
*/
onBeforeProxyRequest: (options: RequestOptions) => {

}
);

// alternatively cancel request with custom http status code and message
Expand Down
16 changes: 13 additions & 3 deletions src/handlers/HttpProxyHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { IncomingMessage, ServerResponse } from "node:http";
import {request as httpRequest, RequestOptions} from 'node:http';
import {request as httpsRequest} from 'node:https';
import {request as httpRequest, RequestOptions, IncomingMessage, ServerResponse} from 'http';
import {request as httpsRequest} from 'https';

import { Configuration, ProxyRequestConfiguration } from "../interfaces";
import { UpstreamConfiguration } from "../interfaces/UpstreamConfiguration";
Expand Down Expand Up @@ -62,6 +61,11 @@ export class HttpProxyHandler {
timeout: this.configuration.proxyRequestTimeout,
};

/* istanbul ignore else */
if (proxyConfiguration && proxyConfiguration.onBeforeProxyRequest) {
proxyConfiguration.onBeforeProxyRequest(options);
}

const client = request(options);

await new Promise<void>((resolve, reject) => {
Expand All @@ -86,6 +90,12 @@ export class HttpProxyHandler {
// istanbul ignore next
proxyConfiguration?.proxyResponseHeaders
);

/* istanbul ignore else */
if (proxyConfiguration && proxyConfiguration.onBeforeResponse) {
proxyConfiguration.onBeforeResponse(res, headersToSet);
}

RequestUtils.updateResponseHeaders(res, headersToSet);

// istanbul ignore else
Expand Down
11 changes: 10 additions & 1 deletion src/handlers/WebSocketProxyHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ export class WebSocketProxyHandler {
): Promise<void> {
try {
WebSocketProxyHandler.debug.incomingSocket = socket;
proxyConfiguration = proxyConfiguration || emptyObj;
/* istanbul ignore next */
if (!proxyConfiguration) {
proxyConfiguration = emptyObj;
}


// istanbul ignore next
let target = proxyConfiguration.target || this.upstream.target;
Expand Down Expand Up @@ -74,6 +78,11 @@ export class WebSocketProxyHandler {
timeout: this.configuration.proxyRequestTimeout,
};

/* istanbul ignore else */
if (proxyConfiguration && proxyConfiguration.onBeforeProxyRequest) {
proxyConfiguration.onBeforeProxyRequest(options);
}

const client = request(options);
WebSocketProxyHandler.debug.upstreamRequest = client;

Expand Down
16 changes: 16 additions & 0 deletions src/interfaces/ProxyRequestConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { OutgoingHttpHeaders, RequestOptions, ServerResponse } from 'http';
import { HttpMethod } from './RequestHandler';

export interface ProxyRequestConfiguration {
Expand All @@ -18,4 +19,19 @@ export interface ProxyRequestConfiguration {

// Proxy response headers to add/replace/remove on top of the Configuration ones (if any)
proxyResponseHeaders?: Record<string, string | string[] | null>;

/**
* Optional handler before making the proxy request
* @param options request options
* @returns
*/
onBeforeProxyRequest?: (options: RequestOptions) => void;

/**
* Optional handler before sending a response
* @param res
* @param outgoingHeaders
* @returns
*/
onBeforeResponse?: (res: ServerResponse, outgoingHeaders: OutgoingHttpHeaders) => void;
}
2 changes: 0 additions & 2 deletions src/utils/RequestUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { OutgoingHttpHeaders, OutgoingMessage, IncomingMessage, IncomingHttpHeaders } from "http";

const emptyObj = {};

export class RequestUtils {
/**
* Extract path from the request
Expand Down
4 changes: 4 additions & 0 deletions test/HttpProxy.success.suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class HttpProxySuccessSuite {
reqproxylevelclear: result.data.headers['reqproxylevelclear'],
reqconfiglevel: result.data.headers['reqconfiglevel'],
test: result.data.headers['test'],
ON_BEFORE_PROXY_HEADER: result.data.headers['ON_BEFORE_PROXY_HEADER'.toLowerCase()],
}

deepEqual(requestHeaders, {
Expand All @@ -139,6 +140,7 @@ export class HttpProxySuccessSuite {
reqproxylevel: 'PROXY-REQUEST',
reqproxylevelclear: undefined,
test: 'true',
ON_BEFORE_PROXY_HEADER: 'yes',
});

const responseHeaders = {
Expand All @@ -147,6 +149,7 @@ export class HttpProxySuccessSuite {
resproxylevel: result.headers['resproxylevel'],
resproxylevelclear: result.headers['resproxylevelclear'],
['res-test']: result.headers['res-test'],
ON_BEFORE_RESPONSE_HEADER: result.headers['ON_BEFORE_RESPONSE_HEADER'.toLowerCase()],
}

deepEqual(responseHeaders, {
Expand All @@ -155,6 +158,7 @@ export class HttpProxySuccessSuite {
resproxylevel: 'PROXY-RESPONSE',
resproxylevelclear: undefined,
['res-test']: 'test-res',
ON_BEFORE_RESPONSE_HEADER: 'yes',
});
}

Expand Down
17 changes: 14 additions & 3 deletions test/helpers/TestProxy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { IncomingMessage, ServerResponse } from 'http';
import { Duplex } from 'stream';
import { ErrorHandler, Prxi, ProxyRequest, WebSocketHandlerFunction, WebSocketHandlerConfig, Configuration } from '../../src';
import { ErrorHandler, Prxi, ProxyRequest, WebSocketHandlerFunction, WebSocketHandlerConfig, Configuration, ProxyRequestConfiguration } from '../../src';
import { TestServer } from './TestServer';
import { RequestOptions } from 'https';

export class TestProxyParams {
configOverride?: Partial<Configuration>;
Expand Down Expand Up @@ -107,6 +108,12 @@ export class TestProxy {
RESConfigLevelOverwrite: 'PROXY-RESPONSE-OVERWRITE',
RESProxyLevelClear: null,
},
onBeforeProxyRequest: (options: RequestOptions) => {
options.headers['ON_BEFORE_PROXY_HEADER'] = 'yes';
},
onBeforeResponse: (res, outgoingHeaders) => {
outgoingHeaders['ON_BEFORE_RESPONSE_HEADER'] = 'yes';
}
});
}

Expand All @@ -117,8 +124,12 @@ export class TestProxy {
* @param head
* @param handle
*/
private async wsHandler(req: IncomingMessage, socket: Duplex, head: Buffer, handle: () => Promise<void>) {
await handle();
private async wsHandler(req: IncomingMessage, socket: Duplex, head: Buffer, handle: (configuration?: ProxyRequestConfiguration) => Promise<void>) {
await handle({
onBeforeProxyRequest: (options: RequestOptions) => {
options.headers['ON_BEFORE_WS_PROXY_HEADER'] = 'yes';
}
});
}

/**
Expand Down

0 comments on commit 21bab59

Please sign in to comment.