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 ..bzr.smart import client, medium
42
43
class _SmartStat(object):
219
223
:see: Transport.get_bytes()/get_file()
221
return StringIO(self.get_bytes(relpath))
225
return BytesIO(self.get_bytes(relpath))
223
227
def get_bytes(self, relpath):
224
228
remote = self._remote_path(relpath)
226
resp, response_handler = self._client.call_expecting_body('get', remote)
227
except errors.ErrorFromSmartServer, err:
230
resp, response_handler = self._client.call_expecting_body(b'get', remote)
231
except errors.ErrorFromSmartServer as err:
228
232
self._translate_error(err, relpath)
233
if resp != (b'ok', ):
230
234
response_handler.cancel_read_body()
231
235
raise errors.UnexpectedSmartServerResponse(resp)
232
236
return response_handler.read_body_bytes()
234
238
def _serialise_optional_mode(self, mode):
242
return ('%d' % mode).encode('ascii')
240
244
def mkdir(self, relpath, mode=None):
241
resp = self._call2('mkdir', self._remote_path(relpath),
245
resp = self._call2(b'mkdir', self._remote_path(relpath),
242
246
self._serialise_optional_mode(mode))
244
248
def open_write_stream(self, relpath, mode=None):
245
249
"""See Transport.open_write_stream."""
246
self.put_bytes(relpath, "", mode)
250
self.put_bytes(relpath, b"", mode)
247
251
result = transport.AppendBasedFileStream(self, relpath)
248
252
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',
255
def put_bytes(self, relpath, raw_bytes, mode=None):
256
if not isinstance(raw_bytes, bytes):
258
'raw_bytes must be bytes string, not %s' % type(raw_bytes))
259
resp = self._call_with_body_bytes(
262
261
(self._remote_path(relpath), self._serialise_optional_mode(mode)),
264
263
self._ensure_ok(resp)
265
return len(upload_contents)
264
return len(raw_bytes)
267
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
266
def put_bytes_non_atomic(self, relpath, raw_bytes, mode=None,
268
267
create_parent_dir=False,
270
269
"""See Transport.put_bytes_non_atomic."""
271
270
# FIXME: no encoding in the transport!
272
create_parent_str = 'F'
271
create_parent_str = b'F'
273
272
if create_parent_dir:
274
create_parent_str = 'T'
273
create_parent_str = b'T'
276
275
resp = self._call_with_body_bytes(
278
277
(self._remote_path(relpath), self._serialise_optional_mode(mode),
279
278
create_parent_str, self._serialise_optional_mode(dir_mode)),
281
280
self._ensure_ok(resp)
283
282
def put_file(self, relpath, upload_file, mode=None):
304
303
def append_bytes(self, relpath, bytes, mode=None):
305
304
resp = self._call_with_body_bytes(
307
306
(self._remote_path(relpath), self._serialise_optional_mode(mode)),
309
if resp[0] == 'appended':
308
if resp[0] == b'appended':
310
309
return int(resp[1])
311
310
raise errors.UnexpectedSmartServerResponse(resp)
313
312
def delete(self, relpath):
314
resp = self._call2('delete', self._remote_path(relpath))
313
resp = self._call2(b'delete', self._remote_path(relpath))
315
314
self._ensure_ok(resp)
317
316
def external_url(self):
318
"""See bzrlib.transport.Transport.external_url."""
317
"""See breezy.transport.Transport.external_url."""
319
318
# the external path for RemoteTransports is the base
359
358
# turn the list of offsets into a single stack to iterate
360
359
offset_stack = iter(offsets)
361
360
# using a list so it can be modified when passing down and coming back
362
next_offset = [offset_stack.next()]
361
next_offset = [next(offset_stack)]
363
362
for cur_request in requests:
365
364
result = self._client.call_with_body_readv_array(
366
('readv', self._remote_path(relpath),),
365
(b'readv', self._remote_path(relpath),),
367
366
[(c.start, c.length) for c in cur_request])
368
367
resp, response_handler = result
369
except errors.ErrorFromSmartServer, err:
368
except errors.ErrorFromSmartServer as err:
370
369
self._translate_error(err, relpath)
372
if resp[0] != 'readv':
371
if resp[0] != b'readv':
373
372
# This should raise an exception
374
373
response_handler.cancel_read_body()
375
374
raise errors.UnexpectedSmartServerResponse(resp)
412
411
while cur_offset_and_size in data_map:
413
412
this_data = data_map.pop(cur_offset_and_size)
414
413
yield cur_offset_and_size[0], this_data
415
cur_offset_and_size = next_offset[0] = offset_stack.next()
414
cur_offset_and_size = next_offset[0] = next(offset_stack)
417
416
def rename(self, rel_from, rel_to):
417
self._call(b'rename',
419
418
self._remote_path(rel_from),
420
419
self._remote_path(rel_to))
422
421
def move(self, rel_from, rel_to):
424
423
self._remote_path(rel_from),
425
424
self._remote_path(rel_to))
427
426
def rmdir(self, relpath):
428
resp = self._call('rmdir', self._remote_path(relpath))
427
resp = self._call(b'rmdir', self._remote_path(relpath))
430
429
def _ensure_ok(self, resp):
432
431
raise errors.UnexpectedSmartServerResponse(resp)
434
433
def _translate_error(self, err, relpath=None):
435
434
remote._translate_error(err, path=relpath)
437
436
def disconnect(self):
438
self.get_smart_medium().disconnect()
437
m = self.get_smart_medium()
440
441
def stat(self, relpath):
441
resp = self._call2('stat', self._remote_path(relpath))
442
if resp[0] == 'stat':
442
resp = self._call2(b'stat', self._remote_path(relpath))
443
if resp[0] == b'stat':
443
444
return _SmartStat(int(resp[1]), int(resp[2], 8))
444
445
raise errors.UnexpectedSmartServerResponse(resp)
462
463
def list_dir(self, relpath):
463
resp = self._call2('list_dir', self._remote_path(relpath))
464
if resp[0] == 'names':
464
resp = self._call2(b'list_dir', self._remote_path(relpath))
465
if resp[0] == b'names':
465
466
return [name.encode('ascii') for name in resp[1:]]
466
467
raise errors.UnexpectedSmartServerResponse(resp)
468
469
def iter_files_recursive(self):
469
resp = self._call2('iter_files_recursive', self._remote_path(''))
470
if resp[0] == 'names':
470
resp = self._call2(b'iter_files_recursive', self._remote_path(''))
471
if resp[0] == b'names':
472
473
raise errors.UnexpectedSmartServerResponse(resp)
510
511
def _build_medium(self):
511
512
location_config = config.LocationConfig(self.base)
512
513
bzr_remote_path = location_config.get_bzr_remote_path()
514
user = self._parsed_url.user
515
516
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)
517
user = auth.get_user('ssh', self._parsed_url.host,
518
self._parsed_url.port)
519
ssh_params = medium.SSHParams(self._parsed_url.host,
520
self._parsed_url.port, user, self._parsed_url.password,
522
client_medium = medium.SmartSSHClientMedium(self.base, ssh_params)
523
return client_medium, (user, self._parsed_url.password)
523
526
class RemoteHTTPTransport(RemoteTransport):