OLD | NEW |
1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """An implementation of the server side of the Chromium sync protocol. | 6 """An implementation of the server side of the Chromium sync protocol. |
7 | 7 |
8 The details of the protocol are described mostly by comments in the protocol | 8 The details of the protocol are described mostly by comments in the protocol |
9 buffer definition at chrome/browser/sync/protocol/sync.proto. | 9 buffer definition at chrome/browser/sync/protocol/sync.proto. |
10 """ | 10 """ |
(...skipping 491 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 # TODO(nick): Implement cycle detection and resolution. | 502 # TODO(nick): Implement cycle detection and resolution. |
503 if not self._CheckParentIdForCommit(entry): | 503 if not self._CheckParentIdForCommit(entry): |
504 return None | 504 return None |
505 | 505 |
506 # At this point, the commit is definitely going to happen. | 506 # At this point, the commit is definitely going to happen. |
507 | 507 |
508 # Deletion works by storing a limited record for an entry, called a | 508 # Deletion works by storing a limited record for an entry, called a |
509 # tombstone. A sync server must track deleted IDs forever, since it does | 509 # tombstone. A sync server must track deleted IDs forever, since it does |
510 # not keep track of client knowledge (there's no deletion ACK event). | 510 # not keep track of client knowledge (there's no deletion ACK event). |
511 if entry.deleted: | 511 if entry.deleted: |
512 # Only the ID, version and deletion state are preserved on a tombstone. | 512 def MakeTombstone(id_string): |
513 # TODO(nick): Does the production server not preserve the type? Not | 513 """Make a tombstone entry that will replace the entry being deleted. |
514 # doing so means that tombstones cannot be filtered based on | 514 |
515 # requested_types at GetUpdates time. | 515 Args: |
516 tombstone = sync_pb2.SyncEntity() | 516 id_string: Index of the SyncEntity to be deleted. |
517 tombstone.id_string = entry.id_string | 517 Returns: |
518 tombstone.deleted = True | 518 A new SyncEntity reflecting the fact that the entry is deleted. |
519 tombstone.name = '' | 519 """ |
520 entry = tombstone | 520 # Only the ID, version and deletion state are preserved on a tombstone. |
| 521 # TODO(nick): Does the production server not preserve the type? Not |
| 522 # doing so means that tombstones cannot be filtered based on |
| 523 # requested_types at GetUpdates time. |
| 524 tombstone = sync_pb2.SyncEntity() |
| 525 tombstone.id_string = id_string |
| 526 tombstone.deleted = True |
| 527 tombstone.name = '' |
| 528 return tombstone |
| 529 |
| 530 def IsChild(child_id): |
| 531 """Check if a SyncEntity is a child of entry, or any of its children. |
| 532 |
| 533 Args: |
| 534 child_id: Index of the SyncEntity that is a possible child of entry. |
| 535 Returns: |
| 536 True if it is a child; false otherwise. |
| 537 """ |
| 538 if child_id not in self._entries: |
| 539 return False |
| 540 if self._entries[child_id].parent_id_string == entry.id_string: |
| 541 return True |
| 542 return IsChild(self._entries[child_id].parent_id_string) |
| 543 |
| 544 # Identify any children entry might have. |
| 545 child_ids = [] |
| 546 for possible_child in self._entries.itervalues(): |
| 547 if IsChild(possible_child.id_string): |
| 548 child_ids.append(possible_child.id_string) |
| 549 |
| 550 # Mark all children that were identified as deleted. |
| 551 for child_id in child_ids: |
| 552 self._SaveEntry(MakeTombstone(child_id)) |
| 553 |
| 554 # Delete entry itself. |
| 555 entry = MakeTombstone(entry.id_string) |
521 else: | 556 else: |
522 # Comments in sync.proto detail how the representation of positional | 557 # Comments in sync.proto detail how the representation of positional |
523 # ordering works: the 'insert_after_item_id' field specifies a | 558 # ordering works: the 'insert_after_item_id' field specifies a |
524 # predecessor during Commit operations, but the 'position_in_parent' | 559 # predecessor during Commit operations, but the 'position_in_parent' |
525 # field provides an absolute ordering in GetUpdates contexts. Here | 560 # field provides an absolute ordering in GetUpdates contexts. Here |
526 # we convert from the former to the latter. Specifically, we'll | 561 # we convert from the former to the latter. Specifically, we'll |
527 # generate a numeric position placing the item just after the object | 562 # generate a numeric position placing the item just after the object |
528 # identified by 'insert_after_item_id', and then clear the | 563 # identified by 'insert_after_item_id', and then clear the |
529 # 'insert_after_item_id' field so that it's not sent back to the client | 564 # 'insert_after_item_id' field so that it's not sent back to the client |
530 # during later GetUpdates requests. | 565 # during later GetUpdates requests. |
531 if entry.HasField('insert_after_item_id'): | 566 if entry.HasField('insert_after_item_id'): |
532 self._WritePosition(entry, entry.parent_id_string, | 567 self._WritePosition(entry, entry.parent_id_string, |
533 entry.insert_after_item_id) | 568 entry.insert_after_item_id) |
534 else: | 569 else: |
535 self._WritePosition(entry, entry.parent_id_string) | 570 self._WritePosition(entry, entry.parent_id_string) |
536 | 571 |
537 # Preserve the originator info, which the client is not required to send | 572 # Preserve the originator info, which the client is not required to send |
538 # when updating. | 573 # when updating. |
539 base_entry = self._entries.get(entry.id_string) | 574 base_entry = self._entries.get(entry.id_string) |
540 if base_entry and not entry.HasField("originator_cache_guid"): | 575 if base_entry and not entry.HasField("originator_cache_guid"): |
541 entry.originator_cache_guid = base_entry.originator_cache_guid | 576 entry.originator_cache_guid = base_entry.originator_cache_guid |
542 entry.originator_client_item_id = base_entry.originator_client_item_id | 577 entry.originator_client_item_id = base_entry.originator_client_item_id |
543 | 578 |
544 # Commit the change. This also updates the version number. | 579 # Commit the change. This also updates the version number. |
545 self._SaveEntry(entry) | 580 self._SaveEntry(entry) |
546 # TODO(nick): Handle recursive deletion. | |
547 return entry | 581 return entry |
548 | 582 |
549 class TestServer(object): | 583 class TestServer(object): |
550 """An object to handle requests for one (and only one) Chrome Sync account. | 584 """An object to handle requests for one (and only one) Chrome Sync account. |
551 | 585 |
552 TestServer consumes the sync command messages that are the outermost | 586 TestServer consumes the sync command messages that are the outermost |
553 layers of the protocol, performs the corresponding actions on its | 587 layers of the protocol, performs the corresponding actions on its |
554 SyncDataModel, and constructs an appropropriate response message. | 588 SyncDataModel, and constructs an appropropriate response message. |
555 """ | 589 """ |
556 | 590 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
658 new_timestamp, entries = self.account.GetChangesFromTimestamp( | 692 new_timestamp, entries = self.account.GetChangesFromTimestamp( |
659 requested_types, update_request.from_timestamp) | 693 requested_types, update_request.from_timestamp) |
660 | 694 |
661 # If the client is up to date, we are careful not to set the | 695 # If the client is up to date, we are careful not to set the |
662 # new_timestamp field. | 696 # new_timestamp field. |
663 if new_timestamp != update_request.from_timestamp: | 697 if new_timestamp != update_request.from_timestamp: |
664 update_response.new_timestamp = new_timestamp | 698 update_response.new_timestamp = new_timestamp |
665 for e in entries: | 699 for e in entries: |
666 reply = update_response.entries.add() | 700 reply = update_response.entries.add() |
667 reply.CopyFrom(e) | 701 reply.CopyFrom(e) |
OLD | NEW |