The Jetty HTTP Server
Introduction
This section describes the important concepts in the Jetty HttpServer
model. It should be read by those with an interest in gaining a
deeper insight into the Jetty architecture and those considering writing
custom handlers.
The Introduction to the HttpServer explained that the server accepts
requests and passes them off to handlers for processing but did not elaborate
on how this is acheived. This section discusses the server, listener,
context and handler entities and the relationships between them.
HttpServer
The org.mortbay.http.HttpServer
class provides a linkage between a collection of request listeners
and collections of request handlers:
HttpListener n-->1 HttpServer 1-->n HttpContext 1-->n HttpHandler
Diagram: HttpServer relationship model
|
It is the responsibility of an HttpServer to accept requests received
by an HttpListener, and match them to suitable HttpContext(s). It does
this by using the host and context
path elements from the request. Note that more than one HttpContext
might match the request, and in this case, all HttpContexts are tried
in the order in which they were registered with the server until
the request is marked as having been handled.
The trivial code snippet from the Introduction to the HttpServer can
then be represented as:
SocketListener1-->1HttpServer1-->1HttpContext1-->1ResourceHandler
port:8080 "/" "./docroot"
Diagram: Trivial file server object relationships
|
This depicts a single listener on port 8080 passing requests
to a single server, which in turn passes them to a single context with a
single handler which returns static content from the directory ./docroot
.
HttpListener
Implementations of the
org.mortbay.http.HttpListener interface are added to a HttpServer
and act as sources of requests for the server. The
org.mortbay.http.SocketListener is the main implementation.
It listens on a standard TCP/IP port for requests, but there are also
listener implementations for SSL,
Non blocking IO, testing and others.
Multiple listeners may be used to listen on different ports and/or
on specific IP addresses. This is most frequently used with SSL or with
multi-hosting:
SocketListener -+
port:80 |
|
JSSEListener -+> HttpServer --> HttpContext --> ResourceHandler
port:443 | "/" "./docroot"
|
SocketListener -+
host:1.2.3.4
port: 80
Diagram: Multiple listeners with multihosting |
Listeners are configured via set methods. Listeners can
be created by using the HttpServer as a factory to create a standard
type of listener:
HttpServer server=new HttpServer();
HttpListener listener=
server.addListener(new InetAddrPort("myhost",8080));
Code Example: Convenience methods for adding standard
listeners |
However, in order to provide detailed configuration, it is
more common to create the listener directly and then add it to the
HttpServer:
HttpServer server = new HttpServer();
SocketListener listener = new SocketListener();
listener.setHost("myhost");
listener.setPort(8080); listener.setMinThreads(5);
listener.setMaxThreads(250);
server.addListener(listener);
Code Example: Configuring a listener |
All HttpListeners are responsible for allocating threads to requests,
so most implementations are extensions of the
org.mortbay.util.ThreadedServer or
org.mortbay.util.ThreadPool. Thus attributes such as min/max
threads, min/max idle times etc are also set via the API.
Jetty has several type of HttpListeners including:
HttpContext
A org.mortbay.http.HttpContext
aggregates
org.mortbay.http.HttpHandler implementations. When a request is
passsed to a HttpContext it tries each of its HttpHandlers in turn (
in the order in which they were registered) until the request is
marked as handled. Note that it is perfectly possible for more than one
handler to process the request, but only one handler can mark the request
as being finally handled.
Note that in Jetty 3.1 and previous releases, the HttpContext class was called HandlerContext.
|
A typical a context might have handlers for security, servlets and static
resources:
+-> SecurityHandler
|
SocketListener --> HttpServer --> HttpContext +-> ServletHandler
port: 80 "/" |
+-> ResourceHandler
Diagram: Single context, multiple handlers |
All HttpHandlers within a single HttpContext share the following attributes:
- Initialization parameters
- An optional virtual host name for the context
- A path prefix for the context
- A resource base for loading static resources (files/urls)
- A memory cache of resources (files/urls)
- A ClassLoader and set of Java permissions
- A request log
- Statistics
- Error page mappings
- MIME type suffix maps
A single HttpServer can have multiple HttpContexts. This is
typically used to serve several applications from the same port(s) using
URL mappings:
SocketListener --> HttpServer +-> HttpContext --> HttpHandler(s)
port:80 | path:"/alpha/*"
|
+-> HttpContext --> HttpHandler(s)
path:"/beta/*"
Diagram: Multiple contexts with URL mapping |
Alternatively, different applications can be served from the
same port using virtual hosts:
SocketListener --> HttpServer +-> HttpContext --> HttpHandler(s)
port:80 | vhost: www.alpha.com
| path: "/"
|
+-> HttpContext --> HttpHandler(s)
vhost: www.beta.com
path: "/"
Diagram: Multiple contexts with virtual hosts |
If multiple contexts are to be served from the same port, but on different
IP addresses, then it is possible to give each context its own HttpServer:
SocketListener --> HttpServer --> HttpContext --> HttpHandler(s)
host:www.alpha.com path:"/"
port:80
SocketListener --> HttpServer --> HttpContext --> HttpHandler(s)
host:www.beta.com path: "/"
port:80
Diagram: Multiple servers
|
HttpContexts can be instantiated by the HttpServer as part of a
call to addContext()
with context args:
HttpContext context = server.addContext("/mydocs/*");
context.setResourceBase("./docroot/");
Code Example: Implied context creation |
As addContext() will always create a new context instance, it is
possible to accidentally create multiple copies of the same context (by
calling addContext() with the same parameters). To avoid this, you can
use the
getContext()
method instead, which will only create a new context
if one with the same specification does not already exist:
HttpContext context = server.getContext("myhost","/mydocs/*");
context.setResourceBase("./docroot/");
Code Example: Lazy context creation |
The previous example highlights that it is possible specify a virtual
host as well as the context path. A single context may be registered with
different virtual hosts. Once context configuration becomes complex, it
is best to take explicit control over context creation:
HttpContext context = new HttpContext();
context.setContextPath("/context/*");
context.setVirtualHosts(new String[]{"alpa.com","beta.com"});
context.setResourceBase("./docroot/");
server.addContext(context);
Code Example: Creating a context with multiple
virtual hosts |
Developers
Note: Derivations of HttpServer may implement the
newHttpContext()
method to change the factory method for creating new contexts.
This is used, for example, by the
org.mortbay.jetty.Server class to return HttpContext derivations
that have convenience methods for configuring servlets. The
org.mortbay.jetty.servlet.WebApplicationContext class is
a specialization of HttpContext that configures the handlers by looking
at the standard web application XML files.
|
HttpHandler
The org.mortbay.http.HttpHandler
interface represents Jetty's core unit of content generation
or manipulation. Implementations of this interface can be used to modify
or handle requests. Typically, handlers are arranged in a list, and
a request presented to each handler in turn until (at most) one indicates
that the request has been handled. This allows handlers to:
- Ignore
requests that are not applicable
- Handle
requests by populating the response and/or generating content
- Modify
the request but allow it to pass onto the next handler(s). Headers and
attributes may be modified or an InputStream filter added
- Modify
the response but allow the request to pass onto the next handler(s).
Headers may be modified or OutputStream filters added.
The handlers provided with the
org.mortbay.http.handler package are:
A ServletHandler
and
WebApplicationHandler
are provided by the
org.mortbay.jetty.servlet package and is discussed in detail
in the Jetty Server section.
HttpHandlers are tried within a context in the order they were added
to the HttpContext. The following code creates a context that checks
authentication, then tries a servlet mapping before trying static content
then finally dropping through to an error page generator if no handler marks
the request as handled:
HttpContext context = server.addContext("/"); context.add(new SecurityHandler()); context.add(new ServletHandler()); context.add(new ResourceHandler()); context.add(new NotFoundHandler());
Code Example: Importance
of handler ordering |
Resource Handler
One of the most common things for a HttpServer to do is to serve static
content from a base directory or URL. The
org.mortbay.http.handler.ResourceHandler implementation
of HttpHandler is provided for this purpose. Its features include:
- Support for GET, PUT, MOVE, DELETE, HEAD and OPTIONS methods.
- Handling of IfModified headers.
- HTTP/1.1 Range support for partial content serving.
- Index/welcome files.
- Generation of directory listings.
The root directory or URL for serving static content is the ResourceBase
of the HttpContext. Thus, to serve static content from the directory
"./docroot/":
HttpContext context = server.getContext("/context/*");
context.setResourceBase("./docroot/");
ResourceHandler handler = new ResourceHandler();
handler.setDirAllowed(true);
handler.setPutAllowed(false);
handler.setDelAllowed(false);
handler.setAcceptRanges(true);
context.addHandler(handler);
context.addHandler(new NotFoundHandler());
Code Example: Detailed configuration of a ResourceHandler
|
The NotFoundHandler is added to generate a 404 for requests for
resources that don't exist. The ResourceHandler lets requests that
it cannot handle fall through to the next handler.
Developers Note:
HttpHandlers ARE ORDER DEPENDANT!. If in the above example the NotFoundHandler
had been added to the context before the ResourceHandler, then all
requests would be 404'd and resources would not be served. It is a common
mistake to put a ResourceHandler before a
ServletHandler with the JSPServlet, so jsp source code is served
rather than the dynamic content from the JSPServlet.
|
Putting It All Together
Finally, here is a fully worked code example to configure a server on
port 8181 serving static content and a dump servlet at "/mystuff/"
:
import java.io.*;
import java.net.*;
import org.mortbay.util.*;
import org.mortbay.http.*;
import org.mortbay.jetty.servlet.*;
import org.mortbay.http.handler.*;
import org.mortbay.servlet.*;
public class SimpleServer
{
public static void main (String[] args)
throws Exception
{
// Create the server
HttpServer server=new HttpServer();
// Create a port listener
SocketListener listener=new SocketListener();
listener.setPort(8181);
server.addListener(listener);
// Create a context
HttpContext context = new HttpContext();
context.setContextPath("/mystuff/*");
server.addContext(context);
// Create a servlet container
ServletHandler servlets = new ServletHandler();
context.addHandler(servlets);
// Map a servlet onto the container
servlets.addServlet("Dump","/Dump/*","org.mortbay.servlet.Dump");
// Serve static content from the context
String home = System.getProperty("jetty.home",".");
context.setResourceBase(home+"/demo/webapps/jetty/tut/");
context.addHandler(new ResourceHandler());
// Start the http server
server.start ();
}
}
|
Code Example: Setting up an HttpServer |