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。我认为这也是进行速率限制的好地方。在网络服务器的事件链中尽快执行。
我对@987654334@ 模块所做的所有更改似乎都有效,除了使用“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 <<any page>>
发送垃圾邮件,而我得到的只是HTTP 200
。 ETS 表也有零条目。使用PUT
,我得到了一个HTTP 405
。我可以ratelimiter:lockout(MY_IP)
并让网页加载到我的浏览器和HTTP 200
和curl
。
我很困惑。是我开始 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 中工作而不在脚本中工作?
Django中的Send_mail,在shell中工作,在本地工作,不在视图中