stas 2002/08/13 08:57:07 Modified: src/docs/2.0/user/handlers handlers.pod Log: more output filter examples Revision Changes Path 1.9 +182 -54 modperl-docs/src/docs/2.0/user/handlers/handlers.pod Index: handlers.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/2.0/user/handlers/handlers.pod,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- handlers.pod 13 Aug 2002 14:26:19 -0000 1.8 +++ handlers.pod 13 Aug 2002 15:57:07 -0000 1.9 @@ -747,7 +747,9 @@ If the attribute is not specified, the default C<FilterRequestHandler> attribute is assumed. Filters specifying subroutine attributes must -subclass C<Apache::Filter>. +subclass C<Apache::Filter>, others only need to: + + use Apache::Filter (); The request filters are usually configured in the C<E<lt>LocationE<gt>> or equivalent sections: @@ -836,6 +838,9 @@ The handler's configuration scope is C<DIR>. +The following sections include several examples of the +C<PerlInputFilterHandler> handler. + =head2 PerlOutputFilterHandler The C<PerlOutputFilterHandler> handler registers and configures output @@ -845,16 +850,20 @@ The handler's configuration scope is C<DIR>. -=head2 Filters All-in-One +The following sections include several examples of the +C<PerlOutputFilterHandler> handler. + + +=head2 All-in-One Filter Before we delve into the details of how to write filters that do -something, lets first write a simple filter that does nothing but -snooping on the data that goes through it. We are going to develop the -C<MyApache::FilterSnoop> handler which can snoop on request and -connection filters, in input and output modes. +something with the data, lets first write a simple filter that does +nothing but snooping on the data that goes through it. We are going to +develop the C<MyApache::FilterSnoop> handler which can snoop on +request and connection filters, in input and output modes. But first let's develop a simple response handler that simply dumps -the request's I<args> and I<content> strings: +the request's I<args> and I<content> as strings: file:MyApache/Dump.pm --------------------- @@ -917,8 +926,7 @@ content: mod_perl rules -As you can see it has simply dumped the query string and the posted -data. +As you can see it simply dumped the query string and the posted data. Now let's write the snooping filter: @@ -1015,8 +1023,8 @@ Finally we dump to STDERR the information about the type of the current mode, and the content of the bucket bridage. -Let's snoop on connection and request levels in both directions, by -applying the following configuration: +Let's snoop on connection and request filter levels in both +directions by applying the following configuration: Listen 8008 <VirtualHost _default_:8008> @@ -1030,6 +1038,7 @@ <Location /dump> SetHandler modperl PerlResponseHandler MyApache::Dump + # Request filters PerlInputFilterHandler MyApache::FilterSnoop::request PerlOutputFilterHandler MyApache::FilterSnoop::request </Location> @@ -1137,7 +1146,8 @@ HEAP: HTTP/1.1 200 OK Date: Tue, 13 Aug 2002 12:36:52 GMT - Server: Apache/2.0.40-dev (Unix) mod_perl/1.99_05-dev Perl/v5.8.0 mod_ssl/2.0.40-dev OpenSSL/0.9.6d DAV/2 + Server: Apache/2.0.40-dev (Unix) mod_perl/1.99_05-dev + Perl/v5.8.0 mod_ssl/2.0.40-dev OpenSSL/0.9.6d DAV/2 Content-Length: 43 Connection: close Content-Type: text/plain; charset=ISO-8859-1 @@ -1171,37 +1181,64 @@ without POSTing any data or simply running a GET request, the request input filter won't be called. -=head2 XXX -The stream-orientered output filter in the following example reverses -every line of the response, preserving the new line characters in -their places: - - file:httpd.conf - --------------- - PerlModule Apache::ReverseFilter - <Location /reverse> - SetHandler modperl - PerlResponseHandler Apache::ReverseFilter::response - PerlOutputFilterHandler Apache::ReverseFilter::output_filter - </Location> - file:Apache/ReverseFilter.pm - ---------------------------- - package Apache::ReverseFilter; +=head2 Bucket Brigades and Stream-Oriented Request Output Filters + +As mentioned earlier output filters can be written using the bucket +brigades manipulation or the simplified stream-oriented interface. + +First let's develop a response handler that send two lines of output: +numerals 0-9 and the English alphabet: + + file:MyApache/SendAlphaNum.pm + ------------------------------- + package MyApache::SendAlphaNum; use strict; - use warnings FATAL => 'all'; + use warnings; use Apache::RequestRec (); use Apache::RequestIO (); + + use Apache::Const -compile => qw(OK); + + sub handler { + my $r = shift; + + $r->content_type('text/plain'); + + $r->print(0..9, "0\n"); + $r->print('a'..'z', "\n"); + + Apache::OK; + } + 1; + +The purpose of our request output filter is to reverse every line of +the response, preserving the new line characters in their places. + + + +=head3 Stream-oriented Output Filter + +The first filter that we are going to implement is using the +stream-oriented interface: + + file:MyApache/FilterReverse1.pm + ---------------------------- + package MyApache::FilterReverse1; + + use strict; + use warnings; + use Apache::Filter (); use Apache::Const -compile => qw(OK); use constant BUFF_LEN => 1024; - sub output_filter { + sub handler { my $filter = shift; while ($filter->read(my $buffer, BUFF_LEN)) { @@ -1213,43 +1250,51 @@ Apache::OK; } - - sub response { - my $r = shift; - - $r->content_type('text/plain'); - $r->puts(1..9, "0\n"); - $r->puts('a'..'z', "\n"); - - Apache::OK; - } 1; +Next, we add the following configuration to I<httpd.conf>: + + PerlModule MyApache::FilterReverse1 + PerlModule MyApache::SendAlphaNum + <Location /reverse1> + SetHandler modperl + PerlResponseHandler MyApache::SendAlphaNum + PerlOutputFilterHandler MyApache::FilterReverse1 + </Location> -In this example when a request to I</reverse> is made, the response -handler C<Apache::ReverseFilter::response()> sends: +Now when a request to I</reverse1> is made, the response handler +C<MyApache::SendAlphaNum::handler()> sends: 1234567890 abcdefghijklmnopqrstuvwxyz as a response and the output filter handler -C<Apache::ReverseFilter::output_filter> reverses the lines, so the -client gets: +C<MyApache::FilterReverse1::handler> reverses the lines, so the client +gets: 0987654321 zyxwvutsrqponmlkjihgfedcba -The reversing filter is quite simple: it reads from the output stream -in the I<readline()> mode in chunks up to the buffer length (1024 in -our example), and then prints each line reversed while preserving the -new line control characters at the end of each line. In order not to -distract the reader from the purpose of the example the used code is -oversimplified and won't handle correctly input lines which are longer -than 1024 characters and possibly using a different line termination -pattern. So here is an example of a more complete handler, which does -takes care of these issues: +The C<Apache::Filter> module loads the C<read()> and C<print()> +methods which encapsulate the stream-oriented filtering interface. - sub output_filter { +The reversing filter is quite simple: in the loop it reads the data in +the I<readline()> mode in chunks up to the buffer length (1024 in our +example), and then prints each line reversed while preserving the new +line control characters at the end of each line. Behind the scenes +C<$filter-E<gt>read()> retrieves the incoming brigade and gets the +data from it, whereas C<$filter-E<gt>print()> appends to the new +brigade which is then sent to the next filter in the stack. C<read()> +breaks the while loop, when the brigade is emptied or the end of +stream is received. + +In order not to distract the reader from the purpose of the example +the used code is oversimplified and won't handle correctly input lines +which are longer than 1024 characters and possibly using a different +line termination pattern. So here is an example of a more complete +handler, which does takes care of these issues: + + sub handler { my $filter = shift; my $left_over = ''; @@ -1273,9 +1318,92 @@ +=head3 Bucket Brigades Output Filter + +The second filter that we are going to implement is using the bucket +brigades interface to accomplish exactly the same task as the first +filter. + + package MyApache::FilterReverse2; + + use strict; + use warnings; + + use Apache::Filter; + + use APR::Brigade (); + use APR::Bucket (); + + use Apache::Const -compile => 'OK'; + use APR::Const -compile => ':common'; + + sub handler { + my($filter, $bb) = @_; + + my $c = $filter->c; + my $new_bb = APR::Brigade->new($c->pool, $c->bucket_alloc); + + while (!$bb->empty) { + my $bucket = $bb->first; + + $bucket->remove; + + if ($bucket->is_eos) { + $new_bb->insert_tail($bucket); + last; + } + + my $data; + my $status = $bucket->read($data); + return $status unless $status == APR::SUCCESS; + + if ($data) { + $data = join "", + map {scalar(reverse $_), "\n"} split "\n", $data; + $bucket = APR::Bucket->new($data); + } + + $new_bb->insert_tail($bucket); + } + + my $rv = $filter->next->pass_brigade($new_bb); + return $rv unless $rv == APR::SUCCESS; + + Apache::OK; + } + 1; + +and the corresponding configuration: + + PerlModule MyApache::FilterReverse2 + PerlModule MyApache::SendAlphaNum + <Location /reverse2> + SetHandler modperl + PerlResponseHandler MyApache::SendAlphaNum + PerlOutputFilterHandler MyApache::FilterReverse2 + </Location> + +Now when a request to I</reverse2> is made, the client gets: + 0987654321 + zyxwvutsrqponmlkjihgfedcba +as expected. +The bucket brigades output filter version is just a bit more +complicated than the stream-oriented one. The handler receives the +incoming bucket brigade C<$bb> as its second argument. Since when the +handler is completed it must pass a brigade to the next filter in the +stack, we create a new bucket brigade into which we are going to put +the modified buckets and which eventually we pass to the next filter. + +The core of the handler is in removing buckets from the head of the +bucket brigade C<$bb> while there are some, reading the data from the +buckets, reversing and putting it into a newly created bucket which is +inserted to the end of the new bucket brigade. If we see a bucket +which designates the end of stream, we insert that bucket to the tail +of the new bucket brigade and break the loop. Finally we pass the +created brigade with modified data to the next filter and return. =head1 Handler (Hook) Types
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]