
管理员
站长
- UID
- 1
- 积分
- 224402
- 余额
- 13 R
- Moe币
- 84883
- 在线时间
- 279 小时
- 注册时间
- 2025-12-28
- 最后登录
- 2026-11-13
|
See :func:`~trio.open_tcp_listeners` for details.
host: Network interfaces the server binds to.
See :func:`~trio.open_tcp_listeners` for details.
backlog: Listen backlog. See :func:`~trio.open_tcp_listeners` for
details.
listeners: Preexisting TCP listeners. ``listeners`` replaces ``port``,
``host``, and ``backlog``. See :func:`trio.serve_listeners` for
details.
ssl: Configuration for enabling TLS on the connection.
origins: Acceptable values of the ``Origin`` header, for defending
against Cross-Site WebSocket Hijacking attacks. Values can be
:class:`str` to test for an exact match or regular expressions
compiled by :func:`re.compile` to test against a pattern. Include
:obj:`None` in the list if the lack of an origin is acceptable.
extensions: List of supported extensions, in order in which they
should be negotiated and run.
subprotocols: List of supported subprotocols, in order of decreasing
preference.
select_subprotocol: Callback for selecting a subprotocol among
those supported by the client and the server. It receives a
:class:`ServerConnection` (not a
:class:`~websockets.server.ServerProtocol`!) instance and a list of
subprotocols offered by the client. Other than the first argument,
it has the same behavior as the
:meth:`ServerProtocol.select_subprotocol
<websockets.server.ServerProtocol.select_subprotocol>` method.
compression: The "permessage-deflate" extension is enabled by default.
Set ``compression`` to :obj:`None` to disable it. See the
:doc:`compression guide <../../topics/compression>` for details.
process_request: Intercept the request during the opening handshake.
Return an HTTP response to force the response or :obj:`None` to
continue normally. When you force an HTTP 101 Continue response, the
handshake is successful. Else, the connection is aborted.
``process_request`` may be a function or a coroutine.
process_response: Intercept the response during the opening handshake.
Return an HTTP response to force the response or :obj:`None` to
continue normally. When you force an HTTP 101 Continue response, the
handshake is successful. Else, the connection is aborted.
``process_response`` may be a function or a coroutine.
server_header: Value of the ``Server`` response header.
It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to
:obj:`None` removes the header.
open_timeout: Timeout for opening connections in seconds.
:obj:`None` disables the timeout.
ping_interval: Interval between keepalive pings in seconds.
:obj:`None` disables keepalive.
ping_timeout: Timeout for keepalive pings in seconds.
:obj:`None` disables timeouts.
close_timeout: Timeout for closing connections in seconds.
:obj:`None` disables the timeout.
max_size: Maximum size of incoming messages in bytes.
:obj:`None` disables the limit. You may pass a ``(max_message_size,
max_fragment_size)`` tuple to set different limits for messages and
fragments when you expect long messages sent in short fragments.
max_queue: High-water mark of the buffer where frames are received.
It defaults to 16 frames. The low-water mark defaults to ``max_queue
// 4``. You may pass a ``(high, low)`` tuple to set the high-water
and low-water marks. If you want to disable flow control entirely,
you may set it to ``None``, although that's a bad idea.
logger: Logger for this server.
It defaults to ``logging.getLogger("websockets.server")``.
See the :doc:`logging guide <../../topics/logging>` for details.
create_connection: Factory for the :class:`ServerConnection` managing
the connection. Set it to a wrapper or a subclass to customize
connection handling.
task_status: For compatibility with :meth:`nursery.start
<trio.Nursery.start>`.
"""
# Process parameters
if subprotocols is not None:
validate_subprotocols(subprotocols)
if compression == "deflate":
extensions = enable_server_permessage_deflate(extensions)
elif compression is not None:
raise ValueError(f"unsupported compression: {compression}")
if create_connection is None:
create_connection = ServerConnection
# Create listeners
if listeners is None:
if port is None:
raise ValueError("port is required when listeners is not provided")
listeners = await trio.open_tcp_listeners(port, host=host, backlog=backlog)
else:
if port is not None:
raise ValueError("port is incompatible with listeners")
if host is not None:
raise ValueError("host is incompatible with listeners")
if backlog is not None:
raise ValueError("backlog is incompatible with listeners")
async def stream_handler(stream: trio.abc.Stream) -> None:
"""
Handle the lifecycle of a WebSocket connection.
Since this coroutine doesn't have a caller that can handle
exceptions, it attempts to log relevant ones.
It guarantees that the TCP connection is closed before exiting.
"""
async with trio.open_nursery() as nursery:
try:
# Apply open_timeout to the TLS and WebSocket handshake.
with (
trio.CancelScope()
if open_timeout is None
else trio.fail_after(open_timeout)
):
# Enable TLS.
if ssl is not None:
# Wrap with SSLStream here rather than with TLSListener
# in order to include the TLS handshake within open_timeout.
stream = trio.SSLStream(
stream,
ssl,
server_side=True,
https_compatible=True,
)
assert isinstance(stream, trio.SSLStream) # help mypy
try:
await stream.do_handshake()
except trio.BrokenResourceError:
return
# Create a closure to give select_subprotocol access to connection.
protocol_select_subprotocol: (
Callable[
[ServerProtocol, Sequence[Subprotocol]],
Subprotocol | None,
]
| None
) = None
if select_subprotocol is not None:
def protocol_select_subprotocol(
protocol: ServerProtocol,
subprotocols: Sequence[Subprotocol],
) -> Subprotocol | None:
# mypy doesn't know that select_subprotocol is immutable.
assert select_subprotocol is not None
# Ensure this function is only used in the intended context.
assert protocol is connection.protocol
return select_subprotocol(connection, subprotocols)
# Initialize WebSocket protocol.
protocol = ServerProtocol(
origins=origins,
extensions=extensions,
subprotocols=subprotocols,
select_subprotocol=protocol_select_subprotocol,
max_size=max_size,
logger=logger,
)
# Initialize WebSocket connection.
connection = create_connection(
nursery,
stream,
protocol,
server,
ping_interval=ping_interval,
ping_timeout=ping_timeout,
close_timeout=close_timeout,
max_queue=max_queue,
)
await connection.handshake(
process_request,
process_response,
server_header,
)
if connection.protocol.state is not OPEN:
await connection.close_stream()
return
server.all_connections.add(connection)
connection.start_keepalive()
try:
await handler(connection)
except Exception:
connection.logger.error("connection handler failed", exc_info=True)
await connection.aclose(CloseCode.INTERNAL_ERROR)
else:
await connection.aclose()
finally:
server.all_connections.discard(connection)
except Exception:
# Don't leak connections when the opening handshake times out or
# an unexpected error occurs.
await trio.aclose_forcefully(stream)
# The server variable is captured by the closure of conn_handler().
server = Server(listeners, stream_handler, logger)
await server.serve_forever(task_status=task_status)
|
|