Blame SOURCES/m2crypto-0.21.1-timeouts.patch

2ca359
diff -urN M2Crypto/M2Crypto/SSL/Connection.py M2Crypto-0.21.1/M2Crypto/SSL/Connection.py
2ca359
--- M2Crypto/M2Crypto/SSL/Connection.py	2013-11-26 20:01:02.591964970 +0100
2ca359
+++ M2Crypto-0.21.1/M2Crypto/SSL/Connection.py	2013-11-26 20:01:19.204950349 +0100
2ca359
@@ -47,9 +47,11 @@
90fa17
             self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
90fa17
             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
90fa17
         self._fileno = self.socket.fileno()
90fa17
-        
90fa17
-        self.blocking = self.socket.gettimeout()
90fa17
-        
90fa17
+
90fa17
+        self._timeout = self.socket.gettimeout()
90fa17
+        if self._timeout is None:
90fa17
+            self._timeout = -1.0
90fa17
+
90fa17
         self.ssl_close_flag = m2.bio_noclose
90fa17
 
90fa17
         
2ca359
@@ -147,7 +149,7 @@
90fa17
         m2.ssl_set_accept_state(self.ssl)
90fa17
 
90fa17
     def accept_ssl(self):
90fa17
-        return m2.ssl_accept(self.ssl)
90fa17
+        return m2.ssl_accept(self.ssl, self._timeout)
90fa17
 
90fa17
     def accept(self):
90fa17
         """Accept an SSL connection. The return value is a pair (ssl, addr) where
2ca359
@@ -169,7 +171,7 @@
90fa17
         m2.ssl_set_connect_state(self.ssl)
90fa17
 
90fa17
     def connect_ssl(self):
90fa17
-        return m2.ssl_connect(self.ssl)
90fa17
+        return m2.ssl_connect(self.ssl, self._timeout)
90fa17
 
90fa17
     def connect(self, addr):
90fa17
         self.socket.connect(addr)
2ca359
@@ -196,7 +198,7 @@
90fa17
         return m2.ssl_pending(self.ssl)
90fa17
 
90fa17
     def _write_bio(self, data):
90fa17
-        return m2.ssl_write(self.ssl, data)
90fa17
+        return m2.ssl_write(self.ssl, data, self._timeout)
90fa17
 
90fa17
     def _write_nbio(self, data):
90fa17
         return m2.ssl_write_nbio(self.ssl, data)
2ca359
@@ -204,7 +206,7 @@
90fa17
     def _read_bio(self, size=1024):
90fa17
         if size <= 0:
90fa17
             raise ValueError, 'size <= 0'
90fa17
-        return m2.ssl_read(self.ssl, size)
90fa17
+        return m2.ssl_read(self.ssl, size, self._timeout)
90fa17
 
90fa17
     def _read_nbio(self, size=1024):
90fa17
         if size <= 0:
2ca359
@@ -212,13 +214,13 @@
90fa17
         return m2.ssl_read_nbio(self.ssl, size)
90fa17
 
90fa17
     def write(self, data):
90fa17
-        if self.blocking:
90fa17
+        if self._timeout != 0.0:
90fa17
             return self._write_bio(data)
90fa17
         return self._write_nbio(data)
90fa17
     sendall = send = write
90fa17
     
90fa17
     def read(self, size=1024):
90fa17
-        if self.blocking:
90fa17
+        if self._timeout != 0.0:
90fa17
             return self._read_bio(size)
90fa17
         return self._read_nbio(size)
90fa17
     recv = read
2ca359
@@ -226,7 +228,17 @@
90fa17
     def setblocking(self, mode):
90fa17
         """Set this connection's underlying socket to _mode_."""
90fa17
         self.socket.setblocking(mode)
90fa17
-        self.blocking = mode
90fa17
+        if mode:
90fa17
+            self._timeout = -1.0
90fa17
+        else:
90fa17
+            self._timeout = 0.0
90fa17
+
90fa17
+    def settimeout(self, timeout):
90fa17
+        """Set this connection's underlying socket's timeout to _timeout_."""
90fa17
+        self.socket.settimeout(timeout)
90fa17
+        self._timeout = timeout
90fa17
+        if self._timeout is None:
90fa17
+            self._timeout = -1.0
90fa17
 
90fa17
     def fileno(self):
