Envie uma única resposta bem-sucedida, escreva o corpo progressivamente e finalize a mensagem normalmente. Este passo a passo no Terminal do Mac mostra como entregar um documento sintético completo sem tratar cada parte como uma resposta separada nem usar 206 Partial Content.

A tarefa consiste em disponibilizar uma página HTML destinada à hospedagem pública e, em seguida, verificar se o cliente recebe todo o conteúdo. A demonstração é executada localmente e não publica nada. Você precisa do Terminal, do curl e de uma versão LTS compatível do Node.js, disponível na página oficial de download do Node.js.

Por que partes não significam respostas parciais

O significado de uma resposta e o enquadramento de sua entrega respondem a perguntas diferentes. Para um GET comum, 200 OK indica uma recuperação bem-sucedida. 206 Partial Content significa que o servidor está atendendo a uma solicitação de intervalo, transferindo partes selecionadas de uma representação. Dividir a entrega em várias gravações não cria uma solicitação de intervalo. RFC 9110: Semântica HTTP, 206 Partial Content.

Por exemplo, um guia sintético com uma introdução, uma descrição e um parágrafo final pode chegar progressivamente e ainda assim continuar sendo um único documento. Envie os cabeçalhos da resposta uma vez, seguidos dessas seções do corpo em ordem. Não acrescente uma nova linha de status HTTP a cada seção.

A codificação de transferência em partes do HTTP/1.1 envolve os bytes do corpo com comprimentos em hexadecimal e termina com uma parte de tamanho zero e a linha vazia obrigatória de encerramento. Esses bytes de enquadramento delimitam a entrega; não fazem parte do conteúdo do artigo. O remetente não deve combinar Content-Length com Transfer-Encoding. RFC 9112: Codificação de transferência em partes, Content-Length.

A regra prática é deixar a biblioteca HTTP enquadrar a mensagem. Seu aplicativo fornece o conteúdo do documento e sinaliza quando ele terminou.

Qual mecanismo de transmissão em fluxo você deve usar?

O mecanismo compatível depende da versão do HTTP. É possível transmitir um corpo completo em fluxo em todas as três versões abaixo, mas o enquadramento difere entre elas.

Versão HTTP Como o conteúdo do corpo é transmitido Mecanismo de conclusão
HTTP/1.1 com codificação em partes Partes com comprimento prefixado Parte terminal de tamanho zero e seção de trailers concluída
HTTP/2 Quadros DATA END_STREAM no quadro final, incluindo a conclusão de qualquer bloco de cabeçalhos
HTTP/3 Quadros DATA Conclusão da resposta e fechamento da direção de envio do fluxo

O HTTP/2 proíbe explicitamente a codificação de transferência em partes do HTTP/1.1. O HTTP/3 não define codificações de transferência e proíbe o cabeçalho Transfer-Encoding. Deixe o servidor implementar o enquadramento do seu protocolo em vez de forçar Transfer-Encoding: chunked em todas as conexões. RFC 9113: Trocas de mensagens HTTP, RFC 9114: Enquadramento de mensagens HTTP.

Para um documento pequeno já montado, enviá-lo normalmente costuma ser a implementação mais simples. Gravações progressivas são úteis quando o conteúdo é produzido ao longo do tempo. O exemplo abaixo espaça deliberadamente três gravações para que você possa observar o comportamento; os atrasos são ilustrativos, não uma recomendação de publicação.

Inicie no seu Mac um documento completo transmitido em fluxo

Abra o Terminal e confira os pré-requisitos:

node --version
curl --version

Se node não estiver disponível, instale uma versão LTS compatível usando a página de download do Node.js e abra uma nova janela do Terminal. O programa usa apenas módulos integrados e não requer pacotes npm.

Cole o bloco completo abaixo no Terminal. Ele inicia um servidor HTTP/1.1 no endereço de demonstração http://127.0.0.1:8080/document. As três strings juntas formam uma única 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

O aplicativo chama writeHead() uma vez, write() três vezes e end() uma vez. A documentação do Node descreve gravações sucessivas como partes do mesmo corpo de resposta e exige end() para concluir cada resposta. O Node cuida do enquadramento HTTP; não adicione comprimentos de partes nem um zero terminal às strings. Documentação HTTP do Node.js.

Esta é uma demonstração limitada com três strings pequenas. Para um produtor de grande porte ou contínuo, respeite a contrapressão: quando write() retornar false, pause a produção até drain, tratando também o fechamento e os erros. Documentação do Node.js sobre gravação de respostas.

Deixe esta janela do Terminal aberta. Se ela informar EADDRINUSE, a porta 8080 está ocupada; escolha outra porta disponível no servidor e altere a porta em cada comando do cliente abaixo. Ao terminar, encerre a demonstração com Control-C.

Inspecione o conteúdo decodificado e o enquadramento das partes HTTP

Em uma segunda janela do Terminal, solicite o documento:

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

Espere uma resposta 200 OK, um Content-Type de text/html; charset=utf-8 e o conteúdo da página montada. Neste exemplo de HTTP/1.1, a resposta deve usar Transfer-Encoding: chunked. Normalmente, o curl decodifica o enquadramento da transferência, então sua saída comum mostra o documento, e não os tamanhos das partes em hexadecimal.

Para inspecionar o enquadramento, repita a solicitação com --raw:

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

A documentação do curl explica que --raw desativa a decodificação interna do conteúdo e das codificações de transferência. --no-buffer desativa o buffer de saída do curl, e --max-time 5 limita a duração da operação. Essas opções fazem deste um comando curto de diagnóstico, em vez de um comando que espera indefinidamente. Manual do curl.

Na saída bruta, linhas de tamanho delimitam o conteúdo, seguidas pela parte terminal de tamanho zero. Sem esse terminador, um corpo HTTP/1.1 em partes está incompleto. Com enquadramento de comprimento fixo, receber menos bytes do que o comprimento declarado também significa que a mensagem está incompleta. RFC 9112: Tratamento de mensagens incompletas.

Use a saída bruta apenas para examinar o transporte. Não copie suas linhas de tamanho para um arquivo de página nem as trate como seções do documento.

Receba o documento inteiro antes de processá-lo

Se a etapa seguinte exigir a página completa, consuma todo o corpo antes de analisá-lo, salvá-lo ou aceitá-lo de outra forma. O WHATWG define o corpo de uma resposta como um fluxo; seu método text() lê o corpo por completo e retorna uma string, rejeitando a operação se o consumo do corpo falhar. Receber apenas os cabeçalhos da resposta não confirma que o corpo terminou. Padrão Fetch do WHATWG: mixin Body, Processamento de respostas.

Com o servidor ainda em execução, cole este cliente completo na segunda janela do 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

O Node fornece uma implementação de fetch() compatível com navegadores. Seu método AbortSignal.timeout() cria um sinal que aborta após o número especificado de milissegundos; aqui, cinco segundos é um limite ilustrativo para este pequeno exemplo local. Documentação dos objetos globais do Node.js, Documentação sobre o tempo limite de AbortSignal.

O cliente verifica duas coisas diferentes: o consumo bem-sucedido do corpo e o final esperado da amostra. Essa segunda verificação é um diagnóstico específico do aplicativo. O HTTP pode transferir um documento com sucesso mesmo que o aplicativo o tenha encurtado acidentalmente antes de chamar end(). Um marcador de conclusão ajuda a detectar esse erro específico, mas não valida todos os parágrafos nem constitui uma validação geral de HTML.

Este cliente armazena a página inteira na memória. Para documentos muito grandes, escolha um processamento incremental com um formato de documento explícito e regras de conclusão. Não presuma que cada leitura entrega um parágrafo completo ou uma resposta com significado independente.

Diagnostique a conclusão antes de alterar o código de status

Use esta sequência para localizar o problema. Ela separa a conclusão do transporte do conteúdo esperado pelo aplicativo, para que um download aparentemente bem-sucedido não esconda a ausência do trecho final.

Observação Próxima ação
Não é possível conectar usando o comando local Confirme que o servidor está em execução e que a porta corresponde.
O curl atinge o tempo limite Verifique se o produtor termina e chama end(); mantenha a saída de erro.
A transferência termina, mas a verificação do final falha Inspecione o conteúdo gerado e o ponto em que o aplicativo encerra a resposta.
O documento completo chega de uma só vez Compare a saída bruta e a decodificada; não deduza que houve várias respostas com base no tempo de exibição.
Uma solicitação normal da página completa recebe 206 Verifique se o tratamento de intervalos foi aplicado sem querer; a entrega progressiva, por si só, não justifica esse código.

As verificações de conclusão são recomendações derivadas do protocolo e do comportamento do Fetch, e não um recurso HTTP separado. Se a operação de leitura do corpo falhar, o cliente acima informa a falha e termina com erro, em vez de imprimir uma mensagem de sucesso para o conteúdo recebido até aquele momento.

O que isso significa para uma página pública e a Pesquisa Google?

Mantenha o conteúdo útil do documento na própria resposta HTML. O Google explica que o rastreamento e a análise funcionam bem quando o HTML da resposta contém todo o conteúdo da página e recomenda a renderização no servidor ou a pré-renderização, pois nem todo bot executa JavaScript. O exemplo segue essa abordagem: o texto chega diretamente no corpo do documento. Google Search Central: Entenda os conceitos básicos de SEO para JavaScript.

A documentação dos rastreadores do Google informa que uma resposta 200 encaminha o conteúdo recebido para a próxima etapa de processamento; isso não garante a indexação. A documentação citada não especifica um tamanho de parte ou um atraso especial que garanta a indexação de uma página. Trate a transmissão em fluxo como uma implementação de entrega e verifique o conteúdo final separadamente. Google: Como os códigos de status HTTP afetam os rastreadores do Google.

Ao adaptar esta demonstração para hospedagem pública, repita as verificações no endpoint real do documento. Confirme o status de sucesso, consuma todo o corpo e compare o conteúdo final com a página desejada. Mantenha a verificação de aceitação independente do número de gravações, quadros ou atualizações visíveis usados para entregar o documento.

Fontes