Erlang:函数在 shell 中工作,但不在 YAWS 中

Posted

技术标签:

【中文标题】Erlang:函数在 shell 中工作,但不在 YAWS 中【英文标题】:Erlang: Functions work in shell but not in YAWS 【发布时间】:2020-07-24 17:30:34 【问题描述】:

我唯一的调试方法 (io:format/2) 在 YAWS 中不起作用。我不知所措。我的主管启动了三个进程:ETS Manager、YAWS Init 和 Ratelimiter。这是成功的。我可以在 shell 中使用速率限制器......调用 YAWS 应该的相同功能。不同之处在于 shell 的行为符合我的预期,我不知道 YAWS 中发生了什么。

我知道如果我在 shell 中发送垃圾邮件命令:ratelimiter:limit(IP),它最终会返回true。我可以执行以下操作,它也会返回true:ratelimiter:lockout(IP), ratelimiter:blacklist(IP)。限制器是gen_server

函数执行以下操作:

limit/1:如果计数器 > 阈值,请检查 ETS 表;更新计数器。如果 counter > blacklist threshold 进入 mnesia 表 blacklist/1:检查mnesia表是否存在;是:重置计时器 lockout/1:立即将ID输入到mnesia表中

在我的arg_rewrite_mod 模块中,我正在做一些检查以确保我收到了我期望的HTTP 请求,即GET、POST 和HEAD。我认为这也是进行速率限制的好地方。在网络服务器的事件链中尽快执行。

我对@9​​87654334@ 模块所做的所有更改似乎都有效,除了使用“printf”和限制器。我是这门语言的新手,所以我不确定我的错误是否明显。

arg_rewrite_mod的骨架:

-module(arg_preproc).
-export([arg_rewrite/1]).

-include("limiter_def.hrl").
-include_lib("/usr/lib/yaws/include/yaws_api.hrl").


is_blacklisted(ID) ->
    case ratelimiter:blacklist(ID) of
    false ->    continue;
    true ->     throw(blacklist)
    end.

is_limited(ID) ->
    case ratelimiter:limit(ID) of
    false ->    continue;
    true ->     throw(limit)
    end.


arg_rewrite(A) ->
    Allow = ['GET','POST', 'HEAD'],

    try
        IP, _ = A#arg.client_ip_port,

        ID = IP,
        is_blacklisted(ID),

io:format("~p ~p ~n",[ID, is_blacklisted(ID)]),         

        %% === Allow expected HTTP requests
        HttpReq = (A#arg.req)#http_request.method,

        case lists:member(HttpReq, Allow) of
            true ->
                _,ReqTgt = (A#arg.req)#http_request.path,
                PassThru = [".css",".jpg",".jpeg",".png",".js"],
                %% ... much more ...
            false ->
                is_limited(ID),
                throw(http_method_denied)
        end
    catch
        throw:blacklist -> %% Send back a 429;
        throw:limit -> %% Same but no Retry-After;
        throw:http_method_denied ->
        %%Only thrown experienced
            AllowedReq = string:join([atom_to_list(M) || M <- Allow], ","),
            A#argstate=#rewrite_responsestatus=405,
                        headers=[header, "Allow", AllowedReq,header, connection, "close"]
            ;
        Type:Reason -> error, unhandled,Type, Reason
    end.

我可以在 bash shell 中尽可能快地向curl -I -X HEAD &lt;&lt;any page&gt;&gt; 发送垃圾邮件,而我得到的只是HTTP 200。 ETS 表也有零条目。使用PUT,我得到了一个HTTP 405。我可以ratelimiter:lockout(MY_IP) 并让网页加载到我的浏览器和HTTP 200curl

我很困惑。是我开始 YAWS 的方式吗?

start() ->
    os:putenv("YAWSHOME", ?HOMEPATH_YAWS),
    code:add_patha(?MODPATH_YAWS),

    ok = case (R = application:start(yaws)) of
        error, already_started, _ -> ok;
        _ -> R
    end,

    ok,self(). %% Tell supervisor everything okay in a manner it expects.

我这样做是因为我认为它会“更容易”。

【问题讨论】:

从您的问题中不清楚您是将 Yaws 作为独立 Web 服务器还是作为更大应用程序中的嵌入式 Web 服务器启动。如果是独立的,您可以使用-i 命令行选项运行 Yaws 以使其具有交互性,这通常对调试很有用。如果嵌入,您应该关注Yaws embedded startup documentation 以确保您正确启动它。最后,您应该为此功能使用shaper 模块;见yaws.conf man page。 我已经开始编写代码以启动它嵌入并意识到我只是将所有内容从 conf 文件复制到代码中。我想我会把它删掉。我会研究一下整形器......还会看看我是否可以在网页中对我的数据存储区做任何事情。 您的推动很有帮助。只需添加application:set_env(yaws,embedded,true) 即可解决问题。在发布之前花费在此上的时间量很大。否则我不会问。感谢您的帮助。 很高兴解决了。 仅供参考,我在Erlang Slack site 上创建了一个#yaws 频道,因此,如果您对本论坛难以描述的shaper 有其他问题,或者对Yaws 的任何其他方面有疑问,随时加入。 【参考方案1】:

当启动 Yaws 作为另一个应用程序的一部分时,使用它的embedding support 很重要。 Yaws 嵌入启动代码所做的一件重要事情是将应用程序环境变量embedded 设置为true

application:set_env(yaws, embedded, true),

Yaws 在它的几个代码路径中检查这个变量,特别是在初始化期间,以避免假设它作为一个独立的守护进程运行。

关于速率限制,您可以考虑使用shaper,而不是使用 arg 重写器。 yaws_shaper 模块提供了一种期望其回调模块实现两个功能的行为:

check/1:yaws_shaper 调用此函数以允许回调模块决定是否允许来自客户端的请求。它将客户端主机信息作为回调参数传递。您的 shaper 回调模块返回原子 allow 以允许请求继续进行,或者返回元组 deny, Status, Message 其中 Status 是返回给客户端的 HTTP 状态代码,例如 429 表示客户端正在制作太多请求,Message 是要返回给客户端的任何额外 html。 (如果Message 也可以包含诸如Retry-After 之类的回复标头可能会很好;这是我将考虑添加到Yaws 的内容。)

update/3:yaws_shaper 在准备好返回客户端响应时调用此函数。第一个参数是客户端主机信息,第二个参数是“命中”数(每个请求的值为 1),第三个参数是响应客户端请求而传递的字节数。您的 shaper 回调模块可以从 update/3 返回 ok(Yaws 不使用返回值)。

整形器可以使用此框架来跟踪每个客户端发出的请求数量以及 Yaws 向每个客户端传递的数据量,并使用该信息来限制或拒绝特定客户端。

最后,虽然“printf 调试”有效,但它并不理想,尤其是在具有内置跟踪功能的 Erlang 中。你应该考虑学习dbg module,这样你就可以跟踪你想要的任何函数,查看谁调用了它,查看传递给它的参数,查看它返回的内容等。

【讨论】:

以上是关于Erlang:函数在 shell 中工作,但不在 YAWS 中的主要内容,如果未能解决你的问题,请参考以下文章

为啥 python 模块在 shell 中工作而不在脚本中工作?

包含不在 Powershell 中工作的运算符

Django中的Send_mail,在shell中工作,在本地工作,不在视图中

Qt。所有功能都在发布文件夹中工作,但不在 Program Files 中

获取在 HTML 页面中工作但不在 JS 中工作 [重复]

请求在CURL中工作但不在Ajax中工作