/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
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
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
2164.2.16 by Vincent Ladeuil
Add tests.
26
# TODO: Some refactoring could be done to avoid the strange idiom
27
# used to capture data and headers while setting up the request
28
# (and having to pass 'header' to _curl_perform to handle
29
# redirections) . This could be achieved by creating a
30
# specialized Curl object and returning code, headers and data
31
# from _curl_perform.  Not done because we may deprecate pycurl in the
32
# future -- vila 20070212
33
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
34
import os
1786.1.42 by John Arbash Meinel
Update _extract_headers, make it less generic, and non recursive.
35
from cStringIO import StringIO
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
36
import httplib
2298.5.1 by Alexander Belchenko
Bugfix #82086: Searching location of CA bundle for PyCurl in env variable (CURL_CA_BUNDLE), and on win32 along the PATH
37
import sys
1540.3.5 by Martin Pool
Raise exception if unicode is passed to transport; formatting fixes
38
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
39
from bzrlib import (
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
40
    debug,
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
41
    errors,
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
42
    trace,
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
43
    __version__ as bzrlib_version,
44
    )
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
45
import bzrlib
1540.3.18 by Martin Pool
Style review fixes (thanks robertc)
46
from bzrlib.trace import mutter
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
47
from bzrlib.transport.http import (
2298.5.1 by Alexander Belchenko
Bugfix #82086: Searching location of CA bundle for PyCurl in env variable (CURL_CA_BUNDLE), and on win32 along the PATH
48
    ca_bundle,
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
49
    HttpTransportBase,
50
    response,
51
    )
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
52
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
53
try:
54
    import pycurl
55
except ImportError, e:
56
    mutter("failed to import pycurl: %s", e)
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
57
    raise errors.DependencyNotPresent('pycurl', e)
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
58
1684.1.5 by Martin Pool
(patch) check that pycurl will actuall initialize as well as load (Alexander)
59
try:
60
    # see if we can actually initialize PyCurl - sometimes it will load but
61
    # fail to start up due to this bug:
62
    #  
63
    #   32. (At least on Windows) If libcurl is built with c-ares and there's
64
    #   no DNS server configured in the system, the ares_init() call fails and
65
    #   thus curl_easy_init() fails as well. This causes weird effects for
66
    #   people who use numerical IP addresses only.
67
    #
68
    # reported by Alexander Belchenko, 2006-04-26
69
    pycurl.Curl()
70
except pycurl.error, e:
71
    mutter("failed to initialize pycurl: %s", e)
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
72
    raise errors.DependencyNotPresent('pycurl', e)
1684.1.5 by Martin Pool
(patch) check that pycurl will actuall initialize as well as load (Alexander)
73
1540.3.7 by Martin Pool
Prepare to select a transport depending on what dependencies can be satisfied.
74
2872.6.1 by Vincent Ladeuil
Fix bug #147530 by enabling more robust error code definitions.
75
76
77
def _get_pycurl_errcode(symbol, default):
78
    """
79
    Returns the numerical error code for a symbol defined by pycurl.
80
81
    Different pycurl implementations define different symbols for error
82
    codes. Old versions never define some symbols (wether they can return the
83
    corresponding error code or not). The following addresses the problem by
84
    defining the symbols we care about.  Note: this allows to define symbols
85
    for errors that older versions will never return, which is fine.
86
    """
87
    return pycurl.__dict__.get(symbol, default)
88
89
CURLE_SSL_CACERT_BADFILE = _get_pycurl_errcode('E_SSL_CACERT_BADFILE', 77)
90
CURLE_COULDNT_CONNECT = _get_pycurl_errcode('E_COULDNT_CONNECT', 7)
91
CURLE_COULDNT_RESOLVE_HOST = _get_pycurl_errcode('E_COULDNT_RESOLVE_HOST', 6)
92
CURLE_COULDNT_RESOLVE_PROXY = _get_pycurl_errcode('E_COULDNT_RESOLVE_PROXY', 5)
93
CURLE_GOT_NOTHING = _get_pycurl_errcode('E_GOT_NOTHING', 52)
94
CURLE_PARTIAL_FILE = _get_pycurl_errcode('E_PARTIAL_FILE', 18)
95
96
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
97
class PyCurlTransport(HttpTransportBase):
1540.3.3 by Martin Pool
Review updates of pycurl transport
98
    """http client transport using pycurl
99
100
    PyCurl is a Python binding to the C "curl" multiprotocol client.
101
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
102
    This transport can be significantly faster than the builtin
103
    Python client.  Advantages include: DNS caching.
1540.3.3 by Martin Pool
Review updates of pycurl transport
104
    """
105
2485.8.59 by Vincent Ladeuil
Update from review comments.
106
    def __init__(self, base, _from_transport=None):
107
        super(PyCurlTransport, self).__init__(base,
108
                                              _from_transport=_from_transport)
2294.3.1 by Vincent Ladeuil
Fix #85305 by issuing an exception instead of a traceback.
109
        if base.startswith('https'):
110
            # Check availability of https into pycurl supported
111
            # protocols
112
            supported = pycurl.version_info()[8]
113
            if 'https' not in supported:
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
114
                raise errors.DependencyNotPresent('pycurl', 'no https support')
2298.5.1 by Alexander Belchenko
Bugfix #82086: Searching location of CA bundle for PyCurl in env variable (CURL_CA_BUNDLE), and on win32 along the PATH
115
        self.cabundle = ca_bundle.get_ca_path()
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
116
117
    def _get_curl(self):
118
        connection = self._get_connection()
119
        if connection is None:
120
            # First connection ever. There is no credentials for pycurl, either
121
            # the password was embedded in the URL or it's not needed. The
122
            # connection for pycurl is just the Curl object, it will not
2485.8.43 by Vincent Ladeuil
Cleaning.
123
            # connect to the http server until the first request (which had
124
            # just called us).
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
125
            connection = pycurl.Curl()
3133.1.2 by Vincent Ladeuil
Fix #177643 by making pycurl handle url-embedded credentials again.
126
            # First request, initialize credentials.
127
            auth = self._create_auth()
128
            # Proxy handling is out of reach, so we punt
129
            self._set_connection(connection, auth)
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
130
        return connection
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
131
1540.3.3 by Martin Pool
Review updates of pycurl transport
132
    def has(self, relpath):
1786.1.32 by John Arbash Meinel
cleanup pass, allow pycurl connections to be shared between transports.
133
        """See Transport.has()"""
134
        # We set NO BODY=0 in _get_full, so it should be safe
135
        # to re-use the non-range curl object
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
136
        curl = self._get_curl()
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
137
        abspath = self._remote_path(relpath)
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
138
        curl.setopt(pycurl.URL, abspath)
139
        self._set_curl_options(curl)
2018.2.28 by Andrew Bennetts
Changes in response to review: re-use _base_curl, rather than keeping a seperate _post_curl object; add docstring to test_http.RecordingServer, set is_user_error on some new exceptions.
140
        curl.setopt(pycurl.HTTPGET, 1)
1540.3.3 by Martin Pool
Review updates of pycurl transport
141
        # don't want the body - ie just do a HEAD request
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
142
        # This means "NO BODY" not 'nobody'
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
143
        curl.setopt(pycurl.NOBODY, 1)
2164.2.16 by Vincent Ladeuil
Add tests.
144
        # But we need headers to handle redirections
145
        header = StringIO()
146
        curl.setopt(pycurl.HEADERFUNCTION, header.write)
2004.1.16 by v.ladeuil+lp at free
Add tests against erroneous http status lines.
147
        # In some erroneous cases, pycurl will emit text on
148
        # stdout if we don't catch it (see InvalidStatus tests
149
        # for one such occurrence).
150
        blackhole = StringIO()
151
        curl.setopt(pycurl.WRITEFUNCTION, blackhole.write)
2164.2.16 by Vincent Ladeuil
Add tests.
152
        self._curl_perform(curl, header)
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
153
        code = curl.getinfo(pycurl.HTTP_CODE)
154
        if code == 404: # not found
155
            return False
2164.2.16 by Vincent Ladeuil
Add tests.
156
        elif code == 200: # "ok"
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
157
            return True
158
        else:
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
159
            self._raise_curl_http_error(curl)
2000.3.1 by v.ladeuil+lp at free
Better connection sharing by using only one curl object.
160
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
161
    def _get(self, relpath, offsets, tail_amount=0):
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
162
        # This just switches based on the type of request
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
163
        if offsets is not None or tail_amount not in (0, None):
164
            return self._get_ranged(relpath, offsets, tail_amount=tail_amount)
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
165
        else:
2164.2.5 by v.ladeuil+lp at free
Simpler implementation using inspect. 'hints' is a kwargs.
166
            return self._get_full(relpath)
2000.3.1 by v.ladeuil+lp at free
Better connection sharing by using only one curl object.
167
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
168
    def _setup_get_request(self, curl, relpath):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
169
        # Make sure we do a GET request. versions > 7.14.1 also set the
170
        # NO BODY flag, but we'll do it ourselves in case it is an older
171
        # pycurl version
172
        curl.setopt(pycurl.NOBODY, 0)
173
        curl.setopt(pycurl.HTTPGET, 1)
174
        return self._setup_request(curl, relpath)
175
176
    def _setup_request(self, curl, relpath):
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
177
        """Do the common setup stuff for making a request
178
179
        :param curl: The curl object to place the request on
180
        :param relpath: The relative path that we want to get
181
        :return: (abspath, data, header) 
182
                 abspath: full url
183
                 data: file that will be filled with the body
184
                 header: file that will be filled with the headers
185
        """
2485.8.25 by Vincent Ladeuil
Separate abspath from _remote_path, the intents are different.
186
        abspath = self._remote_path(relpath)
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
187
        curl.setopt(pycurl.URL, abspath)
188
        self._set_curl_options(curl)
189
190
        data = StringIO()
191
        header = StringIO()
192
        curl.setopt(pycurl.WRITEFUNCTION, data.write)
193
        curl.setopt(pycurl.HEADERFUNCTION, header.write)
194
195
        return abspath, data, header
196
2164.2.5 by v.ladeuil+lp at free
Simpler implementation using inspect. 'hints' is a kwargs.
197
    def _get_full(self, relpath):
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
198
        """Make a request for the entire file"""
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
199
        curl = self._get_curl()
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
200
        abspath, data, header = self._setup_get_request(curl, relpath)
2164.2.16 by Vincent Ladeuil
Add tests.
201
        self._curl_perform(curl, header)
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
202
203
        code = curl.getinfo(pycurl.HTTP_CODE)
204
        data.seek(0)
205
206
        if code == 404:
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
207
            raise errors.NoSuchFile(abspath)
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
208
        if code != 200:
2004.1.25 by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :)
209
            self._raise_curl_http_error(
210
                curl, 'expected 200 or 404 for full response.')
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
211
212
        return code, data
213
3059.2.11 by Vincent Ladeuil
Fix typos mentioned by spiv.
214
    # The parent class use 0 to minimize the requests, but since we can't
3059.2.7 by Vincent Ladeuil
Allow pycurl users to watch the blinkenlights and fix a bug when ranges are contiguous.
215
    # exploit the results as soon as they are received (pycurl limitation) we'd
216
    # better issue more requests and provide a more responsive UI do the cost
217
    # of more latency costs.
3059.2.17 by Vincent Ladeuil
Limit GET requests by body size instead of number of ranges.
218
    # If you modify this, think about modifying the comment in http/__init__.py
3059.2.7 by Vincent Ladeuil
Allow pycurl users to watch the blinkenlights and fix a bug when ranges are contiguous.
219
    # too.
3059.2.17 by Vincent Ladeuil
Limit GET requests by body size instead of number of ranges.
220
    _get_max_size = 4 * 1024 * 1024
3059.2.7 by Vincent Ladeuil
Allow pycurl users to watch the blinkenlights and fix a bug when ranges are contiguous.
221
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
222
    def _get_ranged(self, relpath, offsets, tail_amount):
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
223
        """Make a request for just part of the file."""
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
224
        curl = self._get_curl()
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
225
        abspath, data, header = self._setup_get_request(curl, relpath)
226
2520.2.1 by Vincent Ladeuil
First step to fix #115209 use _coalesce_offsets like other transports.
227
        range_header = self._attempted_range_header(offsets, tail_amount)
2004.1.30 by v.ladeuil+lp at free
Fix #62276 and #62029 by providing a more robust http range handling.
228
        if range_header is None:
229
            # Forget ranges, the server can't handle them
230
            return self._get_full(relpath)
231
2481.3.1 by Vincent Ladeuil
Fix bug #112719 by using the right range header.
232
        self._curl_perform(curl, header, ['Range: bytes=%s' % range_header])
1786.1.33 by John Arbash Meinel
Cleanup pass #2
233
        data.seek(0)
234
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
235
        code = curl.getinfo(pycurl.HTTP_CODE)
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
236
237
        if code == 404: # not found
238
            raise errors.NoSuchFile(abspath)
239
        elif code in (400, 416):
240
            # We don't know which, but one of the ranges we specified was
