博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mochiweb 源码阅读(十五)
阅读量:6152 次
发布时间:2019-06-21

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

  大家好,这一篇我们接着上一篇最后提到的mochiweb_http:request/2继续来和大家分享mochiweb源码:

request(Socket, Body) ->    ok = mochiweb_socket:setopts(Socket, [{active, once}]),    receive        {Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl ->            ok = mochiweb_socket:setopts(Socket, [{packet, httph}]),            headers(Socket, {Method, Path, Version}, [], Body, 0);        {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->            request(Socket, Body);        {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl ->            request(Socket, Body);        {tcp_closed, _} ->            mochiweb_socket:close(Socket),            exit(normal);        {ssl_closed, _} ->            mochiweb_socket:close(Socket),            exit(normal);        _Other ->            handle_invalid_request(Socket)    after ?REQUEST_RECV_TIMEOUT ->        mochiweb_socket:close(Socket),        exit(normal)    end.

  ok = mochiweb_socket:setopts(Socket, [{active, once}]),首先依然调用上一篇提到的mochiweb_socket:setopts/2函数,来修改Socket配置项,我们在这一行之前增加获取原有配置项的代码,来查看下之前的配置:

Default_Opts = inet:getopts(Socket, [active]),    io:format("Default opts: ~p~n", [Default_Opts]),

  输出如下:

Default opts: {ok,[{active,false}]}

  关于{active, once}这个选项,大家可以参阅《Erlang程序设计》第十四章第14.2.3节混合型模式(半阻塞),下面这段部分摘抄:

  在这个模式下,套接字是主动的但是仅仅针对一个消息。在控制进程发过一个消息后,必须显示地调用函数inet:setopts来把它重新激活以便接受下一个消息。在次之前,系统会处于阻塞状态。

  也可以参考erlang doc,地址:,如下图:

  好了,关于这个选项相信大家应该已经明白,我们继续往下看:

  receive

  after ?REQUEST_RECV_TIMEOUT

  end.

  我们从上面知道,如果使用{active, once}选项,一条来自Socket数据消息将被发送到进程。为了得到更多的消息,必须再次调用setopts/2{

activeonce}选项。

  那么这里就是从控制进程中读取一条消息,而这条消息的格式,根据之前调用的mochiweb_http:loop/2函数:

loop(Socket, Body) ->    ok = mochiweb_socket:setopts(Socket, [{packet, http}]),    request(Socket, Body).

  {packet, http}这个选项将决定返回的消息的格式,如下图:

  超文本传输协议。返回数据包的格式HttpPacket中说明。被动模式的套接字将通过gen_tcp:recv返回{ok, HttpPacket},而{

activeonce}的套接字将发送如{http, Socket, HttpPacket}的消息。

  而我们从能够知道如下消息格式:

  HttpPacket = HttpRequest | HttpResponse | HttpHeader | http_eoh | HttpError

  HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}

  也就是最后,读取到的消息应该满足如下格式:

  {http, Socket, {http_request, HttpMethod, HttpUri, HttpVersion}}

  那么接下来就可以看下读取消息以后,根据得到的消息的不同格式不同处理:

{Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl ->            ok = mochiweb_socket:setopts(Socket, [{packet, httph}]),            headers(Socket, {Method, Path, Version}, [], Body, 0);

  这部分就是正确返回,也就是正常的逻辑处理,这部分我们最后来看:

{Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->            request(Socket, Body);        {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl ->            request(Socket, Body);

  这两种情况,读取返回的是http_error错误,递归调用,继续读取;  

{tcp_closed, _} ->            mochiweb_socket:close(Socket),            exit(normal);        {ssl_closed, _} ->            mochiweb_socket:close(Socket),            exit(normal);

  而这种是读取到套接字关闭的处理,调用mochiweb_socket:close/1,关闭套接字,完整代码如下:

close({ssl, Socket}) ->    ssl:close(Socket);close(Socket) ->    gen_tcp:close(Socket).

  下面这部分是其他特殊情况的处理:

_Other ->            handle_invalid_request(Socket)

  调用mochiweb_http:handle_invalid_request/1函数,从函数命名来看,这个函数是处理无效请求的handle:

-spec handle_invalid_request(term()) -> no_return().handle_invalid_request(Socket) ->    handle_invalid_request(Socket, {
'GET', {abs_path, "/"}, {0,9}}, []), exit(normal).-spec handle_invalid_request(term(), term(), term()) -> no_return().handle_invalid_request(Socket, Request, RevHeaders) -> Req = new_request(Socket, Request, RevHeaders), Req:respond({
400, [], []}), mochiweb_socket:close(Socket), exit(normal).

  第一个函数,会简单构造后2个参数,紧接着调用mochiweb_http:handle_invalid_request/3函数,接着调用exit(normal),退出当前进程。

  第二个函数,先调用函数mochiweb_http:new_request/3,我们暂且理解这个函数为处理下一个请求,之后我们讲到正确逻辑的时候还会提到,这里也先跳过。接着调用Req:respond/1返回400错误码,再接着就是调用mochiweb_socket:close/1关闭套接字,最后调用exit(normal),退出当前进程。

  好了,这一篇就到这里,咱们下一篇继续。

  晚安。

转载地址:http://bnffa.baihongyu.com/

你可能感兴趣的文章
Python异步IO --- 轻松管理10k+并发连接
查看>>
Oracle中drop user和drop user cascade的区别
查看>>
登记申请汇总
查看>>
Android Jni调用浅述
查看>>
CodeCombat森林关卡Python代码
查看>>
第一个应用程序HelloWorld
查看>>
(二)Spring Boot 起步入门(翻译自Spring Boot官方教程文档)1.5.9.RELEASE
查看>>
Java并发编程73道面试题及答案
查看>>
企业级负载平衡简介(转)
查看>>
Shell基础之-正则表达式
查看>>
JavaScript异步之Generator、async、await
查看>>
讲讲吸顶效果与react-sticky
查看>>
c++面向对象的一些问题1 0
查看>>
售前工程师的成长---一个老员工的经验之谈
查看>>
Get到的优秀博客网址
查看>>
老男孩教育每日一题-第107天-简述你对***的理解,常见的有哪几种?
查看>>
Python学习--time
查看>>
在OSCHINA上的第一篇博文,以后好好学习吧
查看>>
Spring常用注解
查看>>
linux:yum和apt-get的区别
查看>>