source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/pysimplesoap/transport.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 10.0 KB
Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published by the
5# Free Software Foundation; either version 3, or (at your option) any later
6# version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11# for more details.
12
13"""Pythonic simple SOAP Client transport"""
14
15
16import logging
17import ssl
18import sys
19try:
20    import urllib2
21    from cookielib import CookieJar
22except ImportError:
23    from urllib import request as urllib2
24    from http.cookiejar import CookieJar
25
26from . import __author__, __copyright__, __license__, __version__, TIMEOUT
27from .simplexml import SimpleXMLElement, TYPE_MAP, Struct
28
29log = logging.getLogger(__name__)
30
31#
32# Socket wrapper to enable socket.TCP_NODELAY - this greatly speeds up transactions in Linux
33# WARNING: this will modify the standard library socket module, use with care!
34# TODO: implement this as a transport faciliy
35#       (to pass options directly to httplib2 or pycurl)
36#       be aware of metaclasses and socks.py (SocksiPy) used by httplib2
37
38if False:
39    import socket
40    realsocket = socket.socket
41    def socketwrap(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
42        sockobj = realsocket(family, type, proto)
43        if type == socket.SOCK_STREAM:
44            sockobj.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
45        return sockobj
46    socket.socket = socketwrap
47
48#
49# We store metadata about what available transport mechanisms we have available.
50#
51_http_connectors = {}  # libname: classimpl mapping
52_http_facilities = {}  # functionalitylabel: [sequence of libname] mapping
53
54
55class TransportBase:
56    @classmethod
57    def supports_feature(cls, feature_name):
58        return cls._wrapper_name in _http_facilities[feature_name]
59
60#
61# httplib2 support.
62#
63try:
64    import httplib2
65    if sys.version > '3' and httplib2.__version__ <= "0.7.7":
66        import http.client
67        # httplib2 workaround: check_hostname needs a SSL context with either
68        #                      CERT_OPTIONAL or CERT_REQUIRED
69        # see https://code.google.com/p/httplib2/issues/detail?id=173
70        orig__init__ = http.client.HTTPSConnection.__init__
71        def fixer(self, host, port, key_file, cert_file, timeout, context,
72                        check_hostname, *args, **kwargs):
73            chk = kwargs.get('disable_ssl_certificate_validation', True) ^ True
74            orig__init__(self, host, port=port, key_file=key_file,
75                cert_file=cert_file, timeout=timeout, context=context,
76                check_hostname=chk)
77        http.client.HTTPSConnection.__init__ = fixer
78except ImportError:
79    TIMEOUT = None  # timeout not supported by urllib2
80    pass
81else:
82    class Httplib2Transport(httplib2.Http, TransportBase):
83        _wrapper_version = "httplib2 %s" % httplib2.__version__
84        _wrapper_name = 'httplib2'
85
86        def __init__(self, timeout, proxy=None, cacert=None, sessions=False):
87#            httplib2.debuglevel=4
88            kwargs = {}
89            if proxy:
90                import socks
91                kwargs['proxy_info'] = httplib2.ProxyInfo(proxy_type=socks.PROXY_TYPE_HTTP, **proxy)
92                log.info("using proxy %s" % proxy)
93
94            # set optional parameters according to supported httplib2 version
95            if httplib2.__version__ >= '0.3.0':
96                kwargs['timeout'] = timeout
97            if httplib2.__version__ >= '0.7.0':
98                kwargs['disable_ssl_certificate_validation'] = cacert is None
99                kwargs['ca_certs'] = cacert
100            httplib2.Http.__init__(self, **kwargs)
101
102    _http_connectors['httplib2'] = Httplib2Transport
103    _http_facilities.setdefault('proxy', []).append('httplib2')
104    _http_facilities.setdefault('cacert', []).append('httplib2')
105
106    import inspect
107    if 'timeout' in inspect.getargspec(httplib2.Http.__init__)[0]:
108        _http_facilities.setdefault('timeout', []).append('httplib2')
109
110
111#
112# urllib2 support.
113#
114class urllib2Transport(TransportBase):
115    _wrapper_version = "urllib2 %s" % urllib2.__version__
116    _wrapper_name = 'urllib2'
117
118    def __init__(self, timeout=None, proxy=None, cacert=None, sessions=False):
119        if (timeout is not None) and not self.supports_feature('timeout'):
120            raise RuntimeError('timeout is not supported with urllib2 transport')
121        if proxy:
122            raise RuntimeError('proxy is not supported with urllib2 transport')
123        if cacert:
124            raise RuntimeError('cacert is not support with urllib2 transport')
125       
126        handlers = []
127
128        if ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or
129            (sys.version_info[0] == 3 and sys.version_info >= (3,2,0))):
130            context = ssl.create_default_context()
131            context.check_hostname = False
132            context.verify_mode = ssl.CERT_NONE
133            handlers.append(urllib2.HTTPSHandler(context=context))
134       
135        if sessions:
136            handlers.append(urllib2.HTTPCookieProcessor(CookieJar()))
137       
138        opener = urllib2.build_opener(*handlers)
139        self.request_opener = opener.open
140        self._timeout = timeout
141
142    def request(self, url, method="GET", body=None, headers={}):
143        req = urllib2.Request(url, body, headers)
144        try:
145            f = self.request_opener(req, timeout=self._timeout)
146            return f.info(), f.read()
147        except urllib2.HTTPError as f:
148            if f.code != 500:
149                raise
150            return f.info(), f.read()
151
152_http_connectors['urllib2'] = urllib2Transport
153_http_facilities.setdefault('sessions', []).append('urllib2')
154
155if sys.version_info >= (2, 6):
156    _http_facilities.setdefault('timeout', []).append('urllib2')
157
158#
159# pycurl support.
160# experimental: pycurl seems faster + better proxy support (NTLM) + ssl features
161#
162try:
163    import pycurl
164except ImportError:
165    pass
166else:
167    try:
168        from cStringIO import StringIO
169    except ImportError:
170        try:
171            from StringIO import StringIO
172        except ImportError:
173            from io import StringIO
174
175    class pycurlTransport(TransportBase):
176        _wrapper_version = pycurl.version
177        _wrapper_name = 'pycurl'
178
179        def __init__(self, timeout, proxy=None, cacert=None, sessions=False):
180            self.timeout = timeout
181            self.proxy = proxy or {}
182            self.cacert = cacert
183
184        def request(self, url, method, body, headers):
185            c = pycurl.Curl()
186            c.setopt(pycurl.URL, url)
187            if 'proxy_host' in self.proxy:
188                c.setopt(pycurl.PROXY, self.proxy['proxy_host'])
189            if 'proxy_port' in self.proxy:
190                c.setopt(pycurl.PROXYPORT, self.proxy['proxy_port'])
191            if 'proxy_user' in self.proxy:
192                c.setopt(pycurl.PROXYUSERPWD, "%(proxy_user)s:%(proxy_pass)s" % self.proxy)
193            self.buf = StringIO()
194            c.setopt(pycurl.WRITEFUNCTION, self.buf.write)
195            #c.setopt(pycurl.READFUNCTION, self.read)
196            #self.body = StringIO(body)
197            #c.setopt(pycurl.HEADERFUNCTION, self.header)
198            if self.cacert:
199                c.setopt(c.CAINFO, self.cacert)
200            c.setopt(pycurl.SSL_VERIFYPEER, self.cacert and 1 or 0)
201            c.setopt(pycurl.SSL_VERIFYHOST, self.cacert and 2 or 0)
202            c.setopt(pycurl.CONNECTTIMEOUT, self.timeout)
203            c.setopt(pycurl.TIMEOUT, self.timeout)
204            if method == 'POST':
205                c.setopt(pycurl.POST, 1)
206                c.setopt(pycurl.POSTFIELDS, body)
207            if headers:
208                hdrs = ['%s: %s' % (k, v) for k, v in headers.items()]
209                log.debug(hdrs)
210                c.setopt(pycurl.HTTPHEADER, hdrs)
211            c.perform()
212            c.close()
213            return {}, self.buf.getvalue()
214
215    _http_connectors['pycurl'] = pycurlTransport
216    _http_facilities.setdefault('proxy', []).append('pycurl')
217    _http_facilities.setdefault('cacert', []).append('pycurl')
218    _http_facilities.setdefault('timeout', []).append('pycurl')
219
220
221class DummyTransport:
222    """Testing class to load a xml response"""
223
224    def __init__(self, xml_response):
225        self.xml_response = xml_response
226
227    def request(self, location, method, body, headers):
228        log.debug("%s %s", method, location)
229        log.debug(headers)
230        log.debug(body)
231        return {}, self.xml_response
232
233
234def get_http_wrapper(library=None, features=[]):
235    # If we are asked for a specific library, return it.
236    if library is not None:
237        try:
238            return _http_connectors[library]
239        except KeyError:
240            raise RuntimeError('%s transport is not available' % (library,))
241
242    # If we haven't been asked for a specific feature either, then just return our favourite
243    # implementation.
244    if not features:
245        return _http_connectors.get('httplib2', _http_connectors['urllib2'])
246
247    # If we are asked for a connector which supports the given features, then we will
248    # try that.
249    current_candidates = _http_connectors.keys()
250    new_candidates = []
251    for feature in features:
252        for candidate in current_candidates:
253            if candidate in _http_facilities.get(feature, []):
254                new_candidates.append(candidate)
255        current_candidates = new_candidates
256        new_candidates = []
257
258    # Return the first candidate in the list.
259    try:
260        candidate_name = current_candidates[0]
261    except IndexError:
262        raise RuntimeError("no transport available which supports these features: %s" % (features,))
263    else:
264        return _http_connectors[candidate_name]
265
266
267def set_http_wrapper(library=None, features=[]):
268    """Set a suitable HTTP connection wrapper."""
269    global Http
270    Http = get_http_wrapper(library, features)
271    return Http
272
273
274def get_Http():
275    """Return current transport class"""
276    global Http
277    return Http
278
279
280# define the default HTTP connection class (it can be changed at runtime!):
281set_http_wrapper()
Note: See TracBrowser for help on using the repository browser.