--- a/src/ztfy/utils/protocol/xmlrpc.py Wed Jul 04 08:23:44 2012 +0200
+++ b/src/ztfy/utils/protocol/xmlrpc.py Wed Jul 04 08:25:39 2012 +0200
@@ -4,6 +4,9 @@
# Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net>
# All Rights Reserved.
#
+# Python 2.6/2.7 compatibility code copied from EULExistDB
+# (https://github.com/emory-libraries/eulexistdb)
+#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
@@ -17,6 +20,8 @@
# import standard packages
import base64
import cookielib
+import httplib
+import socket
import urllib2
import xmlrpclib
@@ -29,14 +34,63 @@
# import local packages
+class TimeoutHTTP(httplib.HTTP):
+ def __init__(self, host='', port=None, strict=None, timeout=None):
+ if port == 0:
+ port = None
+ self._setup(self._connection_class(host, port, strict, timeout))
+
+class TimeoutHTTPS(httplib.HTTPS):
+ def __init__(self, host='', port=None, strict=None, timeout=None):
+ if port == 0:
+ port = None
+ self._setup(self._connection_class(host, port, strict, timeout))
+
+
class XMLRPCCookieAuthTransport(xmlrpclib.Transport):
"""An XML-RPC transport handling authentication via cookies"""
- def __init__(self, user_agent, credentials=(), cookies=None):
+ _http_connection = httplib.HTTPConnection
+ _http_connection_compat = TimeoutHTTP
+
+ def __init__(self, user_agent, credentials=(), cookies=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
xmlrpclib.Transport.__init__(self)
self.user_agent = user_agent
self.credentials = credentials
self.cookies = cookies
+ self.timeout = timeout
+ if self._connection_required_compat():
+ self.make_connection = self._make_connection_compat
+
+ def _connection_required_compat(self):
+ # Compatibility code copied from EULExistDB (https://github.com/emory-libraries/eulexistdb)
+ # UGLY HACK ALERT. Python 2.7 xmlrpclib caches connection objects in
+ # self._connection (and sets self._connection in __init__). Python
+ # 2.6 and earlier has no such cache. Thus, if self._connection
+ # exists, we're running the newer-style, and if it doesn't then
+ # we're running older-style and thus need compatibility mode.
+ try:
+ self._connection
+ return False
+ except AttributeError:
+ return True
+
+ def make_connection(self, host):
+ # This is the make_connection that runs under Python 2.7 and newer.
+ # The code is pulled straight from 2.7 xmlrpclib, except replacing
+ # HTTPConnection with self._http_connection
+ if self._connection and host == self._connection[0]:
+ return self._connection[1]
+ chost, self._extra_headers, _x509 = self.get_host_info(host)
+ self._connection = host, self._http_connection(chost, timeout=self.timeout)
+ return self._connection[1]
+
+ def _make_connection_compat(self, host):
+ # This method runs as make_connection under Python 2.6 and older.
+ # __init__ detects which version we need and pastes this method
+ # directly into self.make_connection if necessary.
+ host, _extra_headers, _x509 = self.get_host_info(host)
+ return self._http_connection_compat(host, timeout=self.timeout)
# override the send_host hook to also send authentication info
def send_host(self, connection, host):
@@ -69,8 +123,8 @@
self.send_request(connection, handler, request_body)
self.send_host(connection, host)
self.send_user_agent(connection)
+ self.send_content(connection, request_body)
# get response
- self.send_content(connection, request_body)
errcode, errmsg, headers = connection.getreply()
# extract cookies from response headers
crequest = CookieRequest('http://%s/' % host)
@@ -86,13 +140,26 @@
return self._parse_response(connection.getfile(), sock)
-def getClient(uri, credentials=(), verbose=False):
+class SecureXMLRPCCookieAuthTransport(XMLRPCCookieAuthTransport):
+ """Secure XML-RPC transport"""
+
+ _http_connection = httplib.HTTPSConnection
+ _http_connection_compat = TimeoutHTTPS
+
+
+def getClient(uri, credentials=(), verbose=False, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
"""Get an XML-RPC client which supports basic authentication"""
- transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY basic implementation)', credentials)
+ if uri.startswith('https:'):
+ transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure transport)', credentials, timeout=timeout)
+ else:
+ transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY basic transport)', credentials, timeout=timeout)
return xmlrpclib.Server(uri, transport=transport, verbose=verbose)
-def getClientWithCookies(uri, credentials=(), verbose=False):
+def getClientWithCookies(uri, credentials=(), verbose=False, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
"""Get an XML-RPC client which supports authentication throught cookies"""
- transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY cookie implementation)', credentials, cookielib.CookieJar())
+ if uri.startswith('https:'):
+ transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure cookie transport)', credentials, cookielib.CookieJar(), timeout)
+ else:
+ transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure cookie transport)', credentials, cookielib.CookieJar(), timeout)
return xmlrpclib.Server(uri, transport=transport, verbose=verbose)