/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/http.py

  • Committer: John Arbash Meinel
  • Date: 2005-09-16 14:57:59 UTC
  • mto: (1393.2.1)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: john@arbash-meinel.com-20050916145758-ad06c9ae86840f17
Merged up-to-date against mainline, still broken.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
"""\
 
3
An implementation of the Transport object for http access.
 
4
"""
 
5
 
 
6
from bzrlib.transport import Transport, register_transport, \
 
7
    TransportNotPossible, NoSuchFile, NonRelativePath, \
 
8
    TransportError
 
9
import os, errno
 
10
from cStringIO import StringIO
 
11
import urllib2
 
12
 
 
13
from bzrlib.errors import BzrError, BzrCheckError
 
14
from bzrlib.branch import Branch, BZR_BRANCH_FORMAT
 
15
from bzrlib.trace import mutter
 
16
 
 
17
# velocitynet.com.au transparently proxies connections and thereby
 
18
# breaks keep-alive -- sucks!
 
19
 
 
20
 
 
21
ENABLE_URLGRABBER = False
 
22
 
 
23
 
 
24
if ENABLE_URLGRABBER:
 
25
    import urlgrabber
 
26
    import urlgrabber.keepalive
 
27
    import urlgrabber.grabber
 
28
    urlgrabber.keepalive.DEBUG = 0
 
29
    def get_url(path, compressed=False):
 
30
        try:
 
31
            url = path
 
32
            if compressed:
 
33
                url += '.gz'
 
34
            mutter("grab url %s" % url)
 
35
            url_f = urlgrabber.urlopen(url, keepalive=1, close_connection=0)
 
36
            if not compressed:
 
37
                return url_f
 
38
            else:
 
39
                return gzip.GzipFile(fileobj=StringIO(url_f.read()))
 
40
        except urllib2.URLError, e:
 
41
            raise BzrError("remote fetch failed: %r: %s" % (url, e))
 
42
        except urlgrabber.grabber.URLGrabError, e:
 
43
            raise BzrError("remote fetch failed: %r: %s" % (url, e))
 
44
else:
 
45
    def get_url(url, compressed=False):
 
46
        import urllib2
 
47
        if compressed:
 
48
            url += '.gz'
 
49
        mutter("get_url %s" % url)
 
50
        url_f = urllib2.urlopen(url)
 
51
        if compressed:
 
52
            return gzip.GzipFile(fileobj=StringIO(url_f.read()))
 
53
        else:
 
54
            return url_f
 
55
 
 
56
def _find_remote_root(url):
 
57
    """Return the prefix URL that corresponds to the branch root."""
 
58
    orig_url = url
 
59
    while True:
 
60
        try:
 
61
            ff = get_url(url + '/.bzr/branch-format')
 
62
 
 
63
            fmt = ff.read()
 
64
            ff.close()
 
65
 
 
66
            fmt = fmt.rstrip('\r\n')
 
67
            if fmt != BZR_BRANCH_FORMAT.rstrip('\r\n'):
 
68
                raise BzrError("sorry, branch format %r not supported at url %s"
 
69
                               % (fmt, url))
 
70
            
 
71
            return url
 
72
        except urllib2.URLError:
 
73
            pass
 
74
 
 
75
        try:
 
76
            idx = url.rindex('/')
 
77
        except ValueError:
 
78
            raise BzrError('no branch root found for URL %s' % orig_url)
 
79
 
 
80
        url = url[:idx]        
 
81
        
 
82
class HttpTransportError(TransportError):
 
83
    pass
 
84
 
 
85
class HttpTransport(Transport):
 
86
    """This is the transport agent for http:// access.
 
87
    
 
88
    TODO: Implement pipelined versions of all of the *_multi() functions.
 
89
    """
 
90
 
 
91
    def __init__(self, base):
 
92
        """Set the base path where files will be stored."""
 
93
        assert base.startswith('http://') or base.startswith('https://')
 
94
        super(HttpTransport, self).__init__(base)
 
95
        # In the future we might actually connect to the remote host
 
96
        # rather than using get_url
 
97
        # self._connection = None
 
98
 
 
99
    def should_cache(self):
 
100
        """Return True if the data pulled across should be cached locally.
 
101
        """
 
102
        return True
 
103
 
 
104
    def clone(self, offset=None):
 
105
        """Return a new HttpTransport with root at self.base + offset
 
106
        For now HttpTransport does not actually connect, so just return
 
107
        a new HttpTransport object.
 
108
        """
 
109
        if offset is None:
 
110
            return HttpTransport(self.base)
 
111
        else:
 
112
            return HttpTransport(self.abspath(offset))
 
113
 
 
114
    def abspath(self, relpath):
 
115
        """Return the full url to the given relative path.
 
116
        This can be supplied with a string or a list
 
117
        """
 
118
        if isinstance(relpath, basestring):
 
119
            relpath = [relpath]
 
120
        baseurl = self.base.rstrip('/')
 
121
        # TODO: We should handle ".." in a more appropriate manner
 
122
        # Basically, we need to normalize the path.
 
123
        path = '/'.join([baseurl] + relpath)
 
124
 
 
125
    def relpath(self, abspath):
 
126
        if not abspath.startswith(self.base):
 
