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

本文共 4088 字,大约阅读时间需要 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),退出当前进程。

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

  晚安。

转载于:https://www.cnblogs.com/yourihua/archive/2012/08/03/2620771.html

你可能感兴趣的文章
仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'XXX'中的标识列指定显式值。...
查看>>
winform中的小技巧【自用】
查看>>
winform DataGridView的虚模式填充,CellValueNeeded事件的触发条件
查看>>
java成神之——集合框架之ArrayList,Lists,Sets
查看>>
python编程快速上手
查看>>
实验五:编写、调试具有多个段的程序
查看>>
事件代理与事件处理流程
查看>>
数据分析师
查看>>
Android 网络编程
查看>>
仰空以忧繁星,我将君心聆听,我听君心似水清,清冷好似寒冰
查看>>
mysql中的事物处理
查看>>
面向对象程序的设计模式
查看>>
dos.ORM配置和使用
查看>>
huawei交换机普通远程登陆配置
查看>>
Python学习笔记(2) Python提取《釜山行》人物关系
查看>>
SSE入门
查看>>
【linux学习笔记五】帮助命令
查看>>
web html 在线调试工具
查看>>
VOJ1067 【矩阵经典7 构造矩阵】
查看>>
【模板】线性筛法求素数
查看>>