今天又是早早下班,买了排骨,莲藕,现在正压着排骨莲藕汤,从小就学做饭,发现其实做饭也蛮多乐趣的。等这篇文章发布以后,我应该已经吃完很久了,哈哈。回到主题,上一篇,我们cowboy_http_protocol:dispatch/2 函数,今天我们来看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
注意,这里的HandlerState是default_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).
好了,今天就到这吧,下一篇,我们继续从这个函数往下看吧。
最后,谢谢大家的支持,虽然看的人不多,但是我会继续坚持写下去的,大家好梦。