Erlang:函数在 shell 中有效,但在 YAWS 中无效
Erlang: Functions work in shell but not in YAWS
我唯一的调试方法 (io:format/2
) 在 YAWS 中不起作用。我不知所措。我的主管启动了三个进程:ETS Manager、YAWS Init 和 Ratelimiter。这是成功的。我可以在 shell... 中使用速率限制器来调用 YAWS 应该调用的相同函数。不同之处在于 shell 的行为符合我的预期,我不知道 YAWS 中发生了什么。
我知道如果我在 shell 中发送垃圾邮件命令:ratelimiter:limit(IP)
它最终会 return true
。我可以执行以下命令,它也会 return true
: ratelimiter:lockout(IP), ratelimiter:blacklist(IP)
。限制器是 gen_server
.
函数执行以下操作:
limit/1
:如果计数器 > 阈值,检查 ETS table;更新计数器。如果计数器 > 黑名单阈值 mnesia table
blacklist/1
:检查 mnesia table 是否存在条目;是:重置计时器
lockout/1
: 立即将ID输入mnesia table
在我的 arg_rewrite_mod
模块中,我正在做一些检查以确保我收到我期望的 HTTP 请求,即 GET、POST 和 HEAD。我认为这也是进行速率限制的好地方。在 Web 服务器的事件链中尽快执行此操作。
除了使用 "printf"s 和限制器外,我对 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 table 也有零条目。使用 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.
我这样做是因为我认为它会是 "easier."
当作为另一个应用程序的一部分启动 Yaws 时,使用它的 embedding support 很重要。 Yaws 嵌入启动代码所做的一件重要事情是将应用程序环境变量 embedded
设置为 true
:
application:set_env(yaws, embedded, true),
Yaws 在它的几个代码路径中检查这个变量,特别是在初始化期间,以避免假设它是 运行 作为 stand-alone 守护进程。
关于速率限制,您可以考虑使用 shaper,而不是使用 arg 重写器。 yaws_shaper
模块提供了一种行为,期望其回调模块实现两个功能:
check/1
:yaws_shaper
调用这个让回调模块决定是否允许客户端的请求。它将客户端主机信息作为回调参数传递。您的整形器回调模块 return 要么是允许请求继续的原子 allow
,要么是元组 {deny, Status, Message}
,其中 Status
是到 return 的 HTTP 状态代码给客户端,例如 429 表示客户端发出的请求过多,Message
是任何额外的 HTML 要 returned 给客户端。 (如果 Message
也可以包含回复 header,例如 Retry-After
,那可能会更好;这是我会考虑添加到 Yaws 中的东西。)
update/3
:yaws_shaper
在客户端的响应准备好 returned 时调用它。第一个参数是客户端主机信息,第二个参数是"hits"的数量(每个请求的值为1),第三个参数是响应客户端请求而传送的字节数。您的整形器回调模块可以从 update/3
return ok
(Yaws 不使用 return 值)。
整形器可以使用此框架来跟踪每个客户端发出的请求数量以及 Yaws 向每个客户端传送的数据量,并使用该信息来限制或拒绝特定客户端。
最后,虽然 "printf debugging" 有效,但它不太理想,尤其是在具有 built-in 跟踪功能的 Erlang 中。你应该考虑学习 dbg
module 这样你就可以跟踪你想要的任何函数,看看谁调用了它,看看传递给它的参数是什么,看看它是什么 returns,等等
我唯一的调试方法 (io:format/2
) 在 YAWS 中不起作用。我不知所措。我的主管启动了三个进程:ETS Manager、YAWS Init 和 Ratelimiter。这是成功的。我可以在 shell... 中使用速率限制器来调用 YAWS 应该调用的相同函数。不同之处在于 shell 的行为符合我的预期,我不知道 YAWS 中发生了什么。
我知道如果我在 shell 中发送垃圾邮件命令:ratelimiter:limit(IP)
它最终会 return true
。我可以执行以下命令,它也会 return true
: ratelimiter:lockout(IP), ratelimiter:blacklist(IP)
。限制器是 gen_server
.
函数执行以下操作:
limit/1
:如果计数器 > 阈值,检查 ETS table;更新计数器。如果计数器 > 黑名单阈值 mnesia tableblacklist/1
:检查 mnesia table 是否存在条目;是:重置计时器lockout/1
: 立即将ID输入mnesia table
在我的 arg_rewrite_mod
模块中,我正在做一些检查以确保我收到我期望的 HTTP 请求,即 GET、POST 和 HEAD。我认为这也是进行速率限制的好地方。在 Web 服务器的事件链中尽快执行此操作。
除了使用 "printf"s 和限制器外,我对 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 table 也有零条目。使用 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.
我这样做是因为我认为它会是 "easier."
当作为另一个应用程序的一部分启动 Yaws 时,使用它的 embedding support 很重要。 Yaws 嵌入启动代码所做的一件重要事情是将应用程序环境变量 embedded
设置为 true
:
application:set_env(yaws, embedded, true),
Yaws 在它的几个代码路径中检查这个变量,特别是在初始化期间,以避免假设它是 运行 作为 stand-alone 守护进程。
关于速率限制,您可以考虑使用 shaper,而不是使用 arg 重写器。 yaws_shaper
模块提供了一种行为,期望其回调模块实现两个功能:
check/1
:yaws_shaper
调用这个让回调模块决定是否允许客户端的请求。它将客户端主机信息作为回调参数传递。您的整形器回调模块 return 要么是允许请求继续的原子allow
,要么是元组{deny, Status, Message}
,其中Status
是到 return 的 HTTP 状态代码给客户端,例如 429 表示客户端发出的请求过多,Message
是任何额外的 HTML 要 returned 给客户端。 (如果Message
也可以包含回复 header,例如Retry-After
,那可能会更好;这是我会考虑添加到 Yaws 中的东西。)update/3
:yaws_shaper
在客户端的响应准备好 returned 时调用它。第一个参数是客户端主机信息,第二个参数是"hits"的数量(每个请求的值为1),第三个参数是响应客户端请求而传送的字节数。您的整形器回调模块可以从update/3
returnok
(Yaws 不使用 return 值)。
整形器可以使用此框架来跟踪每个客户端发出的请求数量以及 Yaws 向每个客户端传送的数据量,并使用该信息来限制或拒绝特定客户端。
最后,虽然 "printf debugging" 有效,但它不太理想,尤其是在具有 built-in 跟踪功能的 Erlang 中。你应该考虑学习 dbg
module 这样你就可以跟踪你想要的任何函数,看看谁调用了它,看看传递给它的参数是什么,看看它是什么 returns,等等