~bzr-pqm/bzr/bzr.dev

1540.3.3 by Martin Pool
Review updates of pycurl transport
1
# Copyright (C) 2005, 2006 Canonical Ltd
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
2
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
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.
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
7
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
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.
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
12
#
1540.3.3 by Martin Pool
Review updates of pycurl transport
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
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
17
import errno
1540.3.3 by Martin Pool
Review updates of pycurl transport
18
import urllib, urllib2
1750.1.2 by Michael Ellerman
Add support for HTTP multipart ranges and hook it into http+urllib.
19
import errno
20
from StringIO import StringIO
1540.3.3 by Martin Pool
Review updates of pycurl transport
21
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
22
import bzrlib  # for the version
1786.1.33 by John Arbash Meinel
Cleanup pass #2
23
from bzrlib.errors import (TransportNotPossible, NoSuchFile, BzrError,
24
                           TransportError, ConnectionError)
1540.3.3 by Martin Pool
Review updates of pycurl transport
25
from bzrlib.trace import mutter
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
26
from bzrlib.transport import register_urlparse_netloc_protocol
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
27
from bzrlib.transport.http import (HttpTransportBase, HttpServer,
28
                                   extract_auth, response)
1540.3.3 by Martin Pool
Review updates of pycurl transport
29
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
30
register_urlparse_netloc_protocol('http+urllib')
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
31
32
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
33
class Request(urllib2.Request):
34
    """Request object for urllib2 that allows the method to be overridden."""
35
36
    method = None
37
38
    def get_method(self):
39
        if self.method is not None:
40
            return self.method
41
        else:
42
            return urllib2.Request.get_method(self)
43
44
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
45
class HttpTransport_urllib(HttpTransportBase):
1786.1.33 by John Arbash Meinel
Cleanup pass #2
46
    """Python urllib transport for http and https."""
1540.3.3 by Martin Pool
Review updates of pycurl transport
47
48
    # TODO: Implement pipelined versions of all of the *_multi() functions.
49
1786.1.32 by John Arbash Meinel
cleanup pass, allow pycurl connections to be shared between transports.
50
    def __init__(self, base, from_transport=None):
1540.3.3 by Martin Pool
Review updates of pycurl transport
51
        """Set the base path where files will be stored."""
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
52
        super(HttpTransport_urllib, self).__init__(base)
1786.1.32 by John Arbash Meinel
cleanup pass, allow pycurl connections to be shared between transports.
53
        # HttpTransport_urllib doesn't maintain any per-transport state yet
54
        # so nothing to do with from_transport
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
55
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
56
    def _get(self, relpath, ranges, tail_amount=0):
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
57
        path = relpath
58
        try:
59
            path = self._real_abspath(relpath)
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
60
            resp = self._get_url_impl(path, method='GET', ranges=ranges,
61
                                      tail_amount=tail_amount)
62
            return resp.code, response.handle_response(path,
63
                                resp.code, resp.headers, resp)
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
64
        except urllib2.HTTPError, e:
65
            mutter('url error code: %s for has url: %r', e.code, path)
66
            if e.code == 404:
67
                raise NoSuchFile(path, extra=e)
68
            raise
69
        except (BzrError, IOError), e:
70
            if hasattr(e, 'errno'):
71
                mutter('io error: %s %s for has url: %r',
72
                    e.errno, errno.errorcode.get(e.errno), path)
73
                if e.errno == errno.ENOENT:
74
                    raise NoSuchFile(path, extra=e)
75
            raise ConnectionError(msg = "Error retrieving %s: %s" 
76
                             % (self.abspath(relpath), str(e)),
77
                             orig_error=e)
78
1786.1.9 by John Arbash Meinel
Make tail_amount optional, so that has() still works.
79
    def _get_url_impl(self, url, method, ranges, tail_amount=0):
1540.3.27 by Martin Pool
Integrate http range support for pycurl
80
        """Actually pass get request into urllib
81
82
        :returns: urllib Response object
83
        """
1540.3.3 by Martin Pool
Review updates of pycurl transport
84
        manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
85
        url = extract_auth(url, manager)
86
        auth_handler = urllib2.HTTPBasicAuthHandler(manager)
