Improvements to the Jetty server documentation.
Written connector and handler sections. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
d2844bb102
commit
35dce5f315
|
@ -1,24 +1,4 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
|
||||||
~
|
|
||||||
~ ========================================================================
|
|
||||||
~ Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
|
||||||
~
|
|
||||||
~ This program and the accompanying materials are made available under
|
|
||||||
~ the terms of the Eclipse Public License 2.0 which is available at
|
|
||||||
~ https://www.eclipse.org/legal/epl-2.0
|
|
||||||
~
|
|
||||||
~ This Source Code may also be made available under the following
|
|
||||||
~ Secondary Licenses when the conditions for such availability set
|
|
||||||
~ forth in the Eclipse Public License, v. 2.0 are satisfied:
|
|
||||||
~ the Apache License v2.0 which is available at
|
|
||||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
~
|
|
||||||
~ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
|
||||||
~ ========================================================================
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
@ -63,6 +43,21 @@
|
||||||
<artifactId>jetty-servlet</artifactId>
|
<artifactId>jetty-servlet</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlets</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-rewrite</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-alpn-server</artifactId>
|
<artifactId>jetty-alpn-server</artifactId>
|
||||||
|
@ -88,6 +83,12 @@
|
||||||
<artifactId>http2-http-client-transport</artifactId>
|
<artifactId>http2-http-client-transport</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-slf4j-impl</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -75,14 +75,14 @@ Jetty is compliant with link:https://tools.ietf.org/html/rfc6265[RFC6265], and a
|
||||||
|
|
||||||
Previously, Version=1 cookies defined in link:https://tools.ietf.org/html/rfc2109[RFC2109] (and continued in link:https://tools.ietf.org/html/rfc2965[RFC2965]) allowed for special/reserved characters to be enclosed within double quotes when declared in a `Set-Cookie` response header:
|
Previously, Version=1 cookies defined in link:https://tools.ietf.org/html/rfc2109[RFC2109] (and continued in link:https://tools.ietf.org/html/rfc2965[RFC2965]) allowed for special/reserved characters to be enclosed within double quotes when declared in a `Set-Cookie` response header:
|
||||||
|
|
||||||
[source,subs="{sub-order}"]
|
[source,screen]
|
||||||
----
|
----
|
||||||
Set-Cookie: foo="bar;baz";Version=1;Path="/secur"
|
Set-Cookie: foo="bar;baz";Version=1;Path="/secur"
|
||||||
----
|
----
|
||||||
|
|
||||||
This was added to the HTTP Response as follows:
|
This was added to the HTTP Response as follows:
|
||||||
|
|
||||||
[source,java,subs="{sub-order}"]
|
[source,java]
|
||||||
----
|
----
|
||||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +95,7 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||||
The introduction of RFC6265 has rendered this approach no longer possible; users are now required to encode cookie values that use these special characters.
|
The introduction of RFC6265 has rendered this approach no longer possible; users are now required to encode cookie values that use these special characters.
|
||||||
This can be done utilizing `javax.servlet.http.Cookie` as follows:
|
This can be done utilizing `javax.servlet.http.Cookie` as follows:
|
||||||
|
|
||||||
[source,java,subs="{sub-order}"]
|
[source,java]
|
||||||
----
|
----
|
||||||
javax.servlet.http.Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "UTF-8"));
|
javax.servlet.http.Cookie cookie = new Cookie("foo", URLEncoder.encode("bar;baz", "UTF-8"));
|
||||||
----
|
----
|
||||||
|
|
|
@ -49,7 +49,7 @@ Out of the box features that you get with the Jetty HTTP client include:
|
||||||
The Jetty artifact that provides the main HTTP client implementation is `jetty-client`.
|
The Jetty artifact that provides the main HTTP client implementation is `jetty-client`.
|
||||||
The Maven artifact coordinates are the following:
|
The Maven artifact coordinates are the following:
|
||||||
|
|
||||||
[source,xml,subs="{sub-order}"]
|
[source,xml,subs=normal]
|
||||||
----
|
----
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
|
|
@ -34,7 +34,7 @@ semantic objects that can be used by applications.
|
||||||
The most common protocol format is HTTP/1.1, a textual protocol with lines
|
The most common protocol format is HTTP/1.1, a textual protocol with lines
|
||||||
separated by `\r\n`:
|
separated by `\r\n`:
|
||||||
|
|
||||||
[source,screen,subs="{sub-order}"]
|
[source,screen]
|
||||||
----
|
----
|
||||||
GET /index.html HTTP/1.1\r\n
|
GET /index.html HTTP/1.1\r\n
|
||||||
Host: domain.com\r\n
|
Host: domain.com\r\n
|
||||||
|
@ -44,7 +44,7 @@ Host: domain.com\r\n
|
||||||
|
|
||||||
However, the same request can be made using FastCGI, a binary protocol:
|
However, the same request can be made using FastCGI, a binary protocol:
|
||||||
|
|
||||||
[source,screen,subs="{sub-order}"]
|
[source,screen]
|
||||||
----
|
----
|
||||||
x01 x01 x00 x01 x00 x08 x00 x00
|
x01 x01 x00 x01 x00 x08 x00 x00
|
||||||
x00 x01 x01 x00 x00 x00 x00 x00
|
x00 x01 x01 x00 x00 x00 x00 x00
|
||||||
|
|
|
@ -38,7 +38,7 @@ _frames_, and this is quite a rare use case.
|
||||||
|
|
||||||
The Maven artifact coordinates for the HTTP/2 client library are the following:
|
The Maven artifact coordinates for the HTTP/2 client library are the following:
|
||||||
|
|
||||||
[source,xml,subs="{sub-order}"]
|
[source,xml,subs=normal]
|
||||||
----
|
----
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.http2</groupId>
|
<groupId>org.eclipse.jetty.http2</groupId>
|
||||||
|
@ -198,7 +198,7 @@ In order to send a HTTP request to the server, the client must send a
|
||||||
headers.
|
headers.
|
||||||
Sending the `HEADERS` frame opens the `Stream`:
|
Sending the `HEADERS` frame opens the `Stream`:
|
||||||
|
|
||||||
[source,java,indent=0,subs={sub-order}]
|
[source,java,indent=0,subs=normal]
|
||||||
----
|
----
|
||||||
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=newStream]
|
include::../../{doc_code}/embedded/client/http2/HTTP2ClientDocs.java[tags=newStream]
|
||||||
----
|
----
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
[[server]]
|
[[server]]
|
||||||
== Jetty Server Libraries
|
== OLD DOCUMENTATION
|
||||||
|
|
||||||
include::embedding/chapter.adoc[]
|
include::embedding/chapter.adoc[]
|
||||||
include::maven/chapter.adoc[]
|
include::maven/chapter.adoc[]
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-http-connector]]
|
||||||
|
=== Server Connectors
|
||||||
|
|
||||||
|
A `Connector` is the component that handles incoming requests from clients,
|
||||||
|
and works in conjunction with `ConnectionFactory` instances.
|
||||||
|
|
||||||
|
The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
|
||||||
|
`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen
|
||||||
|
to a TCP port and to accept TCP connections.
|
||||||
|
|
||||||
|
Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured
|
||||||
|
in a similar way, for example the port to listen to, the network address
|
||||||
|
to bind to, etc.:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnector]
|
||||||
|
----
|
||||||
|
|
||||||
|
The _acceptors_ are threads (typically only one) that compete to accept TCP
|
||||||
|
connections on the listening port.
|
||||||
|
When a connection is accepted, `ServerConnector` wraps the accepted
|
||||||
|
`SocketChannel` and passes it to the
|
||||||
|
xref:eg-io-arch-selector-manager[`SelectorManager`].
|
||||||
|
Therefore there is a little moment where the acceptor thread is not accepting
|
||||||
|
new connections because it is busy wrapping the just accepted one to pass it
|
||||||
|
to the `SelectorManager`.
|
||||||
|
Connections that are ready to be accepted but are not accepted yet are queued
|
||||||
|
in a bounded queue (at the OS level) whose capacity can be configured with the
|
||||||
|
`ServerConnector.acceptQueueSize` parameter.
|
||||||
|
|
||||||
|
If your application must withstand a very high rate of connections opened,
|
||||||
|
configuring more than one acceptor thread may be beneficial: when one acceptor
|
||||||
|
thread accepts one connection, another acceptor thread can take over accepting
|
||||||
|
connections.
|
||||||
|
|
||||||
|
The _selectors_ are components that manage a set of connected sockets,
|
||||||
|
implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
|
||||||
|
Each selector requires one thread and uses the Java NIO mechanism to
|
||||||
|
efficiently handle the set of connected sockets.
|
||||||
|
As a rule of thumb, a single selector can easily manage up to 1000-5000
|
||||||
|
sockets, although the number may vary greatly depending on the application.
|
||||||
|
|
||||||
|
It is possible to configure more than one `ServerConnector`, each listening
|
||||||
|
on a different port:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnectors]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-connector-protocol]]
|
||||||
|
==== Configuring Protocols
|
||||||
|
|
||||||
|
For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory`
|
||||||
|
to create a `Connection` object that handles the network traffic on that TCP
|
||||||
|
connection, parsing and generating bytes for a specific protocol (see
|
||||||
|
xref:eg-io-arch[this section] for more details about `Connection` objects).
|
||||||
|
|
||||||
|
A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
|
||||||
|
If no `ConnectionFactory` is specified then `HttpConnectionFactory` is
|
||||||
|
implicitly configured.
|
||||||
|
|
||||||
|
[[eg-server-http-connector-protocol-http11]]
|
||||||
|
===== Configuring HTTP/1.1
|
||||||
|
|
||||||
|
`HttpConnectionFactory` creates `HttpConnection` objects that parse bytes
|
||||||
|
and generate bytes for the HTTP/1.1 protocol.
|
||||||
|
|
||||||
|
This is how you configure Jetty to support clear-text HTTP/1.1:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11]
|
||||||
|
----
|
||||||
|
|
||||||
|
Supporting encrypted HTTP/1.1 (that is, requests with the HTTPS scheme)
|
||||||
|
is supported by configuring an `SslContextFactory` that has access to the
|
||||||
|
keyStore containing the private server key and public server certificate,
|
||||||
|
in this way:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsHttp11]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-connector-protocol-proxy-http11]]
|
||||||
|
===== Configuring Jetty behind a Load Balancer
|
||||||
|
|
||||||
|
It is often the case that Jetty receives connections from a load balancer
|
||||||
|
configured to distribute the load among many Jetty backend servers.
|
||||||
|
|
||||||
|
From the Jetty point of view, all the connections arrive from the load
|
||||||
|
balancer, rather than the real clients, but is possible to configure the load
|
||||||
|
balancer to forward the real client IP address and port to the backend Jetty
|
||||||
|
server using the
|
||||||
|
link:https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].
|
||||||
|
|
||||||
|
NOTE: The PROXY protocol is widely supported by load balancers such as
|
||||||
|
link:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.2-send-proxy[HAProxy]
|
||||||
|
(via its `send-proxy` directive),
|
||||||
|
link:https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol[Nginx]
|
||||||
|
(via its `proxy_protocol on` directive) and others.
|
||||||
|
|
||||||
|
To support this case, Jetty can be configured in this way:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=proxyHTTP]
|
||||||
|
----
|
||||||
|
|
||||||
|
Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
|
||||||
|
first PROXY, then HTTP/1.1.
|
||||||
|
Note also how the PROXY `ConnectionFactory` needs to know its _next_ protocol
|
||||||
|
(in this example, HTTP/1.1).
|
||||||
|
|
||||||
|
Each `ConnectionFactory` is asked to create a `Connection` object for each
|
||||||
|
accepted TCP connection; the `Connection` objects will be chained together
|
||||||
|
to handle the bytes, each for its own protocol.
|
||||||
|
Therefore the `ProxyConnection` will handle the PROXY protocol bytes and
|
||||||
|
`HttpConnection` will handle the HTTP/1.1 bytes producing a request object
|
||||||
|
and response object that will be processed by ``Handler``s.
|
||||||
|
|
||||||
|
[[eg-server-http-connector-protocol-http2]]
|
||||||
|
===== Configuring HTTP/2
|
||||||
|
|
||||||
|
It is well known that the HTTP ports are `80` (for clear-text HTTP) and `443`
|
||||||
|
for encrypted HTTP.
|
||||||
|
By using those ports, a client had _prior knowledge_ that the server would
|
||||||
|
speak, respectively, the HTTP/1.x protocol and the TLS protocol (and, after
|
||||||
|
decryption, the HTTP/1.x protocol).
|
||||||
|
|
||||||
|
HTTP/2 was designed to be a smooth transition from HTTP/1.1 for users and
|
||||||
|
as such the HTTP ports were not changed.
|
||||||
|
However the HTTP/2 protocol is, on the wire, a binary protocol, completely
|
||||||
|
different from HTTP/1.1.
|
||||||
|
Therefore, with HTTP/2, clients that connect to port `80` may speak either
|
||||||
|
HTTP/1.1 or HTTP/2, and the server must figure out which version of the HTTP
|
||||||
|
protocol the client is speaking.
|
||||||
|
|
||||||
|
Jetty can support both HTTP/1.1 and HTTP/2 on the same clear-text port by
|
||||||
|
configuring both the HTTP/1.1 and the HTTP/2 ``ConnectionFactory``s:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11H2C]
|
||||||
|
----
|
||||||
|
|
||||||
|
Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
|
||||||
|
first HTTP/1.1, then HTTP/2.
|
||||||
|
This is necessary to support both protocols on the same port: Jetty will
|
||||||
|
start parsing the incoming bytes as HTTP/1.1, but then realize that they
|
||||||
|
are HTTP/2 bytes and will therefore _upgrade_ from HTTP/1.1 to HTTP/2.
|
||||||
|
|
||||||
|
This configuration is also typical when Jetty is installed in backend servers
|
||||||
|
behind a load balancer that also takes care of offloading TLS.
|
||||||
|
When Jetty is behind a load balancer, you can always prepend the PROXY
|
||||||
|
protocol as described in
|
||||||
|
xref:eg-server-http-connector-protocol-proxy-http11[this section].
|
||||||
|
|
||||||
|
When using encrypted HTTP/2, the unencrypted protocol is negotiated by client
|
||||||
|
and server using an extension to the TLS protocol called ALPN.
|
||||||
|
|
||||||
|
Jetty supports ALPN and encrypted HTTP/2 with this configuration:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsALPNHTTP]
|
||||||
|
----
|
||||||
|
|
||||||
|
Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
|
||||||
|
TLS, ALPN, HTTP/1.1, HTTP/2.
|
||||||
|
|
||||||
|
Jetty starts parsing TLS bytes so that it can obtain the ALPN extension.
|
||||||
|
With the ALPN extension information, Jetty can negotiate a protocol and
|
||||||
|
pick, among the ``ConnectionFactory``s supported by the `ServerConnector`,
|
||||||
|
the `ConnectionFactory` correspondent to the negotiated protocol.
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-http-handler-implement]]
|
||||||
|
==== Implementing Handler
|
||||||
|
|
||||||
|
The `Handler` API consist fundamentally of just one method:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerAPI]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `target` parameter is an identifier for the resource.
|
||||||
|
This is normally the URI that is parsed from an HTTP request.
|
||||||
|
However, a request could be forwarded to either a named resource, in which case
|
||||||
|
`target` will be the name of the resource, or to a different URI, in which case
|
||||||
|
`target` will be the new URI.
|
||||||
|
|
||||||
|
Applications may wrap the request or response (or both) and forward the wrapped
|
||||||
|
request or response to a different URI (which may be possibly handled by a
|
||||||
|
different `Handler`).
|
||||||
|
This is the reason why there are two request parameters in the `Handler` APIs:
|
||||||
|
the first is the unwrapped, original, request while the second is the
|
||||||
|
application-wrapped request.
|
||||||
|
|
||||||
|
[[eg-server-http-handler-impl-hello]]
|
||||||
|
===== Hello World Handler
|
||||||
|
|
||||||
|
A simple "Hello World" `Handler` is the following:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerHello]
|
||||||
|
----
|
||||||
|
|
||||||
|
Such a simple `Handler` extends from `AbstractHandler` and can access the
|
||||||
|
request and response main features, such as reading request headers and
|
||||||
|
content, or writing response headers and content.
|
||||||
|
|
||||||
|
[[eg-server-http-handler-impl-filter]]
|
||||||
|
===== Filtering Handler
|
||||||
|
|
||||||
|
A filtering `Handler` is a handler that perform some modification to the
|
||||||
|
request or response, and then either forwards the request to another
|
||||||
|
`Handler` or produces an error response:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerFilter]
|
||||||
|
----
|
||||||
|
|
||||||
|
Note how a filtering `Handler` extends from `HandlerWrapper` and as such
|
||||||
|
needs another handler to forward the request processing to, and how the
|
||||||
|
two``Handler``s needs to be linked together to work properly.
|
||||||
|
|
|
@ -0,0 +1,464 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use]]
|
||||||
|
==== Using Provided Handlers
|
||||||
|
|
||||||
|
Web applications are the unit of deployment in an HTTP server or Servlet
|
||||||
|
container such as Jetty.
|
||||||
|
|
||||||
|
Two different web applications are typically deployed on different
|
||||||
|
__context path__s, where a _context path_ is the initial segment of the URI
|
||||||
|
path.
|
||||||
|
For example, web application `webappA` that implements a web user interface
|
||||||
|
for an e-commerce site may be deployed to context path `/shop`, while web
|
||||||
|
application `webappB` that implements a REST API for the e-commerce business
|
||||||
|
may be deployed to `/api`.
|
||||||
|
|
||||||
|
A client making a request to URI `/shop/cart` is directed by Jetty to
|
||||||
|
`webappA`, while a request to URI `/api/products` is directed to `webappB`.
|
||||||
|
|
||||||
|
An alternative way to deploy the two web applications of the example above
|
||||||
|
is to use _virtual hosts_.
|
||||||
|
A _virtual host_ is a subdomain of the primary domain that shares the same
|
||||||
|
IP address with the primary domain.
|
||||||
|
If the e-commerce business primary domain is `domain.com`, then a virtual
|
||||||
|
host for `webappA` could be `shop.domain.com`, while a virtual host for
|
||||||
|
`webappB` could be `api.domain.com`.
|
||||||
|
|
||||||
|
Web application `webappA` can now be deployed to virtual host
|
||||||
|
`shop.domain.com` and context path `/`, while web application `webappB`
|
||||||
|
can be deployed to virtual host `api.domain.com` and context path `/`.
|
||||||
|
Both applications have the same context path `/`, but they can be
|
||||||
|
distinguished by the subdomain.
|
||||||
|
|
||||||
|
A client making a request to `+https://shop.domain.com/cart+` is
|
||||||
|
directed by Jetty to `webappA`, while a request to
|
||||||
|
`+https://api.domain.com/products+` is directed to `webappB`.
|
||||||
|
|
||||||
|
Therefore, in general, a web application is deployed to a _context_
|
||||||
|
which can be seen as the pair `(virtual_host, context_path)`.
|
||||||
|
In the first case the contexts were `(domain.com, /shop)` and
|
||||||
|
`(domain.com, /api)`, while in the second case the contexts were
|
||||||
|
`(shop.domain.com, /)` and `(api.domain.com, /)`.
|
||||||
|
Server applications using the Jetty Server Libraries create and
|
||||||
|
configure a _context_ for each web application.
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-context]]
|
||||||
|
===== ContextHandler
|
||||||
|
|
||||||
|
`ContextHandler` is a `Handler` that represents a _context_ for a web
|
||||||
|
application. It is a `HandlerWrapper` that performs some action before
|
||||||
|
and after delegating to the nested `Handler`.
|
||||||
|
// TODO: expand on what the ContextHandler does, e.g. ServletContext.
|
||||||
|
|
||||||
|
The simplest use of `ContextHandler` is the following:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── ContextHandler /shop
|
||||||
|
└── ShopHandler
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-context-collection]]
|
||||||
|
===== ContextHandlerCollection
|
||||||
|
|
||||||
|
Server applications may need to deploy to Jetty more than one web application.
|
||||||
|
|
||||||
|
Recall from the xref:eg-server-http-handler[introduction] that Jetty offers
|
||||||
|
`HandlerCollection` and `HandlerList` that may contain a sequence of children
|
||||||
|
``Handler``s.
|
||||||
|
However, both of these have no knowledge of the concept of _context_ and just
|
||||||
|
iterate through the sequence of ``Handler``s.
|
||||||
|
|
||||||
|
A better choice for multiple web application is `ContextHandlerCollection`,
|
||||||
|
that matches a _context_ from either its _context path_ or _virtual host_,
|
||||||
|
without iterating through the ``Handler``s.
|
||||||
|
|
||||||
|
If `ContextHandlerCollection` does not find a match, it just returns.
|
||||||
|
What happens next depends on the `Handler` tree structure: other ``Handler``s
|
||||||
|
may be invoked after `ContextHandlerCollection`, for example `DefaultHandler`
|
||||||
|
(see xref:eg-server-http-handler-use-util-default-handler[this section]).
|
||||||
|
Eventually, if `Request.setHandled(true)` is not called, Jetty returns a HTTP
|
||||||
|
`404` response to the client.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextHandlerCollection]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
├── ContextHandler /shop
|
||||||
|
│ └── ShopHandler
|
||||||
|
└── ContextHandler /api
|
||||||
|
└── RESTHandler
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-servlet-context]]
|
||||||
|
===== ServletContextHandler
|
||||||
|
|
||||||
|
``Handler``s are easy to write, but often web applications have already been
|
||||||
|
written using the Servlet APIs, using ``Servlet``s and ``Filter``s.
|
||||||
|
|
||||||
|
`ServletContextHandler` is a `ContextHandler` that provides support for the
|
||||||
|
Servlet APIs and implements the behaviors required by the Servlet specification.
|
||||||
|
|
||||||
|
The Maven artifact coordinates are:
|
||||||
|
|
||||||
|
[source,xml,subs=normal]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<version>{version}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=servletContextHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` and Servlet components tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen,subs=normal]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── ServletContextHandler /shop
|
||||||
|
├── _ShopCartServlet /cart/*_
|
||||||
|
└── _CrossOriginFilter /*_
|
||||||
|
----
|
||||||
|
|
||||||
|
Note how the Servlet components (they are not ``Handler``s) are represented in
|
||||||
|
_italic_.
|
||||||
|
|
||||||
|
Note also how adding a `Servlet` or a `Filter` returns a _holder_ object that
|
||||||
|
can be used to specify additional configuration for that particular `Servlet`
|
||||||
|
or `Filter`.
|
||||||
|
|
||||||
|
When a request arrives to `ServletContextHandler` the request URI will be
|
||||||
|
matched against the ``Filter``s and ``Servlet`` mappings and only those that
|
||||||
|
match will process the request, as dictated by the Servlet specification.
|
||||||
|
|
||||||
|
IMPORTANT: `ServletContextHandler` is a terminal `Handler`, that is it always
|
||||||
|
calls `Request.setHandled(true)` when invoked.
|
||||||
|
Server applications must be careful when creating the `Handler`
|
||||||
|
tree to put ``ServletContextHandler``s as last ``Handler``s in a `HandlerList`
|
||||||
|
or as children of `ContextHandlerCollection`.
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-webapp-context]]
|
||||||
|
===== WebAppContext
|
||||||
|
|
||||||
|
`WebAppContext` is a `ServletContextHandler` that auto configures itself by
|
||||||
|
reading a `web.xml` Servlet configuration file.
|
||||||
|
|
||||||
|
Server applications can specify a `+*.war+` file or a directory with the
|
||||||
|
structure of a `+*.war+` file to `WebAppContext` to deploy a standard Servlet
|
||||||
|
web application packaged as a `war` (as defined by the Servlet specification).
|
||||||
|
|
||||||
|
Where server applications using `ServletContextHandler` must manually invoke
|
||||||
|
methods to add ``Servlet``s and ``Filter``s, `WebAppContext` reads
|
||||||
|
`WEB-INF/web.xml` to add ``Servlet``s and ``Filter``s.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=webAppContextHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-resource-handler]]
|
||||||
|
===== ResourceHandler -- Static Content
|
||||||
|
|
||||||
|
Static content such as images or files (HTML, JavaScript, CSS) can be sent
|
||||||
|
by Jetty very efficiently because Jetty can write the content asynchronously,
|
||||||
|
using direct ``ByteBuffer``s to minimize data copy, and using a memory cache
|
||||||
|
for faster access to the data to send.
|
||||||
|
|
||||||
|
Being able to write content asynchronously means that if the network gets
|
||||||
|
congested (for example, the client reads the content very slowly) and the
|
||||||
|
server stalls the send of the requested data, then Jetty will wait to resume
|
||||||
|
the send _without_ blocking a thread to finish the send.
|
||||||
|
|
||||||
|
`ResourceHandler` supports the following features:
|
||||||
|
|
||||||
|
* Welcome files, for example serving `/index.html` for request URI `/`
|
||||||
|
* Precompressed resources, serving a precompressed `/document.txt.gz` for
|
||||||
|
request URI `/document.txt`
|
||||||
|
* link:https://tools.ietf.org/html/rfc7233[Range requests], for requests
|
||||||
|
containing the `Range` header, which allows clients to pause and resume
|
||||||
|
downloads of large files
|
||||||
|
* Directory listing, serving a HTML page with the file list of the requested
|
||||||
|
directory
|
||||||
|
* Conditional headers, for requests containing the `If-Match`, `If-None-Match`,
|
||||||
|
`If-Modified-Since`, `If-Unmodified-Since` headers.
|
||||||
|
|
||||||
|
The number of features supported and the efficiency in sending static content
|
||||||
|
are on the same level as those of common front-end servers used to serve
|
||||||
|
static content such as Nginx or Apache.
|
||||||
|
Therefore, the traditional architecture where Nginx/Apache was the front-end
|
||||||
|
server used only to send static content and Jetty was the back-end server used
|
||||||
|
only to send dynamic content is somehow obsolete as Jetty can perform
|
||||||
|
efficiently both tasks.
|
||||||
|
This leads to simpler systems (less components to configure and manage) and
|
||||||
|
more performance (no need to proxy dynamic requests from front-end servers
|
||||||
|
to back-end servers).
|
||||||
|
|
||||||
|
NOTE: It is common to use Nginx/Apache as load balancers, or as rewrite/redirect
|
||||||
|
servers. We typically recommend link:https://haproxy.org[HAProxy] as load
|
||||||
|
balancer, and Jetty has
|
||||||
|
xref:eg-server-http-handler-use-util-rewrite-handler[rewrite/redirect features]
|
||||||
|
as well.
|
||||||
|
|
||||||
|
This is how you configure a `ResourceHandler` to create a simple file server:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=resourceHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
If you need to serve static resources from multiple directories:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=multipleResourcesHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
If the resource is not found, `ResourceHandler` will not call
|
||||||
|
`Request.setHandled(true)` so what happens next depends on the `Handler`
|
||||||
|
tree structure. See also
|
||||||
|
xref:eg-server-http-handler-use-util-default-handler[how to use] `DefaultHandler`.
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-default-servlet]]
|
||||||
|
===== DefaultServlet -- Static Content for Servlets
|
||||||
|
|
||||||
|
If you have a
|
||||||
|
xref:eg-server-http-handler-use-servlet-context[Servlet web application],
|
||||||
|
you may want to use a `DefaultServlet` instead of `ResourceHandler`.
|
||||||
|
The features are similar, but `DefaultServlet` is more commonly used to
|
||||||
|
serve static files for Servlet web applications.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultServlet]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-util-gzip-handler]]
|
||||||
|
===== GzipHandler
|
||||||
|
|
||||||
|
`GzipHandler` provides supports for automatic decompression of compressed
|
||||||
|
request content and automatic compression of response content.
|
||||||
|
|
||||||
|
`GzipHandler` is a `HandlerWrapper` that inspects the request and, if the
|
||||||
|
request matches the `GzipHandler` configuration, just installs the required
|
||||||
|
components to eventually perform decompression of the request content or
|
||||||
|
compression of the response content.
|
||||||
|
The decompression/compression is not performed until the web application
|
||||||
|
reads request content or writes response content.
|
||||||
|
|
||||||
|
`GzipHandler` can be configured at the server level in this way:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=serverGzipHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── GzipHandler
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
├── ContextHandler 1
|
||||||
|
:── ...
|
||||||
|
└── ContextHandler N
|
||||||
|
----
|
||||||
|
|
||||||
|
However, in less common cases, you can configure `GzipHandler` on a
|
||||||
|
per-context basis, for example because you want to configure `GzipHandler`
|
||||||
|
with different parameters for each context, or because you want only some
|
||||||
|
contexts to have compression support:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=contextGzipHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
├── GzipHandler
|
||||||
|
│ └── ContextHandler /shop
|
||||||
|
│ └── ShopHandler
|
||||||
|
└── ContextHandler /api
|
||||||
|
└── RESTHandler
|
||||||
|
----
|
||||||
|
|
||||||
|
// TODO: does ServletContextHandler really need a special configuration?
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-util-rewrite-handler]]
|
||||||
|
===== RewriteHandler
|
||||||
|
|
||||||
|
`RewriteHandler` provides support for URL rewriting, very similarly to
|
||||||
|
link:https://httpd.apache.org/docs/current/mod/mod_rewrite.html[Apache's mod_rewrite]
|
||||||
|
or
|
||||||
|
link:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html[Nginx rewrite module].
|
||||||
|
|
||||||
|
The Maven artifact coordinates are:
|
||||||
|
|
||||||
|
[source,xml,subs=normal]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-rewrite</artifactId>
|
||||||
|
<version>{version}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
||||||
|
|
||||||
|
`RewriteHandler` can be configured with a set of __rule__s; a _rule_ inspects
|
||||||
|
the request and when it matches it performs some change to the request (for
|
||||||
|
example, changes the URI path, adds/removes headers, etc.).
|
||||||
|
|
||||||
|
The Jetty Server Libraries provide rules for the most common usages, but you
|
||||||
|
can write your own rules by extending the
|
||||||
|
`org.eclipse.jetty.rewrite.handler.Rule` class.
|
||||||
|
|
||||||
|
Please refer to the `jetty-rewrite` module
|
||||||
|
link:{JDURL}/org/eclipse/jetty/rewrite/handler/package-summary.html[javadocs]
|
||||||
|
for the complete list of available rules.
|
||||||
|
|
||||||
|
You typically want to configure `RewriteHandler` at the server level, although
|
||||||
|
it is possible to configure it on a per-context basis.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=rewriteHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── RewriteHandler
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
├── ContextHandler 1
|
||||||
|
:── ...
|
||||||
|
└── ContextHandler N
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-util-stats-handler]]
|
||||||
|
===== StatisticsHandler
|
||||||
|
|
||||||
|
`StatisticsHandler` gathers and exposes a number of statistic values related
|
||||||
|
to request processing such as:
|
||||||
|
|
||||||
|
* Total number of requests
|
||||||
|
* Current number of concurrent requests
|
||||||
|
* Minimum, maximum, average and standard deviation of request processing times
|
||||||
|
* Number of responses grouped by HTTP code (i.e. how many `2xx` responses, how
|
||||||
|
many `3xx` responses, etc.)
|
||||||
|
* Total response content bytes
|
||||||
|
|
||||||
|
Server applications can read these values and use them internally, or expose
|
||||||
|
them via some service, or export them via JMX.
|
||||||
|
// TODO: xref to the JMX section.
|
||||||
|
|
||||||
|
`StatisticsHandler` can be configured at the server level or at the context
|
||||||
|
level.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=statsHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── StatisticsHandler
|
||||||
|
└── ContextHandlerCollection
|
||||||
|
├── ContextHandler 1
|
||||||
|
:── ...
|
||||||
|
└── ContextHandler N
|
||||||
|
----
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-util-secure-handler]]
|
||||||
|
===== SecuredRedirectHandler -- Redirect from HTTP to HTTPS
|
||||||
|
|
||||||
|
// TODO: wait for issue #4766
|
||||||
|
TODO
|
||||||
|
|
||||||
|
[[eg-server-http-handler-use-util-default-handler]]
|
||||||
|
===== DefaultHandler
|
||||||
|
|
||||||
|
`DefaultHandler` is a terminal `Handler` that always calls
|
||||||
|
`Request.setHandled(true)` and performs the following:
|
||||||
|
|
||||||
|
* Serves the `favicon.ico` Jetty icon when it is requested
|
||||||
|
* Sends a HTTP `404` response for any other request
|
||||||
|
* The HTTP `404` response content nicely shows a HTML table with all the
|
||||||
|
contexts deployed on the `Server` instance
|
||||||
|
|
||||||
|
`DefaultHandler` is best used as the last `Handler` of a `HandlerList`,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=defaultHandler]
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
Server
|
||||||
|
└── HandlerList
|
||||||
|
├── ContextHandlerCollection
|
||||||
|
│ ├── ContextHandler 1
|
||||||
|
│ :── ...
|
||||||
|
│ └── ContextHandler N
|
||||||
|
└── DefaultHandler
|
||||||
|
----
|
||||||
|
|
||||||
|
In the example above, `ContextHandlerCollection` will try to match a request
|
||||||
|
to one of the contexts; if the match fails, `HandlerList` will call the next
|
||||||
|
`Handler` which is `DefaultHandler` that will return a HTTP `404` with an
|
||||||
|
HTML page showing the existing contexts deployed on the `Server`.
|
||||||
|
|
||||||
|
NOTE: `DefaultHandler` just sends a nicer HTTP `404` response in case of
|
||||||
|
wrong requests from clients.
|
||||||
|
Jetty will send an HTTP `404` response anyway if `DefaultHandler` is not
|
||||||
|
used.
|
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-http-handler]]
|
||||||
|
=== Server Handlers
|
||||||
|
|
||||||
|
An `org.eclipse.jetty.server.Handler` is the component that processes
|
||||||
|
incoming HTTP requests and eventually produces HTTP responses.
|
||||||
|
|
||||||
|
``Handler``s can be organized in different ways:
|
||||||
|
|
||||||
|
* in a sequence, where ``Handler``s are invoked one after the other
|
||||||
|
** `HandlerCollection` invokes _all_ ``Handler``s one after the other
|
||||||
|
** `HandlerList` invokes ``Handlers``s until one calls `Request.setHandled(true)`
|
||||||
|
to indicate that the request has been handled and no further `Handler` should
|
||||||
|
be invoked
|
||||||
|
* nested, where one `Handler` invokes the next, nested, `Handler`
|
||||||
|
** `HandlerWrapper` implements this behavior
|
||||||
|
|
||||||
|
The `HandlerCollection` behavior (invoking _all_ handlers) is useful when
|
||||||
|
for example the last `Handler` is a logging `Handler` that logs the request
|
||||||
|
(that may have been modified by previous handlers).
|
||||||
|
|
||||||
|
The `HandlerList` behavior (invoking handlers up to the first that calls
|
||||||
|
`Request.setHandled(true)`) is useful when each handler processes a different
|
||||||
|
URIs or a different virtual hosts: ``Handler``s are invoked one after the
|
||||||
|
other until one matches the URI or virtual host.
|
||||||
|
|
||||||
|
The nested behavior is useful to enrich the request with additional services
|
||||||
|
such as HTTP session support (`SessionHandler`), or with specific behaviors
|
||||||
|
dictated by the Servlet specification (`ServletHandler`).
|
||||||
|
|
||||||
|
``Handler``s can be organized in a tree by composing them together:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=handlerTree]
|
||||||
|
----
|
||||||
|
|
||||||
|
The corresponding `Handler` tree structure looks like the following:
|
||||||
|
|
||||||
|
[source,screen]
|
||||||
|
----
|
||||||
|
HandlerCollection
|
||||||
|
├── HandlerList
|
||||||
|
│ ├── App1Handler
|
||||||
|
│ └── HandlerWrapper
|
||||||
|
│ └── App2Handler
|
||||||
|
└── LoggingHandler
|
||||||
|
----
|
||||||
|
|
||||||
|
////
|
||||||
|
PlantUML cannot render a tree left-aligned :(
|
||||||
|
[plantuml]
|
||||||
|
----
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam monochrome true
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam padding 5
|
||||||
|
|
||||||
|
scale 1.5
|
||||||
|
|
||||||
|
hide members
|
||||||
|
hide circle
|
||||||
|
|
||||||
|
HandlerCollection -- HandlerList
|
||||||
|
HandlerCollection -- LoggingHandler
|
||||||
|
HandlerList -- App1Handler
|
||||||
|
HandlerList -- App2Handler
|
||||||
|
App2Handler -- ServletHandler
|
||||||
|
----
|
||||||
|
////
|
||||||
|
|
||||||
|
Server applications should rarely write custom ``Handler``s, preferring
|
||||||
|
instead to use existing ``Handler``s provided by the Jetty Server Libraries
|
||||||
|
for managing web application contexts, security, HTTP sessions and Servlet
|
||||||
|
support.
|
||||||
|
Refer to xref:eg-server-http-handler-use[this section] for more information about
|
||||||
|
how to use the ``Handler``s provided by the Jetty Server Libraries.
|
||||||
|
|
||||||
|
However, in some cases the additional features are not required, or additional
|
||||||
|
constraints on memory footprint, or performance, or just simplicity must be met.
|
||||||
|
In these cases, implementing your own `Handler` may be a better solution.
|
||||||
|
Refer to xref:eg-server-http-handler-implement[this section] for more information
|
||||||
|
about how to write your own ``Handler``s.
|
||||||
|
|
||||||
|
include::server-http-handler-use.adoc[]
|
||||||
|
include::server-http-handler-implement.adoc[]
|
|
@ -22,6 +22,17 @@
|
||||||
The Eclipse Jetty Project has historically provided libraries to embed an HTTP
|
The Eclipse Jetty Project has historically provided libraries to embed an HTTP
|
||||||
server and a Servlet Container.
|
server and a Servlet Container.
|
||||||
|
|
||||||
|
The Maven artifact coordinates are:
|
||||||
|
|
||||||
|
[source,xml,subs=normal]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>{version}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
||||||
|
|
||||||
An `org.eclipse.jetty.server.Server` instance is the central component that
|
An `org.eclipse.jetty.server.Server` instance is the central component that
|
||||||
links together a collection of ``Connector``s and a collection of
|
links together a collection of ``Connector``s and a collection of
|
||||||
``Handler``s, with threads from a `ThreadPool` doing the work.
|
``Handler``s, with threads from a `ThreadPool` doing the work.
|
||||||
|
@ -44,7 +55,7 @@ Server -- Handlers
|
||||||
----
|
----
|
||||||
|
|
||||||
The components that accept connections from clients are
|
The components that accept connections from clients are
|
||||||
`org.eclipse.jetty.server.Connector` instances.
|
`org.eclipse.jetty.server.Connector` implementations.
|
||||||
|
|
||||||
When a Jetty server interprets the HTTP protocol (both HTTP/1.1 and HTTP/2),
|
When a Jetty server interprets the HTTP protocol (both HTTP/1.1 and HTTP/2),
|
||||||
it uses `org.eclipse.jetty.server.Handler` instances to process incoming
|
it uses `org.eclipse.jetty.server.Handler` instances to process incoming
|
||||||
|
@ -66,232 +77,5 @@ applications only need to put the required components together to provide
|
||||||
all the required features.
|
all the required features.
|
||||||
// TODO: link to a place where we discuss the handlers in more details.
|
// TODO: link to a place where we discuss the handlers in more details.
|
||||||
|
|
||||||
[[eg-server-connector]]
|
include::server-http-connector.adoc[]
|
||||||
=== Server Connectors
|
include::server-http-handler.adoc[]
|
||||||
|
|
||||||
A `Connector` is the component that handles incoming requests from clients,
|
|
||||||
and works in conjunction with `ConnectionFactory` instances.
|
|
||||||
|
|
||||||
The primary implementation is `org.eclipse.jetty.server.ServerConnector`.
|
|
||||||
`ServerConnector` uses a `java.nio.channels.ServerSocketChannel` to listen
|
|
||||||
to a TCP port and to accept TCP connections.
|
|
||||||
|
|
||||||
Since `ServerConnector` wraps a `ServerSocketChannel`, it can be configured
|
|
||||||
in a similar way, for example the port to listen to, the network address
|
|
||||||
to bind to, etc.:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnector]
|
|
||||||
----
|
|
||||||
|
|
||||||
The _acceptors_ are threads that compete to accept TCP connections on the
|
|
||||||
listening port, typically only one.
|
|
||||||
When a connection is accepted, `ServerConnector` wraps it and passes it to
|
|
||||||
the xref:eg-io-arch-selector-manager[`SelectorManager`].
|
|
||||||
Therefore there is a little moment where the acceptor thread is not accepting
|
|
||||||
new connections because it is busy wrapping the just accepted one to pass it
|
|
||||||
to the `SelectorManager`.
|
|
||||||
Connections that are ready to be accepted but are not accepted yet are queued
|
|
||||||
in a bounded queue (at the OS level) whose capacity can be configured with the
|
|
||||||
`ServerConnector.acceptQueueSize` parameter.
|
|
||||||
|
|
||||||
If your application must withstand a very high rate of connections opened,
|
|
||||||
configuring more than one acceptor thread may be beneficial: when one acceptor
|
|
||||||
thread accepts one connection, another acceptor thread can take over accepting
|
|
||||||
connections.
|
|
||||||
|
|
||||||
The _selectors_ are components that manage a set of connected sockets,
|
|
||||||
implemented by xref:eg-io-arch-selector-manager[`ManagedSelector`].
|
|
||||||
Each selector requires one thread and uses the Java NIO mechanism to
|
|
||||||
efficiently handle the set of connected sockets.
|
|
||||||
As a rule of thumb, a single selector can easily manage 1000-5000 sockets,
|
|
||||||
although the number may vary greatly depending on the application.
|
|
||||||
|
|
||||||
It is possible to configure more than one `ServerConnector`, each listening
|
|
||||||
on different ports:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=configureConnectors]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[eg-server-connector-protocol]]
|
|
||||||
==== Configuring Protocols
|
|
||||||
|
|
||||||
For each accepted TCP connection, `ServerConnector` asks a `ConnectionFactory`
|
|
||||||
to create a `Connection` object that handles the network traffic on that TCP
|
|
||||||
connection, parsing and generating bytes for a specific protocol (see
|
|
||||||
xref:eg-io-arch[this section] for more details about `Connection` objects).
|
|
||||||
|
|
||||||
A `ServerConnector` can be configured with one or more ``ConnectionFactory``s.
|
|
||||||
If no `ConnectionFactory` is specified then `HttpConnectionFactory` is
|
|
||||||
implicitly configured.
|
|
||||||
|
|
||||||
[[eg-server-connector-protocol-http11]]
|
|
||||||
===== Configuring HTTP/1.1
|
|
||||||
|
|
||||||
`HttpConnectionFactory` creates `HttpConnection` objects that parse bytes
|
|
||||||
and generate bytes for the HTTP/1.1 protocol.
|
|
||||||
|
|
||||||
This is how you configure Jetty to support clear-text HTTP/1.1:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11]
|
|
||||||
----
|
|
||||||
|
|
||||||
Supporting encrypted HTTP/1.1 (that is, requests with the HTTPS scheme)
|
|
||||||
is supported by configuring an `SslContextFactory` that has access to the
|
|
||||||
keyStore containing the private server key and public server certificate,
|
|
||||||
in this way:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsHttp11]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[eg-server-connector-protocol-proxy-http11]]
|
|
||||||
===== Configuring Jetty behind a Load Balancer
|
|
||||||
|
|
||||||
It is often the case that Jetty receives connections from a load balancer
|
|
||||||
configured to distribute the load among many Jetty backend servers.
|
|
||||||
|
|
||||||
From the Jetty point of view, all the connections arrive from the load
|
|
||||||
balancer, rather than the real clients, but is possible to forward the real
|
|
||||||
client IP address and port to the backend Jetty server using the
|
|
||||||
link:https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].
|
|
||||||
|
|
||||||
NOTE: The PROXY protocol is widely supported by load balancers such as
|
|
||||||
link:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.2-send-proxy[HAProxy]
|
|
||||||
(via its `send-proxy` directive), or
|
|
||||||
link:https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol[Nginx]
|
|
||||||
(via its `proxy_protocol on` directive), and others.
|
|
||||||
|
|
||||||
To support this case, Jetty can be configured in this way:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=proxyHTTP]
|
|
||||||
----
|
|
||||||
|
|
||||||
Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
|
|
||||||
first PROXY, then HTTP/1.1.
|
|
||||||
Note also how the PROXY `ConnectionFactory` needs to know its _next_ protocol
|
|
||||||
(in this example, HTTP/1.1).
|
|
||||||
|
|
||||||
Each `ConnectionFactory` is asked to create a `Connection` object for each
|
|
||||||
accepted TCP connection; the `Connection` objects will be chained together
|
|
||||||
to handle the bytes, each for its own protocol.
|
|
||||||
Therefore the `ProxyConnection` will handle the PROXY protocol bytes and
|
|
||||||
`HttpConnection` will handle the HTTP/1.1 bytes producing a request object
|
|
||||||
and response object that will be processed by ``Handler``s.
|
|
||||||
|
|
||||||
[[eg-server-connector-protocol-http2]]
|
|
||||||
===== Configuring HTTP/2
|
|
||||||
|
|
||||||
It is well know that the HTTP ports are `80` (for clear-text HTTP) and `443`
|
|
||||||
for encrypted HTTP.
|
|
||||||
By using those ports, a client had _prior knowledge_ that the server would
|
|
||||||
speak, respectively, the HTTP/1.x protocol and the TLS protocol (and, after
|
|
||||||
decryption, the HTTP/1.x protocol).
|
|
||||||
|
|
||||||
HTTP/2 was designed to be a smooth transition from HTTP/1.1 for users and
|
|
||||||
as such the HTTP ports were not changed.
|
|
||||||
However the HTTP/2 protocol is, on the wire, a binary protocol, completely
|
|
||||||
different from HTTP/1.1.
|
|
||||||
Therefore, with HTTP/2, clients that connect to port `80` may speak either
|
|
||||||
HTTP/1.1 or HTTP/2, and the server must figure out which version of the HTTP
|
|
||||||
protocol the client is speaking.
|
|
||||||
|
|
||||||
Jetty can support both HTTP/1.1 and HTTP/2 on the same clear-text port by
|
|
||||||
configuring both the HTTP/1.1 and the HTTP/2 ``ConnectionFactory``s:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=http11H2C]
|
|
||||||
----
|
|
||||||
|
|
||||||
Note how the ``ConnectionFactory``s passed to `ServerConnector` are in order:
|
|
||||||
first HTTP/1.1, then HTTP/2.
|
|
||||||
This is necessary to support both protocols on the same port: Jetty will
|
|
||||||
start parsing the incoming bytes as HTTP/1.1, but then realize that they
|
|
||||||
are HTTP/2 bytes and will therefore _upgrade_ from HTTP/1.1 to HTTP/2.
|
|
||||||
|
|
||||||
This configuration is also typical when Jetty is installed in backend servers
|
|
||||||
behind a load balancer that also takes care of offloading TLS.
|
|
||||||
When Jetty is behind a load balancer, you can always prepend the PROXY
|
|
||||||
protocol as described in
|
|
||||||
xref:eg-server-connector-protocol-proxy-http11[this section].
|
|
||||||
|
|
||||||
When using encrypted HTTP/2, the unencrypted protocol is negotiated by client
|
|
||||||
and server using an extension to the TLS protocol called ALPN.
|
|
||||||
|
|
||||||
Jetty supports ALPN and encrypted HTTP/2 with this configuration:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tlsALPNHTTP]
|
|
||||||
----
|
|
||||||
|
|
||||||
[[eg-server-handler]]
|
|
||||||
=== Server Handlers
|
|
||||||
|
|
||||||
A `Handler` is the component that processes incoming HTTP requests and
|
|
||||||
eventually produces HTTP responses.
|
|
||||||
|
|
||||||
``Handler``s can be organized in different ways:
|
|
||||||
|
|
||||||
* in a sequence, where ``Handler``s are invoked one after the other
|
|
||||||
** `HandlerCollection` invokes _all_ ``Handler``s one after the other
|
|
||||||
** `HandlerList` invokes ``Handlers``s until one calls `Request.setHandled(true)`
|
|
||||||
to indicate that the request has been handled and no further `Handler` should
|
|
||||||
be invoked.
|
|
||||||
* nested, where one `Handler` invokes the next `Handler`
|
|
||||||
** `HandlerWrapper` implements this behavior
|
|
||||||
|
|
||||||
The `HandlerCollection` behavior (invoking _all_ handlers) is useful when
|
|
||||||
for example the last `Handler` is a logging `Handler` that logs the the
|
|
||||||
request(that may have been modified by previous handlers).
|
|
||||||
|
|
||||||
The `HandlerList` behavior (invoking handlers up to the first that calls
|
|
||||||
`Request.setHandled(true)`) is useful when different handlers process different
|
|
||||||
URIs or different virtual hosts: invoke one after the other until one matches
|
|
||||||
the URI or virtual host.
|
|
||||||
|
|
||||||
The nested behavior is useful to enrich the request with additional services
|
|
||||||
such as HTTP session support (`SessionHandler`), or with specific behaviors
|
|
||||||
dictated by the Servlet specification (`ServletHandler`).
|
|
||||||
|
|
||||||
``Handler``s can be organized in a tree by composing them together:
|
|
||||||
|
|
||||||
[plantuml]
|
|
||||||
----
|
|
||||||
skinparam backgroundColor transparent
|
|
||||||
skinparam monochrome true
|
|
||||||
skinparam shadowing false
|
|
||||||
skinparam padding 5
|
|
||||||
|
|
||||||
scale 1.5
|
|
||||||
|
|
||||||
hide members
|
|
||||||
hide circle
|
|
||||||
|
|
||||||
HandlerCollection -- HandlerList
|
|
||||||
HandlerCollection -- LoggingHandler
|
|
||||||
HandlerList -- App1Handler
|
|
||||||
HandlerList -- App2Handler
|
|
||||||
App2Handler -- ServletHandler
|
|
||||||
----
|
|
||||||
|
|
||||||
In code it looks like this:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=tree]
|
|
||||||
----
|
|
||||||
|
|
||||||
// TODO: old docs introduces briefly ServletHandler but I think it deserves its own section
|
|
||||||
|
|
||||||
// TODO: old docs introduce ContextHandler here and WebAppContext
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-http2]]
|
||||||
|
=== HTTP/2 Server Libraries
|
||||||
|
|
||||||
|
TODO
|
|
@ -46,6 +46,7 @@ xref:eg-server-http2[HTTP/2 libraries]
|
||||||
via the xref:eg-server-websocket[WebSocket libraries]
|
via the xref:eg-server-websocket[WebSocket libraries]
|
||||||
|
|
||||||
include::http/server-http.adoc[]
|
include::http/server-http.adoc[]
|
||||||
include::http/server-http2.adoc[]
|
include::http2/server-http2.adoc[]
|
||||||
include::http/server-websocket.adoc[]
|
include::websocket/server-websocket.adoc[]
|
||||||
include::server-io-arch.adoc[]
|
include::server-io-arch.adoc[]
|
||||||
|
include::../old_docs/server.adoc[]
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under
|
||||||
|
// the terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// This Source Code may also be made available under the following
|
||||||
|
// Secondary Licenses when the conditions for such availability set
|
||||||
|
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||||
|
// the Apache License v2.0 which is available at
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
[[eg-server-websocket]]
|
||||||
|
=== WebSocket Server Libraries
|
||||||
|
|
||||||
|
TODO
|
|
@ -18,13 +18,24 @@
|
||||||
|
|
||||||
package embedded.server.http;
|
package embedded.server.http;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||||
import org.eclipse.jetty.http.HttpCompliance;
|
import org.eclipse.jetty.http.HttpCompliance;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
|
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
|
||||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.CompactPathRule;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.RedirectRegexRule;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||||
|
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
|
@ -35,12 +46,26 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
|
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||||
|
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||||
import org.eclipse.jetty.server.handler.HandlerList;
|
import org.eclipse.jetty.server.handler.HandlerList;
|
||||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||||
|
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||||
|
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
||||||
|
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||||
|
import org.eclipse.jetty.servlet.FilterHolder;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class HTTPServerDocs
|
public class HTTPServerDocs
|
||||||
|
@ -266,7 +291,7 @@ public class HTTPServerDocs
|
||||||
// end::tlsALPNHTTP[]
|
// end::tlsALPNHTTP[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tree() throws Exception
|
public void handlerTree()
|
||||||
{
|
{
|
||||||
class LoggingHandler extends AbstractHandler
|
class LoggingHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
|
@ -292,7 +317,7 @@ public class HTTPServerDocs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag::tree[]
|
// tag::handlerTree[]
|
||||||
// Create a Server instance.
|
// Create a Server instance.
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
|
|
||||||
|
@ -305,10 +330,459 @@ public class HTTPServerDocs
|
||||||
collection.addHandler(new LoggingHandler());
|
collection.addHandler(new LoggingHandler());
|
||||||
|
|
||||||
list.addHandler(new App1Handler());
|
list.addHandler(new App1Handler());
|
||||||
App2Handler app2Handler = new App2Handler();
|
HandlerWrapper wrapper = new HandlerWrapper();
|
||||||
list.addHandler(app2Handler);
|
list.addHandler(wrapper);
|
||||||
|
|
||||||
app2Handler.setHandler(new ServletHandler());
|
wrapper.setHandler(new App2Handler());
|
||||||
// end::tree[]
|
// end::handlerTree[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlerAPI()
|
||||||
|
{
|
||||||
|
class MyHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
// tag::handlerAPI[]
|
||||||
|
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// end::handlerAPI[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlerHello() throws Exception
|
||||||
|
{
|
||||||
|
// tag::handlerHello[]
|
||||||
|
class HelloWorldHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
|
{
|
||||||
|
// Mark the request as handled by this Handler.
|
||||||
|
jettyRequest.setHandled(true);
|
||||||
|
|
||||||
|
response.setStatus(200);
|
||||||
|
response.setContentType("text/html; charset=UTF-8");
|
||||||
|
|
||||||
|
// Write a Hello World response.
|
||||||
|
response.getWriter().print("" +
|
||||||
|
"<!DOCTYPE html>" +
|
||||||
|
"<html>" +
|
||||||
|
"<head>" +
|
||||||
|
" <title>Jetty Hello World Handler</title>" +
|
||||||
|
"</head>" +
|
||||||
|
"<body>" +
|
||||||
|
" <p>Hello World</p>" +
|
||||||
|
"</body>" +
|
||||||
|
"</html>" +
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Set the Hello World Handler.
|
||||||
|
server.setHandler(new HelloWorldHandler());
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::handlerHello[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlerFilter() throws Exception
|
||||||
|
{
|
||||||
|
class HelloWorldHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag::handlerFilter[]
|
||||||
|
class FilterHandler extends HandlerWrapper
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
if (path.startsWith("/old_path/"))
|
||||||
|
{
|
||||||
|
// Rewrite old paths to new paths.
|
||||||
|
HttpURI uri = jettyRequest.getHttpURI();
|
||||||
|
HttpURI newURI = new HttpURI(uri);
|
||||||
|
String newPath = "/new_path/" + path.substring("/old_path/".length());
|
||||||
|
newURI.setPath(newPath);
|
||||||
|
// Modify the request object.
|
||||||
|
jettyRequest.setHttpURI(newURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This Handler is not handling the request, so
|
||||||
|
// it does not call jettyRequest.setHandled(true).
|
||||||
|
|
||||||
|
// Forward to the next Handler.
|
||||||
|
super.handle(target, jettyRequest, request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Link the Handlers.
|
||||||
|
FilterHandler filter = new FilterHandler();
|
||||||
|
filter.setHandler(new HelloWorldHandler());
|
||||||
|
server.setHandler(filter);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::handlerFilter[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contextHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::contextHandler[]
|
||||||
|
class ShopHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
// Implement the shop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create a ContextHandler with contextPath.
|
||||||
|
ContextHandler context = new ContextHandler();
|
||||||
|
context.setContextPath("/shop");
|
||||||
|
context.setHandler(new ShopHandler());
|
||||||
|
|
||||||
|
// Link the context to the server.
|
||||||
|
server.setHandler(context);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::contextHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contextHandlerCollection() throws Exception
|
||||||
|
{
|
||||||
|
// tag::contextHandlerCollection[]
|
||||||
|
class ShopHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
// Implement the shop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RESTHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
// Implement the REST APIs.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create a ContextHandlerCollection to hold contexts.
|
||||||
|
ContextHandlerCollection contextCollection = new ContextHandlerCollection();
|
||||||
|
// Link the ContextHandlerCollection to the Server.
|
||||||
|
server.setHandler(contextCollection);
|
||||||
|
|
||||||
|
// Create the context for the shop web application.
|
||||||
|
ContextHandler shopContext = new ContextHandler("/shop");
|
||||||
|
shopContext.setHandler(new ShopHandler());
|
||||||
|
// Add it to ContextHandlerCollection.
|
||||||
|
contextCollection.addHandler(shopContext);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
// Create the context for the API web application.
|
||||||
|
ContextHandler apiContext = new ContextHandler("/api");
|
||||||
|
apiContext.setHandler(new RESTHandler());
|
||||||
|
// Web applications can be deployed after the Server is started.
|
||||||
|
contextCollection.deployHandler(apiContext, Callback.NOOP);
|
||||||
|
// end::contextHandlerCollection[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void servletContextHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::servletContextHandler[]
|
||||||
|
class ShopCartServlet extends HttpServlet
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
// Implement the shop cart functionality.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create a ServletContextHandler with contextPath.
|
||||||
|
ServletContextHandler context = new ServletContextHandler();
|
||||||
|
context.setContextPath("/shop");
|
||||||
|
|
||||||
|
// Add the Servlet implementing the cart functionality to the context.
|
||||||
|
ServletHolder servletHolder = context.addServlet(ShopCartServlet.class, "/cart/*");
|
||||||
|
// Configure the Servlet with init-parameters.
|
||||||
|
servletHolder.setInitParameter("maxItems", "128");
|
||||||
|
|
||||||
|
// Add the CrossOriginFilter to protect from CSRF attacks.
|
||||||
|
FilterHolder filterHolder = context.addFilter(CrossOriginFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||||
|
// Configure the filter.
|
||||||
|
filterHolder.setAsyncSupported(true);
|
||||||
|
|
||||||
|
// Link the context to the server.
|
||||||
|
server.setHandler(context);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::servletContextHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void webAppContextHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::webAppContextHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create a WebAppContext.
|
||||||
|
WebAppContext context = new WebAppContext();
|
||||||
|
// Configure the path of the packaged web application (file or directory).
|
||||||
|
context.setWar("/path/to/webapp.war");
|
||||||
|
// Configure the contextPath.
|
||||||
|
context.setContextPath("/app");
|
||||||
|
|
||||||
|
// Link the context to the server.
|
||||||
|
server.setHandler(context);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::webAppContextHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resourceHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::resourceHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create and configure a ResourceHandler.
|
||||||
|
ResourceHandler handler = new ResourceHandler();
|
||||||
|
// Configure the directory where static resources are located.
|
||||||
|
handler.setBaseResource(Resource.newResource("/path/to/static/resources/"));
|
||||||
|
// Configure directory listing.
|
||||||
|
handler.setDirectoriesListed(false);
|
||||||
|
// Configure welcome files.
|
||||||
|
handler.setWelcomeFiles(new String[]{"index.html"});
|
||||||
|
// Configure whether to accept range requests.
|
||||||
|
handler.setAcceptRanges(true);
|
||||||
|
|
||||||
|
// Link the context to the server.
|
||||||
|
server.setHandler(handler);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::resourceHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multipleResourcesHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::multipleResourcesHandler[]
|
||||||
|
ResourceHandler handler = new ResourceHandler();
|
||||||
|
|
||||||
|
// For multiple directories, use ResourceCollection.
|
||||||
|
ResourceCollection directories = new ResourceCollection();
|
||||||
|
directories.addPath("/path/to/static/resources/");
|
||||||
|
directories.addPath("/another/path/to/static/resources/");
|
||||||
|
|
||||||
|
handler.setBaseResource(directories);
|
||||||
|
// end::multipleResourcesHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaultServlet()
|
||||||
|
{
|
||||||
|
// tag::defaultServlet[]
|
||||||
|
// Create a ServletContextHandler with contextPath.
|
||||||
|
ServletContextHandler context = new ServletContextHandler();
|
||||||
|
context.setContextPath("/app");
|
||||||
|
|
||||||
|
// Add the DefaultServlet to serve static content.
|
||||||
|
ServletHolder servletHolder = context.addServlet(DefaultServlet.class, "/");
|
||||||
|
// Configure the DefaultServlet with init-parameters.
|
||||||
|
servletHolder.setInitParameter("resourceBase", "/path/to/static/resources/");
|
||||||
|
servletHolder.setAsyncSupported(true);
|
||||||
|
// end::defaultServlet[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serverGzipHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::serverGzipHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create and configure GzipHandler.
|
||||||
|
GzipHandler gzipHandler = new GzipHandler();
|
||||||
|
// Only compress response content larger than this.
|
||||||
|
gzipHandler.setMinGzipSize(1024);
|
||||||
|
// Do not compress these URI paths.
|
||||||
|
gzipHandler.setExcludedPaths("/uncompressed");
|
||||||
|
// Also compress POST responses.
|
||||||
|
gzipHandler.addIncludedMethods("POST");
|
||||||
|
// Do not compress these mime types.
|
||||||
|
gzipHandler.addExcludedMimeTypes("font/ttf");
|
||||||
|
|
||||||
|
// Link a ContextHandlerCollection to manage contexts.
|
||||||
|
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||||
|
gzipHandler.setHandler(contexts);
|
||||||
|
|
||||||
|
// Link the GzipHandler to the Server.
|
||||||
|
server.setHandler(gzipHandler);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::serverGzipHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contextGzipHandler() throws Exception
|
||||||
|
{
|
||||||
|
class ShopHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
// Implement the shop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RESTHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
// Implement the REST APIs.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Server server = new Server();
|
||||||
|
ServerConnector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// tag::contextGzipHandler[]
|
||||||
|
// Create a ContextHandlerCollection to hold contexts.
|
||||||
|
ContextHandlerCollection contextCollection = new ContextHandlerCollection();
|
||||||
|
// Link the ContextHandlerCollection to the Server.
|
||||||
|
server.setHandler(contextCollection);
|
||||||
|
|
||||||
|
// Create the context for the shop web application.
|
||||||
|
ContextHandler shopContext = new ContextHandler("/shop");
|
||||||
|
shopContext.setHandler(new ShopHandler());
|
||||||
|
|
||||||
|
// You want to gzip the shop web application only.
|
||||||
|
GzipHandler shopGzipHandler = new GzipHandler();
|
||||||
|
shopGzipHandler.setHandler(shopContext);
|
||||||
|
|
||||||
|
// Add it to ContextHandlerCollection.
|
||||||
|
contextCollection.addHandler(shopGzipHandler);
|
||||||
|
|
||||||
|
// Create the context for the API web application.
|
||||||
|
ContextHandler apiContext = new ContextHandler("/api");
|
||||||
|
apiContext.setHandler(new RESTHandler());
|
||||||
|
|
||||||
|
// Add it to ContextHandlerCollection.
|
||||||
|
contextCollection.addHandler(apiContext);
|
||||||
|
// end::contextGzipHandler[]
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rewriteHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::rewriteHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
ServerConnector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||||
|
// Compacts URI paths with double slashes, e.g. /ctx//path/to//resource.
|
||||||
|
rewriteHandler.addRule(new CompactPathRule());
|
||||||
|
// Rewrites */products/* to */p/*.
|
||||||
|
rewriteHandler.addRule(new RewriteRegexRule("/(.*)/product/(.*)", "/$1/p/$2"));
|
||||||
|
// Redirects permanently to a different URI.
|
||||||
|
RedirectRegexRule redirectRule = new RedirectRegexRule("/documentation/(.*)", "https://docs.domain.com/$1");
|
||||||
|
redirectRule.setStatusCode(HttpStatus.MOVED_PERMANENTLY_301);
|
||||||
|
rewriteHandler.addRule(redirectRule);
|
||||||
|
|
||||||
|
// Link the RewriteHandler to the Server.
|
||||||
|
server.setHandler(rewriteHandler);
|
||||||
|
|
||||||
|
// Create a ContextHandlerCollection to hold contexts.
|
||||||
|
ContextHandlerCollection contextCollection = new ContextHandlerCollection();
|
||||||
|
// Link the ContextHandlerCollection to the RewriteHandler.
|
||||||
|
rewriteHandler.setHandler(contextCollection);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::rewriteHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void statsHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::statsHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
ServerConnector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
StatisticsHandler statsHandler = new StatisticsHandler();
|
||||||
|
|
||||||
|
// Link the StatisticsHandler to the Server.
|
||||||
|
server.setHandler(statsHandler);
|
||||||
|
|
||||||
|
// Create a ContextHandlerCollection to hold contexts.
|
||||||
|
ContextHandlerCollection contextCollection = new ContextHandlerCollection();
|
||||||
|
// Link the ContextHandlerCollection to the StatisticsHandler.
|
||||||
|
statsHandler.setHandler(contextCollection);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::statsHandler[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaultHandler() throws Exception
|
||||||
|
{
|
||||||
|
// tag::defaultHandler[]
|
||||||
|
Server server = new Server();
|
||||||
|
Connector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
// Create a HandlerList.
|
||||||
|
HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
|
// Add as first a ContextHandlerCollection to manage contexts.
|
||||||
|
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||||
|
handlerList.addHandler(contexts);
|
||||||
|
|
||||||
|
// Add as last a DefaultHandler.
|
||||||
|
DefaultHandler defaultHandler = new DefaultHandler();
|
||||||
|
handlerList.addHandler(defaultHandler);
|
||||||
|
|
||||||
|
// Link the HandlerList to the Server.
|
||||||
|
server.setHandler(handlerList);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
// end::defaultHandler[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
*
|
*
|
||||||
* This handle will deal with unhandled requests in the server.
|
* This handle will deal with unhandled requests in the server.
|
||||||
* For requests for favicon.ico, the Jetty icon is served.
|
* For requests for favicon.ico, the Jetty icon is served.
|
||||||
* For reqests to '/' a 404 with a list of known contexts is served.
|
* For requests to '/' a 404 with a list of known contexts is served.
|
||||||
* For all other requests a normal 404 is served.
|
* For all other requests a normal 404 is served.
|
||||||
*/
|
*/
|
||||||
public class DefaultHandler extends AbstractHandler
|
public class DefaultHandler extends AbstractHandler
|
||||||
|
|
|
@ -47,6 +47,8 @@ public class SecuredRedirectHandler extends AbstractHandler
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
|
||||||
HttpConfiguration httpConfig = channel.getHttpConfiguration();
|
HttpConfiguration httpConfig = channel.getHttpConfiguration();
|
||||||
if (httpConfig == null)
|
if (httpConfig == null)
|
||||||
{
|
{
|
||||||
|
@ -68,7 +70,5 @@ public class SecuredRedirectHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
response.sendError(HttpStatus.FORBIDDEN_403, "Not Secure");
|
response.sendError(HttpStatus.FORBIDDEN_403, "Not Secure");
|
||||||
}
|
}
|
||||||
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue