~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http.py

Merge from integration.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
from bzrlib.transport import Transport, register_transport
20
20
from bzrlib.errors import (TransportNotPossible, NoSuchFile, 
21
 
                           NonRelativePath, TransportError, ConnectionError)
 
21
                           TransportError, ConnectionError)
22
22
import os, errno
23
23
from cStringIO import StringIO
24
 
import urllib2
 
24
import urllib, urllib2
25
25
import urlparse
26
26
 
27
27
from bzrlib.errors import BzrError, BzrCheckError
29
29
from bzrlib.trace import mutter
30
30
 
31
31
 
 
32
def extract_auth(url, password_manager):
 
33
    """
 
34
    Extract auth parameters from am HTTP/HTTPS url and add them to the given
 
35
    password manager.  Return the url, minus those auth parameters (which
 
36
    confuse urllib2).
 
37
    """
 
38
    assert url.startswith('http://') or url.startswith('https://')
 
39
    scheme, host = url.split('//', 1)
 
40
    if '/' in host:
 
41
        host, path = host.split('/', 1)
 
42
        path = '/' + path
 
43
    else:
 
44
        path = ''
 
45
    port = ''
 
46
    if '@' in host:
 
47
        auth, host = host.split('@', 1)
 
48
        if ':' in auth:
 
49
            username, password = auth.split(':', 1)
 
50
        else:
 
51
            username, password = auth, None
 
52
        if ':' in host:
 
53
            host, port = host.split(':', 1)
 
54
            port = ':' + port
 
55
        # FIXME: if password isn't given, should we ask for it?
 
56
        if password is not None:
 
57
            username = urllib.unquote(username)
 
58
            password = urllib.unquote(password)
 
59
            password_manager.add_password(None, host, username, password)
 
60
    url = scheme + '//' + host + port + path
 
61
    return url
 
62
    
32
63
def get_url(url):
33
64
    import urllib2
34
65
    mutter("get_url %s" % url)
35
 
    url_f = urllib2.urlopen(url)
 
66
    manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
 
67
    url = extract_auth(url, manager)
 
68
    auth_handler = urllib2.HTTPBasicAuthHandler(manager)
 
69
    opener = urllib2.build_opener(auth_handler)
 
70
    url_f = opener.open(url)
36
71
    return url_f
37
72
 
38
 
class HttpTransportError(TransportError):
39
 
    pass
40
 
 
41
73
class HttpTransport(Transport):
42
74
    """This is the transport agent for http:// access.
43
75
    
119
151
        cleaner if we just do an http HEAD request, and parse
120
152
        the return code.
121
153
        """
 
154
        path = relpath
122
155
        try:
123
 
            f = get_url(self.abspath(relpath))
 
156
            path = self.abspath(relpath)
 
157
            f = get_url(path)
124
158
            # Without the read and then close()
125
159
            # we tend to have busy sockets.
126
160
            f.read()
127
161
            f.close()
128
162
            return True
129
163
        except urllib2.URLError, e:
 
164
            mutter('url error code: %s for has url: %r', e.code, path)
130
165
            if e.code == 404:
131
166
                return False
132
167
            raise
133
168
        except IOError, e:
 
169
            mutter('io error: %s %s for has url: %r', 
 
170
                e.errno, errno.errorcode.get(e.errno), path)
134
171
            if e.errno == errno.ENOENT:
135
172
                return False
136
 
            raise HttpTransportError(orig_error=e)
 
173
            raise TransportError(orig_error=e)
137
174
 
138
175
    def get(self, relpath, decode=False):
139
176
        """Get the file at the given relative path.
140
177
 
141
178
        :param relpath: The relative path to the file
142
179
        """
 
180
        path = relpath
143
181
        try:
144
 
            return get_url(self.abspath(relpath))
 
182
            path = self.abspath(relpath)
 
183
            return get_url(path)
145
184
        except urllib2.HTTPError, e:
 
185
            mutter('url error code: %s for has url: %r', e.code, path)
146
186
            if e.code == 404:
147
 
                raise NoSuchFile(msg = "Error retrieving %s: %s" 
148
 
                                 % (self.abspath(relpath), str(e)),
149
 
                                 orig_error=e)
 
187
                raise NoSuchFile(path, extra=e)
150
188
            raise
151
189
        except (BzrError, IOError), e:
 
190
            if hasattr(e, 'errno'):
 
191
                mutter('io error: %s %s for has url: %r', 
 
192
                    e.errno, errno.errorcode.get(e.errno), path)
 
193
                if e.errno == errno.ENOENT:
 
194
                    raise NoSuchFile(path, extra=e)
152
195
            raise ConnectionError(msg = "Error retrieving %s: %s" 
153
196
                             % (self.abspath(relpath), str(e)),
154
197
                             orig_error=e)
155
198
 
156
 
    def put(self, relpath, f):
 
199
    def put(self, relpath, f, mode=None):
157
200
        """Copy the file-like or string object into the location.
158
201
 
159
202
        :param relpath: Location to put the contents, relative to base.
161
204
        """
162
205
        raise TransportNotPossible('http PUT not supported')
163
206
 
164
 
    def mkdir(self, relpath):
 
207
    def mkdir(self, relpath, mode=None):
165
208
        """Create a directory at the given path."""
166
209
        raise TransportNotPossible('http does not support mkdir()')
167
210
 
175
218
        """Copy the item at rel_from to the location at rel_to"""
176
219
        raise TransportNotPossible('http does not support copy()')
177
220
 
178
 
    def copy_to(self, relpaths, other, pb=None):
 
221
    def copy_to(self, relpaths, other, mode=None, pb=None):
179
222
        """Copy a set of entries from self into another Transport.
180
223
 
181
224
        :param relpaths: A list/generator of entries to be copied.
189
232
        if isinstance(other, HttpTransport):
190
233
            raise TransportNotPossible('http cannot be the target of copy_to()')
191
234
        else:
192
 
            return super(HttpTransport, self).copy_to(relpaths, other, pb=pb)
 
235
            return super(HttpTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
193
236
 
194
237
    def move(self, rel_from, rel_to):
195
238
        """Move the item at rel_from to the location at rel_to"""