鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > erlang > >

Cowboy 源码分析(二十四)

来源:互联网 作者:佚名 时间:2012-11-15 13:44
大家好,调整下作息时间,以后尽量不熬夜写文章了,改成早上早起写。试试这样的作息习惯吧,不是有句话说吗,早睡早起精神好,其实主要是熬夜确实对身体不太好。好了,回归正题,上一篇,我们分析到 cowboy_http_protocol: terminate_request/3 函数第一行,

  大家好,调整下作息时间,以后尽量不熬夜写文章了,改成早上早起写。试试这样的作息习惯吧,不是有句话说吗,早睡早起精神好,其实主要是熬夜确实对身体不太好。好了,回归正题,上一篇,我们分析到 cowboy_http_protocol:terminate_request/3 函数第一行,香港服务器,今天我们来看下一行,也就是:next_request(Req, State, HandlerRes).

  cowboy_http_protocol:next_request/3函数代码如下:

-spec next_request(#http_req{}, #state{}, any()) -> ok. next_request(Req=#http_req{connection=Conn}, State=#state{ req_keepalive=Keepalive}, HandlerRes) -> RespRes = ensure_response(Req), {BodyRes, Buffer} = ensure_body_processed(Req), {cowboy_http_req, resp_sent} -> ok after 0 -> ok end, case {HandlerRes, BodyRes, RespRes, Conn} of {ok, ok, ok, keepalive} -> ?MODULE:parse_request(State#state{ buffer=Buffer, req_empty_lines=0, req_keepalive=Keepalive + 1}); _Closed -> terminate(State) end.

  这个函数接受三个参数,其中前两个,我们比较熟悉了,最后一个是 HandlerRes = handler_terminate(HandlerState, Req, State), ,第一行代码的返回值,如果正常返回就是 ok,具体可以看上一篇文章有关这个函数的详细分析。

  我们来看下具体逻辑:

  RespRes = ensure_response(Req), 这里调用cowboy_http_protocol:ensure_response/1 函数,该函数完整代码如下:

-spec ensure_response(#http_req{}) -> ok. %% The handler has already fully replied to the client. ensure_response(#http_req{resp_state=done}) -> ok; %% No response has been sent but everything apparently went fine. %% Reply with 204 No Content to indicate this. ensure_response(Req=#http_req{resp_state=waiting}) -> _ = cowboy_http_req:reply(204, [], [], Req), ok; %% Terminate the chunked body for HTTP/1.1 only. ensure_response(#http_req{method='HEAD', resp_state=chunks}) -> ok; ensure_response(#http_req{version={1, 0}, resp_state=chunks}) -> ok; ensure_response(#http_req{socket=Socket, transport=Transport, resp_state=chunks}) -> Transport:send(Socket, <<"0\r\n\r\n">>), ok.

  这个函数会根据参数的不同,逻辑稍有不同,最后返回 ok,这里会调用第一个分支:

%% The handler has already fully replied to the client. ensure_response(#http_req{resp_state=done}) -> ok;

  回到 cowboy_http_protocol:next_request/3 函数,我们来看下这一行:{BodyRes, Buffer} = ensure_body_processed(Req), 这里调用 cowboy_http_protocol:ensure_body_processed/1 函数:

-spec ensure_body_processed(#http_req{}) -> {ok | close, binary()}. ensure_body_processed(#http_req{body_state=done, buffer=Buffer}) -> {ok, Buffer}; ensure_body_processed(Req=#http_req{body_state=waiting}) -> case cowboy_http_req:skip_body(Req) of {ok, Req2} -> {ok, Req2#http_req.buffer}; {error, _Reason} -> {close, <<>>} end; ensure_body_processed(Req=#http_req{body_state={multipart, _, _}}) -> {ok, Req2} = cowboy_http_req:multipart_skip(Req), ensure_body_processed(Req2).

  这里从Debugger,可以知道 body_state=waiting,所以调用下面第二个分支:

ensure_body_processed(Req=#http_req{body_state=waiting}) -> case cowboy_http_req:skip_body(Req) of {ok, Req2} -> {ok, Req2#http_req.buffer}; {error, _Reason} -> {close, <<>>} end;

  这个函数逻辑也比较简单,调用 cowboy_http_req:skip_body/1 函数,然后根据不同返回值,返回不同的状态。这里我们看下cowboy_http_req:skip_body/1 函数:

-spec skip_body(#http_req{}) -> {ok, #http_req{}} | {error, atom()}. skip_body(Req) -> case stream_body(Req) of {ok, _, Req2} -> skip_body(Req2); {done, Req2} -> {ok, Req2}; {error, Reason} -> {error, Reason} end.

  这里主要看下 cowboy_http_req:stream_body/1 函数:

%% @doc Stream the request's body. %% %% This is the most low level function to read the request body. %% %% In most cases, if they weren't defined before using stream_body/4, %% this function will guess which transfer and content encodings were %% used for building the request body, and configure the decoding %% functions that will be used when streaming. %% %% It then starts streaming the body, returning {ok, Data, Req} %% for each streamed part, and {done, Req} when it's finished streaming. -spec stream_body(#http_req{}) -> {ok, binary(), #http_req{}} | {done, #http_req{}} | {error, atom()}. stream_body(Req=#http_req{body_state=waiting}) -> case parse_header('Transfer-Encoding', Req) of {[<<"chunked">>], Req2} -> stream_body(Req2#http_req{body_state= {stream, fun cowboy_http:te_chunked/2, {0, 0}, fun cowboy_http:ce_identity/1}}); {[<<"identity">>], Req2} -> {Length, Req3} = body_length(Req2), case Length of 0 -> {done, Req3#http_req{body_state=done}}; Length -> stream_body(Req3#http_req{body_state= {stream, fun cowboy_http:te_identity/2, {0, Length}, fun cowboy_http:ce_identity/1}}) end end; stream_body(Req=#http_req{buffer=Buffer, body_state={stream, _, _, _}}) when Buffer =/= <<>> -> transfer_decode(Buffer, Req#http_req{buffer= <<>>}); stream_body(Req=#http_req{body_state={stream, _, _, _}}) -> stream_body_recv(Req); stream_body(Req=#http_req{body_state=done}) -> {done, Req}.

  这里,首先匹配第一个分支,我们看下第一个分支的代码:

  case parse_header('Transfer-Encoding', Req) of

  调用  cowboy_http_req:parse_header/2 函数:

网友评论
<