123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- import asyncio
- import logging
- import socket
- import sys
- from argparse import ArgumentParser
- from collections.abc import Iterable
- from importlib import import_module
- from typing import (
- Any as Any,
- Awaitable as Awaitable,
- Callable as Callable,
- Iterable as TypingIterable,
- List as List,
- Optional as Optional,
- Set as Set,
- Type as Type,
- Union as Union,
- cast as cast,
- )
- from .abc import AbstractAccessLogger
- from .helpers import all_tasks
- from .log import access_logger
- from .web_app import Application as Application, CleanupError as CleanupError
- from .web_exceptions import (
- HTTPAccepted as HTTPAccepted,
- HTTPBadGateway as HTTPBadGateway,
- HTTPBadRequest as HTTPBadRequest,
- HTTPClientError as HTTPClientError,
- HTTPConflict as HTTPConflict,
- HTTPCreated as HTTPCreated,
- HTTPError as HTTPError,
- HTTPException as HTTPException,
- HTTPExpectationFailed as HTTPExpectationFailed,
- HTTPFailedDependency as HTTPFailedDependency,
- HTTPForbidden as HTTPForbidden,
- HTTPFound as HTTPFound,
- HTTPGatewayTimeout as HTTPGatewayTimeout,
- HTTPGone as HTTPGone,
- HTTPInsufficientStorage as HTTPInsufficientStorage,
- HTTPInternalServerError as HTTPInternalServerError,
- HTTPLengthRequired as HTTPLengthRequired,
- HTTPMethodNotAllowed as HTTPMethodNotAllowed,
- HTTPMisdirectedRequest as HTTPMisdirectedRequest,
- HTTPMovedPermanently as HTTPMovedPermanently,
- HTTPMultipleChoices as HTTPMultipleChoices,
- HTTPNetworkAuthenticationRequired as HTTPNetworkAuthenticationRequired,
- HTTPNoContent as HTTPNoContent,
- HTTPNonAuthoritativeInformation as HTTPNonAuthoritativeInformation,
- HTTPNotAcceptable as HTTPNotAcceptable,
- HTTPNotExtended as HTTPNotExtended,
- HTTPNotFound as HTTPNotFound,
- HTTPNotImplemented as HTTPNotImplemented,
- HTTPNotModified as HTTPNotModified,
- HTTPOk as HTTPOk,
- HTTPPartialContent as HTTPPartialContent,
- HTTPPaymentRequired as HTTPPaymentRequired,
- HTTPPermanentRedirect as HTTPPermanentRedirect,
- HTTPPreconditionFailed as HTTPPreconditionFailed,
- HTTPPreconditionRequired as HTTPPreconditionRequired,
- HTTPProxyAuthenticationRequired as HTTPProxyAuthenticationRequired,
- HTTPRedirection as HTTPRedirection,
- HTTPRequestEntityTooLarge as HTTPRequestEntityTooLarge,
- HTTPRequestHeaderFieldsTooLarge as HTTPRequestHeaderFieldsTooLarge,
- HTTPRequestRangeNotSatisfiable as HTTPRequestRangeNotSatisfiable,
- HTTPRequestTimeout as HTTPRequestTimeout,
- HTTPRequestURITooLong as HTTPRequestURITooLong,
- HTTPResetContent as HTTPResetContent,
- HTTPSeeOther as HTTPSeeOther,
- HTTPServerError as HTTPServerError,
- HTTPServiceUnavailable as HTTPServiceUnavailable,
- HTTPSuccessful as HTTPSuccessful,
- HTTPTemporaryRedirect as HTTPTemporaryRedirect,
- HTTPTooManyRequests as HTTPTooManyRequests,
- HTTPUnauthorized as HTTPUnauthorized,
- HTTPUnavailableForLegalReasons as HTTPUnavailableForLegalReasons,
- HTTPUnprocessableEntity as HTTPUnprocessableEntity,
- HTTPUnsupportedMediaType as HTTPUnsupportedMediaType,
- HTTPUpgradeRequired as HTTPUpgradeRequired,
- HTTPUseProxy as HTTPUseProxy,
- HTTPVariantAlsoNegotiates as HTTPVariantAlsoNegotiates,
- HTTPVersionNotSupported as HTTPVersionNotSupported,
- )
- from .web_fileresponse import FileResponse as FileResponse
- from .web_log import AccessLogger
- from .web_middlewares import (
- middleware as middleware,
- normalize_path_middleware as normalize_path_middleware,
- )
- from .web_protocol import (
- PayloadAccessError as PayloadAccessError,
- RequestHandler as RequestHandler,
- RequestPayloadError as RequestPayloadError,
- )
- from .web_request import (
- BaseRequest as BaseRequest,
- FileField as FileField,
- Request as Request,
- )
- from .web_response import (
- ContentCoding as ContentCoding,
- Response as Response,
- StreamResponse as StreamResponse,
- json_response as json_response,
- )
- from .web_routedef import (
- AbstractRouteDef as AbstractRouteDef,
- RouteDef as RouteDef,
- RouteTableDef as RouteTableDef,
- StaticDef as StaticDef,
- delete as delete,
- get as get,
- head as head,
- options as options,
- patch as patch,
- post as post,
- put as put,
- route as route,
- static as static,
- view as view,
- )
- from .web_runner import (
- AppRunner as AppRunner,
- BaseRunner as BaseRunner,
- BaseSite as BaseSite,
- GracefulExit as GracefulExit,
- NamedPipeSite as NamedPipeSite,
- ServerRunner as ServerRunner,
- SockSite as SockSite,
- TCPSite as TCPSite,
- UnixSite as UnixSite,
- )
- from .web_server import Server as Server
- from .web_urldispatcher import (
- AbstractResource as AbstractResource,
- AbstractRoute as AbstractRoute,
- DynamicResource as DynamicResource,
- PlainResource as PlainResource,
- Resource as Resource,
- ResourceRoute as ResourceRoute,
- StaticResource as StaticResource,
- UrlDispatcher as UrlDispatcher,
- UrlMappingMatchInfo as UrlMappingMatchInfo,
- View as View,
- )
- from .web_ws import (
- WebSocketReady as WebSocketReady,
- WebSocketResponse as WebSocketResponse,
- WSMsgType as WSMsgType,
- )
- __all__ = (
- # web_app
- "Application",
- "CleanupError",
- # web_exceptions
- "HTTPAccepted",
- "HTTPBadGateway",
- "HTTPBadRequest",
- "HTTPClientError",
- "HTTPConflict",
- "HTTPCreated",
- "HTTPError",
- "HTTPException",
- "HTTPExpectationFailed",
- "HTTPFailedDependency",
- "HTTPForbidden",
- "HTTPFound",
- "HTTPGatewayTimeout",
- "HTTPGone",
- "HTTPInsufficientStorage",
- "HTTPInternalServerError",
- "HTTPLengthRequired",
- "HTTPMethodNotAllowed",
- "HTTPMisdirectedRequest",
- "HTTPMovedPermanently",
- "HTTPMultipleChoices",
- "HTTPNetworkAuthenticationRequired",
- "HTTPNoContent",
- "HTTPNonAuthoritativeInformation",
- "HTTPNotAcceptable",
- "HTTPNotExtended",
- "HTTPNotFound",
- "HTTPNotImplemented",
- "HTTPNotModified",
- "HTTPOk",
- "HTTPPartialContent",
- "HTTPPaymentRequired",
- "HTTPPermanentRedirect",
- "HTTPPreconditionFailed",
- "HTTPPreconditionRequired",
- "HTTPProxyAuthenticationRequired",
- "HTTPRedirection",
- "HTTPRequestEntityTooLarge",
- "HTTPRequestHeaderFieldsTooLarge",
- "HTTPRequestRangeNotSatisfiable",
- "HTTPRequestTimeout",
- "HTTPRequestURITooLong",
- "HTTPResetContent",
- "HTTPSeeOther",
- "HTTPServerError",
- "HTTPServiceUnavailable",
- "HTTPSuccessful",
- "HTTPTemporaryRedirect",
- "HTTPTooManyRequests",
- "HTTPUnauthorized",
- "HTTPUnavailableForLegalReasons",
- "HTTPUnprocessableEntity",
- "HTTPUnsupportedMediaType",
- "HTTPUpgradeRequired",
- "HTTPUseProxy",
- "HTTPVariantAlsoNegotiates",
- "HTTPVersionNotSupported",
- # web_fileresponse
- "FileResponse",
- # web_middlewares
- "middleware",
- "normalize_path_middleware",
- # web_protocol
- "PayloadAccessError",
- "RequestHandler",
- "RequestPayloadError",
- # web_request
- "BaseRequest",
- "FileField",
- "Request",
- # web_response
- "ContentCoding",
- "Response",
- "StreamResponse",
- "json_response",
- # web_routedef
- "AbstractRouteDef",
- "RouteDef",
- "RouteTableDef",
- "StaticDef",
- "delete",
- "get",
- "head",
- "options",
- "patch",
- "post",
- "put",
- "route",
- "static",
- "view",
- # web_runner
- "AppRunner",
- "BaseRunner",
- "BaseSite",
- "GracefulExit",
- "ServerRunner",
- "SockSite",
- "TCPSite",
- "UnixSite",
- "NamedPipeSite",
- # web_server
- "Server",
- # web_urldispatcher
- "AbstractResource",
- "AbstractRoute",
- "DynamicResource",
- "PlainResource",
- "Resource",
- "ResourceRoute",
- "StaticResource",
- "UrlDispatcher",
- "UrlMappingMatchInfo",
- "View",
- # web_ws
- "WebSocketReady",
- "WebSocketResponse",
- "WSMsgType",
- # web
- "run_app",
- )
- try:
- from ssl import SSLContext
- except ImportError: # pragma: no cover
- SSLContext = Any # type: ignore
- HostSequence = TypingIterable[str]
- async def _run_app(
- app: Union[Application, Awaitable[Application]],
- *,
- host: Optional[Union[str, HostSequence]] = None,
- port: Optional[int] = None,
- path: Optional[str] = None,
- sock: Optional[socket.socket] = None,
- shutdown_timeout: float = 60.0,
- ssl_context: Optional[SSLContext] = None,
- print: Callable[..., None] = print,
- backlog: int = 128,
- access_log_class: Type[AbstractAccessLogger] = AccessLogger,
- access_log_format: str = AccessLogger.LOG_FORMAT,
- access_log: Optional[logging.Logger] = access_logger,
- handle_signals: bool = True,
- reuse_address: Optional[bool] = None,
- reuse_port: Optional[bool] = None,
- ) -> None:
- # A internal functio to actually do all dirty job for application running
- if asyncio.iscoroutine(app):
- app = await app # type: ignore
- app = cast(Application, app)
- runner = AppRunner(
- app,
- handle_signals=handle_signals,
- access_log_class=access_log_class,
- access_log_format=access_log_format,
- access_log=access_log,
- )
- await runner.setup()
- sites = [] # type: List[BaseSite]
- try:
- if host is not None:
- if isinstance(host, (str, bytes, bytearray, memoryview)):
- sites.append(
- TCPSite(
- runner,
- host,
- port,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- reuse_address=reuse_address,
- reuse_port=reuse_port,
- )
- )
- else:
- for h in host:
- sites.append(
- TCPSite(
- runner,
- h,
- port,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- reuse_address=reuse_address,
- reuse_port=reuse_port,
- )
- )
- elif path is None and sock is None or port is not None:
- sites.append(
- TCPSite(
- runner,
- port=port,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- reuse_address=reuse_address,
- reuse_port=reuse_port,
- )
- )
- if path is not None:
- if isinstance(path, (str, bytes, bytearray, memoryview)):
- sites.append(
- UnixSite(
- runner,
- path,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- )
- )
- else:
- for p in path:
- sites.append(
- UnixSite(
- runner,
- p,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- )
- )
- if sock is not None:
- if not isinstance(sock, Iterable):
- sites.append(
- SockSite(
- runner,
- sock,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- )
- )
- else:
- for s in sock:
- sites.append(
- SockSite(
- runner,
- s,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- backlog=backlog,
- )
- )
- for site in sites:
- await site.start()
- if print: # pragma: no branch
- names = sorted(str(s.name) for s in runner.sites)
- print(
- "======== Running on {} ========\n"
- "(Press CTRL+C to quit)".format(", ".join(names))
- )
- # sleep forever by 1 hour intervals,
- # on Windows before Python 3.8 wake up every 1 second to handle
- # Ctrl+C smoothly
- if sys.platform == "win32" and sys.version_info < (3, 8):
- delay = 1
- else:
- delay = 3600
- while True:
- await asyncio.sleep(delay)
- finally:
- await runner.cleanup()
- def _cancel_tasks(
- to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop
- ) -> None:
- if not to_cancel:
- return
- for task in to_cancel:
- task.cancel()
- loop.run_until_complete(
- asyncio.gather(*to_cancel, loop=loop, return_exceptions=True)
- )
- for task in to_cancel:
- if task.cancelled():
- continue
- if task.exception() is not None:
- loop.call_exception_handler(
- {
- "message": "unhandled exception during asyncio.run() shutdown",
- "exception": task.exception(),
- "task": task,
- }
- )
- def run_app(
- app: Union[Application, Awaitable[Application]],
- *,
- host: Optional[Union[str, HostSequence]] = None,
- port: Optional[int] = None,
- path: Optional[str] = None,
- sock: Optional[socket.socket] = None,
- shutdown_timeout: float = 60.0,
- ssl_context: Optional[SSLContext] = None,
- print: Callable[..., None] = print,
- backlog: int = 128,
- access_log_class: Type[AbstractAccessLogger] = AccessLogger,
- access_log_format: str = AccessLogger.LOG_FORMAT,
- access_log: Optional[logging.Logger] = access_logger,
- handle_signals: bool = True,
- reuse_address: Optional[bool] = None,
- reuse_port: Optional[bool] = None,
- ) -> None:
- """Run an app locally"""
- loop = asyncio.get_event_loop()
- # Configure if and only if in debugging mode and using the default logger
- if loop.get_debug() and access_log and access_log.name == "aiohttp.access":
- if access_log.level == logging.NOTSET:
- access_log.setLevel(logging.DEBUG)
- if not access_log.hasHandlers():
- access_log.addHandler(logging.StreamHandler())
- try:
- main_task = loop.create_task(
- _run_app(
- app,
- host=host,
- port=port,
- path=path,
- sock=sock,
- shutdown_timeout=shutdown_timeout,
- ssl_context=ssl_context,
- print=print,
- backlog=backlog,
- access_log_class=access_log_class,
- access_log_format=access_log_format,
- access_log=access_log,
- handle_signals=handle_signals,
- reuse_address=reuse_address,
- reuse_port=reuse_port,
- )
- )
- loop.run_until_complete(main_task)
- except (GracefulExit, KeyboardInterrupt): # pragma: no cover
- pass
- finally:
- _cancel_tasks({main_task}, loop)
- _cancel_tasks(all_tasks(loop), loop)
- if sys.version_info >= (3, 6): # don't use PY_36 to pass mypy
- loop.run_until_complete(loop.shutdown_asyncgens())
- loop.close()
- def main(argv: List[str]) -> None:
- arg_parser = ArgumentParser(
- description="aiohttp.web Application server", prog="aiohttp.web"
- )
- arg_parser.add_argument(
- "entry_func",
- help=(
- "Callable returning the `aiohttp.web.Application` instance to "
- "run. Should be specified in the 'module:function' syntax."
- ),
- metavar="entry-func",
- )
- arg_parser.add_argument(
- "-H",
- "--hostname",
- help="TCP/IP hostname to serve on (default: %(default)r)",
- default="localhost",
- )
- arg_parser.add_argument(
- "-P",
- "--port",
- help="TCP/IP port to serve on (default: %(default)r)",
- type=int,
- default="8080",
- )
- arg_parser.add_argument(
- "-U",
- "--path",
- help="Unix file system path to serve on. Specifying a path will cause "
- "hostname and port arguments to be ignored.",
- )
- args, extra_argv = arg_parser.parse_known_args(argv)
- # Import logic
- mod_str, _, func_str = args.entry_func.partition(":")
- if not func_str or not mod_str:
- arg_parser.error("'entry-func' not in 'module:function' syntax")
- if mod_str.startswith("."):
- arg_parser.error("relative module names not supported")
- try:
- module = import_module(mod_str)
- except ImportError as ex:
- arg_parser.error(f"unable to import {mod_str}: {ex}")
- try:
- func = getattr(module, func_str)
- except AttributeError:
- arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}")
- # Compatibility logic
- if args.path is not None and not hasattr(socket, "AF_UNIX"):
- arg_parser.error(
- "file system paths not supported by your operating" " environment"
- )
- logging.basicConfig(level=logging.DEBUG)
- app = func(extra_argv)
- run_app(app, host=args.hostname, port=args.port, path=args.path)
- arg_parser.exit(message="Stopped\n")
- if __name__ == "__main__": # pragma: no branch
- main(sys.argv[1:]) # pragma: no cover
|