sf-veritas 0.10.3__cp39-cp39-manylinux_2_28_x86_64.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.
Potentially problematic release.
This version of sf-veritas might be problematic. Click here for more details.
- sf_veritas/__init__.py +20 -0
- sf_veritas/_sffastlog.c +889 -0
- sf_veritas/_sffastlog.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnet.c +924 -0
- sf_veritas/_sffastnet.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnetworkrequest.c +730 -0
- sf_veritas/_sffastnetworkrequest.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan.c +2155 -0
- sf_veritas/_sffuncspan.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan_config.c +617 -0
- sf_veritas/_sffuncspan_config.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfheadercheck.c +341 -0
- sf_veritas/_sfheadercheck.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfnetworkhop.c +1451 -0
- sf_veritas/_sfnetworkhop.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfservice.c +1175 -0
- sf_veritas/_sfservice.cpython-39-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfteepreload.c +5167 -0
- sf_veritas/app_config.py +49 -0
- sf_veritas/cli.py +336 -0
- sf_veritas/constants.py +10 -0
- sf_veritas/custom_excepthook.py +304 -0
- sf_veritas/custom_log_handler.py +129 -0
- sf_veritas/custom_output_wrapper.py +144 -0
- sf_veritas/custom_print.py +146 -0
- sf_veritas/django_app.py +5 -0
- sf_veritas/env_vars.py +186 -0
- sf_veritas/exception_handling_middleware.py +18 -0
- sf_veritas/exception_metaclass.py +69 -0
- sf_veritas/fast_frame_info.py +116 -0
- sf_veritas/fast_network_hop.py +293 -0
- sf_veritas/frame_tools.py +112 -0
- sf_veritas/funcspan_config_loader.py +556 -0
- sf_veritas/function_span_profiler.py +1174 -0
- sf_veritas/import_hook.py +62 -0
- sf_veritas/infra_details/__init__.py +3 -0
- sf_veritas/infra_details/get_infra_details.py +24 -0
- sf_veritas/infra_details/kubernetes/__init__.py +3 -0
- sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
- sf_veritas/infra_details/kubernetes/get_details.py +7 -0
- sf_veritas/infra_details/running_on/__init__.py +17 -0
- sf_veritas/infra_details/running_on/kubernetes.py +11 -0
- sf_veritas/interceptors.py +497 -0
- sf_veritas/libsfnettee.so +0 -0
- sf_veritas/local_env_detect.py +118 -0
- sf_veritas/package_metadata.py +6 -0
- sf_veritas/patches/__init__.py +0 -0
- sf_veritas/patches/concurrent_futures.py +19 -0
- sf_veritas/patches/constants.py +1 -0
- sf_veritas/patches/exceptions.py +82 -0
- sf_veritas/patches/multiprocessing.py +32 -0
- sf_veritas/patches/network_libraries/__init__.py +76 -0
- sf_veritas/patches/network_libraries/aiohttp.py +281 -0
- sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
- sf_veritas/patches/network_libraries/http_client.py +419 -0
- sf_veritas/patches/network_libraries/httpcore.py +515 -0
- sf_veritas/patches/network_libraries/httplib2.py +204 -0
- sf_veritas/patches/network_libraries/httpx.py +515 -0
- sf_veritas/patches/network_libraries/niquests.py +211 -0
- sf_veritas/patches/network_libraries/pycurl.py +385 -0
- sf_veritas/patches/network_libraries/requests.py +633 -0
- sf_veritas/patches/network_libraries/tornado.py +341 -0
- sf_veritas/patches/network_libraries/treq.py +270 -0
- sf_veritas/patches/network_libraries/urllib_request.py +468 -0
- sf_veritas/patches/network_libraries/utils.py +398 -0
- sf_veritas/patches/os.py +17 -0
- sf_veritas/patches/threading.py +218 -0
- sf_veritas/patches/web_frameworks/__init__.py +54 -0
- sf_veritas/patches/web_frameworks/aiohttp.py +793 -0
- sf_veritas/patches/web_frameworks/async_websocket_consumer.py +317 -0
- sf_veritas/patches/web_frameworks/blacksheep.py +527 -0
- sf_veritas/patches/web_frameworks/bottle.py +502 -0
- sf_veritas/patches/web_frameworks/cherrypy.py +678 -0
- sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
- sf_veritas/patches/web_frameworks/django.py +944 -0
- sf_veritas/patches/web_frameworks/eve.py +395 -0
- sf_veritas/patches/web_frameworks/falcon.py +926 -0
- sf_veritas/patches/web_frameworks/fastapi.py +724 -0
- sf_veritas/patches/web_frameworks/flask.py +520 -0
- sf_veritas/patches/web_frameworks/klein.py +501 -0
- sf_veritas/patches/web_frameworks/litestar.py +551 -0
- sf_veritas/patches/web_frameworks/pyramid.py +428 -0
- sf_veritas/patches/web_frameworks/quart.py +824 -0
- sf_veritas/patches/web_frameworks/robyn.py +697 -0
- sf_veritas/patches/web_frameworks/sanic.py +857 -0
- sf_veritas/patches/web_frameworks/starlette.py +723 -0
- sf_veritas/patches/web_frameworks/strawberry.py +813 -0
- sf_veritas/patches/web_frameworks/tornado.py +481 -0
- sf_veritas/patches/web_frameworks/utils.py +91 -0
- sf_veritas/print_override.py +13 -0
- sf_veritas/regular_data_transmitter.py +409 -0
- sf_veritas/request_interceptor.py +401 -0
- sf_veritas/request_utils.py +550 -0
- sf_veritas/server_status.py +1 -0
- sf_veritas/shutdown_flag.py +11 -0
- sf_veritas/subprocess_startup.py +3 -0
- sf_veritas/test_cli.py +145 -0
- sf_veritas/thread_local.py +970 -0
- sf_veritas/timeutil.py +114 -0
- sf_veritas/transmit_exception_to_sailfish.py +28 -0
- sf_veritas/transmitter.py +132 -0
- sf_veritas/types.py +47 -0
- sf_veritas/unified_interceptor.py +1580 -0
- sf_veritas/utils.py +39 -0
- sf_veritas-0.10.3.dist-info/METADATA +97 -0
- sf_veritas-0.10.3.dist-info/RECORD +132 -0
- sf_veritas-0.10.3.dist-info/WHEEL +5 -0
- sf_veritas-0.10.3.dist-info/entry_points.txt +2 -0
- sf_veritas-0.10.3.dist-info/top_level.txt +1 -0
- sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
- sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
- sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
- sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
- sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
- sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
- sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
- sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
- sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
- sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
- sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
- sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
- sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
- sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
- sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
- sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
- sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
- sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
- sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
- sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
- sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
- sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
- sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import sys
|
|
3
|
+
import sysconfig
|
|
4
|
+
from functools import lru_cache, wraps
|
|
5
|
+
from typing import Any, Callable, Optional, Set, Tuple
|
|
6
|
+
|
|
7
|
+
from ...constants import SAILFISH_TRACING_HEADER
|
|
8
|
+
from ...custom_excepthook import custom_excepthook
|
|
9
|
+
from ...env_vars import (
|
|
10
|
+
PRINT_CONFIGURATION_STATUSES,
|
|
11
|
+
SF_DEBUG,
|
|
12
|
+
SF_NETWORKHOP_CAPTURE_REQUEST_BODY,
|
|
13
|
+
SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS,
|
|
14
|
+
SF_NETWORKHOP_CAPTURE_RESPONSE_BODY,
|
|
15
|
+
SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS,
|
|
16
|
+
SF_NETWORKHOP_REQUEST_LIMIT_MB,
|
|
17
|
+
SF_NETWORKHOP_RESPONSE_LIMIT_MB,
|
|
18
|
+
)
|
|
19
|
+
from ...thread_local import get_or_set_sf_trace_id
|
|
20
|
+
from ...fast_network_hop import fast_send_network_hop_fast, fast_send_network_hop, register_endpoint
|
|
21
|
+
from .utils import _unwrap_user_func
|
|
22
|
+
|
|
23
|
+
# Size limits in bytes
|
|
24
|
+
_REQUEST_LIMIT_BYTES = SF_NETWORKHOP_REQUEST_LIMIT_MB * 1024 * 1024
|
|
25
|
+
_RESPONSE_LIMIT_BYTES = SF_NETWORKHOP_RESPONSE_LIMIT_MB * 1024 * 1024
|
|
26
|
+
|
|
27
|
+
# Pre-registered endpoint IDs
|
|
28
|
+
_ENDPOINT_REGISTRY: dict[tuple, int] = {}
|
|
29
|
+
|
|
30
|
+
# ────────────────────────────────────────────────────
|
|
31
|
+
# User-code predicate: skip stdlib & site-packages
|
|
32
|
+
# ────────────────────────────────────────────────────
|
|
33
|
+
_STDLIB = sysconfig.get_paths()["stdlib"]
|
|
34
|
+
_SITE_TAGS = ("site-packages", "dist-packages")
|
|
35
|
+
_SKIP_PREFIXES = (_STDLIB, "/usr/local/lib/python", "/usr/lib/python")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@lru_cache(maxsize=512)
|
|
39
|
+
def _is_user_code(path: Optional[str] = None) -> bool:
|
|
40
|
+
"""True only for your application files."""
|
|
41
|
+
if not path or path.startswith("<"):
|
|
42
|
+
return False
|
|
43
|
+
for p in _SKIP_PREFIXES:
|
|
44
|
+
if path.startswith(p):
|
|
45
|
+
return False
|
|
46
|
+
return not any(tag in path for tag in _SITE_TAGS)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ────────────────────────────────────────────────────
|
|
50
|
+
# Patch AsyncConsumer.__call__ to hook connect + receive
|
|
51
|
+
# ────────────────────────────────────────────────────
|
|
52
|
+
def patch_async_consumer_call():
|
|
53
|
+
"""
|
|
54
|
+
Wraps AsyncConsumer.__call__ so that for each HTTP or WebSocket
|
|
55
|
+
connection:
|
|
56
|
+
1) SAILFISH_TRACING_HEADER → ContextVar
|
|
57
|
+
2) Emit a NetworkHop at first user frame in websocket_connect
|
|
58
|
+
3) Dynamically wrap websocket_receive to emit a hop on first message
|
|
59
|
+
4) Forward any exception to custom_excepthook
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
from channels.consumer import AsyncConsumer # type: ignore
|
|
63
|
+
|
|
64
|
+
orig_call = AsyncConsumer.__call__
|
|
65
|
+
except:
|
|
66
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
67
|
+
print("Channels AsyncConsumer not found; skipping patch", log=False)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
71
|
+
print("Patching AsyncConsumer.__call__ for NetworkHops", log=False)
|
|
72
|
+
|
|
73
|
+
@wraps(orig_call)
|
|
74
|
+
async def custom_call(self, scope, receive, send):
|
|
75
|
+
# — Propagate header into ContextVar —
|
|
76
|
+
header_val = None
|
|
77
|
+
if scope["type"] in ("http", "websocket"):
|
|
78
|
+
for name, val in scope.get("headers", []):
|
|
79
|
+
if name.lower() == SAILFISH_TRACING_HEADER.lower().encode():
|
|
80
|
+
header_val = val.decode("utf-8")
|
|
81
|
+
break
|
|
82
|
+
get_or_set_sf_trace_id(header_val, is_associated_with_inbound_request=True)
|
|
83
|
+
|
|
84
|
+
# — Capture request headers if enabled —
|
|
85
|
+
req_headers = None
|
|
86
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS:
|
|
87
|
+
try:
|
|
88
|
+
headers = scope.get("headers", [])
|
|
89
|
+
req_headers = {
|
|
90
|
+
name.decode("utf-8"): val.decode("utf-8")
|
|
91
|
+
for name, val in headers
|
|
92
|
+
}
|
|
93
|
+
if SF_DEBUG:
|
|
94
|
+
print(f"[[async_websocket_consumer]] Captured request headers: {len(req_headers)} headers", log=False)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
if SF_DEBUG:
|
|
97
|
+
print(f"[[async_websocket_consumer]] Failed to capture request headers: {e}", log=False)
|
|
98
|
+
|
|
99
|
+
# — Capture request body if enabled (for HTTP, not WebSocket) —
|
|
100
|
+
req_body = None
|
|
101
|
+
body_parts = []
|
|
102
|
+
body_size = 0
|
|
103
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_BODY and scope["type"] == "http":
|
|
104
|
+
try:
|
|
105
|
+
# Save original receive before wrapping
|
|
106
|
+
original_receive = receive
|
|
107
|
+
|
|
108
|
+
async def receive_with_body():
|
|
109
|
+
nonlocal body_size
|
|
110
|
+
message = await original_receive()
|
|
111
|
+
if message["type"] == "http.request":
|
|
112
|
+
body_part = message.get("body", b"")
|
|
113
|
+
if body_part and body_size < _REQUEST_LIMIT_BYTES:
|
|
114
|
+
remaining = _REQUEST_LIMIT_BYTES - body_size
|
|
115
|
+
body_parts.append(body_part[:remaining])
|
|
116
|
+
body_size += len(body_part)
|
|
117
|
+
return message
|
|
118
|
+
|
|
119
|
+
receive = receive_with_body
|
|
120
|
+
except Exception as e:
|
|
121
|
+
if SF_DEBUG:
|
|
122
|
+
print(f"[[async_websocket_consumer]] Failed to setup request body capture: {e}", log=False)
|
|
123
|
+
|
|
124
|
+
# OTEL-STYLE: Capture endpoint info and pre-register
|
|
125
|
+
endpoint_id = None
|
|
126
|
+
endpoint_metadata = {} # Store metadata for fallback
|
|
127
|
+
|
|
128
|
+
def tracer(frame, event, _arg):
|
|
129
|
+
nonlocal endpoint_id
|
|
130
|
+
if event == "call":
|
|
131
|
+
fn_path = frame.f_code.co_filename
|
|
132
|
+
fn_name = frame.f_code.co_name
|
|
133
|
+
|
|
134
|
+
if SF_DEBUG:
|
|
135
|
+
print(
|
|
136
|
+
f"[[async_websocket_consumer]] Tracer saw: {fn_name} @ {fn_path}:{frame.f_lineno} "
|
|
137
|
+
f"is_user_code={_is_user_code(fn_path)}",
|
|
138
|
+
log=False,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if _is_user_code(fn_path):
|
|
142
|
+
hop_key = (fn_path, frame.f_lineno)
|
|
143
|
+
|
|
144
|
+
# Store metadata for potential fallback
|
|
145
|
+
endpoint_metadata["filename"] = fn_path
|
|
146
|
+
endpoint_metadata["line"] = frame.f_lineno
|
|
147
|
+
endpoint_metadata["name"] = fn_name
|
|
148
|
+
|
|
149
|
+
# Pre-register endpoint if not already registered
|
|
150
|
+
endpoint_id = _ENDPOINT_REGISTRY.get(hop_key)
|
|
151
|
+
if endpoint_id is None:
|
|
152
|
+
if SF_DEBUG:
|
|
153
|
+
# Debug: Check if fast network hop is available
|
|
154
|
+
from ...fast_network_hop import _NETWORKHOP_FAST_OK, _FAST_NETWORKHOP_READY
|
|
155
|
+
print(
|
|
156
|
+
f"[[async_websocket_consumer]] Before register: _NETWORKHOP_FAST_OK={_NETWORKHOP_FAST_OK}, "
|
|
157
|
+
f"_FAST_NETWORKHOP_READY={_FAST_NETWORKHOP_READY}",
|
|
158
|
+
log=False,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
endpoint_id = register_endpoint(
|
|
162
|
+
line=str(frame.f_lineno),
|
|
163
|
+
column="0",
|
|
164
|
+
name=fn_name,
|
|
165
|
+
entrypoint=fn_path,
|
|
166
|
+
route=None
|
|
167
|
+
)
|
|
168
|
+
if SF_DEBUG:
|
|
169
|
+
print(
|
|
170
|
+
f"[[async_websocket_consumer]] register_endpoint returned: {endpoint_id}",
|
|
171
|
+
log=False,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if endpoint_id >= 0:
|
|
175
|
+
_ENDPOINT_REGISTRY[hop_key] = endpoint_id
|
|
176
|
+
if SF_DEBUG:
|
|
177
|
+
print(
|
|
178
|
+
f"[[async_websocket_consumer]] Registered endpoint: {fn_name} @ "
|
|
179
|
+
f"{fn_path}:{frame.f_lineno} (id={endpoint_id})",
|
|
180
|
+
log=False,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if SF_DEBUG:
|
|
184
|
+
print(
|
|
185
|
+
f"[[async_websocket_consumer]] Captured endpoint: {fn_name} "
|
|
186
|
+
f"({fn_path}:{frame.f_lineno}) endpoint_id={endpoint_id}",
|
|
187
|
+
log=False,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
sys.setprofile(None)
|
|
191
|
+
return None
|
|
192
|
+
return tracer
|
|
193
|
+
|
|
194
|
+
sys.setprofile(tracer)
|
|
195
|
+
|
|
196
|
+
# Track if we've emitted for this connection
|
|
197
|
+
hop_emitted = False
|
|
198
|
+
|
|
199
|
+
# Emit NetworkHop function - called when websocket_connect completes
|
|
200
|
+
def emit_network_hop_sync():
|
|
201
|
+
"""Emit NetworkHop after websocket_connect handler runs."""
|
|
202
|
+
nonlocal hop_emitted
|
|
203
|
+
if hop_emitted:
|
|
204
|
+
return
|
|
205
|
+
hop_emitted = True
|
|
206
|
+
|
|
207
|
+
if endpoint_id is not None and endpoint_id >= 0:
|
|
208
|
+
# Fast path with C extension
|
|
209
|
+
try:
|
|
210
|
+
_, session_id = get_or_set_sf_trace_id()
|
|
211
|
+
|
|
212
|
+
# Extract raw path and query string from scope for C to parse
|
|
213
|
+
raw_path = scope.get("path", None) # e.g., "/ws/chat"
|
|
214
|
+
raw_query = scope.get("query_string", b"") # e.g., b"room=123"
|
|
215
|
+
|
|
216
|
+
fast_send_network_hop_fast(
|
|
217
|
+
session_id=session_id,
|
|
218
|
+
endpoint_id=endpoint_id,
|
|
219
|
+
raw_path=raw_path,
|
|
220
|
+
raw_query_string=raw_query,
|
|
221
|
+
request_headers=req_headers,
|
|
222
|
+
request_body=None, # WebSocket has no body
|
|
223
|
+
response_headers=None,
|
|
224
|
+
response_body=None,
|
|
225
|
+
)
|
|
226
|
+
if SF_DEBUG:
|
|
227
|
+
print(
|
|
228
|
+
f"[[async_websocket_consumer]] Emitted network hop (fast path): endpoint_id={endpoint_id}",
|
|
229
|
+
log=False,
|
|
230
|
+
)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
if SF_DEBUG:
|
|
233
|
+
print(f"[[async_websocket_consumer]] Failed to emit (fast path): {e}", log=False)
|
|
234
|
+
elif endpoint_metadata:
|
|
235
|
+
# Fallback path without C extension
|
|
236
|
+
try:
|
|
237
|
+
_, session_id = get_or_set_sf_trace_id()
|
|
238
|
+
if SF_DEBUG:
|
|
239
|
+
print(
|
|
240
|
+
f"[[async_websocket_consumer]] Using fallback: {endpoint_metadata['name']} @ "
|
|
241
|
+
f"{endpoint_metadata['filename']}:{endpoint_metadata['line']}",
|
|
242
|
+
log=False,
|
|
243
|
+
)
|
|
244
|
+
fast_send_network_hop(
|
|
245
|
+
session_id=session_id,
|
|
246
|
+
line=str(endpoint_metadata["line"]),
|
|
247
|
+
column="0",
|
|
248
|
+
name=endpoint_metadata["name"],
|
|
249
|
+
entrypoint=endpoint_metadata["filename"],
|
|
250
|
+
)
|
|
251
|
+
if SF_DEBUG:
|
|
252
|
+
print(
|
|
253
|
+
f"[[async_websocket_consumer]] Emitted network hop (fallback): "
|
|
254
|
+
f"{endpoint_metadata['name']}",
|
|
255
|
+
log=False,
|
|
256
|
+
)
|
|
257
|
+
except Exception as e:
|
|
258
|
+
if SF_DEBUG:
|
|
259
|
+
print(f"[[async_websocket_consumer]] Failed to emit (fallback): {e}", log=False)
|
|
260
|
+
|
|
261
|
+
# Capture response headers and body
|
|
262
|
+
resp_headers = None
|
|
263
|
+
resp_body_parts = []
|
|
264
|
+
resp_body_size = 0
|
|
265
|
+
|
|
266
|
+
# Wrap send to capture response data AND emit after websocket.accept
|
|
267
|
+
async def wrapped_send(message):
|
|
268
|
+
nonlocal resp_headers, resp_body_size
|
|
269
|
+
|
|
270
|
+
# Emit NetworkHop after WebSocket is accepted
|
|
271
|
+
if message["type"] == "websocket.accept":
|
|
272
|
+
if SF_DEBUG:
|
|
273
|
+
print(f"[[async_websocket_consumer]] WebSocket accepted, emitting NetworkHop", log=False)
|
|
274
|
+
emit_network_hop_sync()
|
|
275
|
+
|
|
276
|
+
# Capture response headers
|
|
277
|
+
if message["type"] == "http.response.start" and SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS:
|
|
278
|
+
try:
|
|
279
|
+
headers = message.get("headers", [])
|
|
280
|
+
resp_headers = {
|
|
281
|
+
name.decode("utf-8"): val.decode("utf-8")
|
|
282
|
+
for name, val in headers
|
|
283
|
+
}
|
|
284
|
+
if SF_DEBUG:
|
|
285
|
+
print(f"[[async_websocket_consumer]] Captured response headers: {len(resp_headers)} headers", log=False)
|
|
286
|
+
except Exception as e:
|
|
287
|
+
if SF_DEBUG:
|
|
288
|
+
print(f"[[async_websocket_consumer]] Failed to capture response headers: {e}", log=False)
|
|
289
|
+
|
|
290
|
+
# Capture response body
|
|
291
|
+
if message["type"] == "http.response.body" and SF_NETWORKHOP_CAPTURE_RESPONSE_BODY:
|
|
292
|
+
try:
|
|
293
|
+
body_part = message.get("body", b"")
|
|
294
|
+
if body_part and resp_body_size < _RESPONSE_LIMIT_BYTES:
|
|
295
|
+
remaining = _RESPONSE_LIMIT_BYTES - resp_body_size
|
|
296
|
+
resp_body_parts.append(body_part[:remaining])
|
|
297
|
+
resp_body_size += len(body_part)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
if SF_DEBUG:
|
|
300
|
+
print(f"[[async_websocket_consumer]] Failed to capture response body chunk: {e}", log=False)
|
|
301
|
+
|
|
302
|
+
await send(message)
|
|
303
|
+
|
|
304
|
+
# — Call through to original (handles connect, receive, disconnect) —
|
|
305
|
+
try:
|
|
306
|
+
await orig_call(self, scope, receive, wrapped_send)
|
|
307
|
+
except Exception as exc:
|
|
308
|
+
custom_excepthook(type(exc), exc, exc.__traceback__)
|
|
309
|
+
raise
|
|
310
|
+
finally:
|
|
311
|
+
sys.setprofile(None)
|
|
312
|
+
|
|
313
|
+
# Apply the patch
|
|
314
|
+
AsyncConsumer.__call__ = custom_call
|
|
315
|
+
|
|
316
|
+
if PRINT_CONFIGURATION_STATUSES:
|
|
317
|
+
print("AsyncConsumer.__call__ patched successfully", log=False)
|