Envoyez une réponse réussie, écrivez son corps progressivement et terminez le message normalement. Ce tutoriel dans le Terminal de Mac montre comment transmettre un document synthétique complet sans traiter chaque segment comme une réponse distincte ni utiliser 206 Partial Content.

La tâche consiste à diffuser une page HTML destinée à l’hébergement public, puis à vérifier qu’un client en reçoit l’intégralité du contenu. La démonstration s’exécute localement et ne publie rien. Vous avez besoin du Terminal, de curl et d’une version LTS prise en charge de Node.js, disponible sur la page officielle de téléchargement de Node.js.

Pourquoi les segments ne signifient pas des réponses partielles

La signification d’une réponse et son mode de transmission répondent à des questions différentes. Pour une réponse ordinaire réussie GET, 200 OK indique que la récupération a réussi. 206 Partial Content signifie que le serveur répond à une requête de plage en transférant certaines portions d’une représentation. Diviser la transmission en plusieurs écritures ne crée pas une requête de plage. RFC 9110 : sémantique HTTP, 206 Partial Content.

Par exemple, un guide synthétique contenant une introduction, une description et un paragraphe de conclusion peut être transmis progressivement tout en restant un seul document. Envoyez ses en-têtes de réponse une seule fois, puis ses sections de corps dans l’ordre. N’ajoutez pas une nouvelle ligne d’état HTTP devant chaque section.

Le codage de transfert par blocs de HTTP/1.1 encadre les octets du corps avec des longueurs hexadécimales et se termine par un bloc de taille nulle suivi de la ligne vide requise. Ces octets de cadrage délimitent la transmission ; ils ne font pas partie du contenu de l’article. Un expéditeur ne doit pas associer Content-Length et Transfer-Encoding. RFC 9112 : codage de transfert par blocs, Content-Length.

En pratique, laissez la bibliothèque HTTP cadrer le message. Votre application fournit le contenu du document et signale quand celui-ci est terminé.

Quel mécanisme de diffusion en continu utiliser ?

Le mécanisme pris en charge dépend de la version HTTP. Il est possible de diffuser le corps complet dans les trois versions ci-dessous, mais leur cadrage diffère.

Version HTTP Mode de transmission du contenu du corps Mécanisme de fin
HTTP/1.1 avec codage par blocs Blocs précédés de leur longueur Bloc terminal de taille nulle et section des champs de fin terminée
HTTP/2 Trames DATA END_STREAM sur la trame finale, y compris la fin de tout bloc d’en-tête
HTTP/3 Trames DATA Fin de la réponse et fermeture du sens d’envoi du flux

HTTP/2 interdit explicitement le codage de transfert par blocs de HTTP/1.1. HTTP/3 ne définit pas de codages de transfert et interdit l’en-tête Transfer-Encoding. Laissez le serveur mettre en œuvre le cadrage de son protocole au lieu d’imposer Transfer-Encoding: chunked à chaque connexion. RFC 9113 : échanges de messages HTTP, RFC 9114 : cadrage des messages HTTP.

Pour un petit document déjà assemblé, l’envoyer normalement est généralement la solution la plus simple. Les écritures progressives sont utiles lorsque le contenu est produit au fil du temps. L’exemple ci-dessous espace volontairement trois écritures afin que vous puissiez observer le comportement ; ses délais sont illustratifs et ne constituent pas une recommandation de publication.

Démarrer un document complet en continu sur votre Mac

Ouvrez le Terminal et vérifiez les prérequis :

node --version
curl --version

Si node n’est pas disponible, installez une version LTS prise en charge depuis la page de téléchargement de Node.js, puis ouvrez une nouvelle fenêtre du Terminal. Le programme utilise uniquement des modules intégrés et ne nécessite aucun paquet npm.

Collez le bloc complet ci-dessous dans le Terminal. Il démarre un serveur HTTP/1.1 à l’adresse de démonstration http://127.0.0.1:8080/document. Les trois chaînes forment ensemble une page synthétique complète.

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

L’application appelle writeHead() une fois, write() trois fois et end() une fois. La documentation de Node décrit les écritures successives comme des parties du même corps de réponse et exige end() pour terminer chaque réponse. Node se charge du cadrage HTTP ; n’ajoutez pas de longueurs de blocs ni de zéro terminal aux chaînes. Documentation HTTP de Node.js.

Cette démonstration bornée comporte trois petites chaînes. Pour un producteur volumineux ou continu, respectez la contre-pression : lorsque write() renvoie false, suspendez la production jusqu’à drain, tout en gérant également la fermeture et les erreurs. Documentation Node.js sur l’écriture des réponses.

Laissez cette fenêtre du Terminal ouverte. Si elle indique EADDRINUSE, le port 8080 est déjà utilisé ; choisissez un autre port disponible dans le serveur et modifiez le port dans chaque commande client ci-dessous. Arrêtez la démonstration avec Control-C lorsque vous avez terminé.

Examiner le contenu décodé et le cadrage HTTP par blocs

Dans une deuxième fenêtre du Terminal, demandez le document :

curl --http1.1 --include --no-buffer --max-time 5 \
  http://127.0.0.1:8080/document

Vous devriez obtenir une seule réponse 200 OK, un Content-Type égal à text/html; charset=utf-8 et le contenu de la page assemblée. Dans cet exemple HTTP/1.1, la réponse devrait utiliser Transfer-Encoding: chunked. curl décode normalement le cadrage de transfert : sa sortie habituelle affiche donc le document plutôt que les tailles des blocs en hexadécimal.

Pour examiner le cadrage, répétez la requête avec --raw :

curl --http1.1 --include --no-buffer --raw --max-time 5 \
  http://127.0.0.1:8080/document

curl indique que --raw désactive le décodage interne du contenu et des codages de transfert. --no-buffer désactive la mise en tampon de sortie de curl, et --max-time 5 limite la durée de l’opération. Ces options en font une courte commande de diagnostic, plutôt qu’une commande qui attendrait indéfiniment. Manuel de curl.

Dans la sortie brute, des lignes de taille encadrent le contenu, puis apparaît le bloc terminal de taille nulle. Sans ce terminateur, un corps HTTP/1.1 codé par blocs est incomplet. Avec un cadrage de longueur fixe, recevoir moins d’octets que la longueur déclarée signifie que le message est incomplet. RFC 9112 : traitement des messages incomplets.

N’utilisez la sortie brute que pour examiner le transport. Ne copiez pas ses lignes de taille dans un fichier de page et ne les traitez pas comme des sections du document.

Recevoir le document entier avant de le traiter

Si l’étape suivante nécessite la page complète, consommez tout le corps avant de l’analyser, de l’enregistrer ou de l’accepter d’une autre manière. WHATWG définit le corps d’une réponse comme un flux ; sa méthode text() lit intégralement ce corps et renvoie une chaîne, en rejetant l’opération si sa consommation échoue. Recevoir les en-têtes de réponse ne signifie pas que le corps est terminé. Standard Fetch de WHATWG : mixin Body, Traitement des réponses.

Pendant que le serveur fonctionne toujours, collez ce client complet dans la deuxième fenêtre du 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 fournit une implémentation de fetch() compatible avec les navigateurs. Son AbortSignal.timeout() crée un signal qui déclenche l’abandon après le nombre de millisecondes spécifié ; ici, cinq secondes constituent une limite illustrative pour ce petit exemple local. Documentation des objets globaux de Node.js, Documentation sur le délai d’expiration d’AbortSignal.

Le client vérifie deux choses distinctes : la consommation réussie du corps et la fin attendue de l’échantillon. Cette deuxième vérification est un diagnostic propre à l’application. HTTP peut transmettre correctement un document que votre application a accidentellement raccourci avant d’appeler end(). Un marqueur de fin peut aider à détecter cette erreur précise, mais ne valide pas chaque paragraphe et ne constitue pas une validation HTML générale.

Ce client met en mémoire tampon la page entière. Pour les très gros documents, choisissez un traitement incrémental avec un format de document et des règles de fin explicites. Ne supposez pas que chaque lecture fournit un paragraphe complet ou une réponse indépendante ayant un sens.

Diagnostiquer la fin du transfert avant de modifier le code d’état

Suivez cette séquence pour localiser le problème. Elle distingue la fin du transport du contenu attendu par l’application, afin qu’un téléchargement apparemment réussi ne masque pas l’absence de la fin du document.

Observation Action suivante
La commande locale ne parvient pas à se connecter Vérifiez que le serveur fonctionne et que le port correspond.
curl expire Vérifiez que le producteur termine et appelle end() ; conservez la sortie d’erreur.
Le transfert se termine, mais la vérification de fin échoue Examinez le contenu généré et le point où l’application termine la réponse.
Le document complet arrive d’un seul coup Comparez la sortie brute et décodée ; ne déduisez pas qu’il y a plusieurs réponses à partir du moment où elles s’affichent.
Une requête normale de page complète reçoit 206 Vérifiez si la gestion des plages a été appliquée par erreur ; la diffusion progressive ne la justifie pas à elle seule.

Les vérifications de fin sont des recommandations découlant du protocole et du comportement de Fetch, et non une fonctionnalité HTTP distincte. Si la lecture du corps échoue, le client ci-dessus signale l’échec et se termine avec un code d’erreur au lieu d’afficher un message de réussite pour le contenu reçu jusque-là.

Qu’est-ce que cela implique pour une page publique et Google Search ?

Conservez le contenu utile du document dans la réponse HTML elle-même. Google explique que l’exploration et l’analyse fonctionnent bien lorsque le HTML de la réponse contient tout le contenu de la page, et recommande le rendu côté serveur ou le prérendu, car les robots ne peuvent pas tous exécuter JavaScript. L’exemple suit cette approche : son texte arrive directement dans le corps du document. Google Search Central : comprendre les bases du SEO JavaScript.

La documentation sur les robots d’exploration de Google indique qu’une réponse 200 transmet le contenu reçu à l’étape de traitement suivante ; elle ne garantit pas l’indexation. La documentation citée ne précise aucune taille de segment ni aucun délai particulier qui garantirait l’indexation d’une page. Considérez la diffusion en continu comme une méthode de transmission et vérifiez séparément que le contenu est complet. Google : comment les codes d’état HTTP influencent les robots d’exploration de Google.

Lorsque vous adaptez cette démonstration à un hébergement public, répétez les vérifications sur le véritable point de terminaison du document. Confirmez le code d’état de réussite, consommez l’intégralité du corps et comparez son contenu final avec la page attendue. Indépendamment du nombre d’écritures, de trames ou de mises à jour visibles ayant transporté le document, effectuez le contrôle d’acceptation.

Sources