3
An implementation of the Transport object for http access.
6
from bzrlib.transport import Transport, register_transport, \
7
TransportNotPossible, NoSuchFile, NonRelativePath, \
10
from cStringIO import StringIO
13
from bzrlib.errors import BzrError, BzrCheckError
14
from bzrlib.branch import Branch, BZR_BRANCH_FORMAT
15
from bzrlib.trace import mutter
17
# velocitynet.com.au transparently proxies connections and thereby
18
# breaks keep-alive -- sucks!
21
ENABLE_URLGRABBER = False
26
import urlgrabber.keepalive
27
import urlgrabber.grabber
28
urlgrabber.keepalive.DEBUG = 0
29
def get_url(path, compressed=False):
34
mutter("grab url %s" % url)
35
url_f = urlgrabber.urlopen(url, keepalive=1, close_connection=0)
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))
45
def get_url(url, compressed=False):
49
mutter("get_url %s" % url)
50
url_f = urllib2.urlopen(url)
52
return gzip.GzipFile(fileobj=StringIO(url_f.read()))
56
def _find_remote_root(url):
57
"""Return the prefix URL that corresponds to the branch root."""
61
ff = get_url(url + '/.bzr/branch-format')
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"
72
except urllib2.URLError:
78
raise BzrError('no branch root found for URL %s' % orig_url)
82
class HttpTransportError(TransportError):
85
class HttpTransport(Transport):
86
"""This is the transport agent for http:// access.
88
TODO: Implement pipelined versions of all of the *_multi() functions.
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
99
def should_cache(self):
100
"""Return True if the data pulled across should be cached locally.
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.
110
return HttpTransport(self.base)
112
return HttpTransport(self.abspath(offset))
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
118
if isinstance(relpath, basestring):
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)
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))
130
return abspath[pl:].lstrip('/')
132
def has(self, relpath):
133
"""Does the target location exist?
135
TODO: HttpTransport.has() should use a HEAD request,
136
not a full GET request.
139
f = get_url(self.abspath(relpath))
143
except urllib2.URLError:
146
if e.errno == errno.ENOENT:
148
raise HttpTransportError(orig_error=e)
150
def get(self, relpath, decode=False):
151
"""Get the file at the given relative path.
153
:param relpath: The relative path to the file
156
return get_url(self.abspath(relpath))
158
raise NoSuchFile(orig_error=e)
159
except urllib2.URLError, e:
160
raise NoSuchFile(orig_error=e)
162
raise NoSuchFile(orig_error=e)
164
raise HttpTransportError(orig_error=e)
166
def put(self, relpath, f):
167
"""Copy the file-like or string object into the location.
169
:param relpath: Location to put the contents, relative to base.
170
:param f: File-like or string object.
172
raise TransportNotPossible('http PUT not supported')
174
def mkdir(self, relpath):
175
"""Create a directory at the given path."""
176
raise TransportNotPossible('http does not support mkdir()')
178
def append(self, relpath, f):
179
"""Append the text in the file-like object into the final
182
raise TransportNotPossible('http does not support append()')
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()')
188
def copy_to(self, relpaths, other, pb=None):
189
"""Copy a set of entries from self into another Transport.
191
:param relpaths: A list/generator of entries to be copied.
193
TODO: if other is LocalTransport, is it possible to
194
do better than put(get())?
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()')
202
return super(HttpTransport, self).copy_to(relpaths, other, pb=pb)
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()')
208
def delete(self, relpath):
209
"""Delete the item at relpath"""
210
raise TransportNotPossible('http does not support delete()')
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.
218
raise NotImplementedError
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.
225
raise TransportNotPossible('http does not support list_dir()')
227
def stat(self, relpath):
228
"""Return the stat information for a file.
230
raise TransportNotPossible('http does not support stat()')
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()
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):
243
return BogusLock(relpath)
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
249
:return: A lock object, which should be passed to Transport.unlock()
251
raise TransportNotPossible('http does not support lock_write()')
253
register_transport('http://', HttpTransport)
254
register_transport('https://', HttpTransport)