def reset_stats_req(): ''' reset all service statistics to default values ''' # acquire lock, reset stats with bytes_sent_lock: global bytes_sent bytes_sent = 0 with bytes_recvd_lock: global bytes_recvd bytes_recvd = 0 with compression_lock: global payload_bytes payload_bytes = 0 global compressed_bytes compressed_bytes = 0 print("bytes sent %d & bytes recieved %d" % (bytes_sent, bytes_recvd)) print("payload bytes %d & compressed bytes %d" % (payload_bytes, compressed_bytes)) # create appropriate response header -- status OK header = Header(0, 0) headerB = header.serialize() return headerB
def test_bad_magic_val(client): ''' send a ping request with an incorrect magic value should result in error 33 ''' bad_magic_val = 0x53545250 payload_len = 0 req_code = 1 # serialize header data magic_valueB = bad_magic_val.to_bytes(4, byteorder='big', signed=True) payload_lenB = payload_len.to_bytes(2, byteorder='big', signed=True) req_codeB = req_code.to_bytes(2, byteorder='big', signed=True) # manual creation of serialized header header = magic_valueB + payload_lenB + req_codeB client.sendall(header) data = client.recv(1024) # create empty header, deserialize data into it response_header = Header(0, 0) response_header.deserialize(data) got = (response_header.payload_len, response_header.code) expected = (0, 33) # expecting status code 33 -- bad magic value error assert got == expected
def ping_req(): ''' check service is available and operating normally ''' print("PING!") # create appropriate response header -- status OK header = Header(0, 0) headerB = header.serialize() return headerB
def error(error_code): ''' prints error msg & returns response header with appropriate status code ''' # print error msg for logging purposes print("ERROR: " + error_codes.get(error_code)) header = Header(0, error_code) headerB = header.serialize() return headerB
def test_get_stats(client): ''' test get stats request by reseting stats and calling ping 3 times to be able predict output ''' # reset stats (without compression ratio)-- they will now be 8, 8, 0 test_reset(client) i = 0 while i < 3: test_ping(client) i += 1 stats_header = Header(0, 2) client.sendall(stats_header.serialize()) # server response to get stats request stats_data = client.recv(1024) # create empty header, deserialize data into it stats_response_header = Header(0, 0) stats_response_header.deserialize(stats_data) expected = (9, 0) # get stat's payload should be 9 bytes got = (stats_response_header.payload_len, stats_response_header.code) assert got == expected ping_bytes = 3 * 8 # Only one req/rsp of header size since reset. expected = (8 + (8 + 9) + ping_bytes, 8 + 8 + ping_bytes, 0) got = deserialize_stats(stats_data[8:]) assert got == expected
def service_request(data): ''' service a request by putting data received into proper format and calling correct request returns response to request based on request type also keep track of some statistics ''' # stats -- keep track of bytes received with api.bytes_recvd_lock: api.bytes_recvd += len(data) # initialize header object with empty values header = Header(0, 0) # deserialize data received status_code = header.deserialize(data) # problems w/ header if status_code == 33 or status_code == 34: response = errors.error(status_code) # no problems w/ header else: if header.code == 1: # ping request response = api.ping_req() elif header.code == 2: # get stats request response = api.get_stats_req() elif header.code == 3: # reset stats request response = api.reset_stats_req() elif header.code == 4: # compress request payload = data[PAYLOAD_OFFSET:] response = api.compress_req(payload) else: # unsupported request type response = errors.error(3) # stats -- keep track of bytes sent with api.bytes_sent_lock: api.bytes_sent += len(response) return response
def compress_req(payload): print(f"payload is {payload} && type {type(payload)}") ''' return a compressed version of payload using a simple prefix compression scheme only accepts lowercase ascii strings of a maximum length of MAX_PAYLOAD ''' if len(payload) > MAX_PAYLOAD: return errors.error(2) # deserialize payload try: payload = payload.decode('ascii') except UnicodeDecodeError: print('Error: encountered non ascii characters') return errors.error(35) # returns compressed version of payload compressed = compress(payload) print('compressed VALUE is', compressed) payload_len = len(compressed) # compressedB = compressed.encode('ascii') compressedB = bytes(compressed) print(f"type is {type(compressedB)}") # update metrics to be able to calculate compression ratio with compression_lock: global payload_bytes payload_bytes += len(payload) global compressed_bytes compressed_bytes += payload_len # create appropriate response header -- status OK header = Header(payload_len, 0) headerB = header.serialize() print(f"type of headerB {type(headerB)}") return headerB + compressedB
def get_response(s, code): # Receive response data = s.recv(9216) print("this is the response recieved from server: ", data) # log? # create header & init w/ empty values header = Header(0, 0) header.deserialize(data) # print error msgs if header.code in errors.error_codes: print('ERROR: ', errors.error_codes.get(header.code)) else: print('payload length = ', header.payload_len) print('status code = ', header.code) # payload is only present for get stats or compress requests if header.code == 2 or header.code == 4: payload = data[8:] deserialize_payload(payload, code)
def get_stats_req(): ''' returns total bytes sent, total bytes received, and compression ratio from service uptime or last reset ''' # serialize stats with bytes_sent_lock: sentB = bytes_sent.to_bytes(4, byteorder='big') with bytes_recvd_lock: recvdB = bytes_recvd.to_bytes(4, byteorder='big') # calculate & serialize compression ratio with compression_lock: if payload_bytes != 0: compression_ratio = int((compressed_bytes / payload_bytes) * 100) else: # avoid division by 0 compression_ratio = 0 compression_ratioB = compression_ratio.to_bytes( 1, byteorder='big') # should this be inside lock? # print for logging purposes --> log print("bytes sent %d and bytes recieved %d" % (bytes_sent, bytes_recvd)) print('payload bytes %d and compressed bytes %d' % (payload_bytes, compressed_bytes)) print('compression ratio is', compression_ratio) payload = sentB + recvdB + compression_ratioB payload_len = len(payload) # create appropriate response header -- status OK header = Header(payload_len, 0) headerB = header.serialize() return headerB + payload
def test_compress(client): string = b'go go go go gophers gopher gophers' payload_len = len(string) compress_header = Header(payload_len, 4) client.sendall(compress_header.serialize() + string) compress_resp = client.recv(1024) # create empty header, deserialize data into it compress_response_header = Header(0, 0) compress_response_header.deserialize(compress_resp) compressed = compress_resp[8:] expected = b'-\xf6r\xd0\xb9\xd9K\x95\xc2@\x0c\x02t\xe9\xd3\xa6\xcb\xca\xe9\xb2\xf3\xa6\xcb\xca' assert compressed == expected
def test_unexpected_payload_len(client): ''' testing error 34 where payload length specified in header does not match payload length receive ''' ping_header = Header(5, 1) client.sendall(ping_header.serialize()) data = client.recv(1024) # create empty header, deserialize data into it response_header = Header(0, 0) response_header.deserialize(data) got = (response_header.payload_len, response_header.code) expected = (0, 34) assert got == expected
def test_ping(client): ''' testing ping request function should receive response header with status 0 -- OK ''' ping_header = Header(0, 1) client.sendall(ping_header.serialize()) data = client.recv(1024) # create empty header, deserialize data into it response_header = Header(0, 0) response_header.deserialize(data) got = (response_header.payload_len, response_header.code) expected = (0, 0) assert got == expected
def test_unsupported_req(client): ''' create a header with an unsupported request code/type should result in error 3 ''' header = Header(0, 5) client.sendall(header.serialize()) data = client.recv(1024) # create empty header, deserialize data into it response_header = Header(0, 0) response_header.deserialize(data) got = (response_header.payload_len, response_header.code) expected = (0, 3) # expecting status code 3 -- unsupported request error assert got == expected
def send_request(socket, code): # compression request requires payload if code == 4: payload = input('enter a string to compress: ') payloadB = payload.encode('ascii') payload_len = len(payloadB) # create request header, serialize it, and send it header = Header(payload_len, code) socket.sendall(header.serialize() + payloadB) else: header = Header(0, code) socket.sendall(header.serialize())
def test_compress_req_max(client): ''' give compress request a string with length more than max payload should result in error 2 -- message too large ''' # Test Max payload max_payload = b'a' * 8193 compress_header = Header(8193, 4) client.sendall(compress_header.serialize() + max_payload) compress_data = client.recv(1024) # create empty header, deserialize data into it compress_response_header = Header(0, 0) compress_response_header.deserialize(compress_data) expected = (0, 2) got = (compress_response_header.payload_len, compress_response_header.code) assert got == expected
def test_reset(client): ''' test reset stats request by calling reset stats and check reset stats result by calling get stats expected value is 8, 8, 0 because we also call get stats ''' # testing reset_stas reset_header = Header(0, 3) client.sendall(reset_header.serialize()) reset_data = client.recv(1024) # server response to rest stats request # create empty header, deserialize data into it -- bytes_recvd += 8 reset_response_header = Header(0, 0) reset_response_header.deserialize(reset_data) got = (reset_response_header.payload_len, reset_response_header.code) expected = (0, 0) assert got == expected # checking that server returned OK # call get stats request to check reset stats actually works -- bytes_sent += 8 stats_header = Header(0, 2) client.sendall(stats_header.serialize()) # server response to get stats request stats_data = client.recv(1024) # create empty header, deserialize data into it stats_response_header = Header(0, 0) stats_response_header.deserialize(stats_data) expected = (9, 0) # get stat's payload should be 9 bytes got = (stats_response_header.payload_len, stats_response_header.code) assert got == expected expected = (8, 8, 0) # Only one req/rsp of header size since reset. got = deserialize_stats(stats_data[8:]) assert got == expected