上接 cowboy源码分析(二)

我们接着分析cowboy_protocol.erl的request/7模块

-module(cowboy_protocol).

%% API.
-export([start_link/4]).

%% Internal.
-export([init/4]).
-export([parse_request/3]).
-export([resume/6]).

%%----省略若干行

request(B, State=#state{transport=Transport}, M, P, Q, Version, Headers) ->
case lists:keyfind(<<"host">>, 1, Headers) of
false when Version =:= 'HTTP/1.1' ->
error_terminate(400, State);
false ->
request
(B, State, M, P, Q, Version, Headers,
<<>>, default_port(Transport:name()));
{_, RawHost} ->
try parse_host(RawHost, false, <<>>) of
{Host, undefined} ->
request(B, State, M, P, Q, Version, Headers,
Host, default_port(Transport:name()));
{Host, Port} ->
request(B, State, M, P, Q, Version, Headers,
Host, Port)
catch _:_ ->
error_terminate(400, State)
end
end. -spec default_port(atom()) -> 80 | 443.
default_port(ssl) -> 443;
default_port(_) -> 80. %% Same code as cow_http:parse_fullhost/1, but inline because we
%% really want this to go fast.
parse_host(<< $[, Rest/bits >>, false, <<>>) ->
parse_host(Rest, true, << $[ >>);
parse_host(<<>>, false, Acc) ->
{Acc, undefined};
parse_host(<< $:, Rest/bits >>, false, Acc) ->
{Acc, list_to_integer(binary_to_list(Rest))};
parse_host(<< $], Rest/bits >>, true, Acc) ->
parse_host(Rest, false, << Acc/binary, $] >>);
parse_host(<< C, Rest/bits >>, E, Acc) ->
case C of
?INLINE_LOWERCASE(parse_host, Rest, E, Acc)
end. %% End of request parsing.
%%
%% We create the Req object and start handling the request.

request
(Buffer, State=#state{socket=Socket, transport=Transport,
req_keepalive=ReqKeepalive, max_keepalive=MaxKeepalive,
compress=Compress, onresponse=OnResponse},
Method, Path, Query, Version, Headers, Host, Port) ->
case Transport:peername(Socket) of
{ok, Peer} ->
Req = cowboy_req:new(Socket, Transport, Peer, Method, Path,
Query, Version, Headers, Host, Port, Buffer,
ReqKeepalive < MaxKeepalive, Compress, OnResponse),
onrequest(Req, State);
{error, _} ->
%% Couldn't read the peer address; connection is gone.
terminate(State)
end. %% Call the global onrequest callback. The callback can send a reply,
%% in which case we consider the request handled and move on to the next
%% one. Note that since we haven't dispatched yet, we don't know the
%% handler, host_info, path_info or bindings yet.
-spec onrequest(cowboy_req:req(), #state{}) -> ok.
onrequest(Req, State=#state{onrequest=undefined}) ->
execute
(Req, State);
onrequest(Req, State=#state{onrequest=OnRequest}) ->
Req2 = OnRequest(Req),
case cowboy_req:get(resp_state, Req2) of
waiting -> execute(Req2, State);
_ -> next_request(Req2, State, ok)
end. -spec execute(cowboy_req:req(), #state{}) -> ok.
execute(Req, State=#state{middlewares=Middlewares, env=Env}) ->
execute(Req, State, Env, Middlewares). -spec execute(cowboy_req:req(), #state{}, cowboy_middleware:env(), [module()])
-> ok.
execute(Req, State, Env, []) ->
next_request(Req, State, get_value(result, Env, ok));
execute(Req, State, Env, [Middleware|Tail]) ->
case Middleware:execute(Req, Env) of
{ok, Req2, Env2} ->
execute(Req2, State, Env2, Tail);
{suspend, Module, Function, Args} ->
erlang:hibernate(?MODULE, resume,
[State, Env, Tail, Module, Function, Args]);
{halt, Req2} ->
next_request(Req2, State, ok);
{error, Code, Req2} ->
error_terminate(Code, Req2, State)
end. -spec resume(#state{}, cowboy_middleware:env(), [module()],
module(), module(), [any()]) -> ok.
resume(State, Env, Tail, Module, Function, Args) ->
case apply(Module, Function, Args) of
{ok, Req2, Env2} ->
execute(Req2, State, Env2, Tail);
{suspend, Module2, Function2, Args2} ->
erlang:hibernate(?MODULE, resume,
[State, Env, Tail, Module2, Function2, Args2]);
{halt, Req2} ->
next_request(Req2, State, ok);
{error, Code, Req2} ->
error_terminate(Code, Req2, State)
end. -spec next_request(cowboy_req:req(), #state{}, any()) -> ok.
next_request(Req, State=#state{req_keepalive=Keepalive, timeout=Timeout},
HandlerRes) ->
cowboy_req:ensure_response(Req, 204),
%% If we are going to close the connection,
%% we do not want to attempt to skip the body.
case cowboy_req:get(connection, Req) of
close ->
terminate(State);
_ ->
%% Skip the body if it is reasonably sized. Close otherwise.
Buffer = case cowboy_req:body(Req) of
{ok, _, Req2} -> cowboy_req:get(buffer, Req2);
_ -> close
end,
%% Flush the resp_sent message before moving on.
if HandlerRes =:= ok, Buffer =/= close ->
receive {cowboy_req, resp_sent} -> ok after 0 -> ok end,
?MODULE:parse_request(Buffer,
State#state{req_keepalive=Keepalive + 1,
until=until(Timeout)}, 0);
true ->
terminate(State)
end
end. -spec error_terminate(cowboy:http_status(), #state{}) -> ok.
error_terminate(Status, State=#state{socket=Socket, transport=Transport,
compress=Compress, onresponse=OnResponse}) ->
error_terminate(Status, cowboy_req:new(Socket, Transport,
undefined, <<"GET">>, <<>>, <<>>, 'HTTP/1.1', [], <<>>,
undefined, <<>>, false, Compress, OnResponse), State). -spec error_terminate(cowboy:http_status(), cowboy_req:req(), #state{}) -> ok.
error_terminate(Status, Req, State) ->
_ = cowboy_req:reply(Status, Req),
terminate(State). -spec terminate(#state{}) -> ok.
terminate(#state{socket=Socket, transport=Transport}) ->
Transport:close(Socket),
ok.

首先进入request/7函数,取得服务器host和port  =》

=》进入request/9,调用cowboy_req:new/14新建一个Req(等等我们再额外分析cowboy_req:new/14)=》

=》后面进入onrequest/2,判断onrequest参数 =》

=》直接进入execute/2 ,取得Middlewares,和Env =》

=》然后是execute/4 ,对Middlewares的列表模块(['cowboy_router',  'cowboy_handler'])分别执行Middleware:execute =》

先执行cowboy_router:execute/2 =》

-module(cowboy_router).
-behaviour(cowboy_middleware). -export([compile/1]).
-export([execute/2]). %%----省略若干行 -spec execute(Req, Env)
-> {ok, Req, Env} | {error, 400 | 404, Req}
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
execute(Req, Env) ->
{_, Dispatch} = lists:keyfind(dispatch, 1, Env),
[Host, Path] = cowboy_req:get([host, path], Req),
case match(Dispatch, Host, Path) of
{ok, Handler, HandlerOpts, Bindings, HostInfo, PathInfo} ->
Req2 = cowboy_req:set_bindings(HostInfo, PathInfo, Bindings, Req),
{ok, Req2, [{handler, Handler}, {handler_opts, HandlerOpts}|Env]};
{error, notfound, host} ->
{error, 400, Req};
{error, badrequest, path} ->
{error, 400, Req};
{error, notfound, path} ->
{error, 404, Req}
end. %%---省略若干行

这个函数主要是想取得Dispatch(这个就是开始cowboy_router:compile执行取得的路径路由信息,具体参见cowboy源码分析(一)),Host(主机), Path(相对路径) =》

=》 然后就是match函数,match函数这里就不详细的导读了,我们直接看结果,取得Handler(请求的句柄), HandlerOpts(句柄参数), Bindings(变量绑定,这个暂时没找到例子),

HostInfo 服务器信息, PathInfo路径信息, 存入Req;把Handler信息存入Env =》返回Req,和ENV,这个函数执行完成了,

下面就是请求的执行函数,cowboy_handler:execute/2,

对于这个函数的解析和上面cowboy_req:new/14的说明我们放在下一节说明

未完待续~~

cowboy源码分析(三)的更多相关文章

  1. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  2. cowboy源码分析(二)

    接 cowboy源码分析(一) 下面我们重点看看cowboy_protocol.erl代码 -module(cowboy_protocol). %% API. -export([start_link/ ...

  3. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  4. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  5. ABP源码分析三十一:ABP.AutoMapper

    这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...

  6. ABP源码分析三十三:ABP.Web

    ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...

  7. ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  8. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  9. Duilib源码分析(三)XML解析器—CMarkup

    上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...

随机推荐

  1. 微信小程序之回调函数

    在微信小程序中众所周知在js里面得方法都是异步执行,我最近再做项目得时候也遇到了这个问题,再方法里面调用另一个方法里面的接口数据,第一次是调取不到的, 因为两个方法是同时开始执行得,所以怎么都取不到值 ...

  2. 灵雀云:etcd 集群运维实践

    [编者的话]etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境.因此围绕 etcd 相关的运维知识就比较 ...

  3. [python 练习] 计算个税

    题目:利用python计算个税 说明:python有序字典的使用 代码: # -*- coding: utf-8 -*- from collections import OrderedDict # 税 ...

  4. Django框架详细介绍---cookie、session、自定义分页

    1.cookie 在HTTP协议介绍中提到,该协议是无状态的,也就是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的 ...

  5. noVNC支持手机自带键盘输入

    代码修改说明 novnc的web链接类似为:http://192.168.1.177:6080/vnc_auto.html?token=105356fa-bbe3-43e4-a0ce-7703dc42 ...

  6. elasticsearch开机启动脚本

    最近搭建了一个elasticsearch服务,其中机器重启而ES服务没有重启是问题,就有下面的脚本 #!/bin/sh #chkconfig: #description: es export JAVA ...

  7. 容器技术研究-Kubernetes基本概念

    最近在研究容器技术,作为入门,基本概念必须搞明白,今天整理一下Kubernetes的基本概念. 一.什么是Kubernetes Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部 ...

  8. FineReport中日期时间函数使用总结

    说明:凡函数中以日期作为参数因子的,其中日期的形式都必须是yy/mm/dd.而且必须用英文环境下双引号(" ")引用. DATE DATE(year,month,day):返回一个 ...

  9. Linux下/proc目录的作用

    文章是摘抄过来,方便自己查阅! 1. /proc目录 Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在 ...

  10. 让策划也能轻松修改数据的方法:运用Excel2Json2Object插件将xml表格转为Object导入脚本

    让策划也能轻松修改数据的方法:运用Excel2Json2Object插件将xml表格转为Object导入脚本 运用Excel2Json2Object插件将xml表格转为Object导入脚本 下载地址 ...