TOD has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

the questions that I have, as you can see from the title, are related to mod_perl2 and the Perl API for Apache2 filters. I am trying to install a connection filter which blocks flooding attempts by allowing only one initial request per second and blocking all others (which are not subrequests) from the same IP. Obviously, I first thought, this should be accomplished by a PerlPreConnectionHandler. Its code is:

package Foo::FloodBlocker; use strict; use warnings; use Apache2::Connection; use Apache2::Const -compile => qw/FORBIDDEN OK/; use IPC::Shareable ':all'; use constant { FILTERGLUE => 'PIFt', FILTERSHMEM => 1048576 }; sub handler { my $c = shift; return Apache2::Const::OK if $c->keepalives; my $ip = $c->remote_ip; my $time = time; no warnings 'untie'; my ($shmem, %hash); $shmem = tie %hash, 'IPC::Shareable', FILTERGLUE, { create => 0, mode => 0666, size => FILTERSHMEM, exclusive => 0, destroy => 0 }; $ip =~ s/\./_/g; my $block; $shmem->shlock(LOCK_EX); if (exists $hash{$ip}) { if ($hash{$ip} < $time) { delete $hash{$ip}; } else { $block = 1; } } else { $hash{$ip} = $time; } $shmem->shunlock; untie %hash; undef $shmem; return Apache2::Const::FORBIDDEN if $block; Apache2::Const::OK; }
Rather simple, quick and clean. ;)
The funny thing, from which all my troubles arise, lies in $c->keepalives. Because when I try a request after having cleaned the browser cache, I get the HTML-document, maybe one or the other image, and accidentally a javascript document or a CSS-file.

So my first question is: does anyone know, whether Apache2 opens new connections for dealing with subrequests? And if it does so, how can I distinguish them from the initial request?

My first attempt to overcome these problems failed as well: I configured the package to serve as PerlProcessConnectionHandler, which then has the advantage of already defined filters. The code I inserted is as follows:

use APR::Const -compile => qw/SUCCESS/; use APR::Brigade; use APR::Bucket; use APR::Status; use APR::Error; [...] sub handler { [...] my $bb_in = APR::Brigade->new($c->pool, $c->bucket_alloc); my $bb_out = APR::Brigade->new($c->pool, $c->bucket_alloc); while (1) { my $rc = $c->input_filters->get_brigade($bb_in, Apache2::Const::MODE_GETLINE); last if APR::Status::is_EOF($rc); die APR::Error::strerror($rc) unless $rc == APR::Const::SUCCESS; my $last = 0; while (!$bb_in->is_empty) { my $b = $bb_in->first; $b->remove; if ($b->is_eos) { $bb_out->insert_tail($b); $last++; last; } if ($b->read(my $data)) { warn $data; # testing line } $bb_out->insert_tail($b); } if ($last) { $bb_out->insert_tail(APR::Bucket::flush_create($c->bucket_all +oc)); $c->output_filters->pass_brigade($bb_out); last; } } [...]
In fact this is a copy from the tutorial on protocol handlers, which is either buggy, or I misunderstood it. Because the condition if ($b->is_eos) is never fulfilled, and the handler hangs. The loop will only be left after the client has closed the connection.

The idea behind this bucket-sniffing attempt is, that I want to know whether there is a referrer to the request and what it looks like. Since at this stage of the request process the RequestRec object doesn't exist, this seems to be the only method.

Has anybody got a clue? What am I doing wrong?
Thx in advance
TOD (aka DEATH, for en Pratchett fans ;)

Edit: g0n - readmore tags

Replies are listed 'Best First'.
Re: Apache2 Perl(Pre|Process)ConnectionHandler
by perrin (Chancellor) on May 26, 2006 at 18:18 UTC
    I don't really understand what you're trying to do here, but two things come to mind. One is that nearly all browsers open multiple connections to the server, so you should expect to see at least two connections from a single browser at any given time. Another is that many apache modules for limiting bandwidth or request already exist, like mod_bandwidth, mod_evasive, etc. You can find them at http://modules.apache.org/.
      hmm... mod_bandwidth doesn't fit my demand, and mod_evasive doesn't exist. but nevertheless thanx for your reply. i found BruteBan.pm - which is rather dull, but brought me to the point: i should be patient and wait until the request object is present. maybe a PerlPostReadRequestHandler would do.