sf-veritas 0.11.10__cp314-cp314-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.
- sf_veritas/__init__.py +46 -0
- sf_veritas/_auto_preload.py +73 -0
- sf_veritas/_sfconfig.c +162 -0
- sf_veritas/_sfconfig.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfcrashhandler.c +267 -0
- sf_veritas/_sfcrashhandler.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastlog.c +953 -0
- sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnet.c +994 -0
- sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnetworkrequest.c +727 -0
- sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan.c +2791 -0
- sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan_config.c +730 -0
- sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfheadercheck.c +341 -0
- sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfnetworkhop.c +1454 -0
- sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfservice.c +1223 -0
- sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfteepreload.c +6227 -0
- sf_veritas/app_config.py +57 -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 +146 -0
- sf_veritas/custom_output_wrapper.py +153 -0
- sf_veritas/custom_print.py +153 -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 +693 -0
- sf_veritas/function_span_profiler.py +1313 -0
- sf_veritas/get_preload_path.py +34 -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 +543 -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/_patch_tracker.py +74 -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 +99 -0
- sf_veritas/patches/network_libraries/aiohttp.py +294 -0
- sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
- sf_veritas/patches/network_libraries/http_client.py +670 -0
- sf_veritas/patches/network_libraries/httpcore.py +580 -0
- sf_veritas/patches/network_libraries/httplib2.py +315 -0
- sf_veritas/patches/network_libraries/httpx.py +557 -0
- sf_veritas/patches/network_libraries/niquests.py +218 -0
- sf_veritas/patches/network_libraries/pycurl.py +399 -0
- sf_veritas/patches/network_libraries/requests.py +595 -0
- sf_veritas/patches/network_libraries/ssl_socket.py +822 -0
- sf_veritas/patches/network_libraries/tornado.py +360 -0
- sf_veritas/patches/network_libraries/treq.py +270 -0
- sf_veritas/patches/network_libraries/urllib_request.py +483 -0
- sf_veritas/patches/network_libraries/utils.py +598 -0
- sf_veritas/patches/os.py +17 -0
- sf_veritas/patches/threading.py +231 -0
- sf_veritas/patches/web_frameworks/__init__.py +54 -0
- sf_veritas/patches/web_frameworks/aiohttp.py +798 -0
- sf_veritas/patches/web_frameworks/async_websocket_consumer.py +337 -0
- sf_veritas/patches/web_frameworks/blacksheep.py +532 -0
- sf_veritas/patches/web_frameworks/bottle.py +513 -0
- sf_veritas/patches/web_frameworks/cherrypy.py +683 -0
- sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
- sf_veritas/patches/web_frameworks/django.py +963 -0
- sf_veritas/patches/web_frameworks/eve.py +401 -0
- sf_veritas/patches/web_frameworks/falcon.py +931 -0
- sf_veritas/patches/web_frameworks/fastapi.py +738 -0
- sf_veritas/patches/web_frameworks/flask.py +526 -0
- sf_veritas/patches/web_frameworks/klein.py +501 -0
- sf_veritas/patches/web_frameworks/litestar.py +616 -0
- sf_veritas/patches/web_frameworks/pyramid.py +440 -0
- sf_veritas/patches/web_frameworks/quart.py +841 -0
- sf_veritas/patches/web_frameworks/robyn.py +708 -0
- sf_veritas/patches/web_frameworks/sanic.py +874 -0
- sf_veritas/patches/web_frameworks/starlette.py +742 -0
- sf_veritas/patches/web_frameworks/strawberry.py +1446 -0
- sf_veritas/patches/web_frameworks/tornado.py +485 -0
- sf_veritas/patches/web_frameworks/utils.py +170 -0
- sf_veritas/print_override.py +13 -0
- sf_veritas/regular_data_transmitter.py +444 -0
- sf_veritas/request_interceptor.py +401 -0
- sf_veritas/request_utils.py +550 -0
- sf_veritas/segfault_handler.py +116 -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 +1319 -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 +1678 -0
- sf_veritas/utils.py +39 -0
- sf_veritas-0.11.10.dist-info/METADATA +97 -0
- sf_veritas-0.11.10.dist-info/RECORD +141 -0
- sf_veritas-0.11.10.dist-info/WHEEL +5 -0
- sf_veritas-0.11.10.dist-info/entry_points.txt +2 -0
- sf_veritas-0.11.10.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,598 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared helpers used by all network-patch modules.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
from functools import lru_cache
|
|
11
|
+
from typing import List, Optional, Tuple
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
|
+
|
|
14
|
+
from ... import app_config
|
|
15
|
+
from ...constants import FUNCSPAN_OVERRIDE_HEADER, SAILFISH_TRACING_HEADER
|
|
16
|
+
from ...env_vars import SF_DEBUG
|
|
17
|
+
from ...regular_data_transmitter import NetworkRequestTransmitter
|
|
18
|
+
from ...thread_local import (
|
|
19
|
+
funcspan_override_ctx,
|
|
20
|
+
get_current_function_span_id,
|
|
21
|
+
get_funcspan_override,
|
|
22
|
+
get_or_set_sf_trace_id,
|
|
23
|
+
get_outbound_headers_with_new_uuid,
|
|
24
|
+
is_network_recording_suppressed,
|
|
25
|
+
trace_id_ctx,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Try to import the C extension for ultra-fast network request recording
|
|
29
|
+
try:
|
|
30
|
+
from ... import _sffastnet
|
|
31
|
+
|
|
32
|
+
_FAST_NET_AVAILABLE = True
|
|
33
|
+
except ImportError:
|
|
34
|
+
_FAST_NET_AVAILABLE = False
|
|
35
|
+
_sffastnet = None
|
|
36
|
+
|
|
37
|
+
# Try to import the C extension for http.client patching (captures headers/bodies)
|
|
38
|
+
try:
|
|
39
|
+
from ... import _sffastnetworkrequest
|
|
40
|
+
|
|
41
|
+
_FAST_NETWORKREQUEST_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
_FAST_NETWORKREQUEST_AVAILABLE = False
|
|
44
|
+
_sffastnetworkrequest = None
|
|
45
|
+
|
|
46
|
+
# Try to import the C extension for ultra-fast header checking (domain filtering)
|
|
47
|
+
try:
|
|
48
|
+
from ... import _sfheadercheck
|
|
49
|
+
|
|
50
|
+
_HAS_FAST_HEADER_CHECK = True
|
|
51
|
+
except ImportError:
|
|
52
|
+
_HAS_FAST_HEADER_CHECK = False
|
|
53
|
+
_sfheadercheck = None
|
|
54
|
+
|
|
55
|
+
# Pre-compute lowercase header name for fast case-insensitive checks
|
|
56
|
+
_TRACE_HEADER_LOWER = SAILFISH_TRACING_HEADER.lower()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def has_sailfish_header(headers: dict) -> bool:
|
|
60
|
+
"""Fast case-insensitive check for Sailfish tracing header.
|
|
61
|
+
|
|
62
|
+
Optimized for common cases with O(1) dict lookup before O(n) scan:
|
|
63
|
+
- Fast path 1: Exact match 'X-Sf3-Rid' (~10ns, most common)
|
|
64
|
+
- Fast path 2: Lowercase 'x-sf3-rid' (~10ns, httplib2 normalized)
|
|
65
|
+
- Slow path: Full case-insensitive scan (~50ns for 10 headers, rare)
|
|
66
|
+
|
|
67
|
+
Performance: ~10ns for 99% of cases, ~50ns worst case.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
headers: Dictionary of HTTP headers (any case)
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
True if X-Sf3-Rid header exists (case-insensitive), False otherwise
|
|
74
|
+
"""
|
|
75
|
+
# Fast path: exact match (most common, O(1) dict lookup ~10ns)
|
|
76
|
+
if SAILFISH_TRACING_HEADER in headers:
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
# Fast path: lowercase match (httplib2 normalizes to lowercase, O(1) ~10ns)
|
|
80
|
+
if _TRACE_HEADER_LOWER in headers:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
# Slow path: comprehensive case-insensitive check (O(n) ~50ns for 10 headers)
|
|
84
|
+
# Only hits this if header has unusual mixed case like 'X-sf3-RID' or 'x-Sf3-Rid'
|
|
85
|
+
return any(
|
|
86
|
+
isinstance(k, str) and k.lower() == _TRACE_HEADER_LOWER for k in headers.keys()
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
_FAST_NET_INITIALIZED = False
|
|
91
|
+
_FAST_NETWORKREQUEST_INITIALIZED = False
|
|
92
|
+
|
|
93
|
+
# GraphQL mutation for network requests
|
|
94
|
+
_COLLECT_NETWORK_REQUEST_MUTATION = """
|
|
95
|
+
mutation collectNetworkRequest($data: NetworkRequestInput!) {
|
|
96
|
+
collectNetworkRequest(data: $data)
|
|
97
|
+
}
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def init_fast_networkrequest_tracking():
|
|
102
|
+
"""Initialize the C extension for http.client network request tracking (with body/header capture)."""
|
|
103
|
+
global _FAST_NETWORKREQUEST_INITIALIZED
|
|
104
|
+
if (
|
|
105
|
+
not _FAST_NETWORKREQUEST_AVAILABLE
|
|
106
|
+
or _FAST_NETWORKREQUEST_INITIALIZED
|
|
107
|
+
or not _sffastnetworkrequest
|
|
108
|
+
):
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
|
|
113
|
+
ok = _sffastnetworkrequest.init_networkhop(
|
|
114
|
+
url=app_config._sailfish_graphql_endpoint,
|
|
115
|
+
query=_COLLECT_NETWORK_REQUEST_MUTATION,
|
|
116
|
+
api_key=app_config._sailfish_api_key,
|
|
117
|
+
service_uuid=app_config._service_uuid or "",
|
|
118
|
+
library=getattr(app_config, "library", "sf-veritas"),
|
|
119
|
+
version=getattr(app_config, "version", "0.0.0"),
|
|
120
|
+
http2=http2,
|
|
121
|
+
)
|
|
122
|
+
if ok:
|
|
123
|
+
_FAST_NETWORKREQUEST_INITIALIZED = True
|
|
124
|
+
if SF_DEBUG:
|
|
125
|
+
print(
|
|
126
|
+
"[_sffastnetworkrequest] initialized (libcurl sender with body/header capture)",
|
|
127
|
+
log=False,
|
|
128
|
+
)
|
|
129
|
+
return True
|
|
130
|
+
except Exception as e:
|
|
131
|
+
if SF_DEBUG:
|
|
132
|
+
print(f"[_sffastnetworkrequest] init failed; falling back: {e}", log=False)
|
|
133
|
+
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def init_fast_network_tracking():
|
|
138
|
+
"""Initialize the C extensions for network request tracking (both _sffastnet and _sffastnetworkrequest)."""
|
|
139
|
+
global _FAST_NET_INITIALIZED
|
|
140
|
+
|
|
141
|
+
# Initialize _sffastnet (generic network requests)
|
|
142
|
+
net_ok = False
|
|
143
|
+
if _FAST_NET_AVAILABLE and not _FAST_NET_INITIALIZED and _sffastnet:
|
|
144
|
+
try:
|
|
145
|
+
http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
|
|
146
|
+
ok = _sffastnet.init(
|
|
147
|
+
url=app_config._sailfish_graphql_endpoint,
|
|
148
|
+
query=_COLLECT_NETWORK_REQUEST_MUTATION,
|
|
149
|
+
api_key=app_config._sailfish_api_key,
|
|
150
|
+
http2=http2,
|
|
151
|
+
)
|
|
152
|
+
if ok:
|
|
153
|
+
_FAST_NET_INITIALIZED = True
|
|
154
|
+
net_ok = True
|
|
155
|
+
if SF_DEBUG:
|
|
156
|
+
print("[_sffastnet] initialized (libcurl sender)", log=False)
|
|
157
|
+
except Exception as e:
|
|
158
|
+
if SF_DEBUG:
|
|
159
|
+
print(f"[_sffastnet] init failed; falling back: {e}", log=False)
|
|
160
|
+
|
|
161
|
+
# Initialize _sffastnetworkrequest (http.client with body/header capture)
|
|
162
|
+
netreq_ok = init_fast_networkrequest_tracking()
|
|
163
|
+
|
|
164
|
+
return net_ok or netreq_ok
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
###############################################################################
|
|
168
|
+
# ULTRA-FAST Header Injection (<100ns target)
|
|
169
|
+
###############################################################################
|
|
170
|
+
|
|
171
|
+
# Thread-local cache for ultra-fast header injection
|
|
172
|
+
_thread_local = threading.local()
|
|
173
|
+
|
|
174
|
+
# [DIAGNOSTICS] Global counters for tracking request success/failure
|
|
175
|
+
_request_attempt_counter = 0
|
|
176
|
+
_request_success_counter = 0
|
|
177
|
+
_request_failure_counter = 0
|
|
178
|
+
_counter_lock = threading.Lock()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_request_stats() -> dict:
|
|
182
|
+
"""Get diagnostic statistics for request tracking."""
|
|
183
|
+
return {
|
|
184
|
+
"attempts": _request_attempt_counter,
|
|
185
|
+
"success": _request_success_counter,
|
|
186
|
+
"failures": _request_failure_counter,
|
|
187
|
+
"deficit": _request_attempt_counter
|
|
188
|
+
- _request_success_counter
|
|
189
|
+
- _request_failure_counter,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def print_request_stats() -> None:
|
|
194
|
+
"""Print diagnostic statistics for request tracking."""
|
|
195
|
+
stats = get_request_stats()
|
|
196
|
+
print(
|
|
197
|
+
f"\n[REQUEST_STATS] attempts={stats['attempts']} success={stats['success']} failures={stats['failures']} deficit={stats['deficit']}",
|
|
198
|
+
log=False,
|
|
199
|
+
)
|
|
200
|
+
if stats["deficit"] > 0:
|
|
201
|
+
print(
|
|
202
|
+
f"[REQUEST_STATS] ⚠️ WARNING: {stats['deficit']} requests neither succeeded nor failed - possible bug!",
|
|
203
|
+
log=False,
|
|
204
|
+
)
|
|
205
|
+
if stats["failures"] > 0:
|
|
206
|
+
print(
|
|
207
|
+
f"[REQUEST_STATS] ⚠️ WARNING: {stats['failures']} requests failed - check error logs above",
|
|
208
|
+
log=False,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def track_request_result(
|
|
213
|
+
success: bool, error: Optional[Exception] = None, url: str = ""
|
|
214
|
+
) -> None:
|
|
215
|
+
"""Track whether a request succeeded or failed (only when SF_DEBUG is enabled)."""
|
|
216
|
+
if not SF_DEBUG:
|
|
217
|
+
return # Skip tracking entirely to avoid lock contention in production
|
|
218
|
+
|
|
219
|
+
global _request_success_counter, _request_failure_counter
|
|
220
|
+
with _counter_lock:
|
|
221
|
+
if success:
|
|
222
|
+
_request_success_counter += 1
|
|
223
|
+
else:
|
|
224
|
+
_request_failure_counter += 1
|
|
225
|
+
error_type = type(error).__name__ if error else "Unknown"
|
|
226
|
+
error_msg = str(error) if error else "Unknown error"
|
|
227
|
+
print(
|
|
228
|
+
f"[track_request_result] ❌ REQUEST FAILED: {error_type}: {error_msg} (url={url})",
|
|
229
|
+
log=False,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def inject_headers_ultrafast(
|
|
234
|
+
headers_dict: dict, url: str, domains_to_skip: List[str]
|
|
235
|
+
) -> None:
|
|
236
|
+
"""
|
|
237
|
+
ULTRA-FAST header injection (~100ns average).
|
|
238
|
+
|
|
239
|
+
Injects X-Sf3-Rid and X-Sf3-FunctionSpanCaptureOverride headers directly into dict.
|
|
240
|
+
Uses pre-built headers from OutboundHeaderManager with background UUID4 generation.
|
|
241
|
+
|
|
242
|
+
Performance:
|
|
243
|
+
- Filtered domain: ~30ns (domain check only)
|
|
244
|
+
- Fast path (pre-generated): ~100ns (domain check + header injection)
|
|
245
|
+
- Slow path (generate on-demand): ~500ns (domain check + synchronous UUID4)
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
headers_dict: Dictionary to inject headers into (mutated in-place)
|
|
249
|
+
url: Destination URL for domain filtering
|
|
250
|
+
domains_to_skip: List of domains to skip header propagation
|
|
251
|
+
"""
|
|
252
|
+
# [DIAGNOSTICS] Count request attempts (only when SF_DEBUG is enabled to avoid lock contention)
|
|
253
|
+
if SF_DEBUG:
|
|
254
|
+
global _request_attempt_counter
|
|
255
|
+
with _counter_lock:
|
|
256
|
+
_request_attempt_counter += 1
|
|
257
|
+
attempt_id = _request_attempt_counter
|
|
258
|
+
print(
|
|
259
|
+
f"[inject_headers_ultrafast] 🚀 CALLED #{attempt_id} with url={url}, domains_to_skip={domains_to_skip}",
|
|
260
|
+
log=False,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# FAST: Domain filtering check (LRU cached, ~20ns)
|
|
264
|
+
if domains_to_skip:
|
|
265
|
+
domain = extract_domain(url)
|
|
266
|
+
if domain in domains_to_skip:
|
|
267
|
+
if SF_DEBUG:
|
|
268
|
+
print(
|
|
269
|
+
f"[inject_headers_ultrafast] ⛔ Skipped (domain filtered)",
|
|
270
|
+
log=False,
|
|
271
|
+
)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
# ULTRA-FAST: Get pre-built header with new UUID (~10-20ns with LD_PRELOAD)
|
|
275
|
+
# Measure ONLY the critical path (excluding debug prints)
|
|
276
|
+
if SF_DEBUG:
|
|
277
|
+
start_ns = time.perf_counter_ns()
|
|
278
|
+
|
|
279
|
+
outbound_headers = get_outbound_headers_with_new_uuid()
|
|
280
|
+
|
|
281
|
+
if SF_DEBUG:
|
|
282
|
+
get_headers_ns = time.perf_counter_ns() - start_ns
|
|
283
|
+
|
|
284
|
+
if SF_DEBUG:
|
|
285
|
+
print(
|
|
286
|
+
f"[inject_headers_ultrafast] 📦 get_outbound_headers_with_new_uuid() returned: {outbound_headers} (took {get_headers_ns}ns)",
|
|
287
|
+
log=False,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if outbound_headers:
|
|
291
|
+
# FAST: Dict update (~30ns)
|
|
292
|
+
if SF_DEBUG:
|
|
293
|
+
start_update_ns = time.perf_counter_ns()
|
|
294
|
+
|
|
295
|
+
headers_dict.update(outbound_headers)
|
|
296
|
+
|
|
297
|
+
if SF_DEBUG:
|
|
298
|
+
update_ns = time.perf_counter_ns() - start_update_ns
|
|
299
|
+
total_ns = get_headers_ns + update_ns
|
|
300
|
+
# Pre-generated = ContextVar lookup (10-50ns) + dict.get() (10-50ns) + dict.update() (20-50ns) = ~50-150ns typical
|
|
301
|
+
# Allow up to 1μs for variance (CPU scheduler, context switches, etc.)
|
|
302
|
+
status = "pre-generated" if total_ns < 1000 else "generated on-demand"
|
|
303
|
+
print(
|
|
304
|
+
f"[inject_headers_ultrafast] ✅ Updated headers_dict. get={get_headers_ns}ns, update={update_ns}ns, total={total_ns}ns ({status})",
|
|
305
|
+
log=False,
|
|
306
|
+
)
|
|
307
|
+
else:
|
|
308
|
+
if SF_DEBUG:
|
|
309
|
+
print(
|
|
310
|
+
f"[inject_headers_ultrafast] ⚠️ No outbound headers returned (empty dict)",
|
|
311
|
+
log=False,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
###############################################################################
|
|
316
|
+
# Domain-parsing utility (no external network / no tldextract needed)
|
|
317
|
+
###############################################################################
|
|
318
|
+
@lru_cache(maxsize=256)
|
|
319
|
+
def extract_domain(url: str) -> str:
|
|
320
|
+
"""
|
|
321
|
+
Return a canonical host name for header-propagation checks.
|
|
322
|
+
|
|
323
|
+
• Works entirely offline (std-lib only) – no remote download or file locks.
|
|
324
|
+
• Keeps sub-domains intact, just strips a leading “www.” and port numbers.
|
|
325
|
+
|
|
326
|
+
Examples
|
|
327
|
+
--------
|
|
328
|
+
>>> extract_domain("https://www.example.com:443/path")
|
|
329
|
+
'example.com'
|
|
330
|
+
>>> extract_domain("https://api.foo.bar.example.co.uk/v1")
|
|
331
|
+
'api.foo.bar.example.co.uk'
|
|
332
|
+
"""
|
|
333
|
+
try:
|
|
334
|
+
host = urlparse(url).hostname or url
|
|
335
|
+
except Exception:
|
|
336
|
+
host = url # fall back to raw string on malformed URLs
|
|
337
|
+
if host.startswith("www."):
|
|
338
|
+
host = host[4:]
|
|
339
|
+
return host.lower()
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
###############################################################################
|
|
343
|
+
# Header-propagation + network-recording helpers
|
|
344
|
+
###############################################################################
|
|
345
|
+
def get_trace_and_should_propagate(
|
|
346
|
+
url: str,
|
|
347
|
+
domains_to_not_propagate: List[str],
|
|
348
|
+
) -> Tuple[str, bool]:
|
|
349
|
+
"""
|
|
350
|
+
Returns (trace_id, should_propagate?) for the given destination `url`.
|
|
351
|
+
"""
|
|
352
|
+
_, trace_id = get_or_set_sf_trace_id()
|
|
353
|
+
domain = extract_domain(url)
|
|
354
|
+
allow_header = domain not in domains_to_not_propagate
|
|
355
|
+
return trace_id, allow_header
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def init_fast_header_check(domains_to_not_propagate: List[str]) -> bool:
|
|
359
|
+
"""
|
|
360
|
+
Initialize the C extension for ultra-fast header checking with skip list.
|
|
361
|
+
|
|
362
|
+
Should be called once at patch time to set up the domain filtering list.
|
|
363
|
+
|
|
364
|
+
Returns: True if C extension initialized successfully, False otherwise.
|
|
365
|
+
"""
|
|
366
|
+
if _HAS_FAST_HEADER_CHECK and _sfheadercheck:
|
|
367
|
+
try:
|
|
368
|
+
_sfheadercheck.init_header_check(domains_to_not_propagate)
|
|
369
|
+
return True
|
|
370
|
+
except Exception:
|
|
371
|
+
return False
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def get_trace_and_should_propagate_fast(
|
|
376
|
+
url: str,
|
|
377
|
+
domains_to_not_propagate: List[str],
|
|
378
|
+
) -> Tuple[str, bool, Optional[str]]:
|
|
379
|
+
"""
|
|
380
|
+
Ultra-fast path using C extension for domain filtering.
|
|
381
|
+
|
|
382
|
+
Returns: (trace_id, should_propagate, funcspan_override)
|
|
383
|
+
|
|
384
|
+
Performance:
|
|
385
|
+
- Empty skip list: ~15ns (ContextVar reads only)
|
|
386
|
+
- With skip list: ~25ns (C domain parse + hash lookup + ContextVars)
|
|
387
|
+
- 10x faster than Python implementation (50-100ns)
|
|
388
|
+
|
|
389
|
+
Falls back to Python implementation if C extension not available.
|
|
390
|
+
"""
|
|
391
|
+
if _HAS_FAST_HEADER_CHECK and _sfheadercheck:
|
|
392
|
+
# C extension handles domain filtering + ContextVar reads
|
|
393
|
+
# Returns: (should_inject: bool, trace_id: str, funcspan_override: str | None)
|
|
394
|
+
try:
|
|
395
|
+
should_inject, trace_id, funcspan_override = (
|
|
396
|
+
_sfheadercheck.should_inject_headers(url)
|
|
397
|
+
)
|
|
398
|
+
return trace_id, should_inject, funcspan_override
|
|
399
|
+
except Exception:
|
|
400
|
+
# Fall back to Python on any error
|
|
401
|
+
pass
|
|
402
|
+
|
|
403
|
+
# Fallback to Python implementation
|
|
404
|
+
trace_id, allow = get_trace_and_should_propagate(url, domains_to_not_propagate)
|
|
405
|
+
funcspan_override = get_funcspan_override()
|
|
406
|
+
return trace_id, allow, funcspan_override
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def record_network_request(
|
|
410
|
+
trace_id: str,
|
|
411
|
+
url: str,
|
|
412
|
+
method: str,
|
|
413
|
+
status_code: int,
|
|
414
|
+
success: bool,
|
|
415
|
+
error: str | None = None,
|
|
416
|
+
timestamp_start: int | None = None,
|
|
417
|
+
timestamp_end: int | None = None,
|
|
418
|
+
request_data: bytes = b"",
|
|
419
|
+
response_data: bytes = b"",
|
|
420
|
+
request_headers: bytes = b"",
|
|
421
|
+
response_headers: bytes = b"",
|
|
422
|
+
) -> None:
|
|
423
|
+
"""
|
|
424
|
+
Fire off a GraphQL NetworkRequest mutation via C extension (fast path)
|
|
425
|
+
or NetworkRequestTransmitter (fallback).
|
|
426
|
+
Handles tripartite trace-ID splitting and default timestamps.
|
|
427
|
+
NEW: Supports request_data and response_data capture.
|
|
428
|
+
"""
|
|
429
|
+
# Capture parent_span_id IMMEDIATELY for async-safety
|
|
430
|
+
parent_span_id = get_current_function_span_id()
|
|
431
|
+
|
|
432
|
+
if SF_DEBUG:
|
|
433
|
+
print(
|
|
434
|
+
f"[[record_network_request]] CALLED: url={url}, method={method}, status={status_code}, trace_id={trace_id}, parent_span_id={parent_span_id}",
|
|
435
|
+
log=False,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
suppressed = is_network_recording_suppressed()
|
|
439
|
+
if SF_DEBUG:
|
|
440
|
+
print(
|
|
441
|
+
f"[[record_network_request]] is_network_recording_suppressed={suppressed}",
|
|
442
|
+
log=False,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if suppressed:
|
|
446
|
+
if SF_DEBUG:
|
|
447
|
+
print(f"[[record_network_request]] SUPPRESSED - returning early", log=False)
|
|
448
|
+
return
|
|
449
|
+
|
|
450
|
+
session_id, page_visit_id, request_id = None, None, None
|
|
451
|
+
parts = trace_id.split("/")
|
|
452
|
+
if parts:
|
|
453
|
+
session_id = parts[0]
|
|
454
|
+
if len(parts) > 1:
|
|
455
|
+
page_visit_id = parts[1]
|
|
456
|
+
if len(parts) > 2:
|
|
457
|
+
request_id = parts[2]
|
|
458
|
+
|
|
459
|
+
now_ms = lambda: int(time.time() * 1_000) # noqa: E731
|
|
460
|
+
ts0 = timestamp_start or now_ms()
|
|
461
|
+
ts1 = timestamp_end or now_ms()
|
|
462
|
+
|
|
463
|
+
if SF_DEBUG:
|
|
464
|
+
print(
|
|
465
|
+
f"[[record_network_request]] session_id={session_id}, page_visit_id={page_visit_id}, request_id={request_id}",
|
|
466
|
+
log=False,
|
|
467
|
+
)
|
|
468
|
+
print(
|
|
469
|
+
f"[[record_network_request]] service_uuid={app_config._service_uuid}",
|
|
470
|
+
log=False,
|
|
471
|
+
)
|
|
472
|
+
print(
|
|
473
|
+
f"[[record_network_request]] _FAST_NET_AVAILABLE={_FAST_NET_AVAILABLE}, _FAST_NET_INITIALIZED={_FAST_NET_INITIALIZED}",
|
|
474
|
+
log=False,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
# Use C fast path if available (positional args for maximum speed)
|
|
478
|
+
if _FAST_NET_AVAILABLE and _sffastnet and _FAST_NET_INITIALIZED:
|
|
479
|
+
if SF_DEBUG:
|
|
480
|
+
print(
|
|
481
|
+
f"[[record_network_request]] Using C fast path (_sffastnet.network_request)",
|
|
482
|
+
log=False,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Pass bytes directly for zero-copy access
|
|
486
|
+
# Order: request_id, page_visit_id, recording_session_id, service_uuid,
|
|
487
|
+
# timestamp_start, timestamp_end, response_code, success,
|
|
488
|
+
# error, url, method, request_data(bytes), response_data(bytes),
|
|
489
|
+
# request_headers(bytes), response_headers(bytes), parent_span_id
|
|
490
|
+
_sffastnet.network_request(
|
|
491
|
+
request_id or "",
|
|
492
|
+
page_visit_id or "",
|
|
493
|
+
session_id or "",
|
|
494
|
+
app_config._service_uuid or "",
|
|
495
|
+
ts0,
|
|
496
|
+
ts1,
|
|
497
|
+
status_code,
|
|
498
|
+
success,
|
|
499
|
+
None if success else ((error or "")[:255]),
|
|
500
|
+
url or "",
|
|
501
|
+
method.upper(),
|
|
502
|
+
request_data if isinstance(request_data, bytes) else b"",
|
|
503
|
+
response_data if isinstance(response_data, bytes) else b"",
|
|
504
|
+
request_headers if isinstance(request_headers, bytes) else b"",
|
|
505
|
+
response_headers if isinstance(response_headers, bytes) else b"",
|
|
506
|
+
parent_span_id, # Optional parent span ID for function span linking
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
if SF_DEBUG:
|
|
510
|
+
print(
|
|
511
|
+
f"[[record_network_request]] C fast path completed successfully",
|
|
512
|
+
log=False,
|
|
513
|
+
)
|
|
514
|
+
else:
|
|
515
|
+
if SF_DEBUG:
|
|
516
|
+
print(
|
|
517
|
+
f"[[record_network_request]] Using Python fallback (NetworkRequestTransmitter)",
|
|
518
|
+
log=False,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Fallback to Python implementation - convert bytes headers to dicts
|
|
522
|
+
import json
|
|
523
|
+
|
|
524
|
+
req_headers_dict = None
|
|
525
|
+
resp_headers_dict = None
|
|
526
|
+
|
|
527
|
+
if request_headers:
|
|
528
|
+
try:
|
|
529
|
+
req_headers_dict = (
|
|
530
|
+
json.loads(request_headers)
|
|
531
|
+
if isinstance(request_headers, bytes)
|
|
532
|
+
else request_headers
|
|
533
|
+
)
|
|
534
|
+
except Exception:
|
|
535
|
+
pass
|
|
536
|
+
|
|
537
|
+
if response_headers:
|
|
538
|
+
try:
|
|
539
|
+
resp_headers_dict = (
|
|
540
|
+
json.loads(response_headers)
|
|
541
|
+
if isinstance(response_headers, bytes)
|
|
542
|
+
else response_headers
|
|
543
|
+
)
|
|
544
|
+
except Exception:
|
|
545
|
+
pass
|
|
546
|
+
|
|
547
|
+
# Convert bytes data to strings for body fields
|
|
548
|
+
req_body_str = (
|
|
549
|
+
request_data.decode("utf-8", errors="ignore")[:4096]
|
|
550
|
+
if request_data
|
|
551
|
+
else None
|
|
552
|
+
)
|
|
553
|
+
resp_body_str = (
|
|
554
|
+
response_data.decode("utf-8", errors="ignore")[:4096]
|
|
555
|
+
if response_data
|
|
556
|
+
else None
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
if SF_DEBUG:
|
|
560
|
+
print(
|
|
561
|
+
f"[[record_network_request]] Calling NetworkRequestTransmitter().send()",
|
|
562
|
+
log=False,
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
NetworkRequestTransmitter().send(
|
|
566
|
+
request_id=request_id or "",
|
|
567
|
+
page_visit_id=page_visit_id,
|
|
568
|
+
recording_session_id=session_id or "",
|
|
569
|
+
service_uuid=app_config._service_uuid or "",
|
|
570
|
+
timestamp_start=ts0,
|
|
571
|
+
timestamp_end=ts1,
|
|
572
|
+
response_code=status_code,
|
|
573
|
+
success=success,
|
|
574
|
+
error=None if success else ((error or "")[:255]),
|
|
575
|
+
url=url or "",
|
|
576
|
+
method=method.upper(),
|
|
577
|
+
request_headers=req_headers_dict,
|
|
578
|
+
response_headers=resp_headers_dict,
|
|
579
|
+
request_body=req_body_str,
|
|
580
|
+
response_body=resp_body_str,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def is_ssl_socket_active() -> bool:
|
|
585
|
+
"""
|
|
586
|
+
Detect if ssl_socket.py SSL capture is active.
|
|
587
|
+
|
|
588
|
+
When SF_ENABLE_PYTHON_SSL_TEE=true, ssl_socket.py patches ssl.SSLSocket
|
|
589
|
+
to capture ALL SSL/HTTPS traffic at the socket layer.
|
|
590
|
+
|
|
591
|
+
Library patches should check this and SKIP capture for HTTPS URLs
|
|
592
|
+
when ssl_socket is active (to avoid double-capture), but still inject headers.
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
True if ssl_socket.py is handling SSL capture
|
|
596
|
+
False if library patches should handle HTTPS capture themselves
|
|
597
|
+
"""
|
|
598
|
+
return os.getenv("SF_ENABLE_PYTHON_SSL_TEE", "false").lower() == "true"
|
sf_veritas/patches/os.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from ..thread_local import get_context, set_context
|
|
4
|
+
|
|
5
|
+
_original_fork = os.fork
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def patched_fork():
|
|
9
|
+
current_context = get_context()
|
|
10
|
+
pid = _original_fork()
|
|
11
|
+
if pid == 0: # Child process
|
|
12
|
+
set_context(current_context)
|
|
13
|
+
return pid
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def patch_os():
|
|
17
|
+
os.fork = patched_fork
|