I wrote:
> Kyotaro Horiguchi <horikyota....@gmail.com> 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});

Reply via email to