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 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 | 103 |
104 class TransientError(Error): | 104 class TransientError(Error): |
105 """The client would be sent a transient error.""" | 105 """The client would be sent a transient error.""" |
106 | 106 |
107 | 107 |
| 108 class SyncInducedError(Error): |
| 109 """The client would be sent an error.""" |
| 110 |
| 111 |
108 def GetEntryType(entry): | 112 def GetEntryType(entry): |
109 """Extract the sync type from a SyncEntry. | 113 """Extract the sync type from a SyncEntry. |
110 | 114 |
111 Args: | 115 Args: |
112 entry: A SyncEntity protobuf object whose type to determine. | 116 entry: A SyncEntity protobuf object whose type to determine. |
113 Returns: | 117 Returns: |
114 An enum value from ALL_TYPES if the entry's type can be determined, or None | 118 An enum value from ALL_TYPES if the entry's type can be determined, or None |
115 if the type cannot be determined. | 119 if the type cannot be determined. |
116 Raises: | 120 Raises: |
117 ProtobufExtensionNotUnique: More than one type was indicated by the entry. | 121 ProtobufExtensionNotUnique: More than one type was indicated by the entry. |
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 self._version = 0 | 412 self._version = 0 |
409 | 413 |
410 # The definitive copy of this client's items: a map from ID string to a | 414 # The definitive copy of this client's items: a map from ID string to a |
411 # SyncEntity protocol buffer. | 415 # SyncEntity protocol buffer. |
412 self._entries = {} | 416 self._entries = {} |
413 | 417 |
414 self.ResetStoreBirthday() | 418 self.ResetStoreBirthday() |
415 | 419 |
416 self.migration_history = MigrationHistory() | 420 self.migration_history = MigrationHistory() |
417 | 421 |
| 422 self.induced_error = sync_pb2.ClientToServerResponse.Error() |
| 423 |
418 def _SaveEntry(self, entry): | 424 def _SaveEntry(self, entry): |
419 """Insert or update an entry in the change log, and give it a new version. | 425 """Insert or update an entry in the change log, and give it a new version. |
420 | 426 |
421 The ID fields of this entry are assumed to be valid server IDs. This | 427 The ID fields of this entry are assumed to be valid server IDs. This |
422 entry will be updated with a new version number and sync_timestamp. | 428 entry will be updated with a new version number and sync_timestamp. |
423 | 429 |
424 Args: | 430 Args: |
425 entry: The entry to be added or updated. | 431 entry: The entry to be added or updated. |
426 """ | 432 """ |
427 self._version += 1 | 433 self._version += 1 |
(...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
873 nigori_tag = "google_chrome_nigori" | 879 nigori_tag = "google_chrome_nigori" |
874 nigori_original = self._entries.get(self._ServerTagToId(nigori_tag)) | 880 nigori_original = self._entries.get(self._ServerTagToId(nigori_tag)) |
875 if (nigori_original.specifics.Extensions[nigori_specifics_pb2.nigori]. | 881 if (nigori_original.specifics.Extensions[nigori_specifics_pb2.nigori]. |
876 sync_tabs): | 882 sync_tabs): |
877 return | 883 return |
878 nigori_new = copy.deepcopy(nigori_original) | 884 nigori_new = copy.deepcopy(nigori_original) |
879 nigori_new.specifics.Extensions[nigori_specifics_pb2.nigori].sync_tabs = ( | 885 nigori_new.specifics.Extensions[nigori_specifics_pb2.nigori].sync_tabs = ( |
880 True) | 886 True) |
881 self._SaveEntry(nigori_new) | 887 self._SaveEntry(nigori_new) |
882 | 888 |
| 889 def SetInducedError(self, error): |
| 890 self.induced_error = error |
| 891 |
| 892 def GetInducedError(self): |
| 893 return self.induced_error |
| 894 |
883 | 895 |
884 class TestServer(object): | 896 class TestServer(object): |
885 """An object to handle requests for one (and only one) Chrome Sync account. | 897 """An object to handle requests for one (and only one) Chrome Sync account. |
886 | 898 |
887 TestServer consumes the sync command messages that are the outermost | 899 TestServer consumes the sync command messages that are the outermost |
888 layers of the protocol, performs the corresponding actions on its | 900 layers of the protocol, performs the corresponding actions on its |
889 SyncDataModel, and constructs an appropropriate response message. | 901 SyncDataModel, and constructs an appropropriate response message. |
890 """ | 902 """ |
891 | 903 |
892 def __init__(self): | 904 def __init__(self): |
(...skipping 22 matching lines...) Expand all Loading... |
915 if not request.HasField('store_birthday'): | 927 if not request.HasField('store_birthday'): |
916 return | 928 return |
917 if self.account.StoreBirthday() != request.store_birthday: | 929 if self.account.StoreBirthday() != request.store_birthday: |
918 raise StoreBirthdayError | 930 raise StoreBirthdayError |
919 | 931 |
920 def CheckTransientError(self): | 932 def CheckTransientError(self): |
921 """Raises TransientError if transient_error variable is set.""" | 933 """Raises TransientError if transient_error variable is set.""" |
922 if self.transient_error: | 934 if self.transient_error: |
923 raise TransientError | 935 raise TransientError |
924 | 936 |
| 937 def CheckSendError(self): |
| 938 """Raises SyncInducedError if needed.""" |
| 939 if (self.account.induced_error.error_type != |
| 940 sync_pb2.ClientToServerResponse.UNKNOWN): |
| 941 raise SyncInducedError |
| 942 |
925 def HandleMigrate(self, path): | 943 def HandleMigrate(self, path): |
926 query = urlparse.urlparse(path)[4] | 944 query = urlparse.urlparse(path)[4] |
927 code = 200 | 945 code = 200 |
928 self.account_lock.acquire() | 946 self.account_lock.acquire() |
929 try: | 947 try: |
930 datatypes = [DataTypeStringToSyncTypeLoose(x) | 948 datatypes = [DataTypeStringToSyncTypeLoose(x) |
931 for x in urlparse.parse_qs(query).get('type',[])] | 949 for x in urlparse.parse_qs(query).get('type',[])] |
932 if datatypes: | 950 if datatypes: |
933 self.account.TriggerMigration(datatypes) | 951 self.account.TriggerMigration(datatypes) |
934 response = 'Migrated datatypes %s' % ( | 952 response = 'Migrated datatypes %s' % ( |
935 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) | 953 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) |
936 else: | 954 else: |
937 response = 'Please specify one or more <i>type=name</i> parameters' | 955 response = 'Please specify one or more <i>type=name</i> parameters' |
938 code = 400 | 956 code = 400 |
939 except DataTypeIdNotRecognized, error: | 957 except DataTypeIdNotRecognized, error: |
940 response = 'Could not interpret datatype name' | 958 response = 'Could not interpret datatype name' |
941 code = 400 | 959 code = 400 |
942 finally: | 960 finally: |
943 self.account_lock.release() | 961 self.account_lock.release() |
944 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % | 962 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % |
945 (code, code, response)) | 963 (code, code, response)) |
946 | 964 |
| 965 def HandleSetInducedError(self, path): |
| 966 query = urlparse.urlparse(path)[4] |
| 967 self.account_lock.acquire() |
| 968 code = 200; |
| 969 response = 'Success' |
| 970 error = sync_pb2.ClientToServerResponse.Error() |
| 971 try: |
| 972 error_type = urlparse.parse_qs(query)['error'] |
| 973 action = urlparse.parse_qs(query)['action'] |
| 974 error.error_type = int(error_type[0]) |
| 975 error.action = int(action[0]) |
| 976 try: |
| 977 error.url = (urlparse.parse_qs(query)['url'])[0] |
| 978 except KeyError: |
| 979 error.url = '' |
| 980 try: |
| 981 error.error_description =( |
| 982 (urlparse.parse_qs(query)['error_description'])[0]) |
| 983 except KeyError: |
| 984 error.error_description = '' |
| 985 self.account.SetInducedError(error) |
| 986 response = ('Error = %d, action = %d, url = %s, description = %s' % |
| 987 (error.error_type, error.action, |
| 988 error.url, |
| 989 error.error_description)) |
| 990 except error: |
| 991 response = 'Could not parse url' |
| 992 code = 400 |
| 993 finally: |
| 994 self.account_lock.release() |
| 995 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % |
| 996 (code, code, response)) |
| 997 |
947 def HandleCreateBirthdayError(self): | 998 def HandleCreateBirthdayError(self): |
948 self.account.ResetStoreBirthday() | 999 self.account.ResetStoreBirthday() |
949 return ( | 1000 return ( |
950 200, | 1001 200, |
951 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') | 1002 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') |
952 | 1003 |
953 def HandleSetTransientError(self): | 1004 def HandleSetTransientError(self): |
954 self.transient_error = True | 1005 self.transient_error = True |
955 return ( | 1006 return ( |
956 200, | 1007 200, |
(...skipping 28 matching lines...) Expand all Loading... |
985 try: | 1036 try: |
986 request = sync_pb2.ClientToServerMessage() | 1037 request = sync_pb2.ClientToServerMessage() |
987 request.MergeFromString(raw_request) | 1038 request.MergeFromString(raw_request) |
988 contents = request.message_contents | 1039 contents = request.message_contents |
989 | 1040 |
990 response = sync_pb2.ClientToServerResponse() | 1041 response = sync_pb2.ClientToServerResponse() |
991 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS | 1042 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS |
992 self.CheckStoreBirthday(request) | 1043 self.CheckStoreBirthday(request) |
993 response.store_birthday = self.account.store_birthday | 1044 response.store_birthday = self.account.store_birthday |
994 self.CheckTransientError(); | 1045 self.CheckTransientError(); |
| 1046 self.CheckSendError(); |
995 | 1047 |
996 print_context('->') | 1048 print_context('->') |
997 | 1049 |
998 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: | 1050 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: |
999 print 'Authenticate' | 1051 print 'Authenticate' |
1000 # We accept any authentication token, and support only one account. | 1052 # We accept any authentication token, and support only one account. |
1001 # TODO(nick): Mock out the GAIA authentication as well; hook up here. | 1053 # TODO(nick): Mock out the GAIA authentication as well; hook up here. |
1002 response.authenticate.user.email = 'syncjuser@chromium' | 1054 response.authenticate.user.email = 'syncjuser@chromium' |
1003 response.authenticate.user.display_name = 'Sync J User' | 1055 response.authenticate.user.display_name = 'Sync J User' |
1004 elif contents == sync_pb2.ClientToServerMessage.COMMIT: | 1056 elif contents == sync_pb2.ClientToServerMessage.COMMIT: |
(...skipping 18 matching lines...) Expand all Loading... |
1023 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] | 1075 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] |
1024 return (200, response.SerializeToString()) | 1076 return (200, response.SerializeToString()) |
1025 except StoreBirthdayError, error: | 1077 except StoreBirthdayError, error: |
1026 print_context('<-') | 1078 print_context('<-') |
1027 print 'NOT_MY_BIRTHDAY' | 1079 print 'NOT_MY_BIRTHDAY' |
1028 response = sync_pb2.ClientToServerResponse() | 1080 response = sync_pb2.ClientToServerResponse() |
1029 response.store_birthday = self.account.store_birthday | 1081 response.store_birthday = self.account.store_birthday |
1030 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY | 1082 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY |
1031 return (200, response.SerializeToString()) | 1083 return (200, response.SerializeToString()) |
1032 except TransientError, error: | 1084 except TransientError, error: |
| 1085 ### This is deprecated now. Would be removed once test cases are removed. |
1033 print_context('<-') | 1086 print_context('<-') |
1034 print 'TRANSIENT_ERROR' | 1087 print 'TRANSIENT_ERROR' |
1035 response.store_birthday = self.account.store_birthday | 1088 response.store_birthday = self.account.store_birthday |
1036 response.error_code = sync_pb2.ClientToServerResponse.TRANSIENT_ERROR | 1089 response.error_code = sync_pb2.ClientToServerResponse.TRANSIENT_ERROR |
1037 return (200, response.SerializeToString()) | 1090 return (200, response.SerializeToString()) |
| 1091 except SyncInducedError, error: |
| 1092 print_context('<-') |
| 1093 print 'INDUCED_ERROR' |
| 1094 response.store_birthday = self.account.store_birthday |
| 1095 error = self.account.GetInducedError() |
| 1096 response.error.error_type = error.error_type |
| 1097 response.error.url = error.url |
| 1098 response.error.error_description = error.error_description |
| 1099 response.error.action = error.action |
| 1100 return (200, response.SerializeToString()) |
1038 finally: | 1101 finally: |
1039 self.account_lock.release() | 1102 self.account_lock.release() |
1040 | 1103 |
1041 def HandleCommit(self, commit_message, commit_response): | 1104 def HandleCommit(self, commit_message, commit_response): |
1042 """Respond to a Commit request by updating the user's account state. | 1105 """Respond to a Commit request by updating the user's account state. |
1043 | 1106 |
1044 Commit attempts stop after the first error, returning a CONFLICT result | 1107 Commit attempts stop after the first error, returning a CONFLICT result |
1045 for any unattempted entries. | 1108 for any unattempted entries. |
1046 | 1109 |
1047 Args: | 1110 Args: |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1102 | 1165 |
1103 update_sieve.CheckMigrationState() | 1166 update_sieve.CheckMigrationState() |
1104 | 1167 |
1105 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) | 1168 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) |
1106 | 1169 |
1107 update_response.changes_remaining = remaining | 1170 update_response.changes_remaining = remaining |
1108 for entry in entries: | 1171 for entry in entries: |
1109 reply = update_response.entries.add() | 1172 reply = update_response.entries.add() |
1110 reply.CopyFrom(entry) | 1173 reply.CopyFrom(entry) |
1111 update_sieve.SaveProgress(new_timestamp, update_response) | 1174 update_sieve.SaveProgress(new_timestamp, update_response) |
OLD | NEW |