views:

83

answers:

1

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.

+1  A: 

So my question is basically: why do the reader from request.getReader() return -1 when reading.

It will return -1 when there is no body or when it has already been read. You cannot read it twice. Make sure that nothing before in the request/response chain has read it.

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.

It will only throw that when you have already called getInputStream() on the request before, not when it is not available.

I have also tried with InputStream using getInputStream() with the same results.

After all, I'd prefer streaming bytes than characters because you then don't need to take character encoding into account (which you aren't doing as far now, this may lead to future problems when you will get this all to work).

BalusC
It seems like the presence of a cookie prevents the POST body from being read by the servlet container. That explains why the following post requests work, since when the cookie is set it works.The solution for me now, a bit awkward though, is to manually transfer all POST parameters to the proxied request entity body, but it works for now. I belive that the correct way to build a Proxy is by using a servlet filter, then control of the entire request is possible.
Ernelli