Cowboy 源码分析(二十四)
2012-06-29 08:40 rhinovirus 阅读(1468) 评论(2) 编辑 收藏 举报大家好,调整下作息时间,以后尽量不熬夜写文章了,改成早上早起写。试试这样的作息习惯吧,不是有句话说吗,早睡早起精神好,其实主要是熬夜确实对身体不太好。好了,回归正题,上一篇,我们分析到 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), %% Flush the resp_sent message before moving on. receive {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 函数:
%% @doc Semantically parse headers. %% %% When the value isn't found, a proper default value for the type %% returned is used as a return value. %% @see parse_header/3 -spec parse_header(cowboy_http:header(), #http_req{}) -> {any(), #http_req{}} | {error, badarg}. parse_header(Name, Req=#http_req{p_headers=PHeaders}) -> case lists:keyfind(Name, 1, PHeaders) of false -> parse_header(Name, Req, parse_header_default(Name)); {Name, Value} -> {Value, Req} end.
调用这个函数时,接受到的参数的值为:
< PHeaders = [{'Connection',[<<"keep-alive">>]}]
< Name = 'Transfer-Encoding'
这个函数,我在Cowboy 源码分析(十三) Cowboy 源码分析(十四) Cowboy 源码分析(十五) 介绍过这个函数。只不过当时参数为:
< PHeaders = []
< Name = 'Connection'
这里我们还是再看下,很明显,这里 lists:keyfind(Name, 1, PHeaders) 为false,得到的肯定是 false,紧接着调用 cowboy_http_req:parse_header/3 这个函数,其中最后一个参数调用的函数代码如下:
%% @doc Default values for semantic header parsing. -spec parse_header_default(cowboy_http:header()) -> any(). parse_header_default('Connection') -> []; parse_header_default('Transfer-Encoding') -> [<<"identity">>]; parse_header_default(_Name) -> undefined.
这里Name = 'Transfer-Encoding'所以这个函数返回:[<<"identity">>];
再看 cowboy_http_req:parse_header/3 这个函数,函数比较大,我不贴全部了,就贴调用的部分:
parse_header(Name, Req, Default) when Name =:= 'Transfer-Encoding' -> parse_header(Name, Req, Default, fun (Value) -> cowboy_http:nonempty_list(Value, fun cowboy_http:token_ci/2) end);
这个分支除了接受到的Name不同,其他跟上次讲过的没什么区别了,不重复造车了。
但是,我们依然需要关注下返回值,看下面代码:
%% @todo This doesn't look in the cache. parse_header(Name, Req=#http_req{p_headers=PHeaders}, Default, Fun) -> case header(Name, Req) of {undefined, Req2} -> {Default, Req2#http_req{p_headers=[{Name, Default}|PHeaders]}}; {Value, Req2} -> case Fun(Value) of {error, badarg} -> {error, badarg}; P -> {P, Req2#http_req{p_headers=[{Name, P}|PHeaders]}} end end.
如下图:
好了,知道返回值从哪得来的。我们就可以回到 cowboy_http_req:stream_body/1 函数,继续往下看了。
从上图,我们根据返回值,就可以继续往下看了,好了,今天就到这,下一篇继续跟大家分享。
最后,谢谢大家支持。
本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名rhinovirus(包含链接http://www.cnblogs.com/rhinovirus/),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系。