241
            # wrong.
242
            raise errors.InvalidHttpRange(abspath, range_header,
243
                                          'Server return code %d'
244
                                          % curl.getinfo(pycurl.HTTP_CODE))
3059.2.4 by Vincent Ladeuil
Fix typo so that all tests pass now (after merging bzr.dev to get rid of
245
        msg = self._parse_headers(header)
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
246
        return code, response.handle_response(abspath, code, msg, data)
247
248
    def _parse_headers(self, status_and_headers):
249
        """Transform the headers provided by curl into an HTTPMessage"""
250
        status_and_headers.seek(0)
251
        # Ignore status line
252
        status_and_headers.readline()
253
        msg = httplib.HTTPMessage(status_and_headers)
254
        return msg
1786.1.4 by John Arbash Meinel
Adding HEADERFUNCTION which lets us get any response codes we want.
255
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
256
    def _post(self, body_bytes):
257
        fake_file = StringIO(body_bytes)
2485.8.41 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
258
        curl = self._get_curl()
259
        # Other places that use the Curl object (returned by _get_curl)
260
        # for GET requests explicitly set HTTPGET, so it should be safe to
261
        # re-use the same object for both GETs and POSTs.
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
262
        curl.setopt(pycurl.POST, 1)
263
        curl.setopt(pycurl.POSTFIELDSIZE, len(body_bytes))
264
        curl.setopt(pycurl.READFUNCTION, fake_file.read)
265
        abspath, data, header = self._setup_request(curl, '.bzr/smart')
2000.3.4 by v.ladeuil+lp at free
Merge bzr.dev
266
        # We override the Expect: header so that pycurl will send the POST
267
        # body immediately.
2164.2.16 by Vincent Ladeuil
Add tests.
268
        self._curl_perform(curl, header, ['Expect: '])
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
269
        data.seek(0)
270
        code = curl.getinfo(pycurl.HTTP_CODE)
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
271
        msg = self._parse_headers(header)
272
        return code, response.handle_response(abspath, code, msg, data)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
273
1786.1.40 by John Arbash Meinel
code cleanups from Martin Pool.
274
    def _raise_curl_http_error(self, curl, info=None):
1612.1.1 by Martin Pool
Raise errors correctly on pycurl connection failure
275
        code = curl.getinfo(pycurl.HTTP_CODE)
276
        url = curl.getinfo(pycurl.EFFECTIVE_URL)
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
277
        # Some error codes can be handled the same way for all
278
        # requests
279
        if code == 403:
2004.1.34 by v.ladeuil+lp at free
Cosmetic fix for bug #57644.
280
            raise errors.TransportError(
281
                'Server refuses to fullfil the request for: %s' % url)
1786.1.40 by John Arbash Meinel
code cleanups from Martin Pool.
282
        else:
2004.1.27 by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message.
283
            if info is None:
284
                msg = ''
285
            else:
286
                msg = ': ' + info
287
            raise errors.InvalidHttpResponse(
288
                url, 'Unable to handle http code %d%s' % (code,msg))
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
289
1540.3.13 by Martin Pool
Curl should follow http redirects, the same as urllib
290
    def _set_curl_options(self, curl):
291
        """Set options for all requests"""
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
292
        if 'http' in debug.debug_flags:
293
            curl.setopt(pycurl.VERBOSE, 1)
294
            # pycurl doesn't implement the CURLOPT_STDERR option, so we can't
295
            # do : curl.setopt(pycurl.STDERR, trace._trace_file)
296
297
        ua_str = 'bzr/%s (pycurl: %s)' % (bzrlib.__version__, pycurl.version)
1540.3.15 by Martin Pool
[merge] large merge to sync with bzr.dev
298
        curl.setopt(pycurl.USERAGENT, ua_str)
2298.5.1 by Alexander Belchenko
Bugfix #82086: Searching location of CA bundle for PyCurl in env variable (CURL_CA_BUNDLE), and on win32 along the PATH
299
        if self.cabundle:
300
            curl.setopt(pycurl.CAINFO, self.cabundle)
3133.1.2 by Vincent Ladeuil
Fix #177643 by making pycurl handle url-embedded credentials again.
301
        # Set accepted auth methods
302
        curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_ANY)
303
        curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_ANY)
304
        auth = self._get_credentials()
305
        user = auth.get('user', None)
306
        password = auth.get('password', None)
307
        userpass = None
308
        if user is not None:
309
            userpass = user + ':'
310
            if password is not None: # '' is a valid password
311
                userpass += password
312
            curl.setopt(pycurl.USERPWD, userpass)
1540.3.3 by Martin Pool
Review updates of pycurl transport
313
2164.2.16 by Vincent Ladeuil
Add tests.
314
    def _curl_perform(self, curl, header, more_headers=[]):
1540.3.3 by Martin Pool
Review updates of pycurl transport
315
        """Perform curl operation and translate exceptions."""
316
        try:
2000.3.1 by v.ladeuil+lp at free
Better connection sharing by using only one curl object.
317
            # There's no way in http/1.0 to say "must
318
            # revalidate"; we don't want to force it to always
319
            # retrieve.  so just turn off the default Pragma
320
            # provided by Curl.
321
            headers = ['Cache-control: max-age=0',
322
                       'Pragma: no-cache',
323
                       'Connection: Keep-Alive']
324
            curl.setopt(pycurl.HTTPHEADER, headers + more_headers)
1540.3.14 by Martin Pool
[pycurl] Make Curl instance a local variable not a long-lived object.
325
            curl.perform()
1540.3.3 by Martin Pool
Review updates of pycurl transport
326
        except pycurl.error, e:
1786.1.35 by John Arbash Meinel
For pycurl inverse of (NOBODY,1) is (HTTPGET,1) not (NOBODY,0)
327
            url = curl.getinfo(pycurl.EFFECTIVE_URL)
328
            mutter('got pycurl error: %s, %s, %s, url: %s ',
2872.6.1 by Vincent Ladeuil
Fix bug #147530 by enabling more robust error code definitions.
329
                    e[0], e[1], e, url)
330
            if e[0] in (CURLE_SSL_CACERT_BADFILE,
331
                        CURLE_COULDNT_RESOLVE_HOST,
332
                        CURLE_COULDNT_CONNECT,
333
                        CURLE_GOT_NOTHING,
334
                        CURLE_COULDNT_RESOLVE_PROXY,):
3052.3.3 by Vincent Ladeuil
Add -Dhttp support.
335
                raise errors.ConnectionError(
336
                    'curl connection error (%s)\non %s' % (e[1], url))
2872.6.1 by Vincent Ladeuil
Fix bug #147530 by enabling more robust error code definitions.
337
            elif e[0] == CURLE_PARTIAL_FILE:
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
338
                # Pycurl itself has detected a short read.  We do not have all
339
                # the information for the ShortReadvError, but that should be
340
                # enough
2000.3.9 by v.ladeuil+lp at free
The tests that would have help avoid bug #73948 and all that mess :)
341
                raise errors.ShortReadvError(url,
342
                                             offset='unknown', length='unknown',
343
                                             actual='unknown',
344
                                             extra='Server aborted the request')
1786.1.27 by John Arbash Meinel
Fix up the http transports so that tests pass with the new configuration.
345
            raise
2164.2.16 by Vincent Ladeuil
Add tests.
346
        code = curl.getinfo(pycurl.HTTP_CODE)
347
        if code in (301, 302, 303, 307):
348
            url = curl.getinfo(pycurl.EFFECTIVE_URL)
3059.2.2 by Vincent Ladeuil
Read http responses on demand without buffering the whole body
349
            msg = self._parse_headers(header)
350
            redirected_to = msg.getheader('location')
2164.2.16 by Vincent Ladeuil
Add tests.
351
            raise errors.RedirectRequested(url,
352
                                           redirected_to,
2949.4.1 by Vincent Ladeuil
Fix typo (is_permament => is_permanent) reported on IRC
353
                                           is_permanent=(code == 301),
2485.8.24 by Vincent Ladeuil
Finish http refactoring. Test suite passing.
354
                                           qual_proto=self._scheme)
1540.3.1 by Martin Pool
First-cut implementation of pycurl. Substantially faster than using urllib.
355
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
356
357
def get_test_permutations():
358
    """Return the permutations to be used in testing."""
3102.1.1 by Vincent Ladeuil
Rename bzrlib/test/HTTPTestUtils.py to bzrlib/tests/http_utils.py and fix
359
    from bzrlib.tests.http_server import HttpServer_PyCurl
1540.3.24 by Martin Pool
Add new protocol 'http+pycurl' that always uses PyCurl.
360
    return [(PyCurlTransport, HttpServer_PyCurl),
1540.3.10 by Martin Pool
[broken] keep hooking pycurl into test framework
361
            ]