feat(plugin-proxy-backend): revive request bodies

in order to support proxying requests with bodies that have previously
been consumed in a middleware, the proxy config can now specify
`reviveRequestBody: true` in order to propagate the previously consumed
body to the proxied request.

Signed-off-by: Dan Hoizner <dan.hoizner@gmail.com>
This commit is contained in:
Dan Hoizner
2023-02-08 15:34:20 -05:00
parent 5673da2bcb
commit a30d2e8df5
4 changed files with 66 additions and 1 deletions
+14
View File
@@ -0,0 +1,14 @@
---
'@backstage/plugin-proxy-backend': minor
---
The proxy-backend plugin now supports reviving request bodies that have previously been consumed by an express middleware (e.g. `express.json()`). This is done by setting `reviveRequestBody: true` on the proxy configuration. In order to preserve the current behavior, the proxy will **not** revive request bodies by default.
The following is an example of a proxy configuration that revives request bodies:
```diff
proxy:
'/target':
target: 'http://proxy-route'
+ reviveRequestBody: true
```
+5
View File
@@ -50,6 +50,11 @@ export interface Config {
* and headers that are set by the proxy will be forwarded.
*/
allowedHeaders?: string[];
/**
* Revive request body if it was consumed by a middleware before reaching the proxy. By default, the request
* body is not revived.
*/
reviveRequestBody?: boolean;
};
};
}
@@ -18,11 +18,16 @@ import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { Request, Response } from 'express';
import * as http from 'http';
import { createProxyMiddleware, Options } from 'http-proxy-middleware';
import {
createProxyMiddleware,
fixRequestBody,
Options,
} from 'http-proxy-middleware';
import { buildMiddleware, createRouter } from './router';
jest.mock('http-proxy-middleware', () => ({
createProxyMiddleware: jest.fn(() => () => undefined),
fixRequestBody: jest.fn(),
}));
const mockCreateProxyMiddleware = createProxyMiddleware as jest.MockedFunction<
@@ -389,6 +394,41 @@ describe('buildMiddleware', () => {
expect(Object.keys(testClientResponse.headers!)).toEqual(['set-cookie']);
});
it('revives request body when configured', async () => {
buildMiddleware('/proxy', logger, '/test', {
target: 'http://mocked',
reviveRequestBody: true,
});
expect(createProxyMiddleware).toHaveBeenCalledTimes(1);
const config = mockCreateProxyMiddleware.mock.calls[0][1] as Options;
expect(config).toBeDefined();
expect(config.onProxyReq).toBeDefined();
config.onProxyReq!(
{} as http.ClientRequest,
{} as Request,
{} as Response,
{},
);
expect(fixRequestBody).toHaveBeenCalledTimes(1);
});
it('does not revive request body when not configured', async () => {
buildMiddleware('/proxy', logger, '/test', {
target: 'http://mocked',
});
expect(createProxyMiddleware).toHaveBeenCalledTimes(1);
const config = mockCreateProxyMiddleware.mock.calls[0][1] as Options;
expect(config).toBeDefined();
expect(config.onProxyReq).not.toBeDefined();
});
it('rejects malformed target URLs', async () => {
expect(() =>
buildMiddleware('/proxy', logger, '/test', 'backstage.io'),
@@ -19,6 +19,7 @@ import express from 'express';
import Router from 'express-promise-router';
import {
createProxyMiddleware,
fixRequestBody,
Options,
RequestHandler,
} from 'http-proxy-middleware';
@@ -58,6 +59,7 @@ export interface RouterOptions {
export interface ProxyConfig extends Options {
allowedMethods?: string[];
allowedHeaders?: string[];
reviveRequestBody?: boolean;
}
// Creates a proxy middleware, possibly with defaults added on top of the
@@ -175,6 +177,10 @@ export function buildMiddleware(
});
};
if (fullConfig.reviveRequestBody) {
fullConfig.onProxyReq = fixRequestBody;
}
return createProxyMiddleware(filter, fullConfig);
}