diff -r 753233baf27e Modules/_ssl.c --- a/Modules/_ssl.c Thu Apr 02 21:28:28 2015 +0200 +++ b/Modules/_ssl.c Fri Apr 03 14:15:25 2015 +0200 @@ -223,7 +223,7 @@ static PyTypeObject PySSLMemoryBIO_Type; static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args); static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args); -static int PySSL_select(PySocketSockObject *s, int writing); +static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout); static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args); static PyObject *PySSL_cipher(PySSLSocket *self); @@ -565,6 +565,8 @@ static PyObject *PySSL_SSLdo_handshake(P int err; int sockstate, nonblocking; PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; if (sock) { if (((PyObject*)sock) == Py_None) { @@ -580,6 +582,11 @@ static PyObject *PySSL_SSLdo_handshake(P BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } + timeout = sock->sock_timeout; + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; + /* Actually negotiate SSL connection */ /* XXX If SSL_do_handshake() returns 0, it's also a failure. */ do { @@ -591,10 +598,13 @@ static PyObject *PySSL_SSLdo_handshake(P if (PyErr_CheckSignals()) goto error; + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = PySSL_select(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = PySSL_select(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); } else { sockstate = SOCKET_OPERATION_OK; } @@ -1611,7 +1621,7 @@ static void PySSL_dealloc(PySSLSocket *s */ static int -PySSL_select(PySocketSockObject *s, int writing) +PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout) { int rc; #ifdef HAVE_POLL @@ -1624,10 +1634,14 @@ PySSL_select(PySocketSockObject *s, int #endif /* Nothing to do unless we're in timeout mode (not non-blocking) */ - if ((s == NULL) || (s->sock_timeout == 0)) + if ((s == NULL) || (timeout == 0)) return SOCKET_IS_NONBLOCKING; - else if (s->sock_timeout < 0) - return SOCKET_IS_BLOCKING; + else if (timeout < 0) { + if (s->sock_timeout > 0) + return SOCKET_HAS_TIMED_OUT; + else + return SOCKET_IS_BLOCKING; + } /* Guard against closed socket */ if (s->sock_fd < 0) @@ -1639,8 +1653,8 @@ PySSL_select(PySocketSockObject *s, int pollfd.fd = s->sock_fd; pollfd.events = writing ? POLLOUT : POLLIN; - /* s->sock_timeout is in seconds, timeout in ms */ - ms = (int)_PyTime_AsMilliseconds(s->sock_timeout, _PyTime_ROUND_CEILING); + /* timeout is in seconds, poll() uses milliseconds */ + ms = (int)_PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); assert(ms <= INT_MAX); PySSL_BEGIN_ALLOW_THREADS @@ -1651,7 +1665,7 @@ PySSL_select(PySocketSockObject *s, int if (!_PyIsSelectable_fd(s->sock_fd)) return SOCKET_TOO_LARGE_FOR_SELECT; - _PyTime_AsTimeval_noraise(s->sock_timeout, &tv, _PyTime_ROUND_CEILING); + _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING); FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); @@ -1679,6 +1693,8 @@ static PyObject *PySSL_SSLwrite(PySSLSoc int err; int nonblocking; PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; if (sock != NULL) { if (((PyObject*)sock) == Py_None) { @@ -1707,7 +1723,12 @@ static PyObject *PySSL_SSLwrite(PySSLSoc BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } - sockstate = PySSL_select(sock, 1); + timeout = sock->sock_timeout; + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; + + sockstate = PySSL_select(sock, 1, timeout); if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySocketModule.timeout_error, "The write operation timed out"); @@ -1731,10 +1752,13 @@ static PyObject *PySSL_SSLwrite(PySSLSoc if (PyErr_CheckSignals()) goto error; + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = PySSL_select(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = PySSL_select(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); } else { sockstate = SOCKET_OPERATION_OK; } @@ -1801,6 +1825,8 @@ static PyObject *PySSL_SSLread(PySSLSock int err; int nonblocking; PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; if (sock != NULL) { if (((PyObject*)sock) == Py_None) { @@ -1842,6 +1868,11 @@ static PyObject *PySSL_SSLread(PySSLSock BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } + timeout = sock->sock_timeout; + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; + do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); @@ -1851,10 +1882,13 @@ static PyObject *PySSL_SSLread(PySSLSock if (PyErr_CheckSignals()) goto error; + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + if (err == SSL_ERROR_WANT_READ) { - sockstate = PySSL_select(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = PySSL_select(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); } else if (err == SSL_ERROR_ZERO_RETURN && SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN) { @@ -1908,6 +1942,8 @@ static PyObject *PySSL_SSLshutdown(PySSL int err, ssl_err, sockstate, nonblocking; int zeros = 0; PySocketSockObject *sock = GET_SOCKET(self); + _PyTime_t timeout, deadline = 0; + int has_timeout; if (sock != NULL) { /* Guard against closed socket */ @@ -1924,6 +1960,11 @@ static PyObject *PySSL_SSLshutdown(PySSL BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); } + timeout = sock->sock_timeout; + has_timeout = (timeout > 0); + if (has_timeout) + deadline = _PyTime_GetMonotonicClock() + timeout; + while (1) { PySSL_BEGIN_ALLOW_THREADS /* Disable read-ahead so that unwrap can work correctly. @@ -1953,12 +1994,15 @@ static PyObject *PySSL_SSLshutdown(PySSL continue; } + if (has_timeout) + timeout = deadline - _PyTime_GetMonotonicClock(); + /* Possibly retry shutdown until timeout or failure */ ssl_err = SSL_get_error(self->ssl, err); if (ssl_err == SSL_ERROR_WANT_READ) - sockstate = PySSL_select(sock, 0); + sockstate = PySSL_select(sock, 0, timeout); else if (ssl_err == SSL_ERROR_WANT_WRITE) - sockstate = PySSL_select(sock, 1); + sockstate = PySSL_select(sock, 1, timeout); else break;