src/ztfy/utils/protocol/xmlrpc.py
branchZTK-1.1
changeset 160 5fdc01c95cad
parent 148 d3668ecd9137
child 161 33b95dfd6142
equal deleted inserted replaced
159:6404eb9f238d 160:5fdc01c95cad
     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
    15 
    18 
    16 
    19 
    17 # import standard packages
    20 # import standard packages
    18 import base64
    21 import base64
    19 import cookielib
    22 import cookielib
       
    23 import httplib
       
    24 import socket
    20 import urllib2
    25 import urllib2
    21 import xmlrpclib
    26 import xmlrpclib
    22 
    27 
    23 # import Zope3 interfaces
    28 # import Zope3 interfaces
    24 
    29 
    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)