Erlang的持久/保持连接HTTP客户端实现

rta7y2nd  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(177)

I am confused about the keep-alive feature of Erlang inets and also the ibrowse http client(latest version). According to the RFC:

8.1.2.2 Pipelining   
   A client that supports persistent connections MAY "pipeline" its
   requests (i.e., send multiple requests without waiting for each
   response). A server MUST send its responses to those requests in the
   same order that the requests were received.

   Clients which assume persistent connections and pipeline immediately
   after connection establishment SHOULD be prepared to retry their
   connection if the first pipelined attempt fails. If a client does
   such a retry, it MUST NOT pipeline before it knows the connection is
   persistent. Clients MUST also be prepared to resend their requests if
   the server closes the connection before sending all of the
   corresponding responses.

   Clients SHOULD NOT pipeline requests using non-idempotent methods or
   non-idempotent sequences of methods (see section 9.1.2). Otherwise, a
   premature termination of the transport connection could lead to
   indeterminate results. A client wishing to send a non-idempotent
   request SHOULD wait to send that request until it has received the
   response status for the previous request.

There are two modes, 'pipeline' and 'keep-alive', based on the persistent connections, and the difference between them is that requests could be sent on the same connection without waiting for the response of earlier one when 'pipeline' is used. In other words, we should wait for each response before sending other requests on the same connection when use 'keep-alive'.
As a result, I thought the 'keep-alive' implementation should be like:

%% each httpc_handler(gen_server) maintains a persistent connection 
%% with type of keep-alive
httpc_handler:handle_request(Request, keepalive) ->
        %% check if there is request not finished yet
        case is_there_old_request()  of
            true -> queue:in(RQueue, Request);
            %% then send the request
            false -> gen_tcp:send(Request)
        end.

httpc_handler:handle_response(Response) ->
    send_back(Response),
    case queue:out(RQueue) of
        undefined -> ...;
        %% send the next request
        Request -> gen_tcp:send(Request), ...
    end.

But in fact, the implementation of inets and ibrowse is like:

httpc_handler:handle_request(Request, keepalive) ->
            %% send without check
            gen_tcp:send(Request),
            case is_there_old_request()  of
                true -> queue:in(RQueue, Request);
                false -> ...
            end.
    
 httpc_handler:handle_response(Response) ->
        send_back(Response),
        case queue:out(RQueue) of
            undefined -> ...;
            Request -> receive_next_response
        end.

which I think it's actually the 'pipeline' mode without idempotent limit.
So, I've missed something in the code, or just misunderstood the RFC?

vktxenjb

vktxenjb2#

在看了最新的OTP源代码后,幂等逻辑实际上在httpc_manager:select_session中。如果请求不是幂等的,它将返回no_connection,提示系统创建一个新的连接。还有其他的逻辑分散在各处,但我确实在httpc_handler中识别了处理流水线与保持活动的单独函数。

相关问题