Cowboy 源码分析(十八)

  在上一篇中,我们整理了下cowboy_http_protocol:header/3函数,在文章的末尾留下2个没有讲到的函数,今天,我们先看下cowboy_http_protocol:error_terminate/2函数,另一个函数下一篇,我们再看。cowboy_http_protocol:error_terminate/2函数定义如下:

%% Only send an error reply if there is no resp_sent message.
-spec error_terminate(cowboy_http:status(), #state{}) -> ok.
error_terminate(Code, State=#state{socket=Socket, transport=Transport,onresponse=OnResponse}) ->receive{cowboy_http_req, resp_sent} -> okafter 0 ->_ = cowboy_http_req:reply(Code, #http_req{socket=Socket, transport=Transport, onresponse=OnResponse,connection=close, pid=self(), resp_state=waiting}),okend,terminate(State).-spec terminate(#state{}) -> ok.
terminate(#state{socket=Socket, transport=Transport}) ->Transport:close(Socket),ok.

  这个函数,仅仅是给客户端一个错误答复。Code是代表返回的HTTP状态码,具体每个值代表什么意思,大家可以参考下维基百科的HTTP状态码 这个需要大家了解下HTTP协议的相关内容。真的很有必要,了解底层的一些协议。建议大家买几本相关的书看看。

  好了,回到逻辑本身,这里有个知识点,摘自《Erlang程序设计》 109页:

  超时时间为0的receive  

   一个超时时间为0的语句会立即触发一个超时,但在此之前,系统会尝试对邮箱进行模式匹配,我们可以利用这个特性来定一个flush_buffer函数,它可以完全清空进程邮箱中的所有消息:

  flush_buffer() ->receive_Any ->flush_buffer()after 0 ->trueend.

  好了,回到逻辑中,这个函数会检查进程的邮箱中是否存在 {cowboy_http_req, resp_sent} 消息,如果存在,则返回 ok,紧接着马上触发一个超时,我们来看下,超时中的处理代码:

        _ = cowboy_http_req:reply(Code, #http_req{socket=Socket, transport=Transport, onresponse=OnResponse,connection=close, pid=self(), resp_state=waiting}),ok

  这里调用 cowboy_http_req:reply/2 函数,并且忽略返回值,紧接着返回 ok,这里我们来重点看下这个函数:

%% @equiv reply(Status, [], [], Req)
-spec reply(cowboy_http:status(), #http_req{}) -> {ok, #http_req{}}.
reply(Status, Req=#http_req{resp_body=Body}) ->reply(Status, [], Body, Req).%% @equiv reply(Status, Headers, [], Req)
-spec reply(cowboy_http:status(), cowboy_http:headers(), #http_req{})-> {ok, #http_req{}}.
reply(Status, Headers, Req=#http_req{resp_body=Body}) ->reply(Status, Headers, Body, Req).%% @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)endend,{ok, Req2#http_req{connection=RespConn, resp_state=done,resp_headers=[], resp_body= <<>>}}.

  不管是 reply/2,还是reply/3最后都是调用reply/4函数,这个函数是给客户端发一个回复,代码量相对多些,我们来详细看下:

  我们看下,函数参数:参数Status就是Code,也就是HTTP状态码,Headers为空列表 [],Req=#http_req{resp_body=Body},这里的Body为默认值 resp_body  = <<>>,Sokcet的值为连接到服务器的连接,Transport 为cowboy_tcp_transport,Version 值为 {1,1},其他:Connection = keepalive,Method = 'GET',RespHeaders = [],有些值是默认值,大家可以看下记录的定义。

  来看具体逻辑:

  RespConn = response_connection(Headers, Connection), 这里调用cowboy_http_req:response_connection/2函数,函数代码如下:

-spec response_connection(cowboy_http:headers(), keepalive | close)-> keepalive | close.
response_connection([], Connection) ->Connection;
response_connection([{Name, Value}|Tail], Connection) ->case Name of'Connection' -> response_connection_parse(Value);Name when is_atom(Name) -> response_connection(Tail, Connection);Name ->Name2 = cowboy_bstr:to_lower(Name),case Name2 of<<"connection">> -> response_connection_parse(Value);_Any -> response_connection(Tail, Connection)endend.

  这里Headers为空列表 [],所以这里只返回了Connection状态,也就是keepalive,当参数Headers不为空列表时,会走下面的分支,这里判断Name的值,如果为'Connection',则调用cowboy_http_req:response_connection_parse/1函数,代码如下:

-spec response_connection_parse(binary()) -> keepalive | close.
response_connection_parse(ReplyConn) ->Tokens = cowboy_http:nonempty_list(ReplyConn, fun cowboy_http:token/2),cowboy_http:connection_to_atom(Tokens).

  我在Cowboy 源码分析(十三)和Cowboy 源码分析(十四) 很详细的看了cowboy_http:nonempty_list/2cowboy_http:token/2这两个函数,同样的,我们在Cowboy 源码分析(十六) 也讲过ConnAtom = cowboy_http:connection_to_atom(ConnTokens)这个函数,这里就不重复看了,大家如果忘了,可以点链接回忆下,温故而知新,还有个函数cowboy_bstr:to_lower/1更简单,也不打算讲。

  我们接着看 ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end, 这里如果Body的值为{CL, _}格式,则ContentLen的值为CL,否则为iolist_size(Body)。这个函数我们第一次遇到,看下 erlang doc,地址:http://www.erlang.org/doc/man/erlang.html#iolist_size-1。比较简单,大家也可以看下这篇文章,坚强2002同学,很详细的讲解了iolist:http://www.cnblogs.com/me-sa/archive/2012/01/31/erlang0034.html,我自己也做了些简单的练习,如图:

  

  好了,这个函数,大家也好好理解下,我们接着往下看:  

    HTTP11Headers = case Version of{1, 1} -> [{<<"Connection">>, atom_to_connection(Connection)}];_ -> []end,

  构建,HTTP 1.1 Header,如果HTTP协议版本为{1, 1},则返回,[{<<"Connection">>, atom_to_connection(Connection)}];这里,我们看下cowboy_http_req:atom_to_connection/1函数:

-spec atom_to_connection(keepalive) -> <<_:80>>;(close) -> <<_:40>>.
atom_to_connection(keepalive) -><<"keep-alive">>;
atom_to_connection(close) -><<"close">>.

  一看就能明白,不解释了,HTTP11Headers这个变量值可能为[{<<"Connection">>, <<"keep-alive">>}]或者[{<<"Connection">>, <<"close">>}]。

  由于篇幅太长,今天我们就看到这里,下一篇,我们继续从下面这一行开始:

    {ReplyType, Req2} = response(Status, Headers, RespHeaders,  [{<<"Content-Length">>, integer_to_list(ContentLen)},{<<"Date">>, cowboy_clock:rfc1123()},{<<"Server">>, <<"Cowboy">>}|HTTP11Headers], Req),

   最后,谢谢大家支持,晚安。

  

  2012-06-25补充:

  在翻看这篇文章和之后的文章时,发现漏讲了cowboy_http_req:reply/4 函数部分代码,故补充在这里:

    if    Method =:= 'HEAD' -> ok;ReplyType =:= hook -> ok; %% Hook replied for us, stop there.true ->case Body of{_, StreamFun} -> StreamFun();_ -> Transport:send(Socket, Body)endend,{ok, Req2#http_req{connection=RespConn, resp_state=done,resp_headers=[], resp_body= <<>>}}.

  这其实也比较简单,这里判断Method 是否全等于 'HEAD',如果是返回 ok;ReplyType 是否全等于hook,如果是返回ok;否则,根据Body情况进行匹配,这里如果是返回HTTP状态码,也就是Body为[],则都不匹配,如果 Body = <<"Hello world!">>,则把Body发送给连接到服务器的Socket。

  最后,修改了connection的值为RespConn,resp_state为done,其他就不解释了,都能看的懂。

  很抱歉,之前写完,并没有很好的检查。

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

符合语言习惯的 Python 优雅编程技巧

Python最大的优点之一就是语法简洁&#xff0c;好的代码就像伪代码一样&#xff0c;干净、整洁、一目了然。要写出 Pythonic&#xff08;优雅的、地道的、整洁的&#xff09;代码&#xff0c;需要多看多学大牛们写的代码&#xff0c;github 上有很多非常优秀的源代码值得阅读&a…

compress后的bytearray再decode变大_笔记本电脑风扇噪音变大的原因及其解决办法

笔记本使用时间长了之后&#xff0c;风扇声音会变大&#xff0c;那么如何再把风扇声音变小呢&#xff1f;怎么减小笔记本风扇的噪音&#xff1f;是什么原因导致笔记本风扇声音变大&#xff1f;下面给大家介绍笔记本风扇声音变大的原因及其解决办法。原因&#xff1a;1、使用时间…

ML.NET 示例:对象检测-ASP.NET Core Web和WPF桌面示例

ML.NET 版本API 类型状态应用程序类型数据类型场景机器学习任务算法v1.5.0动态API最新端到端应用图像文件对象检测深度学习ONNX: Tiny YOLOv2 & Custom Vision问题对象检测是计算机视觉中的经典问题之一&#xff1a;识别给定图像中包含哪些对象以及它们在图像中的位置。对于…

120天的烧脑只为孩子设计一套教具~

小木研究了适合儿童学习计算机知识的书真的挺多&#xff0c;但是小木要推荐的这本真的是很接地气了&#xff0c;不管是热衷于研究技术的小机灵还是一说计算机逻辑就懵的小可爱们&#xff0c;看到这本萌萌哒电脑漫游记&#xff0c;都会忍不住对计算机越来越感兴趣。它深入浅出&a…

# 睡眠3秒_【for fun】睡眠排序算法

点击上方蓝字关注我&#xff0c;我们一起学编程有任何疑问或者想看的内容&#xff0c;欢迎私信前天我们一起看了猴子排序&#xff0c;今天我们再来看一个奇葩的排序方法&#xff1a;睡眠排序。所谓睡眠排序&#xff0c;就是为待排序数组的每一个元素 x 启动一个线程&#xff0c…

将U盘虚拟成软驱加载控制器驱动安装windows server 2003

转自: http://dengweihua1.blog.51cto.com/134932/293221仅支持9G&#xff0c;10G安装windows2003使用该软件后&#xff0c;U盘的数据会丢失&#xff01;使用该软件后&#xff0c;U盘将无法被Windows系统使用,恢复U盘在最下边。u 安装前的准备&#xff1a; 1. 按F2进入BIOS&…

超级智能玩具《小小机器人》首发|全新50种玩法,创造力之源

致砖《小小机器人》套装全新首发电动机械的完美结合先来看看视频过过眼瘾吧来自美国STEAM教育让孩子跨学科学知识积木向来是STEAM教育很重要的一部分&#xff0c;因为它涉及到了多种学科&#xff1a;要搭建得稳固——这是工程学&#xff1b;要精准搭建——这是数学&#xff1b;…

.NET Core with 微服务 - 什么是微服务

微服务是这几年最流行的架构&#xff0c;说起架构不提微服务都不好意思跟人家打招呼。最近想要再梳理一下关于微服务的知识&#xff0c;并且结合本人的一些实践经验来做一些总结与分享。前面会分享一些概念性的东西&#xff0c;后面也会使用.net来实践&#xff0c;一步步完成一…

雨棚板弹性法计算简图_钢结构工程量计算4点注意事项,还不来看?

一图纸&#xff1a;根据图纸目录&#xff0c;清理核对图纸数量&#xff0c;检查是否有遗漏。二建筑施工图1. 设计总说明 1.1 建筑面积、结构形式、柱距、跨度、结构布置情况&#xff1b;1.2 工程量计算的范围&#xff1a;关于结构、屋面、墙面、门窗等&#xff0c;清楚投标报价…

html注释的爱情故事,爱情故事”为你的婚礼贴上专属标签

这不是一场婚礼&#xff0c;而是彩虹之上的梦每个年轻的女孩都有一个多彩的梦&#xff0c;这个梦绚丽斑斓&#xff0c;若有天梦想实现&#xff0c;年轻的女孩子们是否会欣喜地大叫起来&#xff1f;为你的婚礼注入你自己的梦&#xff0c;贴上只属于你自己的“标签婚礼”吧&#…

震惊整个世界的新发现,科学界的大骗局

全世界有3.14 % 的人已经关注了数据与算法之美布朗洛和N射线闹剧继伦琴发现X射线后&#xff0c;1903年&#xff0c;法国科学院院士、物理学家布朗洛宣布他发现了N射线。法国科学院公布了这一“惊人发现”之后&#xff0c;兴起了一股研究N射线热潮&#xff0c;仅法国科学院院刊在…

TI-89T 教你在C程序里调用TI-BASIC程序,看看是否有人对这个感兴趣

2019独角兽企业重金招聘Python工程师标准>>> 教你在C 程序里调用TI-BASIC程序&#xff0c;看看是否有人对这个感兴趣 昨天翻TIGCC的文档&#xff0c;先瞅了瞅FAQ&#xff0c;话说我看文档有个习惯&#xff0c;就是先看目录&#xff0c;再看FAQ&#xff0c;因为FAQ汇…

利用计算机制作多媒体最后一步,福建省高中会考 多媒体技术应用 选择题专项练习十一(201206)(有答案)...

多媒体技术应用选择题专项练习十一(201206)1.下列都属于数字图像采集工具的是( )A.数码照相机、扫描仪B.麦克风、扫描仪C.打印机、数码照相机D.数码摄像机、打印机2.下列关于制作多媒体作品的叙述&#xff0c;不正确的是( )A.规划与设计是制作多媒体作品的重要步骤B.制作多媒体…

工程师和科学家有什么区别

全世界有3.14 % 的人已经关注了数据与算法之美科学家和工程师有什么不同&#xff1f;通常民众认为&#xff0c;工程师就是搞技术的&#xff0c;科学家就是搞科研的。对科学领域有一定认识的人群会说&#xff0c;工程师是应用科研成果的人&#xff0c;科学家是发现科学规律的人。…

聊一聊Jmeter与多接口测试

背景 前面两篇聊过了 JMeter 的 简单使用 和 参数化&#xff0c;主要都还是单接口的。很多时候&#xff0c;一个业务要走完&#xff0c;它会依赖多个接口&#xff0c;而且这些接口会有依赖性。好比说&#xff0c;我想查询一个订单信息&#xff0c;那么大前提肯定是我已经下单了…

大数据时代,还不认识这些数据分析工具?

在大数据时代的现今&#xff0c;数据庞大且繁杂&#xff0c;因此&#xff0c;如何有效利用它们&#xff0c;达到资源不浪费的目的成为了相关工作者思考的问题&#xff0c;于是数据分析就应运而生。在实际生活中&#xff0c;数据分析已经成为人们作出判断和采取行动的基石。比如…

C# 强大的新特性 Source Generator

C# 强大的新特性 Source GeneratorIntro微软在 .NET 5 中引入了 Source Generator 的新特性&#xff0c;利用 Source Generator 我们可以在应用编译的期间根据当前编译信息动态生成代码&#xff0c;而且可以在我们的 C# 代码中直接引用动态生成的代码&#xff0c;从而大大减少重…

为什么Kubernetes从节点会join失败

有段时间没有鼓捣Kubernetes了&#xff0c;今天重置Kubernetes集群后&#xff0c;slave节点不能加入master节点了&#xff0c;我把问题和解决方案分享给大家。我本地的Kubernetes集群包括一个主节点和一个从节点&#xff0c;如下图&#xff1a;问题主节点启动后&#xff0c;从节…

SharePoint 2007 Select People and Groups中搜索不到其他Domain账户的问题[已解决]

问题描述&#xff1a; 在一个SiteCollection中添加用户的时候发现来自另外一个Domain的账户无法添加。我们要添加NNEAS\HIKC到当前网站并赋予Full Control权限。但是当点击Add Users后发现在弹出的Select People and Groups中搜索不到这个用户&#xff0c;导致无法添加成功。仅…