1 ### -*- coding: utf-8 -*- #################################################### |
1 ### -*- coding: utf-8 -*- #################################################### |
2 ############################################################################## |
2 ############################################################################## |
3 # |
3 # |
4 # Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net> |
4 # Copyright (c) 2008 Thierry Florac <tflorac AT ulthar.net> |
5 # All Rights Reserved. |
5 # All Rights Reserved. |
|
6 # |
|
7 # Python 2.6/2.7 compatibility code copied from EULExistDB |
|
8 # (https://github.com/emory-libraries/eulexistdb) |
6 # |
9 # |
7 # This software is subject to the provisions of the Zope Public License, |
10 # This software is subject to the provisions of the Zope Public License, |
8 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
11 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
9 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
12 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
10 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
13 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
27 # import Zope3 packages |
32 # import Zope3 packages |
28 |
33 |
29 # import local packages |
34 # import local packages |
30 |
35 |
31 |
36 |
|
37 class TimeoutHTTP(httplib.HTTP): |
|
38 def __init__(self, host='', port=None, strict=None, timeout=None): |
|
39 if port == 0: |
|
40 port = None |
|
41 self._setup(self._connection_class(host, port, strict, timeout)) |
|
42 |
|
43 class TimeoutHTTPS(httplib.HTTPS): |
|
44 def __init__(self, host='', port=None, strict=None, timeout=None): |
|
45 if port == 0: |
|
46 port = None |
|
47 self._setup(self._connection_class(host, port, strict, timeout)) |
|
48 |
|
49 |
32 class XMLRPCCookieAuthTransport(xmlrpclib.Transport): |
50 class XMLRPCCookieAuthTransport(xmlrpclib.Transport): |
33 """An XML-RPC transport handling authentication via cookies""" |
51 """An XML-RPC transport handling authentication via cookies""" |
34 |
52 |
35 def __init__(self, user_agent, credentials=(), cookies=None): |
53 _http_connection = httplib.HTTPConnection |
|
54 _http_connection_compat = TimeoutHTTP |
|
55 |
|
56 def __init__(self, user_agent, credentials=(), cookies=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): |
36 xmlrpclib.Transport.__init__(self) |
57 xmlrpclib.Transport.__init__(self) |
37 self.user_agent = user_agent |
58 self.user_agent = user_agent |
38 self.credentials = credentials |
59 self.credentials = credentials |
39 self.cookies = cookies |
60 self.cookies = cookies |
|
61 self.timeout = timeout |
|
62 if self._connection_required_compat(): |
|
63 self.make_connection = self._make_connection_compat |
|
64 |
|
65 def _connection_required_compat(self): |
|
66 # Compatibility code copied from EULExistDB (https://github.com/emory-libraries/eulexistdb) |
|
67 # UGLY HACK ALERT. Python 2.7 xmlrpclib caches connection objects in |
|
68 # self._connection (and sets self._connection in __init__). Python |
|
69 # 2.6 and earlier has no such cache. Thus, if self._connection |
|
70 # exists, we're running the newer-style, and if it doesn't then |
|
71 # we're running older-style and thus need compatibility mode. |
|
72 try: |
|
73 self._connection |
|
74 return False |
|
75 except AttributeError: |
|
76 return True |
|
77 |
|
78 def make_connection(self, host): |
|
79 # This is the make_connection that runs under Python 2.7 and newer. |
|
80 # The code is pulled straight from 2.7 xmlrpclib, except replacing |
|
81 # HTTPConnection with self._http_connection |
|
82 if self._connection and host == self._connection[0]: |
|
83 return self._connection[1] |
|
84 chost, self._extra_headers, _x509 = self.get_host_info(host) |
|
85 self._connection = host, self._http_connection(chost, timeout=self.timeout) |
|
86 return self._connection[1] |
|
87 |
|
88 def _make_connection_compat(self, host): |
|
89 # This method runs as make_connection under Python 2.6 and older. |
|
90 # __init__ detects which version we need and pastes this method |
|
91 # directly into self.make_connection if necessary. |
|
92 host, _extra_headers, _x509 = self.get_host_info(host) |
|
93 return self._http_connection_compat(host, timeout=self.timeout) |
40 |
94 |
41 # override the send_host hook to also send authentication info |
95 # override the send_host hook to also send authentication info |
42 def send_host(self, connection, host): |
96 def send_host(self, connection, host): |
43 xmlrpclib.Transport.send_host(self, connection, host) |
97 xmlrpclib.Transport.send_host(self, connection, host) |
44 if (self.cookies is not None) and (len(self.cookies) > 0): |
98 if (self.cookies is not None) and (len(self.cookies) > 0): |
67 if verbose: |
121 if verbose: |
68 connection.set_debuglevel(1) |
122 connection.set_debuglevel(1) |
69 self.send_request(connection, handler, request_body) |
123 self.send_request(connection, handler, request_body) |
70 self.send_host(connection, host) |
124 self.send_host(connection, host) |
71 self.send_user_agent(connection) |
125 self.send_user_agent(connection) |
|
126 self.send_content(connection, request_body) |
72 # get response |
127 # get response |
73 self.send_content(connection, request_body) |
|
74 errcode, errmsg, headers = connection.getreply() |
128 errcode, errmsg, headers = connection.getreply() |
75 # extract cookies from response headers |
129 # extract cookies from response headers |
76 crequest = CookieRequest('http://%s/' % host) |
130 crequest = CookieRequest('http://%s/' % host) |
77 cresponse = CookieResponse(headers) |
131 cresponse = CookieResponse(headers) |
78 if self.cookies is not None: |
132 if self.cookies is not None: |
84 except AttributeError: |
138 except AttributeError: |
85 sock = None |
139 sock = None |
86 return self._parse_response(connection.getfile(), sock) |
140 return self._parse_response(connection.getfile(), sock) |
87 |
141 |
88 |
142 |
89 def getClient(uri, credentials=(), verbose=False): |
143 class SecureXMLRPCCookieAuthTransport(XMLRPCCookieAuthTransport): |
|
144 """Secure XML-RPC transport""" |
|
145 |
|
146 _http_connection = httplib.HTTPSConnection |
|
147 _http_connection_compat = TimeoutHTTPS |
|
148 |
|
149 |
|
150 def getClient(uri, credentials=(), verbose=False, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): |
90 """Get an XML-RPC client which supports basic authentication""" |
151 """Get an XML-RPC client which supports basic authentication""" |
91 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY basic implementation)', credentials) |
152 if uri.startswith('https:'): |
|
153 transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure transport)', credentials, timeout=timeout) |
|
154 else: |
|
155 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY basic transport)', credentials, timeout=timeout) |
92 return xmlrpclib.Server(uri, transport=transport, verbose=verbose) |
156 return xmlrpclib.Server(uri, transport=transport, verbose=verbose) |
93 |
157 |
94 |
158 |
95 def getClientWithCookies(uri, credentials=(), verbose=False): |
159 def getClientWithCookies(uri, credentials=(), verbose=False, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): |
96 """Get an XML-RPC client which supports authentication throught cookies""" |
160 """Get an XML-RPC client which supports authentication throught cookies""" |
97 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY cookie implementation)', credentials, cookielib.CookieJar()) |
161 if uri.startswith('https:'): |
|
162 transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure cookie transport)', credentials, cookielib.CookieJar(), timeout) |
|
163 else: |
|
164 transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (ZTFY secure cookie transport)', credentials, cookielib.CookieJar(), timeout) |
98 return xmlrpclib.Server(uri, transport=transport, verbose=verbose) |
165 return xmlrpclib.Server(uri, transport=transport, verbose=verbose) |