Beispiel #1
0
    def run(self, host=None, port=None, debug=False, ssl=None,
            sock=None, workers=1, protocol=None,
            backlog=100, stop_event=None, register_sys_signals=True,
            access_log=True):
        """Run the HTTP Server and listen until keyboard interrupt or term
        signal. On termination, drain connections before closing.

        :param host: Address to host on
        :param port: Port to host on
        :param debug: Enables debug output (slows server)
        :param ssl: SSLContext, or location of certificate and key
                            for SSL encryption of worker(s)
        :param sock: Socket for the server to accept connections from
        :param workers: Number of processes
                            received before it is respected
        :param backlog:
        :param stop_event:
        :param register_sys_signals:
        :param protocol: Subclass of asyncio protocol class
        :return: Nothing
        """

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (WebSocketProtocol if self.websocket_enabled
                        else HttpProtocol)
        if stop_event is not None:
            if debug:
                warnings.simplefilter('default')
            warnings.warn("stop_event will be removed from future versions.",
                          DeprecationWarning)
        server_settings = self._helper(
            host=host, port=port, debug=debug, ssl=ssl, sock=sock,
            workers=workers, protocol=protocol, backlog=backlog,
            register_sys_signals=register_sys_signals,
            access_log=access_log)

        try:
            self.is_running = True
            if workers == 1:
                serve(**server_settings)
            else:
                serve_multiple(server_settings, workers)
        except BaseException:
            error_logger.exception(
                'Experienced exception while trying to serve')
            raise
        finally:
            self.is_running = False
        logger.info("Server Stopped")
Beispiel #2
0
    def form(self):
        if self.parsed_form is None:
            self.parsed_form = RequestParameters()
            self.parsed_files = RequestParameters()
            content_type = self.headers.get(
                'Content-Type', DEFAULT_HTTP_CONTENT_TYPE)
            content_type, parameters = parse_header(content_type)
            try:
                if content_type == 'application/x-www-form-urlencoded':
                    self.parsed_form = RequestParameters(
                        parse_qs(self.body.decode('utf-8')))
                elif content_type == 'multipart/form-data':
                    # TODO: Stream this instead of reading to/from memory
                    boundary = parameters['boundary'].encode('utf-8')
                    self.parsed_form, self.parsed_files = (
                        parse_multipart_form(self.body, boundary))
            except Exception:
                error_logger.exception("Failed when parsing form")

        return self.parsed_form
Beispiel #3
0
    def run(
        self,
        host: Optional[str] = None,
        port: Optional[int] = None,
        debug: bool = False,
        ssl: Union[dict, SSLContext, None] = None,
        sock: Optional[socket] = None,
        workers: int = 1,
        protocol: Type[Protocol] = None,
        backlog: int = 100,
        stop_event: Any = None,
        register_sys_signals: bool = True,
        access_log: Optional[bool] = None,
        **kwargs: Any
    ) -> None:
        """Run the HTTP Server and listen until keyboard interrupt or term
        signal. On termination, drain connections before closing.

        :param host: Address to host on
        :type host: str
        :param port: Port to host on
        :type port: int
        :param debug: Enables debug output (slows server)
        :type debug: bool
        :param ssl: SSLContext, or location of certificate and key
            for SSL encryption of worker(s)
        :type ssl:SSLContext or dict
        :param sock: Socket for the server to accept connections from
        :type sock: socket
        :param workers: Number of processes received before it is respected
        :type workers: int
        :param protocol: Subclass of asyncio Protocol class
        :type protocol: type[Protocol]
        :param backlog: a number of unaccepted connections that the system
            will allow before refusing new connections
        :type backlog: int
        :param stop_event: event to be triggered
            before stopping the app - deprecated
        :type stop_event: None
        :param register_sys_signals: Register SIG* events
        :type register_sys_signals: bool
        :param access_log: Enables writing access logs (slows server)
        :type access_log: bool
        :return: Nothing
        """
        if "loop" in kwargs:
            raise TypeError(
                "loop is not a valid argument. To use an existing loop, "
                "change to create_server().\nSee more: "
                "https://sanic.readthedocs.io/en/latest/sanic/deploying.html"
                "#asynchronous-support"
            )

        # Default auto_reload to false
        auto_reload = False
        # If debug is set, default it to true (unless on windows)
        if debug and os.name == "posix":
            auto_reload = True
        # Allow for overriding either of the defaults
        auto_reload = kwargs.get("auto_reload", auto_reload)

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (
                WebSocketProtocol if self.websocket_enabled else HttpProtocol
            )
        if stop_event is not None:
            if debug:
                warnings.simplefilter("default")
            warnings.warn(
                "stop_event will be removed from future versions.",
                DeprecationWarning,
            )
        # if access_log is passed explicitly change config.ACCESS_LOG
        if access_log is not None:
            self.config.ACCESS_LOG = access_log

        server_settings = self._helper(
            host=host,
            port=port,
            debug=debug,
            ssl=ssl,
            sock=sock,
            workers=workers,
            protocol=protocol,
            backlog=backlog,
            register_sys_signals=register_sys_signals,
            auto_reload=auto_reload,
        )

        try:
            self.is_running = True
            if workers == 1:
                if auto_reload and os.name != "posix":
                    # This condition must be removed after implementing
                    # auto reloader for other operating systems.
                    raise NotImplementedError

                if (
                    auto_reload
                    and os.environ.get("SANIC_SERVER_RUNNING") != "true"
                ):
                    reloader_helpers.watchdog(2)
                else:
                    serve(**server_settings)
            else:
                serve_multiple(server_settings, workers)
        except BaseException:
            error_logger.exception(
                "Experienced exception while trying to serve"
            )
            raise
        finally:
            self.is_running = False
        logger.info("Server Stopped")
