latch-asgi 1.0.5__tar.gz → 1.0.6.dev1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: latch-asgi
3
- Version: 1.0.5
3
+ Version: 1.0.6.dev1
4
4
  Summary: ASGI python server
5
5
  Author-Email: Max Smolin <max@latch.bio>
6
6
  License: CC0-1.0
@@ -27,9 +27,42 @@ class Context(
27
27
 
28
28
  @trace_app_function
29
29
  async def accept_connection(
30
- self: Self, *, subprotocol: str | None = None, headers: Headers | None = None
30
+ self: Self,
31
+ *,
32
+ subprotocol: str | None = None,
33
+ headers: Headers | None = None,
34
+ negotiate_permessage_deflate: bool = False,
31
35
  ) -> None:
32
- await accept_connection(self.send, subprotocol=subprotocol, headers=headers)
36
+ if not negotiate_permessage_deflate:
37
+ await accept_connection(self.send, subprotocol=subprotocol, headers=headers)
38
+ return
39
+
40
+ offer = self.header_str("sec-websocket-extensions")
41
+
42
+ headers_out: Headers = {}
43
+ if headers is not None:
44
+ headers_out = dict(headers)
45
+
46
+ def _has_extensions_header(h: Headers) -> bool:
47
+ for k in h.keys():
48
+ if isinstance(k, bytes):
49
+ if k.decode("latin-1").lower() == "sec-websocket-extensions":
50
+ return True
51
+ else:
52
+ if k.lower() == "sec-websocket-extensions":
53
+ return True
54
+ return False
55
+
56
+ if (
57
+ offer is not None
58
+ and "permessage-deflate" in offer.replace(" ", "").lower()
59
+ and not _has_extensions_header(headers_out)
60
+ ):
61
+ headers_out["Sec-WebSocket-Extensions"] = "permessage-deflate"
62
+
63
+ await accept_connection(
64
+ self.send, subprotocol=subprotocol, headers=headers_out or headers
65
+ )
33
66
 
34
67
  @trace_app_function
35
68
  async def receive_message(self: Self, cls: type[T]) -> T:
@@ -22,6 +22,7 @@ otel_header_whitelist = {
22
22
  "sec-gpc",
23
23
  "te",
24
24
  "upgrade-insecure-requests",
25
+ "sec-websocket-extensions",
25
26
  "device-memory",
26
27
  "downlink",
27
28
  "dpr",
@@ -222,6 +222,13 @@ class LatchASGIServer:
222
222
  s.set_attribute("http.route", scope["path"])
223
223
 
224
224
  close_reason: str | None = None
225
+ close_already_sent: bool = False
226
+
227
+ async def send_tracking(e):
228
+ nonlocal close_already_sent
229
+ if isinstance(e, dict) and e.get("type") == "websocket.close":
230
+ close_already_sent = True
231
+ await send(e)
225
232
  try:
226
233
  try:
227
234
  msg = await receive()
@@ -230,7 +237,7 @@ class LatchASGIServer:
230
237
  "ASGI protocol violation: missing websocket.connect event"
231
238
  )
232
239
 
233
- ctx = websocket.Context(scope, receive, send)
240
+ ctx = websocket.Context(scope, receive, send_tracking)
234
241
  close_reason = await handler(ctx)
235
242
  except WebsocketErrorResponse:
236
243
  raise
@@ -241,7 +248,7 @@ class LatchASGIServer:
241
248
  raise WebsocketInternalServerError("Internal Error") from e
242
249
  except WebsocketErrorResponse as e:
243
250
  await close_connection(
244
- send, status=e.status, reason=orjson.dumps({"error": e.data}).decode()
251
+ send_tracking, status=e.status, reason=orjson.dumps({"error": e.data}).decode()
245
252
  )
246
253
 
247
254
  if e.status != WebsocketStatus.server_error:
@@ -249,12 +256,13 @@ class LatchASGIServer:
249
256
 
250
257
  raise
251
258
  else:
252
- if close_reason is None:
253
- close_reason = "Session complete"
259
+ if not close_already_sent:
260
+ if close_reason is None:
261
+ close_reason = "Session complete"
254
262
 
255
- await close_connection(
256
- send, status=WebsocketStatus.normal, reason=close_reason
257
- )
263
+ await close_connection(
264
+ send_tracking, status=WebsocketStatus.normal, reason=close_reason
265
+ )
258
266
 
259
267
  async def scope_http(
260
268
  self: Self,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "latch-asgi"
3
- version = "1.0.5"
3
+ version = "1.0.6.dev1"
4
4
  description = "ASGI python server"
5
5
  authors = [
6
6
  { name = "Max Smolin", email = "max@latch.bio" },
File without changes
File without changes