libapa...:Filter(3pm)

🌸 Apache2::Filter 🌸 β€” the Perl API for Apache 2.0 Filtering

okay bestie, this is from the libapa...:Filter(3pm) page (User Contributed Perl Documentation, for Apache2::Filter), and it's a whole era. Let's get into it πŸ’…


✨ SYNOPSIS ✨

use Apache2::Filter ();

# filter attributes
my $c = $f->c;
my $r = $f->r;
my $frec = $f->frec();
my $next_f = $f->next;

my $ctx = $f->ctx;
$f->ctx($ctx);

# bucket brigade filtering API
$rc = $f->next->get_brigade($bb, $mode, $block, $readbytes);
$rc = $f->next->pass_brigade($bb);
$rc = $f->fflush($bb);

# streaming filtering API
while ($filter->read(my $buffer, $wanted)) {
    # transform $buffer here
    $filter->print($buffer);
}
if ($f->seen_eos) {
    $filter->print("filter signature");
}

# filter manipulations
$r->add_input_filter(\&callback);
$c->add_input_filter(\&callback);
$r->add_output_filter(\&callback);
$c->add_output_filter(\&callback);
$f->remove;

πŸ’– DESCRIPTION πŸ’–

Apache2::Filter hands you the Perl API for the Apache 2.0 filtering framework. it's giving gateway to all the filter vibes ✨

and like, before you go feral with it β€” make sure you actually read "the Filtering tutorial|docs::2.0::user::handlers::filters". it's the lore. required reading, fr fr πŸ“š


πŸ¦‹ Common Filter API πŸ¦‹

these methods? you can call them from any filter handler. every single one. slay.

🌟 c 🌟

grab the current connection object, whether you're in a connection filter or a request filter:

$c = $f->c;
  • obj: $f ( Apache2::Filter object )
  • ret: $c ( Apache2::Connection object )
  • since: 2.0.00

🌟 ctx 🌟

get / set the filter context data. it's the lil storage pocket your filter carries around πŸŽ€

$ctx = $f->ctx;
       $f->ctx($ctx);
  • obj: $f ( Apache2::Filter object )
  • opt arg2: $ctx ( SCALAR ) β€” next context
  • ret: $ctx ( SCALAR ) β€” current context
  • since: 2.0.00

okay so here's the tea on filter context β˜• a filter context is created before the filter gets called for the very first time, and it's destroyed at the end of the request. and it's preserved between filter invocations of the same request β€” so if your filter needs to stash data between invocations, that's literally what this is for. the filter context starts out initialized with the undef value.

the ctx method takes a single SCALAR argument, so if you wanna store any other perl data structure in there you gotta use a reference to it. iykyk.

for example, you can store a hash reference:

$f->ctx({ foo => 'bar' });

and then access it:

$foo = $f->ctx->{foo};

pro tip bestie: if you're accessing the context more than once, it's more efficient to copy its value before you use it:

my $ctx = $f->ctx;
$foo = $ctx->{foo};

that's how you dodge redundant method calls πŸ‘‘ as of this writing $ctx is not a tied variable, so if you modify it you need to store it back at the end:

$f->ctx($ctx);
META: later we might make it a TIEd-variable interface, so it'll be stored automatically.

and besides its main character job of storing context data across multiple filter invocations, this method is also super useful as a flag. for example, here's how to make sure something only happens once during the filter's whole life:

unless ($f->ctx) {
    do_something_once();
    $f->ctx(1);
}

periodt πŸ’…


🌟 frec 🌟

get / set the Apache2::FilterRec (filter record) object:

$frec = $f->frec();
  • obj: $f ( Apache2::Filter object )
  • ret: $frec ( Apache2::FilterRec object )
  • since: 2.0.00

for example you can call $frec->name to get the filter's name. cute, right? 🌸


🌟 next 🌟

returns the Apache2::Filter object of the next filter in the chain β€” the next one in line, bestie 🫢

$next_f = $f->next;
  • obj: $f ( Apache2::Filter object ) β€” the current filter object
  • ret: $next_f ( Apache2::Filter object ) β€” the next filter object in chain
  • since: 2.0.00

since Apache inserts several core filters at the end of each chain, normally this method always returns an object. however β€” if it's not a mod_perl filter handler, you can only call the following methods on it: get_brigade, pass_brigade, c, r, frec and next. call any other method and the behavior is undefined. don't do it. not worth it 😭

the next filter can be a mod_perl one or not, and it's super easy to tell which one it is β€” just call $f->frec->name. vibe check complete ✨


🌟 r 🌟

inside an HTTP request filter, retrieve the current request object:

$r = $f->r;
  • obj: $f ( Apache2::Filter object )
  • ret: $r ( Apache2::RequestRec object )
  • since: 2.0.00

and heads up: if a sub-request adds filters, then that sub-request object is the one associated with the filter. noted πŸ“


🌟 remove 🌟

remove the current filter from the filter chain (for the current request or connection):

$f->remove;
  • obj: $f ( Apache2::Filter object )
  • ret: no return value
  • since: 2.0.00
notice: you should either complete the current filter invocation normally (by calling get_brigade or pass_brigade, depending on the filter kind) or β€” if nothing was done β€” return Apache2::Const::DECLINED, and mod_perl will take care of passing the current bucket brigade through unmodified to the next filter in chain. those are your options, bestie. no in-between πŸ’€
Note: calling remove() on the very top connection filter doesn't affect the filter chain due to a bug in Apache 2.0 (which may be fixed in 2.1). so don't use it with connection filters till it gets fixed in Apache β€” and then make sure to require the minimum Apache version if you rely on it. fr fr, this one bites people πŸ›

remember that if the connection is $c->keepalive and the connection filter is removed, it won't be added again until the connection is closed β€” which may happen after many HTTP requests. so you may wanna keep the filter in place and just pass the data through unmodified by returning Apache2::Const::DECLINED. and if you need to reset the whole thing or parts of the filter context between requests, use the technique based on $c->keepalives counting. iconic workaround energy πŸ‘‘

also: this method works for native Apache (non-mod_perl) filters too. equal opportunity slay 🌈


✨ Bucket Brigade Filter API ✨

ok so these are the methods literally any filter can call, and they mess with bucket brigades directly. no middleman, no nothing. let's get into it πŸ’…


🌊 fflush

Flushes a bucket brigade down the filter stack. bye brigade πŸ‘‹

$rc = $f->fflush($bb);
  • obj: $f (Apache2::Filter object) β€” the current filter
  • arg1: $bb (Apache2::Brigade object) β€” the brigade to flush
  • ret: $rc (APR::Const status constant) β€” go peek at the pass_brigade() entry for the deets
  • excpt: APR::Error β€” exceptions are thrown only when this function is called in the VOID context. refer to the get_brigade() entry for the full story
  • since: 2.0.00

fflush is a shortcut method, iykyk. so instead of doing all of this:

my $b = APR::Bucket::flush_create($f->c->bucket_alloc);
$bb->insert_tail($b);
$f->pass_brigade($bb);

you can just write this instead:

$f->fflush($bb);

iconic behaviour tbh 🫢


πŸ“₯ get_brigade

this is the method for bucket brigade input filters. it acquires a bucket brigade from the upstream input filter.

$rc = $next_f->get_brigade($bb, $mode, $block, $readbytes);
$rc = $next_f->get_brigade($bb, $mode, $block);
$rc = $next_f->get_brigade($bb, $mode)
$rc = $next_f->get_brigade($bb);
yeah bestie those last two synopsis lines be missing their semicolons in the original. we keep them exactly as they are. no cap πŸ’€
  • obj: $next_f (Apache2::Filter object) β€” the next filter in the filter chain.
    • inside filter handlers it's usually $f->next. inside protocol handlers: $c->input_filters.
  • arg1: $bb (APR::Brigade object) β€” the original bucket brigade passed to get_brigade(), which must be empty.
    • inside input filter handlers it's usually the second argument to the filter handler.
    • otherwise it should be created:
my $bb = APR::Brigade->new($c->pool, $c->bucket_alloc);
  • on return it gets populated with the next bucket brigade. that brigade may contain nothing if there was no more data to read. the return status tells you the outcome.
  • opt arg2: $mode (Apache2::Const :input_mode constant) β€” the filter mode in which the data should be read.
    • if you're inside the filter handler, you should normally pass the same mode that was passed to the filter handler (that's the third argument).
    • the available modes get presented at the end of this section πŸ‘‡
    • if the argument $mode is not passed, Apache2::Const::MODE_READBYTES is used as a default value.
  • opt arg3: $block (APR::Const :read_type constant) β€” you may ask the reading operation to be blocking: APR::Const::BLOCK_READ, or nonblocking: APR::Const::NONBLOCK_READ.
    • if you're inside the filter handler, you should normally pass the same blocking mode argument that was passed to the filter handler (the forth argument).
    • if the argument $block is not passed, APR::Const::BLOCK_READ is used as a default value.
  • opt arg4: $readbytes (integer) β€” how many bytes to read from the next filter.
    • if you're inside the filter handler, you may want the same number of bytes, as the upstream filter, i.e. the argument that was passed to the filter handler (the fifth argument).
    • if the argument $block is not passed, 8192 is used as a default value.
  • ret: $rc (APR::Const status constant)
    • on success, APR::Const::SUCCESS is returned and $bb is populated (see the $bb entry).
    • in case of a failure ✨ a failure code is returned, in which case normally it should be returned to the caller.
    • if the bottom-most filter doesn't read from the network, then Apache2::NOBODY_READ is returned (META: need to add this constant).
    • inside protocol handlers the return code can also be APR::Const::EOF, which is success as well.
  • excpt: APR::Error
    • you don't have to ask for the return value. if this function is called in the VOID context, e.g.:
$f->next->get_brigade($bb, $mode, $block, $readbytes);
  • mod_perl will do the error checking on your behalf, and if the return code is not APR::Const::SUCCESS, an APR::Error exception will be thrown. the only time you want to do the error checking yourself, is when return codes besides APR::Const::SUCCESS are considered as successful and you want to manage them by yourself.
  • since: 2.0.00

πŸŽ€ Available input filter modes

these are the optional second argument $mode, bestie:

  • Apache2::Const::MODE_READBYTES β€” the filter should return at most readbytes data
  • Apache2::Const::MODE_GETLINE β€” the filter should return at most one line of CRLF data. (if a potential line is too long or no CRLF is found, the filter may return partial data).
  • Apache2::Const::MODE_EATCRLF β€” the filter should implicitly eat any CRLF pairs that it sees.
  • Apache2::Const::MODE_SPECULATIVE β€” the filter read should be treated as speculative and any returned data should be stored for later retrieval in another mode.
  • Apache2::Const::MODE_EXHAUSTIVE β€” the filter read should be exhaustive and read until it can not read any more. use this mode with extreme caution ⚠️
  • Apache2::Const::MODE_INIT β€” the filter should initialize the connection if needed, NNTP or FTP over SSL for example.

either compile all these constants with:

use Apache2::Const -compile => qw(:input_mode);

but it's a bit more efficient to compile only those constants that you need. πŸ’…

🌸 Example

here's a fragment of a filter handler that receives a bucket brigade from the upstream filter:

use Apache2::Filter ();
use APR::Const    -compile => qw(SUCCESS);
use Apache2::Const -compile => qw(OK);
sub filter {
    my ($f, $bb, $mode, $block, $readbytes) = @_;

    my $rc = $f->next->get_brigade($bb, $mode, $block, $readbytes);
    return $rc unless $rc == APR::Const::SUCCESS;

    # ... process $bb

    return Apache2::Const::OK;
}

usually the arguments $mode, $block, $readbytes are the same as passed to the filter itself.

you can see that in case of a failure, the handler returns immediately with that failure code, which gets propagated to the downstream filter.

if you decide not check the return code, you can write it as:

sub filter {
    my ($f, $bb, $mode, $block, $readbytes) = @_;

    $f->next->get_brigade($bb, $mode, $block, $readbytes);

    # ... process $bb

    return Apache2::Const::OK;
}

and the error checking will be done on your behalf. so ✨convenient✨

you will find many more examples in "the filter handlers|docs::2.0::user::handlers::filters" and "the protocol handlers|docs::2.0::user::handlers::protocols" tutorials.


πŸ“€ pass_brigade

this is the method for bucket brigade output filters. it passes the current bucket brigade to the downstream output filter.

$rc = $next_f->pass_brigade($bb);
  • obj: $next_f (Apache2::Filter object) β€” the next filter in the filter chain.
    • inside output filter handlers it's usually $f->next. inside protocol handlers: $c->output_filters.
  • arg1: $bb (APR::Brigade object) β€” the bucket brigade to pass.
    • inside output filter handlers it's usually the second argument to the filter handler (after potential manipulations).
  • ret: $rc (APR::Const status constant)
    • on success, APR::Const::SUCCESS is returned.
    • in case of a failure ✨ a failure code is returned, in which case normally it should be returned to the caller.
    • if the bottom-most filter doesn't write to the network, then Apache2::NOBODY_WROTE is returned (META: need to add this constant).
    • also refer to the get_brigade() entry to see how to avoid checking the errors explicitly.
  • excpt: APR::Error β€” exceptions are thrown only when this function is called in the VOID context. refer to the get_brigade() entry for details.
  • since: 2.0.00

the caller relinquishes ownership of the brigade (i.e. it may get destroyed/overwritten/etc. by the callee). so like, goodbye brigade, she's someone else's problem now πŸ’€

πŸ¦‹ Example

here's a fragment of a filter handler that passes a bucket brigade to the downstream filter (after some potential processing of the buckets in the bucket brigade):

use Apache2::Filter ();
use APR::Const    -compile => qw(SUCCESS);
use Apache2::Const -compile => qw(OK);
sub filter {
    my ($f, $bb) = @_;

    # ... process $bb

    my $rc = $f->next->pass_brigade($bb);
    return $rc unless $rc == APR::Const::SUCCESS;

    return Apache2::Const::OK;
}

Streaming Filter API 🌊✨

Bestie, these next ones are the moment. Any filter that's vibing with the simplified streaming functionality can call them. Universal access, no gatekeeping πŸ’…


print πŸ’Œ

Sends the contents of $buffer along to the next filter in the chain (via the internal buffer, obvs).

$sent = $f->print($buffer);
  • obj: $f (an Apache2::Filter object)
  • arg1: $buffer (string) β€” the data you're sending, bestie πŸ’–
  • ret: $sent (integer) β€” how many characters were sent. Girl, you do not need to check this one, because everything should go through, and if something goes wrong an exception gets thrown. Trust. 😌
  • excpt: APR::Error
  • since: 2.0.00
Heads up babes: this method should be used only in streaming filters. No exceptions. 🚫

read πŸ“–

Reads data from the filter. The way that this one just works?

$read = $f->read($buffer, $wanted);
  • obj: $f (an Apache2::Filter object)
  • arg1: $buffer (SCALAR) β€” the buffer you're filling. Fr fr, all previous data in it will be lost. Gone. Poof. πŸ’€
  • opt arg2: $wanted (integer) β€” how many bytes to attempt to read.
    • If you don't pass this optional argument, the default 8192 gets used. Default behavior, slay.
  • ret: $read (integer) β€” how many bytes were actually read.
    • $buffer gets populated with the string that got read. If there was nothing to read, it'll hold an empty string. So empty string = nothing happened, not a crisis 🫢
  • excpt: APR::Error
  • since: 2.0.00

So it reads at most $wanted characters into $buffer, and the returned $read tells you exactly how many got read β€” which makes it stupidly easy to drop into a while loop:

while ($filter->read(my $buffer, $wanted)) {
    # transform $buffer here
    $filter->print($buffer);
}

Now here's the tea 🍡: this is a streaming filter method, which means it grabs a single bucket brigade behind the scenes and reads data from all its buckets. So it can only read from one bucket brigade per filter invocation. One. That's the rule, bestie.

And if the EOS bucket gets read? The seen_eos method will return a true value. Foreshadowing ✨


seen_eos 🏁

This method returns a true value when the EOS bucket is seen by the read method. It's giving end-of-story energy.

$ok = $f->seen_eos;
  • obj: $f (an Apache2::Filter object) β€” the filter to remove
  • ret: $ok (boolean) β€” a true value if EOS has been seen, otherwise a false value
  • since: 2.0.00

This one only works in streaming filters that exhaustively $f->read all the incoming data in a while loop, like so:

while ($f->read(my $buffer, $wanted)) {
    # do something with $buffer
}
if ($f->seen_eos) {
    # do something
}

That technique right there is chef's kiss when a streaming filter wants to append something to the very very end of the data, or do a lil something at the end of the last filter invocation. Once the EOS bucket is read, the filter should expect not to be invoked again. It's retired. Enjoy the retirement πŸŽ€

Now, plot twist: if an input streaming filter doesn't consume all the data in the bucket brigade (or even across several bucket brigades, no shame), it has to generate the EOS event by itself. So when the filter is done, it has to set the EOS flag:

$f->seen_eos(1);

And when the filter handler returns, internally mod_perl will handle creating and sending the EOS bucket to the upstream input filter. It's got you πŸ’œ

A similar logic may apply for output filters. Just saying.

⚠️ In most other cases you shouldn't set this flag. Like, don't. When this flag is prematurely set (before the real EOS bucket has arrived) in the current filter invocation, instead of invoking the filter again, mod_perl will create and send the EOS bucket to the next filter and ignore any other bucket brigades that may have been left to consume. That's a big deal. As mentioned earlier, this special behavior is useful for writing special tests that test abnormal situations β€” so, tests only. Not for your regular code. Periodt.

Other methods which affect filters, but get called on non-Apache2::Filter objects. Different object, same drama.

add_input_filter πŸ“₯

Adds &callback filter handler to the input request filter chain.

$r->add_input_filter(\&callback);

Adds &callback filter handler to the input connection filter chain.

$c->add_input_filter(\&callback);
  • obj: $c (an Apache2::Connection object) or $r (an Apache2::RequestRec object)
  • arg1: &callback (CODE ref)
  • ret: no return value
  • since: 2.0.00
[META: It seems that you can't add a filter when another filter is called. I've tried to add an output connection filter from the input connection filter when it was called for the first time. It didn't have any affect for the first request (over keepalive connection). The only way I succeeded to do that is from that input connection filter's filter_init handler. In fact it does work if there is any filter additional filter of the same kind configured from httpd.conf or via filter_init. It looks like there is a bug in httpd, where it doesn't prepare the chain of 3rd party filter if none were inserted before the first filter was called. ] πŸ’€

add_output_filter πŸ“€

Adds &callback filter handler to the output request filter chain.

$r->add_output_filter(\&callback);

Adds &callback filter handler to the output connection filter chain.

$c->add_output_filter(\&callback);
  • obj: $c (an Apache2::Connection object) or $r (an Apache2::RequestRec object)
  • arg1: &callback (CODE ref)
  • ret: no return value
  • since: 2.0.00

Filter Handler Attributes πŸŽ€

Okay so, packages using filter attributes have to subclass Apache2::Filter. Non-negotiable, bestie:

package MyApache2::FilterCool;
use base qw(Apache2::Filter);

Attributes get parsed during the code compilation, by the function MODIFY_CODE_ATTRIBUTES β€” which is inherited from the Apache2::Filter package. Family inheritance, love that for us 🌸

FilterRequestHandler πŸ—‚οΈ

The FilterRequestHandler attribute tells mod_perl to insert the filter into an HTTP request filter chain.

For example, to configure an output request filter handler, drop the FilterRequestHandler attribute into the handler subroutine's declaration:

package MyApache2::FilterOutputReq;
sub handler : FilterRequestHandler { ... }

and then add the configuration entry:

PerlOutputFilterHandler MyApache2::FilterOutputReq

This is the default mode, bestie. So if you're writing an HTTP request filter, you don't even have to specify this attribute. It's free. It's automatic. It's lowkey iconic.

The section HTTP Request vs. Connection Filters delves into more details, if you wanna go deeper πŸ‘‘

FilterConnectionHandler πŸ”Œ

The FilterConnectionHandler attribute tells mod_perl to insert this filter into a connection filter chain.

For example, to configure an output connection filter handler, use the FilterConnectionHandler attribute in the handler subroutine's declaration:

package MyApache2::FilterOutputCon;
sub handler : FilterConnectionHandler { ... }

and add the configuration entry:

PerlOutputFilterHandler MyApache2::FilterOutputCon

The section HTTP Request vs. Connection Filters delves into more details πŸ’–

FilterInitHandler 🌱

The attribute FilterInitHandler marks the function as suitable to be used as a filter initialization callback β€” which gets called immediately after a filter is inserted into the filter chain and before it's actually called. Early bird energy.

sub init : FilterInitHandler {
    my $f = shift;
    #...
    return Apache2::Const::OK;
}

In order to hook this filter callback, the real filter has to assign this callback using the FilterHasInitHandler, which accepts a reference to the callback function. Yes, they're a package deal πŸ’ž

For further discussion and examples, refer to the Filter Initialization Phase tutorial section.

FilterHasInitHandler πŸͺ

If a filter wants to run an initialization callback, it can register one using the FilterHasInitHandler attribute. Similar to push_handlers, the callback reference is expected β€” not a callback name. And the callback function you use has to have the FilterInitHandler attribute. For example:

package MyApache2::FilterBar;
use base qw(Apache2::Filter);
sub init   : FilterInitHandler { ... }
sub filter : FilterRequestHandler FilterHasInitHandler(\&init) {
    my ($f, $bb) = @_;
    # ...
    return Apache2::Const::OK;
}

For further discussion and examples, refer to the Filter Initialization Phase tutorial section. Go read it, bestie, it's good for you πŸ“š


Configuration βš™οΈβœ¨

mod_perl 2.0 filters configuration is explained in the filter handlers tutorial. That's the whole vibe check right there.

PerlInputFilterHandler

See PerlInputFilterHandler.

PerlOutputFilterHandler

See PerlOutputFilterHandler.

PerlSetInputFilter

See PerlSetInputFilter.

PerlSetOutputFilter

See PerlSetInputFilter.

Nope, that's not a typo on my part, bestie β€” it says PerlSetInputFilter for PerlSetOutputFilter in the original. I'm just the messenger 🫠

TIE Interface πŸŽ€πŸͺ’

Apache2::Filter also implements a tied interface, so you can work with the $f object as a hash reference. Fancy.

Real talk though: the TIE interface is mostly unimplemented and might be implemented post 2.0 release. So like, it's giving coming soon πŸ’€

TIEHANDLE

$ret = TIEHANDLE($stashsv, $sv);
  • obj: $stashsv (SCALAR)
  • arg1: $sv (SCALAR)
  • ret: $ret (SCALAR)
  • since: subject to change
$ret = PRINT(...);
  • obj: ... (XXX)
  • ret: $ret (integer)
  • since: subject to change

See Also πŸ‘€πŸ’–

mod_perl 2.0 documentation.


mod_perl 2.0 and its core modules are copyrighted under The Apache Software License, Version 2.0.


Authors πŸ‘‘πŸ«Ά

The mod_perl development team and numerous contributors.


And that's a wrap on the whole page, bestie!! πŸŽ‰ This part came from perl v5.42.3, dated 2026-08-20, and yeah β€” the page name really is that truncated lil libapa...:Filter(3pm) situation. Aesthetic? Debatable. Iconic? Absolutely. Go forth and filter, you main character πŸŒˆβœ¨πŸ’…

πŸ“¦ where this page comes from