Envía una única respuesta satisfactoria, escribe su cuerpo progresivamente y termina el mensaje con normalidad. Este recorrido por Terminal en Mac muestra cómo entregar un documento sintético completo sin tratar cada fragmento como una respuesta independiente ni usar 206 Partial Content.
La tarea consiste en servir una página HTML destinada al alojamiento público y verificar que el cliente recibe todo su contenido. La demostración se ejecuta localmente y no publica nada. Necesitas Terminal, curl y una versión LTS compatible de Node.js, que puedes obtener en la página oficial de descargas de Node.js.
Por qué los fragmentos no significan respuestas parciales
El significado de una respuesta y el encuadre de su entrega responden a preguntas distintas. Para un GET satisfactorio ordinario, 200 OK indica que la recuperación se realizó correctamente. 206 Partial Content significa que el servidor está atendiendo una solicitud de rango al transferir partes seleccionadas de una representación. Dividir la entrega en varias escrituras no crea una solicitud de rango. RFC 9110: semántica HTTP, 206 Partial Content.
Por ejemplo, una guía sintética con una introducción, una descripción y un párrafo final puede llegar progresivamente y seguir siendo un único documento. Envía sus encabezados de respuesta una sola vez y, después, las secciones del cuerpo en orden. No antepongas una nueva línea de estado HTTP a cada sección.
La codificación de transferencia fragmentada de HTTP/1.1 envuelve los bytes del cuerpo con longitudes hexadecimales y termina con un fragmento de tamaño cero seguido de la línea vacía obligatoria. Esos bytes de encuadre delimitan la entrega; no forman parte del contenido del artículo. El emisor no debe combinar Content-Length con Transfer-Encoding. RFC 9112: codificación de transferencia fragmentada, Content-Length.
La regla práctica es dejar que la biblioteca HTTP encuadre el mensaje. Tu aplicación proporciona el contenido del documento e indica cuándo ha terminado.
¿Qué mecanismo de transmisión progresiva debes usar?
El mecanismo compatible depende de la versión de HTTP. Es posible transmitir progresivamente un cuerpo completo en las tres versiones siguientes, pero el encuadre es diferente en cada una.
| Versión de HTTP | Cómo se transmite el contenido del cuerpo | Mecanismo de finalización |
|---|---|---|
| HTTP/1.1 con codificación fragmentada | Fragmentos con longitud prefijada | Fragmento terminal de tamaño cero y sección de tráileres completa |
| HTTP/2 | Tramas DATA |
END_STREAM en la trama final, incluida la finalización de cualquier bloque de encabezados |
| HTTP/3 | Tramas DATA |
Finalización de la respuesta y cierre de la dirección de envío del flujo |
HTTP/2 prohíbe explícitamente la codificación de transferencia fragmentada de HTTP/1.1. HTTP/3 no define codificaciones de transferencia y prohíbe el encabezado Transfer-Encoding. Deja que el servidor implemente el encuadre de su protocolo en vez de forzar Transfer-Encoding: chunked en todas las conexiones. RFC 9113: intercambios de mensajes HTTP, RFC 9114: encuadre de mensajes HTTP.
Para un documento pequeño que ya está ensamblado, lo más sencillo suele ser enviarlo con normalidad. Las escrituras progresivas resultan útiles cuando el contenido se genera con el tiempo. El ejemplo siguiente separa deliberadamente tres escrituras para que puedas observar el comportamiento; las demoras son ilustrativas, no una recomendación de publicación.
Inicia en tu Mac la transmisión progresiva de un documento completo
Abre Terminal y comprueba los requisitos previos:
node --version
curl --version
Si node no está disponible, instala una versión LTS compatible desde la página de descargas de Node.js y, después, abre una nueva ventana de Terminal. El programa solo usa módulos integrados y no necesita paquetes de npm.
Pega el bloque completo de abajo en Terminal. Inicia un servidor HTTP/1.1 en la dirección de demostración http://127.0.0.1:8080/document. Las tres cadenas forman juntas una página sintética completa.
node <<'JS'
const http = require('node:http');
const parts = [
'<!doctype html>\n<html lang="en"><head><meta charset="utf-8">' +
'<title>Paper Shapes</title></head><body><main>\n' +
'<h1>Paper Shapes</h1>\n',
'<p>A circle, a square, and a triangle make this sample collection.</p>\n',
'<p>End of the paper shapes guide.</p>\n</main></body></html>\n'
];
const server = http.createServer((req, res) => {
res.on('error', error => console.error(error.message));
if (req.method !== 'GET' || req.url !== '/document') {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('No sample document at this path.\n');
return;
}
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
let index = 0;
const timer = setInterval(() => {
if (res.destroyed) {
clearInterval(timer);
return;
}
res.write(parts[index++]);
if (index === parts.length) {
clearInterval(timer);
res.end();
}
}, 300);
res.on('close', () => clearInterval(timer));
});
server.on('error', error => {
console.error(error.message);
process.exitCode = 1;
});
server.listen(8080, '127.0.0.1', () => {
console.log('Sample: http://127.0.0.1:8080/document');
});
JS
La aplicación llama a writeHead() una vez, a write() tres veces y a end() una vez. Node describe las escrituras sucesivas como partes del mismo cuerpo de respuesta y exige end() para completar cada respuesta. Node se encarga del encuadre HTTP; no añadas longitudes de fragmentos ni un cero terminal a las cadenas. Documentación de HTTP de Node.js.
Esta es una demostración acotada con tres cadenas pequeñas. Para un productor grande o continuo, respeta la contrapresión: cuando write() devuelve false, pausa la producción hasta drain y gestiona también los cierres y los errores. Documentación de escritura de respuestas de Node.js.
Deja abierta esta ventana de Terminal mientras se ejecuta. Si informa EADDRINUSE, el puerto 8080 está ocupado; elige otro puerto disponible en el servidor y cambia el puerto en todos los comandos de cliente de abajo. Cuando termines, detén la demostración con Control-C.
Examina tanto el contenido decodificado como el encuadre HTTP de los fragmentos
En una segunda ventana de Terminal, solicita el documento:
curl --http1.1 --include --no-buffer --max-time 5 \
http://127.0.0.1:8080/document
Deberías recibir una única respuesta 200 OK, un Content-Type de text/html; charset=utf-8 y el contenido ensamblado de la página. En este ejemplo de HTTP/1.1, la respuesta debe usar Transfer-Encoding: chunked. Normalmente, curl decodifica el encuadre de transferencia, por lo que su salida habitual muestra el documento, no los tamaños hexadecimales de los fragmentos.
Para examinar el encuadre, repite la solicitud con --raw:
curl --http1.1 --include --no-buffer --raw --max-time 5 \
http://127.0.0.1:8080/document
La documentación de curl indica que --raw desactiva la decodificación interna del contenido y de las codificaciones de transferencia. --no-buffer desactiva el almacenamiento en búfer de la salida de curl, y --max-time 5 limita la duración de la operación. Estas opciones convierten el comando en un diagnóstico breve en lugar de uno que espera indefinidamente. Manual de curl.
En la salida sin procesar, hay líneas de tamaño alrededor del contenido, seguidas del fragmento terminal de tamaño cero. Sin ese terminador, un cuerpo HTTP/1.1 fragmentado está incompleto. Con encuadre de longitud fija, recibir menos bytes que la longitud declarada también significa que está incompleto. RFC 9112: gestión de mensajes incompletos.
Usa la salida sin procesar solo para examinar el transporte. No copies sus líneas de tamaño en un archivo de página ni las trates como secciones del documento.
Recibe el documento completo antes de procesarlo
Si el siguiente paso necesita la página completa, consume todo el cuerpo antes de analizarlo, guardarlo o aceptarlo de otro modo. WHATWG define el cuerpo de una respuesta como un flujo; su método text() lo lee por completo y devuelve una cadena, y falla si se produce un error al consumir el cuerpo. Recibir solo los encabezados de respuesta no demuestra que el cuerpo haya terminado. Estándar Fetch de WHATWG: mezcla Body, Procesamiento de respuestas.
Con el servidor aún en ejecución, pega este cliente completo en la segunda ventana de Terminal:
node --input-type=module <<'JS'
try {
const response = await fetch('http://127.0.0.1:8080/document', {
signal: AbortSignal.timeout(5000)
});
if (response.status !== 200) {
throw new Error(`Expected 200; received ${response.status}`);
}
const html = await response.text();
const marker = 'End of the paper shapes guide.';
if (!html.includes(marker) || !html.endsWith('</html>\n')) {
throw new Error('Expected sample ending is missing');
}
console.log(html);
console.log('Complete sample document received.');
} catch (error) {
console.error('Document not accepted:', error.message);
process.exitCode = 1;
}
JS
Node proporciona una implementación de fetch() compatible con los navegadores. Su método AbortSignal.timeout() crea una señal que se cancela después del número de milisegundos especificado; aquí, cinco segundos es un límite ilustrativo para este pequeño ejemplo local. Documentación de objetos globales de Node.js, Documentación del tiempo de espera de AbortSignal.
El cliente comprueba dos cosas distintas: que el consumo del cuerpo se haya completado correctamente y que el final esperado de la muestra esté presente. La segunda comprobación es un diagnóstico específico de la aplicación. HTTP puede transferir correctamente un documento que tu aplicación haya acortado por error antes de llamar a end(). Un marcador de finalización ayuda a detectar ese error concreto, pero no valida todos los párrafos ni constituye una validación general de HTML.
Este cliente almacena en memoria toda la página. Para documentos muy grandes, elige un procesamiento incremental con un formato de documento explícito y reglas de finalización. No des por sentado que cada lectura entrega un párrafo completo o una respuesta con significado independiente.
Diagnostica la finalización antes de cambiar el código de estado
Sigue esta secuencia para localizar el problema. Separa la finalización del transporte del contenido que la aplicación espera, para que una descarga aparentemente correcta no oculte un final omitido.
| Observación | Siguiente paso |
|---|---|
| El comando local no logra conectarse | Confirma que el servidor esté en ejecución y que el puerto coincida. |
| curl agota el tiempo de espera | Comprueba que el productor termine y llame a end(); conserva la salida del error. |
| La transferencia termina, pero falla la comprobación del final | Inspecciona el contenido generado y el punto en que la aplicación finaliza la respuesta. |
| Llega el documento completo de una sola vez | Compara la salida sin procesar con la decodificada; no deduzcas que hubo varias respuestas por el momento en que se muestra el contenido. |
Una solicitud normal de página completa recibe 206 |
Comprueba si se aplicó el manejo de rangos sin querer; la entrega progresiva por sí sola no lo justifica. |
Las comprobaciones de finalización son recomendaciones derivadas del protocolo y del comportamiento de Fetch, no una función HTTP aparte. Si falla la operación de lectura del cuerpo, el cliente anterior informa del fallo y termina con un código de salida distinto de cero, en vez de mostrar un mensaje de éxito para el contenido recibido hasta ese momento.
¿Qué significa esto para una página pública y Google Search?
Incluye el contenido útil del documento en la propia respuesta HTML. Google explica que el rastreo y el análisis funcionan bien en páginas cuya respuesta HTML contiene todo su contenido, y recomienda la representación del lado del servidor o el prerenderizado porque no todos los bots ejecutan JavaScript. La muestra sigue ese enfoque: el texto llega directamente en el cuerpo del documento. Google Search Central: conceptos básicos de SEO para JavaScript.
La documentación del rastreador de Google indica que una respuesta 200 pasa el contenido recibido a la siguiente etapa de procesamiento; eso no garantiza que se indexe. La documentación citada no especifica ningún tamaño de fragmento ni demora que garantice la indexación de una página. Considera la transmisión progresiva una implementación de entrega y verifica por separado que el contenido esté completo. Google: cómo afectan los códigos de estado HTTP a los rastreadores de Google.
Al adaptar esta demostración para el alojamiento público, repite las comprobaciones en el punto de acceso real del documento. Confirma el código de estado satisfactorio, consume todo el cuerpo y compara el contenido final con el de la página prevista. Mantén la comprobación de aceptación independiente del número de escrituras, tramas o actualizaciones visibles que hayan transportado el documento.
Fuentes
- RFC 9110: semántica HTTP — Editor del RFC / IETF.
- RFC 9112: HTTP/1.1 — Editor del RFC / IETF.
- RFC 9113: HTTP/2 — Editor del RFC / IETF.
- RFC 9114: HTTP/3 — Editor del RFC / IETF.
- Estándar Fetch — WHATWG.
- HTTP — Node.js.
- Objetos globales — Node.js.
- Descargar Node.js — Node.js.
- curl: cómo usarlo — curl.
- Conceptos básicos de SEO para JavaScript — Google Search Central.
- Cómo afectan los códigos de estado HTTP a los rastreadores de Google — Google for Developers.