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...
1 parent fd3ca71 commit c650d7762e38ef2ab077b731a2b741ecdc849a2e @capflam capflam committed with vinoski May 4, 2011
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
10 doc/yaws.tex
@@ -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
3 include/yaws.hrl
@@ -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
9 man/yaws.conf.5
@@ -368,6 +368,15 @@ For all of these callbacks, \fBServerName\fR is the virtual server's name,
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
dir listing feature of Yaws. A status code 403 Forbidden will be sent.
View
1 src/Makefile
@@ -52,6 +52,7 @@ MODULES=yaws \
yaws_stats \
yaws_vdir \
yaws_multipart \
+ yaws_shaper \
$(BITSMODS)
View
4 src/yaws_config.erl
@@ -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
9 src/yaws_server.erl
@@ -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
62 src/yaws_shaper.erl
@@ -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.