~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2008-12-12 13:09:26 UTC
  • mfrom: (3878.4.7 303959-redirection)
  • mto: This revision was merged to the branch mainline in revision 3903.
  • Revision ID: v.ladeuil+lp@free.fr-20081212130926-ov09evfb53npj0z0
Fix redirection related bugs: #245964, #265070, #270863 and #303959

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
from bzrlib.transport import (
42
42
    ConnectedTransport,
43
43
    _CoalescedOffset,
 
44
    get_transport,
44
45
    Transport,
45
46
    )
46
47
 
92
93
    # _unqualified_scheme: "http" or "https"
93
94
    # _scheme: may have "+pycurl", etc
94
95
 
95
 
    def __init__(self, base, _from_transport=None):
 
96
    def __init__(self, base, _impl_name, _from_transport=None):
96
97
        """Set the base path where files will be stored."""
97
98
        proto_match = re.match(r'^(https?)(\+\w+)?://', base)
98
99
        if not proto_match:
99
100
            raise AssertionError("not a http url: %r" % base)
100
101
        self._unqualified_scheme = proto_match.group(1)
101
 
        impl_name = proto_match.group(2)
102
 
        if impl_name:
103
 
            impl_name = impl_name[1:]
104
 
        self._impl_name = impl_name
 
102
        self._impl_name = _impl_name
105
103
        super(HttpTransportBase, self).__init__(base,
106
104
                                                _from_transport=_from_transport)
107
105
        self._medium = None
414
412
 
415
413
    def external_url(self):
416
414
        """See bzrlib.transport.Transport.external_url."""
417
 
        # HTTP URL's are externally usable.
418
 
        return self.base
 
415
        # HTTP URL's are externally usable as long as they don't mention their
 
416
        # implementation qualifier
 
417
        return self._unsplit_url(self._unqualified_scheme,
 
418
                                 self._user, self._password,
 
419
                                 self._host, self._port,
 
420
                                 self._path)
419
421
 
420
422
    def is_readonly(self):
421
423
        """See Transport.is_readonly."""
451
453
        """
452
454
        raise errors.TransportNotPossible('http does not support lock_write()')
453
455
 
454
 
    def clone(self, offset=None):
455
 
        """Return a new HttpTransportBase with root at self.base + offset
456
 
 
457
 
        We leave the daughter classes take advantage of the hint
458
 
        that it's a cloning not a raw creation.
459
 
        """
460
 
        if offset is None:
461
 
            return self.__class__(self.base, self)
462
 
        else:
463
 
            return self.__class__(self.abspath(offset), self)
464
 
 
465
456
    def _attempted_range_header(self, offsets, tail_amount):
466
457
        """Prepare a HTTP Range header at a level the server should accept.
467
458
 
517
508
 
518
509
        return ','.join(strings)
519
510
 
 
511
    def _redirected_to(self, source, target):
 
512
        """Returns a transport suitable to re-issue a redirected request.
 
513
 
 
514
        :param source: The source url as returned by the server.
 
515
        :param target: The target url as returned by the server.
 
516
 
 
517
        The redirection can be handled only if the relpath involved is not
 
518
        renamed by the redirection.
 
519
 
 
520
        :returns: A transport or None.
 
521
        """
 
522
        def relpath(abspath):
 
523
            """Returns the path relative to our base.
 
524
 
 
525
            The constraints are weaker than the real relpath method because the
 
526
            abspath is coming from the server and may slightly differ from our
 
527
            base. We don't check the scheme, host, port, user, password parts,
 
528
            relying on the caller to give us a proper url (i.e. one returned by
 
529
            the server mirroring the one we sent).
 
530
            """
 
531
            (scheme,
 
532
             user, password,
 
533
             host, port,
 
534
             path) = self._split_url(abspath)
 
535
            pl = len(self._path)
 
536
            return path[pl:].strip('/')
 
537
 
 
538
        relpath = relpath(source)
 
539
        if not target.endswith(relpath):
 
540
            # The final part of the url has been renamed, we can't handle the
 
541
            # redirection.
 
542
            return None
 
543
        new_transport = None
 
544
        (scheme,
 
545
         user, password,
 
546
         host, port,
 
547
         path) = self._split_url(target)
 
548
        # Recalculate base path. This is needed to ensure that when the
 
549
        # redirected tranport will be used to re-try whatever request was
 
550
        # redirected, we end up with the same url
 
551
        base_path = path[:-len(relpath)]
 
552
        if scheme in ('http', 'https'):
 
553
            # Same protocol family (i.e. http[s]), we will preserve the same
 
554
            # http client implementation when a redirection occurs from one to
 
555
            # the other (otherwise users may be surprised that bzr switches
 
556
            # from one implementation to the other, and devs may suffer
 
557
            # debugging it).
 
558
            if (scheme == self._unqualified_scheme
 
559
                and host == self._host
 
560
                and port == self._port
 
561
                and (user is None or user == self._user)):
 
562
                # If a user is specified, it should match, we don't care about
 
563
                # passwords, wrong passwords will be rejected anyway.
 
564
                new_transport = self.clone(base_path)
 
565
            else:
 
566
                # Rebuild the url preserving the scheme qualification and the
 
567
                # credentials (if they don't apply, the redirected to server
 
568
                # will tell us, but if they do apply, we avoid prompting the
 
569
                # user)
 
570
                redir_scheme = scheme + '+' + self._impl_name
 
571
                new_url = self._unsplit_url(redir_scheme,
 
572
                                            self._user, self._password,
 
573
                                            host, port,
 
574
                                            base_path)
 
575
                new_transport = get_transport(new_url)
 
576
        else:
 
577
            # Redirected to a different protocol
 
578
            new_url = self._unsplit_url(scheme,
 
579
                                        user, password,
 
580
                                        host, port,
 
581
                                        base_path)
 
582
            new_transport = get_transport(new_url)
 
583
        return new_transport
 
584
 
520
585
 
521
586
# TODO: May be better located in smart/medium.py with the other
522
587
# SmartMedium classes