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

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: Upload before commit. 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
« no previous file with comments | « chrome/test/live_sync/sync_errors_test.cc ('k') | net/tools/testserver/chromiumsync_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 103
104 class TransientError(Error):
105 """The client would be sent a transient error."""
106
107
104 def GetEntryType(entry): 108 def GetEntryType(entry):
105 """Extract the sync type from a SyncEntry. 109 """Extract the sync type from a SyncEntry.
106 110
107 Args: 111 Args:
108 entry: A SyncEntity protobuf object whose type to determine. 112 entry: A SyncEntity protobuf object whose type to determine.
109 Returns: 113 Returns:
110 An enum value from ALL_TYPES if the entry's type can be determined, or None 114 An enum value from ALL_TYPES if the entry's type can be determined, or None
111 if the type cannot be determined. 115 if the type cannot be determined.
112 Raises: 116 Raises:
113 ProtobufExtensionNotUnique: More than one type was indicated by the entry. 117 ProtobufExtensionNotUnique: More than one type was indicated by the entry.
(...skipping 756 matching lines...) Expand 10 before | Expand all | Expand 10 after
870 874
871 def __init__(self): 875 def __init__(self):
872 # The implementation supports exactly one account; its state is here. 876 # The implementation supports exactly one account; its state is here.
873 self.account = SyncDataModel() 877 self.account = SyncDataModel()
874 self.account_lock = threading.Lock() 878 self.account_lock = threading.Lock()
875 # Clients that have talked to us: a map from the full client ID 879 # Clients that have talked to us: a map from the full client ID
876 # to its nickname. 880 # to its nickname.
877 self.clients = {} 881 self.clients = {}
878 self.client_name_generator = ('+' * times + chr(c) 882 self.client_name_generator = ('+' * times + chr(c)
879 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z'))) 883 for times in xrange(0, sys.maxint) for c in xrange(ord('A'), ord('Z')))
884 self.transient_error = False
880 885
881 def GetShortClientName(self, query): 886 def GetShortClientName(self, query):
882 parsed = cgi.parse_qs(query[query.find('?')+1:]) 887 parsed = cgi.parse_qs(query[query.find('?')+1:])
883 client_id = parsed.get('client_id') 888 client_id = parsed.get('client_id')
884 if not client_id: 889 if not client_id:
885 return '?' 890 return '?'
886 client_id = client_id[0] 891 client_id = client_id[0]
887 if client_id not in self.clients: 892 if client_id not in self.clients:
888 self.clients[client_id] = self.client_name_generator.next() 893 self.clients[client_id] = self.client_name_generator.next()
889 return self.clients[client_id] 894 return self.clients[client_id]
890 895
891 def CheckStoreBirthday(self, request): 896 def CheckStoreBirthday(self, request):
892 """Raises StoreBirthdayError if the request's birthday is a mismatch.""" 897 """Raises StoreBirthdayError if the request's birthday is a mismatch."""
893 if not request.HasField('store_birthday'): 898 if not request.HasField('store_birthday'):
894 return 899 return
895 if self.account.StoreBirthday() != request.store_birthday: 900 if self.account.StoreBirthday() != request.store_birthday:
896 raise StoreBirthdayError 901 raise StoreBirthdayError
897 902
903 def CheckTransientError(self):
904 """Raises TransientError if transient_error variable is set."""
905 if self.transient_error:
906 raise TransientError
907
898 def HandleMigrate(self, path): 908 def HandleMigrate(self, path):
899 query = urlparse.urlparse(path)[4] 909 query = urlparse.urlparse(path)[4]
900 code = 200 910 code = 200
901 self.account_lock.acquire() 911 self.account_lock.acquire()
902 try: 912 try:
903 datatypes = [DataTypeStringToSyncTypeLoose(x) 913 datatypes = [DataTypeStringToSyncTypeLoose(x)
904 for x in urlparse.parse_qs(query).get('type',[])] 914 for x in urlparse.parse_qs(query).get('type',[])]
905 if datatypes: 915 if datatypes:
906 self.account.TriggerMigration(datatypes) 916 self.account.TriggerMigration(datatypes)
907 response = 'Migrated datatypes %s' % ( 917 response = 'Migrated datatypes %s' % (
908 ' and '.join(SyncTypeToString(x).upper() for x in datatypes)) 918 ' and '.join(SyncTypeToString(x).upper() for x in datatypes))
909 else: 919 else:
910 response = 'Please specify one or more <i>type=name</i> parameters' 920 response = 'Please specify one or more <i>type=name</i> parameters'
911 code = 400 921 code = 400
912 except DataTypeIdNotRecognized, error: 922 except DataTypeIdNotRecognized, error:
913 response = 'Could not interpret datatype name' 923 response = 'Could not interpret datatype name'
914 code = 400 924 code = 400
915 finally: 925 finally:
916 self.account_lock.release() 926 self.account_lock.release()
917 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' % 927 return (code, '<html><title>Migration: %d</title><H1>%d %s</H1></html>' %
918 (code, code, response)) 928 (code, code, response))
919 929
920 def HandleCreateBirthdayError(self): 930 def HandleCreateBirthdayError(self):
921 self.account.ResetStoreBirthday() 931 self.account.ResetStoreBirthday()
922 return ( 932 return (
923 200, 933 200,
924 '<html><title>Birthday error</title><H1>Birthday error</H1></html>') 934 '<html><title>Birthday error</title><H1>Birthday error</H1></html>')
925 935
936 def HandleSetTransientError(self):
937 self.transient_error = True
938 return (
939 200,
940 '<html><title>Transient error</title><H1>Transient error</H1></html>')
941
926 def HandleCommand(self, query, raw_request): 942 def HandleCommand(self, query, raw_request):
927 """Decode and handle a sync command from a raw input of bytes. 943 """Decode and handle a sync command from a raw input of bytes.
928 944
929 This is the main entry point for this class. It is safe to call this 945 This is the main entry point for this class. It is safe to call this
930 method from multiple threads. 946 method from multiple threads.
931 947
932 Args: 948 Args:
933 raw_request: An iterable byte sequence to be interpreted as a sync 949 raw_request: An iterable byte sequence to be interpreted as a sync
934 protocol command. 950 protocol command.
935 Returns: 951 Returns:
936 A tuple (response_code, raw_response); the first value is an HTTP 952 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 953 result code, while the second value is a string of bytes which is the
938 serialized reply to the command. 954 serialized reply to the command.
939 """ 955 """
940 self.account_lock.acquire() 956 self.account_lock.acquire()
941 def print_context(direction): 957 def print_context(direction):
942 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction, 958 print '[Client %s %s %s.py]' % (self.GetShortClientName(query), direction,
943 __name__), 959 __name__),
944 960
945 try: 961 try:
946 request = sync_pb2.ClientToServerMessage() 962 request = sync_pb2.ClientToServerMessage()
947 request.MergeFromString(raw_request) 963 request.MergeFromString(raw_request)
948 contents = request.message_contents 964 contents = request.message_contents
949 965
950 response = sync_pb2.ClientToServerResponse() 966 response = sync_pb2.ClientToServerResponse()
951 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS 967 response.error_code = sync_pb2.ClientToServerResponse.SUCCESS
952 self.CheckStoreBirthday(request) 968 self.CheckStoreBirthday(request)
953 response.store_birthday = self.account.store_birthday 969 response.store_birthday = self.account.store_birthday
970 self.CheckTransientError();
954 971
955 print_context('->') 972 print_context('->')
956 973
957 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE: 974 if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE:
958 print 'Authenticate' 975 print 'Authenticate'
959 # We accept any authentication token, and support only one account. 976 # We accept any authentication token, and support only one account.
960 # TODO(nick): Mock out the GAIA authentication as well; hook up here. 977 # TODO(nick): Mock out the GAIA authentication as well; hook up here.
961 response.authenticate.user.email = 'syncjuser@chromium' 978 response.authenticate.user.email = 'syncjuser@chromium'
962 response.authenticate.user.display_name = 'Sync J User' 979 response.authenticate.user.display_name = 'Sync J User'
963 elif contents == sync_pb2.ClientToServerMessage.COMMIT: 980 elif contents == sync_pb2.ClientToServerMessage.COMMIT:
(...skipping 17 matching lines...) Expand all
981 response.migrated_data_type_id[:] = [ 998 response.migrated_data_type_id[:] = [
982 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes] 999 SyncTypeToProtocolDataTypeId(x) for x in error.datatypes]
983 return (200, response.SerializeToString()) 1000 return (200, response.SerializeToString())
984 except StoreBirthdayError, error: 1001 except StoreBirthdayError, error:
985 print_context('<-') 1002 print_context('<-')
986 print 'NOT_MY_BIRTHDAY' 1003 print 'NOT_MY_BIRTHDAY'
987 response = sync_pb2.ClientToServerResponse() 1004 response = sync_pb2.ClientToServerResponse()
988 response.store_birthday = self.account.store_birthday 1005 response.store_birthday = self.account.store_birthday
989 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY 1006 response.error_code = sync_pb2.ClientToServerResponse.NOT_MY_BIRTHDAY
990 return (200, response.SerializeToString()) 1007 return (200, response.SerializeToString())
1008 except TransientError as error:
1009 print_context('<-')
1010 print 'TRANSIENT_ERROR'
1011 response.store_birthday = self.account.store_birthday
1012 response.error_code = sync_pb2.ClientToServerResponse.TRANSIENT_ERROR
1013 return (200, response.SerializeToString())
991 finally: 1014 finally:
992 self.account_lock.release() 1015 self.account_lock.release()
993 1016
994 def HandleCommit(self, commit_message, commit_response): 1017 def HandleCommit(self, commit_message, commit_response):
995 """Respond to a Commit request by updating the user's account state. 1018 """Respond to a Commit request by updating the user's account state.
996 1019
997 Commit attempts stop after the first error, returning a CONFLICT result 1020 Commit attempts stop after the first error, returning a CONFLICT result
998 for any unattempted entries. 1021 for any unattempted entries.
999 1022
1000 Args: 1023 Args:
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1055 1078
1056 update_sieve.CheckMigrationState() 1079 update_sieve.CheckMigrationState()
1057 1080
1058 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) 1081 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve)
1059 1082
1060 update_response.changes_remaining = remaining 1083 update_response.changes_remaining = remaining
1061 for entry in entries: 1084 for entry in entries:
1062 reply = update_response.entries.add() 1085 reply = update_response.entries.add()
1063 reply.CopyFrom(entry) 1086 reply.CopyFrom(entry)
1064 update_sieve.SaveProgress(new_timestamp, update_response) 1087 update_sieve.SaveProgress(new_timestamp, update_response)
OLDNEW
« no previous file with comments | « chrome/test/live_sync/sync_errors_test.cc ('k') | net/tools/testserver/chromiumsync_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698