sf-veritas 0.10.3__cp311-cp311-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-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnet.c +924 -0
- sf_veritas/_sffastnet.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnetworkrequest.c +730 -0
- sf_veritas/_sffastnetworkrequest.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan.c +2155 -0
- sf_veritas/_sffuncspan.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan_config.c +617 -0
- sf_veritas/_sffuncspan_config.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfheadercheck.c +341 -0
- sf_veritas/_sfheadercheck.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfnetworkhop.c +1451 -0
- sf_veritas/_sfnetworkhop.cpython-311-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfservice.c +1175 -0
- sf_veritas/_sfservice.cpython-311-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,501 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Klein (Twisted web framework) patch for OTEL-style network hop capture.
|
|
3
|
+
Captures request/response headers and bodies when enabled via env vars.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import functools
|
|
7
|
+
import inspect
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from ... import _sffuncspan_config, app_config
|
|
11
|
+
from ...constants import SAILFISH_TRACING_HEADER
|
|
12
|
+
from ...custom_excepthook import custom_excepthook
|
|
13
|
+
from ...env_vars import (
|
|
14
|
+
SF_DEBUG,
|
|
15
|
+
SF_NETWORKHOP_CAPTURE_REQUEST_BODY,
|
|
16
|
+
SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS,
|
|
17
|
+
SF_NETWORKHOP_CAPTURE_RESPONSE_BODY,
|
|
18
|
+
SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS,
|
|
19
|
+
SF_NETWORKHOP_REQUEST_LIMIT_MB,
|
|
20
|
+
SF_NETWORKHOP_RESPONSE_LIMIT_MB,
|
|
21
|
+
)
|
|
22
|
+
from ...fast_network_hop import fast_send_network_hop_fast, register_endpoint
|
|
23
|
+
from ...thread_local import get_or_set_sf_trace_id
|
|
24
|
+
from .utils import _is_user_code, _unwrap_user_func, should_skip_route
|
|
25
|
+
|
|
26
|
+
# Size limits in bytes
|
|
27
|
+
_REQUEST_LIMIT_BYTES = SF_NETWORKHOP_REQUEST_LIMIT_MB * 1024 * 1024
|
|
28
|
+
_RESPONSE_LIMIT_BYTES = SF_NETWORKHOP_RESPONSE_LIMIT_MB * 1024 * 1024
|
|
29
|
+
|
|
30
|
+
# Pre-registered endpoint IDs
|
|
31
|
+
_ENDPOINT_REGISTRY: dict[tuple, int] = {}
|
|
32
|
+
|
|
33
|
+
# Routes to skip (set by patch_klein)
|
|
34
|
+
_ROUTES_TO_SKIP = []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def patch_klein(routes_to_skip: Optional[List[str]] = None):
|
|
38
|
+
"""
|
|
39
|
+
OTEL-STYLE Klein patch:
|
|
40
|
+
• Pre-registers endpoints on first route decoration
|
|
41
|
+
• Captures request headers/body if enabled
|
|
42
|
+
• Emits network hop AFTER handler completes (zero-overhead)
|
|
43
|
+
• Captures response headers/body if enabled
|
|
44
|
+
• Funnels exceptions through custom_excepthook
|
|
45
|
+
No-op if Klein isn't installed.
|
|
46
|
+
"""
|
|
47
|
+
global _ROUTES_TO_SKIP
|
|
48
|
+
_ROUTES_TO_SKIP = routes_to_skip or []
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
import klein
|
|
52
|
+
except ImportError:
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
original_route = klein.Klein.route
|
|
56
|
+
|
|
57
|
+
def patched_route(self, *args, **kwargs):
|
|
58
|
+
# Grab Klein's decorator for this pattern
|
|
59
|
+
original_decorator = original_route(self, *args, **kwargs)
|
|
60
|
+
|
|
61
|
+
def new_decorator(fn):
|
|
62
|
+
real_fn = _unwrap_user_func(fn)
|
|
63
|
+
|
|
64
|
+
# Skip non-user code
|
|
65
|
+
code = getattr(real_fn, "__code__", None)
|
|
66
|
+
if not code or not _is_user_code(code.co_filename):
|
|
67
|
+
return original_decorator(fn)
|
|
68
|
+
|
|
69
|
+
filename = code.co_filename
|
|
70
|
+
line_no = code.co_firstlineno
|
|
71
|
+
fn_name = real_fn.__name__
|
|
72
|
+
hop_key = (filename, line_no)
|
|
73
|
+
|
|
74
|
+
# Get route pattern from args (first arg after self is usually the path)
|
|
75
|
+
route_pattern = args[0] if args else None
|
|
76
|
+
|
|
77
|
+
# Check if route should be skipped
|
|
78
|
+
if route_pattern and should_skip_route(route_pattern, _ROUTES_TO_SKIP):
|
|
79
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
80
|
+
print(
|
|
81
|
+
f"[[Klein]] Skipping endpoint (route matches skip pattern): {route_pattern}",
|
|
82
|
+
log=False,
|
|
83
|
+
)
|
|
84
|
+
return original_decorator(fn)
|
|
85
|
+
|
|
86
|
+
# Pre-register endpoint
|
|
87
|
+
endpoint_id = _ENDPOINT_REGISTRY.get(hop_key)
|
|
88
|
+
if endpoint_id is None:
|
|
89
|
+
endpoint_id = register_endpoint(
|
|
90
|
+
line=str(line_no),
|
|
91
|
+
column="0",
|
|
92
|
+
name=fn_name,
|
|
93
|
+
entrypoint=filename,
|
|
94
|
+
route=route_pattern,
|
|
95
|
+
)
|
|
96
|
+
if endpoint_id >= 0:
|
|
97
|
+
_ENDPOINT_REGISTRY[hop_key] = endpoint_id
|
|
98
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
99
|
+
print(
|
|
100
|
+
f"[[Klein]] Registered endpoint: {fn_name} @ {filename}:{line_no} (id={endpoint_id})",
|
|
101
|
+
log=False,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Check if async or sync
|
|
105
|
+
is_async = inspect.iscoroutinefunction(real_fn)
|
|
106
|
+
|
|
107
|
+
if is_async:
|
|
108
|
+
|
|
109
|
+
@functools.wraps(fn)
|
|
110
|
+
async def wrapped_async(request, *f_args, **f_kwargs):
|
|
111
|
+
# 1. Trace-id propagation
|
|
112
|
+
header = request.getHeader(SAILFISH_TRACING_HEADER)
|
|
113
|
+
if header:
|
|
114
|
+
get_or_set_sf_trace_id(
|
|
115
|
+
header, is_associated_with_inbound_request=True
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Check for function span capture override header (highest priority!)
|
|
119
|
+
funcspan_override_header = request.getHeader(
|
|
120
|
+
"X-Sf3-FunctionSpanCaptureOverride"
|
|
121
|
+
)
|
|
122
|
+
if funcspan_override_header:
|
|
123
|
+
try:
|
|
124
|
+
_sffuncspan_config.set_thread_override(
|
|
125
|
+
funcspan_override_header
|
|
126
|
+
)
|
|
127
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
128
|
+
print(
|
|
129
|
+
f"[[Klein.async]] Set function span override from header: {funcspan_override_header}",
|
|
130
|
+
log=False,
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
134
|
+
print(
|
|
135
|
+
f"[[Klein.async]] Failed to set function span override: {e}",
|
|
136
|
+
log=False,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# 2. Capture request headers if enabled
|
|
140
|
+
req_headers = None
|
|
141
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS:
|
|
142
|
+
try:
|
|
143
|
+
# Klein/Twisted request.requestHeaders is a Headers object
|
|
144
|
+
req_headers = dict(
|
|
145
|
+
request.requestHeaders.getAllRawHeaders()
|
|
146
|
+
)
|
|
147
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
148
|
+
print(
|
|
149
|
+
f"[[Klein]] Captured request headers: {len(req_headers)} headers",
|
|
150
|
+
log=False,
|
|
151
|
+
)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
154
|
+
print(
|
|
155
|
+
f"[[Klein]] Failed to capture request headers: {e}",
|
|
156
|
+
log=False,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# 3. Capture request body if enabled
|
|
160
|
+
req_body = None
|
|
161
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_BODY:
|
|
162
|
+
try:
|
|
163
|
+
# Klein/Twisted request.content is a file-like object
|
|
164
|
+
body = request.content.read(_REQUEST_LIMIT_BYTES)
|
|
165
|
+
request.content.seek(0) # Reset for handler
|
|
166
|
+
req_body = body if body else None
|
|
167
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
168
|
+
print(
|
|
169
|
+
f"[[Klein]] Request body capture: {len(body) if body else 0} bytes (method={request.method})",
|
|
170
|
+
log=False,
|
|
171
|
+
)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
174
|
+
print(
|
|
175
|
+
f"[[Klein]] Failed to capture request body: {e}",
|
|
176
|
+
log=False,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
180
|
+
print(
|
|
181
|
+
f"[[Klein]] Captured endpoint: {fn_name} ({filename}:{line_no}) endpoint_id={endpoint_id}",
|
|
182
|
+
log=False,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# 4. Call handler and capture exceptions
|
|
186
|
+
response = None
|
|
187
|
+
try:
|
|
188
|
+
response = await fn(request, *f_args, **f_kwargs)
|
|
189
|
+
except Exception as exc:
|
|
190
|
+
custom_excepthook(type(exc), exc, exc.__traceback__)
|
|
191
|
+
raise
|
|
192
|
+
|
|
193
|
+
# 5. Capture response headers if enabled
|
|
194
|
+
resp_headers = None
|
|
195
|
+
if SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS:
|
|
196
|
+
try:
|
|
197
|
+
# Klein/Twisted request.responseHeaders is a Headers object
|
|
198
|
+
resp_headers = dict(
|
|
199
|
+
request.responseHeaders.getAllRawHeaders()
|
|
200
|
+
)
|
|
201
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
202
|
+
print(
|
|
203
|
+
f"[[Klein]] Captured response headers: {len(resp_headers)} headers",
|
|
204
|
+
log=False,
|
|
205
|
+
)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
208
|
+
print(
|
|
209
|
+
f"[[Klein]] Failed to capture response headers: {e}",
|
|
210
|
+
log=False,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# 6. Capture response body if enabled
|
|
214
|
+
resp_body = None
|
|
215
|
+
if SF_NETWORKHOP_CAPTURE_RESPONSE_BODY:
|
|
216
|
+
try:
|
|
217
|
+
# Klein handlers return bytes directly
|
|
218
|
+
if isinstance(response, bytes):
|
|
219
|
+
resp_body = response[:_RESPONSE_LIMIT_BYTES]
|
|
220
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
221
|
+
print(
|
|
222
|
+
f"[[Klein]] Captured response body: {len(resp_body)} bytes",
|
|
223
|
+
log=False,
|
|
224
|
+
)
|
|
225
|
+
elif isinstance(response, str):
|
|
226
|
+
resp_body = response.encode("utf-8")[
|
|
227
|
+
:_RESPONSE_LIMIT_BYTES
|
|
228
|
+
]
|
|
229
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
230
|
+
print(
|
|
231
|
+
f"[[Klein]] Captured response body (str): {len(resp_body)} bytes",
|
|
232
|
+
log=False,
|
|
233
|
+
)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
236
|
+
print(
|
|
237
|
+
f"[[Klein]] Failed to capture response body: {e}",
|
|
238
|
+
log=False,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# 7. OTEL-STYLE: Emit network hop AFTER handler completes
|
|
242
|
+
if endpoint_id is not None and endpoint_id >= 0:
|
|
243
|
+
try:
|
|
244
|
+
_, session_id = get_or_set_sf_trace_id()
|
|
245
|
+
|
|
246
|
+
# Extract raw path and query string for C to parse
|
|
247
|
+
raw_path = (
|
|
248
|
+
request.path
|
|
249
|
+
if isinstance(request.path, str)
|
|
250
|
+
else request.path.decode("utf-8")
|
|
251
|
+
)
|
|
252
|
+
# Twisted request.uri includes full URI with query string (bytes)
|
|
253
|
+
uri_parts = request.uri.split(b"?", 1)
|
|
254
|
+
raw_query = uri_parts[1] if len(uri_parts) > 1 else b""
|
|
255
|
+
|
|
256
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
257
|
+
print(
|
|
258
|
+
f"[[Klein]] About to emit network hop: endpoint_id={endpoint_id}, "
|
|
259
|
+
f"req_headers={'present' if req_headers else 'None'}, "
|
|
260
|
+
f"req_body={len(req_body) if req_body else 0} bytes, "
|
|
261
|
+
f"resp_headers={'present' if resp_headers else 'None'}, "
|
|
262
|
+
f"resp_body={len(resp_body) if resp_body else 0} bytes",
|
|
263
|
+
log=False,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Direct C call - queues to background worker, returns instantly
|
|
267
|
+
# C will parse route and query_params from raw data
|
|
268
|
+
fast_send_network_hop_fast(
|
|
269
|
+
session_id=session_id,
|
|
270
|
+
endpoint_id=endpoint_id,
|
|
271
|
+
raw_path=raw_path,
|
|
272
|
+
raw_query_string=raw_query,
|
|
273
|
+
request_headers=req_headers,
|
|
274
|
+
request_body=req_body,
|
|
275
|
+
response_headers=resp_headers,
|
|
276
|
+
response_body=resp_body,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
280
|
+
print(
|
|
281
|
+
f"[[Klein]] Emitted network hop: endpoint_id={endpoint_id} "
|
|
282
|
+
f"session={session_id}",
|
|
283
|
+
log=False,
|
|
284
|
+
)
|
|
285
|
+
except Exception as e: # noqa: BLE001 S110
|
|
286
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
287
|
+
print(
|
|
288
|
+
f"[[Klein]] Failed to emit network hop: {e}",
|
|
289
|
+
log=False,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Clear function span override for this request (thread-local cleanup)
|
|
293
|
+
try:
|
|
294
|
+
_sffuncspan_config.clear_thread_override()
|
|
295
|
+
except Exception:
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
return response
|
|
299
|
+
|
|
300
|
+
return original_decorator(wrapped_async)
|
|
301
|
+
else:
|
|
302
|
+
|
|
303
|
+
@functools.wraps(fn)
|
|
304
|
+
def wrapped_sync(request, *f_args, **f_kwargs):
|
|
305
|
+
# 1. Trace-id propagation
|
|
306
|
+
header = request.getHeader(SAILFISH_TRACING_HEADER)
|
|
307
|
+
if header:
|
|
308
|
+
get_or_set_sf_trace_id(
|
|
309
|
+
header, is_associated_with_inbound_request=True
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Check for function span capture override header (highest priority!)
|
|
313
|
+
funcspan_override_header = request.getHeader(
|
|
314
|
+
"X-Sf3-FunctionSpanCaptureOverride"
|
|
315
|
+
)
|
|
316
|
+
if funcspan_override_header:
|
|
317
|
+
try:
|
|
318
|
+
_sffuncspan_config.set_thread_override(
|
|
319
|
+
funcspan_override_header
|
|
320
|
+
)
|
|
321
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
322
|
+
print(
|
|
323
|
+
f"[[Klein.sync]] Set function span override from header: {funcspan_override_header}",
|
|
324
|
+
log=False,
|
|
325
|
+
)
|
|
326
|
+
except Exception as e:
|
|
327
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
328
|
+
print(
|
|
329
|
+
f"[[Klein.sync]] Failed to set function span override: {e}",
|
|
330
|
+
log=False,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# 2. Capture request headers if enabled
|
|
334
|
+
req_headers = None
|
|
335
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS:
|
|
336
|
+
try:
|
|
337
|
+
# Klein/Twisted request.requestHeaders is a Headers object
|
|
338
|
+
req_headers = dict(
|
|
339
|
+
request.requestHeaders.getAllRawHeaders()
|
|
340
|
+
)
|
|
341
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
342
|
+
print(
|
|
343
|
+
f"[[Klein]] Captured request headers: {len(req_headers)} headers",
|
|
344
|
+
log=False,
|
|
345
|
+
)
|
|
346
|
+
except Exception as e:
|
|
347
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
348
|
+
print(
|
|
349
|
+
f"[[Klein]] Failed to capture request headers: {e}",
|
|
350
|
+
log=False,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# 3. Capture request body if enabled
|
|
354
|
+
req_body = None
|
|
355
|
+
if SF_NETWORKHOP_CAPTURE_REQUEST_BODY:
|
|
356
|
+
try:
|
|
357
|
+
# Klein/Twisted request.content is a file-like object
|
|
358
|
+
body = request.content.read(_REQUEST_LIMIT_BYTES)
|
|
359
|
+
request.content.seek(0) # Reset for handler
|
|
360
|
+
req_body = body if body else None
|
|
361
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
362
|
+
print(
|
|
363
|
+
f"[[Klein]] Request body capture: {len(body) if body else 0} bytes (method={request.method})",
|
|
364
|
+
log=False,
|
|
365
|
+
)
|
|
366
|
+
except Exception as e:
|
|
367
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
368
|
+
print(
|
|
369
|
+
f"[[Klein]] Failed to capture request body: {e}",
|
|
370
|
+
log=False,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
374
|
+
print(
|
|
375
|
+
f"[[Klein]] Captured endpoint: {fn_name} ({filename}:{line_no}) endpoint_id={endpoint_id}",
|
|
376
|
+
log=False,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# 4. Call handler and capture exceptions
|
|
380
|
+
response = None
|
|
381
|
+
try:
|
|
382
|
+
response = fn(request, *f_args, **f_kwargs)
|
|
383
|
+
except Exception as exc:
|
|
384
|
+
custom_excepthook(type(exc), exc, exc.__traceback__)
|
|
385
|
+
raise
|
|
386
|
+
|
|
387
|
+
# 5. Capture response headers if enabled
|
|
388
|
+
resp_headers = None
|
|
389
|
+
if SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS:
|
|
390
|
+
try:
|
|
391
|
+
# Klein/Twisted request.responseHeaders is a Headers object
|
|
392
|
+
resp_headers = dict(
|
|
393
|
+
request.responseHeaders.getAllRawHeaders()
|
|
394
|
+
)
|
|
395
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
396
|
+
print(
|
|
397
|
+
f"[[Klein]] Captured response headers: {len(resp_headers)} headers",
|
|
398
|
+
log=False,
|
|
399
|
+
)
|
|
400
|
+
except Exception as e:
|
|
401
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
402
|
+
print(
|
|
403
|
+
f"[[Klein]] Failed to capture response headers: {e}",
|
|
404
|
+
log=False,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# 6. Capture response body if enabled
|
|
408
|
+
resp_body = None
|
|
409
|
+
if SF_NETWORKHOP_CAPTURE_RESPONSE_BODY:
|
|
410
|
+
try:
|
|
411
|
+
# Klein handlers return bytes directly
|
|
412
|
+
if isinstance(response, bytes):
|
|
413
|
+
resp_body = response[:_RESPONSE_LIMIT_BYTES]
|
|
414
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
415
|
+
print(
|
|
416
|
+
f"[[Klein]] Captured response body: {len(resp_body)} bytes",
|
|
417
|
+
log=False,
|
|
418
|
+
)
|
|
419
|
+
elif isinstance(response, str):
|
|
420
|
+
resp_body = response.encode("utf-8")[
|
|
421
|
+
:_RESPONSE_LIMIT_BYTES
|
|
422
|
+
]
|
|
423
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
424
|
+
print(
|
|
425
|
+
f"[[Klein]] Captured response body (str): {len(resp_body)} bytes",
|
|
426
|
+
log=False,
|
|
427
|
+
)
|
|
428
|
+
except Exception as e:
|
|
429
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
430
|
+
print(
|
|
431
|
+
f"[[Klein]] Failed to capture response body: {e}",
|
|
432
|
+
log=False,
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
# 7. OTEL-STYLE: Emit network hop AFTER handler completes
|
|
436
|
+
if endpoint_id is not None and endpoint_id >= 0:
|
|
437
|
+
try:
|
|
438
|
+
_, session_id = get_or_set_sf_trace_id()
|
|
439
|
+
|
|
440
|
+
# Extract raw path and query string for C to parse
|
|
441
|
+
raw_path = (
|
|
442
|
+
request.path
|
|
443
|
+
if isinstance(request.path, str)
|
|
444
|
+
else request.path.decode("utf-8")
|
|
445
|
+
)
|
|
446
|
+
# Twisted request.uri includes full URI with query string (bytes)
|
|
447
|
+
uri_parts = request.uri.split(b"?", 1)
|
|
448
|
+
raw_query = uri_parts[1] if len(uri_parts) > 1 else b""
|
|
449
|
+
|
|
450
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
451
|
+
print(
|
|
452
|
+
f"[[Klein]] About to emit network hop: endpoint_id={endpoint_id}, "
|
|
453
|
+
f"req_headers={'present' if req_headers else 'None'}, "
|
|
454
|
+
f"req_body={len(req_body) if req_body else 0} bytes, "
|
|
455
|
+
f"resp_headers={'present' if resp_headers else 'None'}, "
|
|
456
|
+
f"resp_body={len(resp_body) if resp_body else 0} bytes",
|
|
457
|
+
log=False,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
# Direct C call - queues to background worker, returns instantly
|
|
461
|
+
# C will parse route and query_params from raw data
|
|
462
|
+
fast_send_network_hop_fast(
|
|
463
|
+
session_id=session_id,
|
|
464
|
+
endpoint_id=endpoint_id,
|
|
465
|
+
raw_path=raw_path,
|
|
466
|
+
raw_query_string=raw_query,
|
|
467
|
+
request_headers=req_headers,
|
|
468
|
+
request_body=req_body,
|
|
469
|
+
response_headers=resp_headers,
|
|
470
|
+
response_body=resp_body,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
474
|
+
print(
|
|
475
|
+
f"[[Klein]] Emitted network hop: endpoint_id={endpoint_id} "
|
|
476
|
+
f"session={session_id}",
|
|
477
|
+
log=False,
|
|
478
|
+
)
|
|
479
|
+
except Exception as e: # noqa: BLE001 S110
|
|
480
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
481
|
+
print(
|
|
482
|
+
f"[[Klein]] Failed to emit network hop: {e}",
|
|
483
|
+
log=False,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
# Clear function span override for this request (thread-local cleanup)
|
|
487
|
+
try:
|
|
488
|
+
_sffuncspan_config.clear_thread_override()
|
|
489
|
+
except Exception:
|
|
490
|
+
pass
|
|
491
|
+
|
|
492
|
+
return response
|
|
493
|
+
|
|
494
|
+
return original_decorator(wrapped_sync)
|
|
495
|
+
|
|
496
|
+
return new_decorator
|
|
497
|
+
|
|
498
|
+
klein.Klein.route = patched_route
|
|
499
|
+
|
|
500
|
+
if SF_DEBUG and app_config._interceptors_initialized:
|
|
501
|
+
print("[[patch_klein]] OTEL-style route patch applied", log=False)
|