AnyEvent-MPRPC

 view release on metacpan or  search on metacpan

lib/AnyEvent/MPRPC/Client.pm  view on Meta::CPAN

package AnyEvent::MPRPC::Client;
use strict;
use warnings;
use Any::Moose;

use Carp;
use Scalar::Util 'weaken';

use AnyEvent;
use AnyEvent::Socket;
use AnyEvent::Handle;
use AnyEvent::MessagePack;
use AnyEvent::MPRPC::Constant;

has host => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

has port => (
    is       => 'ro',
    isa      => 'Int|Str',
    required => 1,
);

has connect_timeout => (
    is       => 'ro',
    isa      => 'Int|Str',
);

has handler => (
    is  => 'rw',
    isa => 'AnyEvent::Handle',
);

has on_error => (
    is      => 'rw',
    isa     => 'CodeRef',
    lazy    => 1,
    default => sub {
        return sub {
            my ($handle, $fatal, $message) = @_;
            croak sprintf "Client got error: %s", $message;
        };
    },
);

has handler_options => (
    is      => 'ro',
    isa     => 'HashRef',
    default => sub { {} },
);

has _request_pool => (
    is      => 'ro',
    isa     => 'ArrayRef',
    lazy    => 1,
    default => sub { [] },
);

has _next_id => (
    is      => 'ro',
    isa     => 'CodeRef',
    lazy    => 1,
    default => sub {
        my $id = 0;

lib/AnyEvent/MPRPC/Client.pm  view on Meta::CPAN

    isa => 'CodeRef',
);

# depreciated!
has 'on_connect' => (
    is  => 'ro',
    isa => 'CodeRef',
);

no Any::Moose;

sub BUILD {
    my $self = shift;

    my $after_connect = $self->after_connect ? sub { $self->after_connect->($self, @_) } : undef;
    my $guard = tcp_connect $self->host, $self->port, sub {
        my $fh = shift
            or return
                $self->on_error->(
                    undef, 1,
                    "Failed to connect $self->{host}:$self->{port}: $!",
                );
        my($host, $port, $retry) = @_;
        $self->after_connect
            and $self->after_connect->($self, $fh, $host, $port, $retry);

        my $handle = AnyEvent::Handle->new(
            on_error => sub {
                my ($h, $fatal, $msg) = @_;
                $self->on_error->(@_);
                $h->destroy;
            },
            %{ $self->handler_options },
            fh => $fh,
        );

        $handle->unshift_read(msgpack => $self->_handle_response_cb);

        while (my $pooled = shift @{ $self->_request_pool }) {
            $handle->push_write( msgpack => $pooled );
        }

        $self->handler( $handle );
    }, sub {
        my $connect_timeout;

        $self->before_connect
            and $self->before_connect->($self, @_);

        # on_conect is depreciated!
        $self->on_connect
            and $connect_timeout = $self->on_connect->($self, @_);

        # For backward compatibility, if connect_timeout option isn't specifed
        # use return value of on_connect callback as connect timeout seconds.
        $self->connect_timeout
            and $connect_timeout = $self->connect_timeout;

        return $connect_timeout;
    };
    weaken $self;

    $self->_connection_guard($guard);
}

sub call {
    my ($self, $method) = (shift, shift);
    my $param = (@_ == 1 && ref $_[0] eq "ARRAY") ? $_[0] : [@_];

    my $msgid = $self->_next_id->();

    my $request = [
        MP_TYPE_REQUEST,
        int($msgid), # should be IV
        $method,
        $param,
    ];

    if ($self->handler) {
        $self->handler->push_write( msgpack => $request );
    }
    else {
        push @{ $self->_request_pool }, $request;
    }

    # $msgid is stringified, but $request->{MP_RES_MSGID] is still IV
    $self->_callbacks->{ $msgid } = AnyEvent->condvar;
}

sub _handle_response_cb {
    my $self = shift;

    weaken $self;

    return sub {
        $self || return;

        my ($handle, $res) = @_;

        my $d = delete $self->_callbacks->{ $res->[MP_RES_MSGID] };

        if (my $error = $res->[MP_RES_ERROR]) {
            if ($d) {
                $d->croak($error);
            } else {
                Carp::croak($error);
            }
        }

        $handle->unshift_read(msgpack => $self->_handle_response_cb);

        if ($d) {
            $d->send($res->[MP_RES_RESULT]);
        } else {
            warn q/Invalid response from server/;
            return;
        }
    };
}

__PACKAGE__->meta->make_immutable;

__END__

=head1 NAME

AnyEvent::MPRPC::Client - Simple TCP-based MessagePack RPC client

=head1 SYNOPSIS

    use AnyEvent::MPRPC::Client;

    my $client = AnyEvent::MPRPC::Client->new(
        host => '127.0.0.1',
        port => 4423,
    );

    # blocking interface
    my $res = $client->call( echo => 'foo bar' )->recv; # => 'foo bar';

    # non-blocking interface
    $client->call( echo => 'foo bar' )->cb(sub {
        my $res = $_[0]->recv;  # => 'foo bar';
    });

=head1 DESCRIPTION

This module is client part of L<AnyEvent::MPRPC>.

=head2 AnyEvent condvars

The main thing you have to remember is that all the data retrieval methods
return an AnyEvent condvar, C<$cv>.  If you want the actual data from the



( run in 1.403 second using v1.01-cache-2.11-cpan-39bf76dae61 )