37
from bzrlib.smart import client, medium, protocol
35
from bzrlib.smart import client, medium
36
from bzrlib.symbol_versioning import (deprecated_method, one_four)
40
39
class _SmartStat(object):
78
77
one is being cloned from. Attributes such as the medium will
81
:param medium: The medium to use for this RemoteTransport. This must be
82
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.
84
85
:param _client: Override the _SmartClient used by this transport. This
85
86
should only be used for testing purposes; normally this is
104
105
self._shared_connection = transport._SharedConnection(medium,
109
# No medium was specified, so share the medium from the
111
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))
108
118
if _client is None:
109
self._client = client._SmartClient(self.get_shared_medium())
119
self._client = client._SmartClient(medium)
111
121
self._client = _client
122
132
def is_readonly(self):
123
133
"""Smart server transport can do read/write file operations."""
124
resp = self._call2('Transport.is_readonly')
125
if resp == ('yes', ):
127
elif resp == ('no', ):
129
elif (resp == ('error', "Generic bzr smart protocol error: "
130
"bad request 'Transport.is_readonly'") or
131
resp == ('error', "Generic bzr smart protocol error: "
132
"bad request u'Transport.is_readonly'")):
135
resp = self._call2('Transport.is_readonly')
136
except errors.UnknownSmartMethod:
133
137
# XXX: nasty hack: servers before 0.16 don't have a
134
138
# 'Transport.is_readonly' verb, so we do what clients before 0.16
135
139
# did: assume False.
141
if resp == ('yes', ):
143
elif resp == ('no', ):
138
146
self._translate_error(resp)
139
147
raise errors.UnexpectedSmartServerResponse(resp)
144
152
def get_smart_medium(self):
145
153
return self._get_connection()
155
@deprecated_method(one_four)
147
156
def get_shared_medium(self):
148
157
return self._get_shared_connection()
152
161
return self._combine_paths(self._path, relpath)
154
163
def _call(self, method, *args):
155
resp = self._call2(method, *args)
165
resp = self._call2(method, *args)
166
except errors.ErrorFromSmartServer, err:
167
self._translate_error(err.error_tuple)
156
168
self._translate_error(resp)
158
170
def _call2(self, method, *args):
159
171
"""Call a method on the remote server."""
160
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)
162
177
def _call_with_body_bytes(self, method, args, body):
163
178
"""Call a method on the remote server with body bytes."""
164
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)
166
184
def has(self, relpath):
167
185
"""Indicate whether a remote file of the given name exists or not.
186
204
def get_bytes(self, relpath):
187
205
remote = self._remote_path(relpath)
188
request = self.get_smart_medium().get_request()
189
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
190
smart_protocol.call('get', remote)
191
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)
192
210
if resp != ('ok', ):
193
smart_protocol.cancel_read_body()
194
self._translate_error(resp, relpath)
195
return smart_protocol.read_body_bytes()
211
response_handler.cancel_read_body()
212
raise errors.UnexpectedSmartServerResponse(resp)
213
return response_handler.read_body_bytes()
197
215
def _serialise_optional_mode(self, mode):
301
319
limit=self._max_readv_combine,
302
320
fudge_factor=self._bytes_to_read_before_seek))
304
request = self.get_smart_medium().get_request()
305
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
306
smart_protocol.call_with_body_readv_array(
307
('readv', self._remote_path(relpath)),
308
[(c.start, c.length) for c in coalesced])
309
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)
311
330
if resp[0] != 'readv':
312
331
# This should raise an exception
313
smart_protocol.cancel_read_body()
314
self._translate_error(resp)
332
response_handler.cancel_read_body()
333
raise errors.UnexpectedSmartServerResponse(resp)
317
335
# FIXME: this should know how many bytes are needed, for clarity.
318
data = smart_protocol.read_body_bytes()
336
data = response_handler.read_body_bytes()
319
337
# Cache the results, but only until they have been fulfilled
321
339
for c_offset in coalesced:
454
472
def _build_medium(self):
455
assert self.base.startswith('bzr://')
456
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
459
478
class RemoteSSHTransport(RemoteTransport):
466
485
def _build_medium(self):
467
assert self.base.startswith('bzr+ssh://')
468
486
# ssh will prompt the user for a password if needed and if none is
469
487
# provided but it will not give it back, so no credentials can be
471
489
location_config = config.LocationConfig(self.base)
472
490
bzr_remote_path = location_config.get_bzr_remote_path()
473
return medium.SmartSSHClientMedium(self._host, self._port,
474
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
477
497
class RemoteHTTPTransport(RemoteTransport):
488
508
def __init__(self, base, _from_transport=None, http_transport=None):
489
assert ( base.startswith('bzr+http://') or base.startswith('bzr+https://') )
491
509
if http_transport is None:
492
510
# FIXME: the password may be lost here because it appears in the
493
511
# url only for an intial construction (when the url came from the