Chapter 16 updates

Signed-off-by: WalkerWatch <ctwalker@gmail.com>
This commit is contained in:
WalkerWatch 2016-07-21 17:19:25 -04:00
parent fa24eb3541
commit 7c9f3ec727
5 changed files with 90 additions and 99 deletions

View File

@ -17,13 +17,13 @@
[[http2-configuring-haproxy]]
=== Configuring HAProxy and Jetty
Typical website deployments have Apache (or Nginx) configured as reverse proxy to talk to one or more backend Jetty instances.
Typical website deployments have Apache (or Nginx) configured as reverse proxy to talk to one or more backend Jetty instances.
This configuration cannot be used for HTTP/2 because Apache does not yet support HTTP/2 (nor does Nginx).
http://haproxy.org[HAProxy] is an open source solution that offers load balancing and proxying for TCP and HTTP based application, and can be used as a replacement for Apache (or Nginx) when these are used as reverse proxies, and has the major benefit that supports HTTP/2.
It also offers load balancing and a ton of other features, so you can probably use it as a replacement for Apache (or Nginx).
http://haproxy.org[HAProxy] is an open source solution that offers load balancing and proxying for TCP and HTTP based application, and can be used as a replacement for Apache or Nginx when these are used as reverse proxies and has the major benefit that supports HTTP/2.
It also offers load balancing and several other features which can position it as a complete replacement for Apache or Nginx.
The deployment proposed here will have HAProxy play the role that Apache (or Nginx) usually do: to perform the TLS offloading (that is, decrypt and encrypt TLS) and then forwarding the now clear-text traffic to a backend Jetty server, speaking either HTTP/1.1 or HTTP/2.
The deployment proposed here will have HAProxy play the role that Apache and Nginx usually do: to perform the TLS offloading (that is, decrypt and encrypt TLS) and then forwarding the now clear-text traffic to a backend Jetty server, speaking either HTTP/1.1 or HTTP/2.
The instructions that follow are for Linux.
@ -32,20 +32,18 @@ The instructions that follow are for Linux.
You will need HAProxy 1.5 or later, because it provides support for SSL and ALPN, both required by HTTP/2. Most Linux distributions have the HAProxy package available to be installed out of the box. For example on Ubuntu 15.04:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ sudo apt-get install haproxy
----
....
Alternatively you can download the HAProxy source code and build it on your environment, by following the README bundled with the HAProxy source code tarball.
Alternatively you can download the HAProxy source code and build it on your environment by following the README bundled with the HAProxy source code tarball.
____
[NOTE]
HAProxy supports ALPN only if built with OpenSSL 1.0.2 or greater.
Alternatively, HAProxy supports NPN when built with OpenSSL 1.0.1 or greater.
HAProxy supports ALPN only if built with OpenSSL 1.0.2 or greater.
Alternatively, HAProxy supports NPN when built with OpenSSL 1.0.1 or greater.
You must upgrade OpenSSL if you have a version earlier than 1.0.1.
Use `haproxy -vv` to know with which OpenSSL version HAProxy has been built.
____
@ -56,7 +54,7 @@ HAProxy will perform the TLS decryption and encryption much more efficiently tha
HAProxy will need a single file containing the X509 certificates and the private key, all in https://en.wikipedia.org/wiki/X.509[PEM format], with the following order:
1. The site certificate; this certificate's Common Name refers to the site domain (for example: CN=*.webtide.com) and it's signed by Certificate Authority #1.
1. The site certificate; this certificate's Common Name refers to the site domain (for example: CN=*.webtide.com) and is signed by Certificate Authority #1.
2. The Certificate Authority #1 certificate; this certificate may be signed by Certificate Authority #2.
3. The Certificate Authority #2 certificate; this certificate may be signed by Certificate Authority #3; and so on until the Root Certificate Authority.
4. The Root Certificate Authority certificate.
@ -64,8 +62,8 @@ HAProxy will need a single file containing the X509 certificates and the private
Let's use `keytool` to generate a self signed certificate:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ keytool -genkeypair -keyalg RSA -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -ext SAN=DNS:domain.com
What is your first and last name?
[Unknown]: *.domain.com
@ -81,44 +79,41 @@ What is the two-letter country code for this unit?
[Unknown]: IT
Is CN=*.domain.com, OU=Unit, O=Domain, L=Torino, ST=TO, C=IT correct?
[no]: yes
----
....
The above command will generate a self signed certificate and private key for `domain.com` and subdomains, stored in the `keystore.p12` file in PKCS#12 format.
The above command will generate a self signed certificate and private key for `domain.com` and subdomains, stored in the `keystore.p12` file in PKCS#12 format.
We need to extract the certificate and the private key in PEM format.
To extract the certificate into `certificate.pem`:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ keytool -exportcert -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -rfc -file certificate.pem
----
....
To export the private key into `private_key.pem`:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ openssl pkcs12 -in keystore.p12 -nodes -nocerts -out private_key.pem -passin pass:storepwd
----
....
At this point you just need to concatenate the two files into one, in the correct order:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ cat certificate.pem private_key.pem > domain.pem
----
....
The `domain.pem` file will be used later by HAProxy.
[[http2-haproxy-cfg]]
==== HAProxy Configuration File
Now we can setup `haproxy.cfg` to configure HAProxy.
Now we can setup `haproxy.cfg` to configure HAProxy.
This is a minimal configuration:
[source, ,subs="{sub-order}"]
....
global
tune.ssl.default-dh-param 1024
@ -142,56 +137,53 @@ default_backend be_http
backend be_http
mode tcp
server domain 127.0.0.1:8282
....
The HAProxy configuration file works in the following way.
The HAProxy configuration file works in the following way.
The `fe_http` front-end accepts connections on port 80 and redirects them to use the `https` scheme.
The `fe_https` front-end accepts connections on port 443 and it is where the TLS decryption/encryption happens.
You must specify the path to the PEM file containing the TLS key material (the `crt domain.pem` part), the ciphers that are suitable for HTTP/2 (the `ciphers TLSv1.2` part), and the ALPN (or NPN if you are using old OpenSSL versions) protocols supported (the `alpn h2,http/1.1` part).
This front-end then forwards the now decrypted bytes to the back-end in `mode tcp`. The `mode tcp`) means that HAProxy will not try to interpret the bytes as HTTP/1.1 but just opaquely forward them to the back-end.
The `fe_https` front-end accepts connections on port 443 and it is where the TLS decryption/encryption happens.
You must specify the path to the PEM file containing the TLS key material (the `crt domain.pem` part), the ciphers that are suitable for HTTP/2 (the `ciphers TLSv1.2`), and the ALPN protocols supported (the `alpn h2,http/1.1` ).
This front-end then forwards the now decrypted bytes to the back-end in `mode tcp`. The `mode tcp` says that HAProxy will not try to interpret the bytes as HTTP/1.1 but instead opaquely forward them to the back-end.
The `be_http` back-end will forward (again in `mode tcp`) the clear-text bytes to a Jetty connector that talks clear-text HTTP/2 and HTTP/1.1 on port 8282.
[[http2-haproxy-jetty]]
==== Setup Jetty for HTTP/2 and HTTP/1.1
The Jetty setup follows the usual steps of having Jetty installed in the `JETTY_HOME` directory, creating a `JETTY_BASE` directory and initializing it using Jetty's command line tools.
You must enable the `http2c` module, that is the module that speaks clear-text HTTP/2.
The Jetty setup follows the steps of having Jetty installed in the `JETTY_HOME` directory, creating a `JETTY_BASE` directory and initializing it using Jetty's command line tools.
You must enable the `http2c` module, that is the module that speaks clear-text HTTP/2.
Since the `http2c` module depends on the `http` module, the `http` module will be enabled transitively, and the final setup will therefore support both HTTP/2 and HTTP/1.1 in clear text.
Additionally, you will also enable the `deploy` module to be able to deploy a sample web application:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ JETTY_BASE=haproxy-jetty-http2
$ mkdir $JETTY_BASE
$ cd $JETTY_BASE
$ java -jar $JETTY_HOME/start.jar --add-to-start=http2c,deploy
----
....
Now let's deploy a demo web application and start Jetty:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ cd $JETTY_BASE
$ cp $JETTY_HOME/demo-base/webapps/async-rest.war $JETTY_BASE/webapps/
$ java -jar $JETTY_HOME/start.jar jetty.http.host=127.0.0.1 jetty.http.port=8282
----
....
Now you can browse https://domain.com/async-rest (replace `domain.com` with your own domain, or with `localhost`, to make this example work).
____
[NOTE]
You want the Jetty connector that listens on port 8282 to be available only to HAProxy, and not to remote clients.
For this reason, you want to specify the `jetty.http.host` property on the command line (or in `start.ini` to make this setting persistent) to bind the Jetty connector only on the loopback interface (127.0.0.1), making it available to HAProxy but not to remote clients.
For this reason, you want to specify the `jetty.http.host` property on the command line (or in `start.ini`/ `start.d/http.ini` to make this setting persistent) to bind the Jetty connector only on the loopback interface (127.0.0.1), making it available to HAProxy but not to remote clients.
If your Jetty instance runs on a different machine and/or on a different (sub)network, you may want to adjust both the back-end section of the HAProxy configuration file and the `jetty.http.host` property to match accordingly.
____
Browsers supporting HTTP/2 will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
Browsers supporting HTTP/2 will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
Likewise, HTTP/1.1 clients will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
The Jetty connector, configured with the `http2c` module (and therefore transitively with the `http` module) is able to distinguish whether the incoming bytes are HTTP/2 or HTTP/1.1 and will handle the request accordingly.

