/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/_urllib2_wrappers.py

  • Committer: Jelmer Vernooij
  • Date: 2009-02-25 15:36:48 UTC
  • mfrom: (4048 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4050.
  • Revision ID: jelmer@samba.org-20090225153648-7r5mk20nr9dttqbf
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
# ensure that.
48
48
 
49
49
import httplib
 
50
try:
 
51
    import kerberos
 
52
except ImportError:
 
53
    have_kerberos = False
 
54
else:
 
55
    have_kerberos = True
50
56
import socket
51
57
import urllib
52
58
import urllib2
67
73
    )
68
74
 
69
75
 
70
 
class _BufferedMakefileSocket(object):
71
 
 
72
 
    def __init__(self, sock):
 
76
class _ReportingFileSocket(object):
 
77
 
 
78
    def __init__(self, filesock, report_activity=None):
 
79
        self.filesock = filesock
 
80
        self._report_activity = report_activity
 
81
 
 
82
 
 
83
    def read(self, size=1):
 
84
        s = self.filesock.read(size)
 
85
        self._report_activity(len(s), 'read')
 
86
        return s
 
87
 
 
88
    def readline(self):
 
89
        # This should be readline(self, size=-1), but httplib in python 2.4 and
 
90
        #  2.5 defines a SSLFile wrapper whose readline method lacks the size
 
91
        #  parameter.  So until we drop support for 2.4 and 2.5 and since we
 
92
        #  don't *need* the size parameter we'll stay with readline(self)
 
93
        #  --  vila 20090209
 
94
        s = self.filesock.readline()
 
95
        self._report_activity(len(s), 'read')
 
96
        return s
 
97
 
 
98
    def __getattr__(self, name):
 
99
        return getattr(self.filesock, name)
 
100
 
 
101
 
 
102
class _ReportingSocket(object):
 
103
 
 
104
    def __init__(self, sock, report_activity=None):
73
105
        self.sock = sock
 
106
        self._report_activity = report_activity
 
107
 
 
108
    def send(self, s, *args):
 
109
        self.sock.send(s, *args)
 
110
        self._report_activity(len(s), 'write')
 
111
 
 
112
    def sendall(self, s, *args):
 
113
        self.sock.send(s, *args)
 
114
        self._report_activity(len(s), 'write')
 
115
 
 
116
    def recv(self, *args):
 
117
        s = self.sock.recv(*args)
 
118
        self._report_activity(len(s), 'read')
 
119
        return s
74
120
 
75
121
    def makefile(self, mode='r', bufsize=-1):
76
 
        return self.sock.makefile(mode, 65536)
 
122
        # httplib creates a fileobject that doesn't do buffering, which
 
123
        # makes fp.readline() very expensive because it only reads one byte
 
124
        # at a time.  So we wrap the socket in an object that forces
 
125
        # sock.makefile to make a buffered file.
 
126
        fsock = self.sock.makefile(mode, 65536)
 
127
        # And wrap that into a reporting kind of fileobject
 
128
        return _ReportingFileSocket(fsock, self._report_activity)
77
129
 
78
130
    def __getattr__(self, name):
79
131
        return getattr(self.sock, name)
96
148
    # 8k chunks should be fine.
97
149
    _discarded_buf_size = 8192
98
150
 
99
 
    def __init__(self, sock, *args, **kwargs):
100
 
        # httplib creates a fileobject that doesn't do buffering, which
101
 
        # makes fp.readline() very expensive because it only reads one byte
102
 
        # at a time.  So we wrap the socket in an object that forces
103
 
        # sock.makefile to make a buffered file.
104
 
        sock = _BufferedMakefileSocket(sock)
105
 
        httplib.HTTPResponse.__init__(self, sock, *args, **kwargs)
106
 
 
107
151
    def begin(self):
108
152
        """Begin to read the response from the server.
109
153
 
178
222
    # we want to warn. But not below a given thresold.
179
223
    _range_warning_thresold = 1024 * 1024
180
224
 
181
 
    def __init__(self):
 
225
    def __init__(self,
 
226
                 report_activity=None):
182
227
        self._response = None
 
228
        self._report_activity = report_activity
183
229
        self._ranges_received_whole_file = None
184
230
 
185
231
    def _mutter_connect(self):
211
257
        # Preserve our preciousss
212
258
        sock = self.sock
213
259
        self.sock = None
214
 
        # Let httplib.HTTPConnection do its housekeeping 
 
260
        # Let httplib.HTTPConnection do its housekeeping
215
261
        self.close()
216
262
        # Restore our preciousss
217
263
        self.sock = sock
218
264
 
 
265
    def _wrap_socket_for_reporting(self, sock):
 
266
        """Wrap the socket before anybody use it."""
 
267
        self.sock = _ReportingSocket(sock, self._report_activity)
 
268
 
219
269
 
220
270
class HTTPConnection(AbstractHTTPConnection, httplib.HTTPConnection):
221
271
 
222
272
    # XXX: Needs refactoring at the caller level.
223
 
    def __init__(self, host, port=None, proxied_host=None):
224
 
        AbstractHTTPConnection.__init__(self)
 
273
    def __init__(self, host, port=None, proxied_host=None,
 
274
                 report_activity=None):
 
275
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
225
276
        # Use strict=True since we don't support HTTP/0.9
226
277
        httplib.HTTPConnection.__init__(self, host, port, strict=True)
227
278
        self.proxied_host = proxied_host
230
281
        if 'http' in debug.debug_flags:
231
282
            self._mutter_connect()
232
283
        httplib.HTTPConnection.connect(self)
 
284
        self._wrap_socket_for_reporting(self.sock)
233
285
 
234
286
 
235
287
# Build the appropriate socket wrapper for ssl
248
300
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
249
301
 
250
302
    def __init__(self, host, port=None, key_file=None, cert_file=None,
251
 
                 proxied_host=None):
252
 
        AbstractHTTPConnection.__init__(self)
 
303
                 proxied_host=None,
 
304
                 report_activity=None):
 
305
        AbstractHTTPConnection.__init__(self, report_activity=report_activity)
253
306
        # Use strict=True since we don't support HTTP/0.9
254
307
        httplib.HTTPSConnection.__init__(self, host, port,
255
308
                                         key_file, cert_file, strict=True)
259
312
        if 'http' in debug.debug_flags:
260
313
            self._mutter_connect()
261
314
        httplib.HTTPConnection.connect(self)
 
315
        self._wrap_socket_for_reporting(self.sock)
262
316
        if self.proxied_host is None:
263
317
            self.connect_to_origin()
264
318
 
265
319
    def connect_to_origin(self):
266
 
        self.sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
 
320
        ssl_sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
 
321
        # Wrap the ssl socket before anybody use it
 
322
        self._wrap_socket_for_reporting(ssl_sock)
267
323
 
268
324
 
269
325
class Request(urllib2.Request):
315
371
 
316
372
    def __init__(self, request):
317
373
        """Constructor
318
 
        
 
374
 
319
375
        :param request: the first request sent to the proxied host, already
320
376
            processed by the opener (i.e. proxied_host is already set).
321
377
        """
355
411
 
356
412
    handler_order = 1000 # after all pre-processings
357
413
 
 
414
    def __init__(self, report_activity=None):
 
415
        self._report_activity = report_activity
 
416
 
358
417
    def create_connection(self, request, http_connection_class):
359
418
        host = request.get_host()
360
419
        if not host:
366
425
        # request is made)
367
426
        try:
368
427
            connection = http_connection_class(
369
 
                host, proxied_host=request.proxied_host)
 
428
                host, proxied_host=request.proxied_host,
 
429
                report_activity=self._report_activity)
370
430
        except httplib.InvalidURL, exception:
371
431
            # There is only one occurrence of InvalidURL in httplib
372
432
            raise errors.InvalidURL(request.get_full_url(),
638
698
                        connect.proxied_host, self.host))
639
699
            # Housekeeping
640
700
            connection.cleanup_pipe()
641
 
            # Establish the connection encryption 
 
701
            # Establish the connection encryption
642
702
            connection.connect_to_origin()
643
703
            # Propagate the connection to the original request
644
704
            request.connection = connection
889
949
    preventively set authentication headers after the first
890
950
    successful authentication.
891
951
 
892
 
    This can be used for http and proxy, as well as for basic and
 
952
    This can be used for http and proxy, as well as for basic, negotiate and
893
953
    digest authentications.
894
954
 
895
955
    This provides an unified interface for all authentication handlers
1009
1069
        (digest's nonce is an example, digest's nonce_count is a
1010
1070
        *counter-example*). Such parameters must be updated by
1011
1071
        using the update_auth() method.
1012
 
        
 
1072
 
1013
1073
        :param header: The authentication header sent by the server.
1014
1074
        :param auth: The auth parameters already known. They may be
1015
1075
             updated.
1089
1149
    https_request = http_request # FIXME: Need test
1090
1150
 
1091
1151
 
 
1152
class NegotiateAuthHandler(AbstractAuthHandler):
 
1153
    """A authentication handler that handles WWW-Authenticate: Negotiate.
 
