~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:13:13 UTC
  • mfrom: (6614.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201191313-wdfvmfff1djde6oq
(vila) Release 2.7.0 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
There are separate implementation modules for each http client implementation.
20
20
"""
21
21
 
22
 
from cStringIO import StringIO
23
 
import mimetools
 
22
from __future__ import absolute_import
 
23
 
 
24
import os
24
25
import re
25
26
import urlparse
26
 
import urllib
27
27
import sys
28
28
import weakref
29
29
 
35
35
    urlutils,
36
36
    )
37
37
from bzrlib.smart import medium
38
 
from bzrlib.symbol_versioning import (
39
 
        deprecated_method,
40
 
        )
41
38
from bzrlib.trace import mutter
42
39
from bzrlib.transport import (
43
40
    ConnectedTransport,
44
 
    _CoalescedOffset,
45
 
    Transport,
46
41
    )
47
42
 
48
 
# TODO: This is not used anymore by HttpTransport_urllib
49
 
# (extracting the auth info and prompting the user for a password
50
 
# have been split), only the tests still use it. It should be
51
 
# deleted and the tests rewritten ASAP to stay in sync.
52
 
def extract_auth(url, password_manager):
53
 
    """Extract auth parameters from am HTTP/HTTPS url and add them to the given
54
 
    password manager.  Return the url, minus those auth parameters (which
55
 
    confuse urllib2).
56
 
    """
57
 
    if not re.match(r'^(https?)(\+\w+)?://', url):
58
 
        raise ValueError(
59
 
            'invalid absolute url %r' % (url,))
60
 
    scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
61
 
 
62
 
    if '@' in netloc:
63
 
        auth, netloc = netloc.split('@', 1)
64
 
        if ':' in auth:
65
 
            username, password = auth.split(':', 1)
66
 
        else:
67
 
            username, password = auth, None
68
 
        if ':' in netloc:
69
 
            host = netloc.split(':', 1)[0]
70
 
        else:
71
 
            host = netloc
72
 
        username = urllib.unquote(username)
73
 
        if password is not None:
74
 
            password = urllib.unquote(password)
75
 
        else:
76
 
            password = ui.ui_factory.get_password(
77
 
                prompt=u'HTTP %(user)s@%(host)s password',
78
 
                user=username, host=host)
79
 
        password_manager.add_password(None, host, username, password)
80
 
    url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
81
 
    return url
82
 
 
83
43
 
84
44
class HttpTransportBase(ConnectedTransport):
85
45
    """Base class for http implementations.
124
84
        :param relpath: The relative path to the file
125
85
        """
126
86
        code, response_file = self._get(relpath, None)
127
 
        # FIXME: some callers want an iterable... One step forward, three steps
128
 
        # backwards :-/ And not only an iterable, but an iterable that can be
129
 
        # seeked backwards, so we will never be able to do that.  One such
130
 
        # known client is bzrlib.bundle.serializer.v4.get_bundle_reader. At the
131
 
        # time of this writing it's even the only known client -- vila20071203
132
 
        return StringIO(response_file.read())
 
87
        return response_file
133
88
 
134
89
    def _get(self, relpath, ranges, tail_amount=0):
135
90
        """Get a file, or part of a file.
247
202
                    # Split the received chunk
248
203
                    for offset, size in cur_coal.ranges:
249
204
                        start = cur_coal.start + offset
250
 
                        rfile.seek(start, 0)
 
205
                        rfile.seek(start, os.SEEK_SET)
251
206
                        data = rfile.read(size)
252
207
                        data_len = len(data)
253
208
                        if data_len != size:
517
472
 
518
473
        :returns: A transport or None.
519
474
        """
520
 
        def relpath(abspath):
521
 
            """Returns the path relative to our base.
522
 
 
523
 
            The constraints are weaker than the real relpath method because the
524
 
            abspath is coming from the server and may slightly differ from our
525
 
            base. We don't check the scheme, host, port, user, password parts,
526
 
            relying on the caller to give us a proper url (i.e. one returned by
527
 
            the server mirroring the one we sent).
528
 
            """
529
 
            parsed_url = self._split_url(abspath)
530
 
            pl = len(self._parsed_url.path)
531
 
            return parsed_url.path[pl:].strip('/')
532
 
 
533
 
        relpath = relpath(source)
534
 
        if not target.endswith(relpath):
 
475
        parsed_source = self._split_url(source)
 
476
        parsed_target = self._split_url(target)
 
477
        pl = len(self._parsed_url.path)
 
478
        # determine the excess tail - the relative path that was in
 
479
        # the original request but not part of this transports' URL.
 
480
        excess_tail = parsed_source.path[pl:].strip("/")
 
481
        if not target.endswith(excess_tail):
535
482
            # The final part of the url has been renamed, we can't handle the
536
483
            # redirection.
537
484
            return None
538
 
        new_transport = None
539
 
        parsed_url = self._split_url(target)
540
 
        # Recalculate base path. This is needed to ensure that when the
541
 
        # redirected transport will be used to re-try whatever request was
542
 
        # redirected, we end up with the same url
543
 
        base_path = parsed_url.path[:-len(relpath)]
544
 
        if parsed_url.scheme in ('http', 'https'):
 
485
 
 
486
        target_path = parsed_target.path
 
487
        if excess_tail:
 
488
            # Drop the tail that was in the redirect but not part of
 
489
            # the path of this transport.
 
490
            target_path = target_path[:-len(excess_tail)]
 
491
 
 
492
        if parsed_target.scheme in ('http', 'https'):
545
493
            # Same protocol family (i.e. http[s]), we will preserve the same
546
494
            # http client implementation when a redirection occurs from one to
547
495
            # the other (otherwise users may be surprised that bzr switches
548
496
            # from one implementation to the other, and devs may suffer
549
497
            # debugging it).
550
 
            if (parsed_url.scheme == self._unqualified_scheme
551
 
                and parsed_url.host == self._parsed_url.host
552
 
                and parsed_url.port == self._parsed_url.port
553
 
                and (parsed_url.user is None or
554
 
                     parsed_url.user == self._parsed_url.user)):
 
498
            if (parsed_target.scheme == self._unqualified_scheme
 
499
                and parsed_target.host == self._parsed_url.host
 
500
                and parsed_target.port == self._parsed_url.port
 
501
                and (parsed_target.user is None or
 
502
                     parsed_target.user == self._parsed_url.user)):
555
503
                # If a user is specified, it should match, we don't care about
556
504
                # passwords, wrong passwords will be rejected anyway.
557
 
                new_transport = self.clone(base_path)
 
505
                return self.clone(target_path)
558
506
            else:
559
507
                # Rebuild the url preserving the scheme qualification and the
560
508
                # credentials (if they don't apply, the redirected to server
561
509
                # will tell us, but if they do apply, we avoid prompting the
562
510
                # user)
563
 
                redir_scheme = parsed_url.scheme + '+' + self._impl_name
 
511
                redir_scheme = parsed_target.scheme + '+' + self._impl_name
564
512
                new_url = self._unsplit_url(redir_scheme,
565
 
                                            self._parsed_url.user,
566
 
                                            self._parsed_url.password,
567
 
                                            parsed_url.host, parsed_url.port,
568
 
                                            base_path)
569
 
                new_transport = transport.get_transport(new_url)
 
513
                    self._parsed_url.user,
 
514
                    self._parsed_url.password,
 
515
                    parsed_target.host, parsed_target.port,
 
516
                    target_path)
 
517
                return transport.get_transport_from_url(new_url)
570
518
        else:
571
519
            # Redirected to a different protocol
572
 
            new_url = self._unsplit_url(parsed_url.scheme,
573
 
                                        parsed_url.user, parsed_url.password,
574
 
                                        parsed_url.host, parsed_url.port,
575
 
                                        base_path)
576
 
            new_transport = transport.get_transport(new_url)
577
 
        return new_transport
 
520
            new_url = self._unsplit_url(parsed_target.scheme,
 
521
                    parsed_target.user,
 
522
                    parsed_target.password,
 
523
                    parsed_target.host, parsed_target.port,
 
524
                    target_path)
 
525
            return transport.get_transport_from_url(new_url)
578
526
 
579
527
 
580
528
# TODO: May be better located in smart/medium.py with the other
602
550
        if transport_base.startswith('bzr+'):
603
551
            transport_base = transport_base[4:]
604
552
        rel_url = urlutils.relative_url(self.base, transport_base)
605
 
        return urllib.unquote(rel_url)
 
553
        return urlutils.unquote(rel_url)
606
554
 
607
555
    def send_http_smart_request(self, bytes):
608
556
        try:
610
558
            t = self._http_transport_ref()
611
559
            code, body_filelike = t._post(bytes)
612
560
            if code != 200:
613
 
                raise InvalidHttpResponse(
 
561
                raise errors.InvalidHttpResponse(
614
562
                    t._remote_path('.bzr/smart'),
615
563
                    'Expected 200 response code, got %r' % (code,))
616
564
        except (errors.InvalidHttpResponse, errors.ConnectionReset), e: