解析 tcpdump 捕获的 HTTP 响应 - 实体为空,但 header 之后有数据

Parsing HTTP response captured by tcpdump - entity is null, but there is data after the header

我正在尝试从 tcpdump 捕获的 .pcap 文件中解析 HTTP 响应消息,使用 pkts.io 解析捕获文件并使用 Apache httpcommons 解析消息。

在解析捕获文件时,我将作为消息一部分的每个数据包的有效负载(通过 Packet.getPayload()doc 获得)附加到 byte[] data.

如果我打印 new String(data, "UTF-8"),我得到这个:

HTTP/1.1 200 OK
    Server: nginx
    Date: Fri, 10 Apr 2015 04:00:04 GMT
    Content-Type: text/html; charset=utf-8
    Transfer-Encoding: chunked
    Connection: keep-alive
    Keep-Alive: timeout=300
    Vary: Accept-Encoding
    Content-Encoding: gzip
    1dd
    ��������������S�n�0��+X_���
��q�b�a���������Ȓf�q��G�K�I��=���������χ/�rg�f�d"kʌ\�+1l���P
]�\^�@r�{�k��;pģ﷐�7�=t� `C+5qg�
...

Full response on pastebin

当我尝试解析 HTTP 消息(下面的代码)时,我得到了所有 headers 没问题,但是 resp.getEntity() returns null.

SessionInputBufferImpl inBuffer = new SessionInputBufferImpl(new HttpTransportMetricsImpl(), packet.getData().length);
InputStream inStream = new ByteArrayInputStream(packet.getData());
inBuffer.bind(inStream);
DefaultHttpResponseParser respParser = new DefaultHttpResponseParser(inBuffer);
HttpResponse resp = (HttpResponse) respParser.parse();

从这里我可以去哪里尝试以文本形式获取响应body?

获取实体主体时,您需要同时查看传输编码和内容编码,并进行适当的解码。参见 section 4 "Transfer Codings" of RFC 7230

查看 类 中的 HttpComponents,例如 ChunkedInputStream(用于分块传输编码)并查找可以解压缩 gzip 文本的代码(用于 gzip 内容编码)。

我无法让 HttpResponse.getEntity() 工作,所以我不得不自己解析响应。这是我拼凑的代码。它遍历包含整个响应内容的 byte[],寻找分隔 header 字段和 body 的空行,并复制之后的所有内容:

private byte[] getContent(byte[] message) {
    int start = -1;
    byte[] content = null;
    for (int i = 0; i < message.length; ++i) {
        if (start >= 0) {
            content[i-start] = message[i];
            continue;
        }
        System.out.print((char)message[i]);
        if (message[i] == (byte) 13 && message[i+1]==(byte)10 && message[i+2] == (byte) 13 && message[i+3]==(byte)10 ) { //CR
            start = i+4;
            content = new byte[message.length-(i+4)];
            i += 3;
        }
    }
    return content;
}

然后,如果响应有 Transfer-Encoding: chunkedContent-Encoding: gzip,我使用 ChunkedInputStream(来自 HttpComponents)和 GZIPInputStream 来自 java.util 来获取实际返回内容。

byte[] content = getContent(packet.getData());
if (content.length > 0) {
    InputStream byteIS = new ByteArrayInputStream(content);
    SessionInputBufferImpl contentBuf = new SessionInputBufferImpl(new HttpTransportMetricsImpl(), content.length);
    contentBuf.bind(byteIS);

    ChunkedInputStream chunkedIS = new ChunkedInputStream(contentBuf);

    GZIPInputStream gzipIS = new GZIPInputStream(chunkedIS);

    while (gzipIS.available() != 0) {
        byte[] buf = new byte[128];
        gzipIS.read(buf);
        contentBuilder.append(new String(buf, "UTF-8"));
    }
    gzipIS.close();
    String contentString = contentBuilder.toString();
}