74
74
# RemoteTransport is an adapter from the Transport object model to the
75
75
# SmartClient model, not an encoder.
77
# FIXME: the medium parameter should be private, only the tests requires
78
# it. It may be even clearer to define a TestRemoteTransport that handles
79
# the specific cases of providing a _client and/or a _medium, and leave
80
# RemoteTransport as an abstract class.
81
def __init__(self, url, _from_transport=None, medium=None, _client=None):
77
def __init__(self, url, clone_from=None, medium=None, _client=None):
84
:param _from_transport: Another RemoteTransport instance that this
85
one is being cloned from. Attributes such as the medium will
80
:param clone_from: Another RemoteTransport instance that this one is
81
being cloned from. Attributes such as credentials and the medium
88
83
:param medium: The medium to use for this RemoteTransport. This must be
89
supplied if _from_transport is None.
84
supplied if clone_from is None.
91
85
:param _client: Override the _SmartClient used by this transport. This
92
86
should only be used for testing purposes; normally this is
93
87
determined from the medium.
95
super(RemoteTransport, self).__init__(url,
96
_from_transport=_from_transport)
98
# The medium is the connection, except when we need to share it with
99
# other objects (RemoteBzrDir, RemoteRepository etc). In these cases
100
# what we want to share is really the shared connection.
102
if _from_transport is None:
103
# If no _from_transport is specified, we need to intialize the
107
medium, credentials = self._build_medium()
108
self._shared_connection= transport._SharedConnection(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
111
106
if _client is None:
112
self._client = client._SmartClient(self.get_shared_medium())
107
self._client = client._SmartClient(self._medium)
114
109
self._client = _client
116
def _build_medium(self):
117
"""Create the medium if _from_transport does not provide one.
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.
119
The medium is analogous to the connection for ConnectedTransport: it
120
allows connection sharing.
122
This essentially opens a handle on a different remote directory.
124
if relative_url is None:
125
return RemoteTransport(self.base, self)
127
return RemoteTransport(self.abspath(relative_url), self)
125
129
def is_readonly(self):
126
130
"""Smart server transport can do read/write file operations."""
142
146
raise errors.UnexpectedSmartServerResponse(resp)
144
148
def get_smart_client(self):
145
return self._get_connection()
147
151
def get_smart_medium(self):
148
return self._get_connection()
154
def _unparse_url(self, path):
155
"""Return URL for a path.
150
def get_shared_medium(self):
151
return self._get_shared_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, '', '', ''))
153
171
def _remote_path(self, relpath):
154
172
"""Returns the Unicode version of the absolute path for relpath."""
376
389
raise UnicodeEncodeError(encoding, val, start, end, reason)
377
390
elif what == "ReadOnlyError":
378
391
raise errors.TransportNotPossible('readonly transport')
379
elif what == "ReadError":
380
if orig_path is not None:
381
error_path = orig_path
384
raise errors.ReadError(error_path)
386
393
raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
388
395
def disconnect(self):
389
self.get_smart_medium().disconnect()
396
self._medium.disconnect()
391
398
def delete_tree(self, relpath):
392
399
raise errors.TransportNotPossible('readonly transport')
436
443
SmartTCPClientMedium).
439
def _build_medium(self):
440
assert self.base.startswith('bzr://')
441
if self._port is None:
442
self._port = BZR_DEFAULT_PORT
443
return medium.SmartTCPClientMedium(self._host, self._port), None
446
def __init__(self, url):
447
_scheme, _username, _password, _host, _port, _path = \
448
transport.split_url(url)
450
_port = BZR_DEFAULT_PORT
454
except (ValueError, TypeError), e:
455
raise errors.InvalidURL(
456
path=url, extra="invalid port %s" % _port)
457
client_medium = medium.SmartTCPClientMedium(_host, _port)
458
super(RemoteTCPTransport, self).__init__(url, medium=client_medium)
446
461
class RemoteSSHTransport(RemoteTransport):
450
465
SmartSSHClientMedium).
453
def _build_medium(self):
454
assert self.base.startswith('bzr+ssh://')
455
# ssh will prompt the user for a password if needed and if none is
456
# provided but it will not give it back, so no credentials can be
458
return medium.SmartSSHClientMedium(self._host, self._port,
459
self._user, self._password), None
468
def __init__(self, url):
469
_scheme, _username, _password, _host, _port, _path = \
470
transport.split_url(url)
472
if _port is not None:
474
except (ValueError, TypeError), e:
475
raise errors.InvalidURL(path=url, extra="invalid port %s" %
477
client_medium = medium.SmartSSHClientMedium(_host, _port,
478
_username, _password)
479
super(RemoteSSHTransport, self).__init__(url, medium=client_medium)
462
482
class RemoteHTTPTransport(RemoteTransport):
470
490
HTTP path into a local path.
473
def __init__(self, base, _from_transport=None, http_transport=None):
474
assert base.startswith('bzr+http://')
493
def __init__(self, url, http_transport=None):
494
assert url.startswith('bzr+http://')
476
496
if http_transport is None:
477
# FIXME: the password may be lost here because it appears in the
478
# url only for an intial construction (when the url came from the
480
http_url = base[len('bzr+'):]
497
http_url = url[len('bzr+'):]
481
498
self._http_transport = transport.get_transport(http_url)
483
500
self._http_transport = http_transport
484
super(RemoteHTTPTransport, self).__init__(
485
base, _from_transport=_from_transport)
487
def _build_medium(self):
488
# We let http_transport take care of the credentials
489
return self._http_transport.get_smart_medium(), None
501
http_medium = self._http_transport.get_smart_medium()
502
super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
491
504
def _remote_path(self, relpath):
492
"""After connecting, HTTP Transport only deals in relative URLs."""
505
"""After connecting HTTP Transport only deals in relative URLs."""
493
506
# Adjust the relpath based on which URL this smart transport is
495
http_base = urlutils.normalize_url(self._http_transport.base)
508
base = urlutils.normalize_url(self._http_transport.base)
496
509
url = urlutils.join(self.base[len('bzr+'):], relpath)
497
510
url = urlutils.normalize_url(url)
498
return urlutils.relative_url(http_base, url)
511
return urlutils.relative_url(base, url)
513
def abspath(self, relpath):
514
"""Return the full url to the given relative path.
516
:param relpath: the relative path or path components
517
:type relpath: str or list
519
return self._unparse_url(self._combine_paths(self._path, relpath))
500
521
def clone(self, relative_url):
501
522
"""Make a new RemoteHTTPTransport related to me.