Performs a removal when there is a DELETE request, returning a response with
the headers and the correct body. Removes the object within the database, based on the received ID.
@@ -203,7 +203,7 @@
request – Request object, containing the body and headers of that request
Returns
-
The answer to this request
+
A byte object containing the response to this request
You may override this method in a subclass. The standard run() method
-invokes the callable object passed to the object’s constructor as the
-target argument, if any, with sequential and keyword arguments taken
-from the args and kwargs arguments, respectively.
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/source/conf.py b/docs/source/conf.py
index c8efbf7..2d3a472 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -61,4 +61,4 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
\ No newline at end of file
+html_static_path = ['static']
\ No newline at end of file
diff --git a/handler/HandlerErrors.py b/handler/HandlerErrors.py
index 937398b..9dfc15d 100644
--- a/handler/HandlerErrors.py
+++ b/handler/HandlerErrors.py
@@ -3,7 +3,13 @@
from message import Response, StatusCode
from string import Template
+
class HandlerErrors:
+ """Class responsible for handling errors"""
+
+ def __init__(self):
+ pass
+
urlTable = {
"/error": {"type": "text/html", "filePath": "./assets/public/error.html"},
"/bootstrap.min.css": {"type": "text/css", "filePath": "./assets/bootstrap.min.css"},
@@ -14,7 +20,15 @@ class HandlerErrors:
}
@staticmethod
- def sendErrorCode(request, statusCode):
+ def sendErrorCode(request: any, statusCode: StatusCode) -> bytes:
+ """Responsible for generating a page with the given status code
+
+ :param request: An object representing the request data. DEPRECATED
+ :param statusCode: A StatusCode object containing the the status soon to be shown in the page
+ :returns: A byte object containing the response header and the error page in the body.
+
+ """
+
response: Response = Response.Response(status_code=statusCode, body="", header={})
error = HandlerErrors.urlTable['/error']
diff --git a/handler/HandlerImage.py b/handler/HandlerImage.py
index d9f88ae..7cbde22 100644
--- a/handler/HandlerImage.py
+++ b/handler/HandlerImage.py
@@ -3,14 +3,28 @@
import os
import base64
+
class HandlerImage:
+ """Class responsible for handling images"""
+
+ def __init__(self):
+ pass
+
imageDatabasePath: str = 'databaseUser/Images/images.json'
imageDatabase: dict = {}
image_formats = [".jpeg", ".jpg", ".png", ".gif"]
directory = "./databaseUser/Images"
@staticmethod
- def data_to_image(data: str, id_user: str):
+ def data_to_image(data: str, id_user: str) -> any:
+ """Responsible for creating an image from a data URI
+
+ :param data: A string containing the data URI
+ :param id_user: A string containing the user id
+ :returns: Three strings each containing the image name, image extension and image path. Can also return None.
+
+ """
+
extension = ".jpg"
for formats in HandlerImage.image_formats:
if data.find(formats[1:], 0, 20) != -1:
@@ -32,7 +46,14 @@ def data_to_image(data: str, id_user: str):
return None
@staticmethod
- def image_to_data(image_path):
+ def image_to_data(image_path: str) -> str:
+ """Responsible for converting an image into a data URI
+
+ :param image_path: A string containing the image path
+ :returns: A string containing the dataURI from the image
+
+ """
+
index = image_path.find('.')
extension = image_path[index+1:]
@@ -48,7 +69,15 @@ def image_to_data(image_path):
return data_uri
@staticmethod
- def insert_image_database(data: str, id_user: str):
+ def insert_image_database(data: str, id_user: str) -> any:
+ """Responsible for inserting the image into our database
+
+ :param data: A string containing the data URI
+ :param id_user: A string containing the user id
+ :returns: A string containing the id of the registered image or None
+
+ """
+
# Download the image
image_id, extension, path = HandlerImage.data_to_image(data, id_user)
@@ -70,7 +99,14 @@ def insert_image_database(data: str, id_user: str):
return None
@staticmethod
- def delete_image_database(image_id):
+ def delete_image_database(image_id: str) -> bool:
+ """Responsible for deleting an image with given id from the database
+
+ :param image_id: A string containing the image id
+ :returns: A boolean representing if the operation was successful or not
+
+ """
+
database = HandlerImage.getData()
if database is None:
return False
@@ -83,7 +119,12 @@ def delete_image_database(image_id):
return False
@staticmethod
- def delete_all_images():
+ def delete_all_images() -> bool:
+ """Responsible for deleting all images from the database
+
+ :returns: A boolean representing if the operation was successful or not
+ """
+
database = HandlerImage.getData()
if database is None:
return False
@@ -106,7 +147,14 @@ def delete_all_images():
return False
@staticmethod
- def remove_img(img_name):
+ def remove_img(img_name: str) -> bool:
+ """Responsible for removing a image from the images directory
+
+ :param: img_name: A string containing the image name
+ :returns: A boolean representing if the operation was successful or not
+
+ """
+
path = HandlerImage.directory + img_name
os.remove(path)
# check if file exists or not
@@ -114,8 +162,16 @@ def remove_img(img_name):
# file did not exists
return False
+ return True
+
@staticmethod
- def getData():
+ def getData() -> any:
+ """Responsible for getting the images from the database
+
+ :returns: A dictionary containing the images or None.
+
+ """
+
try:
with open(HandlerImage.imageDatabasePath, 'r+') as file:
HandlerImage.imageDatabase = json.load(file)
@@ -124,7 +180,14 @@ def getData():
return None
@staticmethod
- def isImageRegistered(image_id: str):
+ def isImageRegistered(image_id: str) -> (bool, any):
+ """Responsible for checking if the image with given id is registered
+
+ :param image_id: A string containing the id of the image to be deleted
+ :returns: A bool containing the result of the operation and the image index in case it's registered
+
+ """
+
database = HandlerImage.getData()
if database is None:
@@ -139,7 +202,14 @@ def isImageRegistered(image_id: str):
return False, None
@staticmethod
- def setData(data: dict):
+ def setData(data: dict) -> bool:
+ """Responsible for replacing the whole image database with the given data
+
+ :param data: a dictionary containing the new image database
+ :returns: A boolean representing if the operation was successful or not
+
+ """
+
HandlerImage.imageDatabase = data
try:
with open(HandlerImage.imageDatabasePath, 'w+') as file:
diff --git a/handler/HandlerRequests.py b/handler/HandlerRequests.py
index c9cb51e..176d691 100644
--- a/handler/HandlerRequests.py
+++ b/handler/HandlerRequests.py
@@ -1,4 +1,5 @@
-from message.StatusCode import StatusCode
+import socket
+
from threading import Thread
from message.ParserMessage import ParserMessage
@@ -13,7 +14,15 @@
from handler.HandlerErrors import HandlerErrors
-def recv(sock, chunkSize=8192):
+def recv(sock: socket.socket, chunkSize: int = 8192) -> any:
+ """Responsible for receiving all the request message chunks from the socket
+
+ :param sock: the tcp socket responsible for receiving and sending messages
+ :param chunkSize: the size of each incoming request message fragment
+ :returns: A byte object containing the whole request message or None
+
+ """
+
fragments = []
data = None
@@ -41,11 +50,26 @@ def recv(sock, chunkSize=8192):
class Handler(Thread):
- def __init__(self, server):
+ """Class responsible for handling incoming requests and sending responses"""
+
+ def __init__(self, server) -> None:
+ """Initializes the class instances attributes
+
+ :param server: A server object containing the socket, host ip and host port
+ :returns: None
+
+ """
+
Thread.__init__(self)
self.server = server
- def run(self):
+ def run(self) -> None:
+ """Responsible for running the server
+
+ :returns: None
+
+ """
+
while True:
connectionSocket, addr = self.server.serverSocket.accept()
@@ -61,7 +85,14 @@ def run(self):
self.checkTypeRequest(request, connectionSocket)
break
- def checkTypeRequest(self, request, connectionSocket):
+ def checkTypeRequest(self, request: Request, connectionSocket: socket.socket) -> None:
+ """Responsible for checking the request type and calling the appropriate method
+
+ :param request: A Request object
+ :param connectionSocket: A tcp socket
+ :returns: None
+
+ """
response = {}
try:
diff --git a/handler/findFile.py b/handler/findFile.py
index 5aab08c..64ff271 100644
--- a/handler/findFile.py
+++ b/handler/findFile.py
@@ -3,6 +3,14 @@
def find(pattern, path):
+ """Responsible for finding files given a path an a regex
+
+ :param pattern: A regex for the fnmatch() function. Generally the file extension.
+ :param path: A path to search for files
+ :returns: A list containing the files found
+
+ """
+
result = []
for root, dirs, files in os.walk(path):
for name in files:
diff --git a/main.py b/main.py
index 5431a3f..fcd67f6 100644
--- a/main.py
+++ b/main.py
@@ -1,10 +1,14 @@
from server import startServer
-""" Calls the function that instantiates a server
-
-"""
+
def main() -> None:
+ """ Calls the function that instantiates a server
+
+ :returns: None
+
+ """
startServer()
+
if __name__ == "__main__":
main()
diff --git a/message/ParserMessage.py b/message/ParserMessage.py
index 74fa753..e444634 100644
--- a/message/ParserMessage.py
+++ b/message/ParserMessage.py
@@ -1,7 +1,16 @@
class ParserMessage:
-
+
+ def __init__(self):
+ pass
+
@staticmethod
- def parseRequest(request):
+ def parseRequest(request: str) -> dict:
+ """Parses the incoming decoded request into a convenient and easy to use dictionary
+
+ :param request: the decoded string received by the socket
+ :returns: A dictionary containing the parsed headers
+
+ """
requestObj = {}
splitRequest = [line.strip('\r') for line in request.split('\n')]
header = splitRequest[0].split(' ')
@@ -11,7 +20,8 @@ def parseRequest(request):
requestObj['HTTP-Version'] = header[2].split('/')[1]
index = 0
-
+
+ # Iterates over every line from the request, splitting at each colon and then, if necessary, at each comma
for line in splitRequest[1:]:
index += 1
line = line.strip('\r')
@@ -25,7 +35,7 @@ def parseRequest(request):
if (value.find(',') != -1):
value = value.split(', ')
requestObj[key] = value
-
+
requestObj['body'] = '\n'.join(splitRequest[index:])
return requestObj
diff --git a/message/Request.py b/message/Request.py
index c73856a..f122980 100644
--- a/message/Request.py
+++ b/message/Request.py
@@ -1,24 +1,39 @@
class Request:
- def __init__(self, request_message):
- self.type = request_message['Request-Type']
- self.URI = request_message['Request-URI']
- self.http_version = request_message['HTTP-Version']
- self.host = request_message['Host']
- self.connection = request_message['Connection']
- self.user_agent = request_message['User-Agent']
- self.body = request_message['body']
- self.valid_content = None if 'Accept' not in request_message else request_message['Accept']
- self.accept_encoding = request_message['Accept-Encoding']
- self.accept_language = None if 'Accept-Language' not in request_message else request_message['Accept-Language']
- self.cookie = None if 'Cookie' not in request_message else request_message['Cookie']
- self.content_type = None if 'Content-Type' not in request_message else request_message['Content-Type']
- self.cache = None if 'Cache-Control' not in request_message else request_message['Cache-Control']
- self.content_length = None if 'Content-Length' not in request_message else request_message['Content-Length']
+ """Responsible for encapsulating a parsed request"""
+
+ def __init__(self, request_message: dict) -> None:
+ """Initializes the Request class instance's attributes
+
+ :param request_message: A dictionary containing a parsed request
+ :returns: None
+
+ """
+ self.type: str = request_message['Request-Type']
+ self.URI: str = request_message['Request-URI']
+ self.http_version: str = request_message['HTTP-Version']
+ self.host: str = request_message['Host']
+ self.connection: str = request_message['Connection']
+ self.user_agent: str = request_message['User-Agent']
+ self.body: str = request_message['body']
+ self.valid_content: str = None if 'Accept' not in request_message else request_message['Accept']
+ self.accept_encoding: str = request_message['Accept-Encoding']
+ self.accept_language: str = None if 'Accept-Language' not in request_message else request_message['Accept-Language']
+ self.cookie: str = None if 'Cookie' not in request_message else request_message['Cookie']
+ self.content_type: str = None if 'Content-Type' not in request_message else request_message['Content-Type']
+ self.cache: str = None if 'Cache-Control' not in request_message else request_message['Cache-Control']
+ self.content_length: str = None if 'Content-Length' not in request_message else request_message['Content-Length']
self.ip = ""
- def setIp(self, ip):
+ def setIp(self, ip: str) -> None:
+ """Responsible for setting a new value to the ip attribute of the Request class instance
+
+ :param ip: A string containing the new ip
+ :returns: None
+
+ """
self.ip = ip
- def __str__(self):
+ def __str__(self) -> str:
+ """Returns a string representation of the Request class instance"""
return f'{self.type, self.URI, self.http_version, self.host, self.body}...'
diff --git a/message/Response.py b/message/Response.py
index 4df8f09..b12c89e 100644
--- a/message/Response.py
+++ b/message/Response.py
@@ -1,21 +1,46 @@
+import enum
from datetime import datetime
from message.StatusCode import StatusCode
class Response:
- def __init__(self, status_code, body, header):
+ """Responsible for encapsulating the response headers and body"""
+
+ def __init__(self, status_code: enum.Enum, body: str, header: dict) -> None:
+ """Initializes the Response class instance's attributes
+
+ :param status_code: A StatusCode enum object representing the outcome of the request
+ :param body: A string containing the content of the response
+ :param header: A dictionary containing the headers of the response
+ :returns: None
+
+ """
self.protocol: str = "HTTP/1.1"
self.status_code: StatusCode.StatusCode = status_code
self.body: str = body
self.headers: dict = header
self.server: str = "Apache/2.22.14 (Ubuntu-20.04)"
- def encodeResponse(self):
+ def encodeResponse(self) -> bytes:
+ """Responsible for encoding the string representation of the Response class instance
+
+ returns: A byte object containing the Response class instance
+
+ """
+
response = self.__str__()
return response.encode()
- def encodeResponseImages(self, image, new_path=None):
+ def encodeResponseImages(self, image: str, new_path: str = None) -> bytes:
+ """Responsible for encoding the response along with the image
+
+ :param image: A string containing the image name
+ :param new_path: A string containing the new image path if the image was altered
+ returns: A byte object containing the response headers and body along with the image
+
+ """
+
response = ""
response += self.statusLine() + "\n"
response += self.headerLine()
@@ -29,18 +54,18 @@ def encodeResponseImages(self, image, new_path=None):
return response.encode() + self.body
def statusLine(self):
- """
- :return: HTTP/1.1 200 OK
+ """Responsible for creating a string with the protocol and status
+
+ :returns: A string containing the protocol and the status
+
"""
return f'{self.protocol} {self.status_code.value[0]} {self.status_code.value[1]}'
- def headerLine(self):
- """
- :return: Date: Mon, 27 Jul 2009 12:28:53 GMT
- Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
- Content-Length: 88
- Content-Type: text/html
- Connection: Closed
+ def headerLine(self) -> str:
+ """Responsible for creating a string containing the headers information
+
+ :returns: A string containing the headers information
+
"""
header = ""
header += Response.getDate() + "\n"
@@ -52,7 +77,13 @@ def headerLine(self):
return header
@staticmethod
- def getDate():
+ def getDate() -> str:
+ """Responsible for creating a string with the current date
+
+ :returns: A string containing the current date
+
+ """
+
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
time = datetime.now()
@@ -63,7 +94,14 @@ def getDate():
return f'{current_day}, {date}'
@staticmethod
- def getDateFromSeconds(seconds):
+ def getDateFromSeconds(seconds: float) -> str:
+ """Creates a date from the given seconds
+
+ :param seconds: A float number containing the total date in seconds
+ :returns: A string containing the date created from the given second
+
+ """
+
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
time = datetime.fromtimestamp(seconds)
@@ -73,7 +111,13 @@ def getDateFromSeconds(seconds):
return f'{current_day}, {date}'
- def __str__(self):
+ def __str__(self) -> str:
+ """Responsible for representing the class as a string
+
+ :returns: A string representation of the Response class instance
+
+ """
+
response = ""
response += self.statusLine() + "\n"
response += self.headerLine()
diff --git a/message/StatusCode.py b/message/StatusCode.py
index 95e9f63..d6227e8 100644
--- a/message/StatusCode.py
+++ b/message/StatusCode.py
@@ -1,6 +1,9 @@
import enum
+
class StatusCode(enum.Enum):
+ """Responsible for encapsulating the different http status codes"""
+
# Successful 2xx
OK = ["200", "OK"]
CREATED = ["201", "Created"]
diff --git a/methods/DELETE.py b/methods/DELETE.py
index ef78182..9c6a8c4 100644
--- a/methods/DELETE.py
+++ b/methods/DELETE.py
@@ -1,17 +1,19 @@
import enum
from databaseUser.HandlerDatabase import HandlerDatabase
from handler.HandlerErrors import HandlerErrors
+from message.Request import Request
from message.Response import Response
from message.StatusCode import StatusCode
+
class DELETE:
@staticmethod
- def response(request) -> str:
+ def response(request: Request) -> bytes:
""" Performs a removal when there is a DELETE request, returning a response with
the headers and the correct body. Removes the object within the database, based on the received ID.
:param request: Request object, containing the body and headers of that request
- :returns: The answer to this request
+ :returns: A byte object containing the response to this request
"""
try:
@@ -37,7 +39,7 @@ def response(request) -> str:
return HandlerErrors.sendErrorCode(request, StatusCode.BAD_REQUEST)
@staticmethod
- def getIdOfUrl(URI) -> int:
+ def getIdOfUrl(URI: str) -> int:
""" Return the id value present in the URL
:param URI: String of the url
diff --git a/methods/GET.py b/methods/GET.py
index 0a29f64..3ca3164 100644
--- a/methods/GET.py
+++ b/methods/GET.py
@@ -74,7 +74,7 @@ def response(request: Request) -> str:
return response.encodeResponse()
# Page images
elif request.URI in GET.imagesTable:
- response: Response = Response(status_code=StatusCode.OK, body={}, header={})
+ response: Response = Response(status_code=StatusCode.OK, body="", header={})
try:
GET.fill_image_params(response, GET.imagesTable, request.URI)
except Exception as e:
diff --git a/server.py b/server.py
index 152ef7a..48bcc43 100644
--- a/server.py
+++ b/server.py
@@ -1,8 +1,9 @@
from socket import *
from handler.HandlerRequests import Handler
+
class Server:
- def __init__(self, ip, port):
+ def __init__(self, ip, port) -> None:
""" Performs the creation of an object of type Server, in addition
will create a handler that will execute on a thread waiting for requests
pFad - Phonifier reborn
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.