136
122
def is_readonly(self):
137
123
"""Smart server transport can do read/write file operations."""
139
resp = self._call2('Transport.is_readonly')
140
except errors.UnknownSmartMethod:
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'")):
141
133
# XXX: nasty hack: servers before 0.16 don't have a
142
134
# 'Transport.is_readonly' verb, so we do what clients before 0.16
143
135
# did: assume False.
145
if resp == ('yes', ):
147
elif resp == ('no', ):
150
raise errors.UnexpectedSmartServerResponse(resp)
138
self._translate_error(resp)
139
raise errors.UnexpectedSmartServerResponse(resp)
152
141
def get_smart_client(self):
153
142
return self._get_connection()
166
154
def _call(self, method, *args):
167
155
resp = self._call2(method, *args)
168
self._ensure_ok(resp)
156
self._translate_error(resp)
170
158
def _call2(self, method, *args):
171
159
"""Call a method on the remote server."""
173
return self._client.call(method, *args)
174
except errors.ErrorFromSmartServer, err:
175
# The first argument, if present, is always a path.
177
context = {'relpath': args[0]}
180
self._translate_error(err, **context)
160
return self._client.call(method, *args)
182
162
def _call_with_body_bytes(self, method, args, body):
183
163
"""Call a method on the remote server with body bytes."""
185
return self._client.call_with_body_bytes(method, args, body)
186
except errors.ErrorFromSmartServer, err:
187
# The first argument, if present, is always a path.
189
context = {'relpath': args[0]}
192
self._translate_error(err, **context)
164
return self._client.call_with_body_bytes(method, args, body)
194
166
def has(self, relpath):
195
167
"""Indicate whether a remote file of the given name exists or not.
214
186
def get_bytes(self, relpath):
215
187
remote = self._remote_path(relpath)
217
resp, response_handler = self._client.call_expecting_body('get', remote)
218
except errors.ErrorFromSmartServer, err:
219
self._translate_error(err, 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)
220
192
if resp != ('ok', ):
221
response_handler.cancel_read_body()
222
raise errors.UnexpectedSmartServerResponse(resp)
223
return response_handler.read_body_bytes()
193
smart_protocol.cancel_read_body()
194
self._translate_error(resp, relpath)
195
return smart_protocol.read_body_bytes()
225
197
def _serialise_optional_mode(self, mode):
321
294
offsets = list(offsets)
323
296
sorted_offsets = sorted(offsets)
297
# turn the list of offsets into a stack
298
offset_stack = iter(offsets)
299
cur_offset_and_size = offset_stack.next()
324
300
coalesced = list(self._coalesce_offsets(sorted_offsets,
325
301
limit=self._max_readv_combine,
326
fudge_factor=self._bytes_to_read_before_seek,
327
max_size=self._max_readv_bytes))
329
# now that we've coallesced things, avoid making enormous requests
334
if c.length + cur_len > self._max_readv_bytes:
335
requests.append(cur_request)
339
cur_request.append(c)
342
requests.append(cur_request)
343
if 'hpss' in debug.debug_flags:
344
trace.mutter('%s.readv %s offsets => %s coalesced'
345
' => %s requests (%s)',
346
self.__class__.__name__, len(offsets), len(coalesced),
347
len(requests), sum(map(len, requests)))
302
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)
311
if resp[0] != 'readv':
312
# This should raise an exception
313
smart_protocol.cancel_read_body()
314
self._translate_error(resp)
317
# FIXME: this should know how many bytes are needed, for clarity.
318
data = smart_protocol.read_body_bytes()
348
319
# Cache the results, but only until they have been fulfilled
350
# turn the list of offsets into a single stack to iterate
351
offset_stack = iter(offsets)
352
# using a list so it can be modified when passing down and coming back
353
next_offset = [offset_stack.next()]
354
for cur_request in requests:
356
result = self._client.call_with_body_readv_array(
357
('readv', self._remote_path(relpath),),
358
[(c.start, c.length) for c in cur_request])
359
resp, response_handler = result
360
except errors.ErrorFromSmartServer, err:
361
self._translate_error(err, relpath)
363
if resp[0] != 'readv':
364
# This should raise an exception
365
response_handler.cancel_read_body()
366
raise errors.UnexpectedSmartServerResponse(resp)
368
for res in self._handle_response(offset_stack, cur_request,
374
def _handle_response(self, offset_stack, coalesced, response_handler,
375
data_map, next_offset):
376
cur_offset_and_size = next_offset[0]
377
# FIXME: this should know how many bytes are needed, for clarity.
378
data = response_handler.read_body_bytes()
380
321
for c_offset in coalesced:
381
322
if len(data) < c_offset.length:
382
323
raise errors.ShortReadvError(relpath, c_offset.start,
383
324
c_offset.length, actual=len(data))
384
325
for suboffset, subsize in c_offset.ranges:
385
326
key = (c_offset.start+suboffset, subsize)
386
this_data = data[data_offset+suboffset:
387
data_offset+suboffset+subsize]
388
# Special case when the data is in-order, rather than packing
389
# into a map and then back out again. Benchmarking shows that
390
# this has 100% hit rate, but leave in the data_map work just
392
# TODO: Could we get away with using buffer() to avoid the
393
# memory copy? Callers would need to realize they may
394
# not have a real string.
395
if key == cur_offset_and_size:
396
yield cur_offset_and_size[0], this_data
397
cur_offset_and_size = next_offset[0] = offset_stack.next()
399
data_map[key] = this_data
400
data_offset += c_offset.length
327
data_map[key] = data[suboffset:suboffset+subsize]
328
data = data[c_offset.length:]
402
330
# Now that we've read some data, see if we can yield anything back
403
331
while cur_offset_and_size in data_map:
404
332
this_data = data_map.pop(cur_offset_and_size)
405
333
yield cur_offset_and_size[0], this_data
406
cur_offset_and_size = next_offset[0] = offset_stack.next()
334
cur_offset_and_size = offset_stack.next()
408
336
def rename(self, rel_from, rel_to):
409
337
self._call('rename',
418
346
def rmdir(self, relpath):
419
347
resp = self._call('rmdir', self._remote_path(relpath))
421
def _ensure_ok(self, resp):
423
raise errors.UnexpectedSmartServerResponse(resp)
425
def _translate_error(self, err, relpath=None):
426
remote._translate_error(err, path=relpath)
349
def _translate_error(self, resp, orig_path=None):
350
"""Raise an exception from a response"""
357
elif what == 'NoSuchFile':
358
if orig_path is not None:
359
error_path = orig_path
362
raise errors.NoSuchFile(error_path)
363
elif what == 'error':
364
raise errors.SmartProtocolError(unicode(resp[1]))
365
elif what == 'FileExists':
366
raise errors.FileExists(resp[1])
367
elif what == 'DirectoryNotEmpty':
368
raise errors.DirectoryNotEmpty(resp[1])
369
elif what == 'ShortReadvError':
370
raise errors.ShortReadvError(resp[1], int(resp[2]),
371
int(resp[3]), int(resp[4]))
372
elif what in ('UnicodeEncodeError', 'UnicodeDecodeError'):
373
encoding = str(resp[1]) # encoding must always be a string
377
reason = str(resp[5]) # reason must always be a string
378
if val.startswith('u:'):
379
val = val[2:].decode('utf-8')
380
elif val.startswith('s:'):
381
val = val[2:].decode('base64')
382
if what == 'UnicodeDecodeError':
383
raise UnicodeDecodeError(encoding, val, start, end, reason)
384
elif what == 'UnicodeEncodeError':
385
raise UnicodeEncodeError(encoding, val, start, end, reason)
386
elif what == "ReadOnlyError":
387
raise errors.TransportNotPossible('readonly transport')
388
elif what == "ReadError":
389
if orig_path is not None:
390
error_path = orig_path
393
raise errors.ReadError(error_path)
394
elif what == "PermissionDenied":
395
if orig_path is not None:
396
error_path = orig_path
399
raise errors.PermissionDenied(error_path)
401
raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
428
403
def disconnect(self):
429
404
self.get_smart_medium().disconnect()
406
def delete_tree(self, relpath):
407
raise errors.TransportNotPossible('readonly transport')
431
409
def stat(self, relpath):
432
410
resp = self._call2('stat', self._remote_path(relpath))
433
411
if resp[0] == 'stat':
434
412
return _SmartStat(int(resp[1]), int(resp[2], 8))
435
raise errors.UnexpectedSmartServerResponse(resp)
414
self._translate_error(resp)
437
416
## def lock_read(self, relpath):
438
417
## """Lock the given file for shared (read) access.
473
454
def _build_medium(self):
474
client_medium = medium.SmartTCPClientMedium(
475
self._host, self._port, self.base)
476
return client_medium, None
479
class RemoteTCPTransportV2Only(RemoteTransport):
480
"""Connection to smart server over plain tcp with the client hard-coded to
481
assume protocol v2 and remote server version <= 1.6.
483
This should only be used for testing.
486
def _build_medium(self):
487
client_medium = medium.SmartTCPClientMedium(
488
self._host, self._port, self.base)
489
client_medium._protocol_version = 2
490
client_medium._remember_remote_is_before((1, 6))
491
return client_medium, None
455
assert self.base.startswith('bzr://')
456
return medium.SmartTCPClientMedium(self._host, self._port), None
494
459
class RemoteSSHTransport(RemoteTransport):
501
466
def _build_medium(self):
467
assert self.base.startswith('bzr+ssh://')
468
# ssh will prompt the user for a password if needed and if none is
469
# provided but it will not give it back, so no credentials can be
502
471
location_config = config.LocationConfig(self.base)
503
472
bzr_remote_path = location_config.get_bzr_remote_path()
506
auth = config.AuthenticationConfig()
507
user = auth.get_user('ssh', self._host, self._port)
508
client_medium = medium.SmartSSHClientMedium(self._host, self._port,
509
user, self._password, self.base,
510
bzr_remote_path=bzr_remote_path)
511
return client_medium, (user, self._password)
473
return medium.SmartSSHClientMedium(self._host, self._port,
474
self._user, self._password, bzr_remote_path=bzr_remote_path), None
514
477
class RemoteHTTPTransport(RemoteTransport):