90fa17
         return self.socket.fileno()
2ca359
@@ -308,15 +320,8 @@
90fa17
         """Set the cipher suites for this connection."""
90fa17
         return m2.ssl_set_cipher_list(self.ssl, cipher_list)
90fa17
 
90fa17
-    def makefile(self, mode='rb', bufsize='ignored'):
90fa17
-        r = 'r' in mode or '+' in mode
90fa17
-        w = 'w' in mode or 'a' in mode or '+' in mode
90fa17
-        b = 'b' in mode
90fa17
-        m2mode = ['', 'r'][r] + ['', 'w'][w] + ['', 'b'][b]      
90fa17
-        # XXX Need to dup().
90fa17
-        bio = BIO.BIO(self.sslbio, _close_cb=self.close)
90fa17
-        m2.bio_do_handshake(bio._ptr())
90fa17
-        return BIO.IOBuffer(bio, m2mode, _pyfree=0)
90fa17
+    def makefile(self, mode='rb', bufsize=-1):
90fa17
+        return socket._fileobject(self, mode, bufsize)
90fa17
 
90fa17
     def getsockname(self):
90fa17
         return self.socket.getsockname()
2ca359
diff -urN M2Crypto/M2Crypto/SSL/__init__.py M2Crypto-0.21.1/M2Crypto/SSL/__init__.py
2ca359
--- M2Crypto/M2Crypto/SSL/__init__.py	2013-11-26 20:01:02.590964971 +0100
2ca359
+++ M2Crypto-0.21.1/M2Crypto/SSL/__init__.py	2013-11-26 20:01:19.204950349 +0100
90fa17
@@ -2,11 +2,14 @@
90fa17
 
