1

我有一个存储 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 根本没有使用localcookie 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 并修复它。

4

1 回答 1

2

也许不是使用local您,而是使用 的clonecookie_jar方法LWP::UserAgent

...

sub request {
    my ($self, $new_cookie_jar) = @_;
    my $ua = $self->ua; # cache user agent

    if( defined $new_cookie_jar ){
        # create a new user agent with the new cookie jar
        $ua = $ua->clone;
        $ua->cookie_jar( $new_cookie_jar );
    }

    my $result = $ua->get('http://www.stackoverflow.com');

    # allow returning the newly cloned user agent
    return ( $result, $ua ) if wantarray;
    return $result;
}

如果你不想这样做,你至少应该使用方法而不是操纵对象的内部。

...

sub request {
    my ($self, $new_cookie_jar) = @_;
    my $ua = $self->ua; # cache user agent

    my $old_cookie_jar = $ua->cookie_jar( $new_cookie_jar );

    my $result = $ua->get('http://www.stackoverflow.com');

    # put the old cookie jar back in place
    $ua->cookie_jar( $old_cookie_jar );

    return $result;
}
于 2014-07-28T18:37:21.133 回答