您的位置:首页 > 其它

Cowboy 源码分析(十六)

2012-06-11 14:05 316 查看
  大家好,有四天没跟新这个系列了,因为这几天连续在公司加班,出版本,都是凌晨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又是怎么处理的,最后,谢谢大家支持。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: