Index: generic/tlsIO.c ================================================================== --- generic/tlsIO.c +++ generic/tlsIO.c @@ -176,12 +176,18 @@ dprintf("Got error: %s", ERR_reason_error_string(backingError)); } bioShouldRetry = 0; if (err <= 0) { - if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT || rc == SSL_ERROR_WANT_READ || rc == SSL_ERROR_WANT_WRITE) { + if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT) { + bioShouldRetry = 1; + } else if (rc == SSL_ERROR_WANT_READ) { + bioShouldRetry = 1; + statePtr->want = TCL_READABLE; + } else if (rc == SSL_ERROR_WANT_WRITE) { bioShouldRetry = 1; + statePtr->want = TCL_WRITABLE; } else if (BIO_should_retry(statePtr->bio)) { bioShouldRetry = 1; } else if (rc == SSL_ERROR_SYSCALL && Tcl_GetErrno() == EAGAIN) { bioShouldRetry = 1; } @@ -419,20 +425,22 @@ break; case SSL_ERROR_WANT_READ: /* Op did not complete due to not enough data was available. Retry later. */ dprintf("Got SSL_ERROR_WANT_READ, mapping this to EAGAIN"); + *errorCodePtr = EAGAIN; bytesRead = -1; - *errorCodePtr = EAGAIN; + statePtr->want = TCL_READABLE; Tls_Error(statePtr, "SSL_ERROR_WANT_READ"); break; case SSL_ERROR_WANT_WRITE: /* Op did not complete due to unable to sent all data to the BIO. Retry later. */ dprintf("Got SSL_ERROR_WANT_WRITE, mapping this to EAGAIN"); + *errorCodePtr = EAGAIN; bytesRead = -1; - *errorCodePtr = EAGAIN; + statePtr->want = TCL_WRITABLE; Tls_Error(statePtr, "SSL_ERROR_WANT_WRITE"); break; case SSL_ERROR_WANT_X509_LOOKUP: /* Op didn't complete since callback set by SSL_CTX_set_client_cert_cb() asked to be called again */ @@ -638,18 +646,20 @@ case SSL_ERROR_WANT_READ: /* Op did not complete due to not enough data was available. Retry later. */ dprintf("Got SSL_ERROR_WANT_READ, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; + statePtr->want = TCL_READABLE; Tls_Error(statePtr, "SSL_ERROR_WANT_READ"); break; case SSL_ERROR_WANT_WRITE: /* Op did not complete due to unable to sent all data to the BIO. Retry later. */ dprintf("Got SSL_ERROR_WANT_WRITE, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; + statePtr->want = TCL_WRITABLE; Tls_Error(statePtr, "SSL_ERROR_WANT_WRITE"); break; case SSL_ERROR_WANT_X509_LOOKUP: /* Op didn't complete since callback set by SSL_CTX_set_client_cert_cb() asked to be called again */ @@ -833,11 +843,11 @@ * *------------------------------------------------------* */ static void TlsChannelHandlerTimer(ClientData clientData) { State *statePtr = (State *) clientData; - int mask = 0; + int mask = statePtr->want; /* Init to SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE */ dprintf("Called"); statePtr->timer = (Tcl_TimerToken) NULL; @@ -853,10 +863,11 @@ mask |= TCL_READABLE; } dprintf("Notifying ourselves"); Tcl_NotifyChannel(statePtr->self, mask); + statePtr->want = 0; dprintf("Returning"); return; } @@ -930,12 +941,12 @@ dprintf("A timer was found, deleting it"); Tcl_DeleteTimerHandler(statePtr->timer); statePtr->timer = (Tcl_TimerToken) NULL; } - if ((mask & TCL_READABLE) && - ((Tcl_InputBuffered(statePtr->self) > 0) || (BIO_ctrl_pending(statePtr->bio) > 0))) { + if (statePtr->want || ((mask & TCL_READABLE) && + ((Tcl_InputBuffered(statePtr->self) > 0) || (BIO_ctrl_pending(statePtr->bio) > 0)))) { /* * There is interest in readable events and we actually have * data waiting, so generate a timer to flush that. */ dprintf("Creating a new timer since data appears to be waiting"); Index: generic/tlsInt.h ================================================================== --- generic/tlsInt.h +++ generic/tlsInt.h @@ -173,10 +173,11 @@ Tcl_Channel self; /* this socket channel */ Tcl_TimerToken timer; int flags; /* see State.flags above */ int watchMask; /* current WatchProc mask */ + int want; /* pending wants from OpenSSL */ int mode; /* current mode of parent channel */ Tcl_Interp *interp; /* interpreter in which this resides */ Tcl_Obj *callback; /* script called for tracing, info, and errors */ Tcl_Obj *password; /* script called for certificate password */