37
from bzrlib.smart import client, medium, protocol
35
from bzrlib.smart import client, medium
38
36
from bzrlib.symbol_versioning import (deprecated_method, one_four)
79
77
one is being cloned from. Attributes such as the medium will
82
:param medium: The medium to use for this RemoteTransport. This must be
83
supplied if _from_transport is None.
80
:param medium: The medium to use for this RemoteTransport. If None,
81
the medium from the _from_transport is shared. If both this
82
and _from_transport are None, a new medium will be built.
83
_from_transport and medium cannot both be specified.
85
85
:param _client: Override the _SmartClient used by this transport. This
86
86
should only be used for testing purposes; normally this is
105
105
self._shared_connection = transport._SharedConnection(medium,
109
# No medium was specified, so share the medium from the
111
medium = self._shared_connection.connection
110
# No medium was specified, so share the medium from the
112
medium = self._shared_connection.connection
113
raise AssertionError(
114
"Both _from_transport (%r) and medium (%r) passed to "
115
"RemoteTransport.__init__, but these parameters are mutally "
116
"exclusive." % (_from_transport, medium))
114
118
if _client is None:
115
self._client = client._SmartClient(medium, self.base)
119
self._client = client._SmartClient(medium)
117
121
self._client = _client
128
132
def is_readonly(self):
129
133
"""Smart server transport can do read/write file operations."""
130
resp = self._call2('Transport.is_readonly')
131
if resp == ('yes', ):
133
elif resp == ('no', ):
135
elif (resp == ('error', "Generic bzr smart protocol error: "
136
"bad request 'Transport.is_readonly'") or
137
resp == ('error', "Generic bzr smart protocol error: "
138
"bad request u'Transport.is_readonly'")):
135
resp = self._call2('Transport.is_readonly')
136
except errors.UnknownSmartMethod:
139
137
# XXX: nasty hack: servers before 0.16 don't have a
140
138
# 'Transport.is_readonly' verb, so we do what clients before 0.16
141
139
# did: assume False.
141
if resp == ('yes', ):
143
elif resp == ('no', ):
144
146
self._translate_error(resp)
145
147
raise errors.UnexpectedSmartServerResponse(resp)
159
161
return self._combine_paths(self._path, relpath)
161
163
def _call(self, method, *args):
162
resp = self._call2(method, *args)
165
resp = self._call2(method, *args)
166
except errors.ErrorFromSmartServer, err:
167
self._translate_error(err.error_tuple)
163
168
self._translate_error(resp)
165
170
def _call2(self, method, *args):
166
171
"""Call a method on the remote server."""
167
return self._client.call(method, *args)
173
return self._client.call(method, *args)
174
except errors.ErrorFromSmartServer, err:
175
self._translate_error(err.error_tuple)
169
177
def _call_with_body_bytes(self, method, args, body):
170
178
"""Call a method on the remote server with body bytes."""
171
return self._client.call_with_body_bytes(method, args, body)
180
return self._client.call_with_body_bytes(method, args, body)
181
except errors.ErrorFromSmartServer, err:
182
self._translate_error(err.error_tuple)
173
184
def has(self, relpath):
174
185
"""Indicate whether a remote file of the given name exists or not.
193
204
def get_bytes(self, relpath):
194
205
remote = self._remote_path(relpath)
195
request = self.get_smart_medium().get_request()
196
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
197
smart_protocol.call('get', remote)
198
resp = smart_protocol.read_response_tuple(True)
207
resp, response_handler = self._client.call_expecting_body('get', remote)
208
except errors.ErrorFromSmartServer, err:
209
self._translate_error(err.error_tuple, relpath)
199
210
if resp != ('ok', ):
200
smart_protocol.cancel_read_body()
201
self._translate_error(resp, relpath)
202
return smart_protocol.read_body_bytes()
211
response_handler.cancel_read_body()
212
raise errors.UnexpectedSmartServerResponse(resp)
213
return response_handler.read_body_bytes()
204
215
def _serialise_optional_mode(self, mode):
308
319
limit=self._max_readv_combine,
309
320
fudge_factor=self._bytes_to_read_before_seek))
311
request = self.get_smart_medium().get_request()
312
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
313
smart_protocol.call_with_body_readv_array(
314
('readv', self._remote_path(relpath)),
315
[(c.start, c.length) for c in coalesced])
316
resp = smart_protocol.read_response_tuple(True)
323
result = self._client.call_with_body_readv_array(
324
('readv', self._remote_path(relpath),),
325
[(c.start, c.length) for c in coalesced])
326
resp, response_handler = result
327
except errors.ErrorFromSmartServer, err:
328
self._translate_error(err.error_tuple)
318
330
if resp[0] != 'readv':
319
331
# This should raise an exception
320
smart_protocol.cancel_read_body()
321
self._translate_error(resp)
332
response_handler.cancel_read_body()
333
raise errors.UnexpectedSmartServerResponse(resp)
324
335
# FIXME: this should know how many bytes are needed, for clarity.
325
data = smart_protocol.read_body_bytes()
336
data = response_handler.read_body_bytes()
326
337
# Cache the results, but only until they have been fulfilled
328
339
for c_offset in coalesced:
461
472
def _build_medium(self):
462
assert self.base.startswith('bzr://')
463
return medium.SmartTCPClientMedium(self._host, self._port), None
473
client_medium = medium.SmartTCPClientMedium(
474
self._host, self._port, self.base)
475
return client_medium, None
466
478
class RemoteSSHTransport(RemoteTransport):
473
485
def _build_medium(self):
474
assert self.base.startswith('bzr+ssh://')
475
486
# ssh will prompt the user for a password if needed and if none is
476
487
# provided but it will not give it back, so no credentials can be
478
489
location_config = config.LocationConfig(self.base)
479
490
bzr_remote_path = location_config.get_bzr_remote_path()
480
return medium.SmartSSHClientMedium(self._host, self._port,
481
self._user, self._password, bzr_remote_path=bzr_remote_path), None
491
client_medium = medium.SmartSSHClientMedium(self._host, self._port,
492
self._user, self._password, self.base,
493
bzr_remote_path=bzr_remote_path)
494
return client_medium, None
484
497
class RemoteHTTPTransport(RemoteTransport):
495
508
def __init__(self, base, _from_transport=None, http_transport=None):
496
assert ( base.startswith('bzr+http://') or base.startswith('bzr+https://') )
498
509
if http_transport is None:
499
510
# FIXME: the password may be lost here because it appears in the
500
511
# url only for an intial construction (when the url came from the