daisy
daisy

Reputation: 23501

HTTP::Request->parse does not work as expected

I'm trying to parse a HTTP request string with HTTP::Request->parse method,

GET / HTTP/1.1
Host: www.google.com
Referer: www.google.com
Cookies: A=B

(There's another "\r\n" at the end of it, but I can't append it without breaking the syntax highlighter ..)

The above string works when I send it with nc www.google.com 80 < request

Now I try to parse and send it throught LWP:

use warnings;
use strict;
use File::Slurp qw/read_file/;
use HTTP::Request;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new;
my $request = HTTP::Request->parse (read_file ('/run/shm/request'));
print Dumper ($request);

my $r = $ua->request ($request);
if ($r->is_success)
{
    print $r->decoded_content;
}
else
{
    print $r->status_line;
}

And I get:

$VAR1 = bless( {
                 '_headers' => bless( {}, 'HTTP::Headers' ),
                 '_content' => '',
                 '_protocol' => 'HTTP/1.1',
                 '_method' => 'GET',
                 '_uri' => bless( do{\(my $o = '/')}, 'URI::_generic' )
               }, 'HTTP::Request' );
400 URL must be absolute

So the parser is not working, it fails to parse both URI and Headers.

Any ideas?

Upvotes: 1

Views: 992

Answers (1)

Borodin
Borodin

Reputation: 126722

I really don't like File::Slurp. It's mostly a replacement for the idiom

my $contents = do {
  open my $fh, '<', 'myfile' or die $!;
  local $/;
  <$fh>;
};

or, if you pass the filename on the command line

my $contents = do {
  local $/;
  <>;
};

which is far from difficult to use, and makes it clear what is happening.

Here, File::Slurp is the cause of your problems because, when called in list context (and the parameters of a subroutine call apply list context) it returns a list of lines from the file instead of the whole file in a single scalar value.

Because HTTP::Request->parse looks only at the first parameter passed, it sees only the GET line, and produces a request with no headers.

You can fix it by writing

my $request = read_file ('/run/shm/request');
$request = HTTP::Request->parse($request);

or you may prefer

my $request = HTTP::Request->parse(scalar read_file ('/run/shm/request'));

But I would write something like

use autodie;

my $request = do {
  open my $fh, '<', '/run/shm/request';
  local $/;
  my $contents = <$fh>;
  HTTP::Request->parse($contents);
};

 


Update

By the way, a better way of viewing the HTTP message that HTTP::Message at al. has built for you is to use print $message->as_string. Using Data::Dumper shows a lot of irrelevant data and context that is used internally by the object, and there is no way of knowing what parts of it are relevant.

In the case of your program above, a corrected version that uses

print $request->as_string

results in this output

GET / HTTP/1.1
Host: www.google.com
Referer: www.google.com
Cookies: A=B

which is exactly what your input file contains, and is hopefully what you would expect.

Upvotes: 2

Related Questions