From beba56ae8dcfac14fb6c945a1821e0aaf45a00ef Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 17 Jun 2010 16:48:43 +0200
Subject: [PATCH] RAPI client: Add support for Python 2.6

The httplib module used by urllib2 requires its sockets to have a
makefile() method to provide a file-like interface (or rather
file-in-Python-like) to the socket. PyOpenSSL doesn't implement
makefile() as the semantics require files to call dup(2) on the
underlying file descriptors, something not easily done on SSL sockets.

Python up to and including 2.5 have a class to simulate makefile(),
httplib.FakeSocket. With the addition of SSL support in Python 2.6, this
class was deprecated and no longer functions.

This patch adds a new, simpler wrapper class which is used in Python 2.6
and above only. It's good enough for this use.

There are general problems in these generic wrapper classes--none of
them handles SSL I/O properly. They break, for example, when the server
requests a renegotiation. This will need more work.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
---
 lib/rapi/client.py | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/lib/rapi/client.py b/lib/rapi/client.py
index 49affb2e6..80d13d5e4 100644
--- a/lib/rapi/client.py
+++ b/lib/rapi/client.py
@@ -24,6 +24,7 @@
 # No Ganeti-specific modules should be imported. The RAPI client is supposed to
 # be standalone.
 
+import sys
 import httplib
 import urllib2
 import logging
@@ -198,6 +199,10 @@ class _HTTPSConnectionOpenSSL(httplib.HTTPSConnection):
   """HTTPS Connection handler that verifies the SSL certificate.
 
   """
+  # Python before version 2.6 had its own httplib.FakeSocket wrapper for
+  # sockets
+  _SUPPORT_FAKESOCKET = (sys.hexversion < 0x2060000)
+
   def __init__(self, *args, **kwargs):
     """Initializes this class.
 
@@ -236,7 +241,35 @@ class _HTTPSConnectionOpenSSL(httplib.HTTPSConnection):
     ssl = OpenSSL.SSL.Connection(ctx, sock)
     ssl.connect((self.host, self.port))
 
-    self.sock = httplib.FakeSocket(sock, ssl)
+    if self._SUPPORT_FAKESOCKET:
+      self.sock = httplib.FakeSocket(sock, ssl)
+    else:
+      self.sock = _SslSocketWrapper(ssl)
+
+
+class _SslSocketWrapper(object):
+  def __init__(self, sock):
+    """Initializes this class.
+
+    """
+    self._sock = sock
+
+  def __getattr__(self, name):
+    """Forward everything to underlying socket.
+
+    """
+    return getattr(self._sock, name)
+
+  def makefile(self, mode, bufsize):
+    """Fake makefile method.
+
+    makefile() on normal file descriptors uses dup2(2), which doesn't work with
+    SSL sockets and therefore is not implemented by pyOpenSSL. This fake method
+    works with the httplib module, but might not work for other modules.
+
+    """
+    # pylint: disable-msg=W0212
+    return socket._fileobject(self._sock, mode, bufsize)
 
 
 class _HTTPSHandler(urllib2.HTTPSHandler):
-- 
GitLab