代码改变世界

Cowboy 源码分析(二十)

2012-06-21 00:13  rhinovirus  阅读(1458)  评论(0编辑  收藏  举报

  大家好,这几天在忙游戏的端午节活动,在这里也提前跟大家说一声端午节快乐,如果你们的公司遵守国家规定,应该是放假三天,那么,大家想吃粽子吃粽子,想划龙舟划龙舟(不知道有没地方),不知道大家老家的粽子都是什么味道,映像最深刻的是小时候,端午节会有新的衣服穿,但是换之前需要用艾叶洗澡,好像我在北京都很少见过,还有就是老家的肉粽还是蛮让人怀念的。

  好了,回到正题,上一篇,我们在文章最后,提到了在cowboy_http_req:response/5函数的最后给这个处理请求的进程发送了一个消息 {?MODULE, resp_sent}, 今天,我们来看下,这个进程收到这条消息,又会怎么做呢?

  我们通过搜索关键字,能发现一共有两个地方处理了 resp_sent 这个消息:

  其中一个:

-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.

  我们从函数命名就容易知道这个函数的作用,不得不说,Cowboy代码,还是比较整齐规范的。这里是当处理完当前请求,处理下一个请求时调用,之后,我们会详细介绍。

  另一个函数:

%% Only send an error reply if there is no resp_sent message.
-spec error_terminate(cowboy_http:status(), #state{}) -> ok.
error_terminate(Code, State=#state{socket=Socket, transport=Transport,
        onresponse=OnResponse}) ->
    receive
        {cowboy_http_req, resp_sent} -> ok
    after 0 ->
        _ = cowboy_http_req:reply(Code, #http_req{
            socket=Socket, transport=Transport, onresponse=OnResponse,
            connection=close, pid=self(), resp_state=waiting}),
        ok
    end,
    terminate(State).

  这个函数不陌生,之前在 Cowboy 源码分析(十八) 我们已经详细讲了,这里就不重复讲了。

  好了,关于 {?MODULE, resp_sent} 我们就先讲到这,之后,我们再讲到处理下一个请求时,还会讲到这个消息。

  接下来,我们来看 Cowboy 源码分析(十七) 提到的cowboy_http_protocol:onrequest/2 函数,不知道大家还记得吗,如果忘了,翻看下之前的文章吧,这篇文章提到的cowboy_http_protocol:header/3 函数其中一个分支,调用了cowboy_http_protocol:onrequest/2 函数:

header(http_eoh, Req, State=#state{buffer=Buffer}) ->
    onrequest(Req#http_req{buffer=Buffer}, State#state{buffer= <<>>});

  不知道大家还记不记得什么情况下,该分支会被调用,如果你忘了,看下图就能回忆起来了:

  

  这里需要注意下,{ok, Header, Rest} -> header(Header, Req, State#state{buffer=Rest}); 这里buffer的值被修改成 Rest = <<>>。

  当调用 header(http_eoh, Req, State=#state{buffer=Buffer}) 这个分支时,参数的值分别为:

  < Req = {http_req,#Port<0.3000>,cowboy_tcp_transport,keepalive,<0.515.0>,
                  'GET',
                  {1,1},
                  undefined,
                  [<<"localhost">>],
                  undefined,<<"localhost">>,8080,[],undefined,<<"/">>,
                  undefined,<<>>,undefined,
                  [{'Cache-Control',<<"max-age=0">>},
                   {'Connection',<<"keep-alive">>},
                   {'Accept-Encoding',<<"gzip, deflate">>},
                   {'Accept-Language',<<"en-us,en;q=0.5">>},
                   {'Accept',<<"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8">>},
                   {'User-Agent',<<"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1">>},
                   {'Host',<<"localhost">>}],
                  [{'Connection',[<<"keep-alive">>]}],
                  undefined,[],waiting,<<>>,waiting,[],<<>>,undefined,
                  {#Fun<cowboy_http.urldecode.2>,crash}}
  < State = {state,<0.270.0>,#Port<0.3000>,cowboy_tcp_transport,
                 [{'_',[{[<<"websocket">>],websocket_handler,[]},
                        {[<<"eventsource">>],eventsource_handler,[]},
                        {[<<"eventsource">>,<<"live">>],
                         eventsource_emitter,[]},
                        {'_',default_handler,[]}]}],
                 undefined,undefined,undefined,
                 {#Fun<cowboy_http.urldecode.2>,crash},
                 0,5,1,infinity,4096,5000,<<>>,false,infinity,undefined}
  < Buffer = <<>>

  这里onrequest(Req#http_req{buffer=Buffer}, State#state{buffer= <<>>}); 分别修改了2个记录的buffer的值。

  好了,梳理完这些,今天就到这,下一篇,我们就可以看 cowboy_http_protocol:onrequest/2 函数的具体逻辑了。

  最后,感谢大家的支持,大家早点休息,晚安。