Cowboy 源码分析(十六)
2012-06-11 14:05 rhinovirus 阅读(1557) 评论(0) 编辑 收藏 举报大家好,有四天没跟新这个系列了,因为这几天连续在公司加班,出版本,都是凌晨3,4点才到家,早上一醒来,又去公司忙,昨晚总算把版本发出去了,今天如果没有紧急BUG,在家写会博客,晚会再去。可能做游戏就是如此的节奏,如果有朋友想进游戏这行,就要想想自己能不能抗住这样无加班工资的加班节奏。
说的有点多了,回到Cowboy,在上一篇结尾,我们提到ConnAtom = cowboy_http:connection_to_atom(ConnTokens),
%% @doc Walk through a tokens list and return whether %% the connection is keepalive or closed. %% %% The connection token is expected to be lower-case. -spec connection_to_atom([binary()]) -> keepalive | close. connection_to_atom([]) -> keepalive; connection_to_atom([<<"keep-alive">>|_Tail]) -> keepalive; connection_to_atom([<<"close">>|_Tail]) -> close; connection_to_atom([_Any|Tail]) -> connection_to_atom(Tail).
这个函数很简单,只是对传递进来的字符串(字符串实际上就是整数列表)做个分解,如果含有 <<"keep-alive">>或者<<"close">>,则返回对应的值,这里我们返回的是keepalive。
回到这个函数,cowboy_http_protocol:header/3,ConnAtom为keepalive,继续看下一行:
header({http_header, _I, 'Connection', _R, Connection}, Req=#http_req{headers=Headers}, State=#state{ req_keepalive=Keepalive, max_keepalive=MaxKeepalive}) when Keepalive < MaxKeepalive -> Req2 = Req#http_req{headers=[{'Connection', Connection}|Headers]}, {ConnTokens, Req3} = cowboy_http_req:parse_header('Connection', Req2), ConnAtom = cowboy_http:connection_to_atom(ConnTokens), parse_header(Req3#http_req{connection=ConnAtom}, State);
parse_header(Req3#http_req{connection=ConnAtom}, State);这行,修改http_req.connection记录的值为keepalive,然后调用cowboy_http_protocol:parse_header/2方法,这个方法在之前的Cowboy 源码分析(十二)这一篇中,我们已经讲过了,这里我把代码贴出来:
-spec parse_header(#http_req{}, #state{}) -> ok. parse_header(Req, State=#state{buffer=Buffer, max_line_length=MaxLength}) -> case erlang:decode_packet(httph_bin, Buffer, []) of {ok, Header, Rest} -> header(Header, Req, State#state{buffer=Rest}); {more, _Length} when byte_size(Buffer) > MaxLength -> error_terminate(413, State); {more, _Length} -> wait_header(Req, State); {error, _Reason} -> error_terminate(400, State) end.
注:这里用Debugger工具,把每次调用erlang:decode_packet/3,读取到的值,都保存下来,大家看下,有助于理解这个函数:
< Buffer = <<"Host: localhost:8080\r\nUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Header = {http_header,14,'Host',undefined,<<"localhost:8080">>}
< Rest = <<"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Buffer = <<"User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Header = {http_header,24,'User-Agent',undefined,
<<"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0">>}
< Rest = <<"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Buffer = <<"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Header = {http_header,8,'Accept',undefined,
<<"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8">>}
< Rest = <<"Accept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Buffer = <<"Accept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Header = {http_header,11,'Accept-Language',undefined,<<"en-us,en;q=0.5">>}
< Rest = <<"Accept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Buffer = <<"Accept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n">>
< Header = {http_header,10,'Accept-Encoding',undefined,<<"gzip, deflate">>}
< Rest = <<"Connection: keep-alive\r\n\r\n">>
< Buffer = <<"Connection: keep-alive\r\n\r\n">>
< Header = {http_header,2,'Connection',undefined,<<"keep-alive">>}
< Rest = <<"\r\n">>
< Buffer = <<"\r\n">>
< Header = http_eoh
< Rest = <<>>
对比,下面这张图,大家好好理解下:
其实从上面erlang:decode_packet/3,很容易得出调用cowboy_http_protocol:header/3的顺序,这里我们整理下:
-spec header({http_header, integer(), cowboy_http:header(), any(), binary()} | http_eoh, #http_req{}, #state{}) -> ok. header({http_header, _I, 'Host', _R, RawHost}, Req=#http_req{ transport=Transport, host=undefined}, State) ->%% Ignore Host headers if we already have it. header({http_header, _I, 'Host', _R, _V}, Req, State) -> header({http_header, _I, 'Connection', _R, Connection}, Req=#http_req{headers=Headers}, State=#state{ req_keepalive=Keepalive, max_keepalive=MaxKeepalive}) when Keepalive < MaxKeepalive -> header({http_header, _I, Field, _R, Value}, Req, State) ->%% The Host header is required in HTTP/1.1. header(http_eoh, #http_req{version={1, 1}, host=undefined}, State) ->%% It is however optional in HTTP/1.0. header(http_eoh, Req=#http_req{version={1, 0}, transport=Transport, host=undefined}, State=#state{buffer=Buffer}) -> header(http_eoh, Req, State=#state{buffer=Buffer}) -> header(_Any, _Req, State) ->
这里我只贴出了函数头部,方便大家根据上面截取的值,来对应调用的函数分支。
好了,这一篇也偏于整理对于之前一些函数的加深理解。下一篇,我会补充各个函数分之,漏掉没讲的部分,以及最后解析到的:
< Buffer = <<"\r\n">>
< Header = http_eoh
< Rest = <<>>
Cowboy又是怎么处理的,最后,谢谢大家支持。
本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名rhinovirus(包含链接http://www.cnblogs.com/rhinovirus/),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系。