I wrote:
> Kyotaro Horiguchi <[email protected]> writes:
>> If psql connected using GSSAPI auth and server restarted, reconnection
>> sequence stalls and won't return.
> Yeah, reproduced here. (I wonder if there's any reasonable way to
> exercise this scenario in src/test/kerberos/.)
I tried writing such a test based on the IO::Pty infrastructure used
by src/bin/psql/t/010_tab_completion.pl, as attached. It works, but
it feels pretty grotty, especially seeing that so much of the patch
is copy-and-pasted from 010_tab_completion.pl. I think if we want
to have a test like this, it'd be good to work a little harder on
refactoring so that more of that code can be shared. My Perl skillz
are a bit weak for that, though.
regards, tom lane
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index b3aeea9574..552d2724e7 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -13,17 +13,30 @@
use strict;
use warnings;
+
use TestLib;
use PostgresNode;
use Test::More;
+use IPC::Run qw(pump finish timer);
+use Data::Dumper;
-if ($ENV{with_gssapi} eq 'yes')
+# Do nothing unless Makefile has told us that the build is --with-gssapi.
+if ($ENV{with_gssapi} ne 'yes')
{
+ plan skip_all => 'GSSAPI/Kerberos not supported by this build';
+}
+
+# If we don't have IO::Pty, we can't run the test with interactive_psql.
+my $have_pty = 1;
+eval { require IO::Pty; };
+if ($@)
+{
+ $have_pty = 0;
plan tests => 18;
}
else
{
- plan skip_all => 'GSSAPI/Kerberos not supported by this build';
+ plan tests => 22;
}
my ($krb5_bin_dir, $krb5_sbin_dir);
@@ -275,6 +288,77 @@ test_query(
"gssencmode=require",
"sending 100K lines works");
+# Test that libpq can reconnect after a server restart.
+if ($have_pty)
+{
+ # fire up an interactive psql session
+ my $in = '';
+ my $out = '';
+
+ my $timer = timer(5);
+
+ my $h = $node->interactive_psql(
+ 'postgres',
+ \$in,
+ \$out,
+ $timer,
+ extra_params => [
+ '-XAtd',
+ $node->connstr('postgres')
+ . " host=$host hostaddr=$hostaddr gssencmode=require user=test1"
+ ]);
+
+ like(
+ $out,
+ qr/GSSAPI-encrypted connection/,
+ "startup banner shows GSSAPI encryption");
+
+ # subroutine to send a command and wait for response
+ sub interactive_command
+ {
+ my ($send, $pattern, $annotation) = @_;
+
+ # report test failures from caller location
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ # reset output collector
+ $out = "";
+ # restart per-command timer
+ $timer->start(5);
+ # send the data to be sent
+ $in .= $send;
+ # wait ...
+ pump $h until ($out =~ $pattern || $timer->is_expired);
+ my $okay = ($out =~ $pattern && !$timer->is_expired);
+ ok($okay, $annotation);
+ # for debugging, log actual output if it didn't match
+ local $Data::Dumper::Terse = 1;
+ local $Data::Dumper::Useqq = 1;
+ diag 'Actual output was '
+ . Dumper($out)
+ . "Did not match \"$pattern\"\n"
+ if !$okay;
+ return;
+ }
+
+ interactive_command("SELECT 2+2;\n", qr/4/, "interactive psql is alive");
+
+ $node->restart;
+
+ interactive_command(
+ "SELECT 2+2;\n",
+ qr/The connection to the server was lost.*GSSAPI-encrypted connection/s,
+ "startup banner shows GSSAPI encryption after reconnection");
+
+ interactive_command("SELECT 20+22;\n", qr/42/, "psql is still alive");
+
+ # send psql an explicit \q to shut it down, else pty won't close properly
+ $timer->start(5);
+ $in .= "\\q\n";
+ finish $h or die "psql returned $?";
+ $timer->reset;
+}
+
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
qq{hostgssenc all all $hostaddr/32 gss map=mymap});