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源码分析(三)的更多相关文章
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
- cowboy源码分析(二)
接 cowboy源码分析(一) 下面我们重点看看cowboy_protocol.erl代码 -module(cowboy_protocol). %% API. -export([start_link/ ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析三十一:ABP.AutoMapper
这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...
- ABP源码分析三十三:ABP.Web
ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...
- ABP源码分析三十四:ABP.Web.Mvc
ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
- Duilib源码分析(三)XML解析器—CMarkup
上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...
随机推荐
- Objective-C RunTime 学习笔记 之 atomic/nonatomic 关键字
atomic修饰的是变量/方法,对于可变对象的指针变量是安全的,内部实现加了锁,但是对可变对象本身没什么影响,不安全还是不安全.另外atomic仅仅对编译器生产的getter.setter有效,如果自 ...
- Windows上IOCP Socket事件模型管理
1.IOCP 2.使用IOCP 1)创建完成端口CreateIoCompletionPort: 2)向完成端口添加管理句柄与管理用户数据: 3)异步发送一个管理的事件请求: 4)开启工作线程来处理I ...
- Vuex 2.0 深入简出
最近面试充斥了流行框架Vue的各种问题,其中Vuex的使用就相当有吸引力.下面我就将自己深入简出的心得记录如下: 1.在vue-init webpack project (创建vue项目) 2.src ...
- PKCS RSA执行标准
RSA是一种算法,但是,在相关应用的时候,还是需要有一些标准的.这就是pkcs.现在的各种程序中,基本都是遵循这个标准来使用RSA的.最近陆续读取RSA相关的内容进行学习. RSA官网:https:/ ...
- 《linux 必读》
1. linux 内核设计与实现 2. 深入理解 linux 内核
- [转载]URL 源码分析
URI 引用包括最多三个部分:模式.模式特定部分和片段标识符.一般为: 模式:模式特定部分:片段 如果省略模式,这个URI引用则是相对的.如果省略片段标识符,这个URI引用就是一个纯URI. URI是 ...
- SSM项目思路整合NEW2
上接于 https://www.cnblogs.com/shijinglu2018/p/10374541.html ...... 三)客户管理模块开发 说明:其实大致思路差不太多,都是首先根据前端页面 ...
- Java笔记 #07# Hibernate Validator
Hibernate Validator是Spring Boot默认附带的标准校验API(javax.validation)实现. 应用实例(配合切面) 采用注解定义切面.java @Aspect @C ...
- Docker Registry私有仓库搭建
部署registry 准备一个registry.mydocker.com 的证书 对私有registry取名registry.mydocker.com 目录规划 仓库数据目录:/data/docker ...
- [Python数据挖掘]第7章、航空公司客户价值分析
一.背景和挖掘目标 二.分析方法与过程 客户价值识别最常用的是RFM模型(最近消费时间间隔Recency,消费频率Frequency,消费金额Monetary) 1.EDA(探索性数据分析) #对数据 ...