问题在servlet中读取请求体

Ern*_*lli 2 java post servlets http tomcat6

我正在编写一个HTTP代理,它是测试/验证系统的一部分.代理过滤来自客户端设备的所有请求,并将它们引导到各种受测试的系统.

代理实现为servlet,每个请求都转发到目标系统,它处理GET和POST.有时,目标系统的响应会被改变以适应各种测试条件,但这不是问题的一部分.

转发请求时,将复制所有标头,但实际HTTP传输的标头除外,例如Content-Length和Connection标头.

如果请求是HTTP POST,那么请求的实体主体也会被转发,这里有时它不起作用.

从servlet请求中读取实体主体的代码如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

//这里是失败的部分

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();
}
Run Code Online (Sandbox Code Playgroud)

所以发生的事情是,第一次收到POST时,从请求读取器读取-1个字符,wireshark跟踪显示包含URL编码的post参数的实体主体在那里并且它在一个TCP段中,因此没有网络相关差异.

第二次,br.read成功返回POST请求实体主体中的232个字节,并且每个即将发出的请求也都有效.

第一个和即将发布的POST请求之间的唯一区别是,在第一个请求中,不存在cookie,但在第二个中,存在映射到JSESSION的cookie.

由于servlet容器中的请求处理已经读取了POST参数,因此它可能是实体主体不可用的副作用,但为什么它适用于即将到来的请求.

我认为解决方案当然是忽略包含URL编码数据的POST请求的实体主体,并使用getParameter从servlet请求中获取所有参数,并将它们重新插入传出请求中.

尽管这很棘手,因为POST请求可能包含GET参数,而不是现在我们的应用程序,但正确实现它是一些工作.

所以我的问题基本上是:为什么来自request.getReader()的阅读器在读取时返回-1并且请求中存在实体主体,如果实体主体不可用于读取,则getReader应该抛出非法状态异常.我也尝试使用具有相同结果的getInputStream()与InputStream.

所有这些都在apache-tomcat-6.0.18上进行了测试.

Bal*_*usC 5

所以我的问题基本上是:为什么request.getReader()的阅读器在阅读时返回-1.

当没有身体或已经被读取时,它将返回-1 .你不能读两遍.确保请求/响应链中的任何内容都没有读过它.

并且请求中存在实体主体,如果实体主体不可读,则getReader应该抛出非法状态异常.

它只会在您之前已经调用getInputStream()过请求时抛出,而不是在它不可用时抛出.

我也尝试使用具有相同结果的getInputStream()与InputStream.

毕竟,我更喜欢流式传输字节而不是字符,因为您不需要考虑字符编码(现在你没有做到这一点,这可能会导致将来出现问题) .