uvicorn 0.34.3__py3-none-any.whl → 0.35.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uvicorn/__init__.py +1 -1
- uvicorn/config.py +2 -1
- uvicorn/main.py +1 -1
- uvicorn/protocols/websockets/websockets_sansio_impl.py +417 -0
- uvicorn/server.py +2 -1
- {uvicorn-0.34.3.dist-info → uvicorn-0.35.0.dist-info}/METADATA +1 -1
- {uvicorn-0.34.3.dist-info → uvicorn-0.35.0.dist-info}/RECORD +10 -9
- {uvicorn-0.34.3.dist-info → uvicorn-0.35.0.dist-info}/WHEEL +0 -0
- {uvicorn-0.34.3.dist-info → uvicorn-0.35.0.dist-info}/entry_points.txt +0 -0
- {uvicorn-0.34.3.dist-info → uvicorn-0.35.0.dist-info}/licenses/LICENSE.md +0 -0
uvicorn/__init__.py
CHANGED
uvicorn/config.py
CHANGED
@@ -25,7 +25,7 @@ from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
|
25
25
|
from uvicorn.middleware.wsgi import WSGIMiddleware
|
26
26
|
|
27
27
|
HTTPProtocolType = Literal["auto", "h11", "httptools"]
|
28
|
-
WSProtocolType = Literal["auto", "none", "websockets", "wsproto"]
|
28
|
+
WSProtocolType = Literal["auto", "none", "websockets", "websockets-sansio", "wsproto"]
|
29
29
|
LifespanType = Literal["auto", "on", "off"]
|
30
30
|
LoopSetupType = Literal["none", "auto", "asyncio", "uvloop"]
|
31
31
|
InterfaceType = Literal["auto", "asgi3", "asgi2", "wsgi"]
|
@@ -47,6 +47,7 @@ WS_PROTOCOLS: dict[WSProtocolType, str | None] = {
|
|
47
47
|
"auto": "uvicorn.protocols.websockets.auto:AutoWebSocketsProtocol",
|
48
48
|
"none": None,
|
49
49
|
"websockets": "uvicorn.protocols.websockets.websockets_impl:WebSocketProtocol",
|
50
|
+
"websockets-sansio": "uvicorn.protocols.websockets.websockets_sansio_impl:WebSocketsSansIOProtocol",
|
50
51
|
"wsproto": "uvicorn.protocols.websockets.wsproto_impl:WSProtocol",
|
51
52
|
}
|
52
53
|
LIFESPAN: dict[LifespanType, str] = {
|
uvicorn/main.py
CHANGED
@@ -223,7 +223,7 @@ def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> No
|
|
223
223
|
"--proxy-headers/--no-proxy-headers",
|
224
224
|
is_flag=True,
|
225
225
|
default=True,
|
226
|
-
help="Enable/Disable X-Forwarded-Proto, X-Forwarded-For
|
226
|
+
help="Enable/Disable X-Forwarded-Proto, X-Forwarded-For to populate url scheme and remote address info.",
|
227
227
|
)
|
228
228
|
@click.option(
|
229
229
|
"--server-header/--no-server-header",
|
@@ -0,0 +1,417 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import logging
|
5
|
+
import sys
|
6
|
+
from asyncio.transports import BaseTransport, Transport
|
7
|
+
from http import HTTPStatus
|
8
|
+
from typing import Any, Literal, cast
|
9
|
+
from urllib.parse import unquote
|
10
|
+
|
11
|
+
from websockets.exceptions import InvalidState
|
12
|
+
from websockets.extensions.permessage_deflate import ServerPerMessageDeflateFactory
|
13
|
+
from websockets.frames import Frame, Opcode
|
14
|
+
from websockets.http11 import Request
|
15
|
+
from websockets.server import ServerProtocol
|
16
|
+
|
17
|
+
from uvicorn._types import (
|
18
|
+
ASGIReceiveEvent,
|
19
|
+
ASGISendEvent,
|
20
|
+
WebSocketAcceptEvent,
|
21
|
+
WebSocketCloseEvent,
|
22
|
+
WebSocketResponseBodyEvent,
|
23
|
+
WebSocketResponseStartEvent,
|
24
|
+
WebSocketScope,
|
25
|
+
WebSocketSendEvent,
|
26
|
+
)
|
27
|
+
from uvicorn.config import Config
|
28
|
+
from uvicorn.logging import TRACE_LOG_LEVEL
|
29
|
+
from uvicorn.protocols.utils import (
|
30
|
+
ClientDisconnected,
|
31
|
+
get_local_addr,
|
32
|
+
get_path_with_query_string,
|
33
|
+
get_remote_addr,
|
34
|
+
is_ssl,
|
35
|
+
)
|
36
|
+
from uvicorn.server import ServerState
|
37
|
+
|
38
|
+
if sys.version_info >= (3, 11): # pragma: no cover
|
39
|
+
from typing import assert_never
|
40
|
+
else: # pragma: no cover
|
41
|
+
from typing_extensions import assert_never
|
42
|
+
|
43
|
+
|
44
|
+
class WebSocketsSansIOProtocol(asyncio.Protocol):
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
config: Config,
|
48
|
+
server_state: ServerState,
|
49
|
+
app_state: dict[str, Any],
|
50
|
+
_loop: asyncio.AbstractEventLoop | None = None,
|
51
|
+
) -> None:
|
52
|
+
if not config.loaded:
|
53
|
+
config.load() # pragma: no cover
|
54
|
+
|
55
|
+
self.config = config
|
56
|
+
self.app = config.loaded_app
|
57
|
+
self.loop = _loop or asyncio.get_event_loop()
|
58
|
+
self.logger = logging.getLogger("uvicorn.error")
|
59
|
+
self.root_path = config.root_path
|
60
|
+
self.app_state = app_state
|
61
|
+
|
62
|
+
# Shared server state
|
63
|
+
self.connections = server_state.connections
|
64
|
+
self.tasks = server_state.tasks
|
65
|
+
self.default_headers = server_state.default_headers
|
66
|
+
|
67
|
+
# Connection state
|
68
|
+
self.transport: asyncio.Transport = None # type: ignore[assignment]
|
69
|
+
self.server: tuple[str, int] | None = None
|
70
|
+
self.client: tuple[str, int] | None = None
|
71
|
+
self.scheme: Literal["wss", "ws"] = None # type: ignore[assignment]
|
72
|
+
|
73
|
+
# WebSocket state
|
74
|
+
self.queue: asyncio.Queue[ASGIReceiveEvent] = asyncio.Queue()
|
75
|
+
self.handshake_initiated = False
|
76
|
+
self.handshake_complete = False
|
77
|
+
self.close_sent = False
|
78
|
+
self.initial_response: tuple[int, list[tuple[str, str]], bytes] | None = None
|
79
|
+
|
80
|
+
extensions = []
|
81
|
+
if self.config.ws_per_message_deflate:
|
82
|
+
extensions = [
|
83
|
+
ServerPerMessageDeflateFactory(
|
84
|
+
server_max_window_bits=12,
|
85
|
+
client_max_window_bits=12,
|
86
|
+
compress_settings={"memLevel": 5},
|
87
|
+
)
|
88
|
+
]
|
89
|
+
self.conn = ServerProtocol(
|
90
|
+
extensions=extensions,
|
91
|
+
max_size=self.config.ws_max_size,
|
92
|
+
logger=logging.getLogger("uvicorn.error"),
|
93
|
+
)
|
94
|
+
|
95
|
+
self.read_paused = False
|
96
|
+
self.writable = asyncio.Event()
|
97
|
+
self.writable.set()
|
98
|
+
|
99
|
+
# Buffers
|
100
|
+
self.bytes = b""
|
101
|
+
|
102
|
+
def connection_made(self, transport: BaseTransport) -> None:
|
103
|
+
"""Called when a connection is made."""
|
104
|
+
transport = cast(Transport, transport)
|
105
|
+
self.connections.add(self)
|
106
|
+
self.transport = transport
|
107
|
+
self.server = get_local_addr(transport)
|
108
|
+
self.client = get_remote_addr(transport)
|
109
|
+
self.scheme = "wss" if is_ssl(transport) else "ws"
|
110
|
+
|
111
|
+
if self.logger.level <= TRACE_LOG_LEVEL:
|
112
|
+
prefix = "%s:%d - " % self.client if self.client else ""
|
113
|
+
self.logger.log(TRACE_LOG_LEVEL, "%sWebSocket connection made", prefix)
|
114
|
+
|
115
|
+
def connection_lost(self, exc: Exception | None) -> None:
|
116
|
+
code = 1005 if self.handshake_complete else 1006
|
117
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": code})
|
118
|
+
self.connections.remove(self)
|
119
|
+
|
120
|
+
if self.logger.level <= TRACE_LOG_LEVEL:
|
121
|
+
prefix = "%s:%d - " % self.client if self.client else ""
|
122
|
+
self.logger.log(TRACE_LOG_LEVEL, "%sWebSocket connection lost", prefix)
|
123
|
+
|
124
|
+
self.handshake_complete = True
|
125
|
+
if exc is None:
|
126
|
+
self.transport.close()
|
127
|
+
|
128
|
+
def eof_received(self) -> None:
|
129
|
+
pass
|
130
|
+
|
131
|
+
def shutdown(self) -> None:
|
132
|
+
if self.handshake_complete:
|
133
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": 1012})
|
134
|
+
self.conn.send_close(1012)
|
135
|
+
output = self.conn.data_to_send()
|
136
|
+
self.transport.write(b"".join(output))
|
137
|
+
else:
|
138
|
+
self.send_500_response()
|
139
|
+
self.transport.close()
|
140
|
+
|
141
|
+
def data_received(self, data: bytes) -> None:
|
142
|
+
self.conn.receive_data(data)
|
143
|
+
if self.conn.parser_exc is not None: # pragma: no cover
|
144
|
+
self.handle_parser_exception()
|
145
|
+
return
|
146
|
+
self.handle_events()
|
147
|
+
|
148
|
+
def handle_events(self) -> None:
|
149
|
+
for event in self.conn.events_received():
|
150
|
+
if isinstance(event, Request):
|
151
|
+
self.handle_connect(event)
|
152
|
+
if isinstance(event, Frame):
|
153
|
+
if event.opcode == Opcode.CONT:
|
154
|
+
self.handle_cont(event) # pragma: no cover
|
155
|
+
elif event.opcode == Opcode.TEXT:
|
156
|
+
self.handle_text(event)
|
157
|
+
elif event.opcode == Opcode.BINARY:
|
158
|
+
self.handle_bytes(event)
|
159
|
+
elif event.opcode == Opcode.PING:
|
160
|
+
self.handle_ping()
|
161
|
+
elif event.opcode == Opcode.PONG:
|
162
|
+
pass # pragma: no cover
|
163
|
+
elif event.opcode == Opcode.CLOSE:
|
164
|
+
self.handle_close(event)
|
165
|
+
else:
|
166
|
+
assert_never(event.opcode) # pragma: no cover
|
167
|
+
|
168
|
+
# Event handlers
|
169
|
+
|
170
|
+
def handle_connect(self, event: Request) -> None:
|
171
|
+
self.request = event
|
172
|
+
self.response = self.conn.accept(event)
|
173
|
+
self.handshake_initiated = True
|
174
|
+
if self.response.status_code != 101:
|
175
|
+
self.handshake_complete = True
|
176
|
+
self.close_sent = True
|
177
|
+
self.conn.send_response(self.response)
|
178
|
+
output = self.conn.data_to_send()
|
179
|
+
self.transport.write(b"".join(output))
|
180
|
+
self.transport.close()
|
181
|
+
return
|
182
|
+
|
183
|
+
headers = [
|
184
|
+
(key.encode("ascii"), value.encode("ascii", errors="surrogateescape"))
|
185
|
+
for key, value in event.headers.raw_items()
|
186
|
+
]
|
187
|
+
raw_path, _, query_string = event.path.partition("?")
|
188
|
+
self.scope: WebSocketScope = {
|
189
|
+
"type": "websocket",
|
190
|
+
"asgi": {"version": self.config.asgi_version, "spec_version": "2.3"},
|
191
|
+
"http_version": "1.1",
|
192
|
+
"scheme": self.scheme,
|
193
|
+
"server": self.server,
|
194
|
+
"client": self.client,
|
195
|
+
"root_path": self.root_path,
|
196
|
+
"path": unquote(raw_path),
|
197
|
+
"raw_path": raw_path.encode("ascii"),
|
198
|
+
"query_string": query_string.encode("ascii"),
|
199
|
+
"headers": headers,
|
200
|
+
"subprotocols": event.headers.get_all("Sec-WebSocket-Protocol"),
|
201
|
+
"state": self.app_state.copy(),
|
202
|
+
"extensions": {"websocket.http.response": {}},
|
203
|
+
}
|
204
|
+
self.queue.put_nowait({"type": "websocket.connect"})
|
205
|
+
task = self.loop.create_task(self.run_asgi())
|
206
|
+
task.add_done_callback(self.on_task_complete)
|
207
|
+
self.tasks.add(task)
|
208
|
+
|
209
|
+
def handle_cont(self, event: Frame) -> None: # pragma: no cover
|
210
|
+
self.bytes += event.data
|
211
|
+
if event.fin:
|
212
|
+
self.send_receive_event_to_app()
|
213
|
+
|
214
|
+
def handle_text(self, event: Frame) -> None:
|
215
|
+
self.bytes = event.data
|
216
|
+
self.curr_msg_data_type: Literal["text", "bytes"] = "text"
|
217
|
+
if event.fin:
|
218
|
+
self.send_receive_event_to_app()
|
219
|
+
|
220
|
+
def handle_bytes(self, event: Frame) -> None:
|
221
|
+
self.bytes = event.data
|
222
|
+
self.curr_msg_data_type = "bytes"
|
223
|
+
if event.fin:
|
224
|
+
self.send_receive_event_to_app()
|
225
|
+
|
226
|
+
def send_receive_event_to_app(self) -> None:
|
227
|
+
if self.curr_msg_data_type == "text":
|
228
|
+
try:
|
229
|
+
self.queue.put_nowait({"type": "websocket.receive", "text": self.bytes.decode()})
|
230
|
+
except UnicodeDecodeError: # pragma: no cover
|
231
|
+
self.logger.exception("Invalid UTF-8 sequence received from client.")
|
232
|
+
self.conn.send_close(1007)
|
233
|
+
self.handle_parser_exception()
|
234
|
+
return
|
235
|
+
else:
|
236
|
+
self.queue.put_nowait({"type": "websocket.receive", "bytes": self.bytes})
|
237
|
+
if not self.read_paused:
|
238
|
+
self.read_paused = True
|
239
|
+
self.transport.pause_reading()
|
240
|
+
|
241
|
+
def handle_ping(self) -> None:
|
242
|
+
output = self.conn.data_to_send()
|
243
|
+
self.transport.write(b"".join(output))
|
244
|
+
|
245
|
+
def handle_close(self, event: Frame) -> None:
|
246
|
+
if not self.close_sent and not self.transport.is_closing():
|
247
|
+
assert self.conn.close_rcvd is not None
|
248
|
+
code = self.conn.close_rcvd.code
|
249
|
+
reason = self.conn.close_rcvd.reason
|
250
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": code, "reason": reason})
|
251
|
+
|
252
|
+
output = self.conn.data_to_send()
|
253
|
+
self.transport.write(b"".join(output))
|
254
|
+
self.transport.close()
|
255
|
+
|
256
|
+
def handle_parser_exception(self) -> None: # pragma: no cover
|
257
|
+
assert self.conn.close_sent is not None
|
258
|
+
code = self.conn.close_sent.code
|
259
|
+
reason = self.conn.close_sent.reason
|
260
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": code, "reason": reason})
|
261
|
+
|
262
|
+
output = self.conn.data_to_send()
|
263
|
+
self.transport.write(b"".join(output))
|
264
|
+
self.close_sent = True
|
265
|
+
self.transport.close()
|
266
|
+
|
267
|
+
def on_task_complete(self, task: asyncio.Task[None]) -> None:
|
268
|
+
self.tasks.discard(task)
|
269
|
+
|
270
|
+
async def run_asgi(self) -> None:
|
271
|
+
try:
|
272
|
+
result = await self.app(self.scope, self.receive, self.send)
|
273
|
+
except ClientDisconnected:
|
274
|
+
self.transport.close() # pragma: no cover
|
275
|
+
except BaseException:
|
276
|
+
self.logger.exception("Exception in ASGI application\n")
|
277
|
+
self.send_500_response()
|
278
|
+
self.transport.close()
|
279
|
+
else:
|
280
|
+
if not self.handshake_complete:
|
281
|
+
self.logger.error("ASGI callable returned without completing handshake.")
|
282
|
+
self.send_500_response()
|
283
|
+
self.transport.close()
|
284
|
+
elif result is not None:
|
285
|
+
self.logger.error("ASGI callable should return None, but returned '%s'.", result)
|
286
|
+
self.transport.close()
|
287
|
+
|
288
|
+
def send_500_response(self) -> None:
|
289
|
+
if self.initial_response or self.handshake_complete:
|
290
|
+
return
|
291
|
+
response = self.conn.reject(500, "Internal Server Error")
|
292
|
+
self.conn.send_response(response)
|
293
|
+
output = self.conn.data_to_send()
|
294
|
+
self.transport.write(b"".join(output))
|
295
|
+
|
296
|
+
async def send(self, message: ASGISendEvent) -> None:
|
297
|
+
await self.writable.wait()
|
298
|
+
|
299
|
+
message_type = message["type"]
|
300
|
+
|
301
|
+
if not self.handshake_complete and self.initial_response is None:
|
302
|
+
if message_type == "websocket.accept":
|
303
|
+
message = cast(WebSocketAcceptEvent, message)
|
304
|
+
self.logger.info(
|
305
|
+
'%s - "WebSocket %s" [accepted]',
|
306
|
+
self.scope["client"],
|
307
|
+
get_path_with_query_string(self.scope),
|
308
|
+
)
|
309
|
+
headers = [
|
310
|
+
(name.decode("latin-1").lower(), value.decode("latin-1").lower())
|
311
|
+
for name, value in (self.default_headers + list(message.get("headers", [])))
|
312
|
+
]
|
313
|
+
accepted_subprotocol = message.get("subprotocol")
|
314
|
+
if accepted_subprotocol:
|
315
|
+
headers.append(("Sec-WebSocket-Protocol", accepted_subprotocol))
|
316
|
+
self.response.headers.update(headers)
|
317
|
+
|
318
|
+
if not self.transport.is_closing():
|
319
|
+
self.handshake_complete = True
|
320
|
+
self.conn.send_response(self.response)
|
321
|
+
output = self.conn.data_to_send()
|
322
|
+
self.transport.write(b"".join(output))
|
323
|
+
|
324
|
+
elif message_type == "websocket.close":
|
325
|
+
message = cast(WebSocketCloseEvent, message)
|
326
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": 1006})
|
327
|
+
self.logger.info(
|
328
|
+
'%s - "WebSocket %s" 403',
|
329
|
+
self.scope["client"],
|
330
|
+
get_path_with_query_string(self.scope),
|
331
|
+
)
|
332
|
+
response = self.conn.reject(HTTPStatus.FORBIDDEN, "")
|
333
|
+
self.conn.send_response(response)
|
334
|
+
output = self.conn.data_to_send()
|
335
|
+
self.close_sent = True
|
336
|
+
self.handshake_complete = True
|
337
|
+
self.transport.write(b"".join(output))
|
338
|
+
self.transport.close()
|
339
|
+
elif message_type == "websocket.http.response.start" and self.initial_response is None:
|
340
|
+
message = cast(WebSocketResponseStartEvent, message)
|
341
|
+
if not (100 <= message["status"] < 600):
|
342
|
+
raise RuntimeError("Invalid HTTP status code '%d' in response." % message["status"])
|
343
|
+
self.logger.info(
|
344
|
+
'%s - "WebSocket %s" %d',
|
345
|
+
self.scope["client"],
|
346
|
+
get_path_with_query_string(self.scope),
|
347
|
+
message["status"],
|
348
|
+
)
|
349
|
+
headers = [
|
350
|
+
(name.decode("latin-1"), value.decode("latin-1"))
|
351
|
+
for name, value in list(message.get("headers", []))
|
352
|
+
]
|
353
|
+
self.initial_response = (message["status"], headers, b"")
|
354
|
+
else:
|
355
|
+
msg = (
|
356
|
+
"Expected ASGI message 'websocket.accept', 'websocket.close' "
|
357
|
+
"or 'websocket.http.response.start' "
|
358
|
+
"but got '%s'."
|
359
|
+
)
|
360
|
+
raise RuntimeError(msg % message_type)
|
361
|
+
|
362
|
+
elif not self.close_sent and self.initial_response is None:
|
363
|
+
try:
|
364
|
+
if message_type == "websocket.send":
|
365
|
+
message = cast(WebSocketSendEvent, message)
|
366
|
+
bytes_data = message.get("bytes")
|
367
|
+
text_data = message.get("text")
|
368
|
+
if text_data:
|
369
|
+
self.conn.send_text(text_data.encode())
|
370
|
+
elif bytes_data:
|
371
|
+
self.conn.send_binary(bytes_data)
|
372
|
+
output = self.conn.data_to_send()
|
373
|
+
self.transport.write(b"".join(output))
|
374
|
+
|
375
|
+
elif message_type == "websocket.close" and not self.transport.is_closing():
|
376
|
+
message = cast(WebSocketCloseEvent, message)
|
377
|
+
code = message.get("code", 1000)
|
378
|
+
reason = message.get("reason", "") or ""
|
379
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": code, "reason": reason})
|
380
|
+
self.conn.send_close(code, reason)
|
381
|
+
output = self.conn.data_to_send()
|
382
|
+
self.transport.write(b"".join(output))
|
383
|
+
self.close_sent = True
|
384
|
+
self.transport.close()
|
385
|
+
else:
|
386
|
+
msg = "Expected ASGI message 'websocket.send' or 'websocket.close', but got '%s'."
|
387
|
+
raise RuntimeError(msg % message_type)
|
388
|
+
except InvalidState:
|
389
|
+
raise ClientDisconnected()
|
390
|
+
elif self.initial_response is not None:
|
391
|
+
if message_type == "websocket.http.response.body":
|
392
|
+
message = cast(WebSocketResponseBodyEvent, message)
|
393
|
+
body = self.initial_response[2] + message["body"]
|
394
|
+
self.initial_response = self.initial_response[:2] + (body,)
|
395
|
+
if not message.get("more_body", False):
|
396
|
+
response = self.conn.reject(self.initial_response[0], body.decode())
|
397
|
+
response.headers.update(self.initial_response[1])
|
398
|
+
self.queue.put_nowait({"type": "websocket.disconnect", "code": 1006})
|
399
|
+
self.conn.send_response(response)
|
400
|
+
output = self.conn.data_to_send()
|
401
|
+
self.close_sent = True
|
402
|
+
self.transport.write(b"".join(output))
|
403
|
+
self.transport.close()
|
404
|
+
else: # pragma: no cover
|
405
|
+
msg = "Expected ASGI message 'websocket.http.response.body' but got '%s'."
|
406
|
+
raise RuntimeError(msg % message_type)
|
407
|
+
|
408
|
+
else:
|
409
|
+
msg = "Unexpected ASGI message '%s', after sending 'websocket.close'."
|
410
|
+
raise RuntimeError(msg % message_type)
|
411
|
+
|
412
|
+
async def receive(self) -> ASGIReceiveEvent:
|
413
|
+
message = await self.queue.get()
|
414
|
+
if self.read_paused and self.queue.empty():
|
415
|
+
self.read_paused = False
|
416
|
+
self.transport.resume_reading()
|
417
|
+
return message
|
uvicorn/server.py
CHANGED
@@ -23,9 +23,10 @@ if TYPE_CHECKING:
|
|
23
23
|
from uvicorn.protocols.http.h11_impl import H11Protocol
|
24
24
|
from uvicorn.protocols.http.httptools_impl import HttpToolsProtocol
|
25
25
|
from uvicorn.protocols.websockets.websockets_impl import WebSocketProtocol
|
26
|
+
from uvicorn.protocols.websockets.websockets_sansio_impl import WebSocketsSansIOProtocol
|
26
27
|
from uvicorn.protocols.websockets.wsproto_impl import WSProtocol
|
27
28
|
|
28
|
-
Protocols = Union[H11Protocol, HttpToolsProtocol, WSProtocol, WebSocketProtocol]
|
29
|
+
Protocols = Union[H11Protocol, HttpToolsProtocol, WSProtocol, WebSocketProtocol, WebSocketsSansIOProtocol]
|
29
30
|
|
30
31
|
HANDLED_SIGNALS = (
|
31
32
|
signal.SIGINT, # Unix signal 2. Sent by Ctrl+C.
|
@@ -1,13 +1,13 @@
|
|
1
|
-
uvicorn/__init__.py,sha256=
|
1
|
+
uvicorn/__init__.py,sha256=pMIkABPLJ9eS0OuDO-bP506aVdLfWlHDtk9uzSTgE5E,147
|
2
2
|
uvicorn/__main__.py,sha256=DQizy6nKP0ywhPpnCHgmRDYIMfcqZKVEzNIWQZjqtVQ,62
|
3
3
|
uvicorn/_subprocess.py,sha256=HbfRnsCkXyg7xCWVAWWzXQTeWlvLKfTlIF5wevFBkR4,2766
|
4
4
|
uvicorn/_types.py,sha256=5FcPvvIfeKsJDjGhTrceDv8TmwzYI8yPF7mXsXTWOUM,7775
|
5
|
-
uvicorn/config.py,sha256=
|
5
|
+
uvicorn/config.py,sha256=OHgnEBBuk7XMhwKByt1hlBJsXk8EGWRYGpCQ_G1deaA,21013
|
6
6
|
uvicorn/importer.py,sha256=nRt0QQ3qpi264-n_mR0l55C2ddM8nowTNzT1jsWaam8,1128
|
7
7
|
uvicorn/logging.py,sha256=-eCE4nOJmFbtB9qfNJuEVNF0Y13LGUHqvFzemYT0PaQ,4235
|
8
|
-
uvicorn/main.py,sha256=
|
8
|
+
uvicorn/main.py,sha256=83-MEMVc6ErEdvUfW6x-gELYJ78GiWRNMsmuing9DUI,17231
|
9
9
|
uvicorn/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
10
|
-
uvicorn/server.py,sha256=
|
10
|
+
uvicorn/server.py,sha256=3AKFM50WbZkwnqjZFxSd8wLBVIkcxZjmvP8gPZf95_s,12998
|
11
11
|
uvicorn/workers.py,sha256=7KGgGAapxGkGeXUcBGunOjSNdI9R44KCoWTGEAqAdfs,3895
|
12
12
|
uvicorn/lifespan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
uvicorn/lifespan/off.py,sha256=nfI6qHAUo_8-BEXMBKoHQ9wUbsXrPaXLCbDSS0vKSr8,332
|
@@ -31,14 +31,15 @@ uvicorn/protocols/http/httptools_impl.py,sha256=tuQBCiD6rf5DQeyQwHVc45rxH9ouojMp
|
|
31
31
|
uvicorn/protocols/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
32
|
uvicorn/protocols/websockets/auto.py,sha256=SH_KV_3vwR8_oGda2GrHFt38VG-IwY0ufjvHOu4VA0o,581
|
33
33
|
uvicorn/protocols/websockets/websockets_impl.py,sha256=qonQJxz9wMKMwB2RnYZezeP-XfmAsxCvNeGgu4sC3Lc,15546
|
34
|
+
uvicorn/protocols/websockets/websockets_sansio_impl.py,sha256=TxpjkbuhaTd0UXYc1VMe4UgBr6kn_JwJV-hqegHHmxs,17145
|
34
35
|
uvicorn/protocols/websockets/wsproto_impl.py,sha256=u2TKyzRUCmQpS1e4E_X6Uou9QIuoKZNNNmHU1IzkWPU,15366
|
35
36
|
uvicorn/supervisors/__init__.py,sha256=wT8eOEIqT1yWQgytZtv5taWMul7xoTIY0xm1m4oyPTw,507
|
36
37
|
uvicorn/supervisors/basereload.py,sha256=MAXSQ3ckZPwqzJ8Un9yDhk3W0yyAArQtZLuOE0OInSc,4036
|
37
38
|
uvicorn/supervisors/multiprocess.py,sha256=Opt0XvOUj1DIMXYwb4OlkJZxeh_RjweFnTmDPYItONw,7507
|
38
39
|
uvicorn/supervisors/statreload.py,sha256=uYblmoxM3IbPbvMDzr5Abw2-WykQl8NxTTzeLfVyvnU,1566
|
39
40
|
uvicorn/supervisors/watchfilesreload.py,sha256=W86Ybb0E5SdMYYuWHJ3bpAFXdw5ZurvLRdFcvLnYEIA,2859
|
40
|
-
uvicorn-0.
|
41
|
-
uvicorn-0.
|
42
|
-
uvicorn-0.
|
43
|
-
uvicorn-0.
|
44
|
-
uvicorn-0.
|
41
|
+
uvicorn-0.35.0.dist-info/METADATA,sha256=a1sVo25u3fbfeGXSMmFdYOuPPnBXF8WjHlYq1oc2qTo,6531
|
42
|
+
uvicorn-0.35.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
43
|
+
uvicorn-0.35.0.dist-info/entry_points.txt,sha256=FW1w-hkc9QgwaGoovMvm0ZY73w_NcycWdGAUfDsNGxw,46
|
44
|
+
uvicorn-0.35.0.dist-info/licenses/LICENSE.md,sha256=7-Gs8-YvuZwoiw7HPlp3O3Jo70Mg_nV-qZQhTktjw3E,1526
|
45
|
+
uvicorn-0.35.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|