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



我唯一的调试方法(io:format/2(在YAWS中不起作用。我不知所措。我的主管启动三个流程:ETS Manager、YAWS Init和Ratelimeter。这是成功的。我可以玩外壳里的限速器。。。调用YAWS应该调用的相同函数。不同的是shell的行为正如我所期望的,我不知道YAWS中发生了什么。

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

功能如下:

  • limit/1:如果计数器>阈值,则检查ETS表;更新计数器。如果计数器>黑名单阈值,则在记忆表中输入
  • blacklist/1:如果存在条目,则检查记忆表;是:重置计时器
  • lockout/1:立即将ID输入记忆表

在我的arg_rewrite_mod模块中,我正在进行一些检查,以确保我得到了预期的HTTP请求,即GET、POST和HEAD。我认为这将是一个很好的地方也做利率限制。在web服务器的事件链中尽快执行此操作。

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

我的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#arg{state=#rewrite_response{status=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})并在浏览器中加载网页,还可以用curl加载HTTP 200

我很困惑。这是我开始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作为另一个应用程序的一部分启动时,使用其嵌入支持非常重要。Yaws嵌入启动代码所做的一件重要事情是将应用程序环境变量embedded设置为true:

application:set_env(yaws, embedded, true),

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

关于速率限制,您可以考虑使用整形器,而不是使用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不使用返回值(。

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

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

相关内容

最新更新