70
class _BufferedMakefileSocket(object):
72
def __init__(self, sock):
76
class _ReportingFileSocket(object):
78
def __init__(self, filesock, report_activity=None):
79
self.filesock = filesock
80
self._report_activity = report_activity
83
def read(self, size=1):
84
s = self.filesock.read(size)
85
self._report_activity(len(s), 'read')
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)
94
s = self.filesock.readline()
95
self._report_activity(len(s), 'read')
98
def __getattr__(self, name):
99
return getattr(self.filesock, name)
102
class _ReportingSocket(object):
104
def __init__(self, sock, report_activity=None):
106
self._report_activity = report_activity
108
def sendall(self, s, *args):
109
self.sock.sendall(s, *args)
110
self._report_activity(len(s), 'write')
112
def recv(self, *args):
113
s = self.sock.recv(*args)
114
self._report_activity(len(s), 'read')
75
117
def makefile(self, mode='r', bufsize=-1):
76
return self.sock.makefile(mode, 65536)
118
# httplib creates a fileobject that doesn't do buffering, which
119
# makes fp.readline() very expensive because it only reads one byte
120
# at a time. So we wrap the socket in an object that forces
121
# sock.makefile to make a buffered file.
122
fsock = self.sock.makefile(mode, 65536)
123
# And wrap that into a reporting kind of fileobject
124
return _ReportingFileSocket(fsock, self._report_activity)
78
126
def __getattr__(self, name):
79
127
return getattr(self.sock, name)
96
144
# 8k chunks should be fine.
97
145
_discarded_buf_size = 8192
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)
108
148
"""Begin to read the response from the server.
211
253
# Preserve our preciousss
214
# Let httplib.HTTPConnection do its housekeeping
256
# Let httplib.HTTPConnection do its housekeeping
216
258
# Restore our preciousss
261
def _wrap_socket_for_reporting(self, sock):
262
"""Wrap the socket before anybody use it."""
263
self.sock = _ReportingSocket(sock, self._report_activity)
220
266
class HTTPConnection(AbstractHTTPConnection, httplib.HTTPConnection):
222
268
# XXX: Needs refactoring at the caller level.
223
def __init__(self, host, port=None, proxied_host=None):
224
AbstractHTTPConnection.__init__(self)
269
def __init__(self, host, port=None, proxied_host=None,
270
report_activity=None):
271
AbstractHTTPConnection.__init__(self, report_activity=report_activity)
225
272
# Use strict=True since we don't support HTTP/0.9
226
273
httplib.HTTPConnection.__init__(self, host, port, strict=True)
227
274
self.proxied_host = proxied_host
248
296
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection):
250
298
def __init__(self, host, port=None, key_file=None, cert_file=None,
252
AbstractHTTPConnection.__init__(self)
300
report_activity=None):
301
AbstractHTTPConnection.__init__(self, report_activity=report_activity)
253
302
# Use strict=True since we don't support HTTP/0.9
254
303
httplib.HTTPSConnection.__init__(self, host, port,
255
304
key_file, cert_file, strict=True)
259
308
if 'http' in debug.debug_flags:
260
309
self._mutter_connect()
261
310
httplib.HTTPConnection.connect(self)
311
self._wrap_socket_for_reporting(self.sock)
262
312
if self.proxied_host is None:
263
313
self.connect_to_origin()
265
315
def connect_to_origin(self):
266
self.sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
316
ssl_sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file)
317
# Wrap the ssl socket before anybody use it
318
self._wrap_socket_for_reporting(ssl_sock)
269
321
class Request(urllib2.Request):
356
408
handler_order = 1000 # after all pre-processings
410
def __init__(self, report_activity=None):
411
self._report_activity = report_activity
358
413
def create_connection(self, request, http_connection_class):
359
414
host = request.get_host()
366
421
# request is made)
368
423
connection = http_connection_class(
369
host, proxied_host=request.proxied_host)
424
host, proxied_host=request.proxied_host,
425
report_activity=self._report_activity)
370
426
except httplib.InvalidURL, exception:
371
427
# There is only one occurrence of InvalidURL in httplib
372
428
raise errors.InvalidURL(request.get_full_url(),
638
694
connect.proxied_host, self.host))
640
696
connection.cleanup_pipe()
641
# Establish the connection encryption
697
# Establish the connection encryption
642
698
connection.connect_to_origin()
643
699
# Propagate the connection to the original request
644
700
request.connection = connection
889
945
preventively set authentication headers after the first
890
946
successful authentication.
892
This can be used for http and proxy, as well as for basic and
948
This can be used for http and proxy, as well as for basic, negotiate and
893
949
digest authentications.
895
951
This provides an unified interface for all authentication handlers
934
993
# in such a cycle by default.
935
994
self._retry_count = None
996
def _parse_auth_header(self, server_header):
997
"""Parse the authentication header.
999
:param server_header: The value of the header sent by the server
1000
describing the authenticaion request.
1002
:return: A tuple (scheme, remainder) scheme being the first word in the
1003
given header (lower cased), remainder may be None.
1006
scheme, remainder = server_header.split(None, 1)
1008
scheme = server_header
1010
return (scheme.lower(), remainder)
937
1012
def update_auth(self, auth, key, value):
938
1013
"""Update a value in auth marking the auth as modified if needed"""
939
1014
old_value = auth.get(key, None)
974
1049
# We already tried that, give up
977
if auth.get('user', None) is None:
1052
if self.requires_username and auth.get('user', None) is None:
978
1053
# Without a known user, we can't authenticate
1009
1084
(digest's nonce is an example, digest's nonce_count is a
1010
1085
*counter-example*). Such parameters must be updated by
1011
1086
using the update_auth() method.
1013
1088
:param header: The authentication header sent by the server.
1014
1089
:param auth: The auth parameters already known. They may be
1089
1164
https_request = http_request # FIXME: Need test
1167
class NegotiateAuthHandler(AbstractAuthHandler):
1168
"""A authentication handler that handles WWW-Authenticate: Negotiate.
1170
At the moment this handler supports just Kerberos. In the future,
1171
NTLM support may also be added.
1176
requires_username = False
1178
def auth_match(self, header, auth):
1179
scheme, raw_auth = self._parse_auth_header(header)
1180
if scheme != 'negotiate':
1182
self.update_auth(auth, 'scheme', scheme)
1183
resp = self._auth_match_kerberos(auth)
1186
# Optionally should try to authenticate using NTLM here
1187
self.update_auth(auth, 'negotiate_response', resp)
1190
def _auth_match_kerberos(self, auth):
1191
"""Try to create a GSSAPI response for authenticating against a host."""
1192
if not have_kerberos:
1194
ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth)
1196
trace.warning('Unable to create GSSAPI context for %s: %d',
1199
ret = kerberos.authGSSClientStep(vc, "")
1201
trace.mutter('authGSSClientStep failed: %d', ret)
1203
return kerberos.authGSSClientResponse(vc)
1205
def build_auth_header(self, auth, request):
1206
return "Negotiate %s" % auth['negotiate_response']
1208
def auth_params_reusable(self, auth):
1209
# If the auth scheme is known, it means a previous
1210
# authentication was successful, all information is
1211
# available, no further checks are needed.
1212
return (auth.get('scheme', None) == 'negotiate' and
1213
auth.get('negotiate_response', None) is not None)
1092
1216
class BasicAuthHandler(AbstractAuthHandler):
1093
1217
"""A custom basic authentication handler."""
1150
1273
class DigestAuthHandler(AbstractAuthHandler):
1151
1274
"""A custom digest authentication handler."""
1153
# Before basic as digest is a bit more secure
1276
# Before basic as digest is a bit more secure and should be preferred
1154
1277
handler_order = 490
1156
1279
def auth_params_reusable(self, auth):
1160
1283
return auth.get('scheme', None) == 'digest'
1162
1285
def auth_match(self, header, auth):
1163
scheme, raw_auth = header.split(None, 1)
1164
scheme = scheme.lower()
1286
scheme, raw_auth = self._parse_auth_header(header)
1165
1287
if scheme != 'digest':
1314
1436
"""Custom proxy basic authentication handler"""
1439
class HTTPNegotiateAuthHandler(NegotiateAuthHandler, HTTPAuthHandler):
1440
"""Custom http negotiate authentication handler"""
1443
class ProxyNegotiateAuthHandler(NegotiateAuthHandler, ProxyAuthHandler):
1444
"""Custom proxy negotiate authentication handler"""
1317
1447
class HTTPErrorProcessor(urllib2.HTTPErrorProcessor):
1318
1448
"""Process HTTP error responses.
1370
1500
def __init__(self,
1371
1501
connection=ConnectionHandler,
1372
1502
redirect=HTTPRedirectHandler,
1373
error=HTTPErrorProcessor,):
1374
self._opener = urllib2.build_opener( \
1375
connection, redirect, error,
1503
error=HTTPErrorProcessor,
1504
report_activity=None):
1505
self._opener = urllib2.build_opener(
1506
connection(report_activity=report_activity),
1376
1508
ProxyHandler(),
1377
1509
HTTPBasicAuthHandler(),
1378
1510
HTTPDigestAuthHandler(),
1511
HTTPNegotiateAuthHandler(),
1379
1512
ProxyBasicAuthHandler(),
1380
1513
ProxyDigestAuthHandler(),
1514
ProxyNegotiateAuthHandler(),
1383
1517
HTTPDefaultErrorHandler,