HTTP: costruisci la richiesta, leggi la risposta

HTTP è il protocollo che stai usando proprio adesso per leggere questa pagina, ed è fatto di testo: si può scrivere a mano. Qui ci sono quattro giochi — monta una richiesta e guarda il server rifiutarla, indovina il codice giusto, decidi se la richiesta parte davvero, e conta i giri di andata e ritorno che separano HTTP/1.0 da HTTP/2.

Quando il browser chiede una pagina, manda al server un pezzo di testo, fatto così. Qui lo rimonti tu, riga per riga — e il server risponde come risponderebbe uno vero: gli errori che ottieni sono errori veri.

Com'è fatta una richiesta: quattro parti, in quest'ordine
riga di richiesta GET /pagina.html HTTP/1.1 cosa vuoi, di preciso — una sola
intestazione Host: esempio.it quante ne servono, una per riga
intestazione Accept: text/html
riga vuota (qui non c'è scritto niente) obbligatoria: dice «le intestazioni finiscono qui»
corpo nome=Ada&voto=10 solo se stai mandando dei dati

Obiettivo: comporre una richiesta che il server accetti con 200 OK. Non servono tutti i pezzi qui sotto — servono quelli giusti, nell'ordine giusto. Tre bastano.

Quello che stai per spedire

Come funziona

Anatomia di una richiesta

Una richiesta HTTP è testo, e ha quattro parti in ordine fisso: la riga di richiesta (metodo, percorso, versione), le intestazioni (una per riga, Nome: valore), una riga vuota, e infine il corpo — che c'è solo se serve.

La riga vuota non è estetica: è il delimitatore che dice al server «le intestazioni finiscono qui». Senza quella riga il server continua ad aspettare intestazioni che non arrivano mai, e la connessione muore di timeout. È l'errore più istruttivo del primo gioco, ed è la ragione per cui i protocolli testuali hanno bisogno di un modo esplicito per dire «ho finito».

E Host: è obbligatoria in HTTP/1.1 per una ragione molto concreta: su uno stesso indirizzo IP vivono centinaia di siti diversi. Senza quella riga il server ha ricevuto il pacchetto ma non sa quale sito gli stai chiedendo. È l'intestazione che ha reso possibile l'hosting condiviso — e infatti è arrivata con la 1.1, non c'era nella 1.0.

I metodi, e due proprietà che li classificano

MetodoCosa faSicuroIdempotente
GETchiedi una risorsa
HEADsolo le intestazioni, niente corpo
POSTmanda dati, crea qualcosanono
PUTsostituisci la risorsano
DELETEcancella la risorsano

Sicuro vuol dire che non modifica niente. Idempotente vuol dire che ripeterlo dà lo stesso risultato di farlo una volta sola: cancellare due volte lo stesso file lascia lo stesso mondo. Ed è per questo che il browser ti chiede «vuoi rimandare il modulo?» quando ricarichi una pagina dopo un POST: quello, idempotente non è, e rifarlo comprerebbe due volte.

I codici: la prima cifra è già una risposta

FasciaSignificatoDi chi è la colpaHa senso ritentare?
1xxinformativo, aspetta
2xxfatto
3xxè altrove, o ce l'hai giàsegui l'indicazione
4xxhai sbagliato tudel clientno, non così
5xxho sbagliato iodel serversì, fra poco

La cache: la richiesta che non parte è la più veloce

Ci sono due meccanismi, e il secondo gioco serve a non confonderli.

Cache con scadenza — non parte proprio niente
Il server dichiara Cache-Control: max-age=3600: per un'ora il browser usa la copia locale senza contattare nessuno. Zero pacchetti, zero attesa.
Richiesta condizionale — parte poco, torna quasi niente
Scaduto il tempo, il browser non riscarica alla cieca: allega l'impronta che possiede (If-None-Match: "v3-8f2c") e chiede se è cambiato. Se non è cambiato torna 304 Not Modified, con il corpo vuoto.

Il guadagno si conta: per un'immagine da 500 kB rimasta uguale, un 304 pesa circa 200 byte contro 500.000. Si risparmia il 99,96% del traffico — al prezzo di un giro di andata e ritorno, che con la scadenza invece non si paga affatto.

Le versioni, e la matematica dei giri

È il calcolo più efficace di tutto il capitolo, ed è quello del quarto gioco. Con 30 risorse, RTT 40 ms e 6 connessioni parallele:

VersioneConteggio dei giriTotaleTempo
1.0, una connessione alla volta30 × (1 + 1)60 RTT2400 ms
1.0, 6 connessioni parallele⌈30/6⌉ × 210 RTT400 ms
1.1 keep-alive, 6 connessioni1 + ⌈30/6⌉6 RTT240 ms
2, una connessione multiplata1 + 12 RTT80 ms

Trenta volte più veloce, senza che la banda c'entri niente: è tutto tempo di attesa, non di trasferimento. È la dimostrazione più netta che sulla rete moderna il collo di bottiglia è quasi sempre la latenza, non la velocità della linea. Comprare una linea più veloce non sposta questi numeri di un millisecondo.

Il salto da 1.1 a 2 elimina il blocco in testa alla coda: in HTTP/1.1 le risposte devono arrivare nell'ordine delle richieste, quindi una risorsa lenta blocca tutte quelle dietro. HTTP/2 spezza tutto in frame etichettati, che possono arrivare mescolati. Ne resta però uno più sottile: se si perde un solo segmento TCP, TCP blocca la consegna di tutti i flussi finché non l'ha recuperato — perché TCP vede un flusso di byte e non sa niente dei flussi HTTP che ci viaggiano dentro. È esattamente il problema che HTTP/3 risolve abbandonando TCP per QUIC.

HTTP non ha memoria

Ogni richiesta è un foglio bianco: il server non ricorda nulla della precedente. Se resti «loggato» è perché il browser riallega ogni volta un cookie che il server gli ha dato — non perché qualcuno si ricordi di te. Tutto ciò che sembra continuità, in HTTP, è un'informazione rispedita ogni singola volta.

HTTP è il contenuto della busta più interna: per vedere le altre tre che gli si chiudono intorno, apri Viaggio nel TCP/IP. La porta 80 e la 443 sono spiegate in Le porte e la quadrupla, e il nome del sito diventa un indirizzo in Dal nome all'indirizzo.

Tutoring Premium

Ripetizioni e tutoring con un docente di ruolo

Matematica, fisica e informatica. Un metodo, non solo esercizi: si parte da come funziona.

Prenota il colloquio gratuito Senza impegno · Posti limitati