From 4bc5dc61e8be490b013be5f9b3326faae2dc99c6 Mon Sep 17 00:00:00 2001
From: Jacob Champion <pchampion@vmware.com>
Date: Thu, 12 Nov 2020 12:02:39 -0800
Subject: [PATCH] nss: handle timeouts and disconnections in pgtls_read

PR_IO_TIMEOUT_ERROR was being treated as a hard error, but since we're
explicitly asking PR_Recv() not to wait, we want to treat it as "zero
bytes read" instead.

This case happens relatively often in psql, during PQconsumeInput() --
but the return value of PQconsumeInput() is being ignored during
notification handling, which masks the problem. This also seems to fix
interoperability with OpenSSL servers on my machine, possibly due to
differences in message fragmentation between the implementations?

Also ensure that SOCK_ERRNO_SET() is called in the disconnection case;
read_errno was being set but not used.
---
 src/interfaces/libpq/fe-secure-nss.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/src/interfaces/libpq/fe-secure-nss.c b/src/interfaces/libpq/fe-secure-nss.c
index 3090f37f5e..8fc8e138e2 100644
--- a/src/interfaces/libpq/fe-secure-nss.c
+++ b/src/interfaces/libpq/fe-secure-nss.c
@@ -429,15 +429,19 @@ pgtls_read(PGconn *conn, void *ptr, size_t len)
 	if (nread == 0)
 	{
 		read_errno = ECONNRESET;
-		return -1;
+		nread = -1;
 	}
-
-	if (nread == -1)
+	else if (nread == -1)
 	{
 		status = PR_GetError();
 
 		switch (status)
 		{
+			case PR_IO_TIMEOUT_ERROR:
+				/* No data available yet. */
+				nread = 0;
+				break;
+
 			case PR_WOULD_BLOCK_ERROR:
 				read_errno = EWOULDBLOCK;
 				break;
@@ -456,9 +460,12 @@ pgtls_read(PGconn *conn, void *ptr, size_t len)
 				break;
 		}
 
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("TLS read error: %s"),
-						  pg_SSLerrmessage(status));
+		if (nread == -1)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("TLS read error: %s"),
+							  pg_SSLerrmessage(status));
+		}
 	}
 
 	SOCK_ERRNO_SET(read_errno);
-- 
2.24.1

