Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Email Address:

Asynchronous HTTP and Comet architectures

An introduction to asynchronous, non-blocking HTTP programming

Print Email Feedback Resources Discuss
Digg Reddit SlashDot Stumble del.icio.us Technorati dzone

Page 4 of 7

Writing a synchronous HTTP proxy

Listing 9 shows an HTTP proxy based on the Servlet API. The servlet's doGet() method will be called each time a new GET request is received. After some proxy-related handling the request will be copied and forwarded using HttpClient. Upon receiving the correlating response some proxy-related handling will be performed and the HttpClient response message will be copied to the servlet response message. After leaving the doGet() method the servlet engine finalizes the response message.

Listing 9. Synchronous proxy example (GET request proxy)

public class ProxyServlet extends HttpServlet {
        
   private final HttpClient httpClient = new HttpClient();
   private String forwardHost;
   private String forwardPort;
        
        
   @Override
   public void init(ServletConfig config) throws ServletException {
      forwardHost = config.getInitParameter("forward.host");
      forwardPort = config.getInitParameter("forward.port");
   }
        
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                
      
      try 
      // compute the new url
      String uri = req.getRequestURI();
      if (req.getQueryString() != null) {
         uri += "?" + req.getQueryString();
      }
      uri = req.getScheme() + "://" + forwardHost + ":" + forwardPort + uri;
                 
      // handle proxy issues (hop-by-hop headers, cache, via header, ...)
      // ...
                 
      // create the forward request 
      HttpRequest forwardRequest = new HttpRequest(req.getMethod(), uri);
        
      // copy the request headers
      for (Enumeration<String> en = req.getHeaderNames(); en.hasMoreElements(); ) {
         String headername = en.nextElement();
         for (Enumeration<String> en2 = req.getHeaders(headername); en2.hasMoreElements(); ) {
            forwardRequest.addHeader(headername, en2.nextElement());
         }
      }
      forwardRequest.setHost(forwardHost + ":" + forwardPort);
                
      // forward the request in a synchronous manner
      HttpResponse response = httpClient.call(forwardRequest);
                
      // handle proxy issues (hop-by-hop headers, ...)
      // ...
                
      // copy the response headers
      response.removeHeader("Server");
      for (String headername : response.getHeaderNameSet()) {
         for (String headervalue : response.getHeaderList(headername)) {
            resp.addHeader(headername, headervalue);
         }
      }
      // copy the body (if exists)
      if (response.hasBody()) {
         byte[] body = response.getBlockingBody().readBytes();
         resp.getOutputStream().write(body);
      }
   }
}

Using the asynchronous HttpClient's send() method instead of the call() method won't help. The current Servlet API doesn't support writing requests out of the scope of the servlet request-handling thread. In essence, the Servlet 2.5 API is insufficient to write an asynchronous message-handling proxy.

Writing an asynchronous HTTP proxy

Writing an asynchronous message-handling proxy requires using an API other than the Servlet 2.5 specification. The HTTP proxy in Listing 10 is based on the same network library (xSocket-http) used in the previous client-side examples. xSocket-http is an extension module of the xSocket network library that supports HTTP programming on the server side, as well as the client side. The network library is independent of the Servlet API and does not implement a servlet container.

Whereas the Servlet API uses an HttpServletResponse object to send a response message, the xSocket-http network library uses an HttpResponseContext object. The xSocket-http network library doesn't pre-create a response message in an implicit way. Furthermore, in contrast to the Servlet API, neither the request object nor the response-context object is bound to the request-handling thread. Both artifacts can be accessed outside the network's library-managed threads.

Like the servlet's doGet() method, the ForwardHandler's onResponse() method will be called each time a request is received. After performing some proxy-related code the received request message will be forwarded using the asynchronous HttpClient's send() method. This method requires a response handler to handle the received response message. As you saw in Listing 2, using the HttpClient's send() method avoids the need for outstanding threads.

The most important aspect of this implementation is that the available threads don't restrict the number of concurrent proxied connections. The scalability of such an asynchronous proxy is only driven by the message-parsing cost and the capability to maintain the required system resources for an open TCP connection in an effective way. Each open TCP connection requires a certain number of socket buffers, control blocks, and file descriptors at the operating-system level.

Listing 10. Asynchronous proxy example

  class ForwardHandler implements IHttpRequestHandler {
   private final HttpClient httpClient = new HttpClient();
   private String forwardHost;
   private int forwardPort;

   public ForwardHandler(String forwardHost, int forwardPort) {
      this.forwardHost = forwardHost;
      this. forwardPort = forwardPort;
   }

   public void onRequest(HttpRequest req, final IHttpResponseContext respCtx) throws IOException {
    
      // handle proxy issues (hop-by-hop headers, cache, via header, ...)
      // ...

      // update the target UTI (Host header will be update automatically)
      req.updateTargetURI(forwardHost, forwardPort);
  
      
      // create the response handler (timeout is not handled here)
      IHttpResponseHandler responseHandler = new IHttpResponseHandler() {

         @Execution(Execution.NONTHREADED)   // performance optimization
         public void onResponse(HttpResponse resp) throws IOException {
            // handle proxy issues (hop-by-hop headers, ...)
            // ...

            // return the response 
            respCtx.send(resp);
         }

         // ...
      };
      
      // .. and forward the request
      try {
         httpClient.send(req, responseHandler);
      } catch (ConnectException ce) {
         respCtx.sendError(502);
      }
   }
}


IServer proxy = new HttpServer(8080, new ForwardHandler("localhost", 80));
proxy.run();
Print Email Feedback Resources
Digg Reddit SlashDot Stumble del.icio.us Technorati dzone
Comment
Welcome, Logout
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
"COMET - The next stage of AJAX " (Alex Russell, IrishDev.com, March 2006) introduced the term Comet to describe the event-driven, server-push data streaming pattern discussed in this article. "New features added to Servlet 2.5" (Jason Hunter, JavaWorld, January 2006) continues the author's JavaWorld series of articles documenting the evolution of the Java Servlet API. "Master Merlin's new I/O classes" (Michael Nygard, JavaWorld, September 2001) introduces the java.nio packages and their premise: to allow Java applications to handle thousands of open connections while delivering scalability and performance. "Architecture of a highly scalable NIO-based server" (Gregor Roth, Java.net, February 2006) describes the fundamental patterns that are used to realize the architecture of a connection-oriented NIO-based server. "Mastering Ajax: Write scalable Comet applications with Jetty and Direct Web Remoting" (Philip McCarthy, IBM developerWorks, July 2007) further introduces the Comet pattern and Jetty 6's Continuations API. "Scripting on the Java platform" (Gregor Roth, JavaWorld, November 2007) features a non-blocking, multithreaded SMTP server example written using JRuby and Java. xSocket-http is an extension module of the NIO-based network library xSocket. The documentation for the current HTTP specification  HTTP/1.1 also includes a definition of chunked transfer encoding. JSR 315: JavaServlet 3.0 Specification documents the current state of Java Servlet API 3.0 The Jetty documentation Wiki includes a page on how Jetty 6 uses continuations to work around the limitations of the Java Sevlet API 2.5. "Advanced IO and Tomcat" describes Tomcat's support for asynchronous message handling and Comet. See the Grizzly project homepage to learn more about Grizzly's approach to Comet. You'll find good, short explanations of HTTP pipelining as well as Comet strategies such as long polling, streaming, and the forever frame implementation on Wikipedia. See the JavaWorld Development Tools Research Center for more about tools, frameworks, and APIs for Java development. Also see Network World's IT Buyer's Guides: Side-by-side comparison of hundreds of products in over 70 categories.



You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser