feat(udp): put command completed

This commit is contained in:
minhtrannhat 2023-12-07 00:38:33 -05:00
parent ae1d34da2a
commit 73b4e06d02
Signed by: minhtrannhat
GPG Key ID: E13CFA85C53F8062
2 changed files with 111 additions and 15 deletions

View File

@ -4,7 +4,7 @@
from socket import socket, AF_INET, SOCK_DGRAM from socket import socket, AF_INET, SOCK_DGRAM
from typing import Pattern, Tuple from typing import Pattern, Tuple, Optional
from argparse import ArgumentParser from argparse import ArgumentParser
import traceback import traceback
import os import os
@ -30,7 +30,7 @@ unknown_request_opcode: int = 0b101
rescode_dict: dict[int, str] = { rescode_dict: dict[int, str] = {
0b011: "File Not Found Error", 0b011: "File Not Found Error",
0b100: "Unknown Request", 0b100: "Unknown Request",
0b101: "Change Unsuccessful Error", 0b101: "Change/Put Unsuccessful Error",
0b000: "Put/Change Request Successful", 0b000: "Put/Change Request Successful",
0b001: "Get Request Successful", 0b001: "Get Request Successful",
0b010: "Summary Request Successful", 0b010: "Summary Request Successful",
@ -87,7 +87,7 @@ class Client:
first_byte = (get_request_opcode << 5) + len(filename) first_byte = (get_request_opcode << 5) + len(filename)
second_byte_to_n_byte: bytes = filename.encode("ascii") second_byte_to_n_byte = filename.encode("ascii")
print( print(
f"myftp> - {self.protocol} - Getting file {filename} from the server" f"myftp> - {self.protocol} - Getting file {filename} from the server"
@ -96,6 +96,11 @@ class Client:
# put command handling # put command handling
elif put_command_pattern.match(command): elif put_command_pattern.match(command):
command_name, filename = command.split(" ", 1) command_name, filename = command.split(" ", 1)
first_byte, second_byte_to_n_byte, data = self.put_payload_handling(
filename
)
print( print(
f"myftp> - {self.protocol} - Putting file {filename} into the server" f"myftp> - {self.protocol} - Putting file {filename} into the server"
) if self.debug else None ) if self.debug else None
@ -120,9 +125,16 @@ class Client:
first_byte: int = unknown_request_opcode << 5 first_byte: int = unknown_request_opcode << 5
# get or put case # get or put case
if command_name == "get" or command_name == "put": if command_name == "get":
payload = first_byte.to_bytes(1, "big") + second_byte_to_n_byte # type: ignore payload = first_byte.to_bytes(1, "big") + second_byte_to_n_byte # type: ignore
elif command_name == "put":
payload = (
first_byte.to_bytes(1, "big") + second_byte_to_n_byte + data # type: ignore
if second_byte_to_n_byte is not None and data is not None # type: ignore
else first_byte.to_bytes(1, "big") # type: ignore
)
elif command_name == "summary": elif command_name == "summary":
pass pass
@ -189,19 +201,50 @@ class Client:
# successful rescodes # successful rescodes
else: else:
# help rescode and successful change or put rescode
if rescode == 0b110: if rescode == 0b110:
print(f"myftp> - {self.protocol} - {response_data.decode('ascii')}") print(f"myftp> - {self.protocol} - {response_data.decode('ascii')}")
else: elif rescode == 0b000:
print(f"myftp> - {self.protocol} - {rescode_dict[rescode]}")
# get rescode
elif rescode == 0b001:
self.handle_get_response_from_server(filename_length, response_data) self.handle_get_response_from_server(filename_length, response_data)
def put_payload_handling(
self, filename: str
) -> Tuple[int, Optional[bytes], Optional[bytes]]:
"""
Assemble the pay load to put the file onto server
Return first_byte, second_byte_to_n_byte and data if successful
Or (None, None, None) if file not found
"""
try:
with open(os.path.join(self.directory_path, filename), "rb") as file:
content = file.read()
content_length = len(content)
first_byte = (put_request_opcode << 5) + len(filename)
second_byte_to_n_byte = filename.encode(
"ascii"
) + content_length.to_bytes(4, "big")
data = content
return (first_byte, second_byte_to_n_byte, data)
except FileNotFoundError:
return ((put_request_opcode << 5), None, None)
def handle_get_response_from_server( def handle_get_response_from_server(
self, filename_length: int, response_data: bytes self, filename_length: int, response_data: bytes
): ):
""" """
response_data is Response_data is
file name (filename_length bytes) + File name (filename_length bytes) +
file size (4 bytes) + File size (4 bytes) +
file content (rest of the bytes) File content (rest of the bytes)
""" """
try: try:
filename = response_data[:filename_length].decode("ascii") filename = response_data[:filename_length].decode("ascii")

View File

@ -6,6 +6,7 @@
from socket import socket, AF_INET, SOCK_DGRAM from socket import socket, AF_INET, SOCK_DGRAM
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Optional, Tuple from typing import Optional, Tuple
import traceback
import os import os
# Res-codes # Res-codes
@ -68,7 +69,9 @@ class Server:
first_byte = bytes([req_payload[0]]) first_byte = bytes([req_payload[0]])
request_type, filename_length = self.decode_first_byte(first_byte) request_type, filename_length_in_bytes = self.decode_first_byte(
first_byte
)
print( print(
f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}" f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}"
@ -83,7 +86,7 @@ class Server:
rescode = rescode_success_dict["help_rescode"] rescode = rescode_success_dict["help_rescode"]
response_data = "get,put,summary,change,help,bye".encode("ascii") response_data = "get,put,summary,change,help,bye".encode("ascii")
filename = None filename = None
filename_length = None filename_length_in_bytes = None
elif request_type == "get": elif request_type == "get":
pre_payload = self.process_get_req(req_payload[1:]) pre_payload = self.process_get_req(req_payload[1:])
@ -95,24 +98,41 @@ class Server:
): ):
rescode = rescode_success_dict["correct_get_request_rescode"] rescode = rescode_success_dict["correct_get_request_rescode"]
filename = pre_payload[0] filename = pre_payload[0]
filename_length = pre_payload[2] filename_length_in_bytes = pre_payload[2]
response_data = pre_payload[1] response_data = pre_payload[1]
else: else:
rescode = rescode_fail_dict["file_not_error_rescode"] rescode = rescode_fail_dict["file_not_error_rescode"]
filename_length = None filename_length_in_bytes = None
filename = None
response_data = None
elif request_type == "put":
# put request failed since there wasnt a file sent from client
if filename_length_in_bytes == 0:
rescode = rescode_fail_dict["unsuccessful_change_rescode"]
filename_length_in_bytes = None
filename = None
response_data = None
# put request success
else:
rescode = self.process_put_req(
filename_length_in_bytes, req_payload[1:]
)
filename_length_in_bytes = None
filename = None filename = None
response_data = None response_data = None
elif request_type == "unknown": elif request_type == "unknown":
rescode = rescode_fail_dict["unknown_request_rescode"] rescode = rescode_fail_dict["unknown_request_rescode"]
filename_length = None filename_length_in_bytes = None
filename = None filename = None
response_data = None response_data = None
res_payload: bytes = self.build_res_payload( res_payload: bytes = self.build_res_payload(
rescode=rescode, # type: ignore rescode=rescode, # type: ignore
filename_length=filename_length, filename_length=filename_length_in_bytes,
filename=filename, # type: ignore filename=filename, # type: ignore
response_data=response_data, # type:ignore response_data=response_data, # type:ignore
) )
@ -154,6 +174,38 @@ class Server:
return request_type, filename_length_in_bytes return request_type, filename_length_in_bytes
def process_put_req(self, filename_length: int, req_payload: bytes) -> int:
"""
Reconstruct file put by client
"""
filename = req_payload[:filename_length].decode("ascii")
filesize = int.from_bytes(
req_payload[filename_length : filename_length + 4], "big"
)
file_content = req_payload[filename_length + 4 :]
print(
f"myftp> - {self.protocol} - Reconstructing the file {filename} of size {filesize} bytes on the server after the client finished sending"
)
try:
with open(os.path.join(self.directory_path, filename), "wb") as file:
file.write(file_content)
print(
f"myftp> - {self.protocol} - File {filename} uploaded successfully"
)
return rescode_success_dict["correct_put_and_change_request_rescode"]
except Exception as error:
traceback_info = traceback.format_exc()
print(f"myftp> - {self.protocol} - {error} happened.")
print(traceback_info)
return rescode_fail_dict["unsuccessful_change_rescode"]
def process_get_req( def process_get_req(
self, second_byte_to_byte_n: bytes self, second_byte_to_byte_n: bytes
) -> Tuple[Optional[str], Optional[bytes], Optional[int]]: ) -> Tuple[Optional[str], Optional[bytes], Optional[int]]:
@ -219,6 +271,7 @@ class Server:
if filename is None: if filename is None:
second_byte_to_FL_plus_five = None second_byte_to_FL_plus_five = None
else: else:
# get case
second_byte_to_FL_plus_five = ( second_byte_to_FL_plus_five = (
filename.encode() + len(response_data).to_bytes(4, "big") filename.encode() + len(response_data).to_bytes(4, "big")
if response_data is not None if response_data is not None