Database-Async-Engine-PostgreSQL
view release on metacpan or search on metacpan
lib/Database/Async/Engine/PostgreSQL.pm view on Meta::CPAN
unless ($^O eq 'MSWin32') { # same as libpq
# libpq also does stat here instead of lstat. So, pgpass can be
# a logical link.
my (undef, undef, $mode) = stat $pwfile or return undef;
unless (-f _) {
$log->warnf("WARNING: password file \"%s\" is not a plain file\n", $pwfile);
return undef;
}
if ($mode & 077) {
$log->warnf("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less", $pwfile);
return undef;
}
# libpq has the same race condition of stat versus open.
}
# It's not an error for this file to be missing: it might not
# be readable for various reasons, but for now we ignore that case as well
# (we've already checked for overly-lax permissions above)
open my $fh, '<', $pwfile or return undef;
while (defined(my $line = readline $fh)) {
next if $line =~ '^#';
chomp $line;
my ($host, $port, $db, $user, $pw) = ($line =~ /((?:\\.|[^:])*)(?::|$)/g)
or next;
s/\\(.)/$1/g for ($host, $port, $db, $user, $pw);
return $pw if (
$host eq '*' || $host eq $self->uri->host and
$port eq '*' || $port eq $self->uri->port and
$user eq '*' || $user eq $self->database_user and
$db eq '*' || $db eq $self->database_name
);
}
return undef;
}
sub database_password {
my $self = shift;
return $self->uri->password // $ENV{PGPASSWORD} || $self->password_from_file
}
=head2 negotiate_ssl
Apply SSL negotiation.
=cut
async sub negotiate_ssl {
my ($self, %args) = @_;
my $stream = delete $args{stream};
# If SSL is disabled entirely, just return the same stream as-is
my $ssl = $self->ssl
or return $stream;
require IO::Async::SSL;
require IO::Socket::SSL;
$log->tracef('Attempting to negotiate SSL');
await $stream->write($self->protocol->ssl_request);
$log->tracef('Waiting for response');
my ($resp, $eof) = await $stream->read_exactly(1);
$log->tracef('Read %v02x from server for SSL response (EOF is %s)', $resp, $eof ? 'true' : 'false');
die 'Server closed connection' if $eof;
if($resp eq 'S') {
# S for SSL...
$log->tracef('This is SSL, let us upgrade');
$stream = await $self->loop->SSL_upgrade(
handle => $stream,
# SSL defaults...
SSL_server => 0,
SSL_hostname => $self->uri->host,
SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),
# Pass through anything SSL-related unchanged, the user knows
# better than we do
(map {; $_ => $self->{$_} } grep { /^SSL_/ } keys %$self)
);
$log->tracef('Upgrade complete');
} elsif($resp eq 'N') {
# N for "no SSL"...
$log->tracef('No to SSL');
die 'Server does not support SSL' if $self->ssl == SSL_REQUIRE;
} else {
# anything else is unexpected
die 'Unknown response to SSL request';
}
return $stream;
}
sub is_replication { shift->{is_replication} //= 0 }
sub application_name { shift->{application_name} //= 'perl' }
=head2 uri_for_dsn
Returns a L<URI> corresponding to the given L<database source name|https://en.wikipedia.org/wiki/Data_source_name>.
May throw an exception if we don't have a valid string.
=cut
sub uri_for_dsn {
my ($class, $dsn) = @_;
die 'invalid DSN, expecting DBI:Pg:...' unless $dsn =~ s/^DBI:Pg://i;
my %args = split /[=;]/, $dsn;
my $uri = URI->new('postgresql://postgres@localhost/postgres');
$uri->$_(delete $args{$_}) for grep exists $args{$_}, qw(host port user password dbname);
$uri
}
sub uri_for_service {
my ($class, $service) = @_;
my $cfg = $class->find_service($service);
# Start with common default values (i.e. follow libpq behaviour unless there's a strong reason not to)
my $uri = URI->new('postgresql://postgres@localhost/postgres');
# Standard fields supported by URI::pg
$uri->$_(delete $cfg->{$_}) for grep exists $cfg->{$_}, qw(host port user password dbname);
# ... note that `hostaddr` takes precedence over plain `host`
$uri->host(delete $cfg->{hostaddr}) if exists $cfg->{hostaddr};
# Everything else is handled via query parameters, this list is non-exhaustive and likely to be
# extended in future (e.g. text/binary protocol mode)
$uri->query_param($_ => delete $cfg->{$_}) for grep exists $cfg->{$_}, qw(
application_name
fallback_application_name
keepalives
options
sslmode
replication
);
$uri
}
( run in 1.854 second using v1.01-cache-2.11-cpan-5a3173703d6 )