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,595 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Monkey-patch the `requests` stack (requests → urllib3 → http.client):
|
|
3
|
+
|
|
4
|
+
• For every outbound request, propagate the SAILFISH_TRACING_HEADER + FUNCSPAN_OVERRIDE_HEADER
|
|
5
|
+
unless the destination host is in `domains_to_not_propagate_headers_to`.
|
|
6
|
+
• Fire NetworkRequestTransmitter via utils.record_network_request
|
|
7
|
+
so we always capture (url, status, timings, success, error).
|
|
8
|
+
• When LD_PRELOAD is active, ONLY inject headers (skip capture - socket layer handles it).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import http.client
|
|
14
|
+
import os
|
|
15
|
+
import time
|
|
16
|
+
from typing import Dict, List, Optional, Tuple
|
|
17
|
+
|
|
18
|
+
import requests
|
|
19
|
+
import urllib3
|
|
20
|
+
from requests.sessions import Session
|
|
21
|
+
|
|
22
|
+
import wrapt
|
|
23
|
+
|
|
24
|
+
# JSON serialization - try fast orjson first, fallback to stdlib json
|
|
25
|
+
try:
|
|
26
|
+
import orjson
|
|
27
|
+
|
|
28
|
+
HAS_ORJSON = True
|
|
29
|
+
except ImportError:
|
|
30
|
+
import json
|
|
31
|
+
|
|
32
|
+
HAS_ORJSON = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
from ...constants import FUNCSPAN_OVERRIDE_HEADER, SAILFISH_TRACING_HEADER
|
|
36
|
+
from ...thread_local import (
|
|
37
|
+
activate_reentrancy_guards_exception,
|
|
38
|
+
activate_reentrancy_guards_logging,
|
|
39
|
+
activate_reentrancy_guards_print,
|
|
40
|
+
get_funcspan_override,
|
|
41
|
+
)
|
|
42
|
+
from .utils import (
|
|
43
|
+
get_trace_and_should_propagate,
|
|
44
|
+
get_trace_and_should_propagate_fast,
|
|
45
|
+
init_fast_header_check,
|
|
46
|
+
inject_headers_ultrafast,
|
|
47
|
+
record_network_request,
|
|
48
|
+
is_ssl_socket_active,
|
|
49
|
+
has_sailfish_header,
|
|
50
|
+
)
|
|
51
|
+
from .._patch_tracker import is_already_patched, mark_as_patched
|
|
52
|
+
|
|
53
|
+
###############################################################################
|
|
54
|
+
# Internal helpers
|
|
55
|
+
###############################################################################
|
|
56
|
+
|
|
57
|
+
# header names used for re-entrancy guards
|
|
58
|
+
REENTRANCY_GUARD_LOGGING_PREACTIVE = "reentrancy_guard_logging_preactive"
|
|
59
|
+
REENTRANCY_GUARD_PRINT_PREACTIVE = "reentrancy_guard_print_preactive"
|
|
60
|
+
REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE = "reentrancy_guard_exception_preactive"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _tee_preload_active() -> bool:
|
|
64
|
+
"""Detect if LD_PRELOAD tee is active (same logic as http_client.py)."""
|
|
65
|
+
if os.getenv("SF_TEE_PRELOAD_ONLY", "0") == "1":
|
|
66
|
+
return True
|
|
67
|
+
ld = os.getenv("LD_PRELOAD", "")
|
|
68
|
+
return "libsfnettee.so" in ld or "_sfteepreload" in ld
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# PERFORMANCE: Reentrancy guards disabled - they add ~10-20μs per request
|
|
72
|
+
# The socket layer (LD_PRELOAD) handles everything we need
|
|
73
|
+
# def _activate_rg(headers: Dict[str, str]) -> None:
|
|
74
|
+
# """Turn the three 'preactive' guard flags ON for downstream hops."""
|
|
75
|
+
# headers[REENTRANCY_GUARD_LOGGING_PREACTIVE] = "true"
|
|
76
|
+
# headers[REENTRANCY_GUARD_PRINT_PREACTIVE] = "true"
|
|
77
|
+
# headers[REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE] = "true"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# def _check_rg(headers: Dict[str, str]) -> None:
|
|
81
|
+
# """If any pre-active guard present, switch the corresponding guard on."""
|
|
82
|
+
# if headers.get(REENTRANCY_GUARD_LOGGING_PREACTIVE, "false").lower() == "true":
|
|
83
|
+
# activate_reentrancy_guards_logging()
|
|
84
|
+
# if headers.get(REENTRANCY_GUARD_PRINT_PREACTIVE, "false").lower() == "true":
|
|
85
|
+
# activate_reentrancy_guards_print()
|
|
86
|
+
# if headers.get(REENTRANCY_GUARD_EXCEPTIONS_PREACTIVE, "false").lower() == "true":
|
|
87
|
+
# activate_reentrancy_guards_exception()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _prepare(
|
|
91
|
+
url: str,
|
|
92
|
+
domains_to_skip: List[str],
|
|
93
|
+
headers: Optional[Dict[str, str]],
|
|
94
|
+
) -> Tuple[str, Dict[str, str], int]:
|
|
95
|
+
"""
|
|
96
|
+
Inject trace header + funcspan override header (unless excluded) and return:
|
|
97
|
+
trace_id, merged_headers, timestamp_ms
|
|
98
|
+
|
|
99
|
+
ULTRA-FAST: <20ns overhead for header injection.
|
|
100
|
+
|
|
101
|
+
PERFORMANCE: Reentrancy guards removed - saves ~10-20μs per request.
|
|
102
|
+
"""
|
|
103
|
+
trace_id, propagate = get_trace_and_should_propagate(url, domains_to_skip)
|
|
104
|
+
hdrs: Dict[str, str] = dict(headers or {})
|
|
105
|
+
# _check_rg(hdrs) # DISABLED for performance
|
|
106
|
+
|
|
107
|
+
# Check if headers are already injected (avoid double-injection with httplib2/http_client)
|
|
108
|
+
# Fast O(1) check for common cases, fallback to O(n) for rare mixed-case variants
|
|
109
|
+
if propagate and not has_sailfish_header(hdrs):
|
|
110
|
+
hdrs[SAILFISH_TRACING_HEADER] = trace_id
|
|
111
|
+
|
|
112
|
+
# Inject funcspan override header if present (ContextVar lookup ~8ns)
|
|
113
|
+
try:
|
|
114
|
+
funcspan_override = get_funcspan_override()
|
|
115
|
+
if funcspan_override is not None:
|
|
116
|
+
hdrs[FUNCSPAN_OVERRIDE_HEADER] = funcspan_override
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
# _activate_rg(hdrs) # DISABLED for performance
|
|
121
|
+
return trace_id, hdrs, int(time.time() * 1_000)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _capture_and_record_requests(
|
|
125
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
126
|
+
):
|
|
127
|
+
"""Capture response data in background thread AFTER response is returned to user."""
|
|
128
|
+
resp_data: bytes = b""
|
|
129
|
+
req_headers: bytes = b""
|
|
130
|
+
resp_headers: bytes = b""
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
# Capture headers efficiently
|
|
134
|
+
if HAS_ORJSON:
|
|
135
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
136
|
+
if resp:
|
|
137
|
+
resp_headers = orjson.dumps({str(k): str(v) for k, v in resp.headers.items()})
|
|
138
|
+
else:
|
|
139
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
140
|
+
if resp:
|
|
141
|
+
resp_headers = json.dumps({str(k): str(v) for k, v in resp.headers.items()}).encode("utf-8")
|
|
142
|
+
|
|
143
|
+
# For response body: check if already cached in _content
|
|
144
|
+
if resp:
|
|
145
|
+
try:
|
|
146
|
+
if hasattr(resp, "_content") and resp._content is not None:
|
|
147
|
+
resp_data = resp._content
|
|
148
|
+
except Exception: # noqa: BLE001
|
|
149
|
+
pass
|
|
150
|
+
except Exception: # noqa: BLE001
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
# Send to C extension in background
|
|
154
|
+
record_network_request(
|
|
155
|
+
trace_id,
|
|
156
|
+
url,
|
|
157
|
+
method,
|
|
158
|
+
status,
|
|
159
|
+
success,
|
|
160
|
+
err,
|
|
161
|
+
timestamp_start=t0,
|
|
162
|
+
timestamp_end=t1,
|
|
163
|
+
request_data=req_data,
|
|
164
|
+
response_data=resp_data,
|
|
165
|
+
request_headers=req_headers,
|
|
166
|
+
response_headers=resp_headers,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _capture_and_record_urllib3(
|
|
171
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
172
|
+
):
|
|
173
|
+
"""Capture urllib3 response data in background thread AFTER response is returned to user."""
|
|
174
|
+
resp_data: bytes = b""
|
|
175
|
+
req_headers: bytes = b""
|
|
176
|
+
resp_headers: bytes = b""
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
# Capture headers efficiently
|
|
180
|
+
if HAS_ORJSON:
|
|
181
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
182
|
+
if resp:
|
|
183
|
+
resp_headers = orjson.dumps({str(k): str(v) for k, v in resp.headers.items()})
|
|
184
|
+
else:
|
|
185
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
186
|
+
if resp:
|
|
187
|
+
resp_headers = json.dumps({str(k): str(v) for k, v in resp.headers.items()}).encode("utf-8")
|
|
188
|
+
|
|
189
|
+
# For response body: check if already available in data attribute
|
|
190
|
+
if resp:
|
|
191
|
+
try:
|
|
192
|
+
resp_data = getattr(resp, "data", b"")
|
|
193
|
+
except Exception: # noqa: BLE001
|
|
194
|
+
pass
|
|
195
|
+
except Exception: # noqa: BLE001
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
# Send to C extension in background
|
|
199
|
+
record_network_request(
|
|
200
|
+
trace_id,
|
|
201
|
+
url,
|
|
202
|
+
method,
|
|
203
|
+
status,
|
|
204
|
+
success,
|
|
205
|
+
err,
|
|
206
|
+
timestamp_start=t0,
|
|
207
|
+
timestamp_end=t1,
|
|
208
|
+
request_data=req_data,
|
|
209
|
+
response_data=resp_data,
|
|
210
|
+
request_headers=req_headers,
|
|
211
|
+
response_headers=resp_headers,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _capture_and_record_http_client(
|
|
216
|
+
resp, trace_id, url, method, status, success, err, t0, t1, req_data, hdrs
|
|
217
|
+
):
|
|
218
|
+
"""Capture http.client response data in background thread AFTER response is returned to user."""
|
|
219
|
+
resp_data: bytes = b""
|
|
220
|
+
req_headers: bytes = b""
|
|
221
|
+
resp_headers: bytes = b""
|
|
222
|
+
|
|
223
|
+
try:
|
|
224
|
+
# Capture headers efficiently
|
|
225
|
+
if HAS_ORJSON:
|
|
226
|
+
req_headers = orjson.dumps({str(k): str(v) for k, v in hdrs.items()})
|
|
227
|
+
else:
|
|
228
|
+
req_headers = json.dumps({str(k): str(v) for k, v in hdrs.items()}).encode("utf-8")
|
|
229
|
+
|
|
230
|
+
# http.client doesn't easily expose response data, skip body capture
|
|
231
|
+
except Exception: # noqa: BLE001
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
# Send to C extension in background
|
|
235
|
+
record_network_request(
|
|
236
|
+
trace_id,
|
|
237
|
+
url,
|
|
238
|
+
method,
|
|
239
|
+
status,
|
|
240
|
+
success,
|
|
241
|
+
err,
|
|
242
|
+
timestamp_start=t0,
|
|
243
|
+
timestamp_end=t1,
|
|
244
|
+
request_data=req_data,
|
|
245
|
+
response_data=resp_data,
|
|
246
|
+
request_headers=req_headers,
|
|
247
|
+
response_headers=resp_headers,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
###############################################################################
|
|
252
|
+
# Top-level patch function
|
|
253
|
+
###############################################################################
|
|
254
|
+
def patch_requests(domains_to_not_propagate_headers_to: Optional[List[str]] = None):
|
|
255
|
+
"""
|
|
256
|
+
Apply all monkey-patches. Safe to call multiple times.
|
|
257
|
+
|
|
258
|
+
When LD_PRELOAD is active:
|
|
259
|
+
- ALWAYS inject headers (trace_id + funcspan_override)
|
|
260
|
+
- SKIP capture/emission (LD_PRELOAD handles at socket layer)
|
|
261
|
+
"""
|
|
262
|
+
# Idempotency guard: prevent double-patching (handles forks, reloading)
|
|
263
|
+
if is_already_patched("requests"):
|
|
264
|
+
return
|
|
265
|
+
mark_as_patched("requests")
|
|
266
|
+
|
|
267
|
+
exclude = domains_to_not_propagate_headers_to or []
|
|
268
|
+
preload_active = _tee_preload_active()
|
|
269
|
+
|
|
270
|
+
# PERFORMANCE DEBUG: Log preload detection
|
|
271
|
+
SF_DEBUG = os.getenv("SF_DEBUG", "false").lower() == "true"
|
|
272
|
+
if SF_DEBUG:
|
|
273
|
+
ld_preload = os.getenv("LD_PRELOAD", "")
|
|
274
|
+
print(f"[[patch_requests]] LD_PRELOAD={ld_preload}", flush=True)
|
|
275
|
+
print(f"[[patch_requests]] preload_active={preload_active}", flush=True)
|
|
276
|
+
print(
|
|
277
|
+
f"[[patch_requests]] Using {'ULTRA-FAST' if preload_active else 'FULL CAPTURE'} path",
|
|
278
|
+
flush=True,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Initialize C extension for ultra-fast header checking (if available)
|
|
282
|
+
if preload_active:
|
|
283
|
+
init_fast_header_check(exclude)
|
|
284
|
+
|
|
285
|
+
# --------------------------------------------------------------------- #
|
|
286
|
+
# 1. Patch `requests.Session.request`
|
|
287
|
+
# --------------------------------------------------------------------- #
|
|
288
|
+
# Save original function BEFORE any patching
|
|
289
|
+
original_request = Session.request
|
|
290
|
+
|
|
291
|
+
if preload_active:
|
|
292
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
293
|
+
# Use wrapt for minimal overhead (OTEL-style)
|
|
294
|
+
def instrumented_request(wrapped, instance, args, kwargs):
|
|
295
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
296
|
+
# args = (method, url, ...), kwargs = {...}
|
|
297
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
298
|
+
|
|
299
|
+
# OPTIMIZED: Avoid kwargs.pop() - use get() with or-pattern (10x faster!)
|
|
300
|
+
headers = kwargs.get("headers") or {}
|
|
301
|
+
|
|
302
|
+
# CRITICAL: Skip if already injected (prevents double injection in requests→urllib3→http.client chain)
|
|
303
|
+
# Fast O(1) check for common cases, fallback to O(n) for rare mixed-case variants
|
|
304
|
+
if not has_sailfish_header(headers):
|
|
305
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
306
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
307
|
+
|
|
308
|
+
kwargs["headers"] = headers
|
|
309
|
+
|
|
310
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
311
|
+
return wrapped(*args, **kwargs)
|
|
312
|
+
|
|
313
|
+
wrapt.wrap_function_wrapper(Session, "request", instrumented_request)
|
|
314
|
+
|
|
315
|
+
else:
|
|
316
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
317
|
+
# PERFORMANCE: Removed thread spawning - saves ~50-200μs per request
|
|
318
|
+
# Direct C extension call instead of background threads
|
|
319
|
+
def patched_request(self: Session, method, url, **kwargs): # type: ignore[override]
|
|
320
|
+
if SF_DEBUG:
|
|
321
|
+
print(f"[[patched_request]] INTERCEPTED: {method} {url}", flush=True)
|
|
322
|
+
|
|
323
|
+
# --- header handling / injection --------------------------------- #
|
|
324
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, kwargs.pop("headers", {}))
|
|
325
|
+
kwargs["headers"] = hdrs
|
|
326
|
+
|
|
327
|
+
# Skip capture for HTTPS when ssl_socket.py is active (avoids double-capture)
|
|
328
|
+
is_https = url.startswith("https://")
|
|
329
|
+
if is_https and is_ssl_socket_active():
|
|
330
|
+
# ssl_socket.py will handle capture, just make the request
|
|
331
|
+
return original_request(self, method, url, **kwargs)
|
|
332
|
+
|
|
333
|
+
status: int = 0
|
|
334
|
+
success: bool = False
|
|
335
|
+
err: str | None = None
|
|
336
|
+
req_data: bytes = b""
|
|
337
|
+
|
|
338
|
+
# PERFORMANCE: Skip request body capture - saves ~10-50μs
|
|
339
|
+
# C extension captures everything at socket layer
|
|
340
|
+
# try:
|
|
341
|
+
# if "json" in kwargs:
|
|
342
|
+
# try:
|
|
343
|
+
# import orjson
|
|
344
|
+
# req_data = orjson.dumps(kwargs["json"])
|
|
345
|
+
# except ImportError:
|
|
346
|
+
# import json
|
|
347
|
+
# req_data = json.dumps(kwargs["json"]).encode('utf-8')
|
|
348
|
+
# elif "data" in kwargs:
|
|
349
|
+
# data = kwargs["data"]
|
|
350
|
+
# if isinstance(data, bytes):
|
|
351
|
+
# req_data = data
|
|
352
|
+
# elif isinstance(data, str):
|
|
353
|
+
# req_data = data.encode('utf-8')
|
|
354
|
+
# except Exception: # noqa: BLE001
|
|
355
|
+
# pass
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
resp = original_request(self, method, url, **kwargs)
|
|
359
|
+
status = resp.status_code
|
|
360
|
+
success = resp.ok
|
|
361
|
+
t1 = int(time.time() * 1_000)
|
|
362
|
+
|
|
363
|
+
if SF_DEBUG:
|
|
364
|
+
print(f"[[patched_request]] Request completed: status={status}, success={success}, calling record_network_request...", flush=True)
|
|
365
|
+
|
|
366
|
+
# PERFORMANCE: Direct C call (NO thread spawning!) - saves ~50-200μs
|
|
367
|
+
record_network_request(
|
|
368
|
+
trace_id,
|
|
369
|
+
url,
|
|
370
|
+
str(method).upper(),
|
|
371
|
+
status,
|
|
372
|
+
success,
|
|
373
|
+
None,
|
|
374
|
+
timestamp_start=t0,
|
|
375
|
+
timestamp_end=t1,
|
|
376
|
+
request_data=req_data,
|
|
377
|
+
response_data=b"",
|
|
378
|
+
request_headers=b"",
|
|
379
|
+
response_headers=b"",
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
if SF_DEBUG:
|
|
383
|
+
print(f"[[patched_request]] ✓ record_network_request() completed successfully", flush=True)
|
|
384
|
+
|
|
385
|
+
return resp
|
|
386
|
+
except Exception as exc: # noqa: BLE001
|
|
387
|
+
err = str(exc)[:255]
|
|
388
|
+
t1 = int(time.time() * 1_000)
|
|
389
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
390
|
+
record_network_request(
|
|
391
|
+
trace_id,
|
|
392
|
+
url,
|
|
393
|
+
str(method).upper(),
|
|
394
|
+
status,
|
|
395
|
+
success,
|
|
396
|
+
err,
|
|
397
|
+
timestamp_start=t0,
|
|
398
|
+
timestamp_end=t1,
|
|
399
|
+
request_data=req_data,
|
|
400
|
+
response_data=b"",
|
|
401
|
+
request_headers=b"",
|
|
402
|
+
response_headers=b"",
|
|
403
|
+
)
|
|
404
|
+
raise
|
|
405
|
+
|
|
406
|
+
# CRITICAL: Always apply patch in full capture path, regardless of wrapt availability
|
|
407
|
+
Session.request = patched_request
|
|
408
|
+
requests.Session.request = (
|
|
409
|
+
patched_request # cover direct `requests.Session(...)`
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# --------------------------------------------------------------------- #
|
|
413
|
+
# 2. Patch urllib3's low-level ConnectionPool.urlopen (used by requests)
|
|
414
|
+
# --------------------------------------------------------------------- #
|
|
415
|
+
# Save original function BEFORE any patching
|
|
416
|
+
original_urlopen = urllib3.connectionpool.HTTPConnectionPool.urlopen
|
|
417
|
+
|
|
418
|
+
if preload_active:
|
|
419
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
420
|
+
# Use wrapt for minimal overhead (OTEL-style)
|
|
421
|
+
def instrumented_urlopen(wrapped, instance, args, kwargs):
|
|
422
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
423
|
+
# args = (method, url, ...), kwargs = {body, headers, ...}
|
|
424
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
425
|
+
|
|
426
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
427
|
+
headers = kwargs.get("headers") or {}
|
|
428
|
+
|
|
429
|
+
# CRITICAL: Skip if already injected by requests.Session (prevents double injection)
|
|
430
|
+
# Fast O(1) check for common cases, fallback to O(n) for rare mixed-case variants
|
|
431
|
+
if not has_sailfish_header(headers):
|
|
432
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
433
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
434
|
+
|
|
435
|
+
kwargs["headers"] = headers
|
|
436
|
+
|
|
437
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
438
|
+
return wrapped(*args, **kwargs)
|
|
439
|
+
|
|
440
|
+
wrapt.wrap_function_wrapper(
|
|
441
|
+
urllib3.connectionpool.HTTPConnectionPool,
|
|
442
|
+
"urlopen",
|
|
443
|
+
instrumented_urlopen,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
else:
|
|
447
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
448
|
+
# PERFORMANCE: Removed thread spawning and body capture
|
|
449
|
+
def patched_urlopen(self, method, url, body=None, headers=None, **kw): # type: ignore[override]
|
|
450
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, headers)
|
|
451
|
+
status: int = 0
|
|
452
|
+
success: bool = False
|
|
453
|
+
err: str | None = None
|
|
454
|
+
|
|
455
|
+
try:
|
|
456
|
+
resp = original_urlopen(
|
|
457
|
+
self, method, url, body=body, headers=hdrs, **kw
|
|
458
|
+
)
|
|
459
|
+
status = getattr(resp, "status", 0)
|
|
460
|
+
success = status < 400
|
|
461
|
+
t1 = int(time.time() * 1_000)
|
|
462
|
+
|
|
463
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
464
|
+
record_network_request(
|
|
465
|
+
trace_id,
|
|
466
|
+
url,
|
|
467
|
+
str(method).upper(),
|
|
468
|
+
status,
|
|
469
|
+
success,
|
|
470
|
+
None,
|
|
471
|
+
timestamp_start=t0,
|
|
472
|
+
timestamp_end=t1,
|
|
473
|
+
request_data=b"",
|
|
474
|
+
response_data=b"",
|
|
475
|
+
request_headers=b"",
|
|
476
|
+
response_headers=b"",
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return resp
|
|
480
|
+
except Exception as exc: # noqa: BLE001
|
|
481
|
+
err = str(exc)[:255]
|
|
482
|
+
t1 = int(time.time() * 1_000)
|
|
483
|
+
record_network_request(
|
|
484
|
+
trace_id,
|
|
485
|
+
url,
|
|
486
|
+
str(method).upper(),
|
|
487
|
+
status,
|
|
488
|
+
success,
|
|
489
|
+
err,
|
|
490
|
+
timestamp_start=t0,
|
|
491
|
+
timestamp_end=t1,
|
|
492
|
+
request_data=b"",
|
|
493
|
+
response_data=b"",
|
|
494
|
+
request_headers=b"",
|
|
495
|
+
response_headers=b"",
|
|
496
|
+
)
|
|
497
|
+
raise
|
|
498
|
+
|
|
499
|
+
# CRITICAL: Always apply patch in full capture path
|
|
500
|
+
urllib3.connectionpool.HTTPConnectionPool.urlopen = patched_urlopen
|
|
501
|
+
|
|
502
|
+
# --------------------------------------------------------------------- #
|
|
503
|
+
# 3. Patch http.client for "raw" stdlib usage (rare but easy to support)
|
|
504
|
+
# --------------------------------------------------------------------- #
|
|
505
|
+
# Save original function BEFORE any patching
|
|
506
|
+
original_http_client_request = http.client.HTTPConnection.request
|
|
507
|
+
|
|
508
|
+
if preload_active:
|
|
509
|
+
# ========== ULTRA-FAST PATH: When LD_PRELOAD is active ==========
|
|
510
|
+
# Use wrapt for minimal overhead (OTEL-style)
|
|
511
|
+
def instrumented_http_request(wrapped, instance, args, kwargs):
|
|
512
|
+
"""ULTRA-FAST header injection (<100ns) via wrapt."""
|
|
513
|
+
# args = (method, url, ...), kwargs = {body, headers, encode_chunked, ...}
|
|
514
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
515
|
+
|
|
516
|
+
# OPTIMIZED: Use or-pattern (10x faster!)
|
|
517
|
+
headers = kwargs.get("headers") or {}
|
|
518
|
+
|
|
519
|
+
# CRITICAL: Skip if already injected by requests/urllib3 (prevents triple injection)
|
|
520
|
+
# Fast O(1) check for common cases, fallback to O(n) for rare mixed-case variants
|
|
521
|
+
if not has_sailfish_header(headers):
|
|
522
|
+
# ULTRA-FAST: Thread-local cache + direct ContextVar.get() (<100ns!)
|
|
523
|
+
inject_headers_ultrafast(headers, url, exclude)
|
|
524
|
+
|
|
525
|
+
kwargs["headers"] = headers
|
|
526
|
+
|
|
527
|
+
# NO timing, NO capture, NO threads - immediate return!
|
|
528
|
+
return wrapped(*args, **kwargs)
|
|
529
|
+
|
|
530
|
+
wrapt.wrap_function_wrapper(
|
|
531
|
+
http.client.HTTPConnection, "request", instrumented_http_request
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
else:
|
|
535
|
+
# ========== FULL CAPTURE PATH: When LD_PRELOAD is NOT active ==========
|
|
536
|
+
# PERFORMANCE: Removed thread spawning and body capture
|
|
537
|
+
def patched_http_request(self, method, url, body=None, headers=None, *, encode_chunked=False): # type: ignore[override]
|
|
538
|
+
trace_id, hdrs, t0 = _prepare(url, exclude, headers)
|
|
539
|
+
status: int = 0
|
|
540
|
+
success: bool = False
|
|
541
|
+
err: str | None = None
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
resp = original_http_client_request(
|
|
545
|
+
self,
|
|
546
|
+
method,
|
|
547
|
+
url,
|
|
548
|
+
body=body,
|
|
549
|
+
headers=hdrs,
|
|
550
|
+
encode_chunked=encode_chunked,
|
|
551
|
+
)
|
|
552
|
+
status = getattr(
|
|
553
|
+
self, "response", getattr(resp, "status", 0)
|
|
554
|
+
) # best-effort
|
|
555
|
+
success = bool(status) and status < 400
|
|
556
|
+
t1 = int(time.time() * 1_000)
|
|
557
|
+
|
|
558
|
+
# PERFORMANCE: Direct C call (NO thread spawning!)
|
|
559
|
+
record_network_request(
|
|
560
|
+
trace_id,
|
|
561
|
+
url,
|
|
562
|
+
str(method).upper(),
|
|
563
|
+
status,
|
|
564
|
+
success,
|
|
565
|
+
None,
|
|
566
|
+
timestamp_start=t0,
|
|
567
|
+
timestamp_end=t1,
|
|
568
|
+
request_data=b"",
|
|
569
|
+
response_data=b"",
|
|
570
|
+
request_headers=b"",
|
|
571
|
+
response_headers=b"",
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
return resp
|
|
575
|
+
except Exception as exc: # noqa: BLE001
|
|
576
|
+
err = str(exc)[:255]
|
|
577
|
+
t1 = int(time.time() * 1_000)
|
|
578
|
+
record_network_request(
|
|
579
|
+
trace_id,
|
|
580
|
+
url,
|
|
581
|
+
str(method).upper(),
|
|
582
|
+
status,
|
|
583
|
+
success,
|
|
584
|
+
err,
|
|
585
|
+
timestamp_start=t0,
|
|
586
|
+
timestamp_end=t1,
|
|
587
|
+
request_data=b"",
|
|
588
|
+
response_data=b"",
|
|
589
|
+
request_headers=b"",
|
|
590
|
+
response_headers=b"",
|
|
591
|
+
)
|
|
592
|
+
raise
|
|
593
|
+
|
|
594
|
+
# CRITICAL: Always apply patch in full capture path
|
|
595
|
+
http.client.HTTPConnection.request = patched_http_request
|