90fa17
 Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved."""
90fa17
 
90fa17
+import socket
90fa17
+
90fa17
 # M2Crypto
90fa17
 from M2Crypto import m2
90fa17
 
90fa17
 class SSLError(Exception): pass
90fa17
-m2.ssl_init(SSLError)
90fa17
+class SSLTimeoutError(SSLError, socket.timeout): pass
90fa17
+m2.ssl_init(SSLError, SSLTimeoutError)
90fa17
 
90fa17
 # M2Crypto.SSL
90fa17
 from Cipher import Cipher, Cipher_Stack
2ca359
diff -urN M2Crypto/SWIG/_ssl.i M2Crypto-0.21.1/SWIG/_ssl.i
2ca359
--- M2Crypto/SWIG/_ssl.i	2013-11-26 20:01:02.612964952 +0100
2ca359
+++ M2Crypto-0.21.1/SWIG/_ssl.i	2013-11-26 20:01:19.205950348 +0100
2ca359
@@ -11,10 +11,13 @@
90fa17
 
90fa17
 %{
90fa17
 #include <pythread.h>
90fa17
+#include <limits.h>
90fa17
 #include <openssl/bio.h>
90fa17
 #include <openssl/dh.h>
90fa17
 #include <openssl/ssl.h>
90fa17
 #include <openssl/x509.h>
90fa17
+#include <poll.h>
90fa17
+#include <sys/time.h>
90fa17
 %}
90fa17
 
90fa17
 %apply Pointer NONNULL { SSL_CTX * };
2ca359
@@ -155,6 +158,11 @@
90fa17
 %rename(ssl_session_get_timeout) SSL_SESSION_get_timeout;
90fa17
 extern long SSL_SESSION_get_timeout(CONST SSL_SESSION *);
90fa17
 
90fa17
+extern PyObject *ssl_accept(SSL *ssl, double timeout = -1);
90fa17
+extern PyObject *ssl_connect(SSL *ssl, double timeout = -1);
90fa17
+extern PyObject *ssl_read(SSL *ssl, int num, double timeout = -1);
90fa17
+extern int ssl_write(SSL *ssl, PyObject *blob, double timeout = -1);
90fa17
+
90fa17
 %constant int ssl_error_none              = SSL_ERROR_NONE;
90fa17
 %constant int ssl_error_ssl               = SSL_ERROR_SSL;
90fa17
 %constant int ssl_error_want_read         = SSL_ERROR_WANT_READ;
2ca359
@@ -210,14 +218,19 @@
90fa17
 %constant int SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = SSL_MODE_ENABLE_PARTIAL_WRITE;
90fa17
 %constant int SSL_MODE_AUTO_RETRY           = SSL_MODE_AUTO_RETRY;
90fa17
 
90fa17
+%ignore ssl_handle_error;
90fa17
+%ignore ssl_sleep_with_timeout;
90fa17
 %inline %{
90fa17
 static PyObject *_ssl_err;
90fa17
+static PyObject *_ssl_timeout_err;
90fa17
 
90fa17
-void ssl_init(PyObject *ssl_err) {
90fa17
+void ssl_init(PyObject *ssl_err, PyObject *ssl_timeout_err) {
90fa17
     SSL_library_init();
90fa17
     SSL_load_error_strings();
90fa17
     Py_INCREF(ssl_err);
90fa17
+    Py_INCREF(ssl_timeout_err);
90fa17
     _ssl_err = ssl_err;
90fa17
+    _ssl_timeout_err = ssl_timeout_err;
90fa17
 }
90fa17
 
90fa17
 void ssl_ctx_passphrase_callback(SSL_CTX *ctx, PyObject *pyfunc) {
2ca359
@@ -403,36 +416,130 @@
90fa17
     return ret;
90fa17
 }
90fa17
 
90fa17
-PyObject *ssl_accept(SSL *ssl) {
90fa17
+static void ssl_handle_error(int ssl_err, int ret) {
90fa17
+    int err;
90fa17
+
90fa17
+    switch (ssl_err) {
90fa17
+        case SSL_ERROR_SSL:
90fa17
+            PyErr_SetString(_ssl_err,
90fa17
+                            ERR_reason_error_string(ERR_get_error()));
90fa17
+            break;
90fa17
+        case SSL_ERROR_SYSCALL:
90fa17
+            err = ERR_get_error();
90fa17
+            if (err)
90fa17
+                PyErr_SetString(_ssl_err, ERR_reason_error_string(err));
90fa17
+            else if (ret == 0)
90fa17
+                PyErr_SetString(_ssl_err, "unexpected eof");
90fa17
+            else if (ret == -1)
90fa17
+                PyErr_SetFromErrno(_ssl_err);
90fa17
+            else
90fa17
+                assert(0);
90fa17
+            break;
90fa17
+		default:
90fa17
+            PyErr_SetString(_ssl_err, "unexpected SSL error");
90fa17
+     }
90fa17
+}
90fa17
+
90fa17
+static int ssl_sleep_with_timeout(SSL *ssl, const struct timeval *start,
90fa17
+                                  double timeout, int ssl_err) {
90fa17
+    struct pollfd fd;
90fa17
+    struct timeval tv;
90fa17
+    int ms, tmp;
90fa17
+
90fa17
+    assert(timeout > 0);
90fa17
+ again:
90fa17
+    gettimeofday(&tv, NULL);
90fa17
+    /* tv >= start */
90fa17
+    if ((timeout + start->tv_sec - tv.tv_sec) > INT_MAX / 1000)
90fa17
+        ms = -1;
90fa17
+    else {
90fa17
+        int fract;
90fa17
+
90fa17
+        ms = ((start->tv_sec + (int)timeout) - tv.tv_sec) * 1000;
90fa17
+        fract = (start->tv_usec + (timeout - (int)timeout) * 1000000
90fa17
+                 - tv.tv_usec + 999) / 1000;
90fa17
+        if (ms > 0 && fract > INT_MAX - ms)
90fa17
+            ms = -1;
90fa17
+        else {
90fa17
+            ms += fract;
90fa17
+            if (ms <= 0)
90fa17
+                goto timeout;
90fa17
+        }
90fa17
+    }
90fa17
+    switch (ssl_err) {
90fa17
+	    case SSL_ERROR_WANT_READ:
90fa17
+            fd.fd = SSL_get_rfd(ssl);
90fa17
+            fd.events = POLLIN;
90fa17
+            break;
90fa17
+
90fa17
+	    case SSL_ERROR_WANT_WRITE:
90fa17
+            fd.fd = SSL_get_wfd(ssl);
90fa17
+            fd.events = POLLOUT;
90fa17
+            break;
90fa17
+
90fa17
+	    case SSL_ERROR_WANT_X509_LOOKUP:
90fa17
+            return 0; /* FIXME: is this correct? */
90fa17
+
90fa17
+	    default:
90fa17
+            assert(0);
90fa17
+    }
90fa17
+    if (fd.fd == -1) {
90fa17
+        PyErr_SetString(_ssl_err, "timeout on a non-FD SSL");
90fa17
+        return -1;
90fa17
+    }
90fa17
+    Py_BEGIN_ALLOW_THREADS
90fa17
+    tmp = poll(&fd, 1, ms);
90fa17
+    Py_END_ALLOW_THREADS
90fa17
+    switch (tmp) {
90fa17
+    	case 1:
90fa17
+            return 0;
90fa17
+    	case 0:
90fa17
+            goto timeout;
90fa17
+    	case -1:
90fa17
+            if (errno == EINTR)
90fa17
+                goto again;
90fa17
+            PyErr_SetFromErrno(_ssl_err);
90fa17
+            return -1;
90fa17
+    }
90fa17
+    return 0;
90fa17
+
90fa17
+ timeout:
90fa17
+    PyErr_SetString(_ssl_timeout_err, "timed out");
90fa17
+    return -1;
90fa17
+}
90fa17
+
90fa17
+PyObject *ssl_accept(SSL *ssl, double timeout) {
90fa17
     PyObject *obj = NULL;
90fa17
-    int r, err;
90fa17
+    int r, ssl_err;
90fa17
+    struct timeval tv;
90fa17
 
90fa17
+    if (timeout > 0)
90fa17
+        gettimeofday(&tv, NULL);
90fa17
+ again:
90fa17
     Py_BEGIN_ALLOW_THREADS
90fa17
     r = SSL_accept(ssl);
90fa17
+    ssl_err = SSL_get_error(ssl, r);
90fa17
     Py_END_ALLOW_THREADS
90fa17
 
90fa17
 
90fa17
-    switch (SSL_get_error(ssl, r)) {
90fa17
+    switch (ssl_err) {
90fa17
         case SSL_ERROR_NONE:
90fa17
         case SSL_ERROR_ZERO_RETURN:
90fa17
             obj = PyInt_FromLong((long)1);
90fa17
             break;
90fa17
         case SSL_ERROR_WANT_WRITE:
90fa17
         case SSL_ERROR_WANT_READ:
90fa17
-            obj = PyInt_FromLong((long)0);
90fa17
-            break;
90fa17
-        case SSL_ERROR_SSL:
90fa17
-            PyErr_SetString(_ssl_err, ERR_reason_error_string(ERR_get_error()));
90fa17
+            if (timeout <= 0) {
90fa17
+                obj = PyInt_FromLong((long)0);
90fa17
+                break;
90fa17
+            }
90fa17
+            if (ssl_sleep_with_timeout(ssl, &tv, timeout, ssl_err) == 0)
90fa17
+                goto again;
90fa17
             obj = NULL;
90fa17
             break;
90fa17
+        case SSL_ERROR_SSL:
90fa17
         case SSL_ERROR_SYSCALL:
90fa17
-            err = ERR_get_error();
90fa17
-            if (err)
90fa17
-                PyErr_SetString(_ssl_err, ERR_reason_error_string(err));
90fa17
-            else if (r == 0)
90fa17
-                PyErr_SetString(_ssl_err, "unexpected eof");
90fa17
-            else if (r == -1)
90fa17
-                PyErr_SetFromErrno(_ssl_err);
90fa17
+            ssl_handle_error(ssl_err, r);
90fa17
             obj = NULL;
90fa17
             break;
90fa17
     }
2ca359
@@ -441,36 +548,38 @@
90fa17
     return obj;
90fa17
 }
90fa17
 
90fa17
-PyObject *ssl_connect(SSL *ssl) {
90fa17
+PyObject *ssl_connect(SSL *ssl, double timeout) {
90fa17
     PyObject *obj = NULL;
90fa17
-    int r, err;
90fa17
+    int r, ssl_err;
90fa17
+    struct timeval tv;
90fa17
 
90fa17
+    if (timeout > 0)
90fa17
+        gettimeofday(&tv, NULL);
90fa17
+ again:
90fa17
     Py_BEGIN_ALLOW_THREADS
90fa17
     r = SSL_connect(ssl);
90fa17
+    ssl_err = SSL_get_error(ssl, r);
90fa17
     Py_END_ALLOW_THREADS
90fa17
 
90fa17
     
90fa17
-    switch (SSL_get_error(ssl, r)) {
90fa17
+    switch (ssl_err) {
90fa17
         case SSL_ERROR_NONE:
90fa17
         case SSL_ERROR_ZERO_RETURN:
90fa17
             obj = PyInt_FromLong((long)1);
90fa17
             break;
90fa17
         case SSL_ERROR_WANT_WRITE:
90fa17
         case SSL_ERROR_WANT_READ:
90fa17
-            obj = PyInt_FromLong((long)0);
90fa17
-            break;
90fa17
-        case SSL_ERROR_SSL:
90fa17
-            PyErr_SetString(_ssl_err, ERR_reason_error_string(ERR_get_error()));
90fa17
+            if (timeout <= 0) {
90fa17
+                obj = PyInt_FromLong((long)0);
90fa17
+                break;
90fa17
+            }
90fa17
+            if (ssl_sleep_with_timeout(ssl, &tv, timeout, ssl_err) == 0)
90fa17
+                goto again;
90fa17
             obj = NULL;
90fa17
             break;
90fa17
+        case SSL_ERROR_SSL:
90fa17
         case SSL_ERROR_SYSCALL:
90fa17
-            err = ERR_get_error();
90fa17
-            if (err)
90fa17
-                PyErr_SetString(_ssl_err, ERR_reason_error_string(err));
90fa17
-            else if (r == 0)
90fa17
-                PyErr_SetString(_ssl_err, "unexpected eof");
90fa17
-            else if (r == -1)
90fa17
-                PyErr_SetFromErrno(_ssl_err);
90fa17
+            ssl_handle_error(ssl_err, r);
90fa17
             obj = NULL;
90fa17
             break;
90fa17
     }
2ca359
@@ -483,10 +592,11 @@
90fa17
     SSL_set_shutdown(ssl, mode);
90fa17
 }
90fa17
 
90fa17
-PyObject *ssl_read(SSL *ssl, int num) {
90fa17
+PyObject *ssl_read(SSL *ssl, int num, double timeout) {
90fa17
     PyObject *obj = NULL;
90fa17
     void *buf;
90fa17
-    int r, err;
90fa17
+    int r;
90fa17
+    struct timeval tv;
90fa17
 
90fa17
     if (!(buf = PyMem_Malloc(num))) {
90fa17
         PyErr_SetString(PyExc_MemoryError, "ssl_read");
2ca359
@@ -494,37 +604,44 @@
90fa17
     }
90fa17
 
90fa17
 
90fa17
+    if (timeout > 0)
90fa17
+        gettimeofday(&tv, NULL);
90fa17
+ again:
90fa17
     Py_BEGIN_ALLOW_THREADS
90fa17
     r = SSL_read(ssl, buf, num);
90fa17
     Py_END_ALLOW_THREADS
90fa17
 
90fa17
 
90fa17
-    switch (SSL_get_error(ssl, r)) {
90fa17
-        case SSL_ERROR_NONE:
90fa17
-        case SSL_ERROR_ZERO_RETURN:
90fa17
-            buf = PyMem_Realloc(buf, r);
90fa17
-            obj = PyString_FromStringAndSize(buf, r);
90fa17
-            break;
90fa17
-        case SSL_ERROR_WANT_WRITE:
90fa17
-        case SSL_ERROR_WANT_READ:
90fa17
-        case SSL_ERROR_WANT_X509_LOOKUP:
90fa17
-            Py_INCREF(Py_None);
90fa17
-            obj = Py_None;
90fa17
-            break;
90fa17
-        case SSL_ERROR_SSL:
90fa17
-            PyErr_SetString(_ssl_err, ERR_reason_error_string(ERR_get_error()));
90fa17
-            obj = NULL;
90fa17
-            break;
90fa17
-        case SSL_ERROR_SYSCALL:
90fa17
-            err = ERR_get_error();
90fa17
-            if (err)
90fa17
-                PyErr_SetString(_ssl_err, ERR_reason_error_string(err));
90fa17
-            else if (r == 0)
90fa17
-                PyErr_SetString(_ssl_err, "unexpected eof");
90fa17
-            else if (r == -1)
90fa17
-                PyErr_SetFromErrno(_ssl_err);
90fa17
-            obj = NULL;
90fa17
-            break;
90fa17
+    if (r >= 0) {
90fa17
+        buf = PyMem_Realloc(buf, r);
90fa17
+        obj = PyString_FromStringAndSize(buf, r);
90fa17
+    } else {
90fa17
+        int ssl_err;
90fa17
+
90fa17
+        ssl_err = SSL_get_error(ssl, r);
90fa17
+        switch (ssl_err) {
90fa17
+            case SSL_ERROR_NONE:
90fa17
+            case SSL_ERROR_ZERO_RETURN:
90fa17
+                assert(0);
90fa17
+
90fa17
+            case SSL_ERROR_WANT_WRITE:
90fa17
+            case SSL_ERROR_WANT_READ:
90fa17
+            case SSL_ERROR_WANT_X509_LOOKUP:
90fa17
+                if (timeout <= 0) {
90fa17
+                    Py_INCREF(Py_None);
90fa17
+                    obj = Py_None;
90fa17
+                    break;
90fa17
+                }
90fa17
+                if (ssl_sleep_with_timeout(ssl, &tv, timeout, ssl_err) == 0)
90fa17
+                    goto again;
90fa17
+                obj = NULL;
90fa17
+                break;
90fa17
+            case SSL_ERROR_SSL:
90fa17
+            case SSL_ERROR_SYSCALL:
90fa17
+                ssl_handle_error(ssl_err, r);
90fa17
+                obj = NULL;
90fa17
+                break;
90fa17
+        }
90fa17
     }
90fa17
     PyMem_Free(buf);
90fa17
 
2ca359
@@ -582,22 +699,26 @@
90fa17
     return obj;
90fa17
 }
90fa17
 
90fa17
-int ssl_write(SSL *ssl, PyObject *blob) {
90fa17
+int ssl_write(SSL *ssl, PyObject *blob, double timeout) {
90fa17
     const void *buf;
90fa17
-    int len, r, err, ret;
90fa17
+    int len, r, ssl_err, ret;
90fa17
+    struct timeval tv;
90fa17
 
90fa17
 
90fa17
     if (m2_PyObject_AsReadBufferInt(blob, &buf, &len) == -1) {
90fa17
         return -1;
90fa17
     }
90fa17
 
90fa17
-    
90fa17
+    if (timeout > 0)
90fa17
+        gettimeofday(&tv, NULL);
90fa17
+ again:
90fa17
     Py_BEGIN_ALLOW_THREADS
90fa17
     r = SSL_write(ssl, buf, len);
90fa17
+    ssl_err = SSL_get_error(ssl, r);
90fa17
     Py_END_ALLOW_THREADS
90fa17
 
90fa17
 
90fa17
-    switch (SSL_get_error(ssl, r)) {
90fa17
+    switch (ssl_err) {
90fa17
         case SSL_ERROR_NONE:
90fa17
         case SSL_ERROR_ZERO_RETURN:
90fa17
             ret = r;
2ca359
@@ -605,20 +726,17 @@
90fa17
         case SSL_ERROR_WANT_WRITE:
90fa17
         case SSL_ERROR_WANT_READ:
90fa17
         case SSL_ERROR_WANT_X509_LOOKUP:
90fa17
+            if (timeout <= 0) {
90fa17
+                ret = -1;
90fa17
+                break;
90fa17
+            }
90fa17
+            if (ssl_sleep_with_timeout(ssl, &tv, timeout, ssl_err) == 0)
90fa17
+                goto again;
90fa17
             ret = -1;
90fa17
             break;
90fa17
         case SSL_ERROR_SSL:
90fa17
-            PyErr_SetString(_ssl_err, ERR_reason_error_string(ERR_get_error()));
90fa17
-            ret = -1;
90fa17
-            break;
90fa17
         case SSL_ERROR_SYSCALL:
90fa17
-            err = ERR_get_error();
90fa17
-            if (err)
90fa17
-                PyErr_SetString(_ssl_err, ERR_reason_error_string(ERR_get_error()));
90fa17
-            else if (r == 0)
90fa17
-                PyErr_SetString(_ssl_err, "unexpected eof");
90fa17
-            else if (r == -1)
90fa17
-                PyErr_SetFromErrno(_ssl_err);
90fa17
+            ssl_handle_error(ssl_err, r);
90fa17
         default:
90fa17
             ret = -1;
90fa17
     }
2ca359
diff -urN M2Crypto/tests/test_ssl.py M2Crypto-0.21.1/tests/test_ssl.py
2ca359
--- M2Crypto/tests/test_ssl.py	2013-11-26 20:01:02.582964980 +0100
2ca359
+++ M2Crypto-0.21.1/tests/test_ssl.py	2013-11-26 20:01:33.268937969 +0100
2ca359
@@ -972,6 +972,77 @@
90fa17
 
90fa17
 class TwistedSSLClientTestCase(BaseSSLClientTestCase):
90fa17
 
90fa17
+    def test_timeout(self):
90fa17
+        pid = self.start_server(self.args)
90fa17
+        try:
90fa17
+            ctx = SSL.Context()
90fa17
+            s = SSL.Connection(ctx)
90fa17
+            # Just a really small number so we can timeout
90fa17
+            s.settimeout(0.000000000000000000000000000001)
90fa17
+            self.assertRaises(SSL.SSLTimeoutError, s.connect, self.srv_addr)
90fa17
+            s.close()
90fa17
+        finally:
90fa17
+            self.stop_server(pid)
90fa17
+
90fa17
+    def test_makefile_timeout(self):
90fa17
+        # httpslib uses makefile to read the response
90fa17
+        pid = self.start_server(self.args)
90fa17
+        try:
90fa17
+            from M2Crypto import httpslib
90fa17
+            c = httpslib.HTTPS(srv_host, srv_port)
90fa17
+            c.putrequest('GET', '/')
90fa17
+            c.putheader('Accept', 'text/html')
90fa17
+            c.putheader('Accept', 'text/plain')
90fa17
+            c.endheaders()
90fa17
+            c._conn.sock.settimeout(100)
90fa17
+            err, msg, headers = c.getreply()
90fa17
+            assert err == 200, err
90fa17
+            f = c.getfile()
90fa17
+            data = f.read()
90fa17
+            c.close()
90fa17
+        finally:
90fa17
+            self.stop_server(pid)
90fa17
+        self.failIf(string.find(data, 's_server -quiet -www') == -1)
90fa17
+
90fa17
+    def test_makefile_timeout_fires(self):
2ca359
+        # This is convoluted because (openssl s_server -www) starts writing the
2ca359
+        # response as soon as it receives the first line of the request, so it's
2ca359
+        # possible for it to send the response before the request is sent and
2ca359
+        # there would be no timeout.  So, let the server spend time reading from
2ca359
+        # an empty pipe
2ca359
+        FIFO_NAME = 'test_makefile_timeout_fires_fifo'
2ca359
+        os.mkfifo('tests/' + FIFO_NAME)
2ca359
+        pipe_pid = os.fork()
90fa17
+        try:
2ca359
+            if pipe_pid == 0:
2ca359
+                try:
2ca359
+                    f = open('tests/' + FIFO_NAME, 'w')
2ca359
+                    try:
2ca359
+                        time.sleep(sleepTime + 1)
2ca359
+                        f.write('Content\n')
2ca359
+                    finally:
2ca359
+                        f.close()
2ca359
+                finally:
2ca359
+                    os._exit(0)
2ca359
+            self.args[self.args.index('-www')] = '-WWW'
2ca359
+            pid = self.start_server(self.args)
2ca359
+            try:
2ca359
+                from M2Crypto import httpslib
2ca359
+                c = httpslib.HTTPS(srv_host, srv_port)
2ca359
+                c.putrequest('GET', '/' + FIFO_NAME)
2ca359
+                c.putheader('Accept', 'text/html')
2ca359
+                c.putheader('Accept', 'text/plain')
2ca359
+                c.endheaders()
2ca359
+                c._conn.sock.settimeout(0.0000000001)
2ca359
+                self.assertRaises(socket.timeout, c.getreply)
2ca359
+                c.close()
2ca359
+            finally:
2ca359
+                self.stop_server(pid)
90fa17
+        finally:
2ca359
+            os.kill(pipe_pid, 1)
2ca359
+            os.waitpid(pipe_pid, 0)
2ca359
+            os.unlink('tests/' + FIFO_NAME)
90fa17
+
90fa17
     def test_twisted_wrapper(self):
90fa17
         # Test only when twisted and ZopeInterfaces are present
90fa17
         try: