How do I create a request from a parsed string with Mojo::UserAgent

82 views Asked by At

My goal is to:

  • create a valid transaction by hand with Mojo::UserAgent
  • once this works, dump the request to a text file using $tx->req->to_string
  • then re-use the transaction by creating a new one via parsing the file's contents

The re-use program looks like this:

use Mojo::Util qw/dumper/;
use Mojo::File qw/path/;
use Mojo::UserAgent;
use Mojo::Message::Request;
use Mojo::Transaction::HTTP;

my $req_string = path($ARGV[0])->slurp;

my $ua = Mojo::UserAgent->new;
my $tx = Mojo::Transaction::HTTP->new;

$tx->req->parse($req_string);

print dumper $tx->req; # seems to print a valid Mojo::Message::Request object

$tx = $ua->start($tx);
# this fails with Can't call method "server" on an undefined value at /home/me/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/Mojo/Server/Daemon.pm line 55.

print dumper $tx->res->body;

which fails at line 16 with

Can't call method "server" on an undefined value at /home/me/perl5/perlbrew/perls/perl-5.30.2/lib/site_perl/5.30.2/Mojo/Server/Daemon.pm line 55.

In general, this seems to fail:

use Mojo::UserAgent;
    
my $ua = Mojo::UserAgent->new;
my $tx = Mojo::Transaction::HTTP->new;

# more transaction defining stuff

$tx = $ua->start($tx);
print $tx->res->body; # success!

my $txt = $tx->req->to_string;

$tx = Mojo::Transaction::HTTP->new;
$tx->req->parse($txt);
$tx = $ua->start($tx); # failure!
print $tx->res->body;

What am I doing wrong?

1

There are 1 answers

4
choroba On BEST ANSWER

I have no idea whether this is a bug or not. This seems to fix the problem:

    $tx = 'Mojo::Transaction::HTTP'->new;
    $tx->req->parse($txt);
    $tx->req->url($tx->req->url->to_abs);
    $tx = $ua->start($tx);  # No more failures!

Why does this help?

If you added Carp::Always, you'd see that the error comes from line 317 in Mojo::UserAgent:

  if (!$url->is_abs && (my $server = $self->server)) {
    my $base = $loop == $self->ioloop ? $server->url : $server->nb_url;  # <- LINE 317.
    $url->scheme($base->scheme)->host($base->host)->port($base->port);
  }

How comes the $url is not absolute? Becuase in Mojo::URL, is_abs is defined as

sub is_abs { !!shift->scheme }

And if we inspect the url coming from the request, it indeed doesn't have a scheme:

#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use Mojo::Message::Request;
use Mojo::URL;

my $url1 = 'Mojo::URL'->new('http://example.com');
say $url1->to_string, ' ', $url1->is_abs ? 'abs' : '!abs';  # Is absolute.

my $txt = join "\r\n", 'GET / HTTP/1.1', 'Host: example.com', "", "";
my $url2 = 'Mojo::Message::Request'->new->parse($txt)->url;
say $url2->to_string, ' ', $url2->is_abs ? 'abs' : '!abs';  # Isn't absolute!
print Dumper $url1, $url2;

You can see the difference in the Data::Dumper output:

$VAR1 = bless( {
                 'query' => bless( {
                                     'pairs' => [],
                                     'charset' => 'UTF-8'
                                   }, 'Mojo::Parameters' ),
                 'path' => bless( {
                                    'parts' => [],
                                    'leading_slash' => '',
                                    'charset' => 'UTF-8',
                                    'trailing_slash' => ''
                                  }, 'Mojo::Path' ),
                 'scheme' => 'http',
                 'host' => 'example.com'
               }, 'Mojo::URL' );
$VAR1 = bless( {
                 'path' => bless( {
                                    'charset' => 'UTF-8',
                                    'path' => '/'
                                  }, 'Mojo::Path' ),
                 'query' => bless( {
                                     'charset' => 'UTF-8',
                                     'pairs' => []
                                   }, 'Mojo::Parameters' ),
                 'base' => bless( {
                                    'scheme' => 'http',
                                    'host' => 'example.com'
                                  }, 'Mojo::URL' )
               }, 'Mojo::URL' );

That's also why the hack above fixes the problem: it sets the missing scheme (and host) in the second URL.

Let's now wait for someone more Mojo-savvy to explain all this to us.