Fix incorrect use of Content-Length when the server compresses the content (fixes #487)

This commit is contained in:
Vanilagy
2026-09-08 17:19:32 +02:00
parent b560977b50
commit 450c5def97
2 changed files with 43 additions and 1 deletions
+7 -1
View File
@@ -955,7 +955,13 @@ export class UrlSource extends PathedSource {
}
outer:
if (this._orchestrator.fileSize === null) {
if (
this._orchestrator.fileSize === null
// Content-Range/Length fields are meaningless if Content-Encoding is present. Content-Encoding is
// basically never used for range responses (since the encoding runs *before* the slicing), so we're set
// in that case.
&& (response.status === 206 || (response.type === 'basic' && !response.headers.has('Content-Encoding')))
) {
// See if we can deduce the file size from the response
const contentRange = response.headers.get('Content-Range');
+36
View File
@@ -2,6 +2,9 @@ import { expect, test } from 'vitest';
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
import { brotliCompressSync } from 'node:zlib';
import { assert } from '../../src/misc.js';
import { Reader, readBytes } from '../../src/reader.js';
import {
ALL_FORMATS,
EncodedPacket,
@@ -225,6 +228,39 @@ test('UrlSource in sequential mode downloads lazily and aborts the response on d
}
}, 10_000);
test('UrlSource reads the full decoded body of a compressed response', async () => {
const text = 'Some playlist text\n'.repeat(256);
const content = Buffer.from(text);
const compressed = brotliCompressSync(content);
const server = http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plain',
'Content-Encoding': 'br',
'Content-Length': compressed.byteLength,
});
res.end(compressed);
});
await new Promise<void>(resolve => server.listen(0, resolve));
try {
const address = server.address();
assert(address && typeof address !== 'string');
const source = new UrlSource(`http://localhost:${address.port}/playlist.m3u8`);
using ref = source.ref();
const reader = new Reader(ref.source);
const slice = await reader.requestEntireFile();
expect(slice).not.toBeNull();
expect(compressed.byteLength).toBeLessThan(content.byteLength);
expect(slice!.length).toBe(content.byteLength);
expect(Buffer.from(readBytes(slice!, slice!.length)).toString()).toBe(text);
} finally {
server.closeAllConnections();
server.close();
}
});
const startRangelessServer = async (
options: { responseByteLimits?: number[]; trailingPaddingSize?: number } = {},
) => {