Merge remote-tracking branch 'origin/dev' into feature/1924-Agent-Interface
This commit is contained in:
@@ -155,10 +155,10 @@ class DatabaseService(Service):
|
||||
if self.password == password:
|
||||
status_code = 200 # ok
|
||||
self.connections[session_id] = datetime.now()
|
||||
self.sys_log.info(f"Connect request for {session_id=} authorised")
|
||||
self.sys_log.info(f"{self.name}: Connect request for {session_id=} authorised")
|
||||
else:
|
||||
status_code = 401 # Unauthorised
|
||||
self.sys_log.info(f"Connect request for {session_id=} declined")
|
||||
self.sys_log.info(f"{self.name}: Connect request for {session_id=} declined")
|
||||
else:
|
||||
status_code = 404 # service not found
|
||||
return {"status_code": status_code, "type": "connect_response", "response": status_code == 200}
|
||||
|
||||
@@ -72,19 +72,25 @@ class DNSClient(Service):
|
||||
:param: session_id: The Session ID the payload is to originate from. Optional.
|
||||
:param: is_reattempt: Checks if the request has been reattempted. Default is False.
|
||||
"""
|
||||
# check if DNS server is configured
|
||||
if self.dns_server is None:
|
||||
self.sys_log.error(f"{self.name}: DNS Server is not configured")
|
||||
return False
|
||||
|
||||
# check if the target domain is in the client's DNS cache
|
||||
payload = DNSPacket(dns_request=DNSRequest(domain_name_request=target_domain))
|
||||
|
||||
# check if the domain is already in the DNS cache
|
||||
if target_domain in self.dns_cache:
|
||||
self.sys_log.info(
|
||||
f"DNS Client: Domain lookup for {target_domain} successful, resolves to {self.dns_cache[target_domain]}"
|
||||
f"{self.name}: Domain lookup for {target_domain} successful,"
|
||||
f"resolves to {self.dns_cache[target_domain]}"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
# return False if already reattempted
|
||||
if is_reattempt:
|
||||
self.sys_log.info(f"DNS Client: Domain lookup for {target_domain} failed")
|
||||
self.sys_log.info(f"{self.name}: Domain lookup for {target_domain} failed")
|
||||
return False
|
||||
else:
|
||||
# send a request to check if domain name exists in the DNS Server
|
||||
@@ -104,14 +110,13 @@ class DNSClient(Service):
|
||||
self,
|
||||
payload: DNSPacket,
|
||||
session_id: Optional[str] = None,
|
||||
dest_ip_address: Optional[IPv4Address] = None,
|
||||
dest_port: Optional[Port] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Sends a payload to the SessionManager.
|
||||
|
||||
The specifics of how the payload is processed and whether a response payload
|
||||
is generated should be implemented in subclasses.
|
||||
|
||||
:param payload: The payload to be sent.
|
||||
:param dest_ip_address: The ip address of the payload destination.
|
||||
:param dest_port: The port of the payload destination.
|
||||
@@ -119,10 +124,11 @@ class DNSClient(Service):
|
||||
|
||||
:return: True if successful, False otherwise.
|
||||
"""
|
||||
# create DNS request packet
|
||||
software_manager: SoftwareManager = self.software_manager
|
||||
software_manager.send_payload_to_session_manager(payload=payload, session_id=session_id)
|
||||
return True
|
||||
self.sys_log.info(f"{self.name}: Sending DNS request to resolve {payload.dns_request.domain_name_request}")
|
||||
|
||||
return super().send(
|
||||
payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, **kwargs
|
||||
)
|
||||
|
||||
def receive(
|
||||
self,
|
||||
@@ -133,9 +139,6 @@ class DNSClient(Service):
|
||||
"""
|
||||
Receives a payload from the SessionManager.
|
||||
|
||||
The specifics of how the payload is processed and whether a response payload
|
||||
is generated should be implemented in subclasses.
|
||||
|
||||
:param payload: The payload to be sent.
|
||||
:param session_id: The Session ID the payload is to originate from. Optional.
|
||||
:return: True if successful, False otherwise.
|
||||
@@ -144,12 +147,16 @@ class DNSClient(Service):
|
||||
if not isinstance(payload, DNSPacket):
|
||||
_LOGGER.debug(f"{payload} is not a DNSPacket")
|
||||
return False
|
||||
# cast payload into a DNS packet
|
||||
payload: DNSPacket = payload
|
||||
|
||||
if payload.dns_reply is not None:
|
||||
# add the IP address to the client cache
|
||||
if payload.dns_reply.domain_name_ip_address:
|
||||
self.sys_log.info(
|
||||
f"{self.name}: Resolved domain name {payload.dns_request.domain_name_request} "
|
||||
f"to {payload.dns_reply.domain_name_ip_address}"
|
||||
)
|
||||
self.dns_cache[payload.dns_request.domain_name_request] = payload.dns_reply.domain_name_ip_address
|
||||
return True
|
||||
|
||||
self.sys_log.error(f"Failed to resolve domain name {payload.dns_request.domain_name_request}")
|
||||
return False
|
||||
|
||||
@@ -96,13 +96,13 @@ class DNSServer(Service):
|
||||
payload: DNSPacket = payload
|
||||
if payload.dns_request is not None:
|
||||
self.sys_log.info(
|
||||
f"DNS Server: Received domain lookup request for {payload.dns_request.domain_name_request} "
|
||||
f"{self.name}: Received domain lookup request for {payload.dns_request.domain_name_request} "
|
||||
f"from session {session_id}"
|
||||
)
|
||||
# generate a reply with the correct DNS IP address
|
||||
payload = payload.generate_reply(self.dns_lookup(payload.dns_request.domain_name_request))
|
||||
self.sys_log.info(
|
||||
f"DNS Server: Responding to domain lookup request for {payload.dns_request.domain_name_request} "
|
||||
f"{self.name}: Responding to domain lookup request for {payload.dns_request.domain_name_request} "
|
||||
f"with ip address: {payload.dns_reply.domain_name_ip_address}"
|
||||
)
|
||||
# send reply
|
||||
|
||||
@@ -39,9 +39,12 @@ class FTPClient(FTPServiceABC):
|
||||
"""
|
||||
# if client service is down, return error
|
||||
if self.operating_state != ServiceOperatingState.RUNNING:
|
||||
self.sys_log.error("FTP Client is not running")
|
||||
payload.status_code = FTPStatusCode.ERROR
|
||||
return payload
|
||||
|
||||
self.sys_log.info(f"{self.name}: Received FTP {payload.ftp_command.name} {payload.ftp_command_args}")
|
||||
|
||||
# process client specific commands, otherwise call super
|
||||
return super()._process_ftp_command(payload=payload, session_id=session_id, **kwargs)
|
||||
|
||||
@@ -49,6 +52,7 @@ class FTPClient(FTPServiceABC):
|
||||
self,
|
||||
dest_ip_address: Optional[IPv4Address] = None,
|
||||
dest_port: Optional[Port] = Port.FTP,
|
||||
session_id: Optional[str] = None,
|
||||
is_reattempt: Optional[bool] = False,
|
||||
) -> bool:
|
||||
"""
|
||||
@@ -72,20 +76,30 @@ class FTPClient(FTPServiceABC):
|
||||
ftp_command=FTPCommand.PORT,
|
||||
ftp_command_args=Port.FTP,
|
||||
)
|
||||
software_manager: SoftwareManager = self.software_manager
|
||||
software_manager.send_payload_to_session_manager(
|
||||
payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port
|
||||
)
|
||||
|
||||
if payload.status_code == FTPStatusCode.OK:
|
||||
return True
|
||||
else:
|
||||
if is_reattempt:
|
||||
# reattempt failed
|
||||
return False
|
||||
if self.send(payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id):
|
||||
if payload.status_code == FTPStatusCode.OK:
|
||||
self.sys_log.info(
|
||||
f"{self.name}: Successfully connected to FTP Server "
|
||||
f"{dest_ip_address} via port {payload.ftp_command_args.value}"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
# try again
|
||||
self._connect_to_server(dest_ip_address=dest_ip_address, dest_port=dest_port, is_reattempt=True)
|
||||
if is_reattempt:
|
||||
# reattempt failed
|
||||
self.sys_log.info(
|
||||
f"{self.name}: Unable to connect to FTP Server "
|
||||
f"{dest_ip_address} via port {payload.ftp_command_args.value}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
# try again
|
||||
self._connect_to_server(
|
||||
dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, is_reattempt=True
|
||||
)
|
||||
else:
|
||||
self.sys_log.error(f"{self.name}: Unable to send FTPPacket")
|
||||
return False
|
||||
|
||||
def _disconnect_from_server(
|
||||
self, dest_ip_address: Optional[IPv4Address] = None, dest_port: Optional[Port] = Port.FTP
|
||||
@@ -119,6 +133,7 @@ class FTPClient(FTPServiceABC):
|
||||
dest_folder_name: str,
|
||||
dest_file_name: str,
|
||||
dest_port: Optional[Port] = Port.FTP,
|
||||
session_id: Optional[str] = None,
|
||||
real_file_path: Optional[str] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
@@ -144,6 +159,9 @@ class FTPClient(FTPServiceABC):
|
||||
|
||||
:param: dest_port: The open port of the machine that hosts the FTP Server. Default is Port.FTP.
|
||||
:type: dest_port: Optional[Port]
|
||||
|
||||
:param: session_id: The id of the session
|
||||
:type: session_id: Optional[str]
|
||||
"""
|
||||
# check if the file to transfer exists on the client
|
||||
file_to_transfer: File = self.file_system.get_file(folder_name=src_folder_name, file_name=src_file_name)
|
||||
@@ -152,10 +170,7 @@ class FTPClient(FTPServiceABC):
|
||||
return False
|
||||
|
||||
# check if FTP is currently connected to IP
|
||||
self.connected = self._connect_to_server(
|
||||
dest_ip_address=dest_ip_address,
|
||||
dest_port=dest_port,
|
||||
)
|
||||
self.connected = self._connect_to_server(dest_ip_address=dest_ip_address, dest_port=dest_port)
|
||||
|
||||
if not self.connected:
|
||||
return False
|
||||
@@ -206,10 +221,7 @@ class FTPClient(FTPServiceABC):
|
||||
:type: dest_port: Optional[Port]
|
||||
"""
|
||||
# check if FTP is currently connected to IP
|
||||
self.connected = self._connect_to_server(
|
||||
dest_ip_address=dest_ip_address,
|
||||
dest_port=dest_port,
|
||||
)
|
||||
self.connected = self._connect_to_server(dest_ip_address=dest_ip_address, dest_port=dest_port)
|
||||
|
||||
if not self.connected:
|
||||
return False
|
||||
@@ -232,10 +244,10 @@ class FTPClient(FTPServiceABC):
|
||||
|
||||
# the payload should have ok status code
|
||||
if payload.status_code == FTPStatusCode.OK:
|
||||
self.sys_log.info(f"File {src_folder_name}/{src_file_name} found in FTP server.")
|
||||
self.sys_log.info(f"{self.name}: File {src_folder_name}/{src_file_name} found in FTP server.")
|
||||
return True
|
||||
else:
|
||||
self.sys_log.error(f"File {src_folder_name}/{src_file_name} does not exist in FTP server")
|
||||
self.sys_log.error(f"{self.name}: File {src_folder_name}/{src_file_name} does not exist in FTP server")
|
||||
return False
|
||||
|
||||
def receive(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> bool:
|
||||
@@ -252,5 +264,14 @@ class FTPClient(FTPServiceABC):
|
||||
self.sys_log.error(f"{payload} is not an FTP packet")
|
||||
return False
|
||||
|
||||
"""
|
||||
Ignore ftp payload if status code is None.
|
||||
|
||||
This helps prevent an FTP request loop - FTP client and servers can exist on
|
||||
the same node.
|
||||
"""
|
||||
if payload.status_code is None:
|
||||
return False
|
||||
|
||||
self._process_ftp_command(payload=payload, session_id=session_id)
|
||||
return True
|
||||
|
||||
@@ -4,7 +4,6 @@ from typing import Any, Dict, Optional
|
||||
from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.core.session_manager import Session
|
||||
from primaite.simulator.system.services.ftp.ftp_service import FTPServiceABC
|
||||
from primaite.simulator.system.services.service import ServiceOperatingState
|
||||
|
||||
@@ -30,14 +29,6 @@ class FTPServer(FTPServiceABC):
|
||||
super().__init__(**kwargs)
|
||||
self.start()
|
||||
|
||||
def _get_session_details(self, session_id: str) -> Session:
|
||||
"""
|
||||
Returns the Session object from the given session id.
|
||||
|
||||
:param: session_id: ID of the session that needs details retrieved
|
||||
"""
|
||||
return self.software_manager.session_manager.sessions_by_uuid[session_id]
|
||||
|
||||
def _process_ftp_command(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> FTPPacket:
|
||||
"""
|
||||
Process the command in the FTP Packet.
|
||||
@@ -47,11 +38,16 @@ class FTPServer(FTPServiceABC):
|
||||
:param: session_id: session ID linked to the FTP Packet. Optional.
|
||||
:type: session_id: Optional[str]
|
||||
"""
|
||||
# error code by default
|
||||
payload.status_code = FTPStatusCode.ERROR
|
||||
|
||||
# if server service is down, return error
|
||||
if self.operating_state != ServiceOperatingState.RUNNING:
|
||||
payload.status_code = FTPStatusCode.ERROR
|
||||
self.sys_log.error("FTP Server not running")
|
||||
return payload
|
||||
|
||||
self.sys_log.info(f"{self.name}: Received FTP {payload.ftp_command.name} {payload.ftp_command_args}")
|
||||
|
||||
if session_id:
|
||||
session_details = self._get_session_details(session_id)
|
||||
|
||||
@@ -67,9 +63,13 @@ class FTPServer(FTPServiceABC):
|
||||
payload.status_code = FTPStatusCode.OK
|
||||
return payload
|
||||
|
||||
self.sys_log.error(f"Invalid Port {payload.ftp_command_args}")
|
||||
return payload
|
||||
|
||||
if payload.ftp_command == FTPCommand.QUIT:
|
||||
self.connections.pop(session_id)
|
||||
payload.status_code = FTPStatusCode.OK
|
||||
return payload
|
||||
|
||||
return super()._process_ftp_command(payload=payload, session_id=session_id, **kwargs)
|
||||
|
||||
@@ -79,5 +79,15 @@ class FTPServer(FTPServiceABC):
|
||||
self.sys_log.error(f"{payload} is not an FTP packet")
|
||||
return False
|
||||
|
||||
"""
|
||||
Ignore ftp payload if status code is defined.
|
||||
|
||||
This means that an FTP server has already handled the packet and
|
||||
prevents an FTP request loop - FTP client and servers can exist on
|
||||
the same node.
|
||||
"""
|
||||
if payload.status_code is not None:
|
||||
return False
|
||||
|
||||
self.send(self._process_ftp_command(payload=payload, session_id=session_id), session_id)
|
||||
return True
|
||||
|
||||
@@ -6,7 +6,6 @@ from typing import Optional
|
||||
from primaite.simulator.file_system.file_system import File
|
||||
from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.core.software_manager import SoftwareManager
|
||||
from primaite.simulator.system.services.service import Service
|
||||
|
||||
|
||||
@@ -58,7 +57,7 @@ class FTPServiceABC(Service, ABC):
|
||||
file_name=file_name, folder_name=folder_name, size=file_size, real=is_real
|
||||
)
|
||||
self.sys_log.info(
|
||||
f"Created item in {self.sys_log.hostname}: {payload.ftp_command_args['dest_folder_name']}/"
|
||||
f"{self.name}: Created item in {self.sys_log.hostname}: {payload.ftp_command_args['dest_folder_name']}/"
|
||||
f"{payload.ftp_command_args['dest_file_name']}"
|
||||
)
|
||||
if is_real:
|
||||
@@ -77,6 +76,7 @@ class FTPServiceABC(Service, ABC):
|
||||
dest_ip_address: Optional[IPv4Address] = None,
|
||||
dest_port: Optional[Port] = None,
|
||||
session_id: Optional[str] = None,
|
||||
is_response: bool = False,
|
||||
) -> bool:
|
||||
"""
|
||||
Sends data from the host FTP Service's machine to another FTP Service's host machine.
|
||||
@@ -98,6 +98,9 @@ class FTPServiceABC(Service, ABC):
|
||||
|
||||
:param: session_id: session ID linked to the FTP Packet. Optional.
|
||||
:type: session_id: Optional[str]
|
||||
|
||||
:param: is_response: is true if the data being sent is in response to a request. Default False.
|
||||
:type: is_response: bool
|
||||
"""
|
||||
# send STOR request
|
||||
payload: FTPPacket = FTPPacket(
|
||||
@@ -109,13 +112,14 @@ class FTPServiceABC(Service, ABC):
|
||||
"real_file_path": file.sim_path if file.real else None,
|
||||
},
|
||||
packet_payload_size=file.sim_size,
|
||||
status_code=FTPStatusCode.OK if is_response else None,
|
||||
)
|
||||
software_manager: SoftwareManager = self.software_manager
|
||||
software_manager.send_payload_to_session_manager(
|
||||
self.sys_log.info(f"{self.name}: Sending file {file.folder.name}/{file.name}")
|
||||
response = self.send(
|
||||
payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id
|
||||
)
|
||||
|
||||
if payload.status_code == FTPStatusCode.OK:
|
||||
if response and payload.status_code == FTPStatusCode.OK:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -149,7 +153,32 @@ class FTPServiceABC(Service, ABC):
|
||||
dest_file_name=dest_file_name,
|
||||
dest_folder_name=dest_folder_name,
|
||||
session_id=session_id,
|
||||
is_response=True,
|
||||
)
|
||||
except Exception as e:
|
||||
self.sys_log.error(f"Unable to retrieve file from {self.sys_log.hostname}: {e}")
|
||||
return False
|
||||
|
||||
def send(
|
||||
self,
|
||||
payload: FTPPacket,
|
||||
session_id: Optional[str] = None,
|
||||
dest_ip_address: Optional[IPv4Address] = None,
|
||||
dest_port: Optional[Port] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Sends a payload to the SessionManager.
|
||||
|
||||
:param payload: The payload to be sent.
|
||||
:param dest_ip_address: The ip address of the payload destination.
|
||||
:param dest_port: The port of the payload destination.
|
||||
:param session_id: The Session ID the payload is to originate from. Optional.
|
||||
|
||||
:return: True if successful, False otherwise.
|
||||
"""
|
||||
self.sys_log.info(f"{self.name}: Sending FTP {payload.ftp_command.name} {payload.ftp_command_args}")
|
||||
|
||||
return super().send(
|
||||
payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, **kwargs
|
||||
)
|
||||
|
||||
@@ -33,12 +33,14 @@ class DataManipulationBot(DatabaseClient):
|
||||
self.server_ip_address = server_ip_address
|
||||
self.payload = payload
|
||||
self.server_password = server_password
|
||||
self.sys_log.info(f"Configured the {self.name} with {server_ip_address=}, {payload=}, {server_password=}.")
|
||||
self.sys_log.info(
|
||||
f"{self.name}: Configured the {self.name} with {server_ip_address=}, {payload=}, {server_password=}."
|
||||
)
|
||||
|
||||
def run(self):
|
||||
"""Run the DataManipulationBot."""
|
||||
if self.server_ip_address and self.payload:
|
||||
self.sys_log.info(f"Attempting to start the {self.name}")
|
||||
self.sys_log.info(f"{self.name}: Attempting to start the {self.name}")
|
||||
super().run()
|
||||
if not self.connected:
|
||||
self.connect()
|
||||
@@ -46,4 +48,4 @@ class DataManipulationBot(DatabaseClient):
|
||||
self.query(self.payload)
|
||||
self.sys_log.info(f"{self.name} payload delivered: {self.payload}")
|
||||
else:
|
||||
self.sys_log.error(f"Failed to start the {self.name} as it requires both a target_io_address and payload.")
|
||||
self.sys_log.error(f"Failed to start the {self.name} as it requires both a target_ip_address and payload.")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Dict, Optional
|
||||
|
||||
from primaite import getLogger
|
||||
from primaite.simulator.core import RequestManager, RequestType
|
||||
@@ -72,45 +72,6 @@ class Service(IOSoftware):
|
||||
"""
|
||||
pass
|
||||
|
||||
def send(
|
||||
self,
|
||||
payload: Any,
|
||||
session_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Sends a payload to the SessionManager.
|
||||
|
||||
The specifics of how the payload is processed and whether a response payload
|
||||
is generated should be implemented in subclasses.
|
||||
|
||||
:param: payload: The payload to send.
|
||||
:param: session_id: The id of the session
|
||||
|
||||
:return: True if successful, False otherwise.
|
||||
"""
|
||||
self.software_manager.send_payload_to_session_manager(payload=payload, session_id=session_id)
|
||||
|
||||
def receive(
|
||||
self,
|
||||
payload: Any,
|
||||
session_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Receives a payload from the SessionManager.
|
||||
|
||||
The specifics of how the payload is processed and whether a response payload
|
||||
is generated should be implemented in subclasses.
|
||||
|
||||
:param: payload: The payload to send.
|
||||
:param: session_id: The id of the session
|
||||
|
||||
:return: True if successful, False otherwise.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Stop the service."""
|
||||
if self.operating_state in [ServiceOperatingState.RUNNING, ServiceOperatingState.PAUSED]:
|
||||
|
||||
145
src/primaite/simulator/system/services/web_server/web_server.py
Normal file
145
src/primaite/simulator/system/services/web_server/web_server.py
Normal file
@@ -0,0 +1,145 @@
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from primaite.simulator.network.protocols.http import (
|
||||
HttpRequestMethod,
|
||||
HttpRequestPacket,
|
||||
HttpResponsePacket,
|
||||
HttpStatusCode,
|
||||
)
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.applications.database_client import DatabaseClient
|
||||
from primaite.simulator.system.services.service import Service
|
||||
|
||||
|
||||
class WebServer(Service):
|
||||
"""Class used to represent a Web Server Service in simulation."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["name"] = "WebServer"
|
||||
kwargs["protocol"] = IPProtocol.TCP
|
||||
# default for web is port 80
|
||||
if kwargs.get("port") is None:
|
||||
kwargs["port"] = Port.HTTP
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self._install_web_files()
|
||||
self.start()
|
||||
|
||||
def _install_web_files(self):
|
||||
"""
|
||||
Installs the files hosted by the web service.
|
||||
|
||||
This is usually HTML, CSS, JS or PHP files requested by browsers to display the webpage.
|
||||
"""
|
||||
# index HTML main file
|
||||
self.file_system.create_file(file_name="index.html", folder_name="primaite")
|
||||
|
||||
def _process_http_request(self, payload: HttpRequestPacket, session_id: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Parse the HttpRequestPacket.
|
||||
|
||||
:param: payload: Payload containing th HttpRequestPacket
|
||||
:type: payload: HttpRequestPacket
|
||||
|
||||
:param: session_id: Session id of the http request
|
||||
:type: session_id: Optional[str]
|
||||
"""
|
||||
response = HttpResponsePacket()
|
||||
|
||||
self.sys_log.info(f"{self.name}: Received HTTP {payload.request_method.name} {payload.request_url}")
|
||||
|
||||
# check the type of HTTP request
|
||||
if payload.request_method == HttpRequestMethod.GET:
|
||||
response = self._handle_get_request(payload=payload)
|
||||
|
||||
elif payload.request_method == HttpRequestMethod.POST:
|
||||
pass
|
||||
|
||||
else:
|
||||
# send a method not allowed response
|
||||
response.status_code = HttpStatusCode.METHOD_NOT_ALLOWED
|
||||
|
||||
# send response to web client
|
||||
self.send(payload=response, session_id=session_id)
|
||||
|
||||
# return true if response is OK
|
||||
return response.status_code == HttpStatusCode.OK
|
||||
|
||||
def _handle_get_request(self, payload: HttpRequestPacket) -> HttpResponsePacket:
|
||||
"""
|
||||
Handle a GET HTTP request.
|
||||
|
||||
:param: payload: HTTP request payload
|
||||
:type: payload: HttpRequestPacket
|
||||
"""
|
||||
response = HttpResponsePacket(status_code=HttpStatusCode.NOT_FOUND, payload=payload)
|
||||
try:
|
||||
parsed_url = urlparse(payload.request_url)
|
||||
path = parsed_url.path.strip("/")
|
||||
|
||||
if len(path) < 1:
|
||||
# query succeeded
|
||||
response.status_code = HttpStatusCode.OK
|
||||
|
||||
if path.startswith("users"):
|
||||
# get data from DatabaseServer
|
||||
db_client: DatabaseClient = self.software_manager.software["DatabaseClient"]
|
||||
# get all users
|
||||
if db_client.query("SELECT * FROM user;"):
|
||||
# query succeeded
|
||||
response.status_code = HttpStatusCode.OK
|
||||
|
||||
return response
|
||||
except Exception:
|
||||
# something went wrong on the server
|
||||
response.status_code = HttpStatusCode.INTERNAL_SERVER_ERROR
|
||||
return response
|
||||
|
||||
def send(
|
||||
self,
|
||||
payload: HttpResponsePacket,
|
||||
session_id: Optional[str] = None,
|
||||
dest_ip_address: Optional[IPv4Address] = None,
|
||||
dest_port: Optional[Port] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Sends a payload to the SessionManager.
|
||||
|
||||
The specifics of how the payload is processed and whether a response payload
|
||||
is generated should be implemented in subclasses.
|
||||
|
||||
:param: payload: The payload to send.
|
||||
:param: session_id: The id of the session
|
||||
:param dest_ip_address: The ip address of the payload destination.
|
||||
:param dest_port: The port of the payload destination.
|
||||
|
||||
:return: True if successful, False otherwise.
|
||||
"""
|
||||
self.sys_log.info(f"{self.name}: Sending HTTP Response {payload.status_code}")
|
||||
|
||||
return super().send(
|
||||
payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, **kwargs
|
||||
)
|
||||
|
||||
def receive(
|
||||
self,
|
||||
payload: Any,
|
||||
session_id: Optional[str] = None,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
Receives a payload from the SessionManager.
|
||||
|
||||
:param: payload: The payload to send.
|
||||
:param: session_id: The id of the session. Optional.
|
||||
"""
|
||||
# check if the payload is an HTTPPacket
|
||||
if not isinstance(payload, HttpRequestPacket):
|
||||
self.sys_log.error("Payload is not an HTTPPacket")
|
||||
return False
|
||||
|
||||
return self._process_http_request(payload=payload, session_id=session_id)
|
||||
Reference in New Issue
Block a user