From c26144403c78d54b28f605ea8421229ac51b2a0e Mon Sep 17 00:00:00 2001 From: Andres Perez <1676612+andresperezl@users.noreply.github.com> Date: Wed, 11 Jun 2025 19:56:42 -0400 Subject: [PATCH] api/stream: add Transfer-Encoding header to tunnel responses For HTTP/1.1 when the length of the content isn't known, you must send the header `Transfer-Encoding: chunked`, so the client knows that it needs to process the response in parts (chunks). This header is deprecated in HTTP/2, and maybe not neccessary when using a proxy (nginx, haproxy, etc), but neccesary when accesing cobalt directly. https://en.wikipedia.org/wiki/Chunked_transfer_encoding --- api/src/core/api.js | 3 ++- api/src/stream/types.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/api/src/core/api.js b/api/src/core/api.js index eb5cf4ff..09d80ecc 100644 --- a/api/src/core/api.js +++ b/api/src/core/api.js @@ -276,7 +276,8 @@ export const runAPI = async (express, app, __dirname, isPrimary = true) => { methods: ['GET'], exposedHeaders: [ 'Estimated-Content-Length', - 'Content-Disposition' + 'Content-Disposition', + 'Transfer-Encoding' ], ...corsConfig, })); diff --git a/api/src/stream/types.js b/api/src/stream/types.js index 6b493efa..ac1e564a 100644 --- a/api/src/stream/types.js +++ b/api/src/stream/types.js @@ -156,6 +156,7 @@ const merge = async (streamInfo, res) => { res.setHeader('Connection', 'keep-alive'); res.setHeader('Content-Disposition', contentDisposition(streamInfo.filename)); res.setHeader('Estimated-Content-Length', await estimateTunnelLength(streamInfo)); + res.setHeader('Transfer-Encoding', 'chunked'); pipe(muxOutput, res, shutdown); @@ -220,6 +221,7 @@ const remux = async (streamInfo, res) => { res.setHeader('Connection', 'keep-alive'); res.setHeader('Content-Disposition', contentDisposition(streamInfo.filename)); res.setHeader('Estimated-Content-Length', await estimateTunnelLength(streamInfo)); + res.setHeader('Transfer-Encoding', 'chunked'); pipe(muxOutput, res, shutdown); @@ -296,6 +298,7 @@ const convertAudio = async (streamInfo, res) => { estimateAudioMultiplier(streamInfo) * 1.1 ) ); + res.setHeader('Transfer-Encoding', 'chunked'); pipe(muxOutput, res, shutdown); res.on('finish', shutdown); @@ -334,6 +337,7 @@ const convertGif = async (streamInfo, res) => { res.setHeader('Connection', 'keep-alive'); res.setHeader('Content-Disposition', contentDisposition(streamInfo.filename)); res.setHeader('Estimated-Content-Length', await estimateTunnelLength(streamInfo, 60)); + res.setHeader('Transfer-Encoding', 'chunked'); pipe(muxOutput, res, shutdown);