src/pyams_utils/protocol/xmlrpc.py
branchdev-tf
changeset 427 63284c98cdc1
parent 393 225e6576d28d
equal deleted inserted replaced
426:2022e4da3ad9 427:63284c98cdc1
     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(