diff --git a/.changeset/thin-trams-work.md b/.changeset/thin-trams-work.md new file mode 100644 index 0000000000..ddc31631d1 --- /dev/null +++ b/.changeset/thin-trams-work.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-proxy-backend': patch +--- + +Fixed handling of proxied sse connections when the upstream server closes the connection diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts index 66d6de2f28..7d9a3dadbc 100644 --- a/plugins/proxy-backend/src/service/router.test.ts +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -479,6 +479,7 @@ describe('buildMiddleware', () => { pragma: 'value', 'set-cookie': ['value'], }, + on: jest.fn(), } as Partial; expect(config).toBeDefined(); @@ -522,6 +523,7 @@ describe('buildMiddleware', () => { 'set-cookie': [], 'x-auth-request-user': 'asd', }, + on: jest.fn(), } as Partial; 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; + + const testClientResponse = { + writableEnded: false, + end: jest.fn(), + } as Partial; + + 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(); + }); });