使用Mojo::UserAgent解析字符串重建请求事务失败的问题排查
问题与解决方案
问题背景
我的目标是:
- 使用Mojo::UserAgent手动创建有效的请求事务
- 事务验证通过后,使用
$tx->req->to_string将请求转存至文本文件 - 通过解析文件内容重建新事务以实现复用
复用程序代码
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; # 输出看起来是有效的Mojo::Message::Request对象 $tx = $ua->start($tx); # 执行失败,报错: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;
报错信息
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.
简化复现代码
use Mojo::UserAgent; my $ua = Mojo::UserAgent->new; my $tx = Mojo::Transaction::HTTP->new; # 事务定义代码 $tx = $ua->start($tx); print $tx->res->body; # 执行成功! my $txt = $tx->req->to_string; $tx = Mojo::Transaction::HTTP->new; $tx->req->parse($txt); $tx = $ua->start($tx); # 执行失败! print $tx->res->body;
问题原因
$tx->req->to_string生成的是HTTP请求报文的原始内容,这部分内容里不包含请求的目标主机和完整URL——因为HTTP报文本身只包含路径、头部和正文,目标服务器地址是在建立TCP连接时单独指定的,不属于报文内容。
当你通过$tx->req->parse($txt)重建请求时,事务的req对象缺少了目标URL的主机信息,Mojo::UserAgent不知道该向哪个服务器发送请求,因此触发了报错。
解决方法
核心是在保存请求时,同时记录请求的完整URL,重建时补全这部分信息。
1. 修改保存逻辑(同时保存URL和请求报文)
# 原成功请求后,同时保存URL和请求字符串 my $successful_tx = $ua->start($tx); my $req_string = $successful_tx->req->to_string; my $target_url = $successful_tx->req->url->to_string; # 将URL和请求报文用分隔符区分后写入文件 path('saved_request.txt')->spurt("$target_url\n---\n$req_string");
2. 修改重建逻辑(先补全URL再解析报文)
my $content = path($ARGV[0])->slurp; # 拆分保存的URL和请求报文 my ($target_url, $req_string) = split /\n---\n/, $content, 2; my $ua = Mojo::UserAgent->new; my $tx = Mojo::Transaction::HTTP->new; # 先设置目标URL,再解析请求报文 $tx->req->url->parse($target_url); $tx->req->parse($req_string); # 发起请求 $tx = $ua->start($tx); print dumper $tx->res->body;
如果已经明确知道目标URL,也可以直接在重建时手动设置,不用额外保存:
my $req_string = path($ARGV[0])->slurp; my $ua = Mojo::UserAgent->new; my $tx = Mojo::Transaction::HTTP->new; # 直接设置目标URL $tx->req->url->parse('https://your-target-server.com/path'); $tx->req->parse($req_string); $tx = $ua->start($tx); print dumper $tx->res->body;
内容的提问来源于stack exchange,提问作者simone
相关产品推荐
相关产品推荐

