diff --git a/app/server/lib/DocApi.ts b/app/server/lib/DocApi.ts index e1dbe3fde2d..223b08bd988 100644 --- a/app/server/lib/DocApi.ts +++ b/app/server/lib/DocApi.ts @@ -1177,6 +1177,10 @@ export class DocWorkerApi { 'Content-Type': 'application/json', } }); + if (!ref.ok) { + res.status(ref.status).send(await ref.text()); + return; + } const states2: DocState[] = (await ref.json()).states; const left = states[0]; const right = states2[0]; diff --git a/test/server/lib/DocApi.ts b/test/server/lib/DocApi.ts index 2886d1d1fcf..d60324f48c5 100644 --- a/test/server/lib/DocApi.ts +++ b/test/server/lib/DocApi.ts @@ -160,21 +160,23 @@ describe('DocApi', function () { testDocApi(); }); - describe("should work behind a reverse-proxy", async () => { - let proxy: TestServerReverseProxy; - - setup('behind-proxy', async () => { - proxy = new TestServerReverseProxy(); + describe('behind a reverse-proxy', function () { + async function setupServersWithProxy(suitename: string, overrideEnvConf?: NodeJS.ProcessEnv) { + const proxy = new TestServerReverseProxy(); const additionalEnvConfiguration = { ALLOWED_WEBHOOK_DOMAINS: `example.com,localhost:${webhooksTestPort}`, GRIST_DATA_DIR: dataDir, APP_HOME_URL: await proxy.getServerUrl(), GRIST_ORG_IN_PATH: 'true', GRIST_SINGLE_PORT: '0', + ...overrideEnvConf }; - home = await TestServer.startServer('home', tmpDir, suitename, additionalEnvConfiguration); - docs = await TestServer.startServer('docs', tmpDir, suitename, additionalEnvConfiguration, home.serverUrl); + const home = await TestServer.startServer('home', tmpDir, suitename, additionalEnvConfiguration); + const docs = await TestServer.startServer( + 'docs', tmpDir, suitename, additionalEnvConfiguration, home.serverUrl + ); proxy.requireFromOutsideHeader(); + await proxy.start(home, docs); homeUrl = serverUrl = await proxy.getServerUrl(); @@ -183,14 +185,69 @@ describe('DocApi', function () { Origin: serverUrl, ...TestServerReverseProxy.FROM_OUTSIDE_HEADER, }; - }); - after(async () => { + return {proxy, home, docs}; + } + + async function tearDown(proxy: TestServerReverseProxy, servers: TestServer[]) { proxy.stop(); + for (const server of servers) { + await server.stop(); + } await flushAllRedis(); + } + + let proxy: TestServerReverseProxy; + + describe('should run usual DocApi test', function () { + setup('behind-proxy-testdocs', async () => { + ({proxy, home, docs} = await setupServersWithProxy(suitename)); + }); + + after(() => tearDown(proxy, [home, docs])); + + testDocApi(); }); - testDocApi(); + async function testCompareDocs(proxy: TestServerReverseProxy, home: TestServer) { + // Pass kiwi's headers as it contains both Authorization and Origin headers + // if run behind a proxy, so we can ensure that the Origin header check is not made. + const chimpy = makeConfig('chimpy'); + const userApiServerUrl = await proxy.getServerUrl(); + const chimpyApi = home.makeUserApi( + ORG_NAME, 'chimpy', { serverUrl: userApiServerUrl, headers: chimpy.headers as Record } + ); + const ws1 = (await chimpyApi.getOrgWorkspaces('current'))[0].id; + const docId1 = await chimpyApi.newDoc({name: 'testdoc1'}, ws1); + const docId2 = await chimpyApi.newDoc({name: 'testdoc2'}, ws1); + const doc1 = chimpyApi.getDocAPI(docId1); + + return doc1.compareDoc(docId2); + } + + describe('with APP_HOME_INTERNAL_URL', function () { + setup('behind-proxy-with-apphomeinternalurl', async () => { + ({proxy, home, docs} = await setupServersWithProxy(suitename)); + }); + + after(() => tearDown(proxy, [home, docs])); + it('should succeed to compare docs', async function () { + const res = await testCompareDocs(proxy, home); + assert.isDefined(res); + }); + }); + + describe('without APP_HOME_INTERNAL_URL', function () { + setup('behind-proxy-without-apphomeinternalurl', async () => { + ({proxy, home, docs} = await setupServersWithProxy(suitename, {APP_HOME_INTERNAL_URL: ''})); + }); + + after(() => tearDown(proxy, [home, docs])); + it('should succeed to compare docs', async function () { + const promise = testCompareDocs(proxy, home); + await assert.isRejected(promise, /TestServerReverseProxy: called public URL/); + }); + }); }); describe("should work directly with a docworker", async () => { diff --git a/test/server/lib/helpers/TestServer.ts b/test/server/lib/helpers/TestServer.ts index 0de180bae4c..acaab5d61ab 100644 --- a/test/server/lib/helpers/TestServer.ts +++ b/test/server/lib/helpers/TestServer.ts @@ -249,7 +249,7 @@ export class TestServerReverseProxy { if (this.stopped) { return; } - log.info("Stopping node TestServerProxy"); + log.info("Stopping node TestServerReverseProxy"); this._server.close(); } @@ -258,8 +258,8 @@ export class TestServerReverseProxy { return (oreq: express.Request, ores: express.Response) => { if (this._requireFromOutsideHeader && !isAffirmative(oreq.get("X-FROM-OUTSIDE"))) { - console.error('TestServerReverseProxy: called public URL from internal'); - return ores.json({error: "TestServerProxy: called public URL from internal "}).status(403); + log.error('TestServerReverseProxy: called public URL from internal'); + return ores.status(403).json({error: "TestServerReverseProxy: called public URL from internal "}); } const options = { @@ -299,7 +299,7 @@ export class TestServerReverseProxy { }) .on('error', e => { // we got an error - console.log(e.message); + log.info('Error caught by TestServerReverseProxy: %s', e.message); try { // attempt to set error message and http status ores.writeHead(500);