jsr340-experts@servlet-spec.java.net

[jsr340-experts] Initial draft of Upgrade Proposal

From: Shing Wai Chan <shing.wai.chan_at_oracle.com>
Date: Fri, 14 Oct 2011 16:38:47 -0700

Hi All,

Please review and provide input on the initial draft of upgrade proposal.
Thanks.

Shing Wai Chan


          Introduction and Assumptions

  * Upgrade requests for protocols that should be transparent to
    existing servlets (e.g., "TLS") will be handled by built-in code in
    the web container; we do not intend to make this support extensible.

  * Upgrade requests for new protocols that will be exposed through new
    APIs (e.g., web sockets), are the subject of this upgrade support in
    the servlet API.

  * We assume that a given endpoint need only support "one" of these
    protocols. That is, we don't have to design to support an upgrade
    request to "websockets, wombats", where websockets and wombats are
    independent protocols. This means the servlet at a given endpoint
    can be specific to the protocol expected to be used with that endpoint.

  * TBD is how to handle an upgrade to "websockets, TLS", where one of
    the protocols is a built-in protocol.

  * There will be a single URL namespace on the server, shared by all
    endpoints no matter which protocol they might be using.

  * The interface between the web container and the protocol handler can
    be in terms of byte streams, with the protocol handler assembling
    the byte streams into messages or whatever abstraction it wants to
    present to its clients. The web sockets API is not expected to
    expose byte streams.


          APIs

  * We define two new classes in javax.servlet.http:

package javax.servlet.http;

public interface WebConnection {
     public InputStream getInputStream();
     public OutputStream getOutputStream();
}

package javax.servlet.http;

public interface ProtocolHandler {
     public void init(WebConnection wc);
     public void inputAvailable(WebConnection wc);
     public void outputReady(WebConnection wc);
}

Note: the use of streams and notification methods in these interfaces
will need to be made consistent with any non-blocking I/O support added
to the Servlet API. Consider this a rough sketch to give you the general
idea of how this could work.

  * Servlet container will provide a HTTP upgrade mechanism. And Servlet
    container itself will have no knowledge about the upgraded protocol.
    The protocol processing is encapsulated in
    javax.servlet.http.ProtocolHandler. And a new API will be added to
    javax.servlet.http.HttpServletRequest:

    /**
      * This notifies the servlet container that the given ProtocolHandler would be
      * to upgrade the request.
      *
      * @param handler
      */
    protected void upgrade(ProtocolHandler handler);

      o When a upgrade request is sent to web container and if one
        decides to do an upgrade,
        HttpServletRequest#upgrade(ProtocolHandler) will then be
        invoked. The application prepares and sends an appropriate
        response to the client as usual.
      o At this point the web container unwinds all the servlet filters
        and marks the connection to be handled by the protocol handler.
        It then calls the protocol handler's init method with a
        WebConnection object that provides access to the data streams.
      o When new data on the connection is ready to read, the web
        container notifies the protocol handler by calling
        #inputAvailable method. The protocol handler read the data, and
        can assemble it into a new protocol message if necessary. When
        it has enough data to form a complete message, the message will
        be passed to a new protocol component. Then a response of the
        given protocol will be prepared and written to the client when
        the ProtocolHandler#outputReady method is invoked. Both input
        and output will be non-blocking (depending on non-blocking support.)
  * The servlet filters only process the initial http request and
    response. They aren't invoked at all for subsequent data on the
    connection. In other words, it does "not" process the "upgraded"
    request and response.


          WebSocket as an example of upgrade (not part of Servlet spec)

  * The web sockets runtime provides a ServletContainerInitializer
    annotated with @HandlesTypes(WebSocket.class). The initializer
    handles all the web socket components and uses
    ServletContext.addServlet() to add an implementation-specific
    WebSocketsServlet at the endpoint corresponding to each web sockets
    component.
    The WebSocketServlet.service method does this:
      o check that it's a GET request (or any other limitations on the
        type of request allowed for web sockets)
      o check that the "Upgrade" header contains "webSocket" and the
        "Connection" header is "Upgrade"
      o if not, return an error response to the client
      o call: new WebSocketsProtocolHandler(req, res)
        That is, construct an implementation-specific protocol handler
        for this web sockets request, passing it the HttpServletRequest
        and HttpServletResponse objects that contain all the context
        information the protocol handler might need.
      o call: req.upgrade(handler)
        Tell the web container to upgrade the connection to the new
        protocol specified by the web sockets protocol handler.
      o send a success response with status code 101 to the client
      o return from the service method

  * WebSocket will build on the top of the HttpServlet#upgrade mechanism.
    As an example, it can be as follows:
    One may define an annotation @WebSocket. The annotated class will
    implement an interface having methods processing the Web Socket request,
    for instance, #onOpen, #onClose, #onTimeout, #onMessage. And the web
    socket runtime will also provide a WebSocketServlet.

  * WebSocketsProtocolHandler is an implementation-specific class,
    written by the same person who wrote the WebSocketsServlet class.
    The constructor gave it all the context it needed about the request
    to find the web sockets component corresponding to this endpoint and
    to pass messages to the component.