Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,355 @@
package Mojo::IOLoop::Client;
use Mojo::Base 'Mojo::EventEmitter';
use Errno qw(EINPROGRESS);
use IO::Socket::IP;
use IO::Socket::UNIX;
use Mojo::IOLoop;
use Mojo::IOLoop::TLS;
use Scalar::Util qw(weaken);
use Socket qw(IPPROTO_TCP SOCK_STREAM TCP_NODELAY);
# Non-blocking name resolution requires Net::DNS::Native
use constant NNR => $ENV{MOJO_NO_NNR} ? 0 : eval { require Net::DNS::Native; Net::DNS::Native->VERSION('0.15'); 1 };
my $NDN;
# SOCKS support requires IO::Socket::Socks
use constant SOCKS => $ENV{MOJO_NO_SOCKS}
? 0
: eval { require IO::Socket::Socks; IO::Socket::Socks->VERSION('0.64'); 1 };
use constant READ => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ() : 0;
use constant WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
sub DESTROY { shift->_cleanup }
sub can_nnr {NNR}
sub can_socks {SOCKS}
sub connect {
my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
# Timeout
weaken $self;
my $reactor = $self->reactor;
$self->{timer} = $reactor->timer($args->{timeout} || 10, sub { $self->emit(error => 'Connect timeout') });
# Blocking name resolution
$_ && s/[[\]]//g for @$args{qw(address socks_address)};
my $address = $args->{socks_address} || ($args->{address} ||= '127.0.0.1');
return $reactor->next_tick(sub { $self && $self->_connect($args) }) if !NNR || $args->{handle} || $args->{path};
# Non-blocking name resolution
$NDN //= Net::DNS::Native->new(pool => 5, extra_thread => 1);
my $handle = $self->{dns}
= $NDN->getaddrinfo($address, _port($args), {protocol => IPPROTO_TCP, socktype => SOCK_STREAM});
$reactor->io(
$handle => sub {
my $reactor = shift;
$reactor->remove($self->{dns});
my ($err, @res) = $NDN->get_result(delete $self->{dns});
return $self->emit(error => "Can't resolve: $err") if $err;
$args->{addr_info} = \@res;
$self->_connect($args);
}
)->watch($handle, 1, 0);
}
sub _cleanup {
my $self = shift;
$NDN->timedout($self->{dns}) if $NDN && $self->{dns};
return $self unless my $reactor = $self->reactor;
$self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
return $self;
}
sub _connect {
my ($self, $args) = @_;
my $path = $args->{path};
my $handle = $self->{handle} = $args->{handle};
unless ($handle) {
my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
my %options = (Blocking => 0);
# UNIX domain socket
if ($path) { $options{Peer} = $path }
# IP socket
else {
if (my $info = $args->{addr_info}) { $options{PeerAddrInfo} = $info }
else {
$options{PeerAddr} = $args->{socks_address} || $args->{address};
$options{PeerPort} = _port($args);
}
$options{LocalAddr} = $args->{local_address} if $args->{local_address};
}
return $self->emit(error => "Can't connect: $@") unless $self->{handle} = $handle = $class->new(%options);
}
$handle->blocking(0);
$path ? $self->_try_socks($args) : $self->_wait('_ready', $handle, $args);
}
sub _port { $_[0]{socks_port} || $_[0]{port} || ($_[0]{tls} ? 443 : 80) }
sub _ready {
my ($self, $args) = @_;
# Socket changes in between attempts and needs to be re-added for epoll/kqueue
my $handle = $self->{handle};
unless ($handle->connect) {
return $self->emit(error => $!) unless $! == EINPROGRESS;
$self->reactor->remove($handle);
return $self->_wait('_ready', $handle, $args);
}
return $self->emit(error => $! || 'Not connected') unless $handle->connected;
# Disable Nagle's algorithm
setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
$self->_try_socks($args);
}
sub _socks {
my ($self, $args) = @_;
# Connected
my $handle = $self->{handle};
return $self->_try_tls($args) if $handle->ready;
# Switch between reading and writing
my $err = $IO::Socket::Socks::SOCKS_ERROR;
if ($err == READ) { $self->reactor->watch($handle, 1, 0) }
elsif ($err == WRITE) { $self->reactor->watch($handle, 1, 1) }
else { $self->emit(error => $err) }
}
sub _try_socks {
my ($self, $args) = @_;
my $handle = $self->{handle};
return $self->_try_tls($args) unless $args->{socks_address};
return $self->emit(error => 'IO::Socket::Socks 0.64+ required for SOCKS support') unless SOCKS;
my %options = (ConnectAddr => $args->{address}, ConnectPort => $args->{port});
@options{qw(AuthType Username Password)} = ('userpass', @$args{qw(socks_user socks_pass)}) if $args->{socks_user};
my $reactor = $self->reactor;
$reactor->remove($handle);
return $self->emit(error => 'SOCKS upgrade failed') unless IO::Socket::Socks->start_SOCKS($handle, %options);
$self->_wait('_socks', $handle, $args);
}
sub _try_tls {
my ($self, $args) = @_;
my $handle = $self->{handle};
return $self->_cleanup->emit(connect => $handle) unless $args->{tls};
my $reactor = $self->reactor;
$reactor->remove($handle);
# Start TLS handshake
weaken $self;
my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
$tls->on(upgrade => sub { $self->_cleanup->emit(connect => pop) });
$tls->on(error => sub { $self->emit(error => pop) });
$tls->negotiate(%$args);
}
sub _wait {
my ($self, $next, $handle, $args) = @_;
weaken $self;
$self->reactor->io($handle => sub { $self->$next($args) })->watch($handle, 0, 1);
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::Client - Non-blocking TCP/IP and UNIX domain socket client
=head1 SYNOPSIS
use Mojo::IOLoop::Client;
# Create socket connection
my $client = Mojo::IOLoop::Client->new;
$client->on(connect => sub ($client, $handle) {...});
$client->on(error => sub ($client, $err) {...});
$client->connect(address => 'example.com', port => 80);
# Start reactor if necessary
$client->reactor->start unless $client->reactor->is_running;
=head1 DESCRIPTION
L<Mojo::IOLoop::Client> opens TCP/IP and UNIX domain socket connections for L<Mojo::IOLoop>.
=head1 EVENTS
L<Mojo::IOLoop::Client> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones.
=head2 connect
$client->on(connect => sub ($client, $handle) {...});
Emitted once the connection is established.
=head2 error
$client->on(error => sub ($client, $err) {...});
Emitted if an error occurs on the connection, fatal if unhandled.
=head1 ATTRIBUTES
L<Mojo::IOLoop::Client> implements the following attributes.
=head2 reactor
my $reactor = $client->reactor;
$client = $client->reactor(Mojo::Reactor::Poll->new);
Low-level event reactor, defaults to the C<reactor> attribute value of the global L<Mojo::IOLoop> singleton. Note that
this attribute is weakened.
=head1 METHODS
L<Mojo::IOLoop::Client> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones.
=head2 can_nnr
my $bool = Mojo::IOLoop::Client->can_nnr;
True if L<Net::DNS::Native> 0.15+ is installed and non-blocking name resolution support enabled.
=head2 can_socks
my $bool = Mojo::IOLoop::Client->can_socks;
True if L<IO::Socket::SOCKS> 0.64+ is installed and SOCKS5 support enabled.
=head2 connect
$client->connect(address => '127.0.0.1', port => 3000);
$client->connect({address => '127.0.0.1', port => 3000});
Open a socket connection to a remote host. Note that non-blocking name resolution depends on L<Net::DNS::Native>
(0.15+), SOCKS5 support on L<IO::Socket::Socks> (0.64), and TLS support on L<IO::Socket::SSL> (2.009+).
These options are currently available:
=over 2
=item address
address => 'mojolicious.org'
Address or host name of the peer to connect to, defaults to C<127.0.0.1>.
=item handle
handle => $handle
Use an already prepared L<IO::Socket::IP> object.
=item local_address
local_address => '127.0.0.1'
Local address to bind to.
=item path
path => '/tmp/myapp.sock'
Path of UNIX domain socket to connect to.
=item port
port => 80
Port to connect to, defaults to C<80> or C<443> with C<tls> option.
=item socks_address
socks_address => '127.0.0.1'
Address or host name of SOCKS5 proxy server to use for connection.
=item socks_pass
socks_pass => 'secr3t'
Password to use for SOCKS5 authentication.
=item socks_port
socks_port => 9050
Port of SOCKS5 proxy server to use for connection.
=item socks_user
socks_user => 'sri'
Username to use for SOCKS5 authentication.
=item timeout
timeout => 15
Maximum amount of time in seconds establishing connection may take before getting canceled, defaults to C<10>.
=item tls
tls => 1
Enable TLS.
=item tls_ca
tls_ca => '/etc/tls/ca.crt'
Path to TLS certificate authority file.
=item tls_cert
tls_cert => '/etc/tls/client.crt'
Path to the TLS certificate file.
=item tls_key
tls_key => '/etc/tls/client.key'
Path to the TLS key file.
=item tls_protocols
tls_protocols => ['foo', 'bar']
ALPN protocols to negotiate.
=item tls_verify
tls_verify => 0x00
TLS verification mode.
=back
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,204 @@
package Mojo::IOLoop::Delay;
use Mojo::Base 'Mojo::Promise';
sub begin {
my ($self, $offset, $len) = @_;
$self->{pending}++;
my $id = $self->{counter}++;
return sub { $self->_step($id, $offset // 1, $len, @_) };
}
sub pass { $_[0]->begin->(@_) }
sub steps {
my ($self, @steps) = @_;
$self->{steps} = \@steps;
$self->ioloop->next_tick($self->begin);
return $self;
}
sub _step {
my ($self, $id, $offset, $len) = (shift, shift, shift, shift);
$self->{args}[$id] = [@_ ? defined $len ? splice @_, $offset, $len : splice @_, $offset : ()];
return $self if $self->{fail} || --$self->{pending} || $self->{lock};
local $self->{lock} = 1;
my @args = map {@$_} @{delete $self->{args}};
$self->{counter} = 0;
if (my $cb = shift @{$self->{steps}}) {
unless (eval { $self->$cb(@args); 1 }) {
my $err = $@;
@{$self}{qw(fail steps)} = (1, []);
return $self->reject($err);
}
}
($self->{steps} = []) and return $self->resolve(@args) unless $self->{counter};
$self->ioloop->next_tick($self->begin) unless $self->{pending};
return $self;
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::Delay - Promises/A+ and flow-control helpers
=head1 SYNOPSIS
use Mojo::IOLoop::Delay;
# Synchronize multiple non-blocking operations
my $delay = Mojo::IOLoop::Delay->new;
$delay->steps(sub { say 'BOOM!' });
for my $i (1 .. 10) {
my $end = $delay->begin;
Mojo::IOLoop->timer($i => sub {
say 10 - $i;
$end->();
});
}
$delay->wait;
# Sequentialize multiple non-blocking operations
Mojo::IOLoop::Delay->new->steps(
# First step (simple timer)
sub ($delay) {
Mojo::IOLoop->timer(2 => $delay->begin);
say 'Second step in 2 seconds.';
},
# Second step (concurrent timers)
sub ($delay, @args) {
Mojo::IOLoop->timer(1 => $delay->begin);
Mojo::IOLoop->timer(3 => $delay->begin);
say 'Third step in 3 seconds.';
},
# Third step (the end)
sub ($delay, @args) {
say 'And done after 5 seconds total.';
}
)->wait;
=head1 DESCRIPTION
L<Mojo::IOLoop::Delay> adds flow-control helpers to L<Mojo::Promise>, which can help you avoid deep nested closures
that often result from continuation-passing style.
use Mojo::IOLoop;
# These deep nested closures are often referred to as "Callback Hell"
Mojo::IOLoop->timer(3 => sub ($loop) {
say '3 seconds';
Mojo::IOLoop->timer(3 => sub ($loop) {
say '6 seconds';
Mojo::IOLoop->timer(3 => sub ($loop) {
say '9 seconds';
Mojo::IOLoop->stop;
});
});
});
Mojo::IOLoop->start;
The idea behind L<Mojo::IOLoop::Delay> is to turn the nested closures above into a flat series of closures. In the
example below, the call to L</"begin"> creates a code reference that we can pass to L<Mojo::IOLoop/"timer"> as a
callback, and that leads to the next closure in the series when executed.
use Mojo::IOLoop;
# Instead of nested closures we now have a simple chain of steps
my $delay = Mojo::IOLoop->delay(
sub ($delay) { Mojo::IOLoop->timer(3 => $delay->begin) },
sub ($delay) {
say '3 seconds';
Mojo::IOLoop->timer(3 => $delay->begin);
},
sub ($delay) {
say '6 seconds';
Mojo::IOLoop->timer(3 => $delay->begin);
},
sub ($delay) { say '9 seconds' }
);
$delay->wait;
Another positive side effect of this pattern is that we do not need to call L<Mojo::IOLoop/"start"> and
L<Mojo::IOLoop/"stop"> manually, because we know exactly when our chain of L</"steps"> has reached the end. So
L<Mojo::Promise/"wait"> can stop the event loop automatically if it had to be started at all in the first place.
=head1 ATTRIBUTES
L<Mojo::IOLoop::Delay> inherits all attributes from L<Mojo::Promise>.
=head1 METHODS
L<Mojo::IOLoop::Delay> inherits all methods from L<Mojo::Promise> and implements the following new ones.
=head2 begin
my $cb = $delay->begin;
my $cb = $delay->begin($offset);
my $cb = $delay->begin($offset, $len);
Indicate an active event by incrementing the event counter, the returned code reference can be used as a callback, and
needs to be executed when the event has completed to decrement the event counter again. When all code references
generated by this method have been executed and the event counter has reached zero, L</"steps"> will continue.
# Capture all arguments except for the first one (invocant)
my $delay = Mojo::IOLoop->delay(sub ($delay, $err, $stream) { ... });
Mojo::IOLoop->client({port => 3000} => $delay->begin);
$delay->wait;
Arguments passed to the returned code reference are spliced with the given offset and length, defaulting to an offset
of C<1> with no default length. The arguments are then combined in the same order L</"begin"> was called, and passed
together to the next step.
# Capture all arguments
my $delay = Mojo::IOLoop->delay(sub ($delay, $loop, $err, $stream) { ... });
Mojo::IOLoop->client({port => 3000} => $delay->begin(0));
$delay->wait;
# Capture only the second argument
my $delay = Mojo::IOLoop->delay(sub ($delay, $err) { ... });
Mojo::IOLoop->client({port => 3000} => $delay->begin(1, 1));
$delay->wait;
# Capture and combine arguments
my $delay = Mojo::IOLoop->delay(sub ($delay, $three_err, $three_stream, $four_err, $four_stream) { ... });
Mojo::IOLoop->client({port => 3000} => $delay->begin);
Mojo::IOLoop->client({port => 4000} => $delay->begin);
$delay->wait;
=head2 pass
$delay = $delay->pass;
$delay = $delay->pass(@args);
Shortcut for passing values between L</"steps">.
# Longer version
$delay->begin(0)->(@args);
=head2 steps
$delay = $delay->steps(sub {...}, sub {...});
Sequentialize multiple events, every time the event counter reaches zero a callback will run, the first one
automatically runs during the next reactor tick unless it is delayed by incrementing the event counter. This chain will
continue until there are no remaining callbacks, a callback does not increment the event counter or an exception gets
thrown in a callback. Finishing the chain will also result in the promise being fulfilled, or if an exception got
thrown it will be rejected.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,313 @@
package Mojo::IOLoop::Server;
use Mojo::Base 'Mojo::EventEmitter';
use Carp qw(croak);
use IO::Socket::IP;
use IO::Socket::UNIX;
use Mojo::File qw(path);
use Mojo::IOLoop;
use Mojo::IOLoop::TLS;
use Scalar::Util qw(weaken);
use Socket qw(IPPROTO_TCP TCP_NODELAY);
has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
sub DESTROY {
my $self = shift;
$ENV{MOJO_REUSE} =~ s/(?:^|\,)\Q$self->{reuse}\E// if $self->{reuse};
$self->stop if $self->{handle} && $self->reactor;
}
sub generate_port { IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport }
sub handle { shift->{handle} }
sub is_accepting { !!shift->{active} }
sub listen {
my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
# Look for reusable file descriptor
my $path = $args->{path};
my $address = $args->{address} || '0.0.0.0';
my $port = $args->{port};
$ENV{MOJO_REUSE} ||= '';
my $fd = ($path && $ENV{MOJO_REUSE} =~ /(?:^|\,)unix:\Q$path\E:(\d+)/)
|| ($port && $ENV{MOJO_REUSE} =~ /(?:^|\,)\Q$address:$port\E:(\d+)/) ? $1 : undef;
# Allow file descriptor inheritance
local $^F = 1023;
# Reuse file descriptor
my $handle;
my $class = $path ? 'IO::Socket::UNIX' : 'IO::Socket::IP';
if (defined($fd //= $args->{fd})) {
$handle = $class->new_from_fd($fd, 'r') or croak "Can't open file descriptor $fd: $!";
}
else {
my %options = (Listen => $args->{backlog} // SOMAXCONN, Type => SOCK_STREAM);
# UNIX domain socket
my $reuse;
if ($path) {
path($path)->remove if -S $path;
$options{Local} = $path;
$handle = $class->new(%options) or croak "Can't create listen socket: $!";
$reuse = $self->{reuse} = join ':', 'unix', $path, fileno $handle;
}
# IP socket
else {
$options{LocalAddr} = $address;
$options{LocalAddr} =~ y/[]//d;
$options{LocalPort} = $port if $port;
$options{ReuseAddr} = 1;
$options{ReusePort} = $args->{reuse};
$handle = $class->new(%options) or croak "Can't create listen socket: $@";
$fd = fileno $handle;
$reuse = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
}
$ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
}
$handle->blocking(0);
@$self{qw(args handle)} = ($args, $handle);
croak 'IO::Socket::SSL 2.009+ required for TLS support' if !Mojo::IOLoop::TLS->can_tls && $args->{tls};
}
sub port { shift->{handle}->sockport }
sub start {
my $self = shift;
weaken $self;
++$self->{active} and $self->reactor->io($self->{handle} => sub { $self->_accept })->watch($self->{handle}, 1, 0);
}
sub stop { delete($_[0]{active}) and $_[0]->reactor->remove($_[0]{handle}) }
sub _accept {
my $self = shift;
# Greedy accept
my $args = $self->{args};
my $accepted = 0;
while ($self->{active} && !($args->{single_accept} && $accepted++)) {
return unless my $handle = $self->{handle}->accept;
$handle->blocking(0);
# Disable Nagle's algorithm
setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
$self->emit(accept => $handle) and next unless $args->{tls};
# Start TLS handshake
my $tls = Mojo::IOLoop::TLS->new($handle)->reactor($self->reactor);
$tls->on(upgrade => sub { $self->emit(accept => pop) });
$tls->on(error => sub { });
$tls->negotiate(%$args, server => 1);
}
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::Server - Non-blocking TCP and UNIX domain socket server
=head1 SYNOPSIS
use Mojo::IOLoop::Server;
# Create listen socket
my $server = Mojo::IOLoop::Server->new;
$server->on(accept => sub ($server, $handle) {...});
$server->listen(port => 3000);
# Start and stop accepting connections
$server->start;
$server->stop;
# Start reactor if necessary
$server->reactor->start unless $server->reactor->is_running;
=head1 DESCRIPTION
L<Mojo::IOLoop::Server> accepts TCP/IP and UNIX domain socket connections for L<Mojo::IOLoop>.
=head1 EVENTS
L<Mojo::IOLoop::Server> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones.
=head2 accept
$server->on(accept => sub ($server, $handle) {...});
Emitted for each accepted connection.
=head1 ATTRIBUTES
L<Mojo::IOLoop::Server> implements the following attributes.
=head2 reactor
my $reactor = $server->reactor;
$server = $server->reactor(Mojo::Reactor::Poll->new);
Low-level event reactor, defaults to the C<reactor> attribute value of the global L<Mojo::IOLoop> singleton. Note that
this attribute is weakened.
=head1 METHODS
L<Mojo::IOLoop::Server> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones.
=head2 generate_port
my $port = Mojo::IOLoop::Server->generate_port;
Find a free TCP port, primarily used for tests.
=head2 handle
my $handle = $server->handle;
Get handle for server, usually an L<IO::Socket::IP> object.
=head2 is_accepting
my $bool = $server->is_accepting;
Check if connections are currently being accepted.
=head2 listen
$server->listen(port => 3000);
$server->listen({port => 3000});
Create a new listen socket. Note that TLS support depends on L<IO::Socket::SSL> (2.009+).
These options are currently available:
=over 2
=item address
address => '127.0.0.1'
Local address to listen on, defaults to C<0.0.0.0>.
=item backlog
backlog => 128
Maximum backlog size, defaults to C<SOMAXCONN>.
=item fd
fd => 3
File descriptor with an already prepared listen socket.
=item path
path => '/tmp/myapp.sock'
Path for UNIX domain socket to listen on.
=item port
port => 80
Port to listen on, defaults to a random port.
=item reuse
reuse => 1
Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket option.
=item single_accept
single_accept => 1
Only accept one connection at a time.
=item tls
tls => 1
Enable TLS.
=item tls_ca
tls_ca => '/etc/tls/ca.crt'
Path to TLS certificate authority file.
=item tls_cert
tls_cert => '/etc/tls/server.crt'
tls_cert => {'mojolicious.org' => '/etc/tls/mojo.crt'}
Path to the TLS cert file, defaults to a built-in test certificate.
=item tls_ciphers
tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH'
TLS cipher specification string. For more information about the format see
L<https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-STRINGS>.
=item tls_key
tls_key => '/etc/tls/server.key'
tls_key => {'mojolicious.org' => '/etc/tls/mojo.key'}
Path to the TLS key file, defaults to a built-in test key.
=item tls_protocols
tls_protocols => ['foo', 'bar']
ALPN protocols to negotiate.
=item tls_verify
tls_verify => 0x00
TLS verification mode.
=item tls_version
tls_version => 'TLSv1_2'
TLS protocol version.
=back
=head2 port
my $port = $server->port;
Get port this server is listening on.
=head2 start
$server->start;
Start or resume accepting connections.
=head2 stop
$server->stop;
Stop accepting connections.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,327 @@
package Mojo::IOLoop::Stream;
use Mojo::Base 'Mojo::EventEmitter';
use Errno qw(EAGAIN ECONNRESET EINTR EWOULDBLOCK);
use Mojo::IOLoop;
use Mojo::Util;
use Scalar::Util qw(weaken);
has high_water_mark => 1048576;
has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
sub DESTROY { Mojo::Util::_global_destruction() or shift->close }
sub bytes_read { shift->{read} || 0 }
sub bytes_waiting { length(shift->{buffer} // '') }
sub bytes_written { shift->{written} || 0 }
sub can_write { $_[0]{handle} && $_[0]->bytes_waiting < $_[0]->high_water_mark }
sub close {
my $self = shift;
return unless my $reactor = $self->reactor;
return unless my $handle = delete $self->timeout(0)->{handle};
$reactor->remove($handle);
$self->emit('close');
}
sub close_gracefully { $_[0]->is_writing ? $_[0]{graceful}++ : $_[0]->close }
sub handle { shift->{handle} }
sub is_readable {
my $self = shift;
$self->_again;
return $self->{handle} && Mojo::Util::_readable(0, fileno $self->{handle});
}
sub is_writing {
my $self = shift;
return undef unless $self->{handle};
return !!length($self->{buffer}) || $self->has_subscribers('drain');
}
sub new { shift->SUPER::new(handle => shift, timeout => 15) }
sub start {
my $self = shift;
# Resume
return unless $self->{handle};
my $reactor = $self->reactor;
return $reactor->watch($self->{handle}, 1, $self->is_writing) if delete $self->{paused};
weaken $self;
my $cb = sub { pop() ? $self->_write : $self->_read };
$reactor->io($self->timeout($self->{timeout})->{handle} => $cb);
}
sub steal_handle {
my $self = shift;
$self->reactor->remove($self->{handle});
return delete $self->{handle};
}
sub stop { $_[0]->reactor->watch($_[0]{handle}, 0, $_[0]->is_writing) if $_[0]{handle} && !$_[0]{paused}++ }
sub timeout {
my ($self, $timeout) = @_;
return $self->{timeout} unless defined $timeout;
$self->{timeout} = $timeout;
my $reactor = $self->reactor;
if ($self->{timer}) {
if (!$self->{timeout}) { $reactor->remove(delete $self->{timer}) }
else { $reactor->again($self->{timer}, $self->{timeout}) }
}
elsif ($self->{timeout}) {
weaken $self;
$self->{timer}
= $reactor->timer($timeout => sub { $self and delete($self->{timer}) and $self->emit('timeout')->close });
}
return $self;
}
sub write {
my ($self, $chunk, $cb) = @_;
# IO::Socket::SSL will corrupt data with the wrong internal representation
utf8::downgrade $chunk;
$self->{buffer} .= $chunk;
if ($cb) { $self->once(drain => $cb) }
elsif (!length $self->{buffer}) { return $self }
$self->reactor->watch($self->{handle}, !$self->{paused}, 1) if $self->{handle};
return $self;
}
sub _again { $_[0]->reactor->again($_[0]{timer}) if $_[0]{timer} }
sub _read {
my $self = shift;
if (defined(my $read = $self->{handle}->sysread(my $buffer, 131072, 0))) {
$self->{read} += $read;
return $read == 0 ? $self->close : $self->emit(read => $buffer)->_again;
}
# Retry
return undef if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK;
# Closed (maybe real error)
$! == ECONNRESET ? $self->close : $self->emit(error => $!)->close;
}
sub _write {
my $self = shift;
# Handle errors only when reading (to avoid timing problems)
my $handle = $self->{handle};
if (length $self->{buffer}) {
return undef unless defined(my $written = $handle->syswrite($self->{buffer}));
$self->{written} += $written;
$self->emit(write => substr($self->{buffer}, 0, $written, ''))->_again;
}
# Clear the buffer to free the underlying SV* memory
undef $self->{buffer}, $self->emit('drain') unless length $self->{buffer};
return undef if $self->is_writing;
return $self->close if $self->{graceful};
$self->reactor->watch($handle, !$self->{paused}, 0) if $self->{handle};
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::Stream - Non-blocking I/O stream
=head1 SYNOPSIS
use Mojo::IOLoop::Stream;
# Create stream
my $stream = Mojo::IOLoop::Stream->new($handle);
$stream->on(read => sub ($stream, $bytes) {...});
$stream->on(close => sub ($stream) {...});
$stream->on(error => sub ($stream, $err) {...});
# Start and stop watching for new data
$stream->start;
$stream->stop;
# Start reactor if necessary
$stream->reactor->start unless $stream->reactor->is_running;
=head1 DESCRIPTION
L<Mojo::IOLoop::Stream> is a container for I/O streams used by L<Mojo::IOLoop>.
=head1 EVENTS
L<Mojo::IOLoop::Stream> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones.
=head2 close
$stream->on(close => sub ($stream) {...});
Emitted if the stream gets closed.
=head2 drain
$stream->on(drain => sub ($stream) {...});
Emitted once all data has been written.
=head2 error
$stream->on(error => sub ($stream, $err) {...});
Emitted if an error occurs on the stream, fatal if unhandled.
=head2 read
$stream->on(read => sub ($stream, $bytes) {...});
Emitted if new data arrives on the stream.
=head2 timeout
$stream->on(timeout => sub ($stream) {...});
Emitted if the stream has been inactive for too long and will get closed automatically.
=head2 write
$stream->on(write => sub ($stream, $bytes) {...});
Emitted if new data has been written to the stream.
=head1 ATTRIBUTES
L<Mojo::IOLoop::Stream> implements the following attributes.
=head2 high_water_mark
my $size = $msg->high_water_mark;
$msg = $msg->high_water_mark(1024);
Maximum size of L</"write"> buffer in bytes before L</"can_write"> returns false, defaults to C<1048576> (1MiB).
=head2 reactor
my $reactor = $stream->reactor;
$stream = $stream->reactor(Mojo::Reactor::Poll->new);
Low-level event reactor, defaults to the C<reactor> attribute value of the global L<Mojo::IOLoop> singleton. Note that
this attribute is weakened.
=head1 METHODS
L<Mojo::IOLoop::Stream> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones.
=head2 bytes_read
my $num = $stream->bytes_read;
Number of bytes received.
=head2 bytes_waiting
my $num = $stream->bytes_waiting;
Number of bytes that have been enqueued with L</"write"> and are waiting to be written.
=head2 bytes_written
my $num = $stream->bytes_written;
Number of bytes written.
=head2 can_write
my $bool = $stream->can_write;
Returns true if calling L</"write"> is safe.
=head2 close
$stream->close;
Close stream immediately.
=head2 close_gracefully
$stream->close_gracefully;
Close stream gracefully.
=head2 handle
my $handle = $stream->handle;
Get handle for stream, usually an L<IO::Socket::IP> or L<IO::Socket::SSL> object.
=head2 is_readable
my $bool = $stream->is_readable;
Quick non-blocking check if stream is readable, useful for identifying tainted sockets.
=head2 is_writing
my $bool = $stream->is_writing;
Check if stream is writing.
=head2 new
my $stream = Mojo::IOLoop::Stream->new($handle);
Construct a new L<Mojo::IOLoop::Stream> object.
=head2 start
$stream->start;
Start or resume watching for new data on the stream.
=head2 steal_handle
my $handle = $stream->steal_handle;
Steal L</"handle"> and prevent it from getting closed automatically.
=head2 stop
$stream->stop;
Stop watching for new data on the stream.
=head2 timeout
my $timeout = $stream->timeout;
$stream = $stream->timeout(45);
Maximum amount of time in seconds stream can be inactive before getting closed automatically, defaults to C<15>.
Setting the value to C<0> will allow this stream to be inactive indefinitely.
=head2 write
$stream = $stream->write($bytes);
$stream = $stream->write($bytes => sub {...});
Enqueue data to be written to the stream as soon as possible, the optional drain callback will be executed once all
data has been written.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,265 @@
package Mojo::IOLoop::Subprocess;
use Mojo::Base 'Mojo::EventEmitter';
use Config;
use Mojo::IOLoop;
use Mojo::IOLoop::Stream;
use Mojo::JSON;
use Mojo::Promise;
use POSIX ();
has deserialize => sub { \&Mojo::JSON::decode_json };
has ioloop => sub { Mojo::IOLoop->singleton }, weak => 1;
has serialize => sub { \&Mojo::JSON::encode_json };
sub exit_code { shift->{exit_code} }
sub pid { shift->{pid} }
sub run {
my ($self, @args) = @_;
$self->ioloop->next_tick(sub { $self->_start(@args) });
return $self;
}
sub run_p {
my ($self, $child) = @_;
my $p = Mojo::Promise->new;
my $parent = sub {
my ($self, $err) = (shift, shift);
$err ? $p->reject($err) : $p->resolve(@_);
};
$self->ioloop->next_tick(sub { $self->_start($child, $parent) });
return $p;
}
sub _start {
my ($self, $child, $parent) = @_;
# No fork emulation support
return $self->$parent('Subprocesses do not support fork emulation') if $Config{d_pseudofork};
# Pipe for subprocess communication
return $self->$parent("Can't create pipe: $!") unless pipe(my $reader, $self->{writer});
$self->{writer}->autoflush(1);
# Child
return $self->$parent("Can't fork: $!") unless defined(my $pid = $self->{pid} = fork);
unless ($pid) {
eval {
$self->ioloop->reset({freeze => 1});
my $results = eval { [$self->$child] } // [];
print {$self->{writer}} '0-', $self->serialize->([$@, @$results]);
$self->emit('cleanup');
} or warn $@;
POSIX::_exit(0);
}
# Parent
my $me = $$;
close $self->{writer};
my $stream = Mojo::IOLoop::Stream->new($reader)->timeout(0);
$self->emit('spawn')->ioloop->stream($stream);
my $buffer = '';
$stream->on(
read => sub {
$buffer .= pop;
while (1) {
my ($len) = $buffer =~ /^([0-9]+)\-/;
last unless $len and length $buffer >= $len + $+[0];
my $snippet = substr $buffer, 0, $len + $+[0], '';
my $args = $self->deserialize->(substr $snippet, $+[0]);
$self->emit(progress => @$args);
}
}
);
$stream->on(
close => sub {
return unless $$ == $me;
waitpid $pid, 0;
$self->{exit_code} = $? >> 8;
substr $buffer, 0, 2, '';
my $results = eval { $self->deserialize->($buffer) } // [];
$self->$parent(shift(@$results) // $@, @$results);
}
);
}
sub progress {
my ($self, @args) = @_;
my $serialized = $self->serialize->(\@args);
print {$self->{writer}} length($serialized), '-', $serialized;
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::Subprocess - Subprocesses
=head1 SYNOPSIS
use Mojo::IOLoop::Subprocess;
# Operation that would block the event loop for 5 seconds
my $subprocess = Mojo::IOLoop::Subprocess->new;
$subprocess->run(
sub ($subprocess) {
sleep 5;
return '♥', 'Mojolicious';
},
sub ($subprocess, $err, @results) {
say "Subprocess error: $err" and return if $err;
say "I $results[0] $results[1]!";
}
);
# Operation that would block the event loop for 5 seconds (with promise)
$subprocess->run_p(sub {
sleep 5;
return '♥', 'Mojolicious';
})->then(sub (@results) {
say "I $results[0] $results[1]!";
})->catch(sub {
my $err = shift;
say "Subprocess error: $err";
});
# Start event loop if necessary
$subprocess->ioloop->start unless $subprocess->ioloop->is_running;
=head1 DESCRIPTION
L<Mojo::IOLoop::Subprocess> allows L<Mojo::IOLoop> to perform computationally expensive operations in subprocesses,
without blocking the event loop.
=head1 EVENTS
L<Mojo::IOLoop::Subprocess> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones.
=head2 cleanup
$subprocess->on(cleanup => sub ($subprocess) {...});
Emitted in the subprocess right before the process will exit.
$subprocess->on(cleanup => sub ($subprocess) { say "Process $$ is about to exit" });
=head2 progress
$subprocess->on(progress => sub ($subprocess, @data) {...});
Emitted in the parent process when the subprocess calls the L<progress|/"progress1"> method.
=head2 spawn
$subprocess->on(spawn => sub ($subprocess) {...});
Emitted in the parent process when the subprocess has been spawned.
$subprocess->on(spawn => sub ($subprocess) {
my $pid = $subprocess->pid;
say "Performing work in process $pid";
});
=head1 ATTRIBUTES
L<Mojo::IOLoop::Subprocess> implements the following attributes.
=head2 deserialize
my $cb = $subprocess->deserialize;
$subprocess = $subprocess->deserialize(sub {...});
A callback used to deserialize subprocess return values, defaults to using L<Mojo::JSON>.
$subprocess->deserialize(sub ($bytes) { return [] });
=head2 ioloop
my $loop = $subprocess->ioloop;
$subprocess = $subprocess->ioloop(Mojo::IOLoop->new);
Event loop object to control, defaults to the global L<Mojo::IOLoop> singleton. Note that this attribute is weakened.
=head2 serialize
my $cb = $subprocess->serialize;
$subprocess = $subprocess->serialize(sub {...});
A callback used to serialize subprocess return values, defaults to using L<Mojo::JSON>.
$subprocess->serialize(sub ($array) { return '' });
=head1 METHODS
L<Mojo::IOLoop::Subprocess> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones.
=head2 exit_code
my $code = $subprocess->exit_code;
Returns the subprocess exit code, or C<undef> if the subprocess is still running.
=head2 pid
my $pid = $subprocess->pid;
Process id of the spawned subprocess if available.
=head2 progress
$subprocess->progress(@data);
Send data serialized with L<Mojo::JSON> to the parent process at any time during the subprocess's execution. Must be
called by the subprocess and emits the L</"progress"> event in the parent process with the data.
# Send progress information to the parent process
$subprocess->run(
sub ($subprocess) {
$subprocess->progress('0%');
sleep 5;
$subprocess->progress('50%');
sleep 5;
return 'Hello Mojo!';
},
sub ($subprocess, $err, @results) {
say 'Progress is 100%';
say $results[0];
}
);
$subprocess->on(progress => sub ($subprocess, @data) { say "Progress is $data[0]" });
=head2 run
$subprocess = $subprocess->run(sub {...}, sub {...});
Execute the first callback in a child process and wait for it to return one or more values, without blocking
L</"ioloop"> in the parent process. Then execute the second callback in the parent process with the results. The return
values of the first callback and exceptions thrown by it, will be serialized with L<Mojo::JSON>, so they can be shared
between processes.
=head2 run_p
my $promise = $subprocess->run_p(sub {...});
Same as L</"run">, but returns a L<Mojo::Promise> object instead of accepting a second callback.
$subprocess->run_p(sub {
sleep 5;
return '♥', 'Mojolicious';
})->then(sub (@results) {
say "I $results[0] $results[1]!";
})->catch(sub ($err) {
say "Subprocess error: $err";
})->wait;
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,221 @@
package Mojo::IOLoop::TLS;
use Mojo::Base 'Mojo::EventEmitter';
use Mojo::File qw(curfile);
use Mojo::IOLoop;
use Scalar::Util qw(weaken);
# TLS support requires IO::Socket::SSL
use constant TLS => $ENV{MOJO_NO_TLS} ? 0 : eval { require IO::Socket::SSL; IO::Socket::SSL->VERSION('2.009'); 1 };
use constant READ => TLS ? IO::Socket::SSL::SSL_WANT_READ() : 0;
use constant WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
has reactor => sub { Mojo::IOLoop->singleton->reactor }, weak => 1;
# To regenerate the certificate run this command (28.06.2019)
# openssl req -x509 -newkey rsa:4096 -nodes -sha256 -out server.crt \
# -keyout server.key -days 7300 -subj '/CN=localhost'
my $CERT = curfile->sibling('resources', 'server.crt')->to_string;
my $KEY = curfile->sibling('resources', 'server.key')->to_string;
sub DESTROY { shift->_cleanup }
sub can_tls {TLS}
sub negotiate {
my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
return $self->emit(error => 'IO::Socket::SSL 2.009+ required for TLS support') unless TLS;
my $handle = $self->{handle};
return $self->emit(error => $IO::Socket::SSL::SSL_ERROR)
unless IO::Socket::SSL->start_SSL($handle, %{$self->_expand($args)});
$self->reactor->io($handle => sub { $self->_tls($handle, $args->{server}) });
}
sub new { shift->SUPER::new(handle => shift) }
sub _cleanup {
my $self = shift;
return undef unless my $reactor = $self->reactor;
$reactor->remove($self->{handle}) if $self->{handle};
return $self;
}
sub _expand {
my ($self, $args) = @_;
weaken $self;
my $tls = {SSL_error_trap => sub { $self->_cleanup->emit(error => $_[1]) }, SSL_startHandshake => 0};
$tls->{SSL_alpn_protocols} = $args->{tls_protocols} if $args->{tls_protocols};
$tls->{SSL_ca_file} = $args->{tls_ca} if $args->{tls_ca} && -T $args->{tls_ca};
$tls->{SSL_cert_file} = $args->{tls_cert} if $args->{tls_cert};
$tls->{SSL_cipher_list} = $args->{tls_ciphers} if $args->{tls_ciphers};
$tls->{SSL_key_file} = $args->{tls_key} if $args->{tls_key};
$tls->{SSL_server} = $args->{server} if $args->{server};
$tls->{SSL_verify_mode} = $args->{tls_verify} if defined $args->{tls_verify};
$tls->{SSL_version} = $args->{tls_version} if $args->{tls_version};
if ($args->{server}) {
$tls->{SSL_cert_file} ||= $CERT;
$tls->{SSL_key_file} ||= $KEY;
}
else {
$tls->{SSL_hostname} = IO::Socket::SSL->can_client_sni ? $args->{address} : '';
$tls->{SSL_verifycn_name} = $args->{address};
}
return $tls;
}
sub _tls {
my ($self, $handle, $server) = @_;
# Switch between reading and writing
if (!($server ? $handle->accept_SSL : $handle->connect_SSL)) {
my $err = $IO::Socket::SSL::SSL_ERROR;
if ($err == READ) { $self->reactor->watch($handle, 1, 0) }
elsif ($err == WRITE) { $self->reactor->watch($handle, 1, 1) }
}
else { $self->_cleanup->emit(upgrade => delete $self->{handle}) }
}
1;
=encoding utf8
=head1 NAME
Mojo::IOLoop::TLS - Non-blocking TLS handshake
=head1 SYNOPSIS
use Mojo::IOLoop::TLS;
# Negotiate TLS
my $tls = Mojo::IOLoop::TLS->new($old_handle);
$tls->on(upgrade => sub ($tls, $new_handle) {...});
$tls->on(error => sub ($tls, $err) {...});
$tls->negotiate(server => 1, tls_version => 'TLSv1_2');
# Start reactor if necessary
$tls->reactor->start unless $tls->reactor->is_running;
=head1 DESCRIPTION
L<Mojo::IOLoop::TLS> negotiates TLS for L<Mojo::IOLoop>.
=head1 EVENTS
L<Mojo::IOLoop::TLS> inherits all events from L<Mojo::EventEmitter> and can emit the following new ones.
=head2 upgrade
$tls->on(upgrade => sub ($tls, $handle) {...});
Emitted once TLS has been negotiated.
=head2 error
$tls->on(error => sub ($tls, $err) {...});
Emitted if an error occurs during negotiation, fatal if unhandled.
=head1 ATTRIBUTES
L<Mojo::IOLoop::TLS> implements the following attributes.
=head2 reactor
my $reactor = $tls->reactor;
$tls = $tls->reactor(Mojo::Reactor::Poll->new);
Low-level event reactor, defaults to the C<reactor> attribute value of the global L<Mojo::IOLoop> singleton. Note that
this attribute is weakened.
=head1 METHODS
L<Mojo::IOLoop::TLS> inherits all methods from L<Mojo::EventEmitter> and implements the following new ones.
=head2 can_tls
my $bool = Mojo::IOLoop::TLS->can_tls;
True if L<IO::Socket::SSL> 2.009+ is installed and TLS support enabled.
=head2 negotiate
$tls->negotiate(server => 1, tls_version => 'TLSv1_2');
$tls->negotiate({server => 1, tls_version => 'TLSv1_2'});
Negotiate TLS.
These options are currently available:
=over 2
=item server
server => 1
Negotiate TLS from the server-side, defaults to the client-side.
=item tls_ca
tls_ca => '/etc/tls/ca.crt'
Path to TLS certificate authority file.
=item tls_cert
tls_cert => '/etc/tls/server.crt'
tls_cert => {'mojolicious.org' => '/etc/tls/mojo.crt'}
Path to the TLS cert file, defaults to a built-in test certificate on the server-side.
=item tls_ciphers
tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH'
TLS cipher specification string. For more information about the format see
L<https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-STRINGS>.
=item tls_key
tls_key => '/etc/tls/server.key'
tls_key => {'mojolicious.org' => '/etc/tls/mojo.key'}
Path to the TLS key file, defaults to a built-in test key on the server-side.
=item tls_protocols
tls_protocols => ['foo', 'bar']
ALPN protocols to negotiate.
=item tls_verify
tls_verify => 0x00
TLS verification mode.
=item tls_version
tls_version => 'TLSv1_2'
TLS protocol version.
=back
=head2 new
my $tls = Mojo::IOLoop::TLS->new($handle);
Construct a new L<Mojo::IOLoop::Stream> object.
=head1 SEE ALSO
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
=cut

View File

@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEpDCCAowCCQD2f63fTFHflTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMTkwNjI4MjExNDI5WhcNMzkwNjIzMjExNDI5WjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2
lW4DOBswU1YJkekNF6c4b1VVcpOvtqsLHhTxUz538bffcvhI2vv+aCltG6g5mlvJ
wo5NEu9l0ZG5TD9Ca4+WOOisVWrAI/i2YxXFQLOdjhKRBB1BvrOxSaFOuCXz9+cj
VRo0R8Dq3k+1aSy93Yf+fq9pL7LFJaUOlxcU2FOM+HW9FYPeVbqCzYqpPJoaBnwN
tQkQg7i8ufbeMS0bCcFpfTSV4pCgpWg1L9z6cVmBHtxc4MQv7rTTal+BF/iZDfDk
qTNFJpuK7IGtSVB5laTcssYKGuY5QhN5BBPoGEMP3f0KiZmgMOUqwR6fMUiidacG
iSIcgy05uOJyZ4oroqOzesz8nm2jH1eRPys2WLLFd801GKOZZE2LvNhCVzNIE0s1
Rr8yyWBU9jbjQuxlTAtyMUKKOqG9qsfEnKOsl9T9/pFcpJjad3spwhQSWhWEPWca
avw0CGVaGQ3nYmr9aJ9vpGBIiIsLQOPTzpOOPCDauMFpAPOoKnvIu+iz3Z8sUrMu
Ld+aT/3yxpAtNkmVv5A951XyFt9WDXF7jZOMHdOSZPvvI/Yn7joJUzfP9d7TLKjz
Xu+dzQnrAN3xuAXuy+jBpMIl3OPzwER6a8v7gUKRA/achNlIeOOmBdNn1cyHddcn
k6wiaXHJlFsl8X6IjCs9ILwv6H+ZGq/5QNU1Nrv5kQIDAQABMA0GCSqGSIb3DQEB
CwUAA4ICAQCo3xjINrsIQYvpVwVLpcO1p+oE5NV0ipA30JT+89Dn+vCejel9NzxT
msuD9aQSiNaB4znlIDqux4bSKkcRXDGINiaGNIDNXOtO/787LXjUZlljPVVHoPWi
hxgwc0nUHz3l/YvoXMKHI8blPkIhXl7xgKSuKQu05evjd//kpdHs1h+7b2vtCB0/
VoYTX/NrIX5oMYCvHkZEypQbDJ3VeAkOhRJ4efGEuEskPRm0+eDSL7elas/65saZ
l8vgkKDHZ9K0pd8JXc7EKmg3OBS22C5Lfhhy8AgqMa6R9p54oE4rH4yFaTe3BzFL
xSA6HWqC987L2OCFr2LJ8hZpawDF1otukGHDou/5+4Q03EZz10RuZfzlCLO5DXzW
Q28AtcCxz40n9o1giWzEj4LSYW4qsrpr5cNIhCqMzGPwp2OyS/TazPNJGoT8WKFU
Kr+Y/prfkXAwgVkXlUSiu7ukiYslSM4BbYWHDxd75Iv4GzzhUirSuJKN95RglxR8
XsJFlQwZ/tLvpflqb1Z8gPIV/4avtF+ybdx1AvqYViBQDf6GmLkM3p6Nwfj1LnCn
kFhnqY80gyVjbZXvp9ClypExzgz55/o2ZIijznCaDkFSVBdv+aUIzl98IicZxHqP
WREB+GMKmkaYrfKqlliQKdkXd2mXP/N8rv7SJEzHHpGRKBXsIAalrA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
0N/URKPaZ8Z9V/c15kNhIZBgJhOoVg==
-----END PRIVATE KEY-----