我有一个存储 LWP::UserAgent 的对象。我想使用不同的 cookie jar 来与那个 UA 进行不同的调用,所以我决定在cookie_jar
local
打电话的时候进行。
以下代码显示了我在没有调试内容的情况下所做的事情(用于阅读,而不是运行)。下面是另一个有大量调试输出的版本。
package Foo;
use strictures;
use Moo;
use LWP::UserAgent;
has ua => (
is => 'ro',
default => sub { my $ua = LWP::UserAgent->new; $ua->cookie_jar( {} ); return $ua; },
);
sub request {
my ($self, $cookie_jar) = @_;
local $self->{ua}->{cookie_jar} = $cookie_jar;
$self->ua->get('http://www.stackoverflow.com');
}
package main;
my $foo = Foo->new;
my $new_jar = HTTP::Cookies->new;
$foo->request( $new_jar );
所以基本上我决定在本地覆盖cookie jar。不幸的是,当我们调用get
它时,它仍然会使用最初位于 UA 对象内的 cookie jar。
package Foo;
use strictures;
use Moo;
use LWP::UserAgent;
use HTTP::Cookies;
use Data::Printer;
use feature 'say';
has ua => (
is => 'ro',
default => sub { my $ua = LWP::UserAgent->new; $ua->cookie_jar( {} ); return $ua; },
);
sub request {
my ($self, $cookie_jar) = @_;
say "before local " . $self->{ua}->{cookie_jar};
local $self->{ua}->{cookie_jar} = $cookie_jar;
$self->ua->get('http://www.stackoverflow.com');
print "local jar " . p $self->{ua}->{cookie_jar};
say "after local " . $self->{ua}->{cookie_jar};
}
package main;
use Data::Printer;
use HTTP::Cookies;
my $foo = Foo->new;
say "before outside of local " . $foo->{ua}->{cookie_jar};
my $new_jar = HTTP::Cookies->new;
say "before outside of local " . $new_jar;
$foo->request( $new_jar );
say "after outside of local " . $foo->{ua}->{cookie_jar};
print "global jar " . p $foo->ua->cookie_jar;
__END__
before outside of local HTTP::Cookies=HASH(0x30e1848)
before outside of local HTTP::Cookies=HASH(0x30e3b20)
before local HTTP::Cookies=HASH(0x30e1848)
local jar HTTP::Cookies {
public methods (13) : add_cookie_header, as_string, clear, clear_temporary_cookies, DESTROY, extract_cookies, load, new, revert, save, scan, set_cookie, set_cookie_ok
private methods (3) : _host, _normalize_path, _url_path
internals: {
COOKIES {}
}
}after local HTTP::Cookies=HASH(0x30e3b20)
after outside of local HTTP::Cookies=HASH(0x30e1848)
global jar HTTP::Cookies {
public methods (13) : add_cookie_header, as_string, clear, clear_temporary_cookies, DESTROY, extract_cookies, load, new, revert, save, scan, set_cookie, set_cookie_ok
private methods (3) : _host, _normalize_path, _url_path
internals: {
COOKIES {
stackoverflow.com {
/ {
prov [
[0] 0,
[1] "185e95c6-a7f4-419a-8802-42394776ef63",
[2] undef,
[3] 1,
[4] undef,
[5] 2682374400,
[6] undef,
[7] {
HttpOnly undef
}
]
}
}
}
}
}
如您所见,HTTP::Cookies 对象已正确本地化和替换。地址看起来完全正确。
但输出p
讲述了一个不同的故事。LWP::UA 根本没有使用local
cookie jar。那仍然是一个新鲜的,空的。
我怎样才能让它使用那个local
呢?
我尝试过使用 Moo、Moose 和经典bless
对象。都表现出这种行为。
编辑:既然这出现在评论中,让我提供更多背景信息,为什么我需要这样做。这将是一个有点咆哮。
TLDR:为什么我不想要替代解决方案但理解并解决问题
我正在构建一个基于 Dancer2 的 web 应用程序,它将与 Plack 和多个工作人员一起运行(Twiggy::Prefork - 多个分叉中的多个线程)。它将允许用户使用第三方公司的服务。那家公司提供 SOAP 网络服务。将我的应用程序视为此服务的自定义前端。网络服务上有一个“登录用户”的调用。它为该特定用户返回一个 cookie (sessionid),我们需要在每次连续调用时传递该 cookie。
为了做 SOAP 的东西,我使用 XML::Compile::WSDL11。编译这个东西非常昂贵,所以我不想在每次处理路由时都这样做。那将是低效的。因此,SOAP 客户端将在应用程序启动时从 WSDL 文件编译。然后它将由所有工人共享。
如果客户端对象是共享的,那么内部的用户代理也是共享的。饼干罐也是如此。这意味着如果同时有两个请求,则 sessionid 可能会混淆。该应用程序最终可能会向用户发送错误的内容。
这就是我决定本地化 cookie jar 的原因。如果它是请求的本地唯一请求,它将永远无法干扰并行发生的另一个工作人员的请求。只是为每个请求制作一个新的 cookie jar 不会削减它。它们仍然会被共享,甚至可能会丢失,因为在最坏的情况下它们会相互覆盖。
另一种方法是实现锁定机制,但这完全超出了拥有多个工作人员的目的。
我看到的唯一其他解决方案是一起使用另一个 SOAP 客户端。有 SOAP::WSDL,它不能在较新的 Perls 上运行。根据 CPAN 测试人员的说法,它在 5.18 上中断,我已经验证了这一点。它会更有效,因为它像代码生成器一样工作,并且预先创建比每次都编译 WSDL 文件更便宜的类。不过既然坏了,那就没办法了。
SOAP::Lite 将编译 WSDL,而且效果很差。如果我认为可以避免的话,任何人都不应该在生产中使用它。我看到的唯一替代方法是在不使用 WSDL 文件的情况下实现调用,并直接使用 XML 解析器解析结果,而忽略模式。但这些都是很大的结果。会很不方便。
我对这个咆哮的结论是,我真的很想了解为什么 Perl 不想在这种情况下本地化 cookie jar 并修复它。