87
        opener = urllib2.build_opener(auth_handler)
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
88
        request = Request(url)
89
        request.method = method
1616.1.11 by Martin Pool
Add Pragma and Cache-control headers to urllib requests
90
        request.add_header('Pragma', 'no-cache')
91
        request.add_header('Cache-control', 'max-age=0')
1786.1.33 by John Arbash Meinel
Cleanup pass #2
92
        request.add_header('User-Agent',
93
                           'bzr/%s (urllib)' % (bzrlib.__version__,))
1786.1.8 by John Arbash Meinel
[merge] Johan Rydberg test updates
94
        if ranges or tail_amount:
1786.1.38 by John Arbash Meinel
small bug found by mpee
95
            bytes = 'bytes=' + self.range_header(ranges, tail_amount)
1786.1.36 by John Arbash Meinel
pycurl expects us to just set the range of bytes, not including bytes=
96
            request.add_header('Range', bytes)
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
97
        response = opener.open(request)
98
        return response
1540.3.3 by Martin Pool
Review updates of pycurl transport
99
100
    def should_cache(self):
101
        """Return True if the data pulled across should be cached locally.
102
        """
103
        return True
104
105
    def has(self, relpath):
106
        """Does the target location exist?
107
        """
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
108
        abspath = self._real_abspath(relpath)
1540.3.3 by Martin Pool
Review updates of pycurl transport
109
        try:
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
110
            f = self._get_url_impl(abspath, 'HEAD', [])
1540.3.3 by Martin Pool
Review updates of pycurl transport
111
            # Without the read and then close()
112
            # we tend to have busy sockets.
113
            f.read()
114
            f.close()
115
            return True
116
        except urllib2.URLError, e:
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
117
            mutter('url error code: %s for has url: %r', e.code, abspath)
1540.3.3 by Martin Pool
Review updates of pycurl transport
118
            if e.code == 404:
119
                return False
120
            raise
121
        except IOError, e:
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
122
            mutter('io error: %s %s for has url: %r',
123
                e.errno, errno.errorcode.get(e.errno), abspath)
1540.3.3 by Martin Pool
Review updates of pycurl transport
124
            if e.errno == errno.ENOENT:
125
                return False
126
            raise TransportError(orig_error=e)
127
128
    def copy_to(self, relpaths, other, mode=None, pb=None):
129
        """Copy a set of entries from self into another Transport.
130
131
        :param relpaths: A list/generator of entries to be copied.
132
133
        TODO: if other is LocalTransport, is it possible to
134
              do better than put(get())?
135
        """
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
136
        # At this point HttpTransport_urllib might be able to check and see if
1540.3.3 by Martin Pool
Review updates of pycurl transport
137
        # the remote location is the same, and rather than download, and
138
        # then upload, it could just issue a remote copy_this command.
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
139
        if isinstance(other, HttpTransport_urllib):
1540.3.3 by Martin Pool
Review updates of pycurl transport
140
            raise TransportNotPossible('http cannot be the target of copy_to()')
141
        else:
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
142
            return super(HttpTransport_urllib, self).copy_to(relpaths, other, mode=mode, pb=pb)
1540.3.3 by Martin Pool
Review updates of pycurl transport
143
144
    def move(self, rel_from, rel_to):
145
        """Move the item at rel_from to the location at rel_to"""
146
        raise TransportNotPossible('http does not support move()')
147
148
    def delete(self, relpath):
149
        """Delete the item at relpath"""
150
        raise TransportNotPossible('http does not support delete()')
151
1540.3.25 by Martin Pool
New 'http+urllib' scheme
152
153
class HttpServer_urllib(HttpServer):
154
    """Subclass of HttpServer that gives http+urllib urls.
155
156
    This is for use in testing: connections to this server will always go
157
    through urllib where possible.
158
    """
159
160
    # urls returned by this server should require the urllib client impl
161
    _url_protocol = 'http+urllib'
162
163
1540.3.6 by Martin Pool
[merge] update from bzr.dev
164
def get_test_permutations():
165
    """Return the permutations to be used in testing."""
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
166
    return [(HttpTransport_urllib, HttpServer_urllib),
1540.3.6 by Martin Pool
[merge] update from bzr.dev
167
            ]