Torsten Förtsch
IT System Development & Security
Kaum macht man's richtig, schon geht's, ;-)

>> Home >> ModPerl >> Infinte output

Potentially infinte output

In this article I'll describe bucket brigade based output in a PerlResponseHandler. This technique can be used similar to the simpler stream based IO that is usually used. As an example I'll implement a browser clock based on HTTP Server Push.

Here is a first solution that uses stream based IO via $r->print:

sub handler {
  my ($r)=@_;

  my $boundary='The Final Frontier';
  $r->content_type(qq{multipart/x-mixed-replace;boundary="$boundary";});
  $boundary="--$boundary\n";

  my $mpheader=<<'HEADER';
Content-type: text/html; charset=UTF-8;

HEADER

  for(1..100) {
    $r->print($boundary);
    $r->print($mpheader);
    my $msg='<html><body><h1>'.localtime()."</h1></body></html>\n";
    $r->print($msg);
    $r->rflush;
    sleep 1;
  }

  $r->print($boundary);

  return Apache2::Const::OK;
}

There is nothing wrong with this code except if you change the for(1..100) into a really infinite while(1) loop you'll notice that the httpd process size increases over time also without limits. This is considered a bug and there is hope that it will be resolved some day.

But fortunately it's quite easy to replace the stream based IO by bucket brigades, the underlying Apache interface. Thus, the bug can be circumvented:

sub handler {
  my ($r)=@_;

  my $boundary='The Final Frontier';
  $r->content_type(qq{multipart/x-mixed-replace;boundary="$boundary";});
  $boundary="--$boundary\n";

  my $mpheader=<<'HEADER';
Content-type: text/html; charset=UTF-8;

HEADER

  my $ba=$r->connection->bucket_alloc;
  my $bb=APR::Brigade->new($r->pool, $ba);
  while(1) {
    $bb->insert_tail(APR::Bucket->new($ba, $boundary));
    $bb->insert_tail(APR::Bucket->new($ba, $mpheader));
    my $msg='<html><body><h1>'.localtime()."</h1></body></html>\n";
    $bb->insert_tail(APR::Bucket->new($ba, $msg));
    $bb->insert_tail(APR::Bucket::flush_create $ba);
    $r->output_filters->pass_brigade($bb);
    $bb->cleanup;
    sleep 1;
  }

  $bb->insert_tail(APR::Bucket->new($ba, $boundary));
  $bb->insert_tail(APR::Bucket::eos_create $ba);
  $r->output_filters->pass_brigade($bb);

  return Apache2::Const::OK;
}

The clock is available here. But it ticks for only 30 seconds due to the limited resources of my server.

Thanks to all who has worked on the solution, see http://www.gossamer-threads.com/lists/modperl/modperl/101225

Letzte Aktualisierung: 20.03.2010