blob: 48b3b00d2b2e53b4d175bb2679009da71fa08713 [file] [log] [blame]
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ========================================================================
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
[[continuations-patterns]]
=== Common Continuation Patterns
==== Suspend Resume Pattern
The suspend/resume style is used when a servlet and/or filter is used to generate the response after an asynchronous wait that is terminated by an asynchronous handler.
Typically a request attribute is used to pass results and to indicate if the request has already been suspended.
[source, java, subs="{sub-order}"]
----
void doGet(HttpServletRequest request, HttpServletResponse response)
{
// if we need to get asynchronous results
Object results = request.getAttribute("results");
if (results==null)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);
// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(response);
return;
}
// suspend the request
continuation.suspend(); // always suspend before registration
// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
continuation.setAttribute("results",results);
continuation.resume();
}
});
return; // or continuation.undispatch();
}
// Send the results
sendMyResultResponse(response,results);
}
----
This style is very good when the response needs the facilities of the servlet container (e.g., it uses a web framework) or if one event may resume many requests so the container's thread pool can be used to handle each of them.
==== Suspend Continue Pattern
The suspend/complete style is used when an asynchronous handler is used to generate the response:
[source, java, subs="{sub-order}"]
----
void doGet(HttpServletRequest request, HttpServletResponse response)
{
final Continuation continuation = ContinuationSupport.getContinuation(request);
// if this is not a timeout
if (continuation.isExpired())
{
sendMyTimeoutResponse(request,response);
return;
}
// suspend the request
continuation.suspend(); // response may be wrapped.
// register with async service. The code here will depend on the
// the service used (see Jetty HttpClient for example)
myAsyncHandler.register(new MyHandler()
{
public void onMyEvent(Object result)
{
sendMyResultResponse(continuation.getServletResponse(),results);
continuation.complete();
}
});
}
----
This style is very good when the response does not need the facilities of the servlet container (e.g., it does not use a web framework) and if an event will resume only one continuation.
If many responses are to be sent (e.g., a chat room), then writing one response may block and cause a DOS on the other responses.
==== Examples
* The https://github.com/eclipse/jetty.project/blob/jetty-8/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java[ChatServlet example] shows how the suspend/resume style can be used to directly code a chat room (See similar https://github.com/eclipse/jetty.project/blob/master/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java[example] using Async Servlets).
The same principles are applied to frameworks like http://cometd.org/[cometd] which provide an richer environment for such applications, based on Continuations.
* The link:{JDURL}/org/eclipse/jetty/servlets/QoSFilter.html[QoSFilter] uses suspend/resume style to limit the number of requests simultaneously within the filter.
This can be used to protect a JDBC connection pool or other limited resource from too many simultaneous requests.
+
If too many requests are received, the extra requests wait for a short time on a semaphore, before being suspended.
As requests within the filter return, they use a priority queue to resume the suspended requests.
This allows your authenticated or priority users to get a better share of your server's resources when the machine is under load.
+
* The link:{JDURL}/org/eclipse/jetty/servlets/DoSFilter.html[DosFilter] is similar to the QoSFilter, but protects a web application from a denial of service attack, as much as is possible from within a web application.
+
If too many requests are detected coming from one source, then those requests are suspended and a warning generated.
This works on the assumption that the attacker may be written in simple blocking style, so by suspending you are hopefully consuming their resources. True protection from DOS can only be achieved by network devices (or eugenics :)).
+
* The link:{JDURL}/org/eclipse/jetty/proxy/ProxyServlet.html[ProxyServlet] uses the suspend/complete style and the Jetty asynchronous HTTP client to implement a scalable Proxy server (or transparent proxy).