74
76
# RemoteTransport is an adapter from the Transport object model to the
75
77
# SmartClient model, not an encoder.
77
def __init__(self, url, clone_from=None, medium=None, _client=None):
79
# FIXME: the medium parameter should be private, only the tests requires
80
# it. It may be even clearer to define a TestRemoteTransport that handles
81
# the specific cases of providing a _client and/or a _medium, and leave
82
# RemoteTransport as an abstract class.
83
def __init__(self, url, _from_transport=None, medium=None, _client=None):
80
:param clone_from: Another RemoteTransport instance that this one is
81
being cloned from. Attributes such as credentials and the medium
86
:param _from_transport: Another RemoteTransport instance that this
87
one is being cloned from. Attributes such as the medium will
83
90
:param medium: The medium to use for this RemoteTransport. This must be
84
supplied if clone_from is None.
91
supplied if _from_transport is None.
85
93
:param _client: Override the _SmartClient used by this transport. This
86
94
should only be used for testing purposes; normally this is
87
95
determined from the medium.
89
### Technically super() here is faulty because Transport's __init__
90
### fails to take 2 parameters, and if super were to choose a silly
91
### initialisation order things would blow up.
92
if not url.endswith('/'):
94
super(RemoteTransport, self).__init__(url)
95
self._scheme, self._username, self._password, self._host, self._port, self._path = \
96
transport.split_url(url)
97
if clone_from is None:
100
# credentials may be stripped from the base in some circumstances
101
# as yet to be clearly defined or documented, so copy them.
102
self._username = clone_from._username
103
# reuse same connection
104
self._medium = clone_from._medium
105
assert self._medium is not None
97
super(RemoteTransport, self).__init__(url,
98
_from_transport=_from_transport)
100
# The medium is the connection, except when we need to share it with
101
# other objects (RemoteBzrDir, RemoteRepository etc). In these cases
102
# what we want to share is really the shared connection.
104
if _from_transport is None:
105
# If no _from_transport is specified, we need to intialize the
109
medium, credentials = self._build_medium()
110
if 'hpss' in debug.debug_flags:
111
trace.mutter('hpss: Built a new medium: %s',
112
medium.__class__.__name__)
113
self._shared_connection = transport._SharedConnection(medium,
106
116
if _client is None:
107
self._client = client._SmartClient(self._medium)
117
self._client = client._SmartClient(self.get_shared_medium())
109
119
self._client = _client
111
def abspath(self, relpath):
112
"""Return the full url to the given relative path.
114
@param relpath: the relative path or path components
115
@type relpath: str or list
117
return self._unparse_url(self._remote_path(relpath))
119
def clone(self, relative_url):
120
"""Make a new RemoteTransport related to me, sharing the same connection.
121
def _build_medium(self):
122
"""Create the medium if _from_transport does not provide one.
122
This essentially opens a handle on a different remote directory.
124
The medium is analogous to the connection for ConnectedTransport: it
125
allows connection sharing.
124
if relative_url is None:
125
return RemoteTransport(self.base, self)
127
return RemoteTransport(self.abspath(relative_url), self)
129
130
def is_readonly(self):
130
131
"""Smart server transport can do read/write file operations."""
146
147
raise errors.UnexpectedSmartServerResponse(resp)
148
149
def get_smart_client(self):
150
return self._get_connection()
151
152
def get_smart_medium(self):
154
def _unparse_url(self, path):
155
"""Return URL for a path.
153
return self._get_connection()
157
:see: SFTPUrlHandling._unparse_url
159
# TODO: Eventually it should be possible to unify this with
160
# SFTPUrlHandling._unparse_url?
163
path = urllib.quote(path)
164
netloc = urllib.quote(self._host)
165
if self._username is not None:
166
netloc = '%s@%s' % (urllib.quote(self._username), netloc)
167
if self._port is not None:
168
netloc = '%s:%d' % (netloc, self._port)
169
return urlparse.urlunparse((self._scheme, netloc, path, '', '', ''))
155
def get_shared_medium(self):
156
return self._get_shared_connection()
171
158
def _remote_path(self, relpath):
172
159
"""Returns the Unicode version of the absolute path for relpath."""
449
441
SmartTCPClientMedium).
452
def __init__(self, url):
453
_scheme, _username, _password, _host, _port, _path = \
454
transport.split_url(url)
456
_port = BZR_DEFAULT_PORT
460
except (ValueError, TypeError), e:
461
raise errors.InvalidURL(
462
path=url, extra="invalid port %s" % _port)
463
client_medium = medium.SmartTCPClientMedium(_host, _port)
464
super(RemoteTCPTransport, self).__init__(url, medium=client_medium)
444
def _build_medium(self):
445
assert self.base.startswith('bzr://')
446
if self._port is None:
447
self._port = BZR_DEFAULT_PORT
448
return medium.SmartTCPClientMedium(self._host, self._port), None
467
451
class RemoteSSHTransport(RemoteTransport):
471
455
SmartSSHClientMedium).
474
def __init__(self, url):
475
_scheme, _username, _password, _host, _port, _path = \
476
transport.split_url(url)
478
if _port is not None:
480
except (ValueError, TypeError), e:
481
raise errors.InvalidURL(path=url, extra="invalid port %s" %
483
client_medium = medium.SmartSSHClientMedium(_host, _port,
484
_username, _password)
485
super(RemoteSSHTransport, self).__init__(url, medium=client_medium)
458
def _build_medium(self):
459
assert self.base.startswith('bzr+ssh://')
460
# ssh will prompt the user for a password if needed and if none is
461
# provided but it will not give it back, so no credentials can be
463
return medium.SmartSSHClientMedium(self._host, self._port,
464
self._user, self._password), None
488
467
class RemoteHTTPTransport(RemoteTransport):
496
475
HTTP path into a local path.
499
def __init__(self, url, http_transport=None):
500
assert url.startswith('bzr+http://')
478
def __init__(self, base, _from_transport=None, http_transport=None):
479
assert base.startswith('bzr+http://')
502
481
if http_transport is None:
503
http_url = url[len('bzr+'):]
482
# FIXME: the password may be lost here because it appears in the
483
# url only for an intial construction (when the url came from the
485
http_url = base[len('bzr+'):]
504
486
self._http_transport = transport.get_transport(http_url)
506
488
self._http_transport = http_transport
507
http_medium = self._http_transport.get_smart_medium()
508
super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
489
super(RemoteHTTPTransport, self).__init__(
490
base, _from_transport=_from_transport)
492
def _build_medium(self):
493
# We let http_transport take care of the credentials
494
return self._http_transport.get_smart_medium(), None
510
496
def _remote_path(self, relpath):
511
"""After connecting HTTP Transport only deals in relative URLs."""
497
"""After connecting, HTTP Transport only deals in relative URLs."""
512
498
# Adjust the relpath based on which URL this smart transport is
514
base = urlutils.normalize_url(self._http_transport.base)
500
http_base = urlutils.normalize_url(self._http_transport.base)
515
501
url = urlutils.join(self.base[len('bzr+'):], relpath)
516
502
url = urlutils.normalize_url(url)
517
return urlutils.relative_url(base, url)
519
def abspath(self, relpath):
520
"""Return the full url to the given relative path.
522
:param relpath: the relative path or path components
523
:type relpath: str or list
525
return self._unparse_url(self._combine_paths(self._path, relpath))
503
return urlutils.relative_url(http_base, url)
527
505
def clone(self, relative_url):
528
506
"""Make a new RemoteHTTPTransport related to me.