-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial build, add basic HTTP, server, and tests
- Loading branch information
1 parent
a59ebb0
commit c4bd482
Showing
26 changed files
with
1,195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# | ||
# An example .env file. | ||
# With all module used keys. | ||
# | ||
|
||
HANDLER = MirrorHandler | ||
SERVER = SocketServer | ||
|
||
HOSTNAME = localhost | ||
PORT = 80 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import protoserver | ||
|
||
server = protoserver.ProtoServer() | ||
server.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from protoserver.config import * | ||
from protoserver.protoServer import * | ||
|
||
from protoserver import handlers | ||
from protoserver import http | ||
from protoserver import servers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from __future__ import annotations | ||
import dotenv | ||
from typing import Mapping, TypeVar, TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from protoserver.handlers.iHandler import IHandler | ||
from protoserver.servers.iServer import IServer | ||
|
||
|
||
_T = TypeVar("_T") | ||
|
||
class Config(object): | ||
|
||
MINIMAL_VALID_CONFIG_OPTIONS = { | ||
"HANDLER": "MirrorHandler", | ||
"SERVER": "FunctionServer", | ||
} | ||
|
||
_registeredHandlers: dict[str, type[IHandler]] = {} | ||
_registeredServers: dict[str, type[IServer]] = {} | ||
|
||
def __init__(self, data: Mapping[str, None | str]) -> None: | ||
self._config = data | ||
|
||
possibleHandlers = self._registeredHandlers.keys() | ||
if self._config["HANDLER"] not in possibleHandlers: | ||
raise ValueError(f"HANDLER not correctly set in .env.\nMust be one of:\n\t{', '.join(possibleHandlers)}") | ||
|
||
possibleServers = self._registeredServers.keys() | ||
if self._config["SERVER"] not in possibleServers: | ||
raise ValueError(f"SERVER not correctly set in .env.\nMust be one of:\n\t{', '.join(possibleServers)}") | ||
|
||
@classmethod | ||
def loadFromFile(cls, path = ".env") -> Config: | ||
return Config(dotenv.dotenv_values(path)) | ||
|
||
@classmethod | ||
def getMinimalValidConfig(cls, data: None | Mapping[str, None | str] = None) -> Config: | ||
if data is None: data = {} | ||
return Config(Config.MINIMAL_VALID_CONFIG_OPTIONS | data) | ||
|
||
|
||
def get(self, key: str, type: type[_T] = str) -> _T: | ||
if key not in self._config: | ||
raise KeyError(f"Unknown config option {key}.\nMaybe it wasn't in .exampleEnv?") | ||
|
||
value = self._config[key] | ||
if value is None: | ||
raise ValueError(f"{key} has no default value, and wasn't set in .env.") | ||
|
||
return type(value) | ||
|
||
def getHandler(self) -> type[IHandler]: | ||
handler = self.get("HANDLER") | ||
return self._registeredHandlers[handler] | ||
|
||
def getServer(self) -> type[IServer]: | ||
server = self.get("SERVER") | ||
return self._registeredServers[server] | ||
|
||
@classmethod | ||
def registerHandler(cls, name: str, handler: type[IHandler]) -> None: | ||
cls._registeredHandlers[name] = handler | ||
|
||
@classmethod | ||
def registerServer(cls, name: str, server: type[IServer]) -> None: | ||
cls._registeredServers[name] = server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from protoserver.handlers.iHandler import IHandler | ||
from protoserver.handlers.mirrorHandler import MirrorHandler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from __future__ import annotations | ||
from abc import ABC, abstractmethod | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from protoserver import Config | ||
from protoserver.http import Request, Response, StatusCode | ||
|
||
|
||
class IHandler(ABC): | ||
|
||
def __init__(self, config: Config) -> None: | ||
self.config = config | ||
|
||
@abstractmethod | ||
def handleRequest(self, request: Request) -> None | Response: | ||
raise NotImplementedError() | ||
|
||
@abstractmethod | ||
def handleError(self, statusCode: StatusCode) -> None | Response: | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from __future__ import annotations | ||
from typing import TYPE_CHECKING | ||
|
||
from protoserver.config import Config | ||
from protoserver.handlers.iHandler import IHandler | ||
from protoserver.http.response import Response | ||
|
||
if TYPE_CHECKING: | ||
from protoserver.http.request import Request | ||
from protoserver.http.statusCodes import StatusCode | ||
|
||
|
||
class MirrorHandler(IHandler): | ||
|
||
def handleRequest(self, request: Request) -> Response | None: | ||
response = Response() | ||
|
||
response.setBody(request.raw.decode("iso-8859-1")) | ||
|
||
return response | ||
|
||
def handleError(self, statusCode: StatusCode) -> Response | None: | ||
return super().handleError(statusCode) | ||
|
||
Config.registerHandler("MirrorHandler", MirrorHandler) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from protoserver.http.client import Client | ||
from protoserver.http.request import Request | ||
from protoserver.http.response import Response | ||
from protoserver.http.statusCodes import StatusCode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from __future__ import annotations | ||
from threading import Thread | ||
from typing import TYPE_CHECKING | ||
|
||
from protoserver.http.request import Request | ||
from protoserver.http.response import Response | ||
from protoserver.http.statusCodes import StatusCode | ||
|
||
if TYPE_CHECKING: | ||
from protoserver import Config | ||
from protoserver.handlers import IHandler | ||
from protoserver.servers import IConnection | ||
|
||
|
||
class Client(object): | ||
|
||
def __init__(self, config: Config, connection: IConnection, handler: IHandler) -> None: | ||
self.config = config | ||
self.connection = connection | ||
self.handler = handler | ||
|
||
self.isRunning = True | ||
self.thread = Thread(target = self.loop) | ||
self.thread.start() | ||
|
||
def loop(self) -> None: | ||
|
||
while self.isRunning: | ||
request = self.recv() | ||
if request is None: | ||
continue | ||
|
||
response = self.handler.handleRequest(request) | ||
if response is None: | ||
continue | ||
|
||
self.send(response) | ||
|
||
self.close() | ||
|
||
def recv(self) -> None | Request: | ||
requestBytes = self.connection.recv() | ||
if requestBytes == b"": | ||
self.isRunning = False | ||
return None | ||
|
||
request = Request(requestBytes) | ||
|
||
match (request.status): | ||
case "OK": | ||
return request | ||
case "BAD_REQUEST": | ||
response = self.handler.handleError(StatusCode.BAD_REQUEST) | ||
if response is not None: | ||
self.send(response) | ||
return None | ||
|
||
def send(self, response: Response) -> None: | ||
self.connection.send(response.build()) | ||
|
||
def close(self) -> None: | ||
self.connection.close() | ||
|
Oops, something went wrong.