博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cowboy 源码分析(二十六)
阅读量:5095 次
发布时间:2019-06-13

本文共 4747 字,大约阅读时间需要 15 分钟。

  大家好,这边给大家推荐一本马上要上市的书《Erlang/OTP并发编程实战》,想要学习Erlang的朋友不要错过,这本书的作者是Martin Logan, Eric Merritt, Richard Carlsson,译者是百度的连城,具体可以看下这里:

  好了,广告发完了,回到今天的正题,继续跟大家分享Cowboy源码。上一篇,讲完了 cowboy_http_req:body_length/1 函数,今天继续往下看,如图:

  

  这里返回的 Length = 0,所以这里返回 {done, Req3#http_req{body_state=done}};

  如果返回的不是0,会递归调用 cowboy_http_req:stream_body/1 处理,这里我不打算详细讲,等下次遇到使用时,再详细看。

  接下来,我们回到 cowboy_http_req:skip_body/1 函数:

-spec skip_body(#http_req{}) -> {ok, #http_req{}} | {error, atom()}.skip_body(Req) ->    case stream_body(Req) of        {ok, _, Req2} -> skip_body(Req2);        {done, Req2} -> {ok, Req2};        {error, Reason} -> {error, Reason}    end.

  从上面我们知道cowboy_http_req:stream_body/1 返回值,这里返回 {ok, Req2};

  这样,我们又回到cowboy_http_protocol:ensure_body_processed/1 函数:

ensure_body_processed(Req=#http_req{body_state=waiting}) ->    case cowboy_http_req:skip_body(Req) of        {ok, Req2} -> {ok, Req2#http_req.buffer};        {error, _Reason} -> {close, <<>>}    end;

  这里 Req2#http_req.buffer = <<>>,这里返回 {ok, <<>>};

  继续,回到 cowboy_http_protocol:next_request/3 函数:

-spec next_request(#http_req{}, #state{}, any()) -> ok.next_request(Req=#http_req{connection=Conn}, State=#state{        req_keepalive=Keepalive}, HandlerRes) ->    RespRes = ensure_response(Req),    {BodyRes, Buffer} = ensure_body_processed(Req),    %% Flush the resp_sent message before moving on.    receive {cowboy_http_req, resp_sent} -> ok after 0 -> ok end,    case {HandlerRes, BodyRes, RespRes, Conn} of        {ok, ok, ok, keepalive} ->            ?MODULE:parse_request(State#state{                buffer=Buffer, req_empty_lines=0,                req_keepalive=Keepalive + 1});        _Closed ->            terminate(State)    end.

  我们看下这一行,这里我们稍微修改,增加一些打印日志,来看函数的执行过程:

%% Flush the resp_sent message before moving on.    receive {cowboy_http_req, resp_sent} ->         io:format("Flush the resp_sent message before moving on.~n"),        ok     after 0 ->         io:format("after 0 -> ok end.~n"),        ok     end,

  不知道大家对 超时时间为0的receive 有没印象,我在文章 提到过,这里我测试的结果,表示我之前的理解是有错误的,我简单描述下,之前我的理解是,如果邮箱中存在 {cowboy_http_req, resp_sent},则会打印 “Flush the resp_sent message before moving on. ”,紧接着立即触发超时,打印 ”after 0 -> ok end.“, 然而这里打印的日志证明我理解错了,正确的测试结果,如果邮箱中存在 {cowboy_http_req, resp_sent},则打印 ”Flush the resp_sent message before moving on.“,如果邮箱中不存在该消息,则立即触发超时,打印 ”after 0 -> ok end.~n“。

  这边感谢网友的帮助 鲁雪林,他的测试例子如下:

-module(main).-compile(export_all).recv()->    receive        a ->        io:format("a.~n")    after 0 ->        io:format("b.~n")    end.

  测试结果如下:

  

  希望能帮助大家理解 after 0 的情况。

  好了,我们继续往下看:

  < HandlerRes = ok

  < BodyRes = ok
  < RespRes = ok
  < Conn = keepalive

case {HandlerRes, BodyRes, RespRes, Conn} of        {ok, ok, ok, keepalive} ->            ?MODULE:parse_request(State#state{                buffer=Buffer, req_empty_lines=0,                req_keepalive=Keepalive + 1});        _Closed ->            terminate(State)    end.

  从上面的结果,能够知道匹配第一个分支,也就是调用 cowboy_http_protocol:parse_request/1 函数,这里有个疑问,为什么不用 cowboy_http_protocol 而用?MODULE 呢?如果有朋友知道,麻烦告知,不过不影响,我们继续:

%% @private-spec parse_request(#state{}) -> ok.%% We limit the length of the Request-line to MaxLength to avoid endlessly%% reading from the socket and eventually crashing.parse_request(State=#state{buffer=Buffer, max_line_length=MaxLength}) ->    case erlang:decode_packet(http_bin, Buffer, []) of        {ok, Request, Rest} -> request(Request, State#state{buffer=Rest});        {more, _Length} when byte_size(Buffer) > MaxLength ->            error_terminate(413, State);        {more, _Length} -> wait_request(State);        {error, _Reason} -> error_terminate(400, State)    end.

  我们在 提到过这个方法,只不过但是匹配的分支是第一个分支,而这次匹配的分支是第三个分支:

{more, _Length} -> wait_request(State);

  这里就一行代码,调用cowboy_http_protocol:wait_request/1 函数:

-spec wait_request(#state{}) -> ok.wait_request(State=#state{socket=Socket, transport=Transport,        timeout=T, buffer=Buffer}) ->    case Transport:recv(Socket, 0, T) of        {ok, Data} ->         io:format("Data = ~p~n", [Data]),        parse_request(State#state{            buffer= << Buffer/binary, Data/binary >>});        {error, _Reason} -> terminate(State)    end.

  我们看下这里,回忆下这个函数,这里再次从客户端Socket读取消息,5000毫秒的超时,如果超过,未读取到消息,则会调用:

{error, _Reason} -> terminate(State)

  这个函数之前也讲过, 关闭Socket,这里我比较好奇,关闭原因,所以我增加打印日志,如下:

case Transport:recv(Socket, 0, T) of        {ok, Data} ->         io:format("Data = ~p~n", [Data]),        parse_request(State#state{            buffer= << Buffer/binary, Data/binary >>});        {error, _Reason} ->             io:format("_Reason: ~p~n", [_Reason]),            terminate(State)    end.

  运行,得到如下结果:

  

  如图,原因是 timeout,超时。

  好了,今天就到这吧,谢谢大家的支持,大家好梦。

  

转载于:https://www.cnblogs.com/yourihua/archive/2012/07/04/2575397.html

你可能感兴趣的文章
PHP深浅拷贝
查看>>
SDN第四次作业
查看>>
ActiveMQ(4) ActiveMQ JDBC 持久化 Mysql 数据库
查看>>
DM8168 DVRRDK软件框架研究
查看>>
django迁移数据库错误
查看>>
epoll学习01
查看>>
yii 跳转页面
查看>>
闭包问题
查看>>
C#一个FTP操作封装类FTPHelper
查看>>
Linux运维基础入门(二):网络基础知识梳理02
查看>>
你所不知道的 CSS 阴影技巧与细节
查看>>
MyBatis框架的使用及源码分析(三) 配置篇 Configuration
查看>>
20172319 实验三《查找与排序》实验报告
查看>>
构造函数的继承
查看>>
Nginx的虚拟主机配置
查看>>
overflow 属性
查看>>
Java中多态的一些简单理解
查看>>
洛谷 1449——后缀表达式(线性数据结构)
查看>>
[最小割][Kruskal] Luogu P5039 最小生成树
查看>>
Data truncation: Out of range value for column 'Quality' at row 1
查看>>