~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
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
75
75
    )
76
76
 
77
77
 
 
78
class addinfourl(urllib2.addinfourl):
 
79
    '''Replacement addinfourl class compatible with python-2.7's xmlrpclib
 
80
 
 
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.
 
85
    '''
 
86
 
 
87
    def getheader(self, name, default=None):
 
88
        if self.headers is None:
 
89
            raise httplib.ResponseNotReady()
 
90
        return self.headers.getheader(name, default)
 
91
 
 
92
    def getheaders(self):
 
93
        if self.headers is None:
 
94
            raise httplib.ResponseNotReady()
 
95
        return self.headers.items()
 
96
 
 
97
 
78
98
class _ReportingFileSocket(object):
79
99
 
80
100
    def __init__(self, filesock, report_activity=None):
90
110
        self.report_activity(len(s), 'read')
91
111
        return s
92
112
 
93
 
    def readline(self):
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)
98
 
        #  --  vila 20090209
99
 
        s = self.filesock.readline()
100
 
        self.report_activity(len(s), 'read')
101
 
        return s
 
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):
 
117
        def readline(self):
 
118
            s = self.filesock.readline()
 
119
            self.report_activity(len(s), 'read')
 
120
            return s
 
121
    else:
 
122
        def readline(self, size=-1):
 
123
            s = self.filesock.readline(size)
 
124
            self.report_activity(len(s), 'read')
 
125
            return s
102
126
 
103
127
    def __getattr__(self, name):
104
128
        return getattr(self.filesock, name)
145
169
    """
146
170
 
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]
149
173
 
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
254
 
                ):
255
 
                self._ranges_received_whole_file = True
256
 
                trace.warning(
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)
 
273
            try:
 
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
 
279
                    ):
 
280
                    self._ranges_received_whole_file = True
 
281
                    trace.warning(
 
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
 
290
                if (len(e.args) == 0
 
291
                    or e.args[0] not in (errno.ECONNRESET, errno.ECONNABORTED)):
 
292
                    raise
 
293
                self.close()
260
294
            self._response = None
261
295
        # Preserve our preciousss
262
296
        sock = self.sock
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.")
570
606
                else:
656
692
            r = response
657
693
            r.recv = r.read
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
906
942
        return None
907
943
 
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.
 
946
 
 
947
        :returns: True to skip the proxy, False otherwise.
 
948
        """
910
949
        no_proxy = self.get_proxy_env_var('no', default_to=None)
 
950
        bypass = self.evaluate_proxy_bypass(host, no_proxy)
 
951
        if bypass is None:
 
952
            # Nevertheless, there are platform-specific ways to
 
953
            # ignore proxies...
 
954
            return urllib.proxy_bypass(host)
 
955
        else:
 
956
            return bypass
 
957
 
 
958
    def evaluate_proxy_bypass(self, host, no_proxy):
 
959
        """Check the host against a comma-separated no_proxy list as a string.
 
960
 
 
961
        :param host: ``host:port`` being requested
 
962
 
 
963
        :param no_proxy: comma-separated list of hosts to access directly.
 
964
 
 
965
        :returns: True to skip the proxy, False not to, or None to
 
966
            leave it to urllib.
 
967
        """
911
968
        if no_proxy is None:
 
969
            # All hosts are proxied
912
970
            return False
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()
 
978
            if domain == '':
 
979
                continue
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):
926
987
                    return True
927
 
        # Nevertheless, there are platform-specific ways to
928
 
        # ignore proxies...
929
 
        return urllib.proxy_bypass(host)
 
988
        # Nothing explicitly avoid the host
 
989
        return None
930
990
 
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)
1185
1246
 
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,
 
1255
                port=port,
1194
1256
                path=auth['path'], realm=realm,
1195
1257
                prompt=self.build_password_prompt(auth))
1196
1258