Назад в Центр знаний
Платформа

Compression Streams API: границы браузера

Используйте CompressionStream и DecompressionStream с gzip или deflate, явно задавая backpressure, отмену и лимиты данных.

BotBrowser Team

Документация

Нужна структурированная документация по теме Платформа?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

Compression Streams API сжимает и распаковывает web-поток без загрузки всех данных в один буфер JavaScript. Проблема начинается, когда тело ответа, декодер текста, сигнал отмены и политика Content-Encoding сервера используют одну цепочку. BotBrowser поддерживает воспроизведение разрешённого fixture и запись видимых состояний успеха, отмены и ошибки; он не сертифицирует политику удалённого сервера или криптографическую целостность. Для соседних границ см. измерение производительности и переносимость WebAssembly.

TL;DR

  • CompressionStream и DecompressionStream преобразуют байты в форматах gzip, deflate или deflate-raw, но не проверяют смысл приложения.
  • ReadableStream ограничивает память только при наличии backpressure у потребителя и отмене всех созданных владельцем ветвей.
  • Декодируйте текст после распаковки, сохраняйте владельца байтов и превращайте неправильный ввод в явную ошибку.
  • Поддержка браузера, Content-Encoding сервера и целостность данных являются разными вопросами.

Содержание

Байты проходят через gzip, backpressure, распаковку, декодирование текста или явное состояние ошибки.

Выбрать формат и владельца

Спецификация Compression Streams определяет CompressionStream и DecompressionStream как интерфейсы TransformStream. Конструктор получает формат. gzip оборачивает DEFLATE заголовком и трейлером, deflate использует форму zlib, а deflate-raw содержит необёрнутый поток DEFLATE. Производитель и потребитель должны согласовать формат, поскольку наличие двух API не создаёт автоматического согласования.

Справочники MDN для CompressionStream и DecompressionStream описывают форматы и доступность. Проверяйте оба конструктора до выбора нативного пути. Отсутствующий конструктор означает ограничение возможности, а не повреждение данных. Статья Chrome Developers описывает потоковую модель, но не обещает одинаковое поведение каждого webview или серверного адаптера.

Назначьте владельца до соединения потоков. Код, получивший Response.body, владеет reader, AbortController, распаковщиком и декодером до передачи полного результата. Записывайте fixtureId, format, source, consumer и outcome, но не приватный текст и полный URL.

Не смешивайте транспортное и прикладное сжатие. fetch может прозрачно декодировать Content-Encoding: gzip; затем DecompressionStream('gzip') станет двойной распаковкой. Бинарный endpoint может вернуть непрозрачный gzip-member, и тогда распаковка принадлежит приложению. Перед преобразованием проверяйте заголовки и контракт.

Та же граница действует для загрузки. Браузерное преобразование само не задаёт Content-Encoding, а заголовок не доказывает корректность gzip-байтов. Храните заголовок, формат преобразования и ответ сервера отдельно. BotBrowser поддерживает воспроизведение разрешённого fixture и запись видимого состояния, но не может управлять политикой удалённого origin.

Создать ограниченный конвейер байтов

Используйте pipeThrough() и pipeTo(), чтобы backpressure и отмена оставались частью модели потоков:

const controller = new AbortController();
const input = new Blob([new TextEncoder().encode('owned fixture')]).stream();
const compressed = input.pipeThrough(new CompressionStream('gzip'));
const output = new WritableStream({
  write(chunk) {
    recordChunk(chunk);
  },
  close() {
    record.outcome = 'compressed';
  },
  abort(reason) {
    record.outcome = 'aborted';
    record.reason = String(reason);
  },
});

await compressed.pipeTo(output, { signal: controller.signal });

Пример записывает chunks, а не собирает бесконечный массив. Потребитель управляет спросом, а владелец может отменить весь pipe. Границы chunks меняются между версиями и не описывают сетевые пакеты. Измеряйте итоговые байты и видимое завершение, а не предполагаемое число вызовов write().

Установите бюджет, например 8 MB сжатых данных и отдельный максимум для распакованного вывода. Лимит защищает от чрезмерного расширения, но не является checksum. Сохраняйте bytesIn, bytesOut, limitBytes и outcome, не сохраняя данные. Успешный codec не подтверждает корректность JSON, архива или схемы.

tee() создаёт ветви с отдельными спросом и отменой. Не дублируйте response только для журналирования сырых байтов. Worker может владеть codec, но должен отправлять странице короткое состояние compression-complete, а не всё тело. После закрытия первого владельца второй reader байты не восстановит.

Обработать текст, отмену и ошибки

Распаковка выдаёт байты. После неё применяйте TextDecoder и выбирайте кодировку из контракта. Успешное преобразование не доказывает UTF-8. Режим { fatal: true } отделяет decode-error от decompress-error, а символы замены могут скрыть повреждение.

const decoded = compressedResponse.body.pipeThrough(new DecompressionStream('gzip'));
const reader = decoded.getReader();
const decoder = new TextDecoder('utf-8', { fatal: true });
let text = '';
try {
  for (;;) {
    const { value, done } = await reader.read();
    if (done) break;
    text += decoder.decode(value, { stream: true });
  }
  text += decoder.decode();
  record.outcome = 'decoded';
} catch (error) {
  record.outcome = error.name === 'AbortError' ? 'cancelled' : 'decode-error';
} finally {
  reader.releaseLock();
}

Для большого ответа потребляйте фрагменты, а не используйте text +=. Body может быть null, уже потреблён или заблокирован другим reader. До повтора различайте body-null, body-locked, unsupported-format, decompress-error и decode-error.

Отмена возможна до байтов, после частичной записи или после удаления компонента навигацией. Разделяйте cancelled, sink-error и owner-gone, если восстановление различается. Abort pipe не удаляет уже отправленные байты и копии другого сервиса.

Распакованные данные не доказывают подлинность. Checksums некоторых форматов обнаруживают ошибки framing, но не являются подписью приложения. Проверяйте отдельную подпись и храните результат рядом с состоянием распаковки.

Проверить границы fixture

Используйте четыре синтетические строки: native gzip compression, native gzip decompression, отменённый pipe и malformed bytes. Повторите их при доступных конструкторах, без конструкторов и с уже потреблённым body:

СлучайОжидаемое состояниеКласс для проверки
CompressionStream('gzip') получает байтыcompressed с ограниченным bytesOutunsupported-format или ошибка sink
DecompressionStream('gzip') получает fixturedecoded и корректный UTF-8decompress-error или decode-error
Срабатывает сигнал отменыcancelled, оба владельца закрытыутёкший reader или частичный результат
К распаковщику приходит malformed memberdecompress-errorбесконечный retry или пустой успех
Response.body null или consumedbody-null или body-lockedнеправильная гипотеза владельца

Записывайте browserVersion, implementation, format, bytesIn, bytesOut, limitBytes, outcome и fixtureId. Значения должны быть синтетическими и ограниченными. BotBrowser может записать compressed, decoded, cancelled и decompress-error, но не управляет удалённым Content-Encoding, сохранением байтов proxy или криптографической целостностью.

Проверьте тот же ресурс через сервисный обработчик и через window. Если worker распаковывает и возвращает новый Response, страница не должна распаковывать повторно. Если worker передаёт непрозрачный binary member, преобразование принадлежит странице. Загрузка и скачивание требуют отдельных строк, поскольку владельцы и восстановление различаются.

Честный запасной путь может использовать проверенную библиотеку, server endpoint или путь без compression. В implementation сохраняйте native, fallback-unavailable или server-decoded; не обещайте одинаковые chunks и timing. После изменения браузера, сервера, parser или сервисного обработчика повторите строки и сравните первое изменившееся поле.

Public sources

Заключение

Объявите формат, назначьте одного владельца каждому потоку и разделите распаковку, декодирование, отмену и целостность. Применяйте backpressure, ограничивайте байты и закрывайте ошибочный reader вместо повтора детерминированной порчи. Повторяйте fixture после изменений браузера, сервера или сервисного обработчика и принимайте решение по первому изменившемуся состоянию.

#Compression Streams API#CompressionStream#DecompressionStream#Потоки Браузера

Эта статья входит в руководство: Поверхности веб-платформы: карта проверки

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.