60
60
# see if we can actually initialize PyCurl - sometimes it will load but
61
61
# fail to start up due to this bug:
63
63
# 32. (At least on Windows) If libcurl is built with c-ares and there's
64
64
# no DNS server configured in the system, the ares_init() call fails and
65
65
# thus curl_easy_init() fails as well. This causes weird effects for
87
87
return pycurl.__dict__.get(symbol, default)
89
CURLE_SSL_CACERT_BADFILE = _get_pycurl_errcode('E_SSL_CACERT_BADFILE', 77)
90
89
CURLE_COULDNT_CONNECT = _get_pycurl_errcode('E_COULDNT_CONNECT', 7)
91
90
CURLE_COULDNT_RESOLVE_HOST = _get_pycurl_errcode('E_COULDNT_RESOLVE_HOST', 6)
92
91
CURLE_COULDNT_RESOLVE_PROXY = _get_pycurl_errcode('E_COULDNT_RESOLVE_PROXY', 5)
93
92
CURLE_GOT_NOTHING = _get_pycurl_errcode('E_GOT_NOTHING', 52)
94
93
CURLE_PARTIAL_FILE = _get_pycurl_errcode('E_PARTIAL_FILE', 18)
95
94
CURLE_SEND_ERROR = _get_pycurl_errcode('E_SEND_ERROR', 55)
95
CURLE_SSL_CACERT = _get_pycurl_errcode('E_SSL_CACERT', 60)
96
CURLE_SSL_CACERT_BADFILE = _get_pycurl_errcode('E_SSL_CACERT_BADFILE', 77)
98
99
class PyCurlTransport(HttpTransportBase):
107
108
def __init__(self, base, _from_transport=None):
108
super(PyCurlTransport, self).__init__(base,
109
super(PyCurlTransport, self).__init__(base, 'pycurl',
109
110
_from_transport=_from_transport)
110
if base.startswith('https'):
111
if self._unqualified_scheme == 'https':
111
112
# Check availability of https into pycurl supported
113
114
supported = pycurl.version_info()[8]
180
181
:param curl: The curl object to place the request on
181
182
:param relpath: The relative path that we want to get
182
:return: (abspath, data, header)
183
:return: (abspath, data, header)
183
184
abspath: full url
184
185
data: file that will be filled with the body
185
186
header: file that will be filled with the headers
215
216
# The parent class use 0 to minimize the requests, but since we can't
216
217
# exploit the results as soon as they are received (pycurl limitation) we'd
217
# better issue more requests and provide a more responsive UI do the cost
218
# of more latency costs.
218
# better issue more requests and provide a more responsive UI incurring
219
# more latency costs.
219
220
# If you modify this, think about modifying the comment in http/__init__.py
221
222
_get_max_size = 4 * 1024 * 1024
286
287
msg = self._parse_headers(header)
287
288
return code, response.handle_response(abspath, code, msg, data)
289
291
def _raise_curl_http_error(self, curl, info=None):
290
292
code = curl.getinfo(pycurl.HTTP_CODE)
291
293
url = curl.getinfo(pycurl.EFFECTIVE_URL)
303
305
raise errors.InvalidHttpResponse(
304
306
url, 'Unable to handle http code %d%s' % (code,msg))
308
def _debug_cb(self, kind, text):
309
if kind in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN,
310
pycurl.INFOTYPE_SSL_DATA_IN):
311
self._report_activity(len(text), 'read')
312
if (kind == pycurl.INFOTYPE_HEADER_IN
313
and 'http' in debug.debug_flags):
314
mutter('< %s' % text)
315
elif kind in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT,
316
pycurl.INFOTYPE_SSL_DATA_OUT):
317
self._report_activity(len(text), 'write')
318
if (kind == pycurl.INFOTYPE_HEADER_OUT
319
and 'http' in debug.debug_flags):
320
mutter('> %s' % text)
321
elif kind == pycurl.INFOTYPE_TEXT and 'http' in debug.debug_flags:
322
mutter('* %s' % text)
306
324
def _set_curl_options(self, curl):
307
325
"""Set options for all requests"""
308
if 'http' in debug.debug_flags:
309
curl.setopt(pycurl.VERBOSE, 1)
310
# pycurl doesn't implement the CURLOPT_STDERR option, so we can't
311
# do : curl.setopt(pycurl.STDERR, trace._trace_file)
313
326
ua_str = 'bzr/%s (pycurl: %s)' % (bzrlib.__version__, pycurl.version)
314
327
curl.setopt(pycurl.USERAGENT, ua_str)
328
curl.setopt(pycurl.VERBOSE, 1)
329
curl.setopt(pycurl.DEBUGFUNCTION, self._debug_cb)
315
330
if self.cabundle:
316
331
curl.setopt(pycurl.CAINFO, self.cabundle)
317
332
# Set accepted auth methods
343
358
url = curl.getinfo(pycurl.EFFECTIVE_URL)
344
359
mutter('got pycurl error: %s, %s, %s, url: %s ',
345
360
e[0], e[1], e, url)
346
if e[0] in (CURLE_SSL_CACERT_BADFILE,
347
CURLE_COULDNT_RESOLVE_HOST,
361
if e[0] in (CURLE_COULDNT_RESOLVE_HOST,
362
CURLE_COULDNT_RESOLVE_PROXY,
348
363
CURLE_COULDNT_CONNECT,
349
364
CURLE_GOT_NOTHING,
350
CURLE_COULDNT_RESOLVE_PROXY,):
366
CURLE_SSL_CACERT_BADFILE,
351
368
raise errors.ConnectionError(
352
369
'curl connection error (%s)\non %s' % (e[1], url))
353
370
elif e[0] == CURLE_PARTIAL_FILE:
366
383
redirected_to = msg.getheader('location')
367
384
raise errors.RedirectRequested(url,
369
is_permanent=(code == 301),
370
qual_proto=self._scheme)
386
is_permanent=(code == 301))
373
389
def get_test_permutations():
374
390
"""Return the permutations to be used in testing."""
375
from bzrlib.tests.http_server import HttpServer_PyCurl
376
return [(PyCurlTransport, HttpServer_PyCurl),
391
from bzrlib import tests
392
from bzrlib.tests import http_server
393
permutations = [(PyCurlTransport, http_server.HttpServer_PyCurl),]
394
if tests.HTTPSServerFeature.available():
395
from bzrlib.tests import (
400
class HTTPS_pycurl_transport(PyCurlTransport):
402
def __init__(self, base, _from_transport=None):
403
super(HTTPS_pycurl_transport, self).__init__(base,
405
self.cabundle = str(ssl_certs.build_path('ca.crt'))
407
permutations.append((HTTPS_pycurl_transport,
408
https_server.HTTPSServer_PyCurl))