apache / apisix-java-plugin-runner

APISIX Plugin Runner in Java
https://apisix.apache.org/
Apache License 2.0
130 stars 96 forks source link

bug: api7-lua-resty-http包在解析带有Expect的响应时不符合RFC2616规范 #306

Open LiuYawei2019 opened 5 months ago

LiuYawei2019 commented 5 months ago

Issue description

apisix作为中间代理层,通过ext-plugin-post-resp扩展使用Java插件,代理转发客户端请求到上游服务。其中客户端发起HTTP请求头中包含Expect属性,后端服务在接收到Expect属性时,分别响应HTTP/1.1 100 Continue回应支持,或者HTTP/1.1 417 Expectation Failed。当Response包含了响应头信息(例如Server属性)后apisix会出现failed to request: couldn't parse HTTP version from response status line:的错误,客户端收到502的请求响应。

就如上业务场景,参考RFC2616规范内容:

在RFC2616规范中 4.4 Message Length内容如下:

1.Any response message which "MUST NOT" include a message-body (such
     as the 1xx, 204, and 304 responses and any response to a HEAD
     request) is always terminated by the first empty line after the
     header fields, regardless of the entity-header fields present in
     the message.

6 Response内容如下:

After receiving and interpreting a request message, a server responds
   with an HTTP response message.

       Response      = Status-Line               ; Section 6.1
                       *(( general-header        ; Section 4.5
                        | response-header        ; Section 6.2
                        | entity-header ) CRLF)  ; Section 7.1
                       CRLF
                       [ message-body ]          ; Section 7.2

结论:在请求头包含Expect时,响应码100允许返回general-headerresponse-header信息。

ext-plugin-post-resp 插件实现依赖http.lua代码如下:

local function _handle_continue(sock, body)
    -- _receive_status 方法中会读取1行数据。即:HTTP/1.1 100 Continue
    local status, version, reason, err = _receive_status(sock) --luacheck: no unused
    if not status then
        return nil, nil, err
    end

    -- Only send body if we receive a 100 Continue
    if status == 100 then
        -- 当响应包含Header信息时会被读取到,后续的解析就会因此出错
        local ok, err = sock:receive("*l") -- Read carriage return
        if not ok then
            return nil, nil, err
        end
        _send_body(sock, body)
    end
    return status, version, err
end

建议代码如下:

local function _handle_continue(sock, body)
    local status, version, reason, err = _receive_status(sock) --luacheck: no unused
    if not status then
        return nil, nil, err
    end

    -- Only send body if we receive a 100 Continue
    if status == 100 then
        -- 读取到空行后再做后续处理
        repeat
            local ok, err = sock:receive("*l") -- Read carriage return
            if not ok then
                return nil, nil, err
            end
        until (ok and #ok == 0)
        _send_body(sock, body)
    end
    return status, version, err
end

Environment

Minimal test code / Steps to reproduce the issue

  1. Postman 通过POST请求接口,Header头中增加Expect: 100-continue属性
  2. Tomcat 容器下返回HTTP/1.1 100 Continue
  3. Undertow 容器下返回(Undertow官方在2.2.21.Final版本修改并去掉了Content-Length的属性)
    HTTP/1.1 100 Continue
    Content-Length: 0

What's the actual result? (including assertion message & call stack if applicable)

当出现类似Undertow带有Content-Length的属性响应时,apisix解析报头时会报错

What's the expected result?

正常处理并返回客户端200