Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(255)

Side by Side Diff: net/tools/testserver/chromiumsync.py

Issue 7477004: Simulate transient error and verify exponential backoff. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixing a typo. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698