Dominic Cleal's Blog

Mon, 07 Jul 2008 22:49:22 GMT

permalink The devil's in the detail

A seemingly innocent refactor of a Java EE web application last week turned into a small nightmare due to a tiny detail in the servlet specification that I hadn't taken into account.

The webapp's main purpose is to proxy requests to a backend server using the jEasy Extensible Proxy (J2EP) project (from Google SoC). This allows us to create a custom implementation of the logic for choosing a backend server to route requests to (linked with user sessions, the user's given permissions etc) with very little effort.

J2EP is implemented as a filter, with a second rewriting filter layered on top of the proxying filter. Originally, the module for J2EP was performing session validation and contained more logic for handling the server choices - the refactor moved this validation procedure out into its own filter, layered on top of the rewriting filter.

Once this was done, after lots of puzzling debug output, that the data in POST requests to the server was simply missing after passing through the proxy server. The detail that caught me out was hidden in the JavaDoc for ServletRequest.getParameter(String name):

public java.lang.String getParameter(java.lang.String name)
Returns the value of a request parameter as a String, or null if the parameter does not exist. Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.

[..]

If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method.


As it turns out, the code that was moved to the new, top layer filter called getParameter() in a couple of places. The J2EP proxy filter was later using getInputStream() to pass the request parameters into the new outbound request. Even though it was after the initial parameter read, the reverse of the situation mentioned in the specification caused getInputStream() to break and return an empty stream!

I wish an IllegalStateException had simply been thrown rather than returning useless streams... *sigh*

(note: this was under Apache Tomcat 5.5)
Archives


Comments for this entry are now closed.