/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
1
# Copyright (C) 2006 Canonical Ltd
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
2
#
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
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.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
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.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
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
"""http/https transport using pycurl"""
18
19
# TODO: test reporting of http errors
20
1616.1.9 by Martin Pool
Set Cache-control: max-age=0 and Pragma: no-cache
21
# TODO: Transport option to control caching of particular requests; broadly we
22
# would want to offer "caching allowed" or "must revalidate", depending on
23
# whether we expect a particular file will be modified after it's committed.
24
# It's probably safer to just always revalidate.  mbp 20060321
25
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
26
import os
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
27
from StringIO import StringIO
1540.3.5 by Martin Pool
Raise exception if unicode is passed to transport; formatting fixes
28
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
29
import bzrlib
1540.3.5 by Martin Pool
Raise exception if unicode is passed to transport; formatting fixes
30
from bzrlib.errors import (TransportNotPossible, NoSuchFile,
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
31
                           TransportError, ConnectionError,
32
                           DependencyNotPresent)
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
33
from bzrlib.trace import mutter
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
34
from bzrlib.transport import register_urlparse_netloc_protocol
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
35
from bzrlib.transport.http import HttpTransportBase, extract_auth, HttpServer
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
36
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
37
try:
38
    import pycurl
39
except ImportError, e:
40
    mutter("failed to import pycurl: %s", e)
41
    raise DependencyNotPresent('pycurl', e)
42
43
1636.1.2 by Robert Collins
More review fixen to the relpath at '/' fixes.
44
register_urlparse_netloc_protocol('http+pycurl')
1636.1.1 by Robert Collins
Fix calling relpath() and abspath() on transports at their root.
45
46
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
47
class PyCurlTransport(HttpTransportBase):
1540.3.3 by Martin Pool
Review updates of pycurl transport
48
    """http client transport using pycurl
49
50
    PyCurl is a Python binding to the C "curl" multiprotocol client.
51
52
    This transport can be significantly faster than the builtin Python client. 
53
    Advantages include: DNS caching, connection keepalive, and ability to 
54
    set headers to allow caching.
55
    """
56
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
57
    def __init__(self, base):
58
        super(PyCurlTransport, self).__init__(base)
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
59
        mutter('using pycurl %s' % pycurl.version)
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
60
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
61
    def should_cache(self):
62
        """Return True if the data pulled across should be cached locally.
63
        """
64
        return True
65
1540.3.3 by Martin Pool
Review updates of pycurl transport
66
    def has(self, relpath):
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
67
        curl = pycurl.Curl()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
68
        abspath = self._real_abspath(relpath)
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
69
        curl.setopt(pycurl.URL, abspath)
70
        curl.setopt(pycurl.FOLLOWLOCATION, 1) # follow redirect responses
71
        self._set_curl_options(curl)
1540.3.3 by Martin Pool
Review updates of pycurl transport
72
        # don't want the body - ie just do a HEAD request
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
73
        curl.setopt(pycurl.NOBODY, 1)
74
        self._curl_perform(curl)
75
        code = curl.getinfo(pycurl.HTTP_CODE)
76
        if code == 404: # not found
77
            return False
78
        elif code in (200, 302): # "ok", "found"
79
            return True
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
80
        elif code == 0:
81
            self._raise_curl_connection_error(curl)
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
82
        else:
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
83
            self._raise_curl_http_error(curl)
1540.3.3 by Martin Pool
Review updates of pycurl transport
84
        
1540.3.26 by Martin Pool
[merge] bzr.dev; pycurl not updated for readv yet
85
    def _get(self, relpath, ranges):
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
86
        curl = pycurl.Curl()
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
87
        abspath = self._real_abspath(relpath)
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
88
        sio = StringIO()
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
89
        curl.setopt(pycurl.URL, abspath)
90
        self._set_curl_options(curl)
91
        curl.setopt(pycurl.WRITEFUNCTION, sio.write)
92
        curl.setopt(pycurl.NOBODY, 0)
1540.3.27 by Martin Pool
Integrate http range support for pycurl
93
        if ranges is not None:
94
            assert len(ranges) == 1
95
            # multiple ranges not supported yet because we can't decode the
96
            # response
97
            curl.setopt(pycurl.RANGE, '%d-%d' % ranges[0])
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
98
        self._curl_perform(curl)
99
        code = curl.getinfo(pycurl.HTTP_CODE)
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
100
        if code == 404:
101
            raise NoSuchFile(abspath)
1540.3.13 by Martin Pool
Curl should follow http redirects, the same as urllib
102
        elif code == 200:
103
            sio.seek(0)
1540.3.27 by Martin Pool
Integrate http range support for pycurl
104
            return code, sio
105
        elif code == 206 and (ranges is not None):
106
            sio.seek(0)
107
            return code, sio
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
108
        elif code == 0:
109
            self._raise_curl_connection_error(curl)
1540.3.13 by Martin Pool
Curl should follow http redirects, the same as urllib
110
        else:
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
111
            self._raise_curl_http_error(curl)
112
113
    def _raise_curl_connection_error(self, curl):
114
        curl_errno = curl.getinfo(pycurl.OS_ERRNO)
115
        url = curl.getinfo(pycurl.EFFECTIVE_URL)
116
        raise ConnectionError('curl connection error (%s) on %s'
117
                              % (os.strerror(curl_errno), url))
118
119
    def _raise_curl_http_error(self, curl):
120
        code = curl.getinfo(pycurl.HTTP_CODE)
121
        url = curl.getinfo(pycurl.EFFECTIVE_URL)
122
        raise TransportError('http error %d probing for %s' %
123
                             (code, url))
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
124
1540.3.13 by Martin Pool
Curl should follow http redirects, the same as urllib
125
    def _set_curl_options(self, curl):
126
        """Set options for all requests"""
1540.3.11 by Martin Pool
doc
127
        # There's no way in http/1.0 to say "must revalidate"; we don't want
128
        # to force it to always retrieve.  so just turn off the default Pragma
129
        # provided by Curl.
1616.1.9 by Martin Pool
Set Cache-control: max-age=0 and Pragma: no-cache
130
        headers = ['Cache-control: max-age=0',
131
                   'Pragma: no-cache']
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
132
        ## curl.setopt(pycurl.VERBOSE, 1)
1616.1.9 by Martin Pool
Set Cache-control: max-age=0 and Pragma: no-cache
133
        # TODO: maybe include a summary of the pycurl version
134
        ua_str = 'bzr/%s (pycurl)' % (bzrlib.__version__)
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
135
        curl.setopt(pycurl.USERAGENT, ua_str)
1540.3.13 by Martin Pool
Curl should follow http redirects, the same as urllib
136
        curl.setopt(pycurl.HTTPHEADER, headers)
137
        curl.setopt(pycurl.FOLLOWLOCATION, 1) # follow redirect responses
1540.3.3 by Martin Pool
Review updates of pycurl transport
138
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
139
    def _curl_perform(self, curl):
1540.3.3 by Martin Pool
Review updates of pycurl transport
140
        """Perform curl operation and translate exceptions."""
141
        try:
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
142
            curl.perform()
1540.3.3 by Martin Pool
Review updates of pycurl transport
143
        except pycurl.error, e:
144
            # XXX: There seem to be no symbolic constants for these values.
145
            if e[0] == 6:
146
                # couldn't resolve host
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
147
                raise NoSuchFile(curl.getinfo(pycurl.EFFECTIVE_URL), e)
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
148
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
149
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
150
class HttpServer_PyCurl(HttpServer):
151
    """Subclass of HttpServer that gives http+pycurl urls.
152
153
    This is for use in testing: connections to this server will always go
154
    through pycurl where possible.
155
    """
156
157
    # urls returned by this server should require the pycurl client impl
158
    _url_protocol = 'http+pycurl'
159
160
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
161
def get_test_permutations():
162
    """Return the permutations to be used in testing."""
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
163
    return [(PyCurlTransport, HttpServer_PyCurl),
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
164
            ]