Volver al Centro de conocimiento
Plataforma

Compression Streams API: límites del navegador

Usa CompressionStream y DecompressionStream con gzip o deflate, manteniendo explícitos el backpressure, la cancelación y los límites de datos.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Plataforma?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

La Compression Streams API comprime y descomprime un stream web sin cargar todos los datos en un único buffer de JavaScript. La dificultad aparece cuando un cuerpo de respuesta, un decodificador de texto, una señal de cancelación y la política Content-Encoding del servidor comparten la tubería. BotBrowser permite repetir un fixture autorizado y registrar estados visibles de éxito, cancelación y error; no puede certificar la política de compresión de un servidor remoto ni su integridad criptográfica. Consulta también medición de rendimiento para latencia y portabilidad de WebAssembly cuando un codec cruza una frontera de runtime.

TL;DR

  • CompressionStream y DecompressionStream transforman bytes con formato gzip, deflate o deflate-raw; no validan el significado de la aplicación.
  • Un pipeline ReadableStream solo limita memoria cuando el consumidor aplica backpressure y el propietario cancela cada rama que creó.
  • Decodifica texto después de descomprimir, conserva la propiedad de los bytes y trata la entrada inválida como error explícito.
  • Soporte del navegador, Content-Encoding del servidor e integridad de los datos son preguntas separadas.

Contents

Los bytes atraviesan una transformación gzip, backpressure, descompresión, decodificación de texto o un estado de error explícito.

Elegir formato y propietario

La especificación Compression Streams define CompressionStream y DecompressionStream como interfaces TransformStream. El constructor recibe un formato. gzip envuelve DEFLATE con cabecera y tráiler, deflate usa la forma envuelta en zlib y deflate-raw contiene el stream DEFLATE sin envoltura. Productor y consumidor deben acordar el formato; que existan ambas APIs no crea negociación automática.

Las referencias de MDN CompressionStream y MDN DecompressionStream describen formatos y disponibilidad. Detecta ambos constructores antes de elegir la ruta nativa. Un constructor ausente es un resultado de capacidad, no prueba de que los datos estén corruptos. El artículo de Chrome Developers presenta la API como alternativa de streaming, no como promesa para cada webview o adaptador de servidor.

Asigna propiedad antes de conectar streams. El código que recibe Response.body posee el reader, el AbortController, el descompresor y el decoder hasta entregar un resultado completo. Registra fixtureId, format, source, consumer y outcome, pero no texto privado ni una URL completa.

No confundas compresión de transporte con compresión de aplicación. Un servidor puede enviar Content-Encoding: gzip y fetch puede decodificarlo antes de entregar Response.body; aplicar después DecompressionStream('gzip') sería doble descompresión. Un endpoint binario puede entregar un miembro gzip opaco y entonces la aplicación posee la descompresión. Inspecciona cabeceras y contrato antes de añadir una transformación.

La misma frontera existe en uploads. Una conversión del navegador no establece Content-Encoding automáticamente, y una cabecera no demuestra que los bytes sean gzip válido. Conserva cabecera, formato de la conversión y respuesta del servidor como campos separados. BotBrowser puede repetir el fixture autorizado y registrar el estado visible, pero no decide la política del origen remoto.

Construir un pipeline de bytes acotado

Usa pipeThrough() y pipeTo() para que backpressure y cancelación formen parte del modelo:

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 });

El ejemplo registra chunks en lugar de concatenarlos sin límite. El consumidor controla la demanda y el propietario puede cancelar toda la tubería. Los límites de chunk pueden cambiar entre versiones y no identifican paquetes de red. Mide bytes totales y finalización visible, no un número supuesto de llamadas a write().

Aplica un presupuesto de bytes, por ejemplo 8 MB comprimidos y un máximo propio para la salida descomprimida. El límite protege contra expansiones enormes, pero no es un checksum. Registra bytesIn, bytesOut, limitBytes y outcome sin conservar los datos. Un stream aceptado por el codec no demuestra que JSON, un archivo o un esquema sean válidos.

Si hay varios consumidores, tee() crea ramas con demanda y cancelación separadas. No dupliques una respuesta solo para guardar bytes crudos. Registra longitudes, formato y estados; los bytes comprimidos también pueden contener credenciales. Un worker puede ser dueño del codec, pero debe enviar al documento un estado corto como compression-complete, no todo el cuerpo.

Tratar texto, cancelación y errores

La descompresión produce bytes. Aplica TextDecoder después y elige la codificación según el contrato. UTF-8 no se deduce de una transformación exitosa. { fatal: true } permite distinguir decode-error de decompress-error, mientras los caracteres de reemplazo pueden ocultar daños.

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();
}

Para respuestas grandes, consume incrementalmente en vez de usar text +=. Un stream puede estar bloqueado, ya consumido, tener body nulo o fallar por red. Clasifica body-null, body-locked, unsupported-format, decompress-error y decode-error antes de reintentar.

La cancelación puede ocurrir antes de recibir bytes, después de una escritura parcial o porque la navegación eliminó el componente. Mantén cancelled, sink-error y owner-gone separados cuando la recuperación difiera. Abortar el pipe no borra bytes ya enviados ni copias retenidas por otro servicio.

No trates datos descomprimidos como prueba de autenticidad. Los checksums de algunos formatos detectan daños de framing, pero no son una firma de aplicación. Verifica una firma o un registro de transporte autenticado por separado y conserva ese resultado junto al estado de descompresión.

Verificar límites con un fixture

Usa cuatro filas sintéticas: compresión gzip nativa, descompresión gzip nativa, pipe abortado y bytes malformados. Repítelas con constructores disponibles, con constructores ausentes y con un cuerpo ya consumido:

CasoEstado esperadoClase a investigar
CompressionStream('gzip') recibe bytescompressed con bytesOut acotadounsupported-format o error del sink
DecompressionStream('gzip') recibe el fixturedecoded y UTF-8 válidodecompress-error o decode-error
Se activa la señal de abortocancelled y ambos propietarios cierranreader filtrado o resultado parcial
Un miembro malformado llega al descompresordecompress-errorreintento infinito o éxito vacío
Response.body es nulo o consumidobody-null o body-lockedsuposición de propiedad incorrecta

Captura browserVersion, implementation, format, bytesIn, bytesOut, limitBytes, outcome y fixtureId. Usa valores sintéticos y acotados. No incluyas cuerpo real, cuenta, endpoint ni archivo comprimido en el registro.

BotBrowser puede repetir este fixture autorizado y registrar compressed, decoded, cancelled y decompress-error. No puede controlar Content-Encoding remoto, demostrar que un proxy conservó cada byte ni certificar integridad criptográfica. Si cambia implementation, revisa detección y cabeceras; si cambia format, revisa el contrato productor; si cambia outcome a body-locked, revisa el propietario del reader.

Prueba una vez el mismo recurso mediante service worker y mediante window. Si el worker descomprime y devuelve un Response nuevo, la página no debe descomprimir otra vez. Si entrega un miembro binario opaco, la página posee la conversión. La subida y la descarga necesitan filas separadas porque comparten API pero no dueño ni recuperación.

Una alternativa puede usar una biblioteca revisada, un endpoint de servidor o una ruta sin compresión. Registra native, fallback-unavailable o server-decoded en implementation; no prometas chunking ni timing idénticos. Tras cambiar parser, cabeceras, service worker o navegador, repite las mismas filas y compara el primer campo diferente.

Public sources

Conclusión práctica

Declara el formato, asigna un único propietario a cada stream y registra por separado descompresión, decodificación, cancelación e integridad. Aplica backpressure, limita bytes y descarta un reader fallido en lugar de reintentar corrupción determinista. Repite el fixture después de cambios de navegador, servidor o service worker y decide desde el primer estado diferente.

#Compression Streams API#CompressionStream#DecompressionStream#Streams Web

Este artículo forma parte de la guía: Superficies de la plataforma web: mapa de revisión

Lleva BotBrowser de la investigación a producción

Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.