~bzr-pqm/bzr/bzr.dev

1540.3.3 by Martin Pool
Review updates of pycurl transport
1
# Copyright (C) 2005, 2006 Canonical Ltd
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
2
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
7
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
12
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2004.1.2 by vila
Implements a BasicAuthManager.
17
from cStringIO import StringIO
1540.3.3 by Martin Pool
Review updates of pycurl transport
18
2004.1.21 by v.ladeuil+lp at free
Trivial cleanup.
19
from bzrlib import ui
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
20
from bzrlib.errors import NoSuchFile
1540.3.3 by Martin Pool
Review updates of pycurl transport
21
from bzrlib.trace import mutter
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
22
from bzrlib.transport import register_urlparse_netloc_protocol
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
23
from bzrlib.transport.http import HttpTransportBase
2004.1.9 by vila
Takes jam's remarks into account when possible, add TODOs for the rest.
24
# TODO: handle_response should be integrated into the _urllib2_wrappers
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
25
from bzrlib.transport.http.response import handle_response
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
26
from bzrlib.transport.http._urllib2_wrappers import (
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
27
    Opener,
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
28
    Request,
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
29
    )
30
1540.3.3 by Martin Pool
Review updates of pycurl transport
31
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
32
register_urlparse_netloc_protocol('http+urllib')
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
33
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
34
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
35
class HttpTransport_urllib(HttpTransportBase):
1786.1.33 by John Arbash Meinel
Cleanup pass #2
36
    """Python urllib transport for http and https."""
1540.3.3 by Martin Pool
Review updates of pycurl transport
37
2004.1.9 by vila
Takes jam's remarks into account when possible, add TODOs for the rest.
38
    # In order to debug we have to issue our traces in sync with
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
39
    # httplib, which use print :(
40
    _debuglevel = 0
2004.3.1 by vila
Test ConnectionError exceptions.
41
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
42
    _opener_class = Opener
43
44
    def __init__(self, base, from_transport=None):
1540.3.3 by Martin Pool
Review updates of pycurl transport
45
        """Set the base path where files will be stored."""
2208.2.1 by v.ladeuil+lp at free
Trivial small fixes.
46
        super(HttpTransport_urllib, self).__init__(base, from_transport)
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
47
        if from_transport is not None:
48
            self._connection = from_transport._connection
2004.1.2 by vila
Implements a BasicAuthManager.
49
            self._user = from_transport._user
50
            self._password = from_transport._password
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
51
            self._opener = from_transport._opener
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
52
        else:
53
            self._connection = None
2004.1.2 by vila
Implements a BasicAuthManager.
54
            self._user = None
55
            self._password = None
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
56
            self._opener = self._opener_class()
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
57
2004.1.2 by vila
Implements a BasicAuthManager.
58
    def ask_password(self, request):
59
        """Ask for a password if none is already provided in the request"""
2004.2.1 by John Arbash Meinel
Cleanup of urllib functions
60
        # TODO: jam 20060915 There should be a test that asserts we ask 
61
        #       for a password at the right time.
2004.1.2 by vila
Implements a BasicAuthManager.
62
        if request.password is None:
2004.1.7 by vila
Better handling of passwords (user should be queried only once).
63
            # We can't predict realm, let's try None, we'll get a
64
            # 401 if we are wrong anyway
65
            realm = None
2004.1.4 by vila
Fix user handling.
66
            host = request.get_host()
2004.1.2 by vila
Implements a BasicAuthManager.
67
            password_manager = self._opener.password_manager
2004.1.9 by vila
Takes jam's remarks into account when possible, add TODOs for the rest.
68
            # Query the password manager first
2004.1.7 by vila
Better handling of passwords (user should be queried only once).
69
            user, password = password_manager.find_user_password(None, host)
70
            if user == request.user and password is not None:
71
                request.password = password
72
            else:
2004.1.9 by vila
Takes jam's remarks into account when possible, add TODOs for the rest.
73
                # Ask the user if we MUST
2004.1.7 by vila
Better handling of passwords (user should be queried only once).
74
                http_pass = 'HTTP %(user)s@%(host)s password'
2004.1.8 by vila
Merge jam's cleanup
75
                request.password = ui.ui_factory.get_password(prompt=http_pass,
76
                                                              user=request.user,
77
                                                              host=host)
2004.1.7 by vila
Better handling of passwords (user should be queried only once).
78
                password_manager.add_password(None, host,
79
                                              request.user, request.password)
2004.1.2 by vila
Implements a BasicAuthManager.
80
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
81
    def _perform(self, request):
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
82
        """Send the request to the server and handles common errors.
83
84
        :returns: urllib2 Response object
85
        """
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
86
        if self._connection is not None:
2004.1.2 by vila
Implements a BasicAuthManager.
87
            # Give back shared info
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
88
            request.connection = self._connection
2004.1.4 by vila
Fix user handling.
89
            if self._user is not None:
90
                request.user = self._user
91
                request.password = self._password
92
        elif request.user is not None:
2004.1.2 by vila
Implements a BasicAuthManager.
93
            # We will issue our first request, time to ask for a
94
            # password if needed
95
            self.ask_password(request)
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
96
97
        mutter('%s: [%s]' % (request.method, request.get_full_url()))
98
        if self._debuglevel > 0:
99
            print 'perform: %s base: %s, url: %s' % (request.method, self.base,
100
                                                     request.get_full_url())
101
102
        response = self._opener.open(request)
103
        if self._connection is None:
104
            # Acquire connection when the first request is able
105
            # to connect to the server
106
            self._connection = request.connection
2004.1.2 by vila
Implements a BasicAuthManager.
107
            self._user = request.user
108
            self._password = request.password
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
109
110
        if request.redirected_to is not None:
111
            # TODO: Update the transport so that subsequent
112
            # requests goes directly to the right host
113
            mutter('redirected from: %s to: %s' % (request.get_full_url(),
114
                                                   request.redirected_to))
115
116
        return response
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
117
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
118
    def _get(self, relpath, ranges, tail_amount=0):
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
119
        """See HttpTransport._get"""
120
121
        abspath = self._real_abspath(relpath)
122
        headers = {}
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
123
        if ranges or tail_amount:
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
124
            range_header = self.attempted_range_header(ranges, tail_amount)
125
            if range_header is not None:
126
                bytes = 'bytes=' + range_header
127
                headers = {'Range': bytes}
2004.3.1 by vila
Test ConnectionError exceptions.
128
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
129
        request = Request('GET', abspath, None, headers)
130
        response = self._perform(request)
131
132
        code = response.code
133
        if code == 404: # not found
134
            self._connection.fake_close()
135
            raise NoSuchFile(abspath)
136
137
        data = handle_response(abspath, code, response.headers, response)
138
        # Close response to free the httplib.HTTPConnection pipeline
139
        self._connection.fake_close()
140
        return code, data
1540.3.3 by Martin Pool
Review updates of pycurl transport
141
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
142
    def _post(self, body_bytes):
2004.1.28 by v.ladeuil+lp at free
Merge bzr.dev. Including http modifications by "smart" related code
143
        abspath = self._real_abspath('.bzr/smart')
144
        response = self._perform(Request('POST', abspath, body_bytes))
145
        code = response.code
146
        data = handle_response(abspath, code, response.headers, response)
147
        # Close response to free the httplib.HTTPConnection pipeline
148
        self._connection.fake_close()
149
        return code, data
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
150
1540.3.3 by Martin Pool
Review updates of pycurl transport
151
    def should_cache(self):
152
        """Return True if the data pulled across should be cached locally.
153
        """
154
        return True
155
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
156
    def _head(self, relpath):
157
        """Request the HEAD of a file.
158
159
        Performs the request and leaves callers handle the results.
160
        """
161
        abspath = self._real_abspath(relpath)
162
        request = Request('HEAD', abspath)
163
        response = self._perform(request)
164
165
        self._connection.fake_close()
166
        return response
167
1540.3.3 by Martin Pool
Review updates of pycurl transport
168
    def has(self, relpath):
169
        """Does the target location exist?
170
        """
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
171
        response = self._head(relpath)
172
173
        code = response.code
174
        # FIXME: 302 MAY have been already processed by the
175
        # redirection handler
176
        if code in (200, 302): # "ok", "found"
1540.3.3 by Martin Pool
Review updates of pycurl transport
177
            return True
178
        else:
2004.1.1 by vila
Connection sharing, with redirection. without authentification.
179
            assert(code == 404, 'Only 200, 404 or may be 302 are correct')
180
            return False
1540.3.25 by Martin Pool
New 'http+urllib' scheme
181
182
1540.3.6 by Martin Pool
[merge] update from bzr.dev
183
def get_test_permutations():
184
    """Return the permutations to be used in testing."""
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
185
    from bzrlib.tests.HttpServer import HttpServer_urllib
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
186
    return [(HttpTransport_urllib, HttpServer_urllib),
1540.3.6 by Martin Pool
[merge] update from bzr.dev
187
            ]