17
17
"""RemoteTransport client for the smart-server.
19
19
This module shouldn't be accessed directly. The classes defined here should be
20
imported from bzrlib.smart.
20
imported from breezy.bzr.smart.
23
from __future__ import absolute_import
23
25
__all__ = ['RemoteTransport', 'RemoteTCPTransport', 'RemoteSSHTransport']
25
from cStringIO import StringIO
27
from io import BytesIO
36
from bzrlib.smart import client, medium
37
from bzrlib.symbol_versioning import (
40
from ..sixish import PY3
41
from ..bzr.smart import client, medium
42
44
class _SmartStat(object):
219
224
:see: Transport.get_bytes()/get_file()
221
return StringIO(self.get_bytes(relpath))
226
return BytesIO(self.get_bytes(relpath))
223
228
def get_bytes(self, relpath):
224
229
remote = self._remote_path(relpath)
226
resp, response_handler = self._client.call_expecting_body('get', remote)
227
except errors.ErrorFromSmartServer, err:
231
resp, response_handler = self._client.call_expecting_body(b'get', remote)
232
except errors.ErrorFromSmartServer as err:
228
233
self._translate_error(err, relpath)
234
if resp != (b'ok', ):
230
235
response_handler.cancel_read_body()
231
236
raise errors.UnexpectedSmartServerResponse(resp)
232
237
return response_handler.read_body_bytes()
234
239
def _serialise_optional_mode(self, mode):
243
return ('%d' % mode).encode('ascii')
240
245
def mkdir(self, relpath, mode=None):
241
resp = self._call2('mkdir', self._remote_path(relpath),
246
resp = self._call2(b'mkdir', self._remote_path(relpath),
242
247
self._serialise_optional_mode(mode))
244
249
def open_write_stream(self, relpath, mode=None):
245
250
"""See Transport.open_write_stream."""
246
self.put_bytes(relpath, "", mode)
251
self.put_bytes(relpath, b"", mode)
247
252
result = transport.AppendBasedFileStream(self, relpath)
248
253
transport._file_streams[self.abspath(relpath)] = result
251
def put_bytes(self, relpath, upload_contents, mode=None):
252
# FIXME: upload_file is probably not safe for non-ascii characters -
253
# should probably just pass all parameters as length-delimited
255
if type(upload_contents) is unicode:
256
# Although not strictly correct, we raise UnicodeEncodeError to be
257
# compatible with other transports.
258
raise UnicodeEncodeError(
259
'undefined', upload_contents, 0, 1,
260
'put_bytes must be given bytes, not unicode.')
261
resp = self._call_with_body_bytes('put',
256
def put_bytes(self, relpath, raw_bytes, mode=None):
257
if not isinstance(raw_bytes, bytes):
259
'raw_bytes must be bytes string, not %s' % type(raw_bytes))
260
resp = self._call_with_body_bytes(
262
262
(self._remote_path(relpath), self._serialise_optional_mode(mode)),
264
264
self._ensure_ok(resp)
265
return len(upload_contents)
265
return len(raw_bytes)
267
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
267
def put_bytes_non_atomic(self, relpath, raw_bytes, mode=None,
268
268
create_parent_dir=False,
270
270
"""See Transport.put_bytes_non_atomic."""
271
271
# FIXME: no encoding in the transport!
272
create_parent_str = 'F'
272
create_parent_str = b'F'
273
273
if create_parent_dir:
274
create_parent_str = 'T'
274
create_parent_str = b'T'
276
276
resp = self._call_with_body_bytes(
278
278
(self._remote_path(relpath), self._serialise_optional_mode(mode),
279
279
create_parent_str, self._serialise_optional_mode(dir_mode)),
281
281
self._ensure_ok(resp)
283
283
def put_file(self, relpath, upload_file, mode=None):
304
304
def append_bytes(self, relpath, bytes, mode=None):
305
305
resp = self._call_with_body_bytes(
307
307
(self._remote_path(relpath), self._serialise_optional_mode(mode)),
309
if resp[0] == 'appended':
309
if resp[0] == b'appended':
310
310
return int(resp[1])
311
311
raise errors.UnexpectedSmartServerResponse(resp)
313
313
def delete(self, relpath):
314
resp = self._call2('delete', self._remote_path(relpath))
314
resp = self._call2(b'delete', self._remote_path(relpath))
315
315
self._ensure_ok(resp)
317
317
def external_url(self):
318
"""See bzrlib.transport.Transport.external_url."""
318
"""See breezy.transport.Transport.external_url."""
319
319
# the external path for RemoteTransports is the base
359
359
# turn the list of offsets into a single stack to iterate
360
360
offset_stack = iter(offsets)
361
361
# using a list so it can be modified when passing down and coming back
362
next_offset = [offset_stack.next()]
362
next_offset = [next(offset_stack)]
363
363
for cur_request in requests:
365
365
result = self._client.call_with_body_readv_array(
366
('readv', self._remote_path(relpath),),
366
(b'readv', self._remote_path(relpath),),
367
367
[(c.start, c.length) for c in cur_request])
368
368
resp, response_handler = result
369
except errors.ErrorFromSmartServer, err:
369
except errors.ErrorFromSmartServer as err:
370
370
self._translate_error(err, relpath)
372
if resp[0] != 'readv':
372
if resp[0] != b'readv':
373
373
# This should raise an exception
374
374
response_handler.cancel_read_body()
375
375
raise errors.UnexpectedSmartServerResponse(resp)
412
415
while cur_offset_and_size in data_map:
413
416
this_data = data_map.pop(cur_offset_and_size)
414
417
yield cur_offset_and_size[0], this_data
415
cur_offset_and_size = next_offset[0] = offset_stack.next()
418
cur_offset_and_size = next_offset[0] = next(offset_stack)
417
420
def rename(self, rel_from, rel_to):
421
self._call(b'rename',
419
422
self._remote_path(rel_from),
420
423
self._remote_path(rel_to))
422
425
def move(self, rel_from, rel_to):
424
427
self._remote_path(rel_from),
425
428
self._remote_path(rel_to))
427
430
def rmdir(self, relpath):
428
resp = self._call('rmdir', self._remote_path(relpath))
431
resp = self._call(b'rmdir', self._remote_path(relpath))
430
433
def _ensure_ok(self, resp):
432
435
raise errors.UnexpectedSmartServerResponse(resp)
434
437
def _translate_error(self, err, relpath=None):
435
438
remote._translate_error(err, path=relpath)
437
440
def disconnect(self):
438
self.get_smart_medium().disconnect()
441
m = self.get_smart_medium()
440
445
def stat(self, relpath):
441
resp = self._call2('stat', self._remote_path(relpath))
442
if resp[0] == 'stat':
446
resp = self._call2(b'stat', self._remote_path(relpath))
447
if resp[0] == b'stat':
443
448
return _SmartStat(int(resp[1]), int(resp[2], 8))
444
449
raise errors.UnexpectedSmartServerResponse(resp)
462
467
def list_dir(self, relpath):
463
resp = self._call2('list_dir', self._remote_path(relpath))
464
if resp[0] == 'names':
465
return [name.encode('ascii') for name in resp[1:]]
468
resp = self._call2(b'list_dir', self._remote_path(relpath))
469
if resp[0] == b'names':
470
return [name.decode('utf-8') if PY3 else name for name in resp[1:]]
466
471
raise errors.UnexpectedSmartServerResponse(resp)
468
473
def iter_files_recursive(self):
469
resp = self._call2('iter_files_recursive', self._remote_path(''))
470
if resp[0] == 'names':
474
resp = self._call2(b'iter_files_recursive', self._remote_path(''))
475
if resp[0] == b'names':
476
return [name.decode('utf-8') if PY3 else name for name in resp[1:]]
472
477
raise errors.UnexpectedSmartServerResponse(resp)
510
515
def _build_medium(self):
511
516
location_config = config.LocationConfig(self.base)
512
517
bzr_remote_path = location_config.get_bzr_remote_path()
518
user = self._parsed_url.user
515
520
auth = config.AuthenticationConfig()
516
user = auth.get_user('ssh', self._host, self._port)
517
client_medium = medium.SmartSSHClientMedium(self._host, self._port,
518
user, self._password, self.base,
519
bzr_remote_path=bzr_remote_path)
520
return client_medium, (user, self._password)
521
user = auth.get_user('ssh', self._parsed_url.host,
522
self._parsed_url.port)
523
ssh_params = medium.SSHParams(self._parsed_url.host,
524
self._parsed_url.port, user, self._parsed_url.password,
526
client_medium = medium.SmartSSHClientMedium(self.base, ssh_params)
527
return client_medium, (user, self._parsed_url.password)
523
530
class RemoteHTTPTransport(RemoteTransport):