I'am writing a HTTP proxy that is part of a test/verification system. The proxy filters all requests coming from the client device and directs them towards various systems under test.
The proxy is implemented as a servlet where each request is forwarded to the target system, it handles both GET and POST. Somtimes the response from the target system is altered to fit various test conditions, but that is not the part of the problem.
When forwarding a request, all headers are copied except for those that is part of the actual HTTP transfer such as Content-Length and Connection headers.
If the request is a HTTP POST, then the entity body of the request is forwarded as well and here is where it doesnt work sometimes.
The code reading the entity body from the servlet request is the following:
URL url = new URL(targetURL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
String method = request.getMethod();
java.util.Enumeration headers = request.getHeaderNames();
while(headers.hasMoreElements()) {
String headerName = (String)headers.nextElement();
String headerValue = request.getHeader(headerName);
if (...) { // do various adaptive stuff based on header
}
conn.setRequestProperty(headerName, headerValue);
}
// here is the part that fails
char postBody[] = new char[1024];
int len;
if(method.equals("POST")) {
logger.debug("guiProxy, handle post, read request body");
conn.setDoOutput(true);
BufferedReader br = request.getReader();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
do {
logger.debug("Read request into buffer of size: " + postBody.length);
len = br.read(postBody, 0, postBody.length);
logger.debug("guiProxy, send request body, got " + len + " bytes from request");
if(len != -1) {
bw.write(postBody, 0, len);
}
} while(len != -1);
bw.close();
}
So what happends is that the first time a POST is received, -1 characters are read from the request reader, a wireshark trace shows that the entity body containing URL encoded post parameters are there and it is in one TCP segment so there is no network related differences.
The second time, br.read successfully returns the 232 bytes in the POST request entity body and every forthcoming request works as well.
The only difference between the first and forthcoming POST requests is that in the first one, no cookies are present, but in the second one, a cookie is present that maps to the JSESSION.
Can it be a side effect of entity body not being available since the request processing in the servlet container allready has read the POST parameters, but why does it work on forthcoming requests.
I believe that the solution is of course to ignore the entity body on POST requests containing URL encoded data and fetch all parameters from the servlet request instead using getParameter and reinsert them int the outgoing request.
Allthough that is tricky since the POST request could contain GET parameters, not in our application right now, but implementing it correctly is some work.
So my question is basically: why do the reader from request.getReader() return -1 when reading and an entity body is present in the request, if the entity body is not available for reading, then getReader should throw an illegal state exception. I have also tried with InputStream using getInputStream() with the same results.
All of this is tested on apache-tomcat-6.0.18.