src/pyams_utils/protocol/xmlrpc.py
changeset 1 3f89629b9e54
child 38 60b0a6b21a12
equal deleted inserted replaced
0:16d47bd81d84 1:3f89629b9e54
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 
       
    16 # import standard library
       
    17 import base64
       
    18 import http.client
       
    19 import http.cookiejar
       
    20 import socket
       
    21 import urllib.request
       
    22 import xmlrpc.client
       
    23 
       
    24 # import interfaces
       
    25 
       
    26 # import packages
       
    27 
       
    28 
       
    29 class XMLRPCCookieAuthTransport(xmlrpc.client.Transport):
       
    30     """An XML-RPC transport handling authentication via cookies"""
       
    31 
       
    32     _http_connection = http.client.HTTPConnection
       
    33 
       
    34     def __init__(self, user_agent, credentials=(), cookies=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None):
       
    35         xmlrpc.client.Transport.__init__(self)
       
    36         self.user_agent = user_agent
       
    37         self.credentials = credentials
       
    38         self.cookies = cookies
       
    39         self.timeout = timeout
       
    40         self.headers = headers
       
    41 
       
    42     def make_connection(self, host):
       
    43         # This is the make_connection that runs under Python 2.7 and newer.
       
    44         # The code is pulled straight from 2.7 xmlrpclib, except replacing
       
    45         # HTTPConnection with self._http_connection
       
    46         if self._connection and host == self._connection[0]:
       
    47             return self._connection[1]
       
    48         chost, self._extra_headers, _x509 = self.get_host_info(host)
       
    49         self._connection = host, self._http_connection(chost, timeout=self.timeout)
       
    50         return self._connection[1]
       
    51 
       
    52     # override the send_host hook to also send authentication info
       
    53     def send_host(self, connection, host):
       
    54         connection.putheader('Host', host)
       
    55         if (self.cookies is not None) and (len(self.cookies) > 0):
       
    56             for cookie in self.cookies:
       
    57                 connection.putheader('Cookie', '%s=%s' % (cookie.name, cookie.value))
       
    58         elif self.credentials:
       
    59             auth = 'Basic %s' % base64.encodebytes("%s:%s" % self.credentials).strip()
       
    60             connection.putheader('Authorization', auth)
       
    61 
       
    62     # send user agent
       
    63     def send_user_agent(self, connection):
       
    64         connection.putheader('User-Agent', self.user_agent)
       
    65 
       
    66     # send custom headers
       
    67     def send_headers(self, connection, headers):
       
    68         xmlrpc.client.Transport.send_headers(self, connection, headers)
       
    69         for k, v in (self.headers or {}).iteritems():
       
    70             connection.putheader(k, v)
       
    71 
       
    72     # dummy request class for extracting cookies
       
    73     class CookieRequest(urllib.request.Request):
       
    74         pass
       
    75 
       
    76     # dummy response info headers helper
       
    77     class CookieResponseHelper:
       
    78         def __init__(self, response):
       
    79             self.response = response
       
    80         def getheaders(self, header):
       
    81             return self.response.msg.getallmatchingheaders(header)
       
    82 
       
    83     # dummy response class for extracting cookies
       
    84     class CookieResponse:
       
    85         def __init__(self, response):
       
    86             self.response = response
       
    87         def info(self):
       
    88             return XMLRPCCookieAuthTransport.CookieResponseHelper(self.response)
       
    89 
       
    90     def request(self, host, handler, request_body, verbose=False):
       
    91         # issue XML-RPC request
       
    92         connection = self.make_connection(host)
       
    93         self.verbose = verbose
       
    94         if verbose:
       
    95             connection.set_debuglevel(1)
       
    96         self.send_request(connection, handler, request_body)
       
    97         self.send_host(connection, host)
       
    98         self.send_user_agent(connection)
       
    99         self.send_headers(connection)
       
   100         self.send_content(connection, request_body)
       
   101         # get response
       
   102         return self.get_response(connection, host, handler)
       
   103 
       
   104     def get_response(self, connection, host, handler):
       
   105         response = connection.getresponse()
       
   106         # extract cookies from response headers
       
   107         if self.cookies is not None:
       
   108             crequest = XMLRPCCookieAuthTransport.CookieRequest('http://%s/' % host)
       
   109             cresponse = XMLRPCCookieAuthTransport.CookieResponse(response)
       
   110             self.cookies.extract_cookies(cresponse, crequest)
       
   111         if response.status != 200:
       
   112             raise xmlrpc.client.ProtocolError(host + handler, response.status, response.reason, response.getheaders())
       
   113         return self.parse_response(response)
       
   114 
       
   115 
       
   116 class SecureXMLRPCCookieAuthTransport(XMLRPCCookieAuthTransport):
       
   117     """Secure XML-RPC transport"""
       
   118 
       
   119     _http_connection = http.client.HTTPSConnection
       
   120 
       
   121 
       
   122 def get_client(uri, credentials=(), verbose=False, allow_none=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, headers=None):
       
   123     """Get an XML-RPC client which supports basic authentication"""
       
   124     if uri.startswith('https:'):
       
   125         transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS secure transport)', credentials,
       
   126                                                     timeout=timeout, headers=headers)
       
   127     else:
       
   128         transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS basic transport)', credentials,
       
   129                                               timeout=timeout, headers=headers)
       
   130     return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none)
       
   131 
       
   132 
       
   133 def get_client_with_cookies(uri, credentials=(), verbose=False, allow_none=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
       
   134                             headers=None, cookies=None):
       
   135     """Get an XML-RPC client which supports authentication through cookies"""
       
   136     if cookies is None:
       
   137         cookies = http.cookiejar.CookieJar()
       
   138     if uri.startswith('https:'):
       
   139         transport = SecureXMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS secure cookie transport)',
       
   140                                                     credentials, cookies, timeout, headers)
       
   141     else:
       
   142         transport = XMLRPCCookieAuthTransport('Python XML-RPC Client/0.1 (PyAMS basic cookie transport)',
       
   143                                               credentials, cookies, timeout, headers)
       
   144     return xmlrpc.client.Server(uri, transport=transport, verbose=verbose, allow_none=allow_none)