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"?>
|
||||
<!--
|
||||
~
|
||||
~ ========================================================================
|
||||
~ 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">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
@ -63,6 +43,21 @@
|
|||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
|
@ -88,6 +83,12 @@
|
|||
<artifactId>http2-http-client-transport</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-slf4j-impl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<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:
|
||||
|
||||
[source,subs="{sub-order}"]
|
||||
[source,screen]
|
||||
----
|
||||
Set-Cookie: foo="bar;baz";Version=1;Path="/secur"
|
||||
----
|
||||
|
||||
This was added to the HTTP Response as follows:
|
||||
|
||||
[source,java,subs="{sub-order}"]
|
||||
[source,java]
|
||||
----
|
||||
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.
|
||||
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"));
|
||||
----
|
||||
|
|
|
@ -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 Maven artifact coordinates are the following:
|
||||
|
||||
[source,xml,subs="{sub-order}"]
|
||||
[source,xml,subs=normal]
|
||||
----
|
||||
<dependency>
|
||||
<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
|
||||
separated by `\r\n`:
|
||||
|
||||
[source,screen,subs="{sub-order}"]
|
||||
[source,screen]
|
||||
----
|
||||
GET /index.html HTTP/1.1\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:
|
||||
|
||||
[source,screen,subs="{sub-order}"]
|
||||
[source,screen]
|
||||
----
|
||||
x01 x01 x00 x01 x00 x08 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:
|
||||
|
||||
[source,xml,subs="{sub-order}"]
|
||||
[source,xml,subs=normal]
|
||||
----
|
||||
<dependency>
|
||||
<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.
|
||||
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]
|
||||
----
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
//
|
||||
|
||||
[[server]]
|
||||
== Jetty Server Libraries
|
||||
== OLD DOCUMENTATION
|
||||
|
||||
include::embedding/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
|
||||
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
|
||||
links together a collection of ``Connector``s and a collection of
|
||||
``Handler``s, with threads from a `ThreadPool` doing the work.
|
||||
|
@ -44,7 +55,7 @@ Server -- Handlers
|
|||
----
|
||||
|
||||
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),
|
||||
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.
|
||||
// TODO: link to a place where we discuss the handlers in more details.
|
||||
|
||||
[[eg-server-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 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
|
||||
|
||||
include::server-http-connector.adoc[]
|
||||
include::server-http-handler.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-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]
|
||||
|
||||
include::http/server-http.adoc[]
|
||||
include::http/server-http2.adoc[]
|
||||
include::http/server-websocket.adoc[]
|
||||
include::http2/server-http2.adoc[]
|
||||
include::websocket/server-websocket.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;
|
||||
|
||||
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.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
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.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.HttpConfiguration;
|
||||
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.SslConnectionFactory;
|
||||
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.HandlerList;
|
||||
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.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class HTTPServerDocs
|
||||
|
@ -266,7 +291,7 @@ public class HTTPServerDocs
|
|||
// end::tlsALPNHTTP[]
|
||||
}
|
||||
|
||||
public void tree() throws Exception
|
||||
public void handlerTree()
|
||||
{
|
||||
class LoggingHandler extends AbstractHandler
|
||||
{
|
||||
|
@ -292,7 +317,7 @@ public class HTTPServerDocs
|
|||
}
|
||||
}
|
||||
|
||||
// tag::tree[]
|
||||
// tag::handlerTree[]
|
||||
// Create a Server instance.
|
||||
Server server = new Server();
|
||||
|
||||
|
@ -305,10 +330,459 @@ public class HTTPServerDocs
|
|||
collection.addHandler(new LoggingHandler());
|
||||
|
||||
list.addHandler(new App1Handler());
|
||||
App2Handler app2Handler = new App2Handler();
|
||||
list.addHandler(app2Handler);
|
||||
HandlerWrapper wrapper = new HandlerWrapper();
|
||||
list.addHandler(wrapper);
|
||||
|
||||
app2Handler.setHandler(new ServletHandler());
|
||||
// end::tree[]
|
||||
wrapper.setHandler(new App2Handler());
|
||||
// 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.
|
||||
* 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.
|
||||
*/
|
||||
public class DefaultHandler extends AbstractHandler
|
||||
|
|
|
@ -47,6 +47,8 @@ public class SecuredRedirectHandler extends AbstractHandler
|
|||
return;
|
||||
}
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
HttpConfiguration httpConfig = channel.getHttpConfiguration();
|
||||
if (httpConfig == null)
|
||||
{
|
||||
|
@ -68,7 +70,5 @@ public class SecuredRedirectHandler extends AbstractHandler
|
|||
{
|
||||
response.sendError(HttpStatus.FORBIDDEN_403, "Not Secure");
|
||||
}
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue