The following configuration defines a
<VirtualHost> listening on port 8085 that
enables the Book::Eliza2 connection handler, which
will run its output through the
Book::Eliza2::lowercase_filter filter:
Listen 8085
<VirtualHost _default_:8085>
PerlModule Book::Eliza2
PerlProcessConnectionHandler Book::Eliza2
PerlOutputFilterHandler Book::Eliza2::lowercase_filter
</VirtualHost>
As before, we start the httpd server:
panic% httpd
and try the new connection handler in action:
panic% telnet localhost 8085
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
Hello Eliza!
hi. what seems to be your problem?
Problem? I don't have any problems ;)
does that trouble you?
Not at all, I don't like problems.
i'm not sure i understand you fully.
I said that I don't like problems.
that is interesting. please continue.
You are boring :(
does it please you to believe i am boring?
Yes, yes!
please tell me some more about this.
Good bye!
i'm not sure i understand you fully.
Connection closed by foreign host.
Example 25-4. Book/Eliza2.pm
package Book::Eliza2;
use strict;
use warnings FATAL => 'all';
use Apache::Connection ( );
use APR::Bucket ( );
use APR::Brigade ( );
use APR::Util ( );
require Chatbot::Eliza;
use APR::Const -compile => qw(SUCCESS EOF);
use Apache::Const -compile => qw(OK MODE_GETLINE);
my $eliza = new Chatbot::Eliza;
sub handler {
my $c = shift;
my $bb_in = APR::Brigade->new($c->pool, $c->bucket_alloc);
my $bb_out = APR::Brigade->new($c->pool, $c->bucket_alloc);
my $last = 0;
while (1) {
my $rv = $c->input_filters->get_brigade($bb_in,
Apache::MODE_GETLINE);
if ($rv != APR::SUCCESS or $bb_in->empty) {
my $error = APR::strerror($rv);
unless ($rv = = APR::EOF) {
warn "[eliza] get_brigade: $error\n";
}
$bb_in->destroy;
last;
}
while (!$bb_in->empty) {
my $bucket = $bb_in->first;
$bucket->remove;
if ($bucket->is_eos) {
$bb_out->insert_tail($bucket);
last;
}
my $data;
my $status = $bucket->read($data);
return $status unless $status = = APR::SUCCESS;
if ($data) {
$data =~ s/[\r\n]*$//;
$last++ if $data =~ /good bye/i;
$data = $eliza->transform( $data ) . "\n\n";
$bucket = APR::Bucket->new($data);
}
$bb_out->insert_tail($bucket);
}
my $b = APR::Bucket::flush_create($c->bucket_alloc);
$bb_out->insert_tail($b);
$c->output_filters->pass_brigade($bb_out);
last if $last;
}
Apache::OK;
}
use base qw(Apache::Filter);
use constant BUFF_LEN => 1024;
sub lowercase_filter : FilterConnectionHandler {
my $filter = shift;
while ($filter->read(my $buffer, BUFF_LEN)) {
$filter->print(lc $buffer);
}
return Apache::OK;
}
1;
For the purpose of explaining how this connection handler works, we
are going to simplify the handler. The whole handler can be
represented by the following pseudocode:
while ($bb_in = get_brigade( )) {
while ($bucket_in = $bb_in->get_bucket( )) {
my $data = $bucket_in->read( );
$data = transform($data);
$bucket_out = new_bucket($data);
$bb_out->insert_tail($bucket_out);
}
$bb_out->insert_tail($flush_bucket);
pass_brigade($bb_out);
}
The handler receives the incoming data via bucket bridages, one at a
time, in a loop. It then processes each brigade, by retrieving the
buckets contained in it, reading in the data, transforming that data,
creating new buckets using the transformed data, and attaching them
to the outgoing brigade. When all the buckets from the incoming
bucket brigade are transformed and attached to the outgoing bucket
brigade, a flush bucket is created and added as the last bucket, so
when the outgoing bucket brigade is passed out to the outgoing
connection filters, it will be sent to the client right away, not
buffered.
If you look at the complete handler, the loop is terminated when one
of the following conditions occurs: an error happens, the
end-of-stream bucket has been seen (i.e., there's no
more input at the connection), or the received data contains the
string "good bye". As you saw in
the demonstration, we used the string "good
bye" to terminate our shrink's
session.