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:
Simone Bordet 2020-04-10 15:40:02 +02:00
parent d2844bb102
commit 35dce5f315
17 changed files with 1408 additions and 271 deletions

View File

@ -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>

View File

@ -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"));
----

View File

@ -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>

View File

@ -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

View File

@ -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]
----

View File

@ -17,7 +17,7 @@
//
[[server]]
== Jetty Server Libraries
== OLD DOCUMENTATION
include::embedding/chapter.adoc[]
include::maven/chapter.adoc[]

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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[]

View File

@ -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[]

View File

@ -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

View File

@ -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[]

View File

@ -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

View File

@ -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[]
}
}

View File

@ -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

View File

@ -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);
}
}