1
# Copyright (C) 2005 Canonical Ltd
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.
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.
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
"""Implementation of Transport over http.
3
An implementation of the Transport object for http access.
19
from bzrlib.transport import Transport, register_transport
20
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
21
NonRelativePath, TransportError)
6
from bzrlib.transport import Transport, register_transport, \
7
TransportNotPossible, NoSuchFile, NonRelativePath, \
23
10
from cStringIO import StringIO
27
13
from bzrlib.errors import BzrError, BzrCheckError
28
from bzrlib.branch import Branch
14
from bzrlib.branch import Branch, BZR_BRANCH_FORMAT
29
15
from bzrlib.trace import mutter
31
17
# velocitynet.com.au transparently proxies connections and thereby
32
18
# breaks keep-alive -- sucks!
37
mutter("get_url %s" % url)
38
url_f = urllib2.urlopen(url)
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)
41
82
class HttpTransportError(TransportError):
80
118
if isinstance(relpath, basestring):
81
119
relpath = [relpath]
82
basepath = self._path.split('/')
83
if len(basepath) > 0 and basepath[-1] == '':
84
basepath = basepath[:-1]
89
# In most filesystems, a request for the parent
90
# of root, just returns root.
98
# Possibly, we could use urlparse.urljoin() here, but
99
# I'm concerned about when it chooses to strip the last
100
# portion of the path, and when it doesn't.
101
path = '/'.join(basepath)
102
return urlparse.urlunparse((self._proto,
103
self._host, path, '', '', ''))
120
baseurl = self.base.rstrip('/')
121
return '/'.join([baseurl] + relpath)
105
123
def relpath(self, abspath):
106
124
if not abspath.startswith(self.base):
145
154
return get_url(self.abspath(relpath))
146
except (BzrError, urllib2.URLError, IOError), e:
156
raise NoSuchFile(orig_error=e)
157
except urllib2.URLError, e:
158
raise NoSuchFile(orig_error=e)
147
160
raise NoSuchFile(orig_error=e)
148
161
except Exception,e:
149
162
raise HttpTransportError(orig_error=e)
151
def get_partial(self, relpath, start, length=None):
152
"""Get just part of a file.
154
:param relpath: Path to the file, relative to base
155
:param start: The starting position to read from
156
:param length: The length to read. A length of None indicates
157
read to the end of the file.
158
:return: A file-like object containing at least the specified bytes.
159
Some implementations may return objects which can be read
160
past this length, but this is not guaranteed.
162
# TODO: You can make specialized http requests for just
163
# a portion of the file. Figure out how to do that.
164
# For now, urllib2 returns files that cannot seek() so
165
# we just read bytes off the beginning, until we
166
# get to the point that we care about.
167
f = self.get(relpath)
168
# TODO: read in smaller chunks, in case things are
169
# buffered internally.
173
164
def put(self, relpath, f):
174
165
"""Copy the file-like or string object into the location.
216
207
"""Delete the item at relpath"""
217
208
raise TransportNotPossible('http does not support delete()')
220
"""See Transport.listable."""
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()')
223
225
def stat(self, relpath):
224
226
"""Return the stat information for a file.