92
93
# _unqualified_scheme: "http" or "https"
93
94
# _scheme: may have "+pycurl", etc
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)
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
415
413
def external_url(self):
416
414
"""See bzrlib.transport.Transport.external_url."""
417
# HTTP URL's are externally usable.
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
422
def is_readonly(self):
421
423
"""See Transport.is_readonly."""
452
454
raise errors.TransportNotPossible('http does not support lock_write()')
454
def clone(self, offset=None):
455
"""Return a new HttpTransportBase with root at self.base + offset
457
We leave the daughter classes take advantage of the hint
458
that it's a cloning not a raw creation.
461
return self.__class__(self.base, self)
463
return self.__class__(self.abspath(offset), self)
465
456
def _attempted_range_header(self, offsets, tail_amount):
466
457
"""Prepare a HTTP Range header at a level the server should accept.
518
509
return ','.join(strings)
511
def _redirected_to(self, source, target):
512
"""Returns a transport suitable to re-issue a redirected request.
514
:param source: The source url as returned by the server.
515
:param target: The target url as returned by the server.
517
The redirection can be handled only if the relpath involved is not
518
renamed by the redirection.
520
:returns: A transport or None.
522
def relpath(abspath):
523
"""Returns the path relative to our base.
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).
534
path) = self._split_url(abspath)
536
return path[pl:].strip('/')
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
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
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)
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
570
redir_scheme = scheme + '+' + self._impl_name
571
new_url = self._unsplit_url(redir_scheme,
572
self._user, self._password,
575
new_transport = get_transport(new_url)
577
# Redirected to a different protocol
578
new_url = self._unsplit_url(scheme,
582
new_transport = get_transport(new_url)
521
586
# TODO: May be better located in smart/medium.py with the other
522
587
# SmartMedium classes