From 003cc6f9cc7de6bff9eaee9fa0bc14a1f7b64cc0 Mon Sep 17 00:00:00 2001 From: Florent BEAUCHAMP Date: Thu, 3 Sep 2026 12:56:36 +0200 Subject: [PATCH] fix(proxy): fetch failed (#10346) * fix(backups): fetch failed disable compresion between xoa on proxy on xoa side * fix(xo-proxy): disable compression on the proxy it was disable with hrp, replaced by #10038 since it was never sending accept-encoding header compression can be put back later on a route by route basis after careful checks ( for example : file restore, backup listing ) --- @xen-orchestra/proxy/app/mixins/api.mjs | 9 +++++++++ CHANGELOG.unreleased.md | 4 ++++ packages/xo-server/src/xo-mixins/proxies.mjs | 16 +++++++++++----- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/@xen-orchestra/proxy/app/mixins/api.mjs b/@xen-orchestra/proxy/app/mixins/api.mjs index 9f0965adf2..4211ca2a2a 100644 --- a/@xen-orchestra/proxy/app/mixins/api.mjs +++ b/@xen-orchestra/proxy/app/mixins/api.mjs @@ -68,6 +68,15 @@ export default class Api { // not be enough for the API. ctx.req.setTimeout(0) + // Compression is disabled for the whole API. + // + // A compressor buffers its input, so a streamed response would not reach the + // client as it is produced. With brotli — which `fetch` negotiates by default + // over HTTPS — nothing at all is emitted before its window fills, not even the + // response headers, and the client times out waiting for them. + // + ctx.compress = false + const profile = await app.authentication.findProfile({ token: ctx.cookies.get('authenticationToken'), }) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 28976f1ba1..5adea7a8cc 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -15,6 +15,8 @@ > Users must be able to say: "I had this issue, happy to know it's fixed" +- [Backups] Fix error `fetch failed` on backup with proxies (PR [#10346](https://github.com/vatesfr/xen-orchestra/pull/10346)) + ### Packages to release > When modifying a package, add it here with its release type. @@ -31,4 +33,6 @@ +- @xen-orchestra/proxy patch +- xo-server patch diff --git a/packages/xo-server/src/xo-mixins/proxies.mjs b/packages/xo-server/src/xo-mixins/proxies.mjs index 4ba687259b..aa61096192 100644 --- a/packages/xo-server/src/xo-mixins/proxies.mjs +++ b/packages/xo-server/src/xo-mixins/proxies.mjs @@ -474,13 +474,19 @@ export default class Proxy { const proxy = await this._getProxy(id) const url = new URL('https://localhost/api/v1') - + const headers = { + 'Content-Type': 'application/json', + Cookie: cookie.serialize('authenticationToken', proxy.authenticationToken), + } + if (assertType !== 'scalar') { + // the proxy streams ndjson and binary responses incrementally; a compressor + // buffers them (brotli emits nothing until its window fills), so the response + // headers are never flushed and `headersTimeout` fires + headers['Accept-Encoding'] = 'identity' + } const request = { body: format.request(0, method, params), - headers: { - 'Content-Type': 'application/json', - Cookie: cookie.serialize('authenticationToken', proxy.authenticationToken), - }, + headers, method: 'POST', dispatcher: this._getAgent(timeout), }