您的位置:首页 > 其它

Cowboy 源码分析(二十二)

2012-06-26 00:01 507 查看
  今天又是早早下班,买了排骨,莲藕,现在正压着排骨莲藕汤,从小就学做饭,发现其实做饭也蛮多乐趣的。等这篇文章发布以后,我应该已经吃完很久了,哈哈。回到主题,上一篇,我们[b]cowboy_http_protocol:dispatch/2[/b] 函数,今天我们来看cowboy_http_protocol:handler_init/2 函数:

-spec handler_init(#http_req{}, #state{}) -> ok.
handler_init(Req, State=#state{transport=Transport,
handler={Handler, Opts}}) ->
try Handler:init({Transport:name(), http}, Req, Opts) of
{ok, Req2, HandlerState} ->
handler_handle(HandlerState, Req2, State);
{loop, Req2, HandlerState} ->
handler_before_loop(HandlerState, Req2, State);
{loop, Req2, HandlerState, hibernate} ->
handler_before_loop(HandlerState, Req2,
State#state{hibernate=true});
{loop, Req2, HandlerState, Timeout} ->
handler_before_loop(HandlerState, Req2,
State#state{loop_timeout=Timeout});
{loop, Req2, HandlerState, Timeout, hibernate} ->
handler_before_loop(HandlerState, Req2,
State#state{hibernate=true, loop_timeout=Timeout});
{shutdown, Req2, HandlerState} ->
handler_terminate(HandlerState, Req2, State);
%% @todo {upgrade, transport, Module}
{upgrade, protocol, Module} ->
upgrade_protocol(Req, State, Module)
catch Class:Reason ->
error_terminate(500, State),
PLReq = lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))),
error_logger:error_msg(
"** Handler ~p terminating in init/3~n"
"   for the reason ~p:~p~n"
"** Options were ~p~n"
"** Request was ~p~n** Stacktrace: ~p~n~n",
[Handler, Class, Reason, Opts, PLReq, erlang:get_stacktrace()])
end.


  查看的参数值如下:

  < Handler = default_handler
  < Transport = cowboy_tcp_transport
  < Req = {http_req,#Port<0.2731>,cowboy_tcp_transport,keepalive,<0.481.0>,
'GET',
{1,1},
undefined,
[<<"localhost">>],
undefined,<<"localhost">>,8080,[],undefined,<<"/">>,
undefined,<<>>,[],
[{'Connection',<<"keep-alive">>},
{'Accept-Encoding',<<"gzip, deflate">>},
{'Accept-Language',<<"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3">>},
{'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}}
  < Opts = []

  看下具体逻辑:

  try Handler:init({Transport:name(), http}, Req, Opts) of

  = try default_handler:init({cowboy_tcp_transport:name(), http}, Req, Opts) of

  首先,看下 cowboy_tcp_transport:name/0 函数:

%% @doc Name of this transport API, <em>tcp</em>.
-spec name() -> tcp.
name() -> tcp.


  一个简单的不能再简单的方法,返回原子tcp。

  接着看 default_handler:init/3 函数:

init({_Any, http}, Req, []) ->
{ok, Req, undefined}.


  这个函数也很简单,只是简单的把参数组成元组返回。

  回到cowboy_http_protocol:handler_init/2 函数:

{ok, Req2, HandlerState} ->
handler_handle(HandlerState, Req2, State);


  我们从default_handler:init/3 函数返回的结果,能知道匹配这个分支,而这里紧接着调用cowboy_http_protocol:handler_handle/3 函数,同时我们注意下,参数的值:

  < HandlerState = undefined
  < Req2 = {http_req,#Port<0.2989>,cowboy_tcp_transport,keepalive,<0.490.0>,
'GET',
{1,1},
undefined,
[<<"localhost">>],
undefined,<<"localhost">>,8080,[],undefined,<<"/">>,undefined,
<<>>,[],
[{'Connection',<<"keep-alive">>},
{'Accept-Encoding',<<"gzip, deflate">>},
{'Accept-Language',<<"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3">>},
{'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.2989>,cowboy_tcp_transport,
[{'_',[{[<<"websocket">>],websocket_handler,[]},
{[<<"eventsource">>],eventsource_handler,[]},
{[<<"eventsource">>,<<"live">>],
eventsource_emitter,[]},
{'_',default_handler,[]}]}],
{default_handler,[]},
undefined,undefined,
{#Fun<cowboy_http.urldecode.2>,crash},
0,5,1,infinity,4096,5000,<<>>,false,infinity,undefined}

  现在我们继续看cowboy_http_protocol:handler_handle/3 函数:

-spec handler_handle(any(), #http_req{}, #state{}) -> ok.
handler_handle(HandlerState, Req, State=#state{handler={Handler, Opts}}) ->
try Handler:handle(Req, HandlerState) of
{ok, Req2, HandlerState2} ->
terminate_request(HandlerState2, Req2, State)
catch Class:Reason ->
PLReq = lists:zip(record_info(fields, http_req), tl(tuple_to_list(Req))),
error_logger:error_msg(
"** Handler ~p terminating in handle/2~n"
"   for the reason ~p:~p~n"
"** Options were ~p~n** Handler state was ~p~n"
"** Request was ~p~n** Stacktrace: ~p~n~n",
[Handler, Class, Reason, Opts,
HandlerState, PLReq, erlang:get_stacktrace()]),
handler_terminate(HandlerState, Req, State),
error_terminate(500, State)
end.


  和上面差不多:

  try Handler:handle(Req, HandlerState) of

  = try default_handler:handle(Req, HandlerState) of

  调用 default_handler:handle/2 函数:

handle(Req, State) ->
{ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello world!">>, Req),
{ok, Req2, State}.


  < Req = {http_req,#Port<0.2990>,cowboy_tcp_transport,keepalive,<0.494.0>,
'GET',
{1,1},
undefined,
[<<"localhost">>],
undefined,<<"localhost">>,8080,[],undefined,<<"/">>,
undefined,<<>>,[],
[{'Cache-Control',<<"max-age=0">>},
{'Connection',<<"keep-alive">>},
{'Accept-Encoding',<<"gzip, deflate">>},
{'Accept-Language',<<"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3">>},
{'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}}
  < HandlerState = undefined

  注意,这里的HandlerStatedefault_handler:init/3 函数返回的状态,继续看:

  {ok, Req2} = cowboy_http_req:reply(200, [], <<"Hello world!">>, Req),

  这里调用 cowboy_http_req:reply/4 函数:

%% @doc Send a reply to the client.
-spec reply(cowboy_http:status(), cowboy_http:headers(), iodata(), #http_req{})
-> {ok, #http_req{}}.
reply(Status, Headers, Body, Req=#http_req{socket=Socket, transport=Transport,
version=Version, connection=Connection,
method=Method, resp_state=waiting, resp_headers=RespHeaders}) ->
RespConn = response_connection(Headers, Connection),
ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end,
HTTP11Headers = case Version of
{1, 1} -> [{<<"Connection">>, atom_to_connection(Connection)}];
_ -> []
end,
{ReplyType, Req2} = response(Status, Headers, RespHeaders,  [
{<<"Content-Length">>, integer_to_list(ContentLen)},
{<<"Date">>, cowboy_clock:rfc1123()},
{<<"Server">>, <<"Cowboy">>}
|HTTP11Headers], Req),
if    Method =:= 'HEAD' -> ok;
ReplyType =:= hook -> ok; %% Hook replied for us, stop there.
true ->
case Body of
{_, StreamFun} -> StreamFun();
_ -> Transport:send(Socket, Body)
end
end,
{ok, Req2#http_req{connection=RespConn, resp_state=done,
resp_headers=[], resp_body= <<>>}}.


  不知道大家对这个函数,还有没印象,我在之前的Cowboy 源码分析(十八) Cowboy 源码分析(十九)详细讲过,这边不重复讲,大家翻看之前的文章,回忆下,这里跟之前直接返回HTTP状态码不同的是,这里是正常返回,Status = 200,Headers = [], Body = <<"Hello world!">>。

  该函数,最后返回新的修改后的#http_req记录,紧接着 default_handler:handle/2 函数,把结果重新组合成新的元组返回。

  我们继续往下看:

try Handler:handle(Req, HandlerState) of
{ok, Req2, HandlerState2} ->
terminate_request(HandlerState2, Req2, State)


  当调用 default_handler:handle/2 函数返回结果后,会调用cowboy_http_protocol:terminate_request/3 函数:

-spec terminate_request(any(), #http_req{}, #state{}) -> ok.
terminate_request(HandlerState, Req, State) ->
HandlerRes = handler_terminate(HandlerState, Req, State),
next_request(Req, State, HandlerRes).


  好了,今天就到这吧,下一篇,我们继续从这个函数往下看吧。

  最后,谢谢大家的支持,虽然看的人不多,但是我会继续坚持写下去的,大家好梦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: