You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Javier Alonso Sánchez <ja...@gmail.com> on 2005/08/03 16:33:08 UTC

Apache2 filters questions

Hi all.

I'm fighting hard with Apache2 filters  + mod_perl, and I have some
doubts to ask you:

I want to analyze a POST request to my Apache2 server, analyze it, and
change the headers to redirect the POST to another uri, depending on
the content (body of the POST).

As I need body+headers, I'm using a Input Connection Filter.

The main problem is how to stop the buckets brigades. It's easy to
analyze the body, but when I process it, the brigades with headers
have just passed to the next filter (or the core).

In order to "stop" the previous brigades, I read all the brigades
using a loop which knows when the body finishes, reading
Content-Length header. As I use Connection Filter, and there isn't
EOS, it is the unique way to know WHEN the filter must stop getting
brigades .... Are there any way else to know it ??

This is the loop in the filter handler:

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

        my $b = $bb->last;
        if ($seen_body_separator && $seen_cl) {
                $remaining -= $b->length;
                if ($remaining == 0) {$seen_eos++;}
        }
        $b->read(my $data);
        if (!$seen_cl and !$seen_body_separator and
              $data =~ /^Content-Length: (\d+)[\r\n]+$/)
        {
             $seen_cl++;
             $remaining = $1;
        }
        if ($data =~ /^[\r\n]+$/) {
                $seen_body_separator++;
        }
        $brigade .= $data;
      } while (!$seen_eos);


With this filter, I have only ONE brigade, with ALL the buckets of the
request. The brigade pass to then next filter, but there is not
response of the apache core. It seems that they were waiting for a
EOS. If I stop the request (Ctrl+C, or STOP in the browser) the
brigade passes to the core. But here, I have another problem, because
the apache core says that it is a bad header request. The full brigade
arrive to the core, like the correct header.

Must be:
POST /uri HTTP/1.1

It is:
POST /uri HTTP/1.1\nTE: deflate\nContent-type ......


Why does the brigade need an artificial Ctrl-C?
Does Apache need one bucket brigade per HEADER?


Is there another way to modify the header of a request once processed
the body content?

Thanks in advance.
Cheers

Re: Apache2 filters questions

Posted by Javier Alonso Sánchez <ja...@gmail.com>.
On 8/3/05, Stas Bekman <st...@stason.org> wrote:> 
> 
> I think you are much better off not to use a filter here. Use
> Apache::Request which can give the same POST body more 
> than once. So I'd
> write a trans hander, grabs the body, rewrites the headers based on
> the body and the content handler can still get the body (this is only
> available in libapreq2)
> 

Thanks for the quick response :)

A new question about Apache::Request.
In according to the documentation:

"The Apache2::Request module provides methods for parsing GET and POST
parameters encoded with either application/x-www-form-urlencoded or
multipart/form-data."

Does only work with these two body types?
Is there another way to get the body content and the headers at the same time?

I'd like to use Proxy Reserve configuration for certain POSTs, so
trans handler seems a good way to change the header before Proxy
phase. Any POSTs would continue their way to the Proxy directive
whithout header changes, and others would change their header and
wouldnt go to the Proxy.
But, how can i do that ?
Any suggestions ?

I have made the next PerTransHandler, but the body doesn't arrive to the uri's.


package MyApache2::HeaderParser;

  use strict;
  use warnings;

  use Apache2::RequestRec ();
  use Apache2::RequestIO ();
  use Apache2::RequestUtil ();
  use Apache2::ServerUtil ();
  use Apache2::ServerRec ();
  use Apache2::Process ();
  use Apache2::Connection ();
  use Apache2::Filter ();
  use APR::Table ();

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

  use constant METHOD        => 'POST';

  sub handler {
        my $r = shift;

        return Apache2::Const::DECLINED unless $r->method eq METHOD;

        #my $content = content($r);

        my $content = $r->content();
        warn("content: $content\n");
        if (0)  {
                my $newuri = '/~jasanchez/test/';
                $r->uri($newuri);
        }


        return Apache2::Const::DECLINED;
        #return Apache2::Const::OK;
}


  use APR::Brigade ();
  use APR::Bucket ();

  use Apache2::Const -compile => qw(MODE_READBYTES);
  use APR::Const    -compile => qw(SUCCESS BLOCK_READ);

  use constant IOBUFSIZE => 8192;

  sub content {
      my $r = shift;

      my $bb = APR::Brigade->new($r->pool, $r->connection->bucket_alloc);

      my $data = '';
      my $seen_eos = 0;
      do {
          $r->input_filters->get_brigade($bb, Apache2::Const::MODE_READBYTES,
                                         APR::Const::BLOCK_READ, IOBUFSIZE);

          for (my $b = $bb->first; $b; $b = $bb->next($b)) {
              if ($b->is_eos) {
                  $seen_eos++;
                  last;
              }

              if ($b->read(my $buf)) {
                  $data .= $buf;
              }

              $b->remove; # optimization to reuse memory
          }
      } while (!$seen_eos);

      $bb->destroy;
      return $data;
  }

  1;

Re: Apache2 filters questions

Posted by Stas Bekman <st...@stason.org>.
Javier Alonso Sánchez wrote:
> Hi all.
> 
> I'm fighting hard with Apache2 filters  + mod_perl, and I have some
> doubts to ask you:
> 
> I want to analyze a POST request to my Apache2 server, analyze it, and
> change the headers to redirect the POST to another uri, depending on
> the content (body of the POST).
> 
> As I need body+headers, I'm using a Input Connection Filter.
> 
> The main problem is how to stop the buckets brigades. It's easy to
> analyze the body, but when I process it, the brigades with headers
> have just passed to the next filter (or the core).
> 
> In order to "stop" the previous brigades, I read all the brigades
> using a loop which knows when the body finishes, reading
> Content-Length header. As I use Connection Filter, and there isn't
> EOS, it is the unique way to know WHEN the filter must stop getting
> brigades .... Are there any way else to know it ??

It's very tricky, as Apache doesn't give you any indication of that event. 
I've solved this in a hacky way. which is explained here:
http://perl.apache.org/docs/2.0/user/handlers/filters.html#Connection_Filters_over_KeepAlive_Connections
and is used in http://search.cpan.org/dist/Apache-Filter-HTTPHeadersFixup/

> Is there another way to modify the header of a request once processed
> the body content?

I think you are much better off not to use a filter here. Use 
Apache::Request which can give the same POST body more than once. So I'd 
write a trans hander, which grabs the body, rewrites the headers based on 
the body and the content handler can still get the body (this is only 
available in libapreq2)


-- 
__________________________________________________________________
Stas Bekman            JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/     mod_perl Guide ---> http://perl.apache.org
mailto:stas@stason.org http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org   http://ticketmaster.com