fix: restore proxied chat event streaming
- Add dedicated SSE passthrough for proxied chat events - Keep generic API proxy behavior for non-stream requests - Add regression coverage for nginx-safe SSE headers
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { afterEach, describe, expect, it } from 'bun:test';
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
|
||||
import { registerOpenCodeProxy } from './lib/opencode/proxy.js';
|
||||
|
||||
const listen = (app, host = '127.0.0.1') => new Promise((resolve, reject) => {
|
||||
const server = app.listen(0, host, () => resolve(server));
|
||||
server.once('error', reject);
|
||||
});
|
||||
|
||||
const closeServer = (server) => new Promise((resolve, reject) => {
|
||||
if (!server) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
server.close((error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
describe('OpenCode proxy SSE forwarding', () => {
|
||||
let upstreamServer;
|
||||
let proxyServer;
|
||||
|
||||
afterEach(async () => {
|
||||
await closeServer(proxyServer);
|
||||
await closeServer(upstreamServer);
|
||||
proxyServer = undefined;
|
||||
upstreamServer = undefined;
|
||||
});
|
||||
|
||||
it('forwards event streams with nginx-safe headers', async () => {
|
||||
let seenAuthorization = null;
|
||||
|
||||
const upstream = express();
|
||||
upstream.get('/global/event', (req, res) => {
|
||||
seenAuthorization = req.headers.authorization ?? null;
|
||||
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
|
||||
res.setHeader('Cache-Control', 'private, max-age=0');
|
||||
res.setHeader('X-Upstream-Test', 'ok');
|
||||
res.write('data: {"ok":true}\n\n');
|
||||
res.end();
|
||||
});
|
||||
upstreamServer = await listen(upstream);
|
||||
const upstreamPort = upstreamServer.address().port;
|
||||
|
||||
const app = express();
|
||||
registerOpenCodeProxy(app, {
|
||||
fs: {},
|
||||
os: {},
|
||||
path,
|
||||
OPEN_CODE_READY_GRACE_MS: 0,
|
||||
getRuntime: () => ({
|
||||
openCodePort: upstreamPort,
|
||||
isOpenCodeReady: true,
|
||||
openCodeNotReadySince: 0,
|
||||
isRestartingOpenCode: false,
|
||||
}),
|
||||
getOpenCodeAuthHeaders: () => ({ Authorization: 'Bearer test-token' }),
|
||||
buildOpenCodeUrl: (requestPath) => `http://127.0.0.1:${upstreamPort}${requestPath}`,
|
||||
ensureOpenCodeApiPrefix: () => {},
|
||||
});
|
||||
proxyServer = await listen(app);
|
||||
const proxyPort = proxyServer.address().port;
|
||||
|
||||
const response = await fetch(`http://127.0.0.1:${proxyPort}/api/global/event`, {
|
||||
headers: { Accept: 'text/event-stream' },
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
||||
expect(response.headers.get('cache-control')).toBe('no-cache');
|
||||
expect(response.headers.get('x-accel-buffering')).toBe('no');
|
||||
expect(response.headers.get('x-upstream-test')).toBe('ok');
|
||||
expect(await response.text()).toBe('data: {"ok":true}\n\n');
|
||||
expect(seenAuthorization).toBe('Bearer test-token');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user