Merge pull request #29498 from nBerg/support-sse-connections-closed

proxy-backend support sse connections closing
This commit is contained in:
Patrik Oldsberg
2025-04-09 19:02:45 +02:00
committed by GitHub
3 changed files with 66 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-proxy-backend': patch
---
Fixed handling of proxied SSE connections when the upstream server closes the connection
@@ -479,6 +479,7 @@ describe('buildMiddleware', () => {
pragma: 'value',
'set-cookie': ['value'],
},
on: jest.fn(),
} as Partial<http.IncomingMessage>;
expect(config).toBeDefined();
@@ -522,6 +523,7 @@ describe('buildMiddleware', () => {
'set-cookie': [],
'x-auth-request-user': 'asd',
},
on: jest.fn(),
} as Partial<http.IncomingMessage>;
expect(config).toBeDefined();
@@ -603,4 +605,49 @@ describe('buildMiddleware', () => {
),
).toThrow(/Proxy target is not a valid URL/);
});
it('closes SSE connections when backend closes the connection', async () => {
buildMiddleware(
'/proxy',
logger,
'/test',
{
target: 'http://mocked',
},
httpRouterService,
);
expect(createProxyMiddleware).toHaveBeenCalledTimes(1);
const config = mockCreateProxyMiddleware.mock.calls[0][1] as Options;
const testServerResponse = {
headers: {},
on: jest.fn((event, callback) => {
if (event === 'close') {
callback();
}
return testServerResponse;
}),
} as Partial<http.IncomingMessage>;
const testClientResponse = {
writableEnded: false,
end: jest.fn(),
} as Partial<Response>;
expect(config).toBeDefined();
expect(config.onProxyRes).toBeDefined();
config.onProxyRes!(
testServerResponse as http.IncomingMessage,
{} as Request,
testClientResponse as Response,
);
expect(testServerResponse.on).toHaveBeenCalledWith(
'close',
expect.any(Function),
);
expect(testClientResponse.end).toHaveBeenCalled();
});
});
+14 -2
View File
@@ -214,8 +214,12 @@ export function buildMiddleware(
].map(h => h.toLocaleLowerCase()),
);
// only forward the allowed headers in backend->client
fullConfig.onProxyRes = (proxyRes: http.IncomingMessage) => {
fullConfig.onProxyRes = (
proxyRes: http.IncomingMessage,
_,
res: express.Response,
) => {
// only forward the allowed headers in backend->client
const headerNames = Object.keys(proxyRes.headers);
headerNames.forEach(h => {
@@ -223,6 +227,14 @@ export function buildMiddleware(
delete proxyRes.headers[h];
}
});
// handle SSE connections closed by the backend
// https://github.com/chimurai/http-proxy-middleware/discussions/765
proxyRes.on('close', () => {
if (!res.writableEnded) {
res.end();
}
});
};
if (reviveConsumedRequestBodies) {