Permalink
Browse files

add shaper directive to control access (capflam)

inspired by the mod_bwshare module of Apache, add the shaper directive
to control access to virtual servers. Access can be controlled based
on the client's IP address. It is also possible to throttle HTTP
requests based on the client's download rate. External modules used to
shape the traffic must implement the new behaviour yaws_shaper.
  • Loading branch information...
capflam authored and vinoski committed May 4, 2011
1 parent fd3ca71 commit c650d7762e38ef2ab077b731a2b741ecdc849a2e
Showing with 96 additions and 2 deletions.
  1. +10 −0 doc/yaws.tex
  2. +2 −1 include/yaws.hrl
  3. +9 −0 man/yaws.conf.5
  4. +1 −0 src/Makefile
  5. +4 −0 src/yaws_config.erl
  6. +8 −1 src/yaws_server.erl
  7. +62 −0 src/yaws_shaper.erl
View
@@ -2282,6 +2282,16 @@ \section{Server Part}
\end{itemize}
+\item \verb+shaper = Module+ -
+ Defines a module to control access to this virtual server.
+
+ Access can be controlled based on the IP address of the client. It
+ is also possible to throttles HTTP requests based on the client's
+ download rate. This module must implement the behaviour
+ \verb+yaws_shaper+.
+
+ There is no such module configured by default.
+
\item \verb+docroot = Directory+ -
This makes the server serve all its content from
Directory.
View
@@ -232,7 +232,8 @@
extra_cgi_vars = [],
stats, %% raw traffic statistics
fcgi_app_server, %% FastCGI application server {host,port}
- php_handler = {cgi, "/usr/bin/php-cgi"}
+ php_handler = {cgi, "/usr/bin/php-cgi"},
+ shaper
}).
%% we cannot compare sconfs directly due to the ets
View
@@ -367,6 +367,15 @@ For all of these callbacks, \fBServerName\fR is the virtual server's name,
\fIType\fR is the atom access or auth and \fIState\fR is the internal state of
the logger.
+.TP
+\fBshaper = Module\fR
+Defines a module to control access to this virtual server. Access can be
+controlled based on the IP address of the client. It is also possible to
+throttles HTTP requests based on the client's download rate. This module must
+implement the behaviour \fIyaws_shaper\fR.
+
+There is no such module configured by default.
+
.TP
\fBdir_listings = true | true_nozip | false\fR
Setting this directive to false disallows the automatic
View
@@ -52,6 +52,7 @@ MODULES=yaws \
yaws_stats \
yaws_vdir \
yaws_multipart \
+ yaws_shaper \
$(BITSMODS)
View
@@ -1239,6 +1239,10 @@ fload(FD, server, GC, C, Cs, Lno, Chars) ->
[Lno, Reason])}
end;
+ ["shaper", '=', Module] ->
+ C2 = C#sconf{shaper = list_to_atom(Module)},
+ fload(FD, server, GC, C2, Cs, Lno+1, Next);
+
[H|T] ->
{error, ?F("Unexpected input ~p at line ~w", [[H|T], Lno])};
Err ->
View
@@ -1126,7 +1126,12 @@ aloop(CliSock, GS, Num) ->
put(sc, SC),
yaws_stats:hit(),
check_keepalive_maxuses(GS, Num),
- Call = call_method(Req#http_request.method, CliSock, Req, H),
+ Call = case yaws_shaper:check(SC, IP) of
+ allow ->
+ call_method(Req#http_request.method,CliSock,Req,H);
+ {deny, Status, Msg} ->
+ deliver_xxx(CliSock, Req, Status, Msg)
+ end,
Call2 = fix_keepalive_maxuses(Call),
handle_method_result(Call2, CliSock, IP, GS, Req, H, Num);
closed ->
@@ -1191,10 +1196,12 @@ erase_transients() ->
handle_method_result(Res, CliSock, IP, GS, Req, H, Num) ->
case Res of
continue ->
+ yaws_shaper:update(get(sc), IP, Req),
maybe_access_log(IP, Req, H),
erase_transients(),
aloop(CliSock, GS, Num+1);
done ->
+ yaws_shaper:update(get(sc), IP, Req),
maybe_access_log(IP, Req, H),
erase_transients(),
{ok, Num+1};
View
@@ -0,0 +1,62 @@
+%%%----------------------------------------------------------------------
+%%% File : yaws_shaper.erl
+%%% Author : Christopher Faulet <christopher@yakaz.com>
+%%% Purpose :
+%%% Created : 14 Dec 2010 by Christopher Faulet <christopher@yakaz.com>
+%%%----------------------------------------------------------------------
+
+-module(yaws_shaper).
+-author('christopher@yakaz.com').
+
+
+-export([behaviour_info/1]).
+
+%% API
+-export([
+ check/2,
+ update/3
+ ]).
+
+
+-include("../include/yaws.hrl").
+-include("../include/yaws_api.hrl").
+-include("yaws_debug.hrl").
+
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+behaviour_info(callbacks) ->
+ [{check,1}, {update,3}];
+behaviour_info(_Other) ->
+ undefined.
+
+
+check(#sconf{shaper=undefined}, _) ->
+ allow;
+check(#sconf{shaper=Mod}, IP) ->
+ case catch Mod:check(IP) of
+ allow ->
+ allow;
+ {deny, Status, Msg} ->
+ {deny, Status, Msg};
+ _ ->
+ allow
+ end.
+
+update(#sconf{shaper=undefined}, _, _) ->
+ ok;
+update(#sconf{shaper=Mod}, IP, Req) ->
+ Bytes = case Req#http_request.method of
+ 'HEAD' -> 0;
+ _ ->
+ case yaws:outh_get_contlen() of
+ undefined ->
+ case yaws:outh_get_act_contlen() of
+ undefined -> 0;
+ Actlen -> Actlen
+ end;
+ I2 -> I2
+ end
+ end,
+ catch Mod:update(IP, 1, Bytes).

0 comments on commit c650d77

Please sign in to comment.