mochiweb 源码阅读(十五)

  大家好,这一篇我们接着上一篇最后提到的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,地址:http://www.erlang.org/doc/man/inet.html#setopts-2,如下图:

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

  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}这个选项将决定返回的消息的格式,如下图:

  超文本传输协议。返回数据包的格式HttpPacketerlang:decode_packet/3中说明。被动模式的套接字将通过gen_tcp:recv返回{ok, HttpPacket},而{activeonce}的套接字将发送如{http, Socket, HttpPacket}的消息。

  而我们从http://www.erlang.org/doc/man/erlang.html#decode_packet-3能够知道如下消息格式:

  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),退出当前进程。

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

  晚安。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值