【问题标题】:Perl: An asynchronous http proxy via mojoliciousPerl:通过 mojolicious 实现的异步 http 代理
【发布时间】:2015-12-17 06:40:12
【问题描述】:

我做了一个简单的http代理,它工作正常,但速度不快,因为在函数handle_request中,我使用了

my $tx = $ua->start( Mojo::Transaction::HTTP->new(req=>$request) );

要执行请求,它是阻塞的。

我尝试使用如下回调:

$ua->start( Mojo::Transaction::HTTP->new(req=>$request) )=>sub{ ... }

让它成为非阻塞的,然后,出错了:

'error' => { 'message' => 'Premature connection close'}

我猜这是因为函数handle_request 立即返回,它不会等待回调完成。如果我使用信号量等待回调,那就意味着它又阻塞了。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Mojo::IOLoop::Server;
use Mojo::UserAgent;
use Mojo::Message::Response;
use Mojo::Message::Request;
use Mojo::Transaction::HTTP;
use Data::Dumper;

binmode STDOUT, ":encoding(UTF-8)";

my %buffer;

Mojo::IOLoop->server( {port => 3128} => sub {
    my ($loop, $stream, $client) = @_;

    $stream->on(
        read => sub {
            my ($stream, $chunk) = @_;

            my $buffer = $buffer{$client}{read_buffer} .= $chunk;

            if ($buffer =~ /^GET\s+|POST\s+|HEAD\s+(.*)\r\n\r\n$/i) {
                $buffer{$client}{read_buffer} = '';
                &handle_request($client,$stream,$buffer);
            }

            elsif ($buffer =~ /^CONNECT\s+(.*)\r\n\r\n$/i) {
                $buffer{$client}{read_buffer} = '';
                &handle_connect($stream,$buffer);
            }

            elsif($buffer{$client}{connection})
            {
                $buffer{$client}{read_buffer} = '';
                Mojo::IOLoop->stream($buffer{$client}{connection})->write($chunk);
            }

            if(length($buffer)>= 20 *1024 * 1024) {
                delete $buffer{$client};
                Mojo::IOLoop->remove($client);
                return;
            }
        });
});

sub handle_request{

    my($client,$stream,$chunk) = @_;

    my $request = Mojo::Message::Request->new;
    $request = $request->parse($chunk);

    my $ua = Mojo::UserAgent->new;
    my $tx = $ua->start( Mojo::Transaction::HTTP->new(req=>$request) );

    $stream->write( $tx->res->to_string );
}

sub handle_connect{
    my ($stream, $chunk) = @_;
    my $request = Mojo::Message::Request->new;
    my $ua = Mojo::UserAgent->new;

    $request = $request->parse($chunk);

    print Dumper($request);
}

Mojo::IOLoop->start;

希望得到一些建议。

【问题讨论】:

    标签: perl asynchronous http-proxy mojolicious


    【解决方案1】:

    你有两个问题:

    1. 当您的代码具有阻塞样式时,您尝试调用$ua->start 的非阻塞变体。函数handle_request 必须有回调作为参数。 如果您有回调链,那么实现它的最佳方法是使用Mojo::IOLoop::Delay
    2. 当您在 subhandle_request 中以非阻塞方式创建变量 $ua 时,您的变量会被垃圾收集器破坏,因为首先执行 sub handle_request 的退出并且 $ua 被销毁,因为它是局部变量然后得到答案来自 $ua。所以你得到Premature connection close。您需要将 $ua 的实例保存在其他地方以防止此类错误。

    更新
    write http/https 代理的错误变体只能通过 CONNECT 方法工作,并且存在第一条 http 消息不完整的错误。

    更新
    我添加了另一个 http/https 代理示例,它可以正确读取第一条 http 消息,并且不仅可以通过 CONNECT 方法工作。

    更新
    哦,Mojo的作者写了example的https代理

    【讨论】:

    • 非常感谢。我将 $ua 存储在哈希表中,现在可以了。对于第一个问题,我不知道我应该做什么。 my $ua = Mojo::UserAgent->new; $buffer{$client}{ua} = $ua; my $tx = $buffer{$client}{ua}->start( Mojo::Transaction::HTTP->new(req=>$request) =>sub {my( $ua, $tx ) = @_;$stream->write( $tx->res->to_string );} });
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    相关资源
    最近更新 更多