Improvements to the Jetty server documentation.

Documented HttpChannel.Listener events.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-04-14 16:01:37 +02:00
parent 474fa8b8e9
commit 79c90402e5
3 changed files with 154 additions and 3 deletions

View File

@ -40,7 +40,7 @@ connections on the listening port.
When a connection is accepted, `ServerConnector` wraps the accepted When a connection is accepted, `ServerConnector` wraps the accepted
`SocketChannel` and passes it to the `SocketChannel` and passes it to the
xref:eg-io-arch-selector-manager[`SelectorManager`]. xref:eg-io-arch-selector-manager[`SelectorManager`].
Therefore there is a little moment where the acceptor thread is not accepting 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 new connections because it is busy wrapping the just accepted one to pass it
to the `SelectorManager`. to the `SelectorManager`.
Connections that are ready to be accepted but are not accepted yet are queued Connections that are ready to be accepted but are not accepted yet are queued

View File

@ -74,8 +74,110 @@ by the Servlet specification.
All these features are provided by the Jetty Server Libraries and server All these features are provided by the Jetty Server Libraries and server
applications only need to put the required components together to provide applications only need to put the required components together to provide
all the required features. all the required features, and it is discussed in details in
// TODO: link to a place where we discuss the handlers in more details. xref:eg-server-http-handler-use[this section].
[[eg-server-http-request-processing]]
==== Server Request Processing
The Jetty HTTP request processing is outlined below in the diagram below.
Request handing is slightly different for each protocol; in HTTP/2 Jetty
takes into account multiplexing, something that is not present in HTTP/1.1.
However, the diagram below captures the essence of request handling that
is common among all protocols that carry HTTP requests.
First, the Jetty I/O layer emits an event that a socket has data to read.
This event is converted to a call to `AbstractConnection.onFillable()`,
where the `Connection` first reads from the `EndPoint` into a `ByteBuffer`,
and then calls a protocol specific parser to parse the bytes in the
`ByteBuffer`.
The parser emit events such that are protocol specific; the HTTP/2 parser,
for example, emits events for each HTTP/2 frame that has been parsed.
The parser events are then converted to protocol independent events such
as _"request start"_, _"request headers"_, _"request content chunk"_, etc.
that in turn are converted into method calls to `HttpChannel`.
When enough of the HTTP request is arrived, the `Connection` calls
`HttpChannel.handle()` that calls the `Handler` chain, that eventually
calls the server application code.
[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false
participant ManagedSelector
participant EndPoint
participant Connection
participant Parser
participant HttpChannel
participant Server
participant Handlers
ManagedSelector -> EndPoint : read ready
EndPoint -> Connection : onFillable()
Connection -> EndPoint : fill()
EndPoint --> Connection
Connection -> Parser : parse()
Parser -> HttpChannel : events
Connection -> HttpChannel : handle()
HttpChannel -> Server : handle()
Server -> Handlers : handle()
----
[[eg-server-http-channel-events]]
===== HttpChannel Events
The central component processing HTTP requests is `HttpChannel`.
There is a 1-to-1 relationship between an HTTP request/response and an
`HttpChannel`, no matter what is the specific protocol that carries the
HTTP request over the network (HTTP/1.1, HTTP/2 or FastCGI).
Advanced server applications may be interested in the progress of the
processing of an HTTP request/response by `HttpChannel`.
A typical case is to know exactly _when_ the HTTP request/response
processing is complete, for example to monitor processing times.
NOTE: A `Handler` or a Servlet `Filter` may not report precisely when
an HTTP request/response processing is finished.
A server application may write a small enough content that is aggregated
by Jetty for efficiency reasons; the write returns immediately, but
nothing has been written to the network yet.
`HttpChannel` notifies ``HttpChannel.Listener``s of the progress of the
HTTP request/response handling.
Currently, the following events are available:
* `requestBegin`
* `beforeDispatch`
* `dispatchFailure`
* `afterDispatch`
* `requestContent`
* `requestContentEnd`
* `requestTrailers`
* `requestEnd`
* `responseBegin`
* `responseCommit`
* `responseContent`
* `responseFailure`
* `responseEnd`
* `complete`
Please refer to the `HttpChannel.Listener`
link:{JDURL}/org/eclipse/jetty/server/HttpChannel.Listener.html[javadocs]
for the complete list of events.
Server applications can register `HttpChannel.Listener` by adding them as
beans to the `Connector`:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/server/http/HTTPServerDocs.java[tags=httpChannelListener]
----
include::server-http-connector.adoc[] include::server-http-connector.adoc[]
include::server-http-handler.adoc[] include::server-http-handler.adoc[]

View File

@ -20,6 +20,8 @@ package embedded.server.http;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@ -37,6 +39,7 @@ import org.eclipse.jetty.rewrite.handler.RedirectRegexRule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler; import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.rewrite.handler.RewriteRegexRule; import org.eclipse.jetty.rewrite.handler.RewriteRegexRule;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.ProxyConnectionFactory; import org.eclipse.jetty.server.ProxyConnectionFactory;
@ -68,6 +71,8 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import static java.lang.System.Logger.Level.INFO;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class HTTPServerDocs public class HTTPServerDocs
{ {
@ -104,6 +109,50 @@ public class HTTPServerDocs
// end::simple[] // end::simple[]
} }
public void httpChannelListener() throws Exception
{
// tag::httpChannelListener[]
class TimingHttpChannelListener implements HttpChannel.Listener
{
private final ConcurrentMap<Request, Long> times = new ConcurrentHashMap<>();
@Override
public void onRequestBegin(Request request)
{
times.put(request, System.nanoTime());
}
@Override
public void onComplete(Request request)
{
long begin = times.remove(request);
long elapsed = System.nanoTime() - begin;
System.getLogger("timing").log(INFO, "Request {0} took {1} ns", request, elapsed);
}
}
Server server = new Server();
Connector connector = new ServerConnector(server);
server.addConnector(connector);
// Add the HttpChannel.Listener as bean to the connector.
connector.addBean(new TimingHttpChannelListener());
// Set a simple Handler to handle requests/responses.
server.setHandler(new AbstractHandler()
{
@Override
public void handle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{
jettyRequest.setHandled(true);
}
});
server.start();
// end::httpChannelListener[]
}
public void configureConnector() throws Exception public void configureConnector() throws Exception
{ {
// tag::configureConnector[] // tag::configureConnector[]