View File

@ -17,16 +17,16 @@
[[http2-configuring]]
=== Configuring HTTP/2
Enabling the HTTP/2 module in the jetty server does not create a HTTP/2 specific connector, but rather it adds a HTTP/2 Connection factory to an
existing connector.
Thus configuring HTTP/2 is a combination of configuring common properties on the connector and HTTP/2 specific properties on the connection factory.
Enabling the HTTP/2 module in the Jetty server does not create a HTTP/2 specific connector, but rather it adds a HTTP/2 Connection factory to an
existing connector.
Thus configuring HTTP/2 is a combination of configuring common properties on the connector and HTTP/2 specific properties on the connection factory.
The modules and XML files involved can be seen with the following commands:
[source,shell]
----
[source,screen, subs="{sub-order}"]
....
$ java -jar $JETTY_HOME/start.jar --list-modules
...
1) protonego-boot <transitive>
1) protonego-boot <transitive>
...
2) http ${jetty.base}/start.d/http.ini
2) ssl ${jetty.base}/start.d/ssl.ini
@ -46,13 +46,17 @@ $ java -jar $JETTY_HOME/start.jar --list-config
...
${jetty.home}/etc/jetty-http2.xml
${jetty.home}/etc/jetty-https.xml
----
....
The common properties associated with connectors (host,port, timeouts, etc.) can be set in the module ini files (or start.ini if --add-to-start was used): `${jetty.base}/start.d/http.ini` `${jetty.base}/start.d/ssl.ini`.
The common properties associated with connectors (host,port, timeouts, etc.) can be set in the module ini files (or `start.ini` if `--add-to-start` was used): `${jetty.base}/start.d/http.ini` and `${jetty.base}/start.d/ssl.ini`.
These properties are instantiated in the associated XML files: `${jetty.home}/etc/jetty-http.xml`; `${jetty.home}/etc/jetty-ssl.xml`, plus the SSL keystore is instantiated in `${jetty.home}/etc/jetty-ssl-context.xml`.
HTTP/2 specific properties can be set in the module ini files: `${jetty.base}/start.d/http2.ini`; `${jetty.base}/start.d/http2c.ini`, which are instantiated in the associated XML files: `${jetty.home}/etc/jetty-http2.xml`; `${jetty.home}/etc/jetty-http2c.xml`.
[NOTE]
If you are planning to edit XML files, make sure to copy them to your `{$jetty.base}/etc/` directory before doing so.
The XML files that come with the Jetty distribution should *not* be modified directly.
----
HTTP/2 specific properties can be set in the module ini files: `${jetty.base}/start.d/http2.ini` and `${jetty.base}/start.d/http2c.ini`, which are instantiated in the associated XML files: `${jetty.home}/etc/jetty-http2.xml`; `${jetty.home}/etc/jetty-http2c.xml`, respectively.
Currently there are very few HTTP/2 configuration properties and the default values are reasonable:
.HTTP/2 Configuration Properties

