Negli ultimi 20 anni il Web ha subito dei cambiamenti drastici, sotto tanti punti di vista. Tanto per snocciolare un po’ di numeri, gli abitanti del villaggio globale hanno ampiamente superato i 3 miliardi di individui, con una penetrazione pari ad oltre il 40% della popolazione mondiale.
Nel 1991, anno in cui il World Wide Web venne annunciato da Tim Berners-Lee (ma anche nel 2001, quando gli internauti sfioravano appena i 500 milioni) probabilmente nessuno ne immaginava la capillare diffusione di oggi, né tantomeno poteva avere la visione di come l’homo technologicus del 2015 avrebbe interagito con esso.
Può suonare assurdo, ma sia lo smartphone che tutti abbiamo in tasca che il browser di ultimissima generazione sul nostro PC usano ancora, per comunicare con i server Web, lo stesso protocollo HTTP/1 (e sua evoluzione HTTP/1.1) progettato agli inizi degli anni ’90. Protocollo che – intendiamoci – è stato progettato benissimo e svolge ancora egregiamente il suo lavoro, ma comincia a mostrare i segni del tempo.
Qual è il limite di HTTP/1?
Una pagina Web moderna consuma un’infinità di risorse in più rispetto al passato, e caricare tutti gli elementi che la compongono (CSS, JS, immagini eccetera) diventa difficile, perché HTTP/1 permette di soddisfare una sola richiesta per connessione. Sarebbe possibile da parte dei browser effettuare tante richieste in parallelo, ma è una pratica molto poco efficiente perché molti dei dati ricevuti in risposta sono duplicati, e si consuma inutilmente banda.
Inoltre, diversamente da altri protocolli, HTTP è stato concepito per essere stateless, ovvero: nel momento in cui il client ha ricevuto il dato, il server ha finito e da quel momento il client per lui è un perfetto estraneo. Ogni richiesta è indipendente dalle altre. Se da una parte ciò ha garantito una grandissima flessibilità, dall’altra obbliga ad aprire una connessione per ogni singolo elemento della pagina e va anche a discapito dell’interattività client-server.
Per ovviare a queste limitazioni, negli anni sono state elaborate molte soluzioni, alcune delle quali al limite dell’hacking, quali ad esempio:
- i grandi portali ad alto traffico, per garantire un minimo di concorrenza, sono stati strutturati definendo più domini su cui suddividere le richieste (ad esempio: un dominio per le pagine, uno per le immagini, uno per JS e CSS)
- sono nate le CDN per distribuire globalmente i contenuti più pesanti come immagini e video
- i webmaster sono stati costretti a minimizzare, concatenare ed ottimizzare all’estremo le risorse statiche, a volte mettendole in linea
- è stato inventato XMLHttpRequest (meglio noto come Ajax) per simulare l’interattività
In sintesi, continuare ad usare HTTP/1 ignorando che nel frattempo l’architettura degli ipertesti stessi è cambiata radicalmente significa trovarsi ad avere un overhead ingiustificato, il che si traduce in:
- sfruttare la connessione in maniera lenta ed inefficiente
- utilizzare molta più banda del necessario a discapito di altri servizi
- consumare molte più risorse hardware sia lato client che lato server
- rendere più difficile (e per certi versi inutile) il caching delle risorse
tutto questo senza considerare altri aspetti pratici come l’interattività spinta che caratterizza il cosiddetto “Web 2.0”.
HTTP/2 cambia tutto. Diamogli uno sguardo
HTTP/2 si basa su un progetto originale di Google chiamato SPDY, successivamente abbracciato ed esteso dall’HTTP Working Group dell’IETF.
A grandi linee, HTTP/2 si presenta così:
- è un protocollo binario, anziché testuale
- sfrutta il multiplexing, cioè può usare un’unica connessione per scaricare in parallelo tutte le risorse
- usa una compressione dedicata per gli header
- permette ai server di inviare risposte push ai client relative alla richiesta iniziale
Vediamo ora nel dettaglio i benefici di ogni caratteristica.
Protocollo binario
I protocolli binari, diversamente da quelli testuali, non consentono una richiesta in testo puro.
Attualmente, se vogliamo chiedere ad un server HTTP/1 di fornirci la risorsa “index.php”, basta aprire un terminale ed eseguire i seguenti comandi (le lettere C->
e S->
nel listato servono a distinguere Client e Server rispettivamente):
$ telnet 192.168.0.10 80
S-> Trying 192.168.0.10...
S-> Connected to webserver.localdomain.
S-> Escape character is '^]'.
C-> GET /index.php HTTP/1
C-> [riga vuota]
S-> HTTP/1.1 200 OK
S-> Date: Sat, 16 May 2015 16:01:15 GMT
S-> Server: Apache
S-> Content-Length: 226
S-> Connection: close
S-> Content-Type: text/html; charset=iso-8859-1
S->
S-> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
S-> <html><head>
S->
S-> (... resto del documento HTML ... )
Con HTTP/2 questo non sarà più possibile, perché richieste e risposte verranno effettuate seguendo un protocollo binario (illeggibile per un essere umano) che è molto più compatto in termini di banda, oltre che meno soggetto ad errori rispetto a quello testuale.
L’unico, ovvio svantaggio è che per effettuare un eventuale debug non si potrà più sfruttare il buon vecchio Telnet come abbiamo fatto finora, ma molti tool sono già aggiornati per supportare il nuovo protocollo. Ad esempio è già pronto un plugin per Wireshark.
Multiplexing
HTTP/1 soffre di un vizio di gioventù chiamato “head-of-line blocking”, dove sostanzialmente una singola richiesta blocca in maniera esclusiva una connessione finché non viene soddisfatta. Alcune soluzioni proposte, come il pipelining, si sono rivelate poco efficaci e comunque non danno i vantaggi sperati se non sono perfettamente supportate da eventuali server intermedi tra client e server finale (ad esempio proxy e caching server). Il client è dunque forzato ad aprire tante connessioni quanti sono gli elementi della pagina (tutto ciò senza contare le chiamate Ajax, che oggi sono all’ordine del giorno).
Il fatto che una sola applicazione apra così tante connessioni simultanee va un po’ contro i principi su cui è basato il protocollo TCP (sul quale, è bene ricordarlo, HTTP si appoggia insieme a tanti altri protocolli di alto livello): ciò amplifica il rischio di buffer overflow a layer 3, con conseguenti ritrasmissioni, congestioni eccetera: un cane che si morde la coda. Inoltre una tale inefficienza porta via tantissima banda che si potrebbe sfruttare diversamente.
HTTP/2 introduce invece il concetto di multiplexing, nel quale una sola connessione può essere sfruttata per effettuare più richieste e ricevere altrettante risposte, perfino in ordine diverso.
Un’interessante dimostrazione dei vantaggi prestazionali del multiplexing è fornita da Akamai a questa pagina (il browser deve supportare HTTP/2).
Compressione degli header
Ci si potrà chiedere “Con tutta la banda che abbiamo oggi a disposizione, perché preoccuparsi di una cosa piccola come gli header?”.
Domanda legittima, ma non tiene conto del meccanismo Slow-start tipico del protocollo TCP, che sostanzialmente limita il numero di pacchetti che possono essere scambiati nelle prime trasmissioni.
Assumiamo che una singola pagina contenga ad esempio 80 assets statici (numero conservativo nel Web di oggi), e che ogni richiesta comporti 1400 bytes di header (valore abbastanza comune, contando i cookies, i referer, eccetera). In un caso simile un tale burst di pacchetti da smaltire obbliga il TCP ad esegure 7 o 8 “viaggi” di richiesta/risposta (in gergo round-trip) prima che tutti i contenuti degli header siano effettivamente trasmessi al client. Basterebbe una compressione anche minima per ridurre il tutto ad un solo “viaggio”, forse addirittura ad un solo pacchetto. L’overhead è considerevole, soprattutto se consideriamo l’alta latenza tipica dei dispositivi mobili.
L’implementazione originale di SPDY proponeva l’uso del semplice GZIP ma è stata dimostrata una sua grave vulnerabilità di sicurezza (attacco CRIME). L’HTTP Working Group ha quindi scelto di adottare uno schema di compressione specifico e ottimizzato per gli header chiamato HPACK.
Risposte “Server Push”
Con HTTP/1 l’unico modo per un client di ricevere una risorsa da un server è… richiederla tramite una richiesta HTTP.
In HTTP/2, il server può inviare al client diverse risposte (in parallelo) in seguito ad una singola richiesta HTTP.
All’atto pratico, oggi il client effettua prima il parsing dell’HTML per poi richiedere gli asset aggiuntivi in esso contenuti (immagini, CSS, JS eccetera). Immaginiamo se invece il server li potesse inviare immediatamente al client, basandosi soltanto sulla prima richiesta! Ad esempio, ecco un ipotetico dialogo.
HTTP/1.x
Client: Ho bisogno della pagina index.html
Server: Eccola!
Client: A proposito, leggendo il codice di questa pagina mi sono accorto
che mi servono style.css, main.js, e home.jpg. Me li mandi?
Server: Eccoti style.css
Client: Grazie, ma main.js che fine ha fatto?
Server: Eccoti main.js
Client: Grazie, e home.jpg dov'è?
Server: Eccoti anche home.jpg
Da notare che il client è sempre molto beneducato.
HTTP/2
Client: Ho bisogno della pagina index.html
Server: Eccola! A proposito, eccoti anche style.css, main.js, e home.jpg.
Te li mando tutti in una volta, so che ti serviranno presto.
Client: Ah, grazie!
Una bella differenza, no?
Facendoci caso, ogni volta che abbiamo messo un JS o un CSS “in linea” nella nostra pagina web, non abbiamo fatto altro che “simulare” il Server Push di HTTP/2. Con la differenza che, per definizione, una risorsa in linea non può essere messa in cache e deve essere duplicata su tutte le pagine che ne hanno bisogno (cosa estremamente inefficiente). Al contrario, le risorse pushed vengono tranquillamente messe in cache dal browser e possono essere riutilizzate quando serve.
Conclusioni
HTTP/2, al di là degli aspetti puramente tecnici, introduce un nuovo mondo di potenzialità per tutti i professionisti del Web, e consente di sfruttare in maniera molto più efficiente il Web per come si è evoluto.
Attualmente è utilizzabile come client sulle versioni più aggiornate di Firefox, Chrome, Opera e IE11 (quest’ultimo solo su Windows 10) mentre lato server il supporto è limitato alle versioni più recenti di LiteSpeed, OpenLiteSpeed, IIS in Windows 10 e Akamai Edge Server. I grandi assenti in questo elenco sono, ovviamente, Apache ed nginx, dove però era già supportato sperimentalmente SPDY. Tutto lascia pensare che questi progetti più importanti preferiscano testare le loro implementazioni più a lungo prima di rilasciarle.
Tra le altre caratteristiche di HTTP/2 ci sono significativi miglioramenti sul lato della sicurezza che meriterebbero un articolo a parte, nonché una più efficiente gestione delle priorità. C’è da aspettarsi che l’adozione di HTTP/2 non sarà rapidissima, anche perché non è certamente semplice aggiornare tutti i webserver esistenti al mondo senza aspettarsi delle conseguenze.
Proviamolo!
Come detto, HTTP/2 è supportato dai browser più recenti e da alcuni server Web, tra cui purtroppo non figurano ancora i diffusissimi Apache ed nginx, quindi per il momento è molto improbabile trovarlo sui normali hosting condivisi. Volendo utilizzare HTTP/2 per il proprio sito web, è d’obbligo quindi utilizzare LiteSpeed o la sua implementazione open source OpenLiteSpeed.
I passaggi per testarlo sono i seguenti:
- ci serve un server dedicato o un server virtuale con già installata una distribuzione Linux, preferibilmente CentOS
- installiamo LiteSpeed a partire dalla guida ufficiale oppure OpenLiteSpeed a partire dal suo wiki
- dalla WebAdmin di LiteSpeed / OpenLiteSpeed entriamo nella voce Configuration > Listeners > SSL
- da questa schermata è possibile abilitare o disabilitare HTTP/2, SPDY/2, e SPDY/3