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
return '/'.join([baseurl] + relpath)
123
def relpath(self, abspath):
124
if not abspath.startswith(self.base):
125
raise NonRelativePath('path %r is not under base URL %r'
126
% (abspath, self.base))
128
return abspath[pl:].lstrip('/')
130
def has(self, relpath):
131
"""Does the target location exist?
133
TODO: HttpTransport.has() should use a HEAD request,
134
not a full GET request.
137
f = get_url(self.abspath(relpath))
141
except urllib2.URLError:
144
if e.errno == errno.ENOENT:
146
raise HttpTransportError(orig_error=e)
148
def get(self, relpath, decode=False):
149
"""Get the file at the given relative path.
151
:param relpath: The relative path to the file
154
return get_url(self.abspath(relpath))
156
raise NoSuchFile(orig_error=e)
157
except urllib2.URLError, e:
158
raise NoSuchFile(orig_error=e)
160
raise NoSuchFile(orig_error=e)
162
raise HttpTransportError(orig_error=e)
164
def put(self, relpath, f):
165
"""Copy the file-like or string object into the location.
167
:param relpath: Location to put the contents, relative to base.
168
:param f: File-like or string object.
170
raise TransportNotPossible('http PUT not supported')
172
def mkdir(self, relpath):
173
"""Create a directory at the given path."""
174
raise TransportNotPossible('http does not support mkdir()')
176
def append(self, relpath, f):
177
"""Append the text in the file-like object into the final
180
raise TransportNotPossible('http does not support append()')
182
def copy(self, rel_from, rel_to):
183
"""Copy the item at rel_from to the location at rel_to"""
184
raise TransportNotPossible('http does not support copy()')
186
def copy_to(self, relpaths, other, pb=None):
187
"""Copy a set of entries from self into another Transport.
189
:param relpaths: A list/generator of entries to be copied.
191
TODO: if other is LocalTransport, is it possible to
192
do better than put(get())?
194
# At this point HttpTransport might be able to check and see if
195
# the remote location is the same, and rather than download, and
196
# then upload, it could just issue a remote copy_this command.
197
if isinstance(other, HttpTransport):
198
raise TransportNotPossible('http cannot be the target of copy_to()')
200
return super(HttpTransport, self).copy_to(relpaths, other, pb=pb)
202
def move(self, rel_from, rel_to):
203
"""Move the item at rel_from to the location at rel_to"""
204
raise TransportNotPossible('http does not support move()')
206
def delete(self, relpath):
207
"""Delete the item at relpath"""
208
raise TransportNotPossible('http does not support delete()')
210
def async_get(self, relpath):
211
"""Make a request for an file at the given location, but
212
don't worry about actually getting it yet.
216
raise NotImplementedError
218
def list_dir(self, relpath):
219
"""Return a list of all files at the given location.
220
WARNING: many transports do not support this, so trying avoid using
221
it if at all possible.
223
raise TransportNotPossible('http does not support list_dir()')
225
def stat(self, relpath):
226
"""Return the stat information for a file.
228
raise TransportNotPossible('http does not support stat()')
230
def lock_read(self, relpath):
231
"""Lock the given file for shared (read) access.
232
:return: A lock object, which should be passed to Transport.unlock()
234
# The old RemoteBranch ignore lock for reading, so we will
235
# continue that tradition and return a bogus lock object.
236
class BogusLock(object):
237
def __init__(self, path):
241
return BogusLock(relpath)
243
def lock_write(self, relpath):
244
"""Lock the given file for exclusive (write) access.
245
WARNING: many transports do not support this, so trying avoid using it
247
:return: A lock object, which should be passed to Transport.unlock()
249
raise TransportNotPossible('http does not support lock_write()')
251
register_transport('http://', HttpTransport)
252
register_transport('https://', HttpTransport)