Re: mp2] [mod_proxy] [filter] mod_proxy is not playing with my request filter.
I'm still having issues attempting to modify the body of an http request - this time using a FilterConnectionHandler. I've had to park this due to deadlines and write it using LWP in a PerlResponseHandler. This is far from ideal as some of the responses are upwards of 5MB, and what I'm trying to do does feel very doable in mod_perl. What I really want is to be able to do is pass the request off to mod_proxy (which I assume is better setup for streaming responses through Apache ... I may be wrong) and then through mod_deflate to compress the results. I've attached the code and configuration if anybody has a second and could shed some light over what I'm doing wrong. I'm basically trying to convert a GET request to 'http://hostname/echo' into a POST request and attach a payload, however as it hangs mod_proxy becomes largely academic. Again any help would be much appreciated. Thanks, James. PerlModule Example::Echo PerlModule Example::ConnectionFilter PerlInputFilterHandler Example::ConnectionFilter::forward_get_as_post Location /echo SetHandler modperl PerlResponseHandler Example::Echo /Location package Example::Echo; use strict; use warnings; use Apache2::Const -compile = qw(OK); use Apache2::RequestIO; use Apache2::RequestRec; sub handler { my $r = shift; $r-content_type('text/plain'); $r-read(my $buffer, 1024); $r-print(received post data: '.$buffer.'); return Apache2::Const::OK; } 1; package Example::ConnectionFilter; use strict; use warnings; use base qw/Apache2::Filter/; use Apache2::Connection; use Apache2::Const -compile = qw(OK DECLINED); use Apache2::FilterRec; use Apache2::Log; use Apache2::RequestRec; use Apache2::RequestIO; use APR::Const -compile = ':common'; use APR::Brigade; use APR::Bucket; use APR::BucketType; use APR::Error; sub forward_get_as_post :FilterConnectionHandler { my ($f, $bb, $mode, $block, $readbytes) = @_; my $ctx = $f-ctx || { 'state' = 'waiting_for_request_line' }; warn state = .$ctx-{'state'}.\r\n; # check whether we need to process this request. return Apache2::Const::DECLINED if ($ctx-{'state'} eq 'ignore'); # read into a tmp brigade. my $connection = $f-c; my $tmp_bb = APR::Brigade-new($connection-pool, $connection-bucket_alloc); my $rv = $f-next-get_brigade($tmp_bb, $mode, $block, $readbytes); return $rv unless $rv == APR::Const::SUCCESS; while (!$tmp_bb-is_empty) { # pop buckets from this brigade. my $bucket = $tmp_bb-first; $bucket-remove(); if ($ctx-{'state'} eq 'waiting_for_request_line') { # assumes request line is first bucket. $bucket-read(my $request_line); my ($method, $uri, $version) = ($request_line =~ m|^(.*?) (.*?) HTTP/(.*?)\r\n$|); if (defined ($method) and $method eq GET and $uri =~ m|^/echo|) { my $new_uri = 'POST '.$uri.' HTTP/'.$version.\r\n; my $new_uri_bucket = APR::Bucket-new($connection-bucket_alloc, $new_uri); $bb-insert_tail($new_uri_bucket); my $bucket2 = APR::Bucket-new($connection-bucket_alloc, Content-Type: application/x-www-form-urlencoded\r\n); $bb-insert_tail($bucket2); my $bucket3 = APR::Bucket-new($connection-bucket_alloc, Content-Length: 9\r\n); $bb-insert_tail($bucket3); $ctx-{'state'} = 'waiting_for_end_of_headers'; } else { $bb-insert_tail($bucket); $ctx-{'state'} = 'ignore'; } } elsif ($ctx-{'state'} eq 'waiting_for_end_of_headers') { $bucket-read(my $header); warn received header ... .$header.\r\n; if ($header =~ m|^\r\n$|) { warn detected end_of_headers\r\n; my $post_data = get_post_data(); ### as soon as I add 'data=test' to this bucket the request appears to hang. my $end_of_headers_bucket = APR::Bucket-new($connection-bucket_alloc, \r\ndata=test); $bb-insert_tail($end_of_headers_bucket); $ctx-{'state'} = 'finished'; } else { $bb-insert_tail($bucket); } } } # set context. $f-ctx($ctx); return Apache2::Const::OK; } 1; On 25 August 2010 09:41, James Lee modperl.n...@gmail.com wrote: Hi Andre, thanks for the response. I don't actually want to use a PerlResponseHandler, I was just using that to make sure my filter did what I wanted it to do. I actually wanted the request filter to add the POST body expecting then that mod_proxy would do the rest. I expected mod_proxy to kick in around PerlTransHandler time but wasn't sure when my request filter got called. Either way it
Re: mp2] [mod_proxy] [filter] mod_proxy is not playing with my request filter.
On Thursday, August 26, 2010 18:28:47 James Lee wrote: A few comments below. The goal of the code is to fill up $bb. Nothing is copied there unless you do. The way you do it is to start with an empty brigade. sub forward_get_as_post :FilterConnectionHandler { my ($f, $bb, $mode, $block, $readbytes) = @_; my $ctx = $f-ctx || { 'state' = 'waiting_for_request_line' }; warn state = .$ctx-{'state'}.\r\n; # check whether we need to process this request. return Apache2::Const::DECLINED if ($ctx-{'state'} eq 'ignore'); # read into a tmp brigade. my $connection = $f-c; my $tmp_bb = APR::Brigade-new($connection-pool, $connection-bucket_alloc); my $rv = $f-next-get_brigade($tmp_bb, $mode, $block, $readbytes); return $rv unless $rv == APR::Const::SUCCESS; while (!$tmp_bb-is_empty) { # pop buckets from this brigade. my $bucket = $tmp_bb-first; $bucket-remove(); if ($ctx-{'state'} eq 'waiting_for_request_line') { # assumes request line is first bucket. $bucket-read(my $request_line); my ($method, $uri, $version) = ($request_line =~ m|^(.*?) (.*?) HTTP/(.*?)\r\n$|); if (defined ($method) and $method eq GET and $uri =~ m|^/echo|) { my $new_uri = 'POST '.$uri.' HTTP/'.$version.\r\n; my $new_uri_bucket = APR::Bucket-new($connection-bucket_alloc, $new_uri); $bb-insert_tail($new_uri_bucket); my $bucket2 = APR::Bucket-new($connection-bucket_alloc, Content-Type: application/x-www-form-urlencoded\r\n); $bb-insert_tail($bucket2); my $bucket3 = APR::Bucket-new($connection-bucket_alloc, Content-Length: 9\r\n); $bb-insert_tail($bucket3); by now you have inserted these lines: POST /echo HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 9 You have read the 1st bucket of the input. That bucket may (or may not) contain the whole request including the empty line that signals end-of- headers. So, you have to check also the rest of the bucket. $ctx-{'state'} = 'waiting_for_end_of_headers'; } else { $bb-insert_tail($bucket); $ctx-{'state'} = 'ignore'; } } elsif ($ctx-{'state'} eq 'waiting_for_end_of_headers') { $bucket-read(my $header); warn received header ... .$header.\r\n; if ($header =~ m|^\r\n$|) { warn detected end_of_headers\r\n; my $post_data = get_post_data(); ### as soon as I add 'data=test' to this bucket the request appears to hang. my $end_of_headers_bucket = APR::Bucket-new($connection-bucket_alloc, \r\ndata=test); $bb-insert_tail($end_of_headers_bucket); by now you have sent POST /echo HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 9 data=test Correct me if I am wrong but don't you have to insert an EOS marker? Haven't tried connection input filter yet. But normally a stream is closed with a $bb-insert_tail(APR::Bucket::eos_create $connection-bucket_alloc); $ctx-{'state'} = 'finished'; } else { $bb-insert_tail($bucket); } } } # set context. $f-ctx($ctx); return Apache2::Const::OK; } If it works with the EOS bucket it will probably work for most of the requests. But RFC2616 doesn't forbid GET requests to have a request body. That means you should read the input until EOS. Don't know if your code does that actually. Otherwise your client may still send the request while your handler is already done with the response. Torsten Förtsch -- Need professional modperl support? Hire me! (http://foertsch.name) Like fantasy? http://kabatinte.net
Re: mp2] [mod_proxy] [filter] mod_proxy is not playing with my request filter.
Hi, thanks for the response. To my knowledge (which is about a weeks worth) you don't need or indeed see an end_of_stream bucket in a connection filter till the connection is closed and then only in the output filter. Conveniently the request headers seem to be passed from the core filter one brigade at a time which makes my code quite easy, no need for buffering buckets between calls. As I said the code works fine until I append the 'data=test' to the end of the headers which should be my request body. It's been driving me crazy all day and I can't understand why its doing what it is doing! It's just frustrating because it sounds like something I should be able to do. James. 2010/8/26 Torsten Förtsch torsten.foert...@gmx.net On Thursday, August 26, 2010 18:28:47 James Lee wrote: A few comments below. The goal of the code is to fill up $bb. Nothing is copied there unless you do. The way you do it is to start with an empty brigade. sub forward_get_as_post :FilterConnectionHandler { my ($f, $bb, $mode, $block, $readbytes) = @_; my $ctx = $f-ctx || { 'state' = 'waiting_for_request_line' }; warn state = .$ctx-{'state'}.\r\n; # check whether we need to process this request. return Apache2::Const::DECLINED if ($ctx-{'state'} eq 'ignore'); # read into a tmp brigade. my $connection = $f-c; my $tmp_bb = APR::Brigade-new($connection-pool, $connection-bucket_alloc); my $rv = $f-next-get_brigade($tmp_bb, $mode, $block, $readbytes); return $rv unless $rv == APR::Const::SUCCESS; while (!$tmp_bb-is_empty) { # pop buckets from this brigade. my $bucket = $tmp_bb-first; $bucket-remove(); if ($ctx-{'state'} eq 'waiting_for_request_line') { # assumes request line is first bucket. $bucket-read(my $request_line); my ($method, $uri, $version) = ($request_line =~ m|^(.*?) (.*?) HTTP/(.*?)\r\n$|); if (defined ($method) and $method eq GET and $uri =~ m|^/echo|) { my $new_uri = 'POST '.$uri.' HTTP/'.$version.\r\n; my $new_uri_bucket = APR::Bucket-new($connection-bucket_alloc, $new_uri); $bb-insert_tail($new_uri_bucket); my $bucket2 = APR::Bucket-new($connection-bucket_alloc, Content-Type: application/x-www-form-urlencoded\r\n); $bb-insert_tail($bucket2); my $bucket3 = APR::Bucket-new($connection-bucket_alloc, Content-Length: 9\r\n); $bb-insert_tail($bucket3); by now you have inserted these lines: POST /echo HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 9 You have read the 1st bucket of the input. That bucket may (or may not) contain the whole request including the empty line that signals end-of- headers. So, you have to check also the rest of the bucket. $ctx-{'state'} = 'waiting_for_end_of_headers'; } else { $bb-insert_tail($bucket); $ctx-{'state'} = 'ignore'; } } elsif ($ctx-{'state'} eq 'waiting_for_end_of_headers') { $bucket-read(my $header); warn received header ... .$header.\r\n; if ($header =~ m|^\r\n$|) { warn detected end_of_headers\r\n; my $post_data = get_post_data(); ### as soon as I add 'data=test' to this bucket the request appears to hang. my $end_of_headers_bucket = APR::Bucket-new($connection-bucket_alloc, \r\ndata=test); $bb-insert_tail($end_of_headers_bucket); by now you have sent POST /echo HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 9 data=test Correct me if I am wrong but don't you have to insert an EOS marker? Haven't tried connection input filter yet. But normally a stream is closed with a $bb-insert_tail(APR::Bucket::eos_create $connection-bucket_alloc); $ctx-{'state'} = 'finished'; } else { $bb-insert_tail($bucket); } } } # set context. $f-ctx($ctx); return Apache2::Const::OK; } If it works with the EOS bucket it will probably work for most of the requests. But RFC2616 doesn't forbid GET requests to have a request body. That means you should read the input until EOS. Don't know if your code does that actually. Otherwise your client may still send the request while your handler is already done with the response. Torsten Förtsch -- Need professional modperl support? Hire me! (http://foertsch.name) Like fantasy? http://kabatinte.net
Looking for an Apache::Sandwich replacement for mod_perl2 / Apache2
Hi, I've been trying to move a site which uses Apache::Sandwich to Apache2/mod_perl2. I'm having trouble replicating the functionality on mod_perl2. Is anyone aware of such a replacement module, or one that could be modified for this purpose? Regards, Rafael Caceres