在上一篇中,我们整理了下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,其他就不解释了,都能看的懂。

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

  

Cowboy 源码分析(十八)相关推荐

  1. 【转】ABP源码分析十八:UI Inputs

    以下图中描述的接口和类都在Abp项目的Runtime/Validation, UI/Inputs目录下的.在当前版本的ABP(0.83)中这些接口和类并没有实际使用到.阅读代码时可以忽略,无需浪费时间 ...

  2. cowboy源码分析

    2013-01-21 by 谢鸿锋 原创文章,转载请注明:转载自Erlang云中漫步 目录 ================================= 一.概述 二.ranch源码分析 三.c ...

  3. GCC源码分析(十六) — gimple转RTL(pass_expand)(下)

    版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/lidan1 ...

  4. Spring AOP源码分析(八)SpringAOP要注意的地方

    2019独角兽企业重金招聘Python工程师标准>>> SpringAOP要注意的地方有很多,下面就举一个,之后想到了再列出来: (1)SpringAOP对于最外层的函数只拦截pub ...

  5. JStorm与Storm源码分析(八)--计时器工具-mk-timer

    Storm使用计时器线程来处理一些周期性调度事件. 与计时器相关的操作主要有:创建计时器线程.查看线程是否活跃.向线程中加入新的待调度事件.取消计时器线程 mk-timer方法用于创建一个计时器线程. ...

  6. Docker源码分析(八):Docker Container网络(下)

    http://www.infoq.com/cn/articles/docker-source-code-analysis-part8 1.Docker Client配置容器网络模式 Docker目前支 ...

  7. 【转】ABP源码分析十九:Auditing

    审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,它们提供了活动序列的文档证据,这些活动序列可以在任何时间影响一个特定的操作. AuditInfo:定义如下图中需要被Audit的信息. Aud ...

  8. 【转】ABP源码分析十六:DTO的设计

    IDTO:空接口,用于标注Dto对象. ComboboxItemDto:用于combobox/list中Item的DTO NameValueDto<T>/NameValueDto:用于na ...

  9. 【转】ABP源码分析十五:ABP中的实用扩展方法

    类名 扩展的类型 方法名 参数 作用 XmlNodeExtensions XmlNode GetAttributeValueOrNull attributeName Gets an   attribu ...

最新文章

  1. 【lidar】基于YOLO的3D目标检测(激光雷达点云)课程设计
  2. SQLite3简单C++包装类源码示例
  3. JVM系列三:JVM参数设置、分析
  4. 对于ARM的启动,系统升级,烧写过程和文件系统等方面的总结分析
  5. checkbox设置颜色,style样式等
  6. 关于eclipse的一些简单配置
  7. CVPR2021 Oral | HOTR:不再需要后处理!Kakao Brain提出端到端Human-Object交互检测模型...
  8. eclipse快捷键 自动生成get set方法,转到所调用的方法
  9. mybatis-plus配置日志
  10. MS SQL入门基础:数据库 统计函数
  11. opencv项目案例_三菱PLC的光学玻璃热处理项目
  12. 工业循环冷却水处理设计规范_循环冷却水系统及其水处理
  13. css background背景拉伸
  14. java 舆情分析_基于Java实现网络舆情分析系统研究与实现.doc
  15. 【Python笔记】pyspark.sql.functions
  16. Win10“混合现实门户”图标更新:加入Fluent Design风格
  17. 2008中国网游老总语录之史玉柱
  18. ORACLE:单行函数
  19. c语言结构体冒泡排序,c语言结构体冒泡排序求教
  20. 云宏与英特尔携手发布了基于英特尔®至强®可扩展平台全面升级

热门文章

  1. oracle 11g 创建 job 20
  2. code point,code unit
  3. 《BI项目笔记》数据源视图设置
  4. CentOS6最小化安装默认启动的服务说明
  5. 电子商务时代企业统计的发展方向
  6. 利用Asp.net中的AJAX制作网页上自动选取开始日期及结束日期的用户自定义控件...
  7. java gui框架_推荐!程序员整理的Java资源大全
  8. 值得一用的Windows软件
  9. 【agc004f】Namori Grundy
  10. .net api 和java平台对接技术总结