* Read data from a secure connection.
*/
ssize_t
-be_tls_read(Port *port, void *ptr, size_t len)
+be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
- int waitfor;
- int latchret;
-rloop:
errno = 0;
n = SSL_read(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n);
port->count += n;
break;
case SSL_ERROR_WANT_READ:
+ *waitfor = WL_SOCKET_READABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_WANT_WRITE:
- /* Don't retry if the socket is in nonblocking mode. */
- if (port->noblock)
- {
- errno = EWOULDBLOCK;
- n = -1;
- break;
- }
-
- waitfor = WL_LATCH_SET;
-
- if (err == SSL_ERROR_WANT_READ)
- waitfor |= WL_SOCKET_READABLE;
- else
- waitfor |= WL_SOCKET_WRITEABLE;
-
- latchret = WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
-
- /*
- * We'll, among other situations, get here if the low level
- * routine doing the actual recv() via the socket got interrupted
- * by a signal. That's so we can handle interrupts once outside
- * openssl, so we don't jump out from underneath its covers. We
- * can check this both, when reading and writing, because even
- * when writing that's just openssl's doing, not a 'proper' write
- * initiated by postgres.
- */
- if (latchret & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- ProcessClientReadInterrupt(true); /* preserves errno */
- }
- goto rloop;
+ *waitfor = WL_SOCKET_WRITEABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_SYSCALL:
/* leave it to caller to ereport the value of errno */
if (n != -1)
* Write data to a secure connection.
*/
ssize_t
-be_tls_write(Port *port, void *ptr, size_t len)
+be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
- int waitfor;
- int latchret;
/*
* If SSL renegotiations are enabled and we're getting close to the
errmsg("SSL failure during renegotiation start")));
}
-wloop:
errno = 0;
n = SSL_write(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n);
port->count += n;
break;
case SSL_ERROR_WANT_READ:
+ *waitfor = WL_SOCKET_READABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_WANT_WRITE:
-
- waitfor = WL_LATCH_SET;
-
- if (err == SSL_ERROR_WANT_READ)
- waitfor |= WL_SOCKET_READABLE;
- else
- waitfor |= WL_SOCKET_WRITEABLE;
-
- latchret = WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
-
- /*
- * Check for interrupts here, in addition to secure_write(),
- * because an interrupted write in secure_raw_write() will return
- * here, and we cannot return to secure_write() until we've
- * written something.
- */
- if (latchret & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- ProcessClientWriteInterrupt(true); /* preserves errno */
- }
-
- goto wloop;
+ *waitfor = WL_SOCKET_WRITEABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_SYSCALL:
/* leave it to caller to ereport the value of errno */
if (n != -1)
secure_read(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ int waitfor;
retry:
#ifdef USE_SSL
+ waitfor = 0;
if (port->ssl_in_use)
{
- n = be_tls_read(port, ptr, len);
+ n = be_tls_read(port, ptr, len, &waitfor);
}
else
#endif
{
n = secure_raw_read(port, ptr, len);
+ waitfor = WL_SOCKET_READABLE;
}
- /* retry after processing interrupts */
- if (n < 0 && errno == EINTR)
+ /* In blocking mode, wait until the socket is ready */
+ if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
{
- /*
- * We tried to read data, the socket was empty, and we were
- * interrupted while waiting for readability. We only process
- * interrupts if we got interrupted while reading and when in blocking
- * mode. In other cases it's better to allow the interrupts to be
- * handled at higher layers.
- */
- ProcessClientReadInterrupt(!port->noblock); /* preserves errno */
+ int w;
+
+ Assert(waitfor);
+
+ w = WaitLatchOrSocket(MyLatch,
+ WL_LATCH_SET | waitfor,
+ port->sock, 0);
+
+ /* Handle interrupt. */
+ if (w & WL_LATCH_SET)
+ {
+ ResetLatch(MyLatch);
+ ProcessClientReadInterrupt(true);
+
+ /*
+ * We'll retry the read. Most likely it will return immediately
+ * because there's still no data available, and we'll wait
+ * for the socket to become ready again.
+ */
+ }
goto retry;
}
* Try to read from the socket without blocking. If it succeeds we're
* done, otherwise we'll wait for the socket using the latch mechanism.
*/
-rloop:
#ifdef WIN32
pgwin32_noblock = true;
#endif
pgwin32_noblock = false;
#endif
- if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- {
- int w;
- int save_errno = errno;
-
- w = WaitLatchOrSocket(MyLatch,
- WL_LATCH_SET | WL_SOCKET_READABLE,
- port->sock, 0);
-
- if (w & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- /*
- * Force a return, so interrupts can be processed when not
- * (possibly) underneath a ssl library.
- */
- errno = EINTR;
- return -1;
- }
- else if (w & WL_SOCKET_READABLE)
- {
- goto rloop;
- }
-
- /*
- * Restore errno, clobbered by WaitLatchOrSocket, so the caller can
- * react properly.
- */
- errno = save_errno;
- }
-
return n;
}
secure_write(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ int waitfor;
retry:
+ waitfor = 0;
#ifdef USE_SSL
if (port->ssl_in_use)
{
- n = be_tls_write(port, ptr, len);
+ n = be_tls_write(port, ptr, len, &waitfor);
}
else
#endif
{
n = secure_raw_write(port, ptr, len);
+ waitfor = WL_SOCKET_WRITEABLE;
}
- /* retry after processing interrupts */
- if (n < 0 && errno == EINTR)
+ if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
{
- /*
- * We tried to send data, the socket was full, and we were interrupted
- * while waiting for writability. We only process interrupts if we got
- * interrupted while writing and when in blocking mode. In other cases
- * it's better to allow the interrupts to be handled at higher layers.
- */
- ProcessClientWriteInterrupt(!port->noblock);
+ int w;
+
+ Assert(waitfor);
+ w = WaitLatchOrSocket(MyLatch,
+ WL_LATCH_SET | waitfor,
+ port->sock, 0);
+
+ /* Handle interrupt. */
+ if (w & WL_LATCH_SET)
+ {
+ ResetLatch(MyLatch);
+ ProcessClientWriteInterrupt(true);
+
+ /*
+ * We'll retry the write. Most likely it will return immediately
+ * because there's still no data available, and we'll wait
+ * for the socket to become ready again.
+ */
+ }
goto retry;
}
+ /*
+ * Process interrupts that happened while (or before) sending. Note that
+ * we signal that we're not blocking, which will prevent some types of
+ * interrupts from being processed.
+ */
+ ProcessClientWriteInterrupt(false);
+
return n;
}
{
ssize_t n;
-wloop:
-
#ifdef WIN32
pgwin32_noblock = true;
#endif
pgwin32_noblock = false;
#endif
- if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- {
- int w;
- int save_errno = errno;
-
- w = WaitLatchOrSocket(MyLatch,
- WL_LATCH_SET | WL_SOCKET_WRITEABLE,
- port->sock, 0);
-
- if (w & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- /*
- * Force a return, so interrupts can be processed when not
- * (possibly) underneath a ssl library.
- */
- errno = EINTR;
- return -1;
- }
- else if (w & WL_SOCKET_WRITEABLE)
- {
- goto wloop;
- }
-
- /*
- * Restore errno, clobbered by WaitLatchOrSocket, so the caller can
- * react properly.
- */
- errno = save_errno;
- }
-
return n;
}