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

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: For review. 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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 Attributes: 92 Attributes:
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
ncarter (slow) 2011/07/29 18:52:17 The style guide requires 2 blank lines between top
lipalani1 2011/08/05 21:33:57 Done.
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 795 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 911
909 def __init__(self): 912 def __init__(self):
910 # The implementation supports exactly one account; its state is here. 913 # The implementation supports exactly one account; its state is here.
911 self.account = SyncDataModel() 914 self.account = SyncDataModel()
912 self.account_lock = threading.Lock() 915 self.account_lock = threading.Lock()
913 # Clients that have talked to us: a map from the full client ID 916 # Clients that have talked to us: a map from the full client ID
914 # to its nickname. 917 # to its nickname.
915 self.clients = {} 918 self.clients = {}
916 self.client_name_generator = ('+' * times + chr(c) 919 self.client_name_generator = ('+' * times + chr(c)
917 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z'))) 920 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z')))
921 self.transient_error = False
918 922
919 def GetShortClientName(self, query): 923 def GetShortClientName(self, query):
920 parsed = cgi.parse_qs(query[query.find('?')+1:]) 924 parsed = cgi.parse_qs(query[query.find('?')+1:])
921 client_id = parsed.get('client_id') 925 client_id = parsed.get('client_id')
922 if not client_id: 926 if not client_id:
923 return '?' 927 return '?'
924 client_id = client_id[0] 928 client_id = client_id[0]
925 if client_id not in self.clients: 929 if client_id not in self.clients:
926 self.clients[client_id] = self.client_name_generator.next() 930 self.clients[client_id] = self.client_name_generator.next()
927 return self.clients[client_id] 931 return self.clients[client_id]
928 932
929 def CheckStoreBirthday(self, request): 933 def CheckStoreBirthday(self, request):
930 """Raises StoreBirthdayError if the request's birthday is a mismatch.""" 934 """Raises StoreBirthdayError if the request's birthday is a mismatch."""
931 if not request.HasField('store_birthday'): 935 if not request.HasField('store_birthday'):
932 return 936 return
933 if self.account.store_birthday != request.store_birthday: 937 if self.account.store_birthday != request.store_birthday:
934 raise StoreBirthdayError 938 raise StoreBirthdayError
935 939
940 def CheckTransientError(self):
941 """Raises Transiet error if |transient_error| variable is set."""
ncarter (slow) 2011/07/29 18:52:17 "Transiet" is a misspelling, you should probably s
lipalani1 2011/08/05 21:33:57 Done.
942 if self.transient_error == True:
ncarter (slow) 2011/07/29 18:52:17 Just say "if self.transient_error:". While this i
lipalani1 2011/08/05 21:33:57 Done.
943 raise TransientError
944
936 def HandleMigrate(self, path): 945 def HandleMigrate(self, path):
937 query = urlparse.urlparse(path)[4] 946 query = urlparse.urlparse(path)[4]
938 code = 200 947 code = 200
939 self.account_lock.acquire() 948 self.account_lock.acquire()
940 try: 949 try:
941 datatypes = [DataTypeStringToSyncTypeLoose(x) 950 datatypes = [DataTypeStringToSyncTypeLoose(x)
942 for x in urlparse.parse_qs(query).get('type',[])] 951 for x in urlparse.parse_qs(query).get('type',[])]
943 if datatypes: 952 if datatypes:
944 self.account.TriggerMigration(datatypes) 953 self.account.TriggerMigration(datatypes)
945 response = 'Migrated datatypes %s' % ( 954 response = 'Migrated datatypes %s' % (
946 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) 955 ' and '.join(SyncTypeToString(x).upper() for x in datatypes))
947 else: 956 else:
948 response = 'Please specify one or more <i>type=name</i> parameters' 957 response = 'Please specify one or more <i>type=name</i> parameters'
949 code = 400 958 code = 400
950 except DataTypeIdNotRecognized, error: 959 except DataTypeIdNotRecognized, error:
951 response = 'Could not interpret datatype name' 960 response = 'Could not interpret datatype name'
952 code = 400 961 code = 400
953 finally: 962 finally:
954 self.account_lock.release() 963 self.account_lock.release()
955 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % 964 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' %
956 (code, code, response)) 965 (code, code, response))
957 966
958 def HandleCreateBirthdayError(self): 967 def HandleCreateBirthdayError(self):
959 self.account.store_birthday = '%0.30f' % random.random() 968 self.account.store_birthday = '%0.30f' % random.random()
960 return ( 969 return (
961 200, 970 200,
962 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') 971 '<html><title>Birthday error</title><H1>Birthday error</H1></html>')
963 972
973 def HandleSetTransientError(self):
974 self.transient_error = True
975 return (
976 200,
977 '<html><title>Transient error</title><H1>Transient error</H1></html>')
978
964 def HandleCommand(self, query, raw_request): 979 def HandleCommand(self, query, raw_request):
965 """Decode and handle a sync command from a raw input of bytes. 980 """Decode and handle a sync command from a raw input of bytes.
966 981
967 This is the main entry point for this class. It is safe to call this 982 This is the main entry point for this class. It is safe to call this
968 method from multiple threads. 983 method from multiple threads.
969 984
970 Args: 985 Args:
971 raw_request: An iterable byte sequence to be interpreted as a sync 986 raw_request: An iterable byte sequence to be interpreted as a sync
972 protocol command. 987 protocol command.
973 Returns: 988 Returns:
974 A tuple (response_code, raw_response); the first value is an HTTP 989 A tuple (response_code, raw_response); the first value is an HTTP
975 result code, while the second value is a string of bytes which is the 990 result code, while the second value is a string of bytes which is the
976 serialized reply to the command. 991 serialized reply to the command.
977 """ 992 """
978 self.account_lock.acquire() 993 self.account_lock.acquire()
979 def print_context(direction): 994 def print_context(direction):
980 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction, 995 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction,
981 __name__), 996 __name__),
982 997
983 try: 998 try:
984 request = sync_pb2.ClientToServerMessage() 999 request = sync_pb2.ClientToServerMessage()
985 request.MergeFromString(raw_request) 1000 request.MergeFromString(raw_request)
986 contents = request.message_contents 1001 contents = request.message_contents
987 1002
988 response = sync_pb2.ClientToServerResponse() 1003 response = sync_pb2.ClientToServerResponse()
989 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS 1004 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS
990 self.CheckStoreBirthday(request) 1005 self.CheckStoreBirthday(request)
991 response.store_birthday = self.account.store_birthday 1006 response.store_birthday = self.account.store_birthday
1007 self.CheckTransientError();
992 1008
993 print_context('->') 1009 print_context('->')
994 1010
995 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: 1011 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE:
996 print 'Authenticate' 1012 print 'Authenticate'
997 # We accept any authentication token, and support only one account. 1013 # We accept any authentication token, and support only one account.
998 # TODO(nick): Mock out the GAIA authentication as well; hook up here. 1014 # TODO(nick): Mock out the GAIA authentication as well; hook up here.
999 response.authenticate.user.email = 'syncjuser@chromium' 1015 response.authenticate.user.email = 'syncjuser@chromium'
1000 response.authenticate.user.display_name = 'Sync J User' 1016 response.authenticate.user.display_name = 'Sync J User'
1001 elif contents == sync_pb2.ClientToServerMessage.COMMIT: 1017 elif contents == sync_pb2.ClientToServerMessage.COMMIT:
(...skipping 17 matching lines...) Expand all
1019 response.migrated_data_type_id[:] = [ 1035 response.migrated_data_type_id[:] = [
1020 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] 1036 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes]
1021 return (200, response.SerializeToString()) 1037 return (200, response.SerializeToString())
1022 except StoreBirthdayError as error: 1038 except StoreBirthdayError as error:
1023 print_context('<-') 1039 print_context('<-')
1024 print 'NOT_MY_BIRTHDAY' 1040 print 'NOT_MY_BIRTHDAY'
1025 response = sync_pb2.ClientToServerResponse() 1041 response = sync_pb2.ClientToServerResponse()
1026 response.store_birthday = self.account.store_birthday 1042 response.store_birthday = self.account.store_birthday
1027 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY 1043 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY
1028 return (200, response.SerializeToString()) 1044 return (200, response.SerializeToString())
1045 except TransientError as error:
1046 print_context('<-')
1047 print 'TRANSIENT_ERROR'
1048 response.store_birthday = self.account.store_birthday
1049 response.error_code = sync_pb2.ClientToServerResponse.TRANSIENT_ERROR
1050 return (200, response.SerializeToString())
1029 finally: 1051 finally:
1030 self.account_lock.release() 1052 self.account_lock.release()
1031 1053
1032 def HandleCommit(self, commit_message, commit_response): 1054 def HandleCommit(self, commit_message, commit_response):
1033 """Respond to a Commit request by updating the user's account state. 1055 """Respond to a Commit request by updating the user's account state.
1034 1056
1035 Commit attempts stop after the first error, returning a CONFLICT result 1057 Commit attempts stop after the first error, returning a CONFLICT result
1036 for any unattempted entries. 1058 for any unattempted entries.
1037 1059
1038 Args: 1060 Args:
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1093 1115
1094 update_sieve.CheckMigrationState() 1116 update_sieve.CheckMigrationState()
1095 1117
1096 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) 1118 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve)
1097 1119
1098 update_response.changes_remaining = remaining 1120 update_response.changes_remaining = remaining
1099 for entry in entries: 1121 for entry in entries:
1100 reply = update_response.entries.add() 1122 reply = update_response.entries.add()
1101 reply.CopyFrom(entry) 1123 reply.CopyFrom(entry)
1102 update_sieve.SaveProgress(new_timestamp, update_response) 1124 update_sieve.SaveProgress(new_timestamp, update_response)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698