8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
10 # FOR A PARTICULAR PURPOSE. |
10 # FOR A PARTICULAR PURPOSE. |
11 # |
11 # |
12 |
12 |
|
13 """PyAMS_utils.protocol.xmlrpc module |
|
14 |
|
15 This module provides a few set of classes and functions usable to improve XML-RPC client usage. |
|
16 |
|
17 It provides custom transports and allows storage of response cookies |
|
18 """ |
|
19 |
13 import base64 |
20 import base64 |
14 import http.client |
21 import http.client |
15 import http.cookiejar |
22 import http.cookiejar |
16 import socket |
23 import socket |
17 import urllib.request |
24 import urllib.request |
18 import xmlrpc.client |
25 import xmlrpc.client |
19 |
|
20 |
26 |
21 try: |
27 try: |
22 import gzip |
28 import gzip |
23 except ImportError: |
29 except ImportError: |
24 gzip = None # python can be built without zlib/gzip support |
30 gzip = None # python can be built without zlib/gzip support |
25 |
31 |
26 __docformat__ = 'restructuredtext' |
32 __docformat__ = 'restructuredtext' |
27 |
33 |
28 |
34 |
29 class XMLRPCCookieAuthTransport(xmlrpc.client.Transport): |
35 class XMLRPCCookieAuthTransport(xmlrpc.client.Transport): |
|
36 # pylint: disable=too-many-instance-attributes |
30 """An XML-RPC transport handling authentication via cookies""" |
37 """An XML-RPC transport handling authentication via cookies""" |
31 |
38 |
32 _http_connection = http.client.HTTPConnection |
39 _http_connection = http.client.HTTPConnection |
33 verbose = False |
40 verbose = False |
34 |
41 |
35 def __init__(self, user_agent, credentials=(), cookies=None, |
42 def __init__(self, user_agent, credentials=(), cookies=None, |
36 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
43 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
|
44 # pylint: disable=protected-access,too-many-arguments |
37 xmlrpc.client.Transport.__init__(self) |
45 xmlrpc.client.Transport.__init__(self) |
38 self.user_agent = user_agent |
46 self.user_agent = user_agent |
39 self.credentials = credentials |
47 self.credentials = credentials |
40 self.cookies = cookies |
48 self.cookies = cookies |
41 self.timeout = timeout |
49 self.timeout = timeout |
73 self.send_user_agent(connection) |
81 self.send_user_agent(connection) |
74 self.send_headers(connection, headers) |
82 self.send_headers(connection, headers) |
75 self.send_content(connection, request_body) |
83 self.send_content(connection, request_body) |
76 return connection |
84 return connection |
77 |
85 |
78 # override the send_host hook to also send authentication info |
|
79 def send_auth(self, connection): |
86 def send_auth(self, connection): |
|
87 """Override the send_host hook to also send authentication info""" |
80 if (self.cookies is not None) and (len(self.cookies) > 0): |
88 if (self.cookies is not None) and (len(self.cookies) > 0): |
81 for cookie in self.cookies: |
89 for cookie in self.cookies: |
82 connection.putheader('Cookie', '%s=%s' % (cookie.name, cookie.value)) |
90 connection.putheader('Cookie', '%s=%s' % (cookie.name, cookie.value)) |
83 elif self.credentials: |
91 elif self.credentials: |
84 creds = base64.encodebytes(("%s:%s" % self.credentials).encode()).strip().decode() |
92 creds = base64.encodebytes(("%s:%s" % self.credentials).encode()).strip().decode() |
85 auth = 'Basic %s' % creds |
93 auth = 'Basic %s' % creds |
86 connection.putheader('Authorization', auth) |
94 connection.putheader('Authorization', auth) |
87 |
95 |
88 # send content type |
96 @staticmethod |
89 def send_content_type(self, connection): |
97 def send_content_type(connection): |
|
98 """Send content type""" |
90 connection.putheader('Content-Type', 'text/xml') |
99 connection.putheader('Content-Type', 'text/xml') |
91 |
100 |
92 # send user agent |
|
93 def send_user_agent(self, connection): |
101 def send_user_agent(self, connection): |
|
102 """Send user agent""" |
94 connection.putheader('User-Agent', self.user_agent) |
103 connection.putheader('User-Agent', self.user_agent) |
95 |
104 |
96 # send custom headers |
|
97 def send_headers(self, connection, headers): |
105 def send_headers(self, connection, headers): |
|
106 """Send custom headers""" |
98 xmlrpc.client.Transport.send_headers(self, connection, headers) |
107 xmlrpc.client.Transport.send_headers(self, connection, headers) |
99 for k, v in (self.headers or {}).items(): |
108 for key, value in (self.headers or {}).items(): |
100 connection.putheader(k, v) |
109 connection.putheader(key, value) |
101 |
110 |
102 # dummy request class for extracting cookies |
|
103 class CookieRequest(urllib.request.Request): |
111 class CookieRequest(urllib.request.Request): |
104 pass |
112 """Dummy request class used for extracting cookies""" |
105 |
113 |
106 # dummy response info headers helper |
|
107 class CookieResponseHelper: |
114 class CookieResponseHelper: |
|
115 """Dummy response headers helper""" |
|
116 |
108 def __init__(self, response): |
117 def __init__(self, response): |
109 self.response = response |
118 self.response = response |
110 |
119 |
111 def getheaders(self, header): |
120 def getheaders(self, header): |
|
121 """Get response headers""" |
112 return self.response.msg.getallmatchingheaders(header) |
122 return self.response.msg.getallmatchingheaders(header) |
113 |
123 |
114 # dummy response class for extracting cookies |
|
115 class CookieResponse: |
124 class CookieResponse: |
|
125 """Dummy response class used to extract cookies""" |
|
126 |
116 def __init__(self, response): |
127 def __init__(self, response): |
117 self.response = response |
128 self.response = response |
118 |
129 |
119 def info(self): |
130 def info(self): |
|
131 """Get response info from cookies""" |
120 return XMLRPCCookieAuthTransport.CookieResponseHelper(self.response) |
132 return XMLRPCCookieAuthTransport.CookieResponseHelper(self.response) |
121 |
133 |
122 def get_response(self, connection, host, handler): |
134 def get_response(self, connection, host, handler): |
|
135 """Get server response""" |
123 response = connection.getresponse() |
136 response = connection.getresponse() |
124 # extract cookies from response headers |
137 # extract cookies from response headers |
125 if self.cookies is not None: |
138 if self.cookies is not None: |
126 crequest = XMLRPCCookieAuthTransport.CookieRequest('http://%s/' % host) |
139 crequest = XMLRPCCookieAuthTransport.CookieRequest('http://%s/' % host) |
127 cresponse = XMLRPCCookieAuthTransport.CookieResponse(response) |
140 cresponse = XMLRPCCookieAuthTransport.CookieResponse(response) |
141 _http_connection = http.client.HTTPSConnection |
154 _http_connection = http.client.HTTPSConnection |
142 |
155 |
143 |
156 |
144 def get_client(uri, credentials=(), verbose=False, allow_none=0, |
157 def get_client(uri, credentials=(), verbose=False, allow_none=0, |
145 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
158 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None): |
|
159 # pylint: disable=protected-access,too-many-arguments |
146 """Get an XML-RPC client which supports basic authentication""" |
160 """Get an XML-RPC client which supports basic authentication""" |
147 if uri.startswith('https:'): |
161 if uri.startswith('https:'): |
148 transport = SecureXMLRPCCookieAuthTransport( |
162 transport = SecureXMLRPCCookieAuthTransport( |
149 'Python XML-RPC Client/0.1 (PyAMS secure transport)', credentials, |
163 'Python XML-RPC Client/0.1 (PyAMS secure transport)', credentials, |
150 timeout=timeout, headers=headers) |
164 timeout=timeout, headers=headers) |
154 return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none) |
168 return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none) |
155 |
169 |
156 |
170 |
157 def get_client_with_cookies(uri, credentials=(), verbose=False, allow_none=0, |
171 def get_client_with_cookies(uri, credentials=(), verbose=False, allow_none=0, |
158 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None, cookies=None): |
172 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None, cookies=None): |
|
173 # pylint: disable=protected-access,too-many-arguments |
159 """Get an XML-RPC client which supports authentication through cookies""" |
174 """Get an XML-RPC client which supports authentication through cookies""" |
160 if cookies is None: |
175 if cookies is None: |
161 cookies = http.cookiejar.CookieJar() |
176 cookies = http.cookiejar.CookieJar() |
162 if uri.startswith('https:'): |
177 if uri.startswith('https:'): |
163 transport = SecureXMLRPCCookieAuthTransport( |
178 transport = SecureXMLRPCCookieAuthTransport( |