/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2008-02-13 03:30:01 UTC
  • mfrom: (3221 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3224.
  • Revision ID: robertc@robertcollins.net-20080213033001-rw70ul0zb02ph856
Merge to fix conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
 
34
34
import os
35
35
from cStringIO import StringIO
 
36
import httplib
36
37
import sys
37
38
 
38
39
from bzrlib import (
 
40
    debug,
39
41
    errors,
 
42
    trace,
40
43
    __version__ as bzrlib_version,
41
44
    )
42
45
import bzrlib
43
 
from bzrlib.errors import (NoSuchFile,
44
 
                           ConnectionError,
45
 
                           DependencyNotPresent)
46
46
from bzrlib.trace import mutter
47
47
from bzrlib.transport.http import (
48
48
    ca_bundle,
49
 
    _extract_headers,
50
49
    HttpTransportBase,
51
50
    response,
52
51
    )
55
54
    import pycurl
56
55
except ImportError, e:
57
56
    mutter("failed to import pycurl: %s", e)
58
 
    raise DependencyNotPresent('pycurl', e)
 
57
    raise errors.DependencyNotPresent('pycurl', e)
59
58
 
60
59
try:
61
60
    # see if we can actually initialize PyCurl - sometimes it will load but
70
69
    pycurl.Curl()
71
70
except pycurl.error, e:
72
71
    mutter("failed to initialize pycurl: %s", e)
73
 
    raise DependencyNotPresent('pycurl', e)
 
72
    raise errors.DependencyNotPresent('pycurl', e)
74
73
 
75
74
 
76
75
 
112
111
            # protocols
113
112
            supported = pycurl.version_info()[8]
114
113
            if 'https' not in supported:
115
 
                raise DependencyNotPresent('pycurl', 'no https support')
 
114
                raise errors.DependencyNotPresent('pycurl', 'no https support')
116
115
        self.cabundle = ca_bundle.get_ca_path()
117
116
 
118
117
    def _get_curl(self):
124
123
            # connect to the http server until the first request (which had
125
124
            # just called us).
126
125
            connection = pycurl.Curl()
127
 
            self._set_connection(connection, None)
 
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)
128
130
        return connection
129
131
 
130
132
    def has(self, relpath):
202
204
        data.seek(0)
203
205
 
204
206
        if code == 404:
205
 
            raise NoSuchFile(abspath)
 
207
            raise errors.NoSuchFile(abspath)
206
208
        if code != 200:
207
209
            self._raise_curl_http_error(
208
210
                curl, 'expected 200 or 404 for full response.')
209
211
 
210
212
        return code, data
211
213
 
 
214
    # The parent class use 0 to minimize the requests, but since we can't
 
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.
 
218
    # If you modify this, think about modifying the comment in http/__init__.py
 
219
    # too.
 
220
    _get_max_size = 4 * 1024 * 1024
 
221
 
212
222
    def _get_ranged(self, relpath, offsets, tail_amount):
213
223
        """Make a request for just part of the file."""
214
224
        curl = self._get_curl()
223
233
        data.seek(0)
224
234
 
225
235
        code = curl.getinfo(pycurl.HTTP_CODE)
226
 
        # mutter('header:\n%r', header.getvalue())
227
 
        headers = _extract_headers(header.getvalue(), abspath)
228
 
        # handle_response will raise NoSuchFile, etc based on the response code
229
 
        return code, response.handle_response(abspath, code, headers, data)
 
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))
 
245
        msg = self._parse_headers(header)
 
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
230
255
 
231
256
    def _post(self, body_bytes):
232
257
        fake_file = StringIO(body_bytes)
243
268
        self._curl_perform(curl, header, ['Expect: '])
244
269
        data.seek(0)
245
270
        code = curl.getinfo(pycurl.HTTP_CODE)
246
 
        headers = _extract_headers(header.getvalue(), abspath)
247
 
        return code, response.handle_response(abspath, code, headers, data)
 
271
        msg = self._parse_headers(header)
 
272
        return code, response.handle_response(abspath, code, msg, data)
248
273
 
249
274
    def _raise_curl_http_error(self, curl, info=None):
250
275
        code = curl.getinfo(pycurl.HTTP_CODE)
264
289
 
265
290
    def _set_curl_options(self, curl):
266
291
        """Set options for all requests"""
267
 
        ## curl.setopt(pycurl.VERBOSE, 1)
268
 
        # TODO: maybe include a summary of the pycurl version
269
 
        ua_str = 'bzr/%s (pycurl)' % (bzrlib.__version__,)
 
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)
270
298
        curl.setopt(pycurl.USERAGENT, ua_str)
271
299
        if self.cabundle:
272
300
            curl.setopt(pycurl.CAINFO, self.cabundle)
 
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)
273
313
 
274
314
    def _curl_perform(self, curl, header, more_headers=[]):
275
315
        """Perform curl operation and translate exceptions."""
292
332
                        CURLE_COULDNT_CONNECT,
293
333
                        CURLE_GOT_NOTHING,
294
334
                        CURLE_COULDNT_RESOLVE_PROXY,):
295
 
                raise ConnectionError('curl connection error (%s)\non %s'
296
 
                              % (e[1], url))
 
335
                raise errors.ConnectionError(
 
336
                    'curl connection error (%s)\non %s' % (e[1], url))
297
337
            elif e[0] == CURLE_PARTIAL_FILE:
298
 
                # Pycurl itself has detected a short read.  We do
299
 
                # not have all the information for the
300
 
                # ShortReadvError, but that should be enough
 
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
301
341
                raise errors.ShortReadvError(url,
302
342
                                             offset='unknown', length='unknown',
303
343
                                             actual='unknown',
304
344
                                             extra='Server aborted the request')
305
 
            # jam 20060713 The code didn't use to re-raise the exception here,
306
 
            # but that seemed bogus
307
345
            raise
308
346
        code = curl.getinfo(pycurl.HTTP_CODE)
309
347
        if code in (301, 302, 303, 307):
310
348
            url = curl.getinfo(pycurl.EFFECTIVE_URL)
311
 
            headers = _extract_headers(header.getvalue(), url)
312
 
            redirected_to = headers['Location']
 
349
            msg = self._parse_headers(header)
 
350
            redirected_to = msg.getheader('location')
313
351
            raise errors.RedirectRequested(url,
314
352
                                           redirected_to,
315
353
                                           is_permanent=(code == 301),
318
356
 
319
357
def get_test_permutations():
320
358
    """Return the permutations to be used in testing."""
321
 
    from bzrlib.tests.HttpServer import HttpServer_PyCurl
 
359
    from bzrlib.tests.http_server import HttpServer_PyCurl
322
360
    return [(PyCurlTransport, HttpServer_PyCurl),
323
361
            ]