Skip to content

Commit

Permalink
WIP Server class to start applications
Browse files Browse the repository at this point in the history
  • Loading branch information
cecton committed Nov 5, 2017
1 parent b30037b commit 641b49f
Showing 1 changed file with 148 additions and 99 deletions.
247 changes: 148 additions & 99 deletions aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from .web_protocol import * # noqa
from .web_request import * # noqa
from .web_response import * # noqa
from .web_server import Server
from .web_server import Server as WebServer
from .web_urldispatcher import * # noqa
from .web_urldispatcher import PrefixedSubAppResource
from .web_ws import * # noqa
Expand Down Expand Up @@ -244,8 +244,8 @@ def make_handler(self, *, loop=None,

if secure_proxy_ssl_header:
self._secure_proxy_ssl_header = secure_proxy_ssl_header
return Server(self._handle, request_factory=self._make_request,
loop=self.loop, **kwargs)
return WebServer(self._handle, request_factory=self._make_request,
loop=self.loop, **kwargs)

@asyncio.coroutine
def startup(self):
Expand Down Expand Up @@ -318,101 +318,156 @@ def __repr__(self):
return "<Application 0x{:x}>".format(id(self))


def run_app(app, *, host=None, port=None, path=None, sock=None,
shutdown_timeout=60.0, ssl_context=None,
print=print, backlog=128, access_log_format=None,
access_log=access_logger, loop=None):
"""Run an app locally"""
user_supplied_loop = loop is not None
if loop is None:
loop = asyncio.get_event_loop()
class Server:
def __init__(self, *, host=None, port=None, path=None, sock=None,
shutdown_timeout=60.0, ssl_context=None, backlog=128,
access_log_format=None, access_log=access_logger, loop=None):
if loop is None:
loop = asyncio.get_event_loop()
self.loop = loop
self._app = app = Application()
make_handler_kwargs = dict()
if access_log_format is not None:
make_handler_kwargs['access_log_format'] = access_log_format
self._handler = app.make_handler(loop=loop, access_log=access_log,
**make_handler_kwargs)

self.shutdown_timeout = shutdown_timeout
self.scheme = scheme = 'https' if ssl_context else 'http'
self.ssl_context = ssl_context
self.backlog = backlog
self.access_log_format = access_log_format
self.access_log = access_log
self.base_url = URL('{}://localhost'.format(scheme)).with_port(port)

if path is None:
self.paths = ()
elif isinstance(path, (str, bytes, bytearray, memoryview))\
or not isinstance(path, Iterable):
self.paths = (path,)
else:
self.paths = path

make_handler_kwargs = dict()
if access_log_format is not None:
make_handler_kwargs['access_log_format'] = access_log_format
handler = app.make_handler(loop=loop, access_log=access_log,
**make_handler_kwargs)

loop.run_until_complete(app.startup())

scheme = 'https' if ssl_context else 'http'
base_url = URL('{}://localhost'.format(scheme)).with_port(port)

if path is None:
paths = ()
elif isinstance(path, (str, bytes, bytearray, memoryview))\
or not isinstance(path, Iterable):
paths = (path,)
else:
paths = path

if sock is None:
socks = ()
elif not isinstance(sock, Iterable):
socks = (sock,)
else:
socks = sock

if host is None:
if (paths or socks) and not port:
hosts = ()
if sock is None:
self.socks = ()
elif not isinstance(sock, Iterable):
self.socks = (sock,)
else:
self.socks = sock

if host is None:
if (self.paths or self.socks) and not port:
self.hosts = ()
else:
self.hosts = ("0.0.0.0",)
elif isinstance(host, (str, bytes, bytearray, memoryview))\
or not isinstance(host, Iterable):
self.hosts = (host,)
else:
hosts = ("0.0.0.0",)
elif isinstance(host, (str, bytes, bytearray, memoryview))\
or not isinstance(host, Iterable):
hosts = (host,)
else:
hosts = host

if hosts and port is None:
port = 8443 if ssl_context else 8080

server_creations = []
uris = [str(base_url.with_host(host)) for host in hosts]
if hosts:
# Multiple hosts bound to same server is available in most loop
# implementations, but only send multiple if we have multiple.
host_binding = hosts[0] if len(hosts) == 1 else hosts
server_creations.append(
loop.create_server(
handler, host_binding, port, ssl=ssl_context, backlog=backlog
self.hosts = host

if self.hosts and port is None:
self.port = 8443 if ssl_context else 8080

self._apps = []
self._servers = None

def register(self, app, *, prefix="/"):
self._apps.append(app)
return self._app.add_subapp(prefix, app)

def _create_servers(self, handler):
server_creations = []

uris = [str(self.base_url.with_host(host)) for host in self.hosts]
if self.hosts:
# Multiple hosts bound to same server is available in most loop
# implementations, but only send multiple if we have multiple.
host_binding = (
self.hosts[0] if len(self.hosts) == 1 else self.hosts
)
)
for path in paths:
# Most loop implementations don't support multiple paths bound in same
# server, so create a server for each.
server_creations.append(
loop.create_unix_server(
handler, path, ssl=ssl_context, backlog=backlog
server_creations.append(
self.loop.create_server(
handler, host_binding, self.port, ssl=self.ssl_context,
backlog=self.backlog
)
)
)
uris.append('{}://unix:{}:'.format(scheme, path))

# Clean up prior socket path if stale and not abstract.
# CPython 3.5.3+'s event loop already does this. See
# https://github.com/python/asyncio/issues/425
if path[0] not in (0, '\x00'): # pragma: no branch
try:
if stat.S_ISSOCK(os.stat(path).st_mode):
os.remove(path)
except FileNotFoundError:
pass
for sock in socks:
server_creations.append(
loop.create_server(
handler, sock=sock, ssl=ssl_context, backlog=backlog

for path in self.paths:
# Most loop implementations don't support multiple paths bound in
# same server, so create a server for each.
server_creations.append(
self.loop.create_unix_server(
handler, path, ssl=self.ssl_context, backlog=self.backlog
)
)
uris.append('{}://unix:{}:'.format(self.scheme, path))

# Clean up prior socket path if stale and not abstract.
# CPython 3.5.3+'s event loop already does this. See
# https://github.com/python/asyncio/issues/425
if path[0] not in (0, '\x00'): # pragma: no branch
try:
if stat.S_ISSOCK(os.stat(path).st_mode):
os.remove(path)
except FileNotFoundError:
pass

for sock in self.socks:
server_creations.append(
self.loop.create_server(
handler, sock=sock, ssl=self.ssl_context,
backlog=self.backlog
)
)
)

if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX:
uris.append('{}://unix:{}:'.format(scheme, sock.getsockname()))
else:
host, port = sock.getsockname()
uris.append(str(base_url.with_host(host).with_port(port)))
if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX:
uris.append('{}://unix:{}:'.format(self.scheme,
sock.getsockname()))
else:
host, port = sock.getsockname()
uris.append(str(self.base_url.with_host(host).with_port(port)))

servers = loop.run_until_complete(
asyncio.gather(*server_creations, loop=loop)
)
self.uris = uris
return asyncio.gather(*server_creations, loop=self.loop)

@asyncio.coroutine
def start(self):
for app in self._apps:
yield from app.startup()

self._servers = yield from self._create_servers(self._handler)

return self.uris

@asyncio.coroutine
def stop(self):
server_closures = []
for srv in self._servers:
srv.close()
server_closures.append(srv.wait_closed())
yield from asyncio.gather(*server_closures,
loop=self.loop)

for app in self._apps:
yield from app.shutdown()

yield from self._handler.shutdown(self.shutdown_timeout)

for app in self._apps:
yield from app.cleanup()


def run_app(app, *, print=print, loop=None, **kwargs):
"""Run an app locally"""
user_supplied_loop = loop is not None
if loop is None:
loop = asyncio.get_event_loop()

server = Server(loop=loop, **kwargs)
server.register(app)

uris = loop.run_until_complete(server.start())

print("======== Running on {} ========\n"
"(Press CTRL+C to quit)".format(', '.join(uris)))
Expand All @@ -422,14 +477,8 @@ def run_app(app, *, host=None, port=None, path=None, sock=None,
except KeyboardInterrupt: # pragma: no cover
pass
finally:
server_closures = []
for srv in servers:
srv.close()
server_closures.append(srv.wait_closed())
loop.run_until_complete(asyncio.gather(*server_closures, loop=loop))
loop.run_until_complete(app.shutdown())
loop.run_until_complete(handler.shutdown(shutdown_timeout))
loop.run_until_complete(app.cleanup())
loop.run_until_complete(server.stop())

if not user_supplied_loop:
loop.close()

Expand Down

0 comments on commit 641b49f

Please sign in to comment.