414
409
def do_repository_request(self, repository, to_network_name):
415
410
"""Get a stream for inserting into a to_format repository.
417
The request body is 'search_bytes', a description of the revisions
420
In 2.3 this verb added support for search_bytes == 'everything'. Older
421
implementations will respond with a BadSearch error, and clients should
422
catch this and fallback appropriately.
424
412
:param repository: The repository to stream from.
425
413
:param to_network_name: The network name of the format of the target
428
416
self._to_format = network_format_registry.get(to_network_name)
429
if self._should_fake_unknown():
430
return FailedSmartServerResponse(
431
('UnknownMethod', 'Repository.get_stream'))
432
417
return None # Signal that we want a body.
434
def _should_fake_unknown(self):
435
"""Return True if we should return UnknownMethod to the client.
437
This is a workaround for bugs in pre-1.19 clients that claim to
438
support receiving streams of CHK repositories. The pre-1.19 client
439
expects inventory records to be serialized in the format defined by
440
to_network_name, but in pre-1.19 (at least) that format definition
441
tries to use the xml5 serializer, which does not correctly handle
442
rich-roots. After 1.19 the client can also accept inventory-deltas
443
(which avoids this issue), and those clients will use the
444
Repository.get_stream_1.19 verb instead of this one.
445
So: if this repository is CHK, and the to_format doesn't match,
446
we should just fake an UnknownSmartMethod error so that the client
447
will fallback to VFS, rather than sending it a stream we know it
450
from_format = self._repository._format
451
to_format = self._to_format
452
if not from_format.supports_chks:
453
# Source not CHK: that's ok
455
if (to_format.supports_chks and
456
from_format.repository_class is to_format.repository_class and
457
from_format._serializer == to_format._serializer):
458
# Source is CHK, but target matches: that's ok
459
# (e.g. 2a->2a, or CHK2->2a)
461
# Source is CHK, and target is not CHK or incompatible CHK. We can't
462
# generate a compatible stream.
465
419
def do_body(self, body_bytes):
466
420
repository = self._repository
467
421
repository.lock_read()
532
472
yield pack_writer.end()
535
class _ByteStreamDecoder(object):
536
"""Helper for _byte_stream_to_stream.
538
The expected usage of this class is via the function _byte_stream_to_stream
539
which creates a _ByteStreamDecoder, pops off the stream format and then
540
yields the output of record_stream(), the main entry point to
543
Broadly this class has to unwrap two layers of iterators:
547
This is complicated by wishing to return type, iterator_for_type, but
548
getting the data for iterator_for_type when we find out type: we can't
549
simply pass a generator down to the NetworkRecordStream parser, instead
550
we have a little local state to seed each NetworkRecordStream instance,
551
and gather the type that we'll be yielding.
553
:ivar byte_stream: The byte stream being decoded.
554
:ivar stream_decoder: A pack parser used to decode the bytestream
555
:ivar current_type: The current type, used to join adjacent records of the
556
same type into a single stream.
557
:ivar first_bytes: The first bytes to give the next NetworkRecordStream.
475
def _byte_stream_to_stream(byte_stream):
476
"""Convert a byte stream into a format and a stream.
478
:param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
479
:return: (RepositoryFormat, stream_generator)
560
def __init__(self, byte_stream, record_counter):
561
"""Create a _ByteStreamDecoder."""
562
self.stream_decoder = pack.ContainerPushParser()
563
self.current_type = None
564
self.first_bytes = None
565
self.byte_stream = byte_stream
566
self._record_counter = record_counter
569
def iter_stream_decoder(self):
570
"""Iterate the contents of the pack from stream_decoder."""
571
# dequeue pending items
572
for record in self.stream_decoder.read_pending_records():
574
# Pull bytes of the wire, decode them to records, yield those records.
575
for bytes in self.byte_stream:
576
self.stream_decoder.accept_bytes(bytes)
577
for record in self.stream_decoder.read_pending_records():
580
def iter_substream_bytes(self):
581
if self.first_bytes is not None:
582
yield self.first_bytes
583
# If we run out of pack records, single the outer layer to stop.
584
self.first_bytes = None
585
for record in self.iter_pack_records:
481
stream_decoder = pack.ContainerPushParser()
483
"""Closure to return the substreams."""
484
# May have fully parsed records already.
485
for record in stream_decoder.read_pending_records():
586
486
record_names, record_bytes = record
587
487
record_name, = record_names
588
488
substream_type = record_name[0]
589
if substream_type != self.current_type:
590
# end of a substream, seed the next substream.
591
self.current_type = substream_type
592
self.first_bytes = record_bytes
596
def record_stream(self):
597
"""Yield substream_type, substream from the byte stream."""
598
def wrap_and_count(pb, rc, substream):
599
"""Yield records from stream while showing progress."""
602
if self.current_type != 'revisions' and self.key_count != 0:
603
# As we know the number of revisions now (in self.key_count)
604
# we can setup and use record_counter (rc).
605
if not rc.is_initialized():
606
rc.setup(self.key_count, self.key_count)
607
for record in substream.read():
609
if rc.is_initialized() and counter == rc.STEP:
610
rc.increment(counter)
611
pb.update('Estimate', rc.current, rc.max)
613
if self.current_type == 'revisions':
614
# Total records is proportional to number of revs
615
# to fetch. With remote, we used self.key_count to
616
# track the number of revs. Once we have the revs
617
# counts in self.key_count, the progress bar changes
618
# from 'Estimating..' to 'Estimate' above.
620
if counter == rc.STEP:
621
pb.update('Estimating..', self.key_count)
627
pb = ui.ui_factory.nested_progress_bar()
628
rc = self._record_counter
629
# Make and consume sub generators, one per substream type:
630
while self.first_bytes is not None:
631
substream = NetworkRecordStream(self.iter_substream_bytes())
632
# after substream is fully consumed, self.current_type is set to
633
# the next type, and self.first_bytes is set to the matching bytes.
634
yield self.current_type, wrap_and_count(pb, rc, substream)
636
pb.update('Done', rc.max, rc.max)
639
def seed_state(self):
640
"""Prepare the _ByteStreamDecoder to decode from the pack stream."""
641
# Set a single generator we can use to get data from the pack stream.
642
self.iter_pack_records = self.iter_stream_decoder()
643
# Seed the very first subiterator with content; after this each one
645
list(self.iter_substream_bytes())
648
def _byte_stream_to_stream(byte_stream, record_counter=None):
649
"""Convert a byte stream into a format and a stream.
651
:param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
652
:return: (RepositoryFormat, stream_generator)
654
decoder = _ByteStreamDecoder(byte_stream, record_counter)
489
substream = NetworkRecordStream([record_bytes])
490
yield substream_type, substream.read()
491
for bytes in byte_stream:
492
stream_decoder.accept_bytes(bytes)
493
for record in stream_decoder.read_pending_records():
494
record_names, record_bytes = record
495
record_name, = record_names
496
substream_type = record_name[0]
497
substream = NetworkRecordStream([record_bytes])
498
yield substream_type, substream.read()
655
499
for bytes in byte_stream:
656
decoder.stream_decoder.accept_bytes(bytes)
657
for record in decoder.stream_decoder.read_pending_records(max=1):
500
stream_decoder.accept_bytes(bytes)
501
for record in stream_decoder.read_pending_records(max=1):
658
502
record_names, src_format_name = record
659
503
src_format = network_format_registry.get(src_format_name)
660
return src_format, decoder.record_stream()
504
return src_format, record_stream()
663
507
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):