欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

不规范的Http 204应答触发Apache的Http协议解析失败  

程序员文章站 2024-03-13 13:49:21
...

今天在Server发现一个错误日志:

 

写道
ParseException#Not a valid protocol version: ""HTTP/1.1 201 Created#org.apache.http.ParseException: Not a valid protocol version: ""HTTP/1.1 201 Created
org.apache.http.message.BasicLineParser.parseProtocolVersion(BasicLineParser.java:147)
org.apache.http.message.BasicLineParser.parseStatusLine(BasicLineParser.java:365)
org.apache.http.impl.nio.codecs.DefaultHttpResponseParser.createMessage(DefaultHttpResponseParser.java:114)
org.apache.http.impl.nio.codecs.DefaultHttpResponseParser.createMessage(DefaultHttpResponseParser.java:51)

 

找到对应的代码:

 

 

    public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
                                                final ParserCursor cursor) throws ParseException {
        Args.notNull(buffer, "Char array buffer");
        Args.notNull(cursor, "Parser cursor");
        final String protoname = this.protocol.getProtocol();
        final int protolength  = protoname.length();

        final int indexFrom = cursor.getPos();
        final int indexTo = cursor.getUpperBound();

        skipWhitespace(buffer, cursor);

        int i = cursor.getPos();

        // long enough for "HTTP/1.1"?
        if (i + protolength + 4 > indexTo) {
            throw new ParseException
                ("Not a valid protocol version: " +
                 buffer.substring(indexFrom, indexTo));
        }

        // check the protocol name and slash
        boolean ok = true;
        for (int j=0; ok && (j<protolength); j++) {
            ok = (buffer.charAt(i+j) == protoname.charAt(j));
        }
        if (ok) {
            ok = (buffer.charAt(i+protolength) == '/');
        }
        if (!ok) {
            throw new ParseException
                ("Not a valid protocol version: " +
                 buffer.substring(indexFrom, indexTo));
        }

 

 

说明协议栈在读取buffer.substring(indexFrom, indexTo)的时候读到的是""HTTP/1.1 201 Created,前面有两个双引号。抓包发现,在同一个长连接上出现这种情况:

 

写道
GET /check HTTP/1.1
Host: abc.com
Connection: keep-alive
Cache-Control: max-age=0
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6


HTTP/1.1 204 OK
Date: Sat, 12 Nov 2016 04:41:29 GMT
Content-Length: 2

""Get /creat HTTP/1.1
Host: abc.com
Connection: keep-alive
Cache-Control: max-age=0
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

HTTP/1.1 201 Created
Date: Sat, 12 Nov 2016 04:41:29 GMT
Content-Length: 2

...
 
大家注意我标红的这个应答,返回的是204,却标明了Content-Length: 2,这个应答是不符合HTTP协议定义的:
https://tools.ietf.org/html/rfc7230#section-3.3.1

3.3.3. Message Body Length

   The length of a message body is determined by one of the following
   (in order of precedence):

   1.  Any response to a HEAD request and any response with a 1xx
       (Informational), 204 (No Content), or 304 (Not Modified) status
       code is always terminated by the first empty line after the
       header fields, regardless of the header fields present in the
       message, and thus cannot contain a message body.

问题定位:

 

在一条长连接上返回的两个应答中,第一个应答返回的应答码是204,却在body中带了两个引号,违反了HTTP1.1的规范,导致遵守了该规范的Apache的读取第一个应答的线程没有从流中读出最后两个引号,而读取第二个应答的时候发现前两个字节是两个双引号,不是HTTP/1.1,直接抛出异常。

 

 

结论,204的应答一定要遵守规范,不能再body中写数据,否则会污染流中的数据,影响长连接上对下个数据包的读取。