Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / requests / packages / urllib3 / connection.py @ 564

History | View | Annotate | Download (10 KB)

1
from __future__ import absolute_import
2
import datetime
3
import os
4
import sys
5
import socket
6
from socket import error as SocketError, timeout as SocketTimeout
7
import warnings
8
from .packages import six
9

    
10
try:  # Python 3
11
    from http.client import HTTPConnection as _HTTPConnection
12
    from http.client import HTTPException  # noqa: unused in this module
13
except ImportError:
14
    from httplib import HTTPConnection as _HTTPConnection
15
    from httplib import HTTPException  # noqa: unused in this module
16

    
17
try:  # Compiled with SSL?
18
    import ssl
19
    BaseSSLError = ssl.SSLError
20
except (ImportError, AttributeError):  # Platform-specific: No SSL.
21
    ssl = None
22

    
23
    class BaseSSLError(BaseException):
24
        pass
25

    
26

    
27
try:  # Python 3:
28
    # Not a no-op, we're adding this to the namespace so it can be imported.
29
    ConnectionError = ConnectionError
30
except NameError:  # Python 2:
31
    class ConnectionError(Exception):
32
        pass
33

    
34

    
35
from .exceptions import (
36
    NewConnectionError,
37
    ConnectTimeoutError,
38
    SubjectAltNameWarning,
39
    SystemTimeWarning,
40
)
41
from .packages.ssl_match_hostname import match_hostname
42

    
43
from .util.ssl_ import (
44
    resolve_cert_reqs,
45
    resolve_ssl_version,
46
    ssl_wrap_socket,
47
    assert_fingerprint,
48
)
49

    
50

    
51
from .util import connection
52

    
53
port_by_scheme = {
54
    'http': 80,
55
    'https': 443,
56
}
57

    
58
RECENT_DATE = datetime.date(2014, 1, 1)
59

    
60

    
61
class DummyConnection(object):
62
    """Used to detect a failed ConnectionCls import."""
63
    pass
64

    
65

    
66
class HTTPConnection(_HTTPConnection, object):
67
    """
68
    Based on httplib.HTTPConnection but provides an extra constructor
69
    backwards-compatibility layer between older and newer Pythons.
70

71
    Additional keyword parameters are used to configure attributes of the connection.
72
    Accepted parameters include:
73

74
      - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool`
75
      - ``source_address``: Set the source address for the current connection.
76

77
        .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x
78

79
      - ``socket_options``: Set specific options on the underlying socket. If not specified, then
80
        defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling
81
        Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy.
82

83
        For example, if you wish to enable TCP Keep Alive in addition to the defaults,
84
        you might pass::
85

86
            HTTPConnection.default_socket_options + [
87
                (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
88
            ]
89

90
        Or you may want to disable the defaults by passing an empty list (e.g., ``[]``).
91
    """
92

    
93
    default_port = port_by_scheme['http']
94

    
95
    #: Disable Nagle's algorithm by default.
96
    #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]``
97
    default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
98

    
99
    #: Whether this connection verifies the host's certificate.
100
    is_verified = False
101

    
102
    def __init__(self, *args, **kw):
103
        if six.PY3:  # Python 3
104
            kw.pop('strict', None)
105

    
106
        # Pre-set source_address in case we have an older Python like 2.6.
107
        self.source_address = kw.get('source_address')
108

    
109
        if sys.version_info < (2, 7):  # Python 2.6
110
            # _HTTPConnection on Python 2.6 will balk at this keyword arg, but
111
            # not newer versions. We can still use it when creating a
112
            # connection though, so we pop it *after* we have saved it as
113
            # self.source_address.
114
            kw.pop('source_address', None)
115

    
116
        #: The socket options provided by the user. If no options are
117
        #: provided, we use the default options.
118
        self.socket_options = kw.pop('socket_options', self.default_socket_options)
119

    
120
        # Superclass also sets self.source_address in Python 2.7+.
121
        _HTTPConnection.__init__(self, *args, **kw)
122

    
123
    def _new_conn(self):
124
        """ Establish a socket connection and set nodelay settings on it.
125

126
        :return: New socket connection.
127
        """
128
        extra_kw = {}
129
        if self.source_address:
130
            extra_kw['source_address'] = self.source_address
131

    
132
        if self.socket_options:
133
            extra_kw['socket_options'] = self.socket_options
134

    
135
        try:
136
            conn = connection.create_connection(
137
                (self.host, self.port), self.timeout, **extra_kw)
138

    
139
        except SocketTimeout as e:
140
            raise ConnectTimeoutError(
141
                self, "Connection to %s timed out. (connect timeout=%s)" %
142
                (self.host, self.timeout))
143

    
144
        except SocketError as e:
145
            raise NewConnectionError(
146
                self, "Failed to establish a new connection: %s" % e)
147

    
148
        return conn
149

    
150
    def _prepare_conn(self, conn):
151
        self.sock = conn
152
        # the _tunnel_host attribute was added in python 2.6.3 (via
153
        # http://hg.python.org/cpython/rev/0f57b30a152f) so pythons 2.6(0-2) do
154
        # not have them.
155
        if getattr(self, '_tunnel_host', None):
156
            # TODO: Fix tunnel so it doesn't depend on self.sock state.
157
            self._tunnel()
158
            # Mark this connection as not reusable
159
            self.auto_open = 0
160

    
161
    def connect(self):
162
        conn = self._new_conn()
163
        self._prepare_conn(conn)
164

    
165

    
166
class HTTPSConnection(HTTPConnection):
167
    default_port = port_by_scheme['https']
168

    
169
    def __init__(self, host, port=None, key_file=None, cert_file=None,
170
                 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **kw):
171

    
172
        HTTPConnection.__init__(self, host, port, strict=strict,
173
                                timeout=timeout, **kw)
174

    
175
        self.key_file = key_file
176
        self.cert_file = cert_file
177

    
178
        # Required property for Google AppEngine 1.9.0 which otherwise causes
179
        # HTTPS requests to go out as HTTP. (See Issue #356)
180
        self._protocol = 'https'
181

    
182
    def connect(self):
183
        conn = self._new_conn()
184
        self._prepare_conn(conn)
185
        self.sock = ssl.wrap_socket(conn, self.key_file, self.cert_file)
186

    
187

    
188
class VerifiedHTTPSConnection(HTTPSConnection):
189
    """
190
    Based on httplib.HTTPSConnection but wraps the socket with
191
    SSL certification.
192
    """
193
    cert_reqs = None
194
    ca_certs = None
195
    ca_cert_dir = None
196
    ssl_version = None
197
    assert_fingerprint = None
198

    
199
    def set_cert(self, key_file=None, cert_file=None,
200
                 cert_reqs=None, ca_certs=None,
201
                 assert_hostname=None, assert_fingerprint=None,
202
                 ca_cert_dir=None):
203

    
204
        if (ca_certs or ca_cert_dir) and cert_reqs is None:
205
            cert_reqs = 'CERT_REQUIRED'
206

    
207
        self.key_file = key_file
208
        self.cert_file = cert_file
209
        self.cert_reqs = cert_reqs
210
        self.assert_hostname = assert_hostname
211
        self.assert_fingerprint = assert_fingerprint
212
        self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
213
        self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
214

    
215
    def connect(self):
216
        # Add certificate verification
217
        conn = self._new_conn()
218

    
219
        resolved_cert_reqs = resolve_cert_reqs(self.cert_reqs)
220
        resolved_ssl_version = resolve_ssl_version(self.ssl_version)
221

    
222
        hostname = self.host
223
        if getattr(self, '_tunnel_host', None):
224
            # _tunnel_host was added in Python 2.6.3
225
            # (See: http://hg.python.org/cpython/rev/0f57b30a152f)
226

    
227
            self.sock = conn
228
            # Calls self._set_hostport(), so self.host is
229
            # self._tunnel_host below.
230
            self._tunnel()
231
            # Mark this connection as not reusable
232
            self.auto_open = 0
233

    
234
            # Override the host with the one we're requesting data from.
235
            hostname = self._tunnel_host
236

    
237
        is_time_off = datetime.date.today() < RECENT_DATE
238
        if is_time_off:
239
            warnings.warn((
240
                'System time is way off (before {0}). This will probably '
241
                'lead to SSL verification errors').format(RECENT_DATE),
242
                SystemTimeWarning
243
            )
244

    
245
        # Wrap socket using verification with the root certs in
246
        # trusted_root_certs
247
        self.sock = ssl_wrap_socket(conn, self.key_file, self.cert_file,
248
                                    cert_reqs=resolved_cert_reqs,
249
                                    ca_certs=self.ca_certs,
250
                                    ca_cert_dir=self.ca_cert_dir,
251
                                    server_hostname=hostname,
252
                                    ssl_version=resolved_ssl_version)
253

    
254
        if self.assert_fingerprint:
255
            assert_fingerprint(self.sock.getpeercert(binary_form=True),
256
                               self.assert_fingerprint)
257
        elif resolved_cert_reqs != ssl.CERT_NONE \
258
                and self.assert_hostname is not False:
259
            cert = self.sock.getpeercert()
260
            if not cert.get('subjectAltName', ()):
261
                warnings.warn((
262
                    'Certificate for {0} has no `subjectAltName`, falling back to check for a '
263
                    '`commonName` for now. This feature is being removed by major browsers and '
264
                    'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 '
265
                    'for details.)'.format(hostname)),
266
                    SubjectAltNameWarning
267
                )
268

    
269
            # In case the hostname is an IPv6 address, strip the square
270
            # brackets from it before using it to validate. This is because
271
            # a certificate with an IPv6 address in it won't have square
272
            # brackets around that address. Sadly, match_hostname won't do this
273
            # for us: it expects the plain host part without any extra work
274
            # that might have been done to make it palatable to httplib.
275
            asserted_hostname = self.assert_hostname or hostname
276
            asserted_hostname = asserted_hostname.strip('[]')
277
            match_hostname(cert, asserted_hostname)
278

    
279
        self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or
280
                            self.assert_fingerprint is not None)
281

    
282

    
283
if ssl:
284
    # Make a copy for testing.
285
    UnverifiedHTTPSConnection = HTTPSConnection
286
    HTTPSConnection = VerifiedHTTPSConnection
287
else:
288
    HTTPSConnection = DummyConnection