Beispiel #4
0
    async def handle_request(self, request, write_callback, stream_callback):
        """Take a request from the HTTP Server and return a response object
        to be sent back The HTTP Server only expects a response object, so
        exception handling must be done here

        :param request: HTTP Request object
        :param write_callback: Synchronous response function to be
            called with the response as the only argument
        :param stream_callback: Coroutine that handles streaming a
            StreamingHTTPResponse if produced by the handler.

        :return: Nothing
        """
        # Define `response` var here to remove warnings about
        # allocation before assignment below.
        response = None
        cancelled = False
        try:
            # -------------------------------------------- #
            # Request Middleware
            # -------------------------------------------- #
            response = await self._run_request_middleware(request)
            # No middleware results
            if not response:
                # -------------------------------------------- #
                # Execute Handler
                # -------------------------------------------- #

                # Fetch handler from router
                handler, args, kwargs, uri = self.router.get(request)

                request.uri_template = uri
                if handler is None:
                    raise ServerError(
                        (
                            "'None' was returned while requesting a "
                            "handler from the router"
                        )
                    )
                else:
                    if not getattr(handler, "__blueprintname__", False):
                        request.endpoint = self._build_endpoint_name(
                            handler.__name__
                        )
                    else:
                        request.endpoint = self._build_endpoint_name(
                            getattr(handler, "__blueprintname__", ""),
                            handler.__name__,
                        )

                # Run response handler
                response = handler(request, *args, **kwargs)
                if isawaitable(response):
                    response = await response
        except CancelledError:
            # If response handler times out, the server handles the error
            # and cancels the handle_request job.
            # In this case, the transport is already closed and we cannot
            # issue a response.
            response = None
            cancelled = True
        except Exception as e:
            # -------------------------------------------- #
            # Response Generation Failed
            # -------------------------------------------- #

            try:
                response = self.error_handler.response(request, e)
                if isawaitable(response):
                    response = await response
            except Exception as e:
                if isinstance(e, SanicException):
                    response = self.error_handler.default(
                        request=request, exception=e
                    )
                elif self.debug:
                    response = HTTPResponse(
                        "Error while handling error: {}\nStack: {}".format(
                            e, format_exc()
                        ),
                        status=500,
                    )
                else:
                    response = HTTPResponse(
                        "An error occurred while handling an error", status=500
                    )
        finally:
            # -------------------------------------------- #
            # Response Middleware
            # -------------------------------------------- #
            # Don't run response middleware if response is None
            if response is not None:
                try:
                    response = await self._run_response_middleware(
                        request, response
                    )
                except CancelledError:
                    # Response middleware can timeout too, as above.
                    response = None
                    cancelled = True
                except BaseException:
                    error_logger.exception(
                        "Exception occurred in one of response "
                        "middleware handlers"
                    )
            if cancelled:
                raise CancelledError()

        # pass the response to the correct callback
        if isinstance(response, StreamingHTTPResponse):
            await stream_callback(response)
        else:
            write_callback(response)
Beispiel #5
0
    def run(
        self,
        host: Optional[str] = None,
        port: Optional[int] = None,
        *,
        debug: bool = False,
        auto_reload: Optional[bool] = None,
        ssl: Union[dict, SSLContext, None] = None,
        sock: Optional[socket] = None,
        workers: int = 1,
        protocol: Optional[Type[Protocol]] = None,
        backlog: int = 100,
        register_sys_signals: bool = True,
        access_log: Optional[bool] = None,
        unix: Optional[str] = None,
        loop: None = None,
    ) -> None:
        """Run the HTTP Server and listen until keyboard interrupt or term
        signal. On termination, drain connections before closing.

        :param host: Address to host on
        :type host: str
        :param port: Port to host on
        :type port: int
        :param debug: Enables debug output (slows server)
        :type debug: bool
        :param auto_reload: Reload app whenever its source code is changed.
                            Enabled by default in debug mode.
        :type auto_relaod: bool
        :param ssl: SSLContext, or location of certificate and key
                    for SSL encryption of worker(s)
        :type ssl: SSLContext or dict
        :param sock: Socket for the server to accept connections from
        :type sock: socket
        :param workers: Number of processes received before it is respected
        :type workers: int
        :param protocol: Subclass of asyncio Protocol class
        :type protocol: type[Protocol]
        :param backlog: a number of unaccepted connections that the system
                        will allow before refusing new connections
        :type backlog: int
        :param register_sys_signals: Register SIG* events
        :type register_sys_signals: bool
        :param access_log: Enables writing access logs (slows server)
        :type access_log: bool
        :param unix: Unix socket to listen on instead of TCP port
        :type unix: str
        :return: Nothing
        """
        if loop is not None:
            raise TypeError(
                "loop is not a valid argument. To use an existing loop, "
                "change to create_server().\nSee more: "
                "https://sanic.readthedocs.io/en/latest/sanic/deploying.html"
                "#asynchronous-support")

        if auto_reload or auto_reload is None and debug:
            if os.environ.get("SANIC_SERVER_RUNNING") != "true":
                return reloader_helpers.watchdog(1.0)

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (WebSocketProtocol
                        if self.websocket_enabled else HttpProtocol)
        # if access_log is passed explicitly change config.ACCESS_LOG
        if access_log is not None:
            self.config.ACCESS_LOG = access_log

        server_settings = self._helper(
            host=host,
            port=port,
            debug=debug,
            ssl=ssl,
            sock=sock,
            unix=unix,
            workers=workers,
            protocol=protocol,
            backlog=backlog,
            register_sys_signals=register_sys_signals,
            auto_reload=auto_reload,
        )

        try:
            self.is_running = True
            self.is_stopping = False
            if workers > 1 and os.name != "posix":
                logger.warn(
                    f"Multiprocessing is currently not supported on {os.name},"
                    " using workers=1 instead")
                workers = 1
            if workers == 1:
                serve(**server_settings)
            else:
                serve_multiple(server_settings, workers)
        except BaseException:
            error_logger.exception(
                "Experienced exception while trying to serve")
            raise
        finally:
            self.is_running = False
        logger.info("Server Stopped")
Beispiel #6
0
async def _static_request_handler(
    file_or_directory,
    use_modified_since,
    use_content_range,
    stream_large_files,
    request,
    content_type=None,
    file_uri=None,
):
    # Using this to determine if the URL is trying to break out of the path
    # served.  os.path.realpath seems to be very slow
    if file_uri and "../" in file_uri:
        raise InvalidUsage("Invalid URL")
    # Merge served directory and requested file if provided
    # Strip all / that in the beginning of the URL to help prevent python
    # from herping a derp and treating the uri as an absolute path
    root_path = file_path = file_or_directory
    if file_uri:
        file_path = path.join(file_or_directory, sub("^[/]*", "", file_uri))

    # URL decode the path sent by the browser otherwise we won't be able to
    # match filenames which got encoded (filenames with spaces etc)
    file_path = path.abspath(unquote(file_path))
    if not file_path.startswith(path.abspath(unquote(root_path))):
        error_logger.exception(f"File not found: path={file_or_directory}, "
                               f"relative_url={file_uri}")
        raise FileNotFound("File not found",
                           path=file_or_directory,
                           relative_url=file_uri)
    try:
        headers = {}
        # Check if the client has been sent this file before
        # and it has not been modified since
        stats = None
        if use_modified_since:
            stats = await stat_async(file_path)
            modified_since = strftime("%a, %d %b %Y %H:%M:%S GMT",
                                      gmtime(stats.st_mtime))
            if request.headers.get("If-Modified-Since") == modified_since:
                return HTTPResponse(status=304)
            headers["Last-Modified"] = modified_since
        _range = None
        if use_content_range:
            _range = None
            if not stats:
                stats = await stat_async(file_path)
            headers["Accept-Ranges"] = "bytes"
            headers["Content-Length"] = str(stats.st_size)
            if request.method != "HEAD":
                try:
                    _range = ContentRangeHandler(request, stats)
                except HeaderNotFound:
                    pass
                else:
                    del headers["Content-Length"]
                    for key, value in _range.headers.items():
                        headers[key] = value
        headers["Content-Type"] = (content_type or guess_type(file_path)[0]
                                   or "text/plain")
        if request.method == "HEAD":
            return HTTPResponse(headers=headers)
        else:
            if stream_large_files:
                if type(stream_large_files) == int:
                    threshold = stream_large_files
                else:
                    threshold = 1024 * 1024

                if not stats:
                    stats = await stat_async(file_path)
                if stats.st_size >= threshold:
                    return await file_stream(file_path,
                                             headers=headers,
                                             _range=_range)
            return await file(file_path, headers=headers, _range=_range)
    except ContentRangeError:
        raise
    except Exception:
        error_logger.exception(f"File not found: path={file_or_directory}, "
                               f"relative_url={file_uri}")
        raise FileNotFound("File not found",
                           path=file_or_directory,
                           relative_url=file_uri)
Beispiel #7
0
    def run(self,
            host=None,
            port=None,
            debug=False,
            ssl=None,
            sock=None,
            workers=1,
            protocol=None,
            backlog=100,
            stop_event=None,
            register_sys_signals=True,
            access_log=True):
        """Run the HTTP Server and listen until keyboard interrupt or term
        signal. On termination, drain connections before closing.

        :param host: Address to host on
        :param port: Port to host on
        :param debug: Enables debug output (slows server)
        :param ssl: SSLContext, or location of certificate and key
                            for SSL encryption of worker(s)
        :param sock: Socket for the server to accept connections from
        :param workers: Number of processes
                            received before it is respected
        :param backlog:
        :param stop_event:
        :param register_sys_signals:
        :param protocol: Subclass of asyncio protocol class
        :return: Nothing
        """

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (WebSocketProtocol
                        if self.websocket_enabled else HttpProtocol)
        if stop_event is not None:
            if debug:
                warnings.simplefilter('default')
            warnings.warn("stop_event will be removed from future versions.",
                          DeprecationWarning)
        server_settings = self._helper(
            host=host,
            port=port,
            debug=debug,
            ssl=ssl,
            sock=sock,
            workers=workers,
            protocol=protocol,
            backlog=backlog,
            register_sys_signals=register_sys_signals,
            access_log=access_log)

        try:
            self.is_running = True
            if workers == 1:
                serve(**server_settings)
            else:
                serve_multiple(server_settings, workers)
        except BaseException:
            error_logger.exception(
                'Experienced exception while trying to serve')
            raise
        finally:
            self.is_running = False
        logger.info("Server Stopped")
Beispiel #8
0
    async def handle_request(self, request, write_callback, stream_callback):
        """Take a request from the HTTP Server and return a response object
        to be sent back The HTTP Server only expects a response object, so
        exception handling must be done here

        :param request: HTTP Request object
        :param write_callback: Synchronous response function to be
            called with the response as the only argument
        :param stream_callback: Coroutine that handles streaming a
            StreamingHTTPResponse if produced by the handler.

        :return: Nothing
        """
        try:
            # -------------------------------------------- #
            # Request Middleware
            # -------------------------------------------- #

            request.app = self
            response = await self._run_request_middleware(request)
            # No middleware results
            if not response:
                # -------------------------------------------- #
                # Execute Handler
                # -------------------------------------------- #

                # Fetch handler from router
                handler, args, kwargs, uri = self.router.get(request)

                request.uri_template = uri
                if handler is None:
                    raise ServerError(
                        ("'None' was returned while requesting a "
                         "handler from the router"))

                # Run response handler
                response = handler(request, *args, **kwargs)
                if isawaitable(response):
                    response = await response
        except Exception as e:
            # -------------------------------------------- #
            # Response Generation Failed
            # -------------------------------------------- #

            try:
                response = self.error_handler.response(request, e)
                if isawaitable(response):
                    response = await response
            except Exception as e:
                if isinstance(e, SanicException):
                    response = self.error_handler.default(request=request,
                                                          exception=e)
                elif self.debug:
                    response = HTTPResponse(
                        "Error while handling error: {}\nStack: {}".format(
                            e, format_exc()),
                        status=500)
                else:
                    response = HTTPResponse(
                        "An error occurred while handling an error",
                        status=500)
        finally:
            # -------------------------------------------- #
            # Response Middleware
            # -------------------------------------------- #
            try:
                response = await self._run_response_middleware(
                    request, response)
            except BaseException:
                error_logger.exception(
                    'Exception occurred in one of response middleware handlers'
                )

        # pass the response to the correct callback
        if isinstance(response, StreamingHTTPResponse):
            await stream_callback(response)
        else:
            write_callback(response)
Beispiel #9
0
def main():
    parser = SanicArgumentParser(
        prog="sanic",
        description=BASE_LOGO,
        formatter_class=RawDescriptionHelpFormatter,
    )
    parser.add_argument(
        "-H",
        "--host",
        dest="host",
        type=str,
        default="127.0.0.1",
        help="host address [default 127.0.0.1]",
    )
    parser.add_argument(
        "-p",
        "--port",
        dest="port",
        type=int,
        default=8000,
        help="port to serve on [default 8000]",
    )
    parser.add_argument(
        "-u",
        "--unix",
        dest="unix",
        type=str,
        default="",
        help="location of unix socket",
    )
    parser.add_argument("--cert",
                        dest="cert",
                        type=str,
                        help="location of certificate for SSL")
    parser.add_argument("--key",
                        dest="key",
                        type=str,
                        help="location of keyfile for SSL.")
    parser.add_argument(
        "-w",
        "--workers",
        dest="workers",
        type=int,
        default=1,
        help="number of worker processes [default 1]",
    )
    parser.add_argument("-d", "--debug", dest="debug", action="store_true")
    parser.add_argument(
        "-r",
        "--auto-reload",
        dest="auto_reload",
        action="store_true",
        help="Watch source directory for file changes and reload on changes",
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"Sanic {__version__}; Routing {__routing_version__}",
    )
    parser.add_bool_arguments("--access-logs",
                              dest="access_log",
                              help="display access logs")
    parser.add_argument(
        "module", help="path to your Sanic app. Example: path.to.server:app")
    args = parser.parse_args()

    try:
        module_path = os.path.abspath(os.getcwd())
        if module_path not in sys.path:
            sys.path.append(module_path)

        delimiter = ":" if ":" in args.module else "."
        module_name, app_name = args.module.rsplit(delimiter, 1)

        module = import_module(module_name)
        app = getattr(module, app_name, None)
        app_name = type(app).__name__

        if not isinstance(app, Sanic):
            raise ValueError(
                f"Module is not a Sanic app, it is a {app_name}.  "
                f"Perhaps you meant {args.module}.app?")
        if args.cert is not None or args.key is not None:
            ssl: Optional[Dict[str, Any]] = {
                "cert": args.cert,
                "key": args.key,
            }
        else:
            ssl = None

        kwargs = {
            "host": args.host,
            "port": args.port,
            "unix": args.unix,
            "workers": args.workers,
            "debug": args.debug,
            "access_log": args.access_log,
            "ssl": ssl,
        }
        if args.auto_reload:
            kwargs["auto_reload"] = True
        app.run(**kwargs)
    except ImportError as e:
        if module_name.startswith(e.name):
            error_logger.error(
                f"No module named {e.name} found.\n"
                "  Example File: project/sanic_server.py -> app\n"
                "  Example Module: project.sanic_server.app")
        else:
            raise e
    except ValueError:
        error_logger.exception("Failed to run app")
Beispiel #10
0
    def run(self,
            host=None,
            port=None,
            debug=False,
            ssl=None,
            sock=None,
            workers=1,
            protocol=None,
            backlog=100,
            stop_event=None,
            register_sys_signals=True,
            access_log=True,
            **kwargs):
        """Run the HTTP Server and listen until keyboard interrupt or term
        signal. On termination, drain connections before closing.

        :param host: Address to host on
        :param port: Port to host on
        :param debug: Enables debug output (slows server)
        :param ssl: SSLContext, or location of certificate and key
                            for SSL encryption of worker(s)
        :param sock: Socket for the server to accept connections from
        :param workers: Number of processes
                            received before it is respected
        :param backlog:
        :param stop_event:
        :param register_sys_signals:
        :param protocol: Subclass of asyncio protocol class
        :return: Nothing
        """
        # Default auto_reload to false
        auto_reload = False
        # If debug is set, default it to true (unless on windows)
        if debug and os.name == 'posix':
            auto_reload = True
        # Allow for overriding either of the defaults
        auto_reload = kwargs.get("auto_reload", auto_reload)

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (WebSocketProtocol
                        if self.websocket_enabled else HttpProtocol)
        if stop_event is not None:
            if debug:
                warnings.simplefilter('default')
            warnings.warn("stop_event will be removed from future versions.",
                          DeprecationWarning)
        server_settings = self._helper(
            host=host,
            port=port,
            debug=debug,
            ssl=ssl,
            sock=sock,
            workers=workers,
            protocol=protocol,
            backlog=backlog,
            register_sys_signals=register_sys_signals,
            access_log=access_log,
            auto_reload=auto_reload)

        try:
            self.is_running = True
            if workers == 1:
                if auto_reload and os.name != 'posix':
                    # This condition must be removed after implementing
                    # auto reloader for other operating systems.
                    raise NotImplementedError

                if auto_reload and \
                        os.environ.get('SANIC_SERVER_RUNNING') != 'true':
                    reloader_helpers.watchdog(2)
                else:
                    serve(**server_settings)
            else:
                serve_multiple(server_settings, workers)
        except BaseException:
            error_logger.exception(
                'Experienced exception while trying to serve')
            raise
        finally:
            self.is_running = False
        logger.info("Server Stopped")
Beispiel #11
0
    async def respond(
        self,
        response: Optional[BaseHTTPResponse] = None,
        *,
        status: int = 200,
        headers: Optional[Union[Header, Dict[str, str]]] = None,
        content_type: Optional[str] = None,
    ):
        """Respond to the request without returning.

        This method can only be called once, as you can only respond once.
        If no ``response`` argument is passed, one will be created from the
        ``status``, ``headers`` and ``content_type`` arguments.

        **The first typical usecase** is if you wish to respond to the
        request without returning from the handler:

        .. code-block:: python

            @app.get("/")
            async def handler(request: Request):
                data = ...  # Process something

                json_response = json({"data": data})
                await request.respond(json_response)

                # You are now free to continue executing other code
                ...

            @app.on_response
            async def add_header(_, response: HTTPResponse):
                # Middlewares still get executed as expected
                response.headers["one"] = "two"

        **The second possible usecase** is for when you want to directly
        respond to the request:

        .. code-block:: python

            response = await request.respond(content_type="text/csv")
            await response.send("foo,")
            await response.send("bar")

            # You can control the completion of the response by calling
            # the 'eof()' method:
            await response.eof()

        :param response: response instance to send
        :param status: status code to return in the response
        :param headers: headers to return in the response
        :param content_type: Content-Type header of the response
        :return: final response being sent (may be different from the
            ``response`` parameter because of middlewares) which can be
            used to manually send data
        """
        try:
            if self.stream is not None and self.stream.response:
                raise ServerError("Second respond call is not allowed.")
        except AttributeError:
            pass
        # This logic of determining which response to use is subject to change
        if response is None:
            response = HTTPResponse(
                status=status,
                headers=headers,
                content_type=content_type,
            )

        # Connect the response
        if isinstance(response, BaseHTTPResponse) and self.stream:
            response = self.stream.respond(response)
        # Run response middleware
        try:
            response = await self.app._run_response_middleware(
                self, response, request_name=self.name)
        except CancelledErrors:
            raise
        except Exception:
            error_logger.exception(
                "Exception occurred in one of response middleware handlers")
        self.responded = True
        return response
Beispiel #12
0
def serve(
        host,
        port,
        app: Sanic,
        ssl: Optional[SSLContext] = None,
        sock: Optional[socket.socket] = None,
        unix: Optional[str] = None,
        reuse_port: bool = False,
        loop=None,
        protocol: Type[asyncio.Protocol] = HttpProtocol,
        backlog: int = 100,
        register_sys_signals: bool = True,
        run_multiple: bool = False,
        run_async: bool = False,
        connections=None,
        signal=Signal(),
        state=None,
        asyncio_server_kwargs=None,
):
    """Start asynchronous HTTP Server on an individual process.

    :param host: Address to host on
    :param port: Port to host on
    :param before_start: function to be executed before the server starts
                         listening. Takes arguments `app` instance and `loop`
    :param after_start: function to be executed after the server starts
                        listening. Takes  arguments `app` instance and `loop`
    :param before_stop: function to be executed when a stop signal is
                        received before it is respected. Takes arguments
                        `app` instance and `loop`
    :param after_stop: function to be executed when a stop signal is
                       received after it is respected. Takes arguments
                       `app` instance and `loop`
    :param ssl: SSLContext
    :param sock: Socket for the server to accept connections from
    :param unix: Unix socket to listen on instead of TCP port
    :param reuse_port: `True` for multiple workers
    :param loop: asyncio compatible event loop
    :param run_async: bool: Do not create a new event loop for the server,
                      and return an AsyncServer object rather than running it
    :param asyncio_server_kwargs: key-value args for asyncio/uvloop
                                  create_server method
    :return: Nothing
    """
    if not run_async and not loop:
        # create new event_loop after fork
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

    if app.debug:
        loop.set_debug(app.debug)

    app.asgi = False

    connections = connections if connections is not None else set()
    protocol_kwargs = _build_protocol_kwargs(protocol, app.config)
    server = partial(
        protocol,
        loop=loop,
        connections=connections,
        signal=signal,
        app=app,
        state=state,
        unix=unix,
        **protocol_kwargs,
    )
    asyncio_server_kwargs = (asyncio_server_kwargs
                             if asyncio_server_kwargs else {})
    # UNIX sockets are always bound by us (to preserve semantics between modes)
    if unix:
        sock = bind_unix_socket(unix, backlog=backlog)
    server_coroutine = loop.create_server(
        server,
        None if sock else host,
        None if sock else port,
        ssl=ssl,
        reuse_port=reuse_port,
        sock=sock,
        backlog=backlog,
        **asyncio_server_kwargs,
    )

    if run_async:
        return AsyncioServer(
            app=app,
            loop=loop,
            serve_coro=server_coroutine,
            connections=connections,
        )

    loop.run_until_complete(app._startup())
    loop.run_until_complete(app._server_event("init", "before"))

    try:
        http_server = loop.run_until_complete(server_coroutine)
    except BaseException:
        error_logger.exception("Unable to start server")
        return

    # Ignore SIGINT when run_multiple
    if run_multiple:
        signal_func(SIGINT, SIG_IGN)

    # Register signals for graceful termination
    if register_sys_signals:
        if OS_IS_WINDOWS:
            ctrlc_workaround_for_windows(app)
        else:
            for _signal in [SIGTERM] if run_multiple else [SIGINT, SIGTERM]:
                loop.add_signal_handler(_signal, app.stop)

    loop.run_until_complete(app._server_event("init", "after"))
    pid = os.getpid()
    try:
        logger.info("Starting worker [%s]", pid)
        loop.run_forever()
    finally:
        logger.info("Stopping worker [%s]", pid)

        # Run the on_stop function if provided
        loop.run_until_complete(app._server_event("shutdown", "before"))

        # Wait for event loop to finish and all connections to drain
        http_server.close()
        loop.run_until_complete(http_server.wait_closed())

        # Complete all tasks on the loop
        signal.stopped = True
        for connection in connections:
            connection.close_if_idle()

        # Gracefully shutdown timeout.
        # We should provide graceful_shutdown_timeout,
        # instead of letting connection hangs forever.
        # Let's roughly calcucate time.
        graceful = app.config.GRACEFUL_SHUTDOWN_TIMEOUT
        start_shutdown: float = 0
        while connections and (start_shutdown < graceful):
            loop.run_until_complete(asyncio.sleep(0.1))
            start_shutdown = start_shutdown + 0.1

        # Force close non-idle connection after waiting for
        # graceful_shutdown_timeout
        for conn in connections:
            if hasattr(conn, "websocket") and conn.websocket:
                conn.websocket.fail_connection(code=1001)
            else:
                conn.abort()
        loop.run_until_complete(app._server_event("shutdown", "after"))

        remove_unix_socket(unix)
Beispiel #13
0
    def serve(cls, primary: Optional[Sanic] = None) -> None:
        apps = list(cls._app_registry.values())

        if not primary:
            try:
                primary = apps[0]
            except IndexError:
                raise RuntimeError("Did not find any applications.")

        reloader_start = primary.listeners.get("reload_process_start")
        reloader_stop = primary.listeners.get("reload_process_stop")
        # We want to run auto_reload if ANY of the applications have it enabled
        if (
            cls.should_auto_reload()
            and os.environ.get("SANIC_SERVER_RUNNING") != "true"
        ):  # no cov
            loop = new_event_loop()
            trigger_events(reloader_start, loop, primary)
            reload_dirs: Set[Path] = primary.state.reload_dirs.union(
                *(app.state.reload_dirs for app in apps)
            )
            reloader_helpers.watchdog(1.0, reload_dirs)
            trigger_events(reloader_stop, loop, primary)
            return

        # This exists primarily for unit testing
        if not primary.state.server_info:  # no cov
            for app in apps:
                app.state.server_info.clear()
            return

        primary_server_info = primary.state.server_info[0]
        primary.before_server_start(partial(primary._start_servers, apps=apps))

        try:
            primary_server_info.stage = ServerStage.SERVING

            if primary.state.workers > 1 and os.name != "posix":  # no cov
                logger.warn(
                    f"Multiprocessing is currently not supported on {os.name},"
                    " using workers=1 instead"
                )
                primary.state.workers = 1
            if primary.state.workers == 1:
                serve_single(primary_server_info.settings)
            elif primary.state.workers == 0:
                raise RuntimeError("Cannot serve with no workers")
            else:
                serve_multiple(
                    primary_server_info.settings, primary.state.workers
                )
        except BaseException:
            error_logger.exception(
                "Experienced exception while trying to serve"
            )
            raise
        finally:
            primary_server_info.stage = ServerStage.STOPPED
        logger.info("Server Stopped")
        for app in apps:
            app.state.server_info.clear()
            app.router.reset()
            app.signal_router.reset()
Beispiel #14
0
    async def handle_request(self, request, write_callback, stream_callback):
        # Define `response` var here to remove warnings about
        # allocation before assignment below.
        response = None
        cancelled = False
        middlewares = []
        try:
            # --------------------------------------------------------------- #
            # request "global" middlewares
            # --------------------------------------------------------------- #
            request.app = self
            if self.request_middleware:
                response = await self._run_request_middleware(
                    request, self.request_middleware
                )
            # No middleware result
            if not response:
                # Fetch handler from router
                handler, middlewares, kwargs, uri = self.router.get(request)
                request.uri_template = uri
                # handler = self.request.route_handlers.endpoint
                # middlewares = self.request.route_handlers.middlewares
                # kwargs = self.request.route_params

                # run layered request middlewares
                request_middleware = [
                    m.handler
                    for m in middlewares
                    if m.attach_to == MiddlewareType.REQUEST
                ]

                if request_middleware:
                    response = await self._run_request_middleware(
                        request, request_middleware
                    )

                if not response:
                    # run response handler
                    ret = await self.resolver.resolve(
                        request=request, func=handler, prefetched=kwargs
                    )
                    response = handler(**ret)
                    if isawaitable(response):
                        response = await response
        except CancelledError:
            # If response handler times out, the server handles the error
            # and cancels the handle_request job.
            # In this case, the transport is already closed and we cannot
            # issue a response.
            response = None
            cancelled = True
        except Exception as e:  # noqa this was copied from Sanic "as is"
            # -------------------------------------------- #
            # Response Generation Failed
            # -------------------------------------------- #

            try:
                response = self.error_handler.response(request, e)
                if isawaitable(response):
                    response = await response
            except Exception as e:
                if isinstance(e, SanicException):
                    response = self.error_handler.default(
                        request=request, exception=e
                    )
                elif self.debug:
                    response = HTTPResponse(
                        "Error while handling error: {}\nStack: {}".format(
                            e, format_exc()
                        ),
                        status=500,
                    )
                else:
                    response = HTTPResponse(
                        "An error occurred while handling an error", status=500
                    )
        finally:
            # --------------------------------------------------------------- #
            # Response Middleware
            # --------------------------------------------------------------- #
            # Don't run response middleware if response is None
            if response is not None:
                try:
                    if self.response_middleware:
                        response = await self._run_response_middleware(
                            request, response, self.response_middleware
                        )
                    # run layered response middlewares
                    response_middleware = [
                        m.handler
                        for m in middlewares
                        if m.attach_to == MiddlewareType.RESPONSE
                    ]

                    if response_middleware:
                        response = await self._run_response_middleware(
                            request, response, response_middleware
                        )

                except CancelledError:
                    # Response middleware can timeout too, as above.
                    response = None
                    cancelled = True
                except BaseException:
                    error_logger.exception(
                        "Exception occurred in one of response "
                        "middleware handlers"
                    )
            if cancelled:
                raise CancelledError()

        # pass the response to the correct callback
        if isinstance(response, StreamingHTTPResponse):  # noqa copied code
            await stream_callback(response)
        else:
            write_callback(response)
Beispiel #15
0
    async def handle_request(self, request, write_callback, stream_callback):
        """Take a request from the HTTP Server and return a response object
        to be sent back The HTTP Server only expects a response object, so
        exception handling must be done here

        :param request: HTTP Request object
        :param write_callback: Synchronous response function to be
            called with the response as the only argument
        :param stream_callback: Coroutine that handles streaming a
            StreamingHTTPResponse if produced by the handler.

        :return: Nothing
        """
        try:
            # -------------------------------------------- #
            # Request Middleware
            # -------------------------------------------- #

            request.app = self
            response = await self._run_request_middleware(request)
            # No middleware results
            if not response:
                # -------------------------------------------- #
                # Execute Handler
                # -------------------------------------------- #

                # Fetch handler from router
                handler, args, kwargs, uri = self.router.get(request)
                request.uri_template = uri
                if handler is None:
                    raise ServerError(
                        ("'None' was returned while requesting a "
                         "handler from the router"))

                # Run response handler
                response = handler(request, *args, **kwargs)
                if isawaitable(response):
                    response = await response
        except Exception as e:
            # -------------------------------------------- #
            # Response Generation Failed
            # -------------------------------------------- #

            try:
                response = self.error_handler.response(request, e)
                if isawaitable(response):
                    response = await response
            except Exception as e:
                if self.debug:
                    response = HTTPResponse(
                        "Error while handling error: {}\nStack: {}".format(
                            e, format_exc()))
                else:
                    response = HTTPResponse(
                        "An error occurred while handling an error")
        finally:
            # -------------------------------------------- #
            # Response Middleware
            # -------------------------------------------- #
            try:
                response = await self._run_response_middleware(request,
                                                               response)
            except BaseException:
                error_logger.exception(
                    'Exception occurred in one of response middleware handlers'
                )

        # pass the response to the correct callback
        if isinstance(response, StreamingHTTPResponse):
            await stream_callback(response)
        else:
            write_callback(response)
Beispiel #16
0
def main():
    parser = SanicArgumentParser(
        prog="sanic",
        description=BASE_LOGO,
        formatter_class=RawDescriptionHelpFormatter,
    )
    parser.add_argument(
        "-H",
        "--host",
        dest="host",
        type=str,
        default="127.0.0.1",
        help="host address [default 127.0.0.1]",
    )
    parser.add_argument(
        "-p",
        "--port",
        dest="port",
        type=int,
        default=8000,
        help="port to serve on [default 8000]",
    )
    parser.add_argument(
        "-u",
        "--unix",
        dest="unix",
        type=str,
        default="",
        help="location of unix socket",
    )
    parser.add_argument("--cert",
                        dest="cert",
                        type=str,
                        help="location of certificate for SSL")
    parser.add_argument("--key",
                        dest="key",
                        type=str,
                        help="location of keyfile for SSL.")
    parser.add_argument(
        "-w",
        "--workers",
        dest="workers",
        type=int,
        default=1,
        help="number of worker processes [default 1]",
    )
    parser.add_argument("--debug", dest="debug", action="store_true")
    parser.add_bool_arguments("--access-logs",
                              dest="access_log",
                              help="display access logs")
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"Sanic {__version__}",
    )
    parser.add_argument(
        "module", help="path to your Sanic app. Example: path.to.server:app")
    args = parser.parse_args()

    try:
        module_path = os.path.abspath(os.getcwd())
        if module_path not in sys.path:
            sys.path.append(module_path)

        if ":" in args.module:
            module_name, app_name = args.module.rsplit(":", 1)
        else:
            module_parts = args.module.split(".")
            module_name = ".".join(module_parts[:-1])
            app_name = module_parts[-1]

        module = import_module(module_name)
        app = getattr(module, app_name, None)
        app_name = type(app).__name__

        if not isinstance(app, Sanic):
            raise ValueError(
                f"Module is not a Sanic app, it is a {app_name}.  "
                f"Perhaps you meant {args.module}.app?")
        if args.cert is not None or args.key is not None:
            ssl = {
                "cert": args.cert,
                "key": args.key,
            }  # type: Optional[Dict[str, Any]]
        else:
            ssl = None

        app.run(
            host=args.host,
            port=args.port,
            unix=args.unix,
            workers=args.workers,
            debug=args.debug,
            access_log=args.access_log,
            ssl=ssl,
        )
    except ImportError as e:
        error_logger.error(f"No module named {e.name} found.\n"
                           f"  Example File: project/sanic_server.py -> app\n"
                           f"  Example Module: project.sanic_server.app")
    except ValueError:
        error_logger.exception("Failed to run app")