Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 7
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.
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 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.
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();
xSocket.
Archived Discussions (Read only)