1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2006-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
78
class addinfourl(urllib2.addinfourl):
79
'''Replacement addinfourl class compatible with python-2.7's xmlrpclib
81
In python-2.7, xmlrpclib expects that the response object that it receives
82
has a getheader method. httplib.HTTPResponse provides this but
83
urllib2.addinfourl does not. Add the necessary functions here, ported to
84
use the internal data structures of addinfourl.
87
def getheader(self, name, default=None):
88
if self.headers is None:
89
raise httplib.ResponseNotReady()
90
return self.headers.getheader(name, default)
93
if self.headers is None:
94
raise httplib.ResponseNotReady()
95
return self.headers.items()
78
98
class _ReportingFileSocket(object):
80
100
def __init__(self, filesock, report_activity=None):
90
110
self.report_activity(len(s), 'read')
94
# This should be readline(self, size=-1), but httplib in python 2.4 and
95
# 2.5 defines a SSLFile wrapper whose readline method lacks the size
96
# parameter. So until we drop support for 2.4 and 2.5 and since we
97
# don't *need* the size parameter we'll stay with readline(self)
99
s = self.filesock.readline()
100
self.report_activity(len(s), 'read')
113
# httplib in python 2.4 and 2.5 defines a SSLFile wrapper whose readline
114
# method lacks the size parameter. python2.6 provides a proper ssl socket
115
# and added it. python2.7 uses it, forcing us to provide it.
116
if sys.version_info < (2, 6):
118
s = self.filesock.readline()
119
self.report_activity(len(s), 'read')
122
def readline(self, size=-1):
123
s = self.filesock.readline(size)
124
self.report_activity(len(s), 'read')
103
127
def __getattr__(self, name):
104
128
return getattr(self.filesock, name)
147
171
# Some responses have bodies in which we have no interest
148
_body_ignored_responses = [301,302, 303, 307, 401, 403, 404]
172
_body_ignored_responses = [301,302, 303, 307, 400, 401, 403, 404, 501]
150
174
# in finish() below, we may have to discard several MB in the worst
151
175
# case. To avoid buffering that much, we read and discard by chunks
246
270
def cleanup_pipe(self):
247
271
"""Read the remaining bytes of the last response if any."""
248
272
if self._response is not None:
249
pending = self._response.finish()
250
# Warn the user (once)
251
if (self._ranges_received_whole_file is None
252
and self._response.status == 200
253
and pending and pending > self._range_warning_thresold
255
self._ranges_received_whole_file = True
257
'Got a 200 response when asking for multiple ranges,'
258
' does your server at %s:%s support range requests?',
259
self.host, self.port)
274
pending = self._response.finish()
275
# Warn the user (once)
276
if (self._ranges_received_whole_file is None
277
and self._response.status == 200
278
and pending and pending > self._range_warning_thresold
280
self._ranges_received_whole_file = True
282
'Got a 200 response when asking for multiple ranges,'
283
' does your server at %s:%s support range requests?',
284
self.host, self.port)
285
except socket.error, e:
286
# It's conceivable that the socket is in a bad state here
287
# (including some test cases) and in this case, it doesn't need
288
# cleaning anymore, so no need to fail, we just get rid of the
289
# socket and let callers reconnect
291
or e.args[0] not in (errno.ECONNRESET, errno.ECONNABORTED)):
260
294
self._response = None
261
295
# Preserve our preciousss
564
598
'Bad status line received',
565
599
orig_error=exc_val)
566
600
elif (isinstance(exc_val, socket.error) and len(exc_val.args)
567
and exc_val.args[0] in (errno.ECONNRESET, 10054)):
601
and exc_val.args[0] in (errno.ECONNRESET, 10053, 10054)):
602
# 10053 == WSAECONNABORTED
603
# 10054 == WSAECONNRESET
568
604
raise errors.ConnectionReset(
569
605
"Connection lost while sending request.")
658
694
fp = socket._fileobject(r, bufsize=65536)
659
resp = urllib2.addinfourl(fp, r.msg, req.get_full_url())
695
resp = addinfourl(fp, r.msg, req.get_full_url())
660
696
resp.code = r.status
661
697
resp.msg = r.reason
662
698
resp.version = r.version
908
944
def proxy_bypass(self, host):
909
"""Check if host should be proxied or not"""
945
"""Check if host should be proxied or not.
947
:returns: True to skip the proxy, False otherwise.
910
949
no_proxy = self.get_proxy_env_var('no', default_to=None)
950
bypass = self.evaluate_proxy_bypass(host, no_proxy)
952
# Nevertheless, there are platform-specific ways to
954
return urllib.proxy_bypass(host)
958
def evaluate_proxy_bypass(self, host, no_proxy):
959
"""Check the host against a comma-separated no_proxy list as a string.
961
:param host: ``host:port`` being requested
963
:param no_proxy: comma-separated list of hosts to access directly.
965
:returns: True to skip the proxy, False not to, or None to
911
968
if no_proxy is None:
969
# All hosts are proxied
913
971
hhost, hport = urllib.splitport(host)
914
972
# Does host match any of the domains mentioned in
916
974
# are fuzzy (to say the least). We try to allow most
917
975
# commonly seen values.
918
976
for domain in no_proxy.split(','):
977
domain = domain.strip()
919
980
dhost, dport = urllib.splitport(domain)
920
981
if hport == dport or dport is None:
921
982
# Protect glob chars
924
985
dhost = dhost.replace("?", r".")
925
986
if re.match(dhost, hhost, re.IGNORECASE):
927
# Nevertheless, there are platform-specific ways to
929
return urllib.proxy_bypass(host)
988
# Nothing explicitly avoid the host
931
991
def set_proxy(self, request, type):
932
992
if self.proxy_bypass(request.get_host()):
1182
1242
user = auth.get('user', None)
1183
1243
password = auth.get('password', None)
1184
1244
realm = auth['realm']
1245
port = auth.get('port', None)
1186
1247
if user is None:
1187
1248
user = auth_conf.get_user(auth['protocol'], auth['host'],
1188
port=auth['port'], path=auth['path'],
1249
port=port, path=auth['path'],
1189
1250
realm=realm, ask=True,
1190
1251
prompt=self.build_username_prompt(auth))
1191
1252
if user is not None and password is None:
1192
1253
password = auth_conf.get_password(
1193
auth['protocol'], auth['host'], user, port=auth['port'],
1254
auth['protocol'], auth['host'], user,
1194
1256
path=auth['path'], realm=realm,
1195
1257
prompt=self.build_password_prompt(auth))