diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/tools/testserver/echo_message.py | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/net/tools/testserver/echo_message.py')
-rw-r--r-- | chromium/net/tools/testserver/echo_message.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/chromium/net/tools/testserver/echo_message.py b/chromium/net/tools/testserver/echo_message.py new file mode 100644 index 00000000000..b2f7b04e8a1 --- /dev/null +++ b/chromium/net/tools/testserver/echo_message.py @@ -0,0 +1,385 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Provides utility functions for TCP/UDP echo servers and clients. + +This program has classes and functions to encode, decode, calculate checksum +and verify the "echo request" and "echo response" messages. "echo request" +message is an echo message sent from the client to the server. "echo response" +message is a response from the server to the "echo request" message from the +client. + +The format of "echo request" message is +<version><checksum><payload_size><payload>. <version> is the version number +of the "echo request" protocol. <checksum> is the checksum of the <payload>. +<payload_size> is the size of the <payload>. <payload> is the echo message. + +The format of "echo response" message is +<version><checksum><payload_size><key><encoded_payload>.<version>, +<checksum> and <payload_size> are same as what is in the "echo request" message. +<encoded_payload> is encoded version of the <payload>. <key> is a randomly +generated key that is used to encode/decode the <payload>. +""" + +__author__ = 'rtenneti@google.com (Raman Tenneti)' + + +from itertools import cycle +from itertools import izip +import random + + +class EchoHeader(object): + """Class to keep header info of the EchoRequest and EchoResponse messages. + + This class knows how to parse the checksum, payload_size from the + "echo request" and "echo response" messages. It holds the checksum, + payload_size of the "echo request" and "echo response" messages. + """ + + # This specifies the version. + VERSION_STRING = '01' + + # This specifies the starting position of the checksum and length of the + # checksum. Maximum value for the checksum is less than (2 ** 31 - 1). + CHECKSUM_START = 2 + CHECKSUM_LENGTH = 10 + CHECKSUM_FORMAT = '%010d' + CHECKSUM_END = CHECKSUM_START + CHECKSUM_LENGTH + + # This specifies the starting position of the <payload_size> and length of the + # <payload_size>. Maximum number of bytes that can be sent in the <payload> is + # 9,999,999. + PAYLOAD_SIZE_START = CHECKSUM_END + PAYLOAD_SIZE_LENGTH = 7 + PAYLOAD_SIZE_FORMAT = '%07d' + PAYLOAD_SIZE_END = PAYLOAD_SIZE_START + PAYLOAD_SIZE_LENGTH + + def __init__(self, checksum=0, payload_size=0): + """Initializes the checksum and payload_size of self (EchoHeader). + + Args: + checksum: (int) + The checksum of the payload. + payload_size: (int) + The size of the payload. + """ + self.checksum = checksum + self.payload_size = payload_size + + def ParseAndInitialize(self, echo_message): + """Parses the echo_message and initializes self with the parsed data. + + This method extracts checksum, and payload_size from the echo_message + (echo_message could be either echo_request or echo_response messages) and + initializes self (EchoHeader) with checksum and payload_size. + + Args: + echo_message: (string) + The string representation of EchoRequest or EchoResponse objects. + Raises: + ValueError: Invalid data + """ + if not echo_message or len(echo_message) < EchoHeader.PAYLOAD_SIZE_END: + raise ValueError('Invalid data:%s' % echo_message) + self.checksum = int(echo_message[ + EchoHeader.CHECKSUM_START:EchoHeader.CHECKSUM_END]) + self.payload_size = int(echo_message[ + EchoHeader.PAYLOAD_SIZE_START:EchoHeader.PAYLOAD_SIZE_END]) + + def InitializeFromPayload(self, payload): + """Initializes the EchoHeader object with the payload. + + It calculates checksum for the payload and initializes self (EchoHeader) + with the calculated checksum and size of the payload. + + This method is used by the client code during testing. + + Args: + payload: (string) + The payload is the echo string (like 'hello'). + Raises: + ValueError: Invalid data + """ + if not payload: + raise ValueError('Invalid data:%s' % payload) + self.payload_size = len(payload) + self.checksum = Checksum(payload, self.payload_size) + + def __str__(self): + """String representation of the self (EchoHeader). + + Returns: + A string representation of self (EchoHeader). + """ + checksum_string = EchoHeader.CHECKSUM_FORMAT % self.checksum + payload_size_string = EchoHeader.PAYLOAD_SIZE_FORMAT % self.payload_size + return EchoHeader.VERSION_STRING + checksum_string + payload_size_string + + +class EchoRequest(EchoHeader): + """Class holds data specific to the "echo request" message. + + This class holds the payload extracted from the "echo request" message. + """ + + # This specifies the starting position of the <payload>. + PAYLOAD_START = EchoHeader.PAYLOAD_SIZE_END + + def __init__(self): + """Initializes EchoRequest object.""" + EchoHeader.__init__(self) + self.payload = '' + + def ParseAndInitialize(self, echo_request_data): + """Parses and Initializes the EchoRequest object from the echo_request_data. + + This method extracts the header information (checksum and payload_size) and + payload from echo_request_data. + + Args: + echo_request_data: (string) + The string representation of EchoRequest object. + Raises: + ValueError: Invalid data + """ + EchoHeader.ParseAndInitialize(self, echo_request_data) + if len(echo_request_data) <= EchoRequest.PAYLOAD_START: + raise ValueError('Invalid data:%s' % echo_request_data) + self.payload = echo_request_data[EchoRequest.PAYLOAD_START:] + + def InitializeFromPayload(self, payload): + """Initializes the EchoRequest object with payload. + + It calculates checksum for the payload and initializes self (EchoRequest) + object. + + Args: + payload: (string) + The payload string for which "echo request" needs to be constructed. + """ + EchoHeader.InitializeFromPayload(self, payload) + self.payload = payload + + def __str__(self): + """String representation of the self (EchoRequest). + + Returns: + A string representation of self (EchoRequest). + """ + return EchoHeader.__str__(self) + self.payload + + +class EchoResponse(EchoHeader): + """Class holds data specific to the "echo response" message. + + This class knows how to parse the "echo response" message. This class holds + key, encoded_payload and decoded_payload of the "echo response" message. + """ + + # This specifies the starting position of the |key_| and length of the |key_|. + # Minimum and maximum values for the |key_| are 100,000 and 999,999. + KEY_START = EchoHeader.PAYLOAD_SIZE_END + KEY_LENGTH = 6 + KEY_FORMAT = '%06d' + KEY_END = KEY_START + KEY_LENGTH + KEY_MIN_VALUE = 0 + KEY_MAX_VALUE = 999999 + + # This specifies the starting position of the <encoded_payload> and length + # of the <encoded_payload>. + ENCODED_PAYLOAD_START = KEY_END + + def __init__(self, key='', encoded_payload='', decoded_payload=''): + """Initializes the EchoResponse object.""" + EchoHeader.__init__(self) + self.key = key + self.encoded_payload = encoded_payload + self.decoded_payload = decoded_payload + + def ParseAndInitialize(self, echo_response_data=None): + """Parses and Initializes the EchoResponse object from echo_response_data. + + This method calls EchoHeader to extract header information from the + echo_response_data and it then extracts key and encoded_payload from the + echo_response_data. It holds the decoded payload of the encoded_payload. + + Args: + echo_response_data: (string) + The string representation of EchoResponse object. + Raises: + ValueError: Invalid echo_request_data + """ + EchoHeader.ParseAndInitialize(self, echo_response_data) + if len(echo_response_data) <= EchoResponse.ENCODED_PAYLOAD_START: + raise ValueError('Invalid echo_response_data:%s' % echo_response_data) + self.key = echo_response_data[EchoResponse.KEY_START:EchoResponse.KEY_END] + self.encoded_payload = echo_response_data[ + EchoResponse.ENCODED_PAYLOAD_START:] + self.decoded_payload = Crypt(self.encoded_payload, self.key) + + def InitializeFromEchoRequest(self, echo_request): + """Initializes EchoResponse with the data from the echo_request object. + + It gets the checksum, payload_size and payload from the echo_request object + and then encodes the payload with a random key. It also saves the payload + as decoded_payload. + + Args: + echo_request: (EchoRequest) + The EchoRequest object which has "echo request" message. + """ + self.checksum = echo_request.checksum + self.payload_size = echo_request.payload_size + self.key = (EchoResponse.KEY_FORMAT % + random.randrange(EchoResponse.KEY_MIN_VALUE, + EchoResponse.KEY_MAX_VALUE)) + self.encoded_payload = Crypt(echo_request.payload, self.key) + self.decoded_payload = echo_request.payload + + def __str__(self): + """String representation of the self (EchoResponse). + + Returns: + A string representation of self (EchoResponse). + """ + return EchoHeader.__str__(self) + self.key + self.encoded_payload + + +def Crypt(payload, key): + """Encodes/decodes the payload with the key and returns encoded payload. + + This method loops through the payload and XORs each byte with the key. + + Args: + payload: (string) + The string to be encoded/decoded. + key: (string) + The key used to encode/decode the payload. + + Returns: + An encoded/decoded string. + """ + return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in izip(payload, cycle(key))) + + +def Checksum(payload, payload_size): + """Calculates the checksum of the payload. + + Args: + payload: (string) + The payload string for which checksum needs to be calculated. + payload_size: (int) + The number of bytes in the payload. + + Returns: + The checksum of the payload. + """ + checksum = 0 + length = min(payload_size, len(payload)) + for i in range (0, length): + checksum += ord(payload[i]) + return checksum + + +def GetEchoRequestData(payload): + """Constructs an "echo request" message from the payload. + + It builds an EchoRequest object from the payload and then returns a string + representation of the EchoRequest object. + + This is used by the TCP/UDP echo clients to build the "echo request" message. + + Args: + payload: (string) + The payload string for which "echo request" needs to be constructed. + + Returns: + A string representation of the EchoRequest object. + Raises: + ValueError: Invalid payload + """ + try: + echo_request = EchoRequest() + echo_request.InitializeFromPayload(payload) + return str(echo_request) + except (IndexError, ValueError): + raise ValueError('Invalid payload:%s' % payload) + + +def GetEchoResponseData(echo_request_data): + """Verifies the echo_request_data and returns "echo response" message. + + It builds the EchoRequest object from the echo_request_data and then verifies + the checksum of the EchoRequest is same as the calculated checksum of the + payload. If the checksums don't match then it returns None. It checksums + match, it builds the echo_response object from echo_request object and returns + string representation of the EchoResponse object. + + This is used by the TCP/UDP echo servers. + + Args: + echo_request_data: (string) + The string that echo servers send to the clients. + + Returns: + A string representation of the EchoResponse object. It returns None if the + echo_request_data is not valid. + Raises: + ValueError: Invalid echo_request_data + """ + try: + if not echo_request_data: + raise ValueError('Invalid payload:%s' % echo_request_data) + + echo_request = EchoRequest() + echo_request.ParseAndInitialize(echo_request_data) + + if Checksum(echo_request.payload, + echo_request.payload_size) != echo_request.checksum: + return None + + echo_response = EchoResponse() + echo_response.InitializeFromEchoRequest(echo_request) + + return str(echo_response) + except (IndexError, ValueError): + raise ValueError('Invalid payload:%s' % echo_request_data) + + +def DecodeAndVerify(echo_request_data, echo_response_data): + """Decodes and verifies the echo_response_data. + + It builds EchoRequest and EchoResponse objects from the echo_request_data and + echo_response_data. It returns True if the EchoResponse's payload and + checksum match EchoRequest's. + + This is used by the TCP/UDP echo clients for testing purposes. + + Args: + echo_request_data: (string) + The request clients sent to echo servers. + echo_response_data: (string) + The response clients received from the echo servers. + + Returns: + True if echo_request_data and echo_response_data match. + Raises: + ValueError: Invalid echo_request_data or Invalid echo_response + """ + + try: + echo_request = EchoRequest() + echo_request.ParseAndInitialize(echo_request_data) + except (IndexError, ValueError): + raise ValueError('Invalid echo_request:%s' % echo_request_data) + + try: + echo_response = EchoResponse() + echo_response.ParseAndInitialize(echo_response_data) + except (IndexError, ValueError): + raise ValueError('Invalid echo_response:%s' % echo_response_data) + + return (echo_request.checksum == echo_response.checksum and + echo_request.payload == echo_response.decoded_payload) |