Jsou to již téměř 2 roky od vydání HTTP protokolu ve verzi HTTP/2 (RFC7540). Jedná se o první novou verzi protokolu od roku 1997, kdy byl standardizován HTTP 1.1 jako RFC2068. Za tu dobu ušel internet dlouhou cestu a velmi se změnil způsob, jakým ho používáme.

Dnes žijeme ve světě interaktivních responsivních webových nebo mobilních aplikací, které na pozadí provádí desítky drobných AJAXových požadavků, jejichž backend je často implementován formou desítek, stovek a v některých případech dokonce tisíců mikroservice, které spolu navzájem komunikují prostřednictvím RESTových rozhraní. Na vykreslení jedné webové stránky je tak nezřídka třeba několik stovek HTTP požadavků. Uživatel přitom očekává bleskovou odezvu. V takovéto architektuře a s takovými požadavky mají úspory na úrovni aplikačního protokolu značný význam. Co nám tedy protokol HTTP/2 v tomto ohledu přináší?

Novinky v HTTP/2 protokolu

Blíže si o tom můžete přečíst např. v článku Jak funguje nový protokol HTTP/2 na root.cz. Zde pouze stručně uvedu přehled nejzásadnějších změn oproti HTTP 1.1:

  • Proudy – HTTP 1.1 zavádí persistentní spojení, avšak na tomto spojení nemůže klient poslat nový požadavek, dokud neobdrží odpověď na ten předchozí. HTTP/2 zavádí koncept proudů, kterých může na jednom spojení probíhat paralelně několik, mohou se vzájemně překrývat i míchat na úrovni TCP packetů. Klient tak může odeslat několik HTTP požadavků, aniž by musel čekat na vyřízení toho prvního.
  • Binární formát – Na rozdíl od svého předchůdce není HTTP/2 textovým, ale binárním protokolem. Binární zprávy jsou úspornější a lépe se strojově zpracovávají.
  • Komprimované hlavičky – Zjednodušeně řečeno, klient i server si udržují tabulky hlaviček a jejich hodnot. V samotných zprávách je tak možné nahradit hlavičku odkazem do této tabulky. Díky tomu není nutné ty samé hlavičky ve zprávách stále opakovat.
  • Server push korelovaných resources – Načtení jedné webové stránky typicky zahrnuje několik HTTP požadavků. Prohlížeč nejprve obdrží HTML dokument z něhož zjistí další zdroje, které je potřeba načíst – kaskádové styly, JavaScript, multimédia atd. Následuje řada dalších HTTP požadavků. HTTP/2 umožňuje provést server push, jehož využití se však omezuje zejména na to, aby server mohl poslat klientovi všechny zdroje s danou stránkou spojené.

Klienti a servery

Jak je to s podporou HTTP/2 na straně klientů a serverů? Hlavní prohlížeče podporují HTTP/2 od konce roku 2015. S podporou na straně serverů je to o něco složitější a souvisí to s implementací v prohlížečích. V průběhu vývoje protokolu se uvažovalo i tom, že by použití šifrovaného TLS spojení bylo pro HTTP/2 povinné. Do standardu se tento striktní požadavek sice nedostal, nicméně díky podpoře v prohlížečích TLS de facto povinné je. Chrome, Firefox, Opera, Safari i Edge totiž TLS pro HTTP/2 vyžadují.

V případě TLS (HTTPS) spojení se klient se serverem na použití aplikačního protokolu domluví ještě před zahájením samotné šifrované komunikace jako součást TLS handshaku. Slouží k tomu vlastní protokoly jako rozšíření TLS. Dříve používaný NPN (Next Protocol Negotiation) nahradil protokol ALPN (Application Layer Protocol Negotiation, RFC7301). Chrome přestal NPN podporovat už v květnu 2016, Firefox se uchýlil ke stejnému kroku na začátku roku 2017 a podporu NPN by jste marně hledali i v současné Opeře.

Tímto jsou v podstatě dané požadavky na podporu HTTP/2 na straně serverů. Je vyžadováno HTTP/2 over TLS a tím pádem i ALPN. Právě s ALPN je potíž, protože např. OpenSSL, na kterém je postavena podpora TLS velké části webových serverů, tento protokol podporuje až od verze 1.0.2, která nebyla dlouhou dobu v produkčním prostředí dostupná (byl vyžadován update celého OS). V OpenSSL je však ALPN dostupné už od ledna 2015. Jak si na tomto poli stojí Java?

Dobrá zpráva je, že specifikace Servlet 4.0 pro compliant containery HTTP/2 vyžaduje. K tomu je však ještě dlouhá cesta, protože očekávané datum vydání Java EE 8 specifikace se aktuálně pohybuje na konci roku 2017. Z pohledu Servlet API je však HTTP/2 z velké části nezajímavé, protokol je především záležitostí implementace samotného containeru. Na Servlet 4.0 však naštěstí čekat nemusíme. Už současné Servlet 3.1 containery HTTP/2 podporují:

  • Apache Tomcat 8.5, 9 (milestone release)
  • Jetty 9.3, 9.4
  • Undertow 1.3, 1.4

Problémy a řešení

Má to však jeden drobný háček. Implementace TLS v Javě 8 (Oracle i OpenJDK) nepodporuje protokol ALPN. ALPN bude k dispozici až v Javě 9, která by (snad!) měla vyjít v červenci 2017. Poté by měla být podpora ALPN backportována také do Javy 8. Do té doby se musíme spolehnout na řešení, která se pokusím popsat.

Jednou z možností (zejména pro Jetty a Undertow) je použít Jetty implementaci ALPN. Mělo by stačit přidat na boot classpath odpovídající JAR, jak je popsané např. v Jetty dokumentaci.

Pro Tomcat lze využít Apache Portable Library (APR) nebo-li de facto core Apache HTTP Serveru, který je volán z Javy přes JNI. Instalace APR je popsaná v dokumentaci Tomcatu. Instalaci pro Tomcat 8.5.13 jsem provedl na Linux Mint 18.1 bez větších problémů. Před samotnou instalací je potřeba do OS nainstalovat následující balíčky:

  • APR 1.2+ development headers (libapr1-dev package)
  • OpenSSL 1.0.2+ development headers (libssl-dev package)
  • JNI headers from Java compatible JDK 1.4+
  • GNU development environment (gccmake)

V instalačním adresáři Tomcatu (dále $CATALINA_HOME) se v cestě $CATALINA_HOME/bin nachází archiv tomcat-native.tar.gz. Ten je potřeba v tomto adresáři rozbalit. V rozbaleném adresáři v cestě $CATALINA_HOME/bin/tomcat-native-1.2.12-src/native se nachází spustitelný soubor configure, který slouží pro přípravu makefilu pro build. Pod rootem je třeba provést:


sudo ./configure
sudo make
sudo make install

Pokud jsou všechny potřebné závislosti splněny, mělo by vše doběhnout bez problémů a skript configure není třeba nijak parametrizovat.

Cestu k nativní knihovně je ještě potřeba přidat do java.library.path. To lze udělat např. VM argumentem, tedy do souboru setenv.sh v $CATALINA_HOME/bin přidat:

export CATALINA_OPTS=”$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib”

V cestě /usr/local/apr je APR defaultně nainstalované.

server.xml v $CATALINA_HOME/conf můžeme vytvořit HTTPS connector pro HTTP/2, např. takto:


<connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol" maxThreads="150" SSLEnabled="true" >
<upgradeprotocol className="org.apache.coyote.http2.Http2Protocol"></upgradeprotocol>
<sslhostconfig>
<certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA"></certificate>
</sslhostconfig>
</connector>

Nyní máme container připravený pro použití APR.

Takto je container nastaven, aby umožňoval klientovi přejít na HTTP/2 v případě, že ho podporuje. V opačném případě použije HTTP 1.1.

Když nyní spustím container a přistoupím na domovskou stránku na portu 8443, ukáže mi Chrome Developer Tools v sekci Network ve sloupci Protocol hodnotu h2, tedy HTTP/2, viz obrázek.

h2 Protocol

Co říci závěrem? Protokol HTTP/2 přináší nepopiratelné výhody a vzhledem k podpoře na straně klientů není důvod čekat na Javu 9 nebo backport do Javy 8. Pokud to produkční prostředí (zejména verze containeru) umožňuje, určitě stojí za to zvážit přechod na HTTP/2.


Tento článek vychází z části přednášky Juergena Hoellera o Springu během meet-upu v kancelářích MoroSystems v Brně. Konkrétní část přednášky viz ve videu níže. Celou přednášku s Juergenem Hoellerem je možné shlédnout zde (video na YouTube).


Zajímají tě nejlepší technologie na zajímavých projektech?

Aktuálně hledáme seniorní Java / JEE Developery do kanceláří v Hradci Králové a Brně, ale i na home-office.

Připoj se k Pavlovi v Hradci! Případně mrkni na všechny naše otevřené pozice a dej nám o sobě vědět, bez ohledu na to, zda máš rád front-end nebo back-end, jestli nějakou technologii umíš nebo ne. Pokud budeš ty sám chtít, dostaneš u nás příležitost naučit se, co tě zajímá nebo udělat něco výjimečného.