Net-Async-Blockchain/lib/Net/Async/Blockchain/Client/RPC.pm
package Net::Async::Blockchain::Client::RPC;
use strict;
use warnings;
our $VERSION = '0.005';
=head1 NAME
Net::Async::Blockchain::Client::RPC - Async RPC Client.
=head1 SYNOPSIS
Objects of this type would not normally be constructed directly.
=head1 DESCRIPTION
Centralize all asynchronous RPC calls.
=over 4
=back
=cut
no indirect;
use Future::AsyncAwait;
use Net::Async::HTTP;
use JSON::MaybeUTF8 qw(encode_json_utf8 decode_json_utf8);
use IO::Async::Notifier;
use parent qw(IO::Async::Notifier);
use constant {
# default value for the Net::Async::HTTP stall_timeout configuration.
DEFAULT_TIMEOUT => 100,
DEFAULT_MAX_CONNECTIONS => 6
};
sub endpoint { shift->{endpoint} }
sub rpc_user { shift->{rpc_user} }
sub rpc_password { shift->{rpc_password} }
sub timeout { shift->{timeout} // DEFAULT_TIMEOUT }
=head2 max_connections
L<https://metacpan.org/pod/Net::Async::HTTP#max_connections_per_host-=%3E-INT>
=over 4
=back
returns the configured max_connections value or DEFAULT_MAX_CONNECTIONS
=cut
sub max_connections { shift->{max_connections} // DEFAULT_MAX_CONNECTIONS }
=head2 http_client
Create an L<Net::Async::HTTP> instance, if it is already defined just return
the object
=over 4
=back
L<Net::Async::HTTP>
=cut
sub http_client {
my ($self) = @_;
return $self->{http_client} //= do {
$self->add_child(
my $http_client = Net::Async::HTTP->new(
decode_content => 1,
stall_timeout => $self->timeout,
timeout => $self->timeout,
max_connections_per_host => $self->max_connections,
));
$http_client;
};
}
=head2 configure
Any additional configuration that is not described on L<IO::Async::Notifier>
must be included and removed here.
=over 4
=item * C<endpoint>
=item * C<timeout> connection timeout (seconds)
=item * C<rpc_user> RPC user. (optional, default: undef)
=item * C<rpc_password> RPC password. (optional, default: undef)
=back
=cut
sub configure {
my ($self, %params) = @_;
for my $k (qw(endpoint rpc_user rpc_password timeout max_connections)) {
$self->{$k} = delete $params{$k} if exists $params{$k};
}
$self->SUPER::configure(%params);
}
=head2 _request
Use any argument as the method parameter for the client RPC call
=over 4
=item * C<method>
=item * C<params> (any parameter required by the RPC call)
=back
L<Future> - node response as decoded json
=cut
async sub _request {
my ($self, $method, @params) = @_;
my $obj = {
id => 1,
method => $method,
params => [@params],
};
# for Geth JSON-RPC spec requires the version field to be exactly "jsonrpc": "2.0"
$obj->{jsonrpc} = $self->jsonrpc if $self->can('jsonrpc');
my @post_params = ($self->endpoint, encode_json_utf8($obj), content_type => 'application/json');
# for ETH based, we don't require user+password. Check to send user+password if exists.
push @post_params, (user => $self->rpc_user) if $self->rpc_user;
push @post_params, (pass => $self->rpc_password) if $self->rpc_password;
my $response = await $self->http_client->POST(@post_params);
return decode_json_utf8($response->decoded_content)->{result};
}
1;