Loading ...
Sorry, an error occurred while loading the content.

Re: [QUESTION] Relating a request to a response

Expand Messages
  • Stas Bekman
    ... Yes, sorry about that. ... Yes, at some point. We need help with getting the 2.0 docs done.
    Message 1 of 12 , Oct 3, 2003
    • 0 Attachment
      Pringle, Chris (HP-PSG) wrote:
      > Notes problem solved...I should have been using the "notes" object not
      > the "note" object. Found it in the mod_perl 1.0 documentation.

      Yes, sorry about that.

      > Will the mod_perl 2.0 documentation include all the mod_perl 1.0 API at
      > some point? At the moment, the API for 2.0 seems to be just changes from
      > 1.0.

      Yes, at some point. We need help with getting the 2.0 docs done.

      __________________________________________________________________
      Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
      http://stason.org/ mod_perl Guide ---> http://perl.apache.org
      mailto:stas@... http://use.perl.org http://apacheweek.com
      http://modperlbook.org http://apache.org http://ticketmaster.com
    • Stas Bekman
      ... Actually it was a much more complicated task than I suggested. This is due to the assumptions taken by the headers_in core http filter which populates
      Message 2 of 12 , Oct 3, 2003
      • 0 Attachment
        Pringle, Chris (HP-PSG) wrote:
        >>That should be pretty trivial to accomplish.
        >>
        >>To answer your question of passing data: use notes:
        >>
        >> $filter->r->note->set(transform => 1);
        >>
        >>if you work with the connection level filter you will need to use:
        >>
        >> $filter->c->note->set(transform => 1);
        >>
        >>though remember that this note will be seen through the
        >>connection, i.e. for
        >>all requests coming over the same connection.
        >>
        >>However I believe there is a much simpler solution:
        >>
        >>add a connection level filter similar to this one:
        >>http://perl.apache.org/docs/2.0/user/handlers/filters.html#Con
        >>nection_Input_Filters
        >>rewrite the request (URI) line to strip your magic argument and add a
        >>new
        >>header, like:
        >>X-Chris-Transform: On
        >>after the request line (well may be you need to do it after the Host:
        >>header,
        >>I'm not sure whether the server expects the Host: header second).
        >>
        >>Now your request level filter can lookup this header:
        >>
        >>$filter->r->headers_in->get('X-Chris-Transform');
        >>
        >>as simple as that.
        >
        >
        > Thanks for this.
        >
        > I get errors when I try and use notes. Exact error is:
        > Can't locate object method "note" via package "Apache::Connection"....
        >
        > I have got a line reading "use Apache::Connection();" at the top of my
        > code :)
        >
        >
        > I prefer the second method, however I'm having some problems with it. If
        > I add the header in the request filter, it doesn't appear to be set when
        > I do
        > $filter->r->headers_in->get("X-iProxy");
        >
        > The returned result is empty.
        >
        > If I telnet into the proxy and type the request manually, with the
        > header there, it works fine.
        >
        > It's definitly adding the header in the request filter, but I can't
        > retrieve it through the headers_in function. It only works when the
        > client sends the "X-iProxy" header, not when it's added via the filter.
        > Any ideas?

        Actually it was a much more complicated task than I suggested. This is due to
        the assumptions taken by the headers_in core http filter which populates
        $r->headers_in. It wan't each header to reside in its own bucket. Moreover, it
        has to be in its own bucket brigade. So forget the streaming filter API and
        welcome bucket brigades. The implementation is still pretty trivial, but
        requires some tricky queueing and unqueueing of the headers, because each
        filter invocation needs to send exactly one header.

        I suppose the headers_in core http filter does that in order to get a better
        performance. so it always grabs just the first bucket and ignores the rest. I
        should really look at its source code.

        The following is a much more generic example than you need, so you will be
        able to simplify it a great deal. I'll commit that soon as a new test. I see
        some problem which I need to debug more, but it won't affect your task.

        If you fail to comprehend my notes below and convert that example into what
        you want, I'll help you to get your particular task done, which should be
        pretty easy once you understand how the thing work ;)

        In fact I'll commit that test, so you can see it in action, including the
        client side. Update your modperl-2.0 cvs, run

        t/TEST -conf

        to update the config

        and now run:

        /TEST -v -trace=debug filter/in_bbs_inject_header

        and you will see a trace of everything that's happening inside the filter in
        error_log.

        When I'll resolve the problem I see, I'll commit a fix. But the test passes
        regardless that problem.

        Here is the code. It's pretty small if you remove all the comments.

        package TestFilter::in_bbs_inject_header;

        # this filter demonstrates two things:
        # 1. how to write a filter that will work only on HTTP headers
        # 2. how to inject extra HTTP headers
        #
        # the first task is simple -- as soon as a bucket which matches
        # /^[\r\n]+$/ is read we can store that event in the filter context and
        # simply 'return Apache::DECLINED on the future invocation, so not to
        # slow things.
        #
        #
        # the second task is a bit trickier, as the headers_in core httpd
        # filter is picky and it wants each header to arrive in a separate
        # bucket, and moreover this bucket needs to be in its own brigade.
        # so this test arranges for this to happen.
        #
        # the test shows how to push headers at the end of all headers
        # and in the middle, whichever way you prefer.

        use strict;
        use warnings;# FATAL => 'all';

        use base qw(Apache::Filter);

        use Apache::RequestRec ();
        use Apache::RequestIO ();
        use APR::Brigade ();
        use APR::Bucket ();

        use Apache::Test;
        use Apache::TestUtil;
        use Apache::TestTrace;

        use Apache::Const -compile => qw(OK DECLINED);
        use APR::Const -compile => ':common';

        my $header1_key = 'X-My-Protocol';
        my $header1_val = 'POST-IT';

        my %headers = (
        'X-Extra-Header2' => 'Value 2',
        'X-Extra-Header3' => 'Value 3',
        );

        # returns 1 if a bucket with a header was inserted to the $bb's tail,
        # otherwise returns 0 (i.e. if there are no buckets to insert)
        sub inject_header_bucket {
        my ($bb, $ctx) = @_;

        return 0 unless @{ $ctx->{buckets} };

        my $bucket = shift @{ $ctx->{buckets} };
        $bb->insert_tail($bucket);

        if (1) {
        # extra debug, wasting cycles
        my $data;
        $bucket->read($data);
        debug "injected header: [$data]";
        }
        else {
        debug "injected header";
        }

        # next filter invocations will bring the request body if any
        if ($ctx->{seen_body_separator} && !@{ $ctx->{buckets} }) {
        $ctx->{done_with_headers} = 1;
        $ctx->{seen_body_separator} = 0;
        }

        return 1;
        }

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

        debug join '', "-" x 20 , " filter called ", "-" x 20;

        use Data::Dumper;
        warn Dumper $filter->ctx;

        my $ctx;
        unless ($ctx = $filter->ctx) {
        debug "filter context init";
        $ctx = {
        buckets => [],
        done_with_headers => 0,
        seen_body_separator => 0,
        };
        # since we are going to manipulate the reference stored in
        # ctx, it's enough to store it only once, we will get the same
        # reference in the following invocations of that filter
        $filter->ctx($ctx);
        }

        # handling the HTTP request body
        if ($ctx->{done_with_headers}) {
        # XXX: when the bug in httpd filter will be fixed all the
        # code in this branch will be replaced with $filter->remove;
        # at the moment (2.0.48) it doesn't work
        # so meanwhile tell the mod_perl filter core to pass-through
        # the brigade unmodified
        debug "passing the body through unmodified";
        #return Apache::DECLINED;
        my $rv = $filter->next->get_brigade($bb, $mode, $block, $readbytes);
        return $rv unless $rv == APR::SUCCESS;
        return Apache::OK;
        }

        # any custom HTTP header buckets to inject?
        #return Apache::OK if inject_header_bucket($bb, $ctx);

        if (inject_header_bucket($bb, $ctx)) {
        use Data::Dumper;
        warn Dumper $ctx;
        #$filter->ctx($ctx);
        return Apache::OK;
        }

        # normal HTTP headers processing
        my $c = $filter->c;
        my $ctx_bb = APR::Brigade->new($c->pool, $c->bucket_alloc);
        my $rv = $filter->next->get_brigade($ctx_bb, $mode, $block, $readbytes);
        return $rv unless $rv == APR::SUCCESS;

        while (!$ctx_bb->empty) {
        my $data;
        my $bucket = $ctx_bb->first;

        $bucket->remove;

        if ($bucket->is_eos) {
        debug "EOS!!!";
        $bb->insert_tail($bucket);
        last;
        }

        my $status = $bucket->read($data);
        debug "filter read:\n[$data]";

        if ($status != APR::SUCCESS) {
        return $status;
        }

        if ($data and $data =~ /^POST/) {
        # demonstrate how to add a header while processing other headers
        my $header = "$header1_key: $header1_val\n";
        push @{ $ctx->{buckets} }, APR::Bucket->new($header);
        debug "queued header [$header]";
        }
        elsif ($data =~ /^[\r\n]+$/) {
        # normally the body will start coming in the next call to
        # get_brigade, so if your filter only wants to work with
        # the headers, it can decline all other invocations if that
        # flag is set. However since in this test we need to send
        # a few extra bucket brigades, we will turn another flag
        # 'done_with_headers' when 'seen_body_separator' is on and
        # all headers were sent out
        debug "END of original HTTP Headers";
        $ctx->{seen_body_separator}++;

        # we hit the headers and body separator, which is a good
        # time to add extra headers:
        for my $key (keys %headers) {
        my $header = "$key: $headers{$key}\n";
        push @{ $ctx->{buckets} }, APR::Bucket->new($header);
        debug "queued header [$header]";
        }

        # but at the same time we must ensure that the
        # the separator header will be sent as a last header
        # so we send one newly added header and push the separator
        # to the end of the queue
        push @{ $ctx->{buckets} }, $bucket;
        debug "queued header [$data]";
        inject_header_bucket($bb, $ctx);
        next; # inject_header_bucket already called insert_tail
        # notice that if we didn't inject any headers, this will
        # still work ok, as inject_header_bucket will send the
        # separator header which we just pushed to its queue
        }
        else {
        # fall through
        }

        $bb->insert_tail($bucket);
        }



        Apache::OK;
        }

        sub response {
        my $r = shift;

        plan $r, tests => 2 + keys %headers;

        my $data = ModPerl::Test::read_post($r);
        ok t_cmp(8, length($data), "whatever");

        ok t_cmp($header1_val,
        $r->headers_in->get($header1_key),
        "injected header $header1_key");

        for my $key (sort keys %headers) {
        ok t_cmp($headers{$key},
        $r->headers_in->get($key),
        "injected header $key");
        }

        Apache::OK;
        }

        1;
        __END__
        <NoAutoConfig>
        <VirtualHost TestFilter::in_bbs_inject_header>
        # must be preloaded so the FilterConnectionHandler attributes will
        # be set by the time the filter is inserted into the filter chain
        PerlModule TestFilter::in_bbs_inject_header
        #PerlInputFilterHandler ModPerl::TestFilterDebug::snoop_connection
        PerlInputFilterHandler TestFilter::in_bbs_inject_header
        #PerlInputFilterHandler ModPerl::TestFilterDebug::snoop_connection
        <Location /TestFilter__in_bbs_inject_header>
        SetHandler modperl
        PerlResponseHandler TestFilter::in_bbs_inject_header::response
        </Location>

        </VirtualHost>
        </NoAutoConfig>


        __________________________________________________________________
        Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
        http://stason.org/ mod_perl Guide ---> http://perl.apache.org
        mailto:stas@... http://use.perl.org http://apacheweek.com
        http://modperlbook.org http://apache.org http://ticketmaster.com
      • Stas Bekman
        Stas Bekman wrote: [...] ... I ve committed a fix to this problem. the test that I committed earlier demonstrates how to write filters that manipulate only
        Message 3 of 12 , Oct 6, 2003
        • 0 Attachment
          Stas Bekman wrote:
          [...]
          > The following is a much more generic example than you need, so you will
          > be able to simplify it a great deal. I'll commit that soon as a new
          > test. I see some problem which I need to debug more, but it won't affect
          > your task.

          I've committed a fix to this problem. the test that I committed earlier
          demonstrates how to write filters that manipulate only HTTP headers, without
          ever touching or even seeing the request/response body.

          I thought a bit more about your request and came to conclusion that it should
          be trivial to generalize this code and put it on CPAN as
          Apache::FilterHeadersManip, which will be very simple to use for end users.
          You will need to provide a callback function, which will receive a ref to an
          array of headers (input or output), which that callback will manipulate, by
          changing and/or removing the existing headers and/or adding new ones. Once
          this callback returns Apache::FilterHeadersManip will use the correct way to
          flush the headers in that array.

          __________________________________________________________________
          Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
          http://stason.org/ mod_perl Guide ---> http://perl.apache.org
          mailto:stas@... http://use.perl.org http://apacheweek.com
          http://modperlbook.org http://apache.org http://ticketmaster.com
        • Pringle, Chris (HP-PSG)
          Thanks for your help on this issue. I have modified my code so that it uses the same technique as the one you wrote, and it s now working quite well :) I m
          Message 4 of 12 , Oct 7, 2003
          • 0 Attachment
            Thanks for your help on this issue. I have modified my code so that it
            uses the same technique as the one you wrote, and it's now working quite
            well :)

            I'm quite interested in the idea of having a generalized bit of code to
            modify HTTP headers. However, I'm not a particularly experienced Perl
            programmer, and have little knowledge of Mod_Perl's interaction with
            Apache.

            Thanks once again for your help. It would have taken me a very long time
            to overcome this problem without it.

            ---
            Regards,
            Chris Pringle

            UK PSG
            Hewlett-Packard, Bristol
            Tel: +44 117 31 29664


            > -----Original Message-----
            > From: Stas Bekman [mailto:stas@...]
            > Sent: 07 October 2003 02:12
            > Cc: Pringle, Chris (HP-PSG); modperl@...
            > Subject: Re: [QUESTION] Relating a request to a response
            >
            >
            > Stas Bekman wrote:
            > [...]
            > > The following is a much more generic example than you need, so you
            > > will
            > > be able to simplify it a great deal. I'll commit that soon as a new
            > > test. I see some problem which I need to debug more, but it
            > won't affect
            > > your task.
            >
            > I've committed a fix to this problem. the test that I
            > committed earlier
            > demonstrates how to write filters that manipulate only HTTP
            > headers, without
            > ever touching or even seeing the request/response body.
            >
            > I thought a bit more about your request and came to
            > conclusion that it should
            > be trivial to generalize this code and put it on CPAN as
            > Apache::FilterHeadersManip, which will be very simple to use
            > for end users.
            > You will need to provide a callback function, which will
            > receive a ref to an
            > array of headers (input or output), which that callback will
            > manipulate, by
            > changing and/or removing the existing headers and/or adding
            > new ones. Once
            > this callback returns Apache::FilterHeadersManip will use the
            > correct way to
            > flush the headers in that array.
            >
            > __________________________________________________________________
            > Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
            > http://stason.org/ mod_perl Guide ---> http://perl.apache.org
            > mailto:stas@... http://use.perl.org http://apacheweek.com
            > http://modperlbook.org http://apache.org http://ticketmaster.com
            >
            >
          • Stas Bekman
            ... Good to hear that. I might do that somewhere next week and post an announcement here. __________________________________________________________________
            Message 5 of 12 , Oct 7, 2003
            • 0 Attachment
              Pringle, Chris (HP-PSG) wrote:
              > Thanks for your help on this issue. I have modified my code so that it
              > uses the same technique as the one you wrote, and it's now working quite
              > well :)
              >
              > I'm quite interested in the idea of having a generalized bit of code to
              > modify HTTP headers. However, I'm not a particularly experienced Perl
              > programmer, and have little knowledge of Mod_Perl's interaction with
              > Apache.
              >
              > Thanks once again for your help. It would have taken me a very long time
              > to overcome this problem without it.

              Good to hear that. I might do that somewhere next week and post an
              announcement here.

              __________________________________________________________________
              Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
              http://stason.org/ mod_perl Guide ---> http://perl.apache.org
              mailto:stas@... http://use.perl.org http://apacheweek.com
              http://modperlbook.org http://apache.org http://ticketmaster.com
            • Pringle, Chris (HP-PSG)
              I would be quite interested in testing it once it s complete. ... Regards, Chris Pringle UK PSG Hewlett-Packard, Bristol Tel: +44 117 31 29664 Mob: +44 7793
              Message 6 of 12 , Oct 8, 2003
              • 0 Attachment
                I would be quite interested in testing it once it's complete.

                ---
                Regards,
                Chris Pringle

                UK PSG
                Hewlett-Packard, Bristol
                Tel: +44 117 31 29664
                Mob: +44 7793 681024


                > -----Original Message-----
                > From: Stas Bekman [mailto:stas@...]
                > Sent: 07 October 2003 17:27
                > To: Pringle, Chris (HP-PSG)
                > Cc: modperl@...
                > Subject: Re: [QUESTION] Relating a request to a response
                >
                >
                > Pringle, Chris (HP-PSG) wrote:
                > > Thanks for your help on this issue. I have modified my code
                > so that it
                > > uses the same technique as the one you wrote, and it's now working
                > > quite well :)
                > >
                > > I'm quite interested in the idea of having a generalized
                > bit of code
                > > to modify HTTP headers. However, I'm not a particularly experienced
                > > Perl programmer, and have little knowledge of Mod_Perl's
                > interaction
                > > with Apache.
                > >
                > > Thanks once again for your help. It would have taken me a very long
                > > time to overcome this problem without it.
                >
                > Good to hear that. I might do that somewhere next week and post an
                > announcement here.
                >
                > __________________________________________________________________
                > Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
                > http://stason.org/ mod_perl Guide ---> http://perl.apache.org
                > mailto:stas@... http://use.perl.org http://apacheweek.com
                > http://modperlbook.org http://apache.org http://ticketmaster.com
                >
                >
              Your message has been successfully submitted and would be delivered to recipients shortly.