1154
 
 
1155
    At the moment this handler supports just Kerberos. In the future,
 
1156
    NTLM support may also be added.
 
1157
    """
 
1158
 
 
1159
    handler_order = 480
 
1160
 
 
1161
    def auth_match(self, header, auth):
 
1162
        scheme = header.lower()
 
1163
        if scheme != 'negotiate':
 
1164
            return False
 
1165
        self.update_auth(auth, 'scheme', scheme)
 
1166
        resp = self._auth_match_kerberos(auth)
 
1167
        if resp is None:
 
1168
            return False
 
1169
        # Optionally should try to authenticate using NTLM here
 
1170
        self.update_auth(auth, 'negotiate_response', resp)
 
1171
        return True
 
1172
 
 
1173
    def _auth_match_kerberos(self, auth):
 
1174
        """Try to create a GSSAPI response for authenticating against a host."""
 
1175
        if not have_kerberos:
 
1176
            return None
 
1177
        ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth)
 
1178
        if ret < 1:
 
1179
            trace.warning('Unable to create GSSAPI context for %s: %d',
 
1180
                auth['host'], ret)
 
1181
            return None
 
1182
        ret = kerberos.authGSSClientStep(vc, "")
 
1183
        if ret < 0:
 
1184
            trace.mutter('authGSSClientStep failed: %d', ret)
 
1185
            return None
 
1186
        return kerberos.authGSSClientResponse(vc)
 
1187
 
 
1188
    def build_auth_header(self, auth, request):
 
1189
        return "Negotiate %s" % auth['negotiate_response']
 
1190
 
 
1191
    def auth_params_reusable(self, auth):
 
1192
        # If the auth scheme is known, it means a previous
 
1193
        # authentication was successful, all information is
 
1194
        # available, no further checks are needed.
 
1195
        return (auth.get('scheme', None) == 'negotiate' and
 
1196
                auth.get('negotiate_response', None) is not None)
 
1197
 
 
1198
 
1092
1199
class BasicAuthHandler(AbstractAuthHandler):
1093
1200
    """A custom basic authentication handler."""
1094
1201
 
1314
1421
    """Custom proxy basic authentication handler"""
1315
1422
 
1316
1423
 
 
1424
class HTTPNegotiateAuthHandler(NegotiateAuthHandler, HTTPAuthHandler):
 
1425
    """Custom http negotiate authentication handler"""
 
1426
 
 
1427
 
 
1428
class ProxyNegotiateAuthHandler(NegotiateAuthHandler, ProxyAuthHandler):
 
1429
    """Custom proxy negotiate authentication handler"""
 
1430
 
 
1431
 
1317
1432
class HTTPErrorProcessor(urllib2.HTTPErrorProcessor):
1318
1433
    """Process HTTP error responses.
1319
1434
 
1370
1485
    def __init__(self,
1371
1486
                 connection=ConnectionHandler,
1372
1487
                 redirect=HTTPRedirectHandler,
1373
 
                 error=HTTPErrorProcessor,):
1374
 
        self._opener = urllib2.build_opener( \
1375
 
            connection, redirect, error,
 
1488
                 error=HTTPErrorProcessor,
 
1489
                 report_activity=None):
 
1490
        self._opener = urllib2.build_opener(
 
1491
            connection(report_activity=report_activity),
 
1492
            redirect, error,
1376
1493
            ProxyHandler(),
1377
1494
            HTTPBasicAuthHandler(),
1378
1495
            HTTPDigestAuthHandler(),
 
1496
            HTTPNegotiateAuthHandler(),
1379
1497
            ProxyBasicAuthHandler(),
1380
1498
            ProxyDigestAuthHandler(),
 
1499
            ProxyNegotiateAuthHandler(),
1381
1500
            HTTPHandler,
1382
1501
            HTTPSHandler,
1383
1502
            HTTPDefaultErrorHandler,