From fe231709088a9ad8a3f8d3be586557237f05039c Mon Sep 17 00:00:00 2001 From: minhtrannhat Date: Thu, 7 Dec 2023 17:44:08 -0500 Subject: [PATCH] feat(udp): summary command completed --- Dockerfile.server | 7 +- server_directory/numbers.txt | 150 +++++++++++++++++++++++++++++++++++ src/myftp/client.py | 9 ++- src/myftp/server.py | 72 +++++++++++++++++ 4 files changed, 231 insertions(+), 7 deletions(-) create mode 100644 server_directory/numbers.txt diff --git a/Dockerfile.server b/Dockerfile.server index 3d8b45c..8f499d8 100644 --- a/Dockerfile.server +++ b/Dockerfile.server @@ -9,9 +9,10 @@ WORKDIR /server # Create files with random content in the /server directory RUN mkdir /server_directory -# these files is only 750 bytes + +# These files is only 750 bytes, can fit inside a UDP/TCP packet RUN dd if=/dev/urandom of=/server_directory/file_server.txt bs=1 count=750 RUN dd if=/dev/urandom of=/server_directory/image_server.png bs=1 count=750 -# Start your Python application -#CMD python server.py --port_number 12000 --debug 1 +# generate a file that has random numbers +RUN for _ in $(seq 1 150); do shuf -i 0-1000 -n 1 >> /server_directory/numbers.txt; done diff --git a/server_directory/numbers.txt b/server_directory/numbers.txt new file mode 100644 index 0000000..10fd8b8 --- /dev/null +++ b/server_directory/numbers.txt @@ -0,0 +1,150 @@ +970 +408 +627 +774 +374 +425 +292 +570 +681 +971 +141 +468 +271 +120 +450 +298 +802 +34 +448 +979 +181 +117 +322 +885 +733 +31 +818 +582 +83 +524 +104 +285 +932 +964 +292 +306 +621 +584 +109 +302 +555 +250 +2 +59 +602 +452 +357 +404 +486 +37 +616 +667 +154 +938 +552 +887 +969 +602 +701 +284 +358 +37 +802 +290 +1 +94 +829 +854 +678 +938 +156 +466 +420 +391 +757 +22 +843 +346 +658 +330 +383 +274 +229 +537 +444 +14 +656 +646 +616 +357 +930 +975 +626 +964 +497 +860 +290 +558 +714 +969 +496 +871 +667 +148 +262 +424 +402 +337 +770 +61 +899 +153 +567 +129 +922 +12 +375 +810 +658 +991 +399 +820 +198 +25 +17 +696 +885 +307 +254 +600 +508 +983 +703 +175 +363 +197 +831 +998 +534 +833 +291 +434 +218 +858 +795 +140 +102 +170 +182 +992 diff --git a/src/myftp/client.py b/src/myftp/client.py index 20a751c..96dbf91 100644 --- a/src/myftp/client.py +++ b/src/myftp/client.py @@ -112,6 +112,10 @@ class Client: f"myftp> - {self.protocol} - Summary file {filename} from the server" ) if self.debug else None + first_byte = (summary_request_opcode << 5) + len(filename) + + second_byte_to_n_byte = filename.encode("ascii") + # change command handling elif change_command_pattern.match(command): command_name, old_filename, new_filename = command.split() @@ -125,7 +129,7 @@ class Client: first_byte: int = unknown_request_opcode << 5 # get or put case - if command_name == "get": + if command_name == "get" or command_name == "summary": payload = first_byte.to_bytes(1, "big") + second_byte_to_n_byte # type: ignore elif command_name == "put": @@ -135,9 +139,6 @@ class Client: else first_byte.to_bytes(1, "big") # type: ignore ) - elif command_name == "summary": - pass - elif command == "change": pass diff --git a/src/myftp/server.py b/src/myftp/server.py index b842afe..a66bb99 100644 --- a/src/myftp/server.py +++ b/src/myftp/server.py @@ -124,6 +124,20 @@ class Server: filename = None response_data = None + elif request_type == "summary": + # empty filename error + if filename_length_in_bytes <= 0: + rescode = rescode_fail_dict["file_not_error_rescode"] + else: + ( + rescode, + filename, # "summary.txt" + filename_length_in_bytes, # of the summary file + response_data, # summary.txt file content + ) = self.process_summary_req( + filename_length_in_bytes, req_payload[1:] + ) + elif request_type == "unknown": rescode = rescode_fail_dict["unknown_request_rescode"] filename_length_in_bytes = None @@ -174,6 +188,64 @@ class Server: return request_type, filename_length_in_bytes + def process_summary_req( + self, filename_length: int, req_payload: bytes + ) -> Tuple[int, Optional[str], Optional[int], Optional[bytes]]: + """ + Find the filename mentioned + Calculate the min,max,avg + Put those numbers into a file called summary.txt + """ + filename = req_payload[:filename_length].decode("ascii") + + print( + f"myftp> - {self.protocol} - Summarizing the file named {filename} on the server" + ) + + try: + with open(os.path.join(self.directory_path, filename), "r") as file: + numbers = [int(line.strip()) for line in file if line.strip().isdigit()] + + # Find the largest, smallest, and calculate the average + largest_number = max(numbers) + smallest_number = min(numbers) + average_value = sum(numbers) / len(numbers) if numbers else 0 + + print( + f"myftp> - {self.protocol} - File {filename} summarized successfully. The max is {largest_number}, the min is {smallest_number}, the average is {average_value}" + ) + + with open( + os.path.join(self.directory_path, "summary.txt"), "w" + ) as summary_file: + summary_file.write(f"min: {smallest_number}\n") + summary_file.write(f"max: {largest_number}\n") + summary_file.write(f"avg: {average_value}\n") + + print( + f"myftp> - {self.protocol} - Created file summary.txt summarized successfully. Sending it back to the client" + ) + + with open( + os.path.join(self.directory_path, "summary.txt"), "rb" + ) as summary_file: + binary_content = summary_file.read() + + return ( + rescode_success_dict["correct_summary_request_rescode"], + "summary.txt", + 11, + binary_content, + ) + + except Exception as error: + traceback_info = traceback.format_exc() + + print(f"myftp> - {self.protocol} - {error} happened.") + + print(traceback_info) + return rescode_fail_dict["file_not_error_rescode"], None, None, None + def process_put_req(self, filename_length: int, req_payload: bytes) -> int: """ Reconstruct file put by client