Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Does CGI::Application expose a HTTP::Request object?

by isync (Hermit)
on Aug 10, 2010 at 15:33 UTC ( #854073=perlquestion: print w/ replies, xml ) Need Help??
isync has asked for the wisdom of the Perl Monks concerning the following question:

Does it?
What would you suggest is the easiest way to build one from the CGI::Application object?

Why?
Running C::A under CGI::Fast, at least I am unable to get access to a request's Headers and Content... And further, I need to feed a HTTP::Request object, with it's uri(), method(), etc. methods to a module..

Comment on Does CGI::Application expose a HTTP::Request object?
Re: Does CGI::Application expose a HTTP::Request object?
by jaldhar (Vicar) on Aug 10, 2010 at 19:42 UTC

    Untested but I think this should work. Hopefully it atleast gives you something to build on.

    # Add this to your CGI::Application subclass. sub request_from_cgi { my ($self) = @_; my $q = $self->query; my $uri = $q->url; my $method = uc $q->request_method; my $headers = $q->header($self->header_props); # does this cover all cases??? Perhaps it ought to # look at content-type? if ($method eq 'POST') { $content = $q->param('POSTDATA'); } elsif ($method eq 'PUT') { $content = $q->param('PUTDATA'); } else { $content = undef; } return HTTP::Request->new( $method, $uri, $headers, $content, ); }

    --
    જલધર

      I'm going to test this out soon.
      But I can already say that you seem to have solved some of the details I couldn't work out, for example: Where is the $uri value? Where did the $content end up? ..

      Update:
      The value of $header doesn't seem to work for HTTP::Request, it complains with "Bad header argument". Further, it doesn't seem to hold the headers of the request, but of the response CGI::Application is about to deliver, for example in my test it held a "Content-Type: text/html" header.

      Your elaborate attempts to get the $content *of the request* do not seem to work. None of my tests showed the request content getting through. my @params = $q->param should return all parameters but is empty except for those keys passed in $ENV{QUERY_STRING}, no PUTDATA etc. there.
      Is a CGI::Fast + CGI::Application based script aware of request data which is not coming from POST requests - at all?

        The value of $header doesn't seem to work for HTTP::Request, it complains with "Bad header argument". Further, it doesn't seem to hold the headers of the request, but of the response CGI::Application is about to deliver, for example in my test it held a "Content-Type: text/html" header.

        That should have been @{ $self->header_props } because HTTP::Request wants a reference to an array of key-value pairs but in any case it wouldn't work because as you pointed out, it gives outgoing headers not incoming.

        You would have to get the special HTTP envvars out of the environment and make them into headers.

        Your elaborate attempts to get the $content *of the request* do not seem to work. None of my tests showed the request content getting through. my @params = $q->param should return all parameters but is empty except for those keys passed in $ENV{QUERY_STRING}, no PUTDATA etc. there. Is a CGI::Fast + CGI::Application based script aware of request data which is not coming from POST requests - at all?

        As I alluded to in my last reply, I think the problem is POSTDATA and PUTDATA don't get set in some cases. (They are not real params but specially set up by CGI.pm) I think this happens if the MIME type is application/x-www-form-urlencoded or multipart/form-data which is usual for an HTML form. So in this case or if we can't get the them for any other reason, we should read from standard input based on the Content-Length header (which doesn't seem to have CGI.pm method I don't know why.)

        Try this. Again untested I'm afraid.

        sub request_from_cgi { my ($self) = @_; my $q = $self->query; my $uri = $q->url; my $method = uc $q->request_method; # probably not an exhaustive list. my @http_envvars = qw/ DOCUMENT_ROOT HTTP_COOKIE HTTP_REFERER HTTP_USER_AGENT HTTPS PATH QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_PORT REMOTE_USER REQUEST_METHOD REQUEST_URI SCRIPT_FILENAME SCRIPT_NAME SERVER_ADMIN SERVER_NAME SERVER_PORT SERVER_SOFTWARE /; my $headers = {}; foreach my $var (@http_envvars) { if (exists $ENV{$var}) { $headers->{$var} = $ENV{$var}; } } my $content = undef; if ($method eq 'POST') { $content = $q->param('POSTDATA'); } elsif ($method eq 'PUT') { $content = $q->param('PUTDATA'); } if (!defined $content) { my $len = $ENV{CONTENT_LENGTH} || 0; read STDIN, $content, $len; } return HTTP::Request->new( $method, $uri, HTTP::Headers->new($headers), $content, ); }

        --
        જલધર

Re: Does CGI::Application expose a HTTP::Request object?
by Anonymous Monk on Aug 11, 2010 at 11:53 UTC
    Why? Running C::A under CGI::Fast, at least I am unable to get access to a request's Headers and Content... And further, I need to feed a HTTP::Request object, with it's uri(), method(), etc. methods to a module..

    Are you using CGI::Application::FastCGI?

      No. I am using:
      #!/usr/bin/perl use CGI::Fast(); use MyApp; use utf8; # fastcgi usage as told in: http://cgiapp.erlbaum.net/index.cgi?FastCG +I while( my $q = new CGI::Fast ){ $q->header(-charset => 'utf-8'); my $app = new Engine( QUERY => $q ); $app->run(); }

      Back then, when I started using C::A under fcgi, I think I had issues with C::A::FastCGI initializing things which should only run once under fcgi over and over. Which resulted in the above DIY solution and a $self->{initialized} = 1 flag later on on code which should really run only once.
Re: Does CGI::Application expose a HTTP::Request object?
by isync (Hermit) on Aug 11, 2010 at 18:33 UTC
    I've reworked the code again, now making it a bit more clever on the mapping %ENV to HTTP::Headers: (comments welcome)
    # build a HTTP::Request Object from what CGI.pm provides us with # see http://www.perlmonks.org/?node_id=854073 sub request_from_cgi { my $self = shift; my $q = $self->query; my $uri = $q->url; my $method = uc $q->request_method; my %env_to_httpheader = ( # hash of ENV keys which do not follow the HTTP_* nami +ng convention 'HTTP_TE' => 'Accept-Encoding', 'CONTENT_LENGTH' => 'Content-Length', # from here on blurry from memory: (and probably more +to include as well..), now commented out, see why below: # 'CONTENT_ENCODING' => 'Content-Encoding', + # I think there is only Accept-Encoding on requests # 'CONTENT_TYPE' => 'Content-Type', # I th +ink the content-type equivalent on the req side is Accept ); my $headers; foreach my $key (keys %ENV) { if($key =~ /^HTTP_(.*)$/ && !exists($env_to_httpheader +{$key})){ push(@{ $headers }, lc($1)); push(@{ $headers }, $ENV{$key}); }elsif(exists($env_to_httpheader{$key})){ push(@{ $headers }, $env_to_httpheader{$key}); push(@{ $headers }, $ENV{$key}); } } my $content = undef; if($method eq 'POST'){ $content = $q->param('POSTDATA'); }elsif($method eq 'PUT'){ $content = $q->param('PUTDATA'); } if(!defined $content){ my $len = $ENV{CONTENT_LENGTH} || 0; read STDIN, $content, $len; } return HTTP::Request->new( $method,$uri,$headers,$content); }
    Update: The PUTDATA param being available might depend on your version of CGI.pm, or use a recent CGI::Simple, which worked for me.
      After some testing I've found out that basically this works except for reading the $content in on PUT. As it seems there's nothing on STDIN, although the content-length header is correct. And reading the PUTDATA param fails, nothing there.

        After more testing and reading this node the problem seems to be CGI.pm.
        Since finding out, I upgraded my CGI.pm from 3.39 to 3.49, but either CGI.pm's PUTDATA param has gone missing again or soemthing is wrong on my side. Still, binary files end up as garbled mess in the .parmaters and param parts of __QUERY_OBJ -- Can anyone explain this??

        I've given up since, and replaced CGI.pm with CGI::Simple according to the above mentioned node, and - guess what - CGI::Simple properly parses out the $q->param('PUTDATA') parameter from the request and PUT finally works for me!
Re: Does CGI::Application expose a HTTP::Request object?
by isync (Hermit) on Aug 12, 2010 at 21:28 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://854073]
Approved by oko1
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2014-09-02 02:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (18 votes), past polls