Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 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 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 datatypes: a list of the datatypes (python enum) needing migration. | 93 datatypes: a list of the datatypes (python enum) needing migration. |
| 94 """ | 94 """ |
| 95 | 95 |
| 96 def __init__(self, datatypes): | 96 def __init__(self, datatypes): |
| 97 self.datatypes = datatypes | 97 self.datatypes = datatypes |
| 98 | 98 |
| 99 | 99 |
| 100 class StoreBirthdayError(Error): | 100 class StoreBirthdayError(Error): |
| 101 """The client sent a birthday that doesn't correspond to this server.""" | 101 """The client sent a birthday that doesn't correspond to this server.""" |
| 102 | 102 |
| 103 class TransientError(Error): | |
| 104 """The client would be sent a transient error.""" | |
| 105 | |
| 103 | 106 |
| 104 def GetEntryType(entry): | 107 def GetEntryType(entry): |
| 105 """Extract the sync type from a SyncEntry. | 108 """Extract the sync type from a SyncEntry. |
| 106 | 109 |
| 107 Args: | 110 Args: |
| 108 entry: A SyncEntity protobuf object whose type to determine. | 111 entry: A SyncEntity protobuf object whose type to determine. |
| 109 Returns: | 112 Returns: |
| 110 An enum value from ALL_TYPES if the entry's type can be determined, or None | 113 An enum value from ALL_TYPES if the entry's type can be determined, or None |
| 111 if the type cannot be determined. | 114 if the type cannot be determined. |
| 112 Raises: | 115 Raises: |
| (...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 553 | 556 |
| 554 Args: | 557 Args: |
| 555 requested_types: A list of sync data types from ALL_TYPES. | 558 requested_types: A list of sync data types from ALL_TYPES. |
| 556 Permanent items of only these types will be created. | 559 Permanent items of only these types will be created. |
| 557 """ | 560 """ |
| 558 for spec in self._PERMANENT_ITEM_SPECS: | 561 for spec in self._PERMANENT_ITEM_SPECS: |
| 559 if spec.sync_type in requested_types: | 562 if spec.sync_type in requested_types: |
| 560 self._CreatePermanentItem(spec) | 563 self._CreatePermanentItem(spec) |
| 561 | 564 |
| 562 def ResetStoreBirthday(self): | 565 def ResetStoreBirthday(self): |
| 566 <<<<<<< HEAD | |
| 563 """Resets the store birthday to a random value.""" | 567 """Resets the store birthday to a random value.""" |
| 568 ======= | |
| 569 """Resets the store birthday to a random value. | |
| 570 """ | |
| 571 >>>>>>> fix. | |
|
Raghu Simha
2011/08/05 18:04:52
Merge.
lipalani1
2011/08/05 21:33:57
Done.
| |
| 564 # TODO(nick): uuid.uuid1() is better, but python 2.5 only. | 572 # TODO(nick): uuid.uuid1() is better, but python 2.5 only. |
| 565 self.store_birthday = '%0.30f' % random.random() | 573 self.store_birthday = '%0.30f' % random.random() |
| 566 | 574 |
| 567 def StoreBirthday(self): | 575 def StoreBirthday(self): |
| 576 <<<<<<< HEAD | |
| 568 """Gets the store birthday.""" | 577 """Gets the store birthday.""" |
| 578 ======= | |
| 579 """Gets the store birthday. | |
| 580 """ | |
| 581 >>>>>>> fix. | |
|
Raghu Simha
2011/08/05 18:04:52
Merge.
lipalani1
2011/08/05 21:33:57
Done.
| |
| 569 return self.store_birthday | 582 return self.store_birthday |
| 570 | 583 |
| 571 def GetChanges(self, sieve): | 584 def GetChanges(self, sieve): |
| 572 """Get entries which have changed, oldest first. | 585 """Get entries which have changed, oldest first. |
| 573 | 586 |
| 574 The returned entries are limited to being _BATCH_SIZE many. The entries | 587 The returned entries are limited to being _BATCH_SIZE many. The entries |
| 575 are returned in strict version order. | 588 are returned in strict version order. |
| 576 | 589 |
| 577 Args: | 590 Args: |
| 578 sieve: An update sieve to use to filter out updates the client | 591 sieve: An update sieve to use to filter out updates the client |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 870 | 883 |
| 871 def __init__(self): | 884 def __init__(self): |
| 872 # The implementation supports exactly one account; its state is here. | 885 # The implementation supports exactly one account; its state is here. |
| 873 self.account = SyncDataModel() | 886 self.account = SyncDataModel() |
| 874 self.account_lock = threading.Lock() | 887 self.account_lock = threading.Lock() |
| 875 # Clients that have talked to us: a map from the full client ID | 888 # Clients that have talked to us: a map from the full client ID |
| 876 # to its nickname. | 889 # to its nickname. |
| 877 self.clients = {} | 890 self.clients = {} |
| 878 self.client_name_generator = ('+' * times + chr(c) | 891 self.client_name_generator = ('+' * times + chr(c) |
| 879 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z'))) | 892 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z'))) |
| 893 self.transient_error = False | |
| 880 | 894 |
| 881 def GetShortClientName(self, query): | 895 def GetShortClientName(self, query): |
| 882 parsed = cgi.parse_qs(query[query.find('?')+1:]) | 896 parsed = cgi.parse_qs(query[query.find('?')+1:]) |
| 883 client_id = parsed.get('client_id') | 897 client_id = parsed.get('client_id') |
| 884 if not client_id: | 898 if not client_id: |
| 885 return '?' | 899 return '?' |
| 886 client_id = client_id[0] | 900 client_id = client_id[0] |
| 887 if client_id not in self.clients: | 901 if client_id not in self.clients: |
| 888 self.clients[client_id] = self.client_name_generator.next() | 902 self.clients[client_id] = self.client_name_generator.next() |
| 889 return self.clients[client_id] | 903 return self.clients[client_id] |
| 890 | 904 |
| 891 def CheckStoreBirthday(self, request): | 905 def CheckStoreBirthday(self, request): |
| 892 """Raises StoreBirthdayError if the request's birthday is a mismatch.""" | 906 """Raises StoreBirthdayError if the request's birthday is a mismatch.""" |
| 893 if not request.HasField('store_birthday'): | 907 if not request.HasField('store_birthday'): |
| 894 return | 908 return |
| 895 if self.account.StoreBirthday() != request.store_birthday: | 909 if self.account.StoreBirthday() != request.store_birthday: |
| 896 raise StoreBirthdayError | 910 raise StoreBirthdayError |
| 897 | 911 |
| 912 def CheckTransientError(self): | |
| 913 """Raises Transiet error if |transient_error| variable is set.""" | |
|
Raghu Simha
2011/08/05 18:04:52
s/Transiet/Transient/
lipalani1
2011/08/05 21:33:57
Done.
| |
| 914 if self.transient_error == True: | |
| 915 raise TransientError | |
| 916 | |
| 898 def HandleMigrate(self, path): | 917 def HandleMigrate(self, path): |
| 899 query = urlparse.urlparse(path)[4] | 918 query = urlparse.urlparse(path)[4] |
| 900 code = 200 | 919 code = 200 |
| 901 self.account_lock.acquire() | 920 self.account_lock.acquire() |
| 902 try: | 921 try: |
| 903 datatypes = [DataTypeStringToSyncTypeLoose(x) | 922 datatypes = [DataTypeStringToSyncTypeLoose(x) |
| 904 for x in urlparse.parse_qs(query).get('type',[])] | 923 for x in urlparse.parse_qs(query).get('type',[])] |
| 905 if datatypes: | 924 if datatypes: |
| 906 self.account.TriggerMigration(datatypes) | 925 self.account.TriggerMigration(datatypes) |
| 907 response = 'Migrated datatypes %s' % ( | 926 response = 'Migrated datatypes %s' % ( |
| 908 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) | 927 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) |
| 909 else: | 928 else: |
| 910 response = 'Please specify one or more <i>type=name</i> parameters' | 929 response = 'Please specify one or more <i>type=name</i> parameters' |
| 911 code = 400 | 930 code = 400 |
| 912 except DataTypeIdNotRecognized, error: | 931 except DataTypeIdNotRecognized, error: |
| 913 response = 'Could not interpret datatype name' | 932 response = 'Could not interpret datatype name' |
| 914 code = 400 | 933 code = 400 |
| 915 finally: | 934 finally: |
| 916 self.account_lock.release() | 935 self.account_lock.release() |
| 917 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % | 936 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % |
| 918 (code, code, response)) | 937 (code, code, response)) |
| 919 | 938 |
| 920 def HandleCreateBirthdayError(self): | 939 def HandleCreateBirthdayError(self): |
| 921 self.account.ResetStoreBirthday() | 940 self.account.ResetStoreBirthday() |
| 922 return ( | 941 return ( |
| 923 200, | 942 200, |
| 924 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') | 943 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') |
| 925 | 944 |
| 945 <<<<<<< HEAD | |
| 946 ======= | |
| 947 def HandleSetTransientError(self): | |
| 948 self.transient_error = True | |
| 949 return ( | |
| 950 200, | |
| 951 '<html><title>Transient error</title><H1>Transient error</H1></html>') | |
| 952 | |
| 953 >>>>>>> fix. | |
|
Raghu Simha
2011/08/05 18:04:52
Merge.
lipalani1
2011/08/05 21:33:57
Done.
| |
| 926 def HandleCommand(self, query, raw_request): | 954 def HandleCommand(self, query, raw_request): |
| 927 """Decode and handle a sync command from a raw input of bytes. | 955 """Decode and handle a sync command from a raw input of bytes. |
| 928 | 956 |
| 929 This is the main entry point for this class. It is safe to call this | 957 This is the main entry point for this class. It is safe to call this |
| 930 method from multiple threads. | 958 method from multiple threads. |
| 931 | 959 |
| 932 Args: | 960 Args: |
| 933 raw_request: An iterable byte sequence to be interpreted as a sync | 961 raw_request: An iterable byte sequence to be interpreted as a sync |
| 934 protocol command. | 962 protocol command. |
| 935 Returns: | 963 Returns: |
| 936 A tuple (response_code, raw_response); the first value is an HTTP | 964 A tuple (response_code, raw_response); the first value is an HTTP |
| 937 result code, while the second value is a string of bytes which is the | 965 result code, while the second value is a string of bytes which is the |
| 938 serialized reply to the command. | 966 serialized reply to the command. |
| 939 """ | 967 """ |
| 940 self.account_lock.acquire() | 968 self.account_lock.acquire() |
| 941 def print_context(direction): | 969 def print_context(direction): |
| 942 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction, | 970 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction, |
| 943 __name__), | 971 __name__), |
| 944 | 972 |
| 945 try: | 973 try: |
| 946 request = sync_pb2.ClientToServerMessage() | 974 request = sync_pb2.ClientToServerMessage() |
| 947 request.MergeFromString(raw_request) | 975 request.MergeFromString(raw_request) |
| 948 contents = request.message_contents | 976 contents = request.message_contents |
| 949 | 977 |
| 950 response = sync_pb2.ClientToServerResponse() | 978 response = sync_pb2.ClientToServerResponse() |
| 951 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS | 979 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS |
| 952 self.CheckStoreBirthday(request) | 980 self.CheckStoreBirthday(request) |
| 953 response.store_birthday = self.account.store_birthday | 981 response.store_birthday = self.account.store_birthday |
| 982 self.CheckTransientError(); | |
| 954 | 983 |
| 955 print_context('->') | 984 print_context('->') |
| 956 | 985 |
| 957 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: | 986 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: |
| 958 print 'Authenticate' | 987 print 'Authenticate' |
| 959 # We accept any authentication token, and support only one account. | 988 # We accept any authentication token, and support only one account. |
| 960 # TODO(nick): Mock out the GAIA authentication as well; hook up here. | 989 # TODO(nick): Mock out the GAIA authentication as well; hook up here. |
| 961 response.authenticate.user.email = 'syncjuser@chromium' | 990 response.authenticate.user.email = 'syncjuser@chromium' |
| 962 response.authenticate.user.display_name = 'Sync J User' | 991 response.authenticate.user.display_name = 'Sync J User' |
| 963 elif contents == sync_pb2.ClientToServerMessage.COMMIT: | 992 elif contents == sync_pb2.ClientToServerMessage.COMMIT: |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 981 response.migrated_data_type_id[:] = [ | 1010 response.migrated_data_type_id[:] = [ |
| 982 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] | 1011 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] |
| 983 return (200, response.SerializeToString()) | 1012 return (200, response.SerializeToString()) |
| 984 except StoreBirthdayError, error: | 1013 except StoreBirthdayError, error: |
| 985 print_context('<-') | 1014 print_context('<-') |
| 986 print 'NOT_MY_BIRTHDAY' | 1015 print 'NOT_MY_BIRTHDAY' |
| 987 response = sync_pb2.ClientToServerResponse() | 1016 response = sync_pb2.ClientToServerResponse() |
| 988 response.store_birthday = self.account.store_birthday | 1017 response.store_birthday = self.account.store_birthday |
| 989 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY | 1018 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY |
| 990 return (200, response.SerializeToString()) | 1019 return (200, response.SerializeToString()) |
| 1020 except TransientError as error: | |
| 1021 print_context('<-') | |
| 1022 print 'TRANSIENT_ERROR' | |
| 1023 response.store_birthday = self.account.store_birthday | |
| 1024 response.error_code = sync_pb2.ClientToServerResponse.TRANSIENT_ERROR | |
| 1025 return (200, response.SerializeToString()) | |
| 991 finally: | 1026 finally: |
| 992 self.account_lock.release() | 1027 self.account_lock.release() |
| 993 | 1028 |
| 994 def HandleCommit(self, commit_message, commit_response): | 1029 def HandleCommit(self, commit_message, commit_response): |
| 995 """Respond to a Commit request by updating the user's account state. | 1030 """Respond to a Commit request by updating the user's account state. |
| 996 | 1031 |
| 997 Commit attempts stop after the first error, returning a CONFLICT result | 1032 Commit attempts stop after the first error, returning a CONFLICT result |
| 998 for any unattempted entries. | 1033 for any unattempted entries. |
| 999 | 1034 |
| 1000 Args: | 1035 Args: |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1055 | 1090 |
| 1056 update_sieve.CheckMigrationState() | 1091 update_sieve.CheckMigrationState() |
| 1057 | 1092 |
| 1058 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) | 1093 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) |
| 1059 | 1094 |
| 1060 update_response.changes_remaining = remaining | 1095 update_response.changes_remaining = remaining |
| 1061 for entry in entries: | 1096 for entry in entries: |
| 1062 reply = update_response.entries.add() | 1097 reply = update_response.entries.add() |
| 1063 reply.CopyFrom(entry) | 1098 reply.CopyFrom(entry) |
| 1064 update_sieve.SaveProgress(new_timestamp, update_response) | 1099 update_sieve.SaveProgress(new_timestamp, update_response) |
| OLD | NEW |