View File

@ -17,14 +17,13 @@
[[http2-configuring-push]]
=== Configuring HTTP/2 Push
HTTP/2 Push is a mechanism that allows the server to send multiple resources to the client for a single client request.
This will reduce the amount of round-trips necessary to retrieve all the resources that make up a web page, and can significantly improve the page load time.
HTTP/2 Push is a mechanism that allows the server to send multiple resources to the client for a single client request.
This will reduce the amount of round-trips necessary to retrieve all the resources that make up a web page and can significantly improve the page load time.
HTTP/2 Push can be automated in your application by simply configuring a link:{JDURL}/org/eclipse/jetty/servlets/PushCacheFilter.html[`PushCacheFilter`] in your `web.xml`, in this way:
HTTP/2 Push can be automated in your application by configuring a link:{JDURL}/org/eclipse/jetty/servlets/PushCacheFilter.html[`PushCacheFilter`] in the `web.xml`, in this way:
[source, xml, subs="{sub-order}"]
----
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
@ -34,7 +33,6 @@ HTTP/2 Push can be automated in your application by simply configuring a link:{J
version="3.1">
...
<filter>
<filter-name>PushFilter</filter-name>
<filter-class>org.eclipse.jetty.servlets.PushCacheFilter</filter-class>
@ -44,17 +42,14 @@ HTTP/2 Push can be automated in your application by simply configuring a link:{J
<filter-name>PushFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
----
`PushCacheFilter` analyzes the HTTP requests for resources that arrive to your web application.
Some of these requests contain the HTTP `Referrer` header that points to a resource that has been requested previously.
This allows the `PushCacheFilter` to organize resources in _primary_ resources (those referenced by the `Referrer` header) and _secondary_ resources (those that have the `Referer` header).
`PushCacheFilter` analyzes the HTTP requests for resources that arrive to your web application.
Some of these requests contain the HTTP `Referrer` header that points to a resource that has been requested previously.
This allows the `PushCacheFilter` to organize resources in to two categories: _primary_ resources (those referenced by the `Referrer` header) and _secondary_ resources (those that have the `Referer` header).
`PushCacheFilter` associates secondary resources to primary resources.
Only secondary resources that have been requested within a time window from the request of the primary resource are associated with the primary resource.
@ -62,4 +57,3 @@ Only secondary resources that have been requested within a time window from the
`PushCacheFilter` can be configured with the following `init-params`::
* `associatePeriod`: the time window, in milliseconds, within which a request for a secondary resource will be associated to a primary resource.
* `maxAssociations`: the max number of secondary resources that may be associated to a primary resource.

View File

@ -17,7 +17,8 @@
[[http2-enabling]]
=== Enabling HTTP/2
This section is written assuming that a jetty base directory is being used and a demo jetty base that support HTTP/1, HTTPS/1 and deployment from a webapps directory can be created with the commands:
This section is written assuming that a link:#startup-base-and-home[Jetty base directory] is being used.
A demo Jetty base that supports HTTP/1, HTTPS/1 and deployment from a webapps directory can be created with the commands:
[source, screen, subs="{sub-order}"]
....
@ -27,21 +28,21 @@ $ cd $JETTY_BASE
$ java -jar $JETTY_HOME/start.jar --add-to-startd=http,https,deploy
....
The commands above create a `$JETTY_BASE` directory called `http2-demo`, and initializes the `http,` `https` and `deploy` modules (and their dependencies) to run a typical Jetty Server on port 8080 (for HTTP/1) and 8443 (for HTTPS/1).
Note that the https module downloads a demo keystore file with a self signed certificate, which needs to be replaced by a Certificate Authority issued certificate for real deployment.
The commands above create a `$JETTY_BASE` directory called `http2-demo`, and initializes the `http,` `https` and `deploy` modules (and their dependencies) to run a typical Jetty Server on port 8080 (for HTTP/1) and 8443 (for HTTPS/1).
Note that the HTTPS module downloads a demo keystore file with a self signed certificate, which needs to be replaced by a Certificate Authority issued certificate for real deployment.
To add HTTP/2 to this demo base, it is just a matter of enabling the http2 module with the following command:
To add HTTP/2 to this demo base, it is just a matter of enabling the `http2` module with the following command:
[source,shell]
----
[source, screen, subs="{sub-order}"]
....
$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2
----
....
This command does not create a new connector, but instead simply adds the HTTP/2 protocol to the existing HTTPS/1 connector, so that it now supports both protocols on port 8443.
To do this, it also transitively enables the ALPN module for protocol negotiation.
This command does not create a new connector, but instead simply adds the HTTP/2 protocol to the existing HTTPS/1 connector, so that it now supports both protocols on port 8443.
To do this, it also transitively enables the ALPN module for protocol negotiation.
The support for each protocol can be seen in the info logging when the server is started:
[source,shell]
[source,screen, subs="{sub-order}"]
----
$ java -jar $JETTY_HOME/start.jar
...
@ -50,22 +51,22 @@ $ java -jar $JETTY_HOME/start.jar
...
----
This log shows that port 8080 supports only HTTP/1.1 (which by specification includes HTTP/1.0 support), while port 8443 supports the SSL protocol, with ALPN negotiation to select between several versions of HTTP/2 (h2 & the draft h2-17) and HTTP/1.1.
What is not shown, is that http/1.1 is the default ALPN protocol, so that if a client connects that does not speak ALPN, then HTTP/1.1 will be assumed.
This log shows that port 8080 supports only HTTP/1.1 (which by specification includes HTTP/1.0 support), while port 8443 supports the SSL protocol, with ALPN negotiation to select between several versions of HTTP/2 (h2 & the draft h2-17) and HTTP/1.1.
What is not shown is that HTTP/1.1 is the default ALPN protocol, so that if a client connects that does not speak ALPN, then HTTP/1.1 will be assumed.
A browser can now be pointed at `https://localhost:8443/` and if it supports HTTP/2 then it will be used (often indicated by a lightening bolt icon in the address bar).
A browser can now be pointed at `https://localhost:8443/` and if it supports HTTP/2 then it will be used (often indicated by a lightening bolt icon in the address bar).
Note that a browser pointed at this server with URL starting with `http://localhost:8080/` will still talk HTTP/1.1, as HTTP/2 has not been enabled on the plain text connector.
HTTP/2 can be enabled on the plain text connector and the server restarted with the following command:
[source,shell]
----
[source,screen]
....
$ java -jar $JETTY_HOME/start.jar --add-to-startd=http2c
$ java -jar $JETTY_HOME/start.jar
...
..
2015-06-17 14:16:12.549:INFO:oejs.ServerConnector:main: Started ServerConnector@6f32cd1e{HTTP/1.1,[http/1.1, h2c, h2c-17]}{0.0.0.0:8080}
2015-06-17 14:16:12.782:INFO:oejs.ServerConnector:main: Started ServerConnector@711f39f9{SSL,[ssl, alpn, h2, h2-17, http/1.1]}{0.0.0.0:8443}
...
----
..
....
However, no major browser currently supports plain text HTTP/2, so the 8080 port will only be able to use HTTP/2 with specific clients (eg curl) that use the upgrade mechanism or assume HTTP/2
No major browser currently supports plain text HTTP/2, so the 8080 port will only be able to use HTTP/2 with specific clients (eg `curl`) that use the upgrade mechanism or assume HTTP/2.

View File

@ -25,18 +25,18 @@ A server deployed over TLS (SSL) normally advertises the HTTP/2 protocol via the
____
[IMPORTANT]
To use HTTP/2 in Jetty via a TLS connector you need to add the link:#alpn-starting[ALPN boot Jar in the boot classpath.]
This is done automatically when using the jetty distributions start.jar module system, but must be configured directly otherwise.
To use HTTP/2 in Jetty via a TLS connector you need to add the link:#alpn-starting[ALPN boot jar] in the boot classpath.
This is done automatically when using the Jetty distribution's start.jar link:#startup-modules[module system], but must be configured directly otherwise.
____
[[http2-modules]]
==== Jetty HTTP/2 Sub Projects
=== Jetty HTTP/2 Sub Projects
The Jetty HTTP/2 implementation consists of the following sub-projects (each producing a jar):
The Jetty HTTP/2 implementation consists of the following sub-projects (each producing a jar file):
1. `http2-common`: contains the HTTP/2 API and a partial implementation shared across other modules.
2. `http2-hpack`: contains the HTTP/2 HPACK implementation for HTTP header compression.
3. `http2-server`: provides the server-side implementation of HTTP/2.
4. `http2-client`: provides the implementation of HTTP/2 client with a low level HTTP/2 API, dealing with HTTP/2 streams, frames, etc.
5. `http2-http-client-transport`: provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:http-client[]).
Applications can use the higher level API provided by `HttpClient` to send HTTP requests and receive HTTP responses, and the HTTP/2 transport will take care of converting them in HTTP/2 format (see also https://webtide.com/http2-support-for-httpclient/[this blog entry]).
1. `http2-common`: Contains the HTTP/2 API and a partial implementation shared across other modules.
2. `http2-hpack`: Contains the HTTP/2 HPACK implementation for HTTP header compression.
3. `http2-server`: Provides the server-side implementation of HTTP/2.
4. `http2-client`: Provides the implementation of HTTP/2 client with a low level HTTP/2 API, dealing with HTTP/2 streams, frames, etc.
5. `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:http-client[]).
Applications can use the higher level API provided by `HttpClient` to send HTTP requests and receive HTTP responses, and the HTTP/2 transport will take care of converting them in HTTP/2 format (see also https://webtide.com/http2-support-for-httpclient/[this blog entry]).