上接 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. 【原创】canvas裁剪上传图片异步提交

    <!DOCTYPE html> canvas裁剪图片,纯前端 裁剪文件 后台返回获取裁剪后的文件

  2. SV-assertion

    断言(assert)是一种描述性语言,通过描述的期望结果来进行仿真验证. 断言有一个更加基础的信息,我们称为属性(property),属性可以作为断言结果,功能覆盖点,形式检查和约束随机激励生成. 断 ...

  3. 《ASP.NET Core In Action》读书笔记系列五 ASP.NET Core 解决方案结构解析1

    创建好项目后,解决方案资源管理器窗口里我们看到,增加了不少文件夹及文件,如下图所示: 在解决方案文件夹中,找到项目文件夹,该文件夹又包含五个子文件夹 -Models.Controllers.Views ...

  4. SQL*Plus命令

    简介set命令 一般使用SQL PLUS导出数据时一般使用以下参数就可以了. set echo off; -- 不显示脚本中的每个sql命令(缺省为on)set feedback off; -- 禁止 ...

  5. 【shell】shuf命令提取文件的随机行

    shuf命令可以随机提取一个文本文件的不同行,输出到文件或者标准输出中. shuf 用法: shuf [选项]... [文件] 或者: shuf -e [选项]... [参数]... 或者: shuf ...

  6. zabbix监控服务搭建

    命令配置 浏览器配置: 检查必须全部ok(若有warning,检查是否漏装包,重启php-fpm服务) 数据库信息 主机名(zabbix server ip).端口号(默认).名字(随意) 配置摘要 ...

  7. 在linux和windows用c++编写c接口的动态库

    linux 动态的头文件api.h #ifndef _API_H #define _API_H #ifdef DLL_IMPLEMENT #define DLL_EXPORT extern " ...

  8. springMVC注解总结

    由于BookController类加了value="/book"的@RequestMapping的注解,所以相关路径都要加上"/book",即请求的url分别为 ...

  9. 【读书笔记】使用代理录制Web性能测试脚本

    读书笔记:<零成本实现Web性能测试>第3章 基本操作步骤: 在测试计划中添加线程组. 在该线程组中添加HTTP请求默认值.设置服务器名称或ip.端口. 在工作台添加HTTP代理服务器.设 ...

  10. 主成分分析 SPSS、python实例分析

    今天,在西瓜书上看到了主成分分析法,之前建模有接触过但是理解不够深刻,今天再次和这一位老朋友聊聊. 主成分分析(Principal Component Analysis,PCA), 是一种统计方法.通 ...