Skicka ett lyckat svar, skriv dess kropp fortlöpande och avsluta meddelandet normalt. Den här genomgången av Mac Terminal visar hur du levererar ett komplett syntetiskt dokument utan att behandla varje del som ett separat svar eller använda 206 Partial Content.
Uppgiften är att tillhandahålla en HTML-sida avsedd för offentlig webbhosting och sedan verifiera att klienten tar emot hela innehållet. Demonstrationen körs lokalt och publicerar ingenting. Du behöver Terminal, curl och en version av Node.js LTS som stöds från den officiella nedladdningssidan för Node.js.
Varför delar inte betyder partiella svar
Ett svars innebörd och dess leveransinramning besvarar olika frågor. För ett vanligt lyckat GET står 200 OK för en lyckad hämtning. 206 Partial Content betyder att servern uppfyller en intervallbegäran genom att överföra utvalda delar av en representation. Att dela upp leveransen i flera skrivningar skapar inte en intervallbegäran. RFC 9110: HTTP Semantics, 206 Partial Content.
Till exempel kan en syntetisk guide med en introduktion, en beskrivning och ett avslutande stycke levereras stegvis samtidigt som den förblir ett dokument. Skicka svarsrubrikerna en gång och följ dem sedan med dessa delar av innehållet i rätt ordning. Lägg inte till en ny HTTP-statusrad framför varje del.
HTTP/1.1:s chunked transfer coding omsluter kroppens byte med längder i hexadecimal form och avslutas med en chunk med storleken noll och den obligatoriska avslutande tomraden. Dessa inramningsbyte avgränsar leveransen; de är inte artikelinnehåll. En avsändare får inte kombinera Content-Length med Transfer-Encoding. RFC 9112: Chunked Transfer Coding, Content-Length.
Den praktiska regeln är att låta HTTP-biblioteket rama in meddelandet. Applikationen tillhandahåller dokumentinnehållet och signalerar när det är färdigt.
Vilken strömningsmekanism ska du använda?
Vilken mekanism som stöds beror på HTTP-versionen. Det går att strömma en komplett kropp med alla tre versionerna nedan, men deras inramning skiljer sig åt.
| HTTP-version | Hur innehållet i kroppen överförs | Mekanism för slutförande |
|---|---|---|
| HTTP/1.1 med chunked coding | Längdprefixerade delar | Avslutande chunk med storleken noll och färdig trailer-sektion |
| HTTP/2 | DATA-ramar |
END_STREAM på den avslutande ramen, inklusive slutförande av eventuellt headerblock |
| HTTP/3 | DATA-ramar |
Slutförande av svaret och stängning av strömmens sändningsriktning |
HTTP/2 förbjuder uttryckligen HTTP/1.1:s chunked transfer coding. HTTP/3 definierar inte transfer codings och förbjuder headern Transfer-Encoding. Låt servern implementera sin protokolls inramning i stället för att tvinga Transfer-Encoding: chunked på varje anslutning. RFC 9113: HTTP Message Exchanges, RFC 9114: HTTP Message Framing.
För ett litet dokument som redan har satts samman är det oftast enklast att skicka det på vanligt sätt. Fortlöpande skrivningar är användbara när innehållet skapas över tid. Exemplet nedan väntar avsiktligt mellan tre skrivningar så att du kan granska beteendet; fördröjningarna är illustrativa och inte en rekommendation för publicering.
Starta ett komplett strömmande dokument på din Mac
Öppna Terminal och kontrollera förutsättningarna:
node --version
curl --version
Om node inte är tillgängligt installerar du en version av LTS som stöds via nedladdningssidan för Node.js och öppnar sedan ett nytt Terminal-fönster. Programmet använder bara inbyggda moduler och behöver inga npm-paket.
Klistra in hela blocket nedan i Terminal. Det startar en HTTP/1.1-server på demonstrationsadressen http://127.0.0.1:8080/document. De tre strängarna utgör tillsammans en komplett syntetisk sida.
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
Applikationen anropar writeHead() en gång, write() tre gånger och end() en gång. Node beskriver successiva skrivningar som delar av samma svarskropp och kräver end() för att slutföra varje svar. Node hanterar HTTP-inramningen; lägg inte till chunk-längder eller en avslutande nolla i strängarna. Node.js HTTP-dokumentation.
Det här är en avgränsad demonstration med tre små strängar. För en stor eller kontinuerlig producent ska du hantera backpressure: när write() returnerar false pausar du produktionen tills drain, samtidigt som du hanterar stängning och fel. Node.js-dokumentation om svarsskrivning.
Låt det här Terminal-fönstret vara öppet. Om det rapporterar EADDRINUSE är port 8080 upptagen; välj en annan tillgänglig port i servern och ändra porten i varje klientkommando nedan. Stoppa demonstrationen med Control-C när du är klar.
Granska både avkodat innehåll och HTTP-chunk-inramning
Begär dokumentet i ett andra Terminal-fönster:
curl --http1.1 --include --no-buffer --max-time 5 \
http://127.0.0.1:8080/document
Förvänta dig ett enda 200 OK-svar, en Content-Type på text/html; charset=utf-8 och den sammansatta sidans innehåll. I det här HTTP/1.1-exemplet ska svaret använda Transfer-Encoding: chunked. Curl avkodar normalt överföringsinramningen, så den vanliga utdata visar dokumentet i stället för hexadecimala chunk-storlekar.
Upprepa begäran med --raw för att granska inramningen:
curl --http1.1 --include --no-buffer --raw --max-time 5 \
http://127.0.0.1:8080/document
Curl-dokumentationen beskriver --raw som ett alternativ som inaktiverar intern avkodning av innehåll och överföringskodningar. --no-buffer inaktiverar curl-utdatans buffring, och --max-time 5 begränsar körningen. Alternativen gör detta till ett kort diagnostiskt kommando i stället för ett kommando som väntar på obestämd tid. curl-manualen.
I råutdata omsluter storleksraderna innehållet, följt av den avslutande chunken med storleken noll. Utan den avslutande chunken är en chunkad HTTP/1.1-kropp ofullständig. Med fastlängdsinramning är det ofullständigt om färre byte tas emot än den angivna längden. RFC 9112: Hantering av ofullständiga meddelanden.
Använd råutdata endast för att granska transporten. Kopiera inte storleksraderna till en sidfil och behandla dem inte som dokumentavsnitt.
Ta emot hela dokumentet innan du bearbetar det
Om nästa steg kräver hela sidan ska du läsa hela kroppen innan du tolkar, sparar eller på annat sätt godkänner den. WHATWG definierar en svarskropp som en ström; metoden text() läser hela kroppen och returnerar en sträng, men misslyckas om konsumtionen av kroppen misslyckas. Att ta emot svarsrubrikerna innebär inte att kroppen är färdig. WHATWG Fetch Standard: Body mixin, Bearbeta svar.
Klistra in hela den här klienten i det andra Terminal-fönstret medan servern fortfarande kör:
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 har en fetch()-implementation som är kompatibel med webbläsare. Dess AbortSignal.timeout() skapar en signal som avbryter efter det angivna antalet millisekunder; här är fem sekunder en illustrativ gräns för det här lilla lokala exemplet. Node.js-dokumentation om globala objekt, Dokumentation om AbortSignal-timeout.
Klienten kontrollerar två olika saker: att kroppen har lästs in utan fel och att den förväntade exempeltexten finns i slutet. Den andra kontrollen är en applikationsspecifik diagnostik. HTTP kan överföra ett dokument utan problem även om applikationen av misstag förkortade det innan den anropade end(). En slutmarkör hjälper till att upptäcka just det misstaget, men validerar inte varje stycke och utgör ingen allmän HTML-validering.
Den här klienten buffrar hela sidan i minnet. För mycket stora dokument väljer du inkrementell bearbetning med ett uttryckligt dokumentformat och regler för slutförande. Utgå inte från att varje läsning levererar ett komplett stycke eller ett svar med självständig betydelse.
Diagnostisera slutförandet innan du ändrar statuskoden
Använd den här ordningen för att hitta problemet. Den skiljer transportens slutförande från applikationens avsedda innehåll, så att en till synes lyckad nedladdning inte döljer ett utelämnat slut.
| Observation | Nästa åtgärd |
|---|---|
| Det lokala kommandot kan inte ansluta | Kontrollera att servern körs och att porten stämmer. |
| Curl får timeout | Kontrollera att producenten blir klar och anropar end(); behåll felutdata. |
| Överföringen blir klar men slutkontrollen misslyckas | Granska det genererade innehållet och var i applikationen svaret avslutas. |
| Hela dokumentet anländer på en gång | Jämför rå och avkodad utdata; dra inte slutsatsen att det kom flera svar utifrån när det visas. |
En vanlig begäran om en hel sida får 206 |
Kontrollera om intervallhantering tillämpades av misstag; fortlöpande leverans i sig motiverar inte detta. |
Slutförandekontrollerna är rekommendationer som bygger på protokollet och Fetch-beteendet, inte en separat HTTP-funktion. Om läsningen av kroppen misslyckas rapporterar klienten ovan ett fel och avslutas med en felstatus, i stället för att skriva ut ett framgångsmeddelande för det innehåll som hittills tagits emot.
Vad innebär detta för en offentlig sida och Google Search?
Låt det användbara dokumentinnehållet finnas i själva HTML-svaret. Google förklarar att genomsökning och tolkning fungerar bra för sidor vars HTML-svar innehåller allt innehåll och rekommenderar rendering på serversidan eller förhandsrendering eftersom inte alla botar kör JavaScript. Exemplet följer den metoden: texten levereras direkt i dokumentkroppen. Google Search Central: Förstå grunderna i SEO för JavaScript.
Googles dokumentation om sökrobotar säger att ett svar med statusen 200 skickar mottaget innehåll vidare till nästa bearbetningssteg; det garanterar inte indexering. Den citerade dokumentationen anger ingen särskild chunk-storlek eller fördröjning som garanterar att en sida indexeras. Se strömning som en implementering av leveransen och verifiera det färdiga innehållet separat. Google: Hur HTTP-statuskoder påverkar Googles sökrobotar.
När du anpassar demonstrationen för offentlig webbhosting upprepar du kontrollerna mot dokumentets faktiska slutpunkt. Bekräfta att statusen anger framgång, läs in hela kroppen och jämför dess slutliga innehåll med den avsedda sidan. Låt godkännandekontrollen vara oberoende av hur många skrivningar, ramar eller synliga uppdateringar som levererade dokumentet.
Källor
- RFC 9110: HTTP Semantics — RFC Editor / IETF.
- RFC 9112: HTTP/1.1 — RFC Editor / IETF.
- RFC 9113: HTTP/2 — RFC Editor / IETF.
- RFC 9114: HTTP/3 — RFC Editor / IETF.
- Fetch Standard — WHATWG.
- HTTP — Node.js.
- Global objects — Node.js.
- Download Node.js — Node.js.
- curl — How To Use — curl.
- Understand the JavaScript SEO basics — Google Search Central.
- How HTTP status codes affect Google's crawlers — Google for Developers.