~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/_urllib.py

  • Committer: Martin Pool
  • Date: 2006-01-13 09:57:13 UTC
  • mto: This revision was merged to the branch mainline in revision 1611.
  • Revision ID: mbp@sourcefrog.net-20060113095713-1fa5912229a3898e
Review updates of pycurl transport

Split them out into 

  bzrlib.transport.http             common base
  bzrlib.transport.http._urllib     pure python
  bzrlib.transport.http._pycurl     calls pycurl

Update to work with robert's nice transport test multiplexer.

Add PyCurlTransport.has() which does just a HEAD request; should be faster.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
 
 
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.
 
7
 
 
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.
 
12
 
 
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
 
 
17
import urllib, urllib2
 
18
 
 
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)
 
24
 
 
25
 
 
26
class HttpTransport(HttpTransportBase):
 
27
    """Python urllib transport for http and https.
 
28
    """
 
29
 
 
30
    # TODO: Implement pipelined versions of all of the *_multi() functions.
 
31
 
 
32
    def __init__(self, base):
 
33
        """Set the base path where files will be stored."""
 
34
        super(HttpTransport, self).__init__(base)
 
35
 
 
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)
 
43
        return url_f
 
44
 
 
45
    def should_cache(self):
 
46
        """Return True if the data pulled across should be cached locally.
 
47
        """
 
48
        return True
 
49
 
 
50
    def has(self, relpath):
 
51
        """Does the target location exist?
 
52
 
 
53
        TODO: HttpTransport.has() should use a HEAD request,
 
54
        not a full GET request.
 
55
 
 
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
 
59
        the return code.
 
60
        """
 
61
        path = relpath
 
62
        try:
 
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.
 
67
            f.read()
 
68
            f.close()
 
69
            return True
 
70
        except urllib2.URLError, e:
 
71
            mutter('url error code: %s for has url: %r', e.code, path)
 
72
            if e.code == 404:
 
73
                return False
 
74
            raise
 
75
        except IOError, e:
 
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:
 
79
                return False
 
80
            raise TransportError(orig_error=e)
 
81
 
 
82
    def get(self, relpath):
 
83
        """Get the file at the given relative path.
 
84
 
 
85
        :param relpath: The relative path to the file
 
86
        """
 
87
        path = relpath
 
88
        try:
 
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)
 
93
            if e.code == 404:
 
94
                raise NoSuchFile(path, extra=e)
 
95
            raise
 
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)),
 
104
                             orig_error=e)
 
105
 
 
106
    def copy_to(self, relpaths, other, mode=None, pb=None):
 
107
        """Copy a set of entries from self into another Transport.
 
108
 
 
109
        :param relpaths: A list/generator of entries to be copied.
 
110
 
 
111
        TODO: if other is LocalTransport, is it possible to
 
112
              do better than put(get())?
 
113
        """
 
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()')
 
119
        else:
 
120
            return super(HttpTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
 
121
 
 
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()')
 
125
 
 
126
    def delete(self, relpath):
 
127
        """Delete the item at relpath"""
 
128
        raise TransportNotPossible('http does not support delete()')
 
129