Compare commits

..

No commits in common. "865529c402afe77e5d42d70392e68e21a4d9b498" and "73b4e06d02642ff3fa7ad87c7cd277f1bbcee035" have entirely different histories.

7 changed files with 78 additions and 484 deletions

View File

@ -9,10 +9,9 @@ WORKDIR /server
# Create files with random content in the /server directory # Create files with random content in the /server directory
RUN mkdir /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/file_server.txt bs=1 count=750
RUN dd if=/dev/urandom of=/server_directory/image_server.png bs=1 count=750 RUN dd if=/dev/urandom of=/server_directory/image_server.png bs=1 count=750
# generate a file that has random numbers # Start your Python application
RUN for _ in $(seq 1 150); do shuf -i 0-1000 -n 1 >> /server_directory/numbers.txt; done #CMD python server.py --port_number 12000 --debug 1

View File

@ -15,8 +15,3 @@ client-no-debug:
clean: clean:
docker-compose down --volumes docker-compose down --volumes
docker-client:
docker exec -it project-ftp_client-1 bash
docker-server:
docker exec -it project-ftp_server-1 bash

View File

@ -1,35 +1,20 @@
# MyFTP # MyFTP
A Python implementation of a FTP server. Supports both TCP and UDP protocols. Tested on Python 3.11 and Python 3.10.
## Dependencies ## Dependencies
Zero. Only python standard libs were used. Zero. Only python standard libs were used. Tested on Python 3.11
## Running ## Running
### Client ### Client
You can run `python3 src/myftp/client.py --directory <insert valid directory that you have read/write permissions>` to start the client. You can run `python3 src/myftp/client.py --directory <insert valid directory that you have read/write permissions>` to start the client or `python3 src/myftp/client.py --debug 1 --directory <insert valid directory that you have read/write permissions>` for debugging purposes.
To run with debug info: `python3 src/myftp/client.py --debug 1 --directory <insert valid directory that you have read/write permissions>`.
Some example test commands:
- `get file_server.txt`
- `summary numbers.txt`
- `put file_local.txt`
- `put image_local.png`
- `change file_server.txt file_server1.txt`
- `help`
### Server ### Server
By default, the server IP address or hostname or server name will be `0.0.0.0` or `localhost` (meaning it will bind to all interfaces). The `--port_number` flag, if not specified will be by default `12000`. By default, the server IP address or hostname or server name will be `0.0.0.0` or `localhost` (meaning it will bind to all interfaces). The `--port_number` flag, if not specified will be by default `12000`.
You can run `python3 src/myftp/server.py --directory <insert valid directory that you have read/write permissions>` to start the server. You can run `python3 src/myftp/server.py --directory <insert valid directory that you have read/write permissions>` to start the server or `python3 src/myftp/server.py --ip_addr <insert ip addr of the server> --port_number <insert port number here> --debug 1 --directory <insert valid directory that you have read/write permissions>` for debugging purposes and to specify the port number.
Or run `python3 src/myftp/server.py --ip_addr <insert ip addr of the server> --port_number <insert port number here> --debug 1 --directory <insert valid directory that you have read/write permissions>` for debugging purposes.
## Localhost testing ## Localhost testing
@ -61,8 +46,6 @@ Run `python3 src/myftp/server.py --debug 1 --directory server_directory`
- Run the server with `make server` in a terminal. - Run the server with `make server` in a terminal.
- Run the client with `make client` in a terminal. - Run the client with `make client` in a terminal.
- For the client, when asked to put in the ip address and port number of the server, you can put in `ftp_server 12000` or adjust to your chosen port number. The IP address is resolved by Docker so ftp_server can not be changed. - For the client, when asked to put in the ip address and port number of the server, you can put in `ftp_server 12000` or adjust to your chosen port number. The IP address is resolved by Docker so ftp_server can not be changed.
- Go into the `client` docker container with `make docker-client`. The folder in which FTP is using to host client files is located at `/client_directory/`
- Or go into the `server` docker container with `make docker-server`. The folder in which FTP is using to host server files is located at `/server_directory/`
- Tear down everything with `make clean`. - Tear down everything with `make clean`.
#### Fast setup #### Fast setup

