1
# Copyright (C) 2005, 2006 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
17
import urllib, urllib2
19
from bzrlib.errors import BzrError
20
from bzrlib.trace import mutter
21
from bzrlib.transport.http import HttpTransportBase, extract_auth
22
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
23
TransportError, ConnectionError)
26
class HttpTransport(HttpTransportBase):
27
"""Python urllib transport for http and https.
30
# TODO: Implement pipelined versions of all of the *_multi() functions.
32
def __init__(self, base):
33
"""Set the base path where files will be stored."""
34
super(HttpTransport, self).__init__(base)
36
def _get_url(self, url):
37
mutter("get_url %s" % url)
38
manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
39
url = extract_auth(url, manager)
40
auth_handler = urllib2.HTTPBasicAuthHandler(manager)
41
opener = urllib2.build_opener(auth_handler)
42
url_f = opener.open(url)
45
def should_cache(self):
46
"""Return True if the data pulled across should be cached locally.
50
def has(self, relpath):
51
"""Does the target location exist?
53
TODO: HttpTransport.has() should use a HEAD request,
54
not a full GET request.
56
TODO: This should be changed so that we don't use
57
urllib2 and get an exception, the code path would be
58
cleaner if we just do an http HEAD request, and parse
63
path = self.abspath(relpath)
64
f = self._get_url(path)
65
# Without the read and then close()
66
# we tend to have busy sockets.
70
except urllib2.URLError, e:
71
mutter('url error code: %s for has url: %r', e.code, path)
76
mutter('io error: %s %s for has url: %r',
77
e.errno, errno.errorcode.get(e.errno), path)
78
if e.errno == errno.ENOENT:
80
raise TransportError(orig_error=e)
82
def get(self, relpath):
83
"""Get the file at the given relative path.
85
:param relpath: The relative path to the file
89
path = self.abspath(relpath)
90
return self._get_url(path)
91
except urllib2.HTTPError, e:
92
mutter('url error code: %s for has url: %r', e.code, path)
94
raise NoSuchFile(path, extra=e)
96
except (BzrError, IOError), e:
97
if hasattr(e, 'errno'):
98
mutter('io error: %s %s for has url: %r',
99
e.errno, errno.errorcode.get(e.errno), path)
100
if e.errno == errno.ENOENT:
101
raise NoSuchFile(path, extra=e)
102
raise ConnectionError(msg = "Error retrieving %s: %s"
103
% (self.abspath(relpath), str(e)),
106
def copy_to(self, relpaths, other, mode=None, pb=None):
107
"""Copy a set of entries from self into another Transport.
109
:param relpaths: A list/generator of entries to be copied.
111
TODO: if other is LocalTransport, is it possible to
112
do better than put(get())?
114
# At this point HttpTransport might be able to check and see if
115
# the remote location is the same, and rather than download, and
116
# then upload, it could just issue a remote copy_this command.
117
if isinstance(other, HttpTransport):
118
raise TransportNotPossible('http cannot be the target of copy_to()')
120
return super(HttpTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
122
def move(self, rel_from, rel_to):
123
"""Move the item at rel_from to the location at rel_to"""
124
raise TransportNotPossible('http does not support move()')
126
def delete(self, relpath):
127
"""Delete the item at relpath"""
128
raise TransportNotPossible('http does not support delete()')