【问题标题】:Parsing HTTP response captured by tcpdump - entity is null, but there is data after the header解析 tcpdump 捕获的 HTTP 响应 - 实体为空,但标头后有数据
【发布时间】:2015-06-18 00:40:40
【问题描述】:

我正在尝试从 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 消息(下面的代码)时,我得到了所有标题,但 resp.getEntity() 返回 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();

我可以从哪里尝试将响应正文作为文本获取?

【问题讨论】:

    标签: java pcap apache-httpcomponents


    【解决方案1】:

    获取实体主体时,您需要同时查看 Transfer-Encoding 和 Content-Encoding,并进行适当的解码。见section 4 "Transfer Codings" of RFC 7230。

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

    【讨论】:

    • 这两个都只适用于消息的内容,不是吗? (即,如果实体不为空,我想包装 someHttpResponse.getEntity().getContent() 返回的 InputStream。)
    • 如果 HTTP 响应在 Content-Encoding: gzip 和 1dd 之间确实有 no 空行,则它的格式不正确,因为标头之间应该有一个空行 (CRLF)领域和身体。如果是这样,那么您不应该期望它是可解析的,尽管 HttpComponents 应该指示错误。如果它确实有一个空行,那么你应该得到一个实体,如果 HttpComponents 没有为你解压和解压缩它,你需要连接一些东西以便它这样做。
    • 嗯。我尝试手动获取内容,我看到了双 CRLF,并且可以使用包装在 gzip 流中的分块流中的字节数组流来解析它。但是HttpResponse 对象仍然为实体返回 null。
    • 响应是否在多个 TCP 段中发送?如果是这样,是否通过您的代码或 HttpComponents 重新组装 TCP 段?复制您的 pastebin 项目并计算其中的字节数表明它有 4225 个字节,这可能不适合一个 TCP 段,因此它可能已被重新组装。这是否包括所有的块?
    • 是的,我的代码正在重新组装它,是的。我对HttpResponse 使用与我的代码相同的byte[],并且我已经使用Wireshark 仔细检查了消息中应该包含的数据包编号/内容。
    【解决方案2】:

    我无法让HttpResponse.getEntity() 工作,所以我不得不自己解析响应。这是我放在一起的代码。它遍历包含整个响应内容的byte[],寻找分隔标题字段和正文的空白行,然后复制所有内容:

    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: chunked 和Content-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();
    }
    

    【讨论】:

    • 谢谢。太蹩脚了,如何使用 DefaultHttpResponseParser 轻松准备正文并不明显。
    猜你喜欢
    • 2015-09-28
    • 1970-01-01
    • 2021-07-08
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多