193 lines
7.4 KiB
Python
193 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from typing import Any, Optional, Self, Sequence
|
|
from threading import Thread
|
|
from socket import (
|
|
socket as Socket,
|
|
AF_INET as ADDRESS_FAMILY_IPV4,
|
|
SOCK_STREAM as SOCKET_STREAM,
|
|
SOL_SOCKET as SOCKET_LAYER,
|
|
SO_REUSEADDR as SOCKET_REUSE_ADDRESS
|
|
)
|
|
import datetime
|
|
from Interfaces.Application.AnPInterface import AnPInterface
|
|
from Abstracts.HTTPServersAbstract import HTTPServersAbstract
|
|
from Models.RequestModel import RequestModel
|
|
from Utils.Common import Common
|
|
from Utils.Checks import Check
|
|
from Utils.Patterns import RE
|
|
|
|
class HTTPSocketServerDriver(HTTPServersAbstract):
|
|
|
|
def __get(self:Self, keys:str|Sequence[str], default:Any|None = None) -> Any|None:
|
|
|
|
real_keys:list[str] = []
|
|
key:str
|
|
|
|
for key in Common.get_keys(keys):
|
|
|
|
header:str
|
|
|
|
for header in (
|
|
"http_socket_server_driver",
|
|
"http_server_driver",
|
|
"http_server",
|
|
"http",
|
|
""
|
|
):
|
|
|
|
final_key:str = (header + "_" if header else "") + key
|
|
|
|
if final_key not in keys:
|
|
real_keys.append(final_key)
|
|
|
|
return self.anp.settings.get(real_keys, self._inputs, default)
|
|
|
|
def __init__(self:Self,
|
|
anp:AnPInterface,
|
|
key:str,
|
|
inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
|
|
) -> None:
|
|
|
|
self.__server:Socket|None = None
|
|
self.__thread:Thread|None = None
|
|
self.__working:bool = False
|
|
super().__init__(anp, key, inputs)
|
|
self.__maximum_connections:int = self.__get("maximum_connections", 5)
|
|
self.__cache_size:int = self.__get("cache_size", 1024)
|
|
self.__response_header:str = self.__get("response_header")
|
|
|
|
def __listen(self:Self) -> None:
|
|
while self.__working:
|
|
try:
|
|
|
|
client:Socket
|
|
address:str
|
|
port:int
|
|
data:bytes = b""
|
|
header:str
|
|
body:str
|
|
header_lines:list[str]
|
|
line:str
|
|
request:RequestModel = RequestModel()
|
|
get_block:str
|
|
hash_block:str
|
|
response:bytes
|
|
response_body:bytes
|
|
|
|
client, (address, port) = self.__server.accept()
|
|
|
|
while True:
|
|
|
|
buffer:bytes = client.recv(self.__cache_size)
|
|
|
|
data += buffer
|
|
if len(buffer) < self.__cache_size:
|
|
break
|
|
|
|
header, body = RE.DOUBLE_NEW_LINE.split(data.decode("utf-8", errors = "ignore"), 1)
|
|
header_lines = RE.NEW_LINE.split(header)
|
|
(
|
|
request.method,
|
|
request.path,
|
|
get_block,
|
|
hash_block,
|
|
request.protocol,
|
|
request.protocol_version
|
|
) = RE.HTTP_REQUEST.match(header_lines[0]).groups()
|
|
|
|
for line in header_lines[1:]:
|
|
if RE.HTTP_HEADER_PARAMETER.match(line):
|
|
|
|
key:str
|
|
value:str
|
|
|
|
key, value = RE.HTTP_HEADER_PARAMETER.match(line).groups()
|
|
key = key.strip().lower()
|
|
value = value.strip()
|
|
if key in request.request_headers:
|
|
if Check.is_array(request.request_headers[key]):
|
|
request.request_headers[key].append(value)
|
|
else:
|
|
request.request_headers[key] = [request.request_headers[key], value]
|
|
else:
|
|
request.request_headers[key] = value
|
|
|
|
request.get_variables = self.get_variables_from(get_block)
|
|
request.post_variables = self.get_variables_from(body)
|
|
request.cookies = self.load_cookies(request.request_headers.get("cookie"))
|
|
request.hash_variables = self.get_variables_from(hash_block)
|
|
|
|
self.anp.routes.go([self.key], request.method, request.path, request)
|
|
|
|
response_body = (
|
|
request.response if Check.is_binary(request.response) else
|
|
request.response.encode() if Check.is_string(request.response) else
|
|
str(request.response).encode())
|
|
response = Common.string_variables(self.__response_header, {
|
|
"content_length" : len(response_body),
|
|
"protocol" : request.get("protocol", request.protocol or self.protocol),
|
|
"protocol_version" : request.get("protocol_version", request.protocol_version or self.protocol_version),
|
|
"http_code" : request.response_code,
|
|
"http_message" : self.get_http_message(request.response_code),
|
|
"date" : self.format_datetime(datetime.datetime.now(datetime.timezone.utc)),
|
|
"last_modified" : self.format_datetime(request.last_modified or request.get("last_modified") or datetime.datetime.now(datetime.timezone.utc)),
|
|
"accept_range" : request.get("accept_range", self.accept_range),
|
|
"response_length" : len(response_body),
|
|
"access_control_max_age" : request.get("access_control_max_age", self.access_control_max_age),
|
|
"keep_alive_maximum" : request.get("keep_alive_maximum", self.keep_alive_maximum),
|
|
"keep_alive_timeout" : request.get("keep_alive_timeout", self.keep_alive_timeout),
|
|
"cors" : request.get("cors", self.cors),
|
|
"mime" : request.response_mime or request.get("mime", self.mime),
|
|
"charset" : request.response_charset or request.get("charset", self.charset)
|
|
}).encode() + response_body
|
|
|
|
client.sendall(response)
|
|
client.close()
|
|
|
|
except Exception as exception:
|
|
self.anp.exception(exception, "anp_http_socket_server_driver_run_exception", {
|
|
"key" : self.key,
|
|
"host" : self.host,
|
|
"port" : self.port
|
|
})
|
|
|
|
def start(self:Self) -> None:
|
|
|
|
self.__working = True
|
|
self.__server = Socket(ADDRESS_FAMILY_IPV4, SOCKET_STREAM)
|
|
|
|
try:
|
|
|
|
self.__server.setsockopt(SOCKET_LAYER, SOCKET_REUSE_ADDRESS, 1)
|
|
self.__server.bind((self.host, self.port))
|
|
self.__server.listen(self.__maximum_connections)
|
|
|
|
self.__thread = Thread(target = self.__listen)
|
|
self.__thread.start()
|
|
|
|
except Exception as exception:
|
|
self.anp.exception(exception, "anp_http_socket_server_driver_start_exception", {
|
|
"key" : self.key,
|
|
"host" : self.host,
|
|
"port" : self.port
|
|
})
|
|
|
|
def close(self:Self) -> None:
|
|
|
|
self.__working = False
|
|
|
|
if self.__server:
|
|
try:
|
|
self.__server.close()
|
|
except Exception as _:
|
|
pass
|
|
self.__server = None
|
|
|
|
if self.__thread:
|
|
try:
|
|
self.__thread.join()
|
|
except Exception as _:
|
|
pass
|
|
self.__thread = None |