View File

@ -1,150 +0,0 @@
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

View File

@ -3,7 +3,7 @@
# Description: FTP client (both UDP and TCP implemented) # Description: FTP client (both UDP and TCP implemented)
from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM from socket import socket, AF_INET, SOCK_DGRAM
from typing import Pattern, Tuple, Optional from typing import Pattern, Tuple, Optional
from argparse import ArgumentParser from argparse import ArgumentParser
import traceback import traceback
@ -11,15 +11,12 @@ import os
import re import re
# Patterns for command matchings # patterns for command matchings
# - compiled for extra performance # compiled for extra performance
# - Ignore case, get or GET both works get_command_pattern: Pattern = re.compile(r"^get\s+[^\s]+$")
get_command_pattern: Pattern = re.compile(r"^get\s+[^\s]+$", re.IGNORECASE) put_command_pattern: Pattern = re.compile(r"^put\s+[^\s]+$")
put_command_pattern: Pattern = re.compile(r"^put\s+[^\s]+$", re.IGNORECASE) summary_command_pattern: Pattern = re.compile(r"^summary\s+[^\s]+$")
summary_command_pattern: Pattern = re.compile(r"^summary\s+[^\s]+$", re.IGNORECASE) change_command_pattern: Pattern = re.compile(r"^change\s+[^\s]+\s+[^\s]+$")
change_command_pattern: Pattern = re.compile(
r"^change\s+[^\s]+\s+[^\s]+$", re.IGNORECASE
)
# opcodes # opcodes
put_request_opcode: int = 0b000 put_request_opcode: int = 0b000
@ -61,35 +58,22 @@ class Client:
self.debug = debug self.debug = debug
def run(self): def run(self):
self.client_socket = socket( client_socket = socket(AF_INET, SOCK_DGRAM)
AF_INET, (SOCK_DGRAM if self.protocol == "UDP" else SOCK_STREAM) client_socket.settimeout(10)
)
self.client_socket.settimeout(10)
# only if using TCP
try:
self.client_socket.connect(
(self.server_name, self.server_port)
) if self.protocol == "TCP" else None
except ConnectionRefusedError:
print(
f"myftp> - {self.protocol} - ConnectionRefusedError happened. Please restart the client program, make sure the server is running and/or put a different server name and server port."
)
return
try: try:
while True: while True:
# get command from user # get command from user
command = input(f"myftp> - {self.protocol} - : ").strip() command = input(f"myftp> - {self.protocol} - : ").strip().lower()
# handling the "bye" command # handling the "bye" command
if command == "bye" or command == "BYE": if command == "bye":
self.client_socket.close() client_socket.close()
print(f"myftp> - {self.protocol} - Session is terminated") print(f"myftp> - {self.protocol} - Session is terminated")
break break
# help # help
elif command == "help" or command == "HELP": elif command == "help":
first_byte: int = help_request_opcode << 5 first_byte: int = help_request_opcode << 5
command_name = "help" command_name = "help"
@ -99,8 +83,7 @@ class Client:
# get command handling # get command handling
elif get_command_pattern.match(command): elif get_command_pattern.match(command):
_, filename = command.split(" ", 1) command_name, filename = command.split(" ", 1)
command_name = "get"
first_byte = (get_request_opcode << 5) + len(filename) first_byte = (get_request_opcode << 5) + len(filename)
@ -112,8 +95,7 @@ class Client:
# put command handling # put command handling
elif put_command_pattern.match(command): elif put_command_pattern.match(command):
_, filename = command.split(" ", 1) command_name, filename = command.split(" ", 1)
command_name = "put"
first_byte, second_byte_to_n_byte, data = self.put_payload_handling( first_byte, second_byte_to_n_byte, data = self.put_payload_handling(
filename filename
@ -125,45 +107,25 @@ class Client:
# summary command handling # summary command handling
elif summary_command_pattern.match(command): elif summary_command_pattern.match(command):
_, filename = command.split(" ", 1) command_name, filename = command.split(" ", 1)
command_name = "summary"
print( print(
f"myftp> - {self.protocol} - Summary file {filename} from the server" f"myftp> - {self.protocol} - Summary file {filename} from the server"
) if self.debug else None ) if self.debug else None
first_byte = (summary_request_opcode << 5) + len(filename)
second_byte_to_n_byte = filename.encode("ascii")
# change command handling # change command handling
elif change_command_pattern.match(command): elif change_command_pattern.match(command):
_, old_filename, new_filename = command.split() command_name, old_filename, new_filename = command.split()
command_name = "change"
print( print(
f"myftp> - {self.protocol} - Changing file named {old_filename} into {new_filename} on the server" f"myftp> - {self.protocol} - Changing file named {old_filename} into {new_filename} on the server"
) if self.debug else None ) if self.debug else None
first_byte = (change_request_opcode << 5) + len(old_filename)
second_byte_to_n_byte = (
old_filename.encode("ascii")
+ len(new_filename).to_bytes(1, "big")
+ new_filename.encode("ascii")
)
# unknown request, assigned opcode is 0b101 # unknown request, assigned opcode is 0b101
else: else:
command_name = None command_name = None
first_byte: int = unknown_request_opcode << 5 first_byte: int = unknown_request_opcode << 5
# get change put cases # get or put case
if ( if command_name == "get":
command_name == "get"
or command_name == "summary"
or command_name == "change"
):
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": elif command_name == "put":
@ -173,22 +135,23 @@ class Client:
else first_byte.to_bytes(1, "big") # type: ignore else first_byte.to_bytes(1, "big") # type: ignore
) )
elif command_name == "summary":
pass
elif command == "change":
pass
# help case and unknown request # help case and unknown request
else: else:
payload: bytes = first_byte.to_bytes(1, "big") # type: ignore payload: bytes = first_byte.to_bytes(1, "big") # type: ignore
print( print(
f"myftp> - {self.protocol} - sent payload {payload} to the server. Payload length is {len(payload)}" # type: ignore f"myftp> - {self.protocol} - sent payload {bin(int.from_bytes(payload, byteorder='big'))[2:]} to the server" # type: ignore
) if self.debug else None ) if self.debug else None
if self.protocol == "UDP": client_socket.sendto(payload, (self.server_name, self.server_port)) # type: ignore
self.client_socket.sendto(
payload, (self.server_name, self.server_port)
) # type: ignore
else:
self.client_socket.sendall(payload) # type: ignore
response_payload = self.client_socket.recv(2048) response_payload = client_socket.recv(2048)
self.parse_response_payload(response_payload) self.parse_response_payload(response_payload)
@ -203,9 +166,6 @@ class Client:
f"myftp> - {self.protocol} - Server at {self.server_name} did not respond within 5 seconds. Check the address or server status." f"myftp> - {self.protocol} - Server at {self.server_name} did not respond within 5 seconds. Check the address or server status."
) )
except KeyboardInterrupt:
print(f"\nmyftp> - {self.protocol} - Client shutting down")
except Exception as error: except Exception as error:
traceback_info = traceback.format_exc() traceback_info = traceback.format_exc()
@ -214,14 +174,9 @@ class Client:
print(traceback_info) print(traceback_info)
finally: finally:
self.client_socket.close() client_socket.close()
def parse_response_payload(self, response_payload: bytes): def parse_response_payload(self, response_payload: bytes):
"""
Parse response payload for further processing
response_payload is the the entire packet that was sent from the server
"""
first_byte = bytes([response_payload[0]]) first_byte = bytes([response_payload[0]])
first_byte_binary = int.from_bytes(first_byte, "big") first_byte_binary = int.from_bytes(first_byte, "big")
rescode = first_byte_binary >> 5 rescode = first_byte_binary >> 5
@ -242,7 +197,6 @@ class Client:
# error rescodes # error rescodes
if rescode in [0b011, 0b100, 0b101]: if rescode in [0b011, 0b100, 0b101]:
# print to client
print(f"myftp> - {self.protocol} - {rescode_dict[rescode]}") print(f"myftp> - {self.protocol} - {rescode_dict[rescode]}")
# successful rescodes # successful rescodes
@ -255,15 +209,12 @@ class Client:
# get rescode # get rescode
elif rescode == 0b001: elif rescode == 0b001:
self.handle_get_response_from_server(filename_length, response_data) self.handle_get_response_from_server(filename_length, response_data)
# summary rescode
elif rescode == 0b010:
self.handle_summary_response_from_server(filename_length, response_data)
def put_payload_handling( def put_payload_handling(
self, filename: str self, filename: str
) -> Tuple[int, Optional[bytes], Optional[bytes]]: ) -> Tuple[int, Optional[bytes], Optional[bytes]]:
""" """
Assemble the payload to put the file onto server Assemble the pay load to put the file onto server
Return first_byte, second_byte_to_n_byte and data if successful Return first_byte, second_byte_to_n_byte and data if successful
Or (None, None, None) if file not found Or (None, None, None) if file not found
@ -290,8 +241,6 @@ class Client:
self, filename_length: int, response_data: bytes self, filename_length: int, response_data: bytes
): ):
""" """
Handle the get response from the server
Response_data is Response_data is
File name (filename_length bytes) + File name (filename_length bytes) +
File size (4 bytes) + File size (4 bytes) +
@ -308,42 +257,8 @@ class Client:
print( print(
f"myftp> - {self.protocol} - Filename: {filename}, File_size: {file_size} bytes" f"myftp> - {self.protocol} - Filename: {filename}, File_size: {file_size} bytes"
) if self.debug else None
with open(os.path.join(self.directory_path, filename), "wb") as file:
file.write(file_content)
print(
f"myftp> - {self.protocol} - File {filename} has been downloaded successfully"
) )
except Exception:
raise
def handle_summary_response_from_server(
self, filename_length: int, response_data: bytes
):
"""
Handle summary response from server
Response_data is
File name (filename_length bytes) +
File size (4 bytes) +
File content (rest of the bytes)
"""
try:
filename = response_data[:filename_length].decode("ascii")
file_size = int.from_bytes(
response_data[filename_length : filename_length + 4], "big"
)
file_content = response_data[
filename_length + 4 : filename_length + 4 + file_size
]
print(
f"myftp> - {self.protocol} - Filename: {filename}, File_size: {file_size} bytes"
) if self.debug else None
with open(os.path.join(self.directory_path, filename), "wb") as file: with open(os.path.join(self.directory_path, filename), "wb") as file:
file.write(file_content) file.write(file_content)
@ -426,15 +341,27 @@ def init():
user_supplied_address = get_address_input() user_supplied_address = get_address_input()
client = Client( # UDP client selected here
if protocol_selection == "2":
udp_client = Client(
user_supplied_address[0], user_supplied_address[0],
user_supplied_address[1], user_supplied_address[1],
args.directory, args.directory,
args.debug, args.debug,
("UDP" if protocol_selection == "2" else "TCP"), "UDP",
) )
client.run() udp_client.run()
else:
tcp_client = Client(
user_supplied_address[0],
user_supplied_address[1],
args.directory,
args.debug,
"TCP",
)
tcp_client.run()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -3,7 +3,7 @@
# Description: FTP server (both UDP and TCP implemented) # Description: FTP server (both UDP and TCP implemented)
from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM 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 traceback
@ -50,14 +50,8 @@ class Server:
self.debug = debug self.debug = debug
def run(self): def run(self):
server_socket = socket( self.server_socket = socket(AF_INET, SOCK_DGRAM)
AF_INET, (SOCK_DGRAM if self.protocol == "UDP" else SOCK_STREAM) self.server_socket.bind((self.server_name, self.server_port))
)
server_socket.bind((self.server_name, self.server_port))
# only needed for TCP
server_socket.listen(5) if self.protocol == "TCP" else None
print( print(
f"myftp> - {self.protocol} - Server is ready to receive at {self.server_name}:{self.server_port}" f"myftp> - {self.protocol} - Server is ready to receive at {self.server_name}:{self.server_port}"
@ -66,27 +60,12 @@ class Server:
shut_down = False shut_down = False
try: try:
if self.protocol == "TCP":
client_socket, clientAddress = server_socket.accept()
print(
f"myftp> - {self.protocol} - Connected to TCP client at {clientAddress}"
) if self.debug else None
while not shut_down: while not shut_down:
print( print(
f"myftp> - {self.protocol} ------------------------------------------------------------------" f"myftp> - {self.protocol} ------------------------------------------------------------------"
) if self.debug else None ) if self.debug else None
if self.protocol == "UDP": req_payload, clientAddress = self.server_socket.recvfrom(2048)
req_payload, clientAddress = server_socket.recvfrom(2048)
else:
req_payload = client_socket.recv(2048) # type: ignore
# TCP client disconnected
if not req_payload:
client_socket.close() # type: ignore
return
first_byte = bytes([req_payload[0]]) first_byte = bytes([req_payload[0]])
@ -95,7 +74,7 @@ class Server:
) )
print( print(
f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}. Payload length is {len(req_payload)}" # type: ignore f"myftp> - {self.protocol} - Received message from client at {clientAddress}: {req_payload}"
) if self.debug else None ) if self.debug else None
# help request handling # help request handling
@ -145,28 +124,6 @@ class Server:
filename = None filename = None
response_data = 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 == "change":
rescode = self.process_change_req(
filename_length_in_bytes, req_payload[1:]
)
filename_length_in_bytes = None
filename = 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_in_bytes = None filename_length_in_bytes = None
@ -180,22 +137,15 @@ class Server:
response_data=response_data, # type:ignore response_data=response_data, # type:ignore
) )
if self.protocol == "UDP": self.server_socket.sendto(res_payload, clientAddress)
server_socket.sendto(res_payload, clientAddress) # type: ignore
else:
client_socket.sendall(res_payload) # type: ignore
print( print(
f"myftp> - {self.protocol} - Sent message to client at {clientAddress}: {res_payload}. Payload length is {len(res_payload)}" # type: ignore f"myftp> - {self.protocol} - Sent message to client at {clientAddress}: {res_payload}"
) if self.debug else None ) if self.debug else None
except KeyboardInterrupt: except KeyboardInterrupt:
shut_down = True shut_down = True
if self.protocol == "UDP": self.server_socket.close()
server_socket.close()
else:
client_socket.close() # type: ignore
print(f"myftp> - {self.protocol} - Server shutting down") print(f"myftp> - {self.protocol} - Server shutting down")
finally: finally:
@ -224,112 +174,6 @@ class Server:
return request_type, filename_length_in_bytes return request_type, filename_length_in_bytes
def process_change_req(
self, old_filename_length_in_bytes: int, req_payload: bytes
) -> int:
"""
Process change request from client
"""
old_filename = req_payload[:old_filename_length_in_bytes].decode("ascii")
new_filename_length = int.from_bytes(
req_payload[
old_filename_length_in_bytes : old_filename_length_in_bytes + 1
],
"big",
)
new_filename = req_payload[old_filename_length_in_bytes + 1 :].decode("ascii")
actual_new_filename_length = len(new_filename)
try:
if new_filename_length <= 31 or actual_new_filename_length <= 31:
old_filename_full_path = os.path.normpath(
os.path.join(self.directory_path, old_filename)
)
new_filename_full_path = os.path.normpath(
os.path.join(self.directory_path, new_filename)
)
print(
f"myftp> - {self.protocol} - Changing file named {old_filename_full_path} to new file {new_filename_full_path}"
)
os.rename(old_filename_full_path, new_filename_full_path)
return rescode_success_dict["correct_put_and_change_request_rescode"]
else:
print(
f"myftp> - {self.protocol} - New file name longer than 31 characters error"
)
return rescode_fail_dict["unsuccessful_change_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_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: def process_put_req(self, filename_length: int, req_payload: bytes) -> int:
""" """
Reconstruct file put by client Reconstruct file put by client
@ -373,7 +217,6 @@ class Server:
If not, return None, None, None tuple If not, return None, None, None tuple
""" """
filename = second_byte_to_byte_n.decode("ascii") filename = second_byte_to_byte_n.decode("ascii")
print(f"myftp> - {self.protocol} - trying to find file {filename}")
try: try:
with open(os.path.join(self.directory_path, filename), "rb") as file: with open(os.path.join(self.directory_path, filename), "rb") as file:
@ -386,10 +229,6 @@ class Server:
print(f"myftp> - {self.protocol} - file {filename} not found") print(f"myftp> - {self.protocol} - file {filename} not found")
return (None, None, None) return (None, None, None)
except IsADirectoryError:
print(f"myftp> - {self.protocol} - filename is blank")
return (None, None, None)
# assembling the payload to send back to the client # assembling the payload to send back to the client
def build_res_payload( def build_res_payload(
self, self,
@ -424,14 +263,13 @@ class Server:
# help case # help case
elif filename is None and response_data is not None: elif filename is None and response_data is not None:
first_byte = ((rescode << 5) + len(response_data)).to_bytes(1, "big") first_byte = ((rescode << 5) + len(response_data)).to_bytes(1, "big")
# other cases # unsuccessful cases
else: else:
first_byte = (rescode << 5).to_bytes(1, "big") first_byte = (rescode << 5).to_bytes(1, "big")
# we only need the firstbyte # we only need the firstbyte
if filename is None: if filename is None:
second_byte_to_FL_plus_five = None second_byte_to_FL_plus_five = None
# second byte and more are needed
else: else:
# get case # get case
second_byte_to_FL_plus_five = ( second_byte_to_FL_plus_five = (
@ -444,13 +282,11 @@ class Server:
f"myftp> - {self.protocol} - First byte assembled for rescode {format(rescode, '03b')}: {bin(int.from_bytes(first_byte, byteorder='big'))[2:]}" f"myftp> - {self.protocol} - First byte assembled for rescode {format(rescode, '03b')}: {bin(int.from_bytes(first_byte, byteorder='big'))[2:]}"
) if self.debug else None ) if self.debug else None
# get/summary case
if second_byte_to_FL_plus_five is not None and response_data is not None: if second_byte_to_FL_plus_five is not None and response_data is not None:
res_payload = first_byte + second_byte_to_FL_plus_five + response_data res_payload = first_byte + second_byte_to_FL_plus_five + response_data
# help case # help case
elif second_byte_to_FL_plus_five is None and response_data is not None: elif second_byte_to_FL_plus_five is None and response_data is not None:
res_payload = first_byte + response_data res_payload = first_byte + response_data
# change/put case
else: else:
res_payload = first_byte res_payload = first_byte
@ -518,16 +354,20 @@ def init():
) )
return return
# start the server # UDP client selected here
server = Server( if protocol_selection == "2":
args.ip_addr, udp_server = Server(
args.port_number, args.ip_addr, args.port_number, args.directory, args.debug, "UDP"
args.directory,
args.debug,
("UDP" if protocol_selection == "2" else "TCP"),
) )
server.run() udp_server.run()
else:
tcp_server = Server(
args.ip_addr, args.port_number, args.directory, args.debug, "TCP"
)
tcp_server.run()
if __name__ == "__main__": if __name__ == "__main__":

0
tests/__init__.py Normal file
View File