This commit is contained in:
Izalia Mae 2022-04-22 20:29:30 -04:00
parent 216a779f22
commit a376236b17

View file

@ -7,16 +7,17 @@ import traceback
from functools import partial
from http_router import Router, MethodNotAllowed, NotFound
from izzylib import DotDict, Path, logging, signal_handler
from izzylib import DotDict, Path, convert_to_boolean, logging, signal_handler
from jinja2.exceptions import TemplateNotFound
from threading import Event, Thread
from . import http_methods, error, __file__ as module_root
from . import http_methods, error
from .config import Config
from .response import ServerResponse
#from .router import Router
from .view import Static, Manifest, Robots, Style
from .. import __file__ as module_root
from ..exceptions import MethodNotHandledException, NoBlueprintForPath
from ..utils import AsyncTransport
from ..template import Template
@ -27,7 +28,7 @@ except ImportError:
Database = NotImplementedError('Failed to import SQL database class')
frontend = Path(module_root).join('../../frontend').resolve()
frontend = Path(module_root).join('../frontend').resolve()
class ApplicationBase:
@ -190,6 +191,7 @@ class Application(ApplicationBase):
loop = asyncio.new_event_loop()
self.loop = loop
self.template = None
asyncio.set_event_loop(self.loop)
self.client = self.cfg.client_class(loop, *self.cfg.client_args, **self.cfg.client_kwargs)
@ -221,8 +223,9 @@ class Application(ApplicationBase):
self.add_view(Style)
self.add_static('/framework/static/', frontend.join('static'))
else:
self.template = None
def __repr__(self):
return f'Application({self.name}, listen={self.cfg.listen}, port={self.cfg.port})'
@property
@ -230,6 +233,15 @@ class Application(ApplicationBase):
return self._running.is_set()
@running.setter
def running(self, value):
if convert_to_boolean(value):
self._running.set()
else:
self._running.clear()
def add_task(self, state, callback, *args, **kwargs):
assert state in ['startup', 'shutdown']
assert asyncio.iscoroutinefunction(callback)
@ -278,65 +290,19 @@ class Application(ApplicationBase):
return self.template.render(*args, **kwargs)
async def create_task(self, log=True):
if self.cfg.socket:
if log:
logging.info(f'Starting server on {self.cfg.socket}')
return await asyncio.start_unix_server(
self.handle_client,
path = self.cfg.socket
)
else:
if log:
logging.info(f'Starting server on {self.cfg.listen}:{self.cfg.port}')
return await asyncio.start_server(
self.handle_client,
host = self.cfg.listen,
port = self.cfg.port,
family = socket.AF_INET,
reuse_address = True,
reuse_port = True
)
def run(self):
task = self.start()
while not task.done():
time.sleep(1)
def start(self, log=True):
if self._server:
return
if self.cfg.socket:
if log:
logging.info(f'Starting server on {self.cfg.socket}')
signal_handler(self.stop)
return asyncio.run(self.handle_run_server())
server = asyncio.start_unix_server(
self.handle_client,
path = self.cfg.socket
)
else:
if log:
logging.info(f'Starting server on {self.cfg.listen}:{self.cfg.port}')
server = asyncio.start_server(
self.handle_client,
host = self.cfg.listen,
port = self.cfg.port,
family = socket.AF_INET,
reuse_address = True,
reuse_port = True
)
def start(self):
if self._server:
return
signal_handler(self.stop)
self._server = self.loop.run_until_complete(server)
return asyncio.ensure_future(self.handle_run_server())
@ -345,19 +311,35 @@ class Application(ApplicationBase):
print('server not running')
return
self._running.clear()
#self._server.close()
self.loop.run_until_complete(self.handle_stop_server())
self.running = False
if self.cfg.sig_handler:
self.cfg.sig_handler(self, *self.cfg.sig_handler_args, **self.cfg.sig_handler_kwargs)
signal_handler(None)
async def handle_run_server(self):
self._running.set()
## Create _server object
if self.cfg.socket:
logging.info(f'Starting server on {self.cfg.socket}')
self._server = await asyncio.start_unix_server(
self.handle_client,
path = self.cfg.socket
)
else:
logging.info(f'Starting server on {self.cfg.listen}:{self.cfg.port}')
self._server = await asyncio.start_server(
self.handle_client,
host = self.cfg.listen,
port = self.cfg.port,
family = socket.AF_INET,
reuse_address = True,
reuse_port = True
)
self.running = True
## Run startup tasks
for callback, args, kwargs in self._callbacks['startup']:
@ -368,12 +350,14 @@ class Application(ApplicationBase):
while self._server.is_serving() and self.running:
await asyncio.sleep(0.1)
self._server.close()
await self._server.wait_closed()
except:
traceback.print_exc()
self._running.clear()
self.running = False
signal_handler(None)
## Run shutdown tasks
for callback, args, kwargs in self._callbacks['shutdown']:
@ -397,26 +381,6 @@ class Application(ApplicationBase):
logging.info('Server stopped')
async def handle_stop_server(self):
for callback, args, kwargs in self._callbacks['shutdown']:
if asyncio.iscoroutinefunction(callback):
await asyncio.wait_for(callback(self, *args, **kwargs), 10)
else:
callback(self, *args, **kwargs)
for task in self._tasks:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
self._tasks = []
async def handle_client(self, reader, writer):
transport = AsyncTransport(reader, writer, self.cfg.timeout)
request = None
@ -471,7 +435,7 @@ class Application(ApplicationBase):
response.headers.update(self.cfg.default_headers)
await transport.write(response.compile())
if request and request.log and not request.path.startswith('/framework'):
if self.cfg.access_log and request and request.log and not request.path.startswith('/framework'):
logging.info(f'{request.remote} {request.method} {request.path} {response.status} {len(response.body)} {request.agent}')
except: