Standardize init of exceptions (#2545)

This commit is contained in:
Adam Hopkins 2023-03-20 14:50:50 +02:00 committed by GitHub
parent 89188f5fc6
commit 71cd53b64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 481 additions and 51 deletions

View File

@ -2,6 +2,22 @@ from sanic.__version__ import __version__
from sanic.app import Sanic from sanic.app import Sanic
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.constants import HTTPMethod from sanic.constants import HTTPMethod
from sanic.exceptions import (
BadRequest,
ExpectationFailed,
FileNotFound,
Forbidden,
HeaderNotFound,
InternalServerError,
InvalidHeader,
MethodNotAllowed,
NotFound,
RangeNotSatisfiable,
SanicException,
ServerError,
ServiceUnavailable,
Unauthorized,
)
from sanic.request import Request from sanic.request import Request
from sanic.response import ( from sanic.response import (
HTTPResponse, HTTPResponse,
@ -9,6 +25,7 @@ from sanic.response import (
file, file,
html, html,
json, json,
raw,
redirect, redirect,
text, text,
) )
@ -17,16 +34,34 @@ from sanic.server.websockets.impl import WebsocketImplProtocol as Websocket
__all__ = ( __all__ = (
"__version__", "__version__",
# Common objects
"Sanic", "Sanic",
"Blueprint", "Blueprint",
"HTTPMethod", "HTTPMethod",
"HTTPResponse", "HTTPResponse",
"Request", "Request",
"Websocket", "Websocket",
# Common exceptions
"BadRequest",
"ExpectationFailed",
"FileNotFound",
"Forbidden",
"HeaderNotFound",
"InternalServerError",
"InvalidHeader",
"MethodNotAllowed",
"NotFound",
"RangeNotSatisfiable",
"SanicException",
"ServerError",
"ServiceUnavailable",
"Unauthorized",
# Common response methods
"empty", "empty",
"file", "file",
"html", "html",
"json", "json",
"raw",
"redirect", "redirect",
"text", "text",
) )

View File

@ -1,5 +1,6 @@
from asyncio import CancelledError from asyncio import CancelledError, Protocol
from typing import Any, Dict, Optional, Union from os import PathLike
from typing import Any, Dict, Optional, Sequence, Union
from sanic.helpers import STATUS_CODES from sanic.helpers import STATUS_CODES
@ -9,51 +10,158 @@ class RequestCancelled(CancelledError):
class ServerKilled(Exception): class ServerKilled(Exception):
... """
Exception Sanic server uses when killing a server process for something
unexpected happening.
"""
class SanicException(Exception): class SanicException(Exception):
"""
Generic exception that will generate an HTTP response when raised
in the context of a request lifecycle.
Usually it is best practice to use one of the more specific exceptions
than this generic. Even when trying to raise a 500, it is generally
preferrable to use :class:`.ServerError`
.. code-block:: python
raise SanicException(
"Something went wrong",
status_code=999,
context={
"info": "Some additional details",
},
headers={
"X-Foo": "bar"
}
)
:param message: The message to be sent to the client. If ``None``
then the appropriate HTTP response status message will be used
instead, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param status_code: The HTTP response code to send, if applicable. If
``None``, then it will be 500, defaults to None
:type status_code: Optional[int], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
"""
status_code: int = 500
quiet: Optional[bool] = False
headers: Dict[str, str] = {}
message: str = "" message: str = ""
def __init__( def __init__(
self, self,
message: Optional[Union[str, bytes]] = None, message: Optional[Union[str, bytes]] = None,
status_code: Optional[int] = None, status_code: Optional[int] = None,
*,
quiet: Optional[bool] = None, quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None, context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None, extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
) -> None: ) -> None:
self.context = context self.context = context
self.extra = extra self.extra = extra
status_code = status_code or getattr(
self.__class__, "status_code", None
)
quiet = quiet or getattr(self.__class__, "quiet", None)
headers = headers or getattr(self.__class__, "headers", {})
if message is None: if message is None:
if self.message: if self.message:
message = self.message message = self.message
elif status_code is not None: elif status_code:
msg: bytes = STATUS_CODES.get(status_code, b"") msg: bytes = STATUS_CODES.get(status_code, b"")
message = msg.decode("utf8") message = msg.decode("utf8")
super().__init__(message) super().__init__(message)
if status_code is not None: self.status_code = status_code
self.status_code = status_code self.quiet = quiet
self.headers = headers
# quiet=None/False/True with None meaning choose by status
if quiet or quiet is None and status_code not in (None, 500):
self.quiet = True
class NotFound(SanicException): class HTTPException(SanicException):
"""
A base class for other exceptions and should not be called directly.
"""
def __init__(
self,
message: Optional[Union[str, bytes]] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
) -> None:
super().__init__(
message,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
class NotFound(HTTPException):
""" """
**Status**: 404 Not Found **Status**: 404 Not Found
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Not Found' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 404 status_code = 404
quiet = True quiet = True
class BadRequest(SanicException): class BadRequest(HTTPException):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Bad Request' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 400 status_code = 400
@ -61,51 +169,133 @@ class BadRequest(SanicException):
InvalidUsage = BadRequest InvalidUsage = BadRequest
BadURL = BadRequest
class BadURL(BadRequest): class MethodNotAllowed(HTTPException):
...
class MethodNotAllowed(SanicException):
""" """
**Status**: 405 Method Not Allowed **Status**: 405 Method Not Allowed
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Method Not Allowed' will be sent,
defaults to None
:type message: Optional[Union[str, bytes]], optional
:param method: The HTTP method that was used, defaults to an empty string
:type method: Optional[str], optional
:param allowed_methods: The HTTP methods that can be used instead of the
one that was attempted
:type allowed_methods: Optional[Sequence[str]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 405 status_code = 405
quiet = True quiet = True
def __init__(self, message, method, allowed_methods): def __init__(
super().__init__(message) self,
self.headers = {"Allow": ", ".join(allowed_methods)} message: Optional[Union[str, bytes]] = None,
method: str = "",
allowed_methods: Optional[Sequence[str]] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
):
super().__init__(
message,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
if allowed_methods:
self.headers = {
**self.headers,
"Allow": ", ".join(allowed_methods),
}
self.method = method
self.allowed_methods = allowed_methods
MethodNotSupported = MethodNotAllowed MethodNotSupported = MethodNotAllowed
class ServerError(SanicException): class ServerError(HTTPException):
""" """
**Status**: 500 Internal Server Error **Status**: 500 Internal Server Error
A general server-side error has occurred. If no other HTTP exception is
appropriate, then this should be used
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Internal Server Error' will be sent,
defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 500 status_code = 500
class ServiceUnavailable(SanicException): InternalServerError = ServerError
class ServiceUnavailable(HTTPException):
""" """
**Status**: 503 Service Unavailable **Status**: 503 Service Unavailable
The server is currently unavailable (because it is overloaded or The server is currently unavailable (because it is overloaded or
down for maintenance). Generally, this is a temporary state. down for maintenance). Generally, this is a temporary state.
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Bad Request' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 503 status_code = 503
quiet = True quiet = True
class URLBuildError(ServerError): class URLBuildError(HTTPException):
""" """
**Status**: 500 Internal Server Error **Status**: 500 Internal Server Error
An exception used by Sanic internals when unable to build a URL.
""" """
status_code = 500 status_code = 500
@ -114,30 +304,77 @@ class URLBuildError(ServerError):
class FileNotFound(NotFound): class FileNotFound(NotFound):
""" """
**Status**: 404 Not Found **Status**: 404 Not Found
A specific form of :class:`.NotFound` that is specifically when looking
for a file on the file system at a known path.
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Not Found' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param path: The path, if any, to the file that could not
be found, defaults to None
:type path: Optional[PathLike], optional
:param relative_url: A relative URL of the file, defaults to None
:type relative_url: Optional[str], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
def __init__(self, message, path, relative_url): def __init__(
super().__init__(message) self,
message: Optional[Union[str, bytes]] = None,
path: Optional[PathLike] = None,
relative_url: Optional[str] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
):
super().__init__(
message,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
self.path = path self.path = path
self.relative_url = relative_url self.relative_url = relative_url
class RequestTimeout(SanicException): class RequestTimeout(HTTPException):
"""The Web server (running the Web site) thinks that there has been too """
The Web server (running the Web site) thinks that there has been too
long an interval of time between 1) the establishment of an IP long an interval of time between 1) the establishment of an IP
connection (socket) between the client and the server and connection (socket) between the client and the server and
2) the receipt of any data on that socket, so the server has dropped 2) the receipt of any data on that socket, so the server has dropped
the connection. The socket connection has actually been lost - the Web the connection. The socket connection has actually been lost - the Web
server has 'timed out' on that particular socket connection. server has 'timed out' on that particular socket connection.
This is an internal exception thrown by Sanic and should not be used
directly.
""" """
status_code = 408 status_code = 408
quiet = True quiet = True
class PayloadTooLarge(SanicException): class PayloadTooLarge(HTTPException):
""" """
**Status**: 413 Payload Too Large **Status**: 413 Payload Too Large
This is an internal exception thrown by Sanic and should not be used
directly.
""" """
status_code = 413 status_code = 413
@ -147,34 +384,126 @@ class PayloadTooLarge(SanicException):
class HeaderNotFound(BadRequest): class HeaderNotFound(BadRequest):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Bad Request' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
class InvalidHeader(BadRequest): class InvalidHeader(BadRequest):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Bad Request' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
class RangeNotSatisfiable(SanicException): class ContentRange(Protocol):
total: int
class RangeNotSatisfiable(HTTPException):
""" """
**Status**: 416 Range Not Satisfiable **Status**: 416 Range Not Satisfiable
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Range Not Satisfiable' will be sent,
defaults to None
:type message: Optional[Union[str, bytes]], optional
:param content_range: An object meeting the :class:`.ContentRange` protocol
that has a ``total`` property, defaults to None
:type content_range: Optional[ContentRange], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 416 status_code = 416
quiet = True quiet = True
def __init__(self, message, content_range): def __init__(
super().__init__(message) self,
self.headers = {"Content-Range": f"bytes */{content_range.total}"} message: Optional[Union[str, bytes]] = None,
content_range: Optional[ContentRange] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
):
super().__init__(
message,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
if content_range is not None:
self.headers = {
**self.headers,
"Content-Range": f"bytes */{content_range.total}",
}
ContentRangeError = RangeNotSatisfiable ContentRangeError = RangeNotSatisfiable
class ExpectationFailed(SanicException): class ExpectationFailed(HTTPException):
""" """
**Status**: 417 Expectation Failed **Status**: 417 Expectation Failed
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Expectation Failed' will be sent,
defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 417 status_code = 417
@ -184,9 +513,25 @@ class ExpectationFailed(SanicException):
HeaderExpectationFailed = ExpectationFailed HeaderExpectationFailed = ExpectationFailed
class Forbidden(SanicException): class Forbidden(HTTPException):
""" """
**Status**: 403 Forbidden **Status**: 403 Forbidden
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Forbidden' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 403 status_code = 403
@ -202,20 +547,33 @@ class InvalidRangeType(RangeNotSatisfiable):
quiet = True quiet = True
class PyFileError(Exception): class PyFileError(SanicException):
def __init__(self, file): def __init__(
super().__init__("could not execute config file %s", file) self,
file,
status_code: Optional[int] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
):
super().__init__(
"could not execute config file %s" % file,
status_code=status_code,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
class Unauthorized(SanicException): class Unauthorized(HTTPException):
""" """
**Status**: 401 Unauthorized **Status**: 401 Unauthorized
:param message: Message describing the exception. When present, additional keyword arguments may be used to complete
:param status_code: HTTP Status code. the WWW-Authentication header.
:param scheme: Name of the authentication scheme to be used.
When present, kwargs is used to complete the WWW-Authentication header.
Examples:: Examples::
@ -240,21 +598,58 @@ class Unauthorized(SanicException):
raise Unauthorized("Auth required.", raise Unauthorized("Auth required.",
scheme="Bearer", scheme="Bearer",
realm="Restricted Area") realm="Restricted Area")
:param message: The message to be sent to the client. If ``None``
then the HTTP status 'Bad Request' will be sent, defaults to None
:type message: Optional[Union[str, bytes]], optional
:param scheme: Name of the authentication scheme to be used.
:type scheme: Optional[str], optional
:param quiet: When ``True``, the error traceback will be suppressed
from the logs, defaults to None
:type quiet: Optional[bool], optional
:param context: Additional mapping of key/value data that will be
sent to the client upon exception, defaults to None
:type context: Optional[Dict[str, Any]], optional
:param extra: Additional mapping of key/value data that will NOT be
sent to the client when in PRODUCTION mode, defaults to None
:type extra: Optional[Dict[str, Any]], optional
:param headers: Additional headers that should be sent with the HTTP
response, defaults to None
:type headers: Optional[Dict[str, Any]], optional
""" """
status_code = 401 status_code = 401
quiet = True quiet = True
def __init__(self, message, status_code=None, scheme=None, **kwargs): def __init__(
super().__init__(message, status_code) self,
message: Optional[Union[str, bytes]] = None,
scheme: Optional[str] = None,
*,
quiet: Optional[bool] = None,
context: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, Any]] = None,
**challenges,
):
super().__init__(
message,
quiet=quiet,
context=context,
extra=extra,
headers=headers,
)
# if auth-scheme is specified, set "WWW-Authenticate" header # if auth-scheme is specified, set "WWW-Authenticate" header
if scheme is not None: if scheme is not None:
values = ['{!s}="{!s}"'.format(k, v) for k, v in kwargs.items()] values = [
'{!s}="{!s}"'.format(k, v) for k, v in challenges.items()
]
challenge = ", ".join(values) challenge = ", ".join(values)
self.headers = { self.headers = {
"WWW-Authenticate": f"{scheme} {challenge}".rstrip() **self.headers,
"WWW-Authenticate": f"{scheme} {challenge}".rstrip(),
} }

View File

@ -75,4 +75,4 @@ class ContentRangeHandler:
} }
def __bool__(self): def __bool__(self):
return self.size > 0 return hasattr(self, "size") and self.size > 0

View File

@ -10,7 +10,7 @@ except ImportError: # websockets >= 11.0
from websockets.typing import Subprotocol from websockets.typing import Subprotocol
from sanic.exceptions import ServerError from sanic.exceptions import SanicException
from sanic.log import logger from sanic.log import logger
from sanic.server import HttpProtocol from sanic.server import HttpProtocol
@ -123,7 +123,7 @@ class WebSocketProtocol(HttpProtocol):
"Failed to open a WebSocket connection.\n" "Failed to open a WebSocket connection.\n"
"See server log for more information.\n" "See server log for more information.\n"
) )
raise ServerError(msg, status_code=500) raise SanicException(msg, status_code=500)
if 100 <= resp.status_code <= 299: if 100 <= resp.status_code <= 299:
first_line = ( first_line = (
f"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\r\n" f"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\r\n"
@ -138,7 +138,7 @@ class WebSocketProtocol(HttpProtocol):
rbody += b"\r\n\r\n" rbody += b"\r\n\r\n"
await super().send(rbody) await super().send(rbody)
else: else:
raise ServerError(resp.body, resp.status_code) raise SanicException(resp.body, resp.status_code)
self.websocket = WebsocketImplProtocol( self.websocket = WebsocketImplProtocol(
ws_proto, ws_proto,
ping_interval=self.websocket_ping_interval, ping_interval=self.websocket_ping_interval,