127
            raise NonRelativePath('path %r is not under base URL %r'
 
128
                           % (abspath, self.base))
 
129
        pl = len(self.base)
 
130
        return abspath[pl:].lstrip('/')
 
131
 
 
132
    def has(self, relpath):
 
133
        """Does the target location exist?
 
134
 
 
135
        TODO: HttpTransport.has() should use a HEAD request,
 
136
        not a full GET request.
 
137
        """
 
138
        try:
 
139
            f = get_url(self.abspath(relpath))
 
140
            return True
 
141
        except BzrError:
 
142
            return False
 
143
        except urllib2.URLError:
 
144
            return False
 
145
        except IOError, e:
 
146
            if e.errno == errno.ENOENT:
 
147
                return False
 
148
            raise HttpTransportError(orig_error=e)
 
149
 
 
150
    def get(self, relpath, decode=False):
 
151
        """Get the file at the given relative path.
 
152
 
 
153
        :param relpath: The relative path to the file
 
154
        """
 
155
        try:
 
156
            return get_url(self.abspath(relpath))
 
157
        except BzrError, e:
 
158
            raise NoSuchFile(orig_error=e)
 
159
        except urllib2.URLError, e:
 
160
            raise NoSuchFile(orig_error=e)
 
161
        except IOError, e:
 
162
            raise NoSuchFile(orig_error=e)
 
163
        except Exception,e:
 
164
            raise HttpTransportError(orig_error=e)
 
165
 
 
166
    def put(self, relpath, f):
 
167
        """Copy the file-like or string object into the location.
 
168
 
 
169
        :param relpath: Location to put the contents, relative to base.
 
170
        :param f:       File-like or string object.
 
171
        """
 
172
        raise TransportNotPossible('http PUT not supported')
 
173
 
 
174
    def mkdir(self, relpath):
 
175
        """Create a directory at the given path."""
 
176
        raise TransportNotPossible('http does not support mkdir()')
 
177
 
 
178
    def append(self, relpath, f):
 
179
        """Append the text in the file-like object into the final
 
180
        location.
 
181
        """
 
182
        raise TransportNotPossible('http does not support append()')
 
183
 
 
184
    def copy(self, rel_from, rel_to):
 
185
        """Copy the item at rel_from to the location at rel_to"""
 
186
        raise TransportNotPossible('http does not support copy()')
 
187
 
 
188
    def copy_to(self, relpaths, other, pb=None):
 
189
        """Copy a set of entries from self into another Transport.
 
190
 
 
191
        :param relpaths: A list/generator of entries to be copied.
 
192
 
 
193
        TODO: if other is LocalTransport, is it possible to
 
194
              do better than put(get())?
 
195
        """
 
196
        # At this point HttpTransport might be able to check and see if
 
197
        # the remote location is the same, and rather than download, and
 
198
        # then upload, it could just issue a remote copy_this command.
 
199
        if isinstance(other, HttpTransport):
 
200
            raise TransportNotPossible('http cannot be the target of copy_to()')
 
201
        else:
 
202
            return super(HttpTransport, self).copy_to(relpaths, other, pb=pb)
 
203
 
 
204
    def move(self, rel_from, rel_to):
 
205
        """Move the item at rel_from to the location at rel_to"""
 
206
        raise TransportNotPossible('http does not support move()')
 
207
 
 
208
    def delete(self, relpath):
 
209
        """Delete the item at relpath"""
 
210
        raise TransportNotPossible('http does not support delete()')
 
211
 
 
212
    def async_get(self, relpath):
 
213
        """Make a request for an file at the given location, but
 
214
        don't worry about actually getting it yet.
 
215
 
 
216
        :rtype: AsyncFile
 
217
        """
 
218
        raise NotImplementedError
 
219
 
 
220
    def list_dir(self, relpath):
 
221
        """Return a list of all files at the given location.
 
222
        WARNING: many transports do not support this, so trying avoid using
 
223
        it if at all possible.
 
224
        """
 
225
        raise TransportNotPossible('http does not support list_dir()')
 
226
 
 
227
    def stat(self, relpath):
 
228
        """Return the stat information for a file.
 
229
        """
 
230
        raise TransportNotPossible('http does not support stat()')
 
231
 
 
232
    def lock_read(self, relpath):
 
233
        """Lock the given file for shared (read) access.
 
234
        :return: A lock object, which should be passed to Transport.unlock()
 
235
        """
 
236
        # The old RemoteBranch ignore lock for reading, so we will
 
237
        # continue that tradition and return a bogus lock object.
 
238
        class BogusLock(object):
 
239
            def __init__(self, path):
 
240
                self.path = path
 
241
            def unlock(self):
 
242
                pass
 
243
        return BogusLock(relpath)
 
244
 
 
245
    def lock_write(self, relpath):
 
246
        """Lock the given file for exclusive (write) access.
 
247
        WARNING: many transports do not support this, so trying avoid using it
 
248
 
 
249
        :return: A lock object, which should be passed to Transport.unlock()
 
250
        """
 
251
        raise TransportNotPossible('http does not support lock_write()')
 
252
 
 
253
register_transport('http://', HttpTransport)
 
254
register_transport('https://', HttpTransport)
 
255