代码改变世界

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/3ConnAtomkeepalive,继续看下一行:

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又是怎么处理的,最后,谢谢大家支持。