sf-veritas 0.10.3__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.
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-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnet.c +924 -0
- sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffastnetworkrequest.c +730 -0
- sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan.c +2155 -0
- sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sffuncspan_config.c +617 -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 +1451 -0
- sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
- sf_veritas/_sfservice.c +1175 -0
- sf_veritas/_sfservice.cpython-314-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,419 @@
|
|
|
1
|
+
# sf_veritas/patches/network_libraries/http_client.py
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from typing import List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import wrapt
|
|
8
|
+
|
|
9
|
+
HAS_WRAPT = True
|
|
10
|
+
except ImportError:
|
|
11
|
+
HAS_WRAPT = False
|
|
12
|
+
|
|
13
|
+
from ... import _sffastnetworkrequest as _fast # native module
|
|
14
|
+
from ...env_vars import SF_DEBUG
|
|
15
|
+
from ...thread_local import is_network_recording_suppressed, trace_id_ctx
|
|
16
|
+
from .utils import init_fast_header_check, inject_headers_ultrafast
|
|
17
|
+
|
|
18
|
+
# JSON serialization - try fast orjson first, fallback to stdlib json
|
|
19
|
+
try:
|
|
20
|
+
import orjson
|
|
21
|
+
|
|
22
|
+
HAS_ORJSON = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
import json
|
|
25
|
+
|
|
26
|
+
HAS_ORJSON = False
|
|
27
|
+
# --- Native fast path (C) readiness probe -------------------------
|
|
28
|
+
_FAST = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fast_ready() -> bool:
|
|
32
|
+
global _FAST
|
|
33
|
+
if _FAST is None:
|
|
34
|
+
try:
|
|
35
|
+
_FAST = _fast
|
|
36
|
+
|
|
37
|
+
if SF_DEBUG:
|
|
38
|
+
try:
|
|
39
|
+
print(
|
|
40
|
+
"[http_client] _sffastnetworkrequest loaded successfully",
|
|
41
|
+
log=False,
|
|
42
|
+
)
|
|
43
|
+
except TypeError:
|
|
44
|
+
print("[http_client] _sffastnetworkrequest loaded successfully")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
_FAST = False
|
|
47
|
+
|
|
48
|
+
if SF_DEBUG:
|
|
49
|
+
try:
|
|
50
|
+
print(
|
|
51
|
+
f"[http_client] _sffastnetworkrequest NOT available: {e}",
|
|
52
|
+
log=False,
|
|
53
|
+
)
|
|
54
|
+
except TypeError:
|
|
55
|
+
print(f"[http_client] _sffastnetworkrequest NOT available: {e}")
|
|
56
|
+
if _FAST is False:
|
|
57
|
+
return False
|
|
58
|
+
try:
|
|
59
|
+
ready = bool(_FAST.is_ready())
|
|
60
|
+
|
|
61
|
+
if SF_DEBUG:
|
|
62
|
+
try:
|
|
63
|
+
print(
|
|
64
|
+
f"[http_client] _sffastnetworkrequest.is_ready() = {ready}",
|
|
65
|
+
log=False,
|
|
66
|
+
)
|
|
67
|
+
except TypeError:
|
|
68
|
+
print(f"[http_client] _sffastnetworkrequest.is_ready() = {ready}")
|
|
69
|
+
return ready
|
|
70
|
+
except Exception as e:
|
|
71
|
+
if SF_DEBUG:
|
|
72
|
+
try:
|
|
73
|
+
print(f"[http_client] is_ready() check failed: {e}", log=False)
|
|
74
|
+
except TypeError:
|
|
75
|
+
print(f"[http_client] is_ready() check failed: {e}")
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _split_headers_and_body_from_send_chunk(
|
|
80
|
+
chunk: memoryview, state
|
|
81
|
+
) -> Tuple[Optional[bytes], Optional[bytes]]:
|
|
82
|
+
if state["seen_hdr_end"]:
|
|
83
|
+
return None, bytes(chunk)
|
|
84
|
+
|
|
85
|
+
mv = chunk
|
|
86
|
+
pos = mv.tobytes().find(b"\r\n\r\n")
|
|
87
|
+
if pos == -1:
|
|
88
|
+
state["hdr_buf"].append(bytes(mv))
|
|
89
|
+
return None, None
|
|
90
|
+
|
|
91
|
+
hdr_part = bytes(mv[: pos + 4])
|
|
92
|
+
body_part = bytes(mv[pos + 4 :])
|
|
93
|
+
state["hdr_buf"].append(hdr_part)
|
|
94
|
+
state["seen_hdr_end"] = True
|
|
95
|
+
return b"".join(state["hdr_buf"]), body_part if body_part else None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _parse_request_headers_from_block(block: bytes) -> dict:
|
|
99
|
+
headers = {}
|
|
100
|
+
lines = block.split(b"\r\n")
|
|
101
|
+
for raw in lines[1:]:
|
|
102
|
+
if not raw:
|
|
103
|
+
break
|
|
104
|
+
i = raw.find(b":")
|
|
105
|
+
if i <= 0:
|
|
106
|
+
continue
|
|
107
|
+
k = raw[:i].decode("latin1", "replace").strip()
|
|
108
|
+
v = raw[i + 1 :].decode("latin1", "replace").strip()
|
|
109
|
+
headers[k] = v
|
|
110
|
+
return headers
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _tee_preload_active() -> bool:
|
|
114
|
+
"""Detect if the LD_PRELOAD tee is active; if so, skip Python-level patch."""
|
|
115
|
+
if os.getenv("SF_TEE_PRELOAD_ONLY", "0") == "1":
|
|
116
|
+
return True
|
|
117
|
+
ld = os.getenv("LD_PRELOAD", "")
|
|
118
|
+
# match our shipped name
|
|
119
|
+
return "libsfnettee.so" in ld or "_sfteepreload" in ld
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def patch_http_client(domains_to_not_propagate_headers_to: Optional[List[str]] = None):
|
|
123
|
+
"""
|
|
124
|
+
ALWAYS patch for header injection (trace_id + funcspan_override).
|
|
125
|
+
Skip capture/emission if LD_PRELOAD tee is active (socket layer already captures).
|
|
126
|
+
|
|
127
|
+
This ensures headers propagate correctly regardless of capture mechanism.
|
|
128
|
+
"""
|
|
129
|
+
preload_active = _tee_preload_active()
|
|
130
|
+
|
|
131
|
+
# Initialize C extension for ultra-fast header checking (if available)
|
|
132
|
+
if preload_active:
|
|
133
|
+
init_fast_header_check(domains_to_not_propagate_headers_to or [])
|
|
134
|
+
if SF_DEBUG:
|
|
135
|
+
try:
|
|
136
|
+
print(
|
|
137
|
+
"[http_client] LD_PRELOAD tee active; patching for headers only (no capture)",
|
|
138
|
+
log=False,
|
|
139
|
+
)
|
|
140
|
+
except TypeError:
|
|
141
|
+
print(
|
|
142
|
+
"[http_client] LD_PRELOAD tee active; patching for headers only (no capture)"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Check if C extension is available for capture (not required for header injection)
|
|
146
|
+
fast_available = False
|
|
147
|
+
if not preload_active:
|
|
148
|
+
fast_available = _fast_ready()
|
|
149
|
+
if not fast_available and SF_DEBUG:
|
|
150
|
+
try:
|
|
151
|
+
print(
|
|
152
|
+
"[http_client] C extension not ready - will patch for headers only (no capture)",
|
|
153
|
+
log=False,
|
|
154
|
+
)
|
|
155
|
+
except TypeError:
|
|
156
|
+
print(
|
|
157
|
+
"[http_client] C extension not ready - will patch for headers only (no capture)"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
_fast = _FAST if (not preload_active and fast_available) else None # type: ignore[assignment]
|
|
161
|
+
if domains_to_not_propagate_headers_to is None:
|
|
162
|
+
domains_to_not_propagate_headers_to = []
|
|
163
|
+
|
|
164
|
+
if SF_DEBUG:
|
|
165
|
+
mode = "headers only" if preload_active else "full capture"
|
|
166
|
+
try:
|
|
167
|
+
print(
|
|
168
|
+
f"[http_client] Patching http.client ({mode})",
|
|
169
|
+
log=False,
|
|
170
|
+
)
|
|
171
|
+
except TypeError:
|
|
172
|
+
print(f"[http_client] Patching http.client ({mode})")
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
import http.client as _hc
|
|
176
|
+
except ImportError:
|
|
177
|
+
if SF_DEBUG:
|
|
178
|
+
try:
|
|
179
|
+
print("[http_client] http.client not available to patch", log=False)
|
|
180
|
+
except TypeError:
|
|
181
|
+
print("[http_client] http.client not available to patch")
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
# Body size limits (only needed if NOT using preload)
|
|
185
|
+
if not preload_active:
|
|
186
|
+
try:
|
|
187
|
+
SFF_MAX_REQ_BODY = getattr(_fast, "SFF_MAX_REQ_BODY", 8192)
|
|
188
|
+
SFF_MAX_RESP_BODY = getattr(_fast, "SFF_MAX_RESP_BODY", 8192)
|
|
189
|
+
except Exception:
|
|
190
|
+
SFF_MAX_REQ_BODY = 8192
|
|
191
|
+
SFF_MAX_RESP_BODY = 8192
|
|
192
|
+
else:
|
|
193
|
+
SFF_MAX_REQ_BODY = 0
|
|
194
|
+
SFF_MAX_RESP_BODY = 0
|
|
195
|
+
|
|
196
|
+
original_request = _hc.HTTPConnection.request
|
|
197
|
+
original_send = _hc.HTTPConnection.send
|
|
198
|
+
original_getresponse = _hc.HTTPConnection.getresponse
|
|
199
|
+
|
|
200
|
+
def patched_request(
|
|
201
|
+
self, method, url, body=None, headers=None, *, encode_chunked=False
|
|
202
|
+
):
|
|
203
|
+
# Build full URL for domain checking (http.client uses relative paths)
|
|
204
|
+
full_url = url
|
|
205
|
+
if not url.startswith(("http://", "https://")):
|
|
206
|
+
# Relative path - build full URL from connection
|
|
207
|
+
scheme = "https" if isinstance(self, _hc.HTTPSConnection) else "http"
|
|
208
|
+
full_url = (
|
|
209
|
+
f"{scheme}://{self.host}:{self.port}{url}"
|
|
210
|
+
if self.port not in (80, 443)
|
|
211
|
+
else f"{scheme}://{self.host}{url}"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# ULTRA-FAST header injection using inject_headers_ultrafast() (~100ns)
|
|
215
|
+
# Create dict for injection, then check if we actually added anything
|
|
216
|
+
hdrs_dict = dict(headers) if headers else {}
|
|
217
|
+
original_keys = set(hdrs_dict.keys())
|
|
218
|
+
inject_headers_ultrafast(
|
|
219
|
+
hdrs_dict, full_url, domains_to_not_propagate_headers_to
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Only use dict if we added headers OR original had headers (preserve None if nothing to add)
|
|
223
|
+
if headers or set(hdrs_dict.keys()) != original_keys:
|
|
224
|
+
hdrs_out = hdrs_dict
|
|
225
|
+
else:
|
|
226
|
+
hdrs_out = None # Preserve None if no headers were originally provided and none were injected
|
|
227
|
+
|
|
228
|
+
# Only capture state if NOT using LD_PRELOAD (preload captures at socket layer)
|
|
229
|
+
if not preload_active:
|
|
230
|
+
# Get trace_id for capture (already injected in headers)
|
|
231
|
+
trace_id = trace_id_ctx.get(None) or ""
|
|
232
|
+
|
|
233
|
+
start_ts = int(time.time() * 1_000)
|
|
234
|
+
# Store state as list [start_ts, trace_id, url, method, req_hdr_buf, req_body_buf, seen_end]
|
|
235
|
+
# Lists are mutable so patched_send can append to buffers
|
|
236
|
+
self._sf_req_capture = [
|
|
237
|
+
start_ts,
|
|
238
|
+
trace_id,
|
|
239
|
+
url,
|
|
240
|
+
method,
|
|
241
|
+
bytearray(),
|
|
242
|
+
bytearray(),
|
|
243
|
+
False,
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
return original_request(
|
|
247
|
+
self,
|
|
248
|
+
method,
|
|
249
|
+
url,
|
|
250
|
+
body=body,
|
|
251
|
+
headers=hdrs_out,
|
|
252
|
+
encode_chunked=encode_chunked,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def patched_send(self, data):
|
|
256
|
+
state = getattr(self, "_sf_req_capture", None)
|
|
257
|
+
if state is not None:
|
|
258
|
+
# FAST: Capture headers and body without parsing
|
|
259
|
+
# state = [start_ts, trace_id, url, method, req_hdr_buf, req_body_buf, seen_end]
|
|
260
|
+
hdr_buf, body_buf, seen_end = state[4], state[5], state[6]
|
|
261
|
+
|
|
262
|
+
if not seen_end:
|
|
263
|
+
# Look for \r\n\r\n to split headers from body
|
|
264
|
+
pos = data.find(b"\r\n\r\n")
|
|
265
|
+
if pos >= 0:
|
|
266
|
+
hdr_buf.extend(data[: pos + 4])
|
|
267
|
+
if len(data) > pos + 4:
|
|
268
|
+
cap = SFF_MAX_REQ_BODY - len(body_buf)
|
|
269
|
+
if cap > 0:
|
|
270
|
+
body_buf.extend(data[pos + 4 : pos + 4 + cap])
|
|
271
|
+
state[6] = True # Mark seen_end
|
|
272
|
+
else:
|
|
273
|
+
hdr_buf.extend(data)
|
|
274
|
+
else:
|
|
275
|
+
# Already saw headers, just capture body
|
|
276
|
+
cap = SFF_MAX_REQ_BODY - len(body_buf)
|
|
277
|
+
if cap > 0:
|
|
278
|
+
body_buf.extend(data[:cap])
|
|
279
|
+
|
|
280
|
+
return original_send(self, data)
|
|
281
|
+
|
|
282
|
+
def patched_getresponse(self):
|
|
283
|
+
response = original_getresponse(self)
|
|
284
|
+
|
|
285
|
+
state = getattr(self, "_sf_req_capture", None)
|
|
286
|
+
if not state:
|
|
287
|
+
return response
|
|
288
|
+
|
|
289
|
+
# Check if network recording is suppressed (e.g., by @skip_network_tracing decorator)
|
|
290
|
+
if is_network_recording_suppressed():
|
|
291
|
+
delattr(self, "_sf_req_capture")
|
|
292
|
+
return response
|
|
293
|
+
|
|
294
|
+
# ULTRA-FAST: Extract captured data, call C extension, return immediately
|
|
295
|
+
try:
|
|
296
|
+
# state = [start_ts, trace_id, url, method, req_hdr_buf, req_body_buf, seen_end]
|
|
297
|
+
start_ts, trace_id, url, method, req_hdr_buf, req_body_buf, _ = state
|
|
298
|
+
delattr(self, "_sf_req_capture")
|
|
299
|
+
|
|
300
|
+
status = int(getattr(response, "status", 0))
|
|
301
|
+
ok = 1 if status < 400 else 0
|
|
302
|
+
end_ts = int(time.time() * 1_000)
|
|
303
|
+
|
|
304
|
+
# FAST: Parse request headers from buffer
|
|
305
|
+
req_headers_json = "{}"
|
|
306
|
+
hdr_dict = {}
|
|
307
|
+
if req_hdr_buf:
|
|
308
|
+
try:
|
|
309
|
+
hdr_dict = _parse_request_headers_from_block(bytes(req_hdr_buf))
|
|
310
|
+
except Exception:
|
|
311
|
+
pass
|
|
312
|
+
|
|
313
|
+
if HAS_ORJSON:
|
|
314
|
+
req_headers_json = orjson.dumps(hdr_dict).decode("utf-8")
|
|
315
|
+
else:
|
|
316
|
+
req_headers_json = json.dumps(hdr_dict).decode("utf-8")
|
|
317
|
+
|
|
318
|
+
# FAST: Get response headers
|
|
319
|
+
resp_headers_json = "{}"
|
|
320
|
+
if HAS_ORJSON:
|
|
321
|
+
resp_headers_json = orjson.dumps(
|
|
322
|
+
{str(k): str(v) for k, v in response.getheaders()}
|
|
323
|
+
).decode("utf-8")
|
|
324
|
+
else:
|
|
325
|
+
resp_headers_json = json.dumps(
|
|
326
|
+
{str(k): str(v) for k, v in response.getheaders()}
|
|
327
|
+
).decode("utf-8")
|
|
328
|
+
|
|
329
|
+
# FAST: Peek response body (non-blocking)
|
|
330
|
+
resp_body = b""
|
|
331
|
+
try:
|
|
332
|
+
if (
|
|
333
|
+
SFF_MAX_RESP_BODY > 0
|
|
334
|
+
and hasattr(response, "fp")
|
|
335
|
+
and hasattr(response.fp, "peek")
|
|
336
|
+
):
|
|
337
|
+
resp_body = bytes(
|
|
338
|
+
response.fp.peek(SFF_MAX_RESP_BODY)[:SFF_MAX_RESP_BODY]
|
|
339
|
+
)
|
|
340
|
+
except Exception:
|
|
341
|
+
pass
|
|
342
|
+
|
|
343
|
+
# Call C extension (releases GIL internally for JSON building)
|
|
344
|
+
_fast.networkhop_async(
|
|
345
|
+
trace_id=trace_id,
|
|
346
|
+
url=url,
|
|
347
|
+
method=method,
|
|
348
|
+
status=status,
|
|
349
|
+
ok=ok,
|
|
350
|
+
timestamp_start=start_ts,
|
|
351
|
+
timestamp_end=end_ts,
|
|
352
|
+
request_body=bytes(req_body_buf) if req_body_buf else b"",
|
|
353
|
+
response_body=resp_body,
|
|
354
|
+
request_headers_json=req_headers_json,
|
|
355
|
+
response_headers_json=resp_headers_json,
|
|
356
|
+
)
|
|
357
|
+
except Exception:
|
|
358
|
+
pass
|
|
359
|
+
|
|
360
|
+
return response
|
|
361
|
+
|
|
362
|
+
# ALWAYS patch request() for header injection (even with LD_PRELOAD or no C extension)
|
|
363
|
+
if HAS_WRAPT:
|
|
364
|
+
|
|
365
|
+
def instrumented_request(wrapped, instance, args, kwargs):
|
|
366
|
+
"""Ultra-fast header injection using wrapt."""
|
|
367
|
+
method = args[0] if len(args) > 0 else kwargs.get("method", "GET")
|
|
368
|
+
url = args[1] if len(args) > 1 else kwargs.get("url", "")
|
|
369
|
+
body = args[2] if len(args) > 2 else kwargs.get("body", None)
|
|
370
|
+
headers = args[3] if len(args) > 3 else kwargs.get("headers", None)
|
|
371
|
+
encode_chunked = kwargs.get("encode_chunked", False)
|
|
372
|
+
return patched_request(
|
|
373
|
+
instance, method, url, body, headers, encode_chunked=encode_chunked
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
wrapt.wrap_function_wrapper(_hc.HTTPConnection, "request", instrumented_request)
|
|
377
|
+
else:
|
|
378
|
+
_hc.HTTPConnection.request = patched_request
|
|
379
|
+
|
|
380
|
+
# ONLY patch send/getresponse if NOT using LD_PRELOAD AND C extension is available (for capture/emission)
|
|
381
|
+
if not preload_active and fast_available:
|
|
382
|
+
if HAS_WRAPT:
|
|
383
|
+
|
|
384
|
+
def instrumented_send(wrapped, instance, args, kwargs):
|
|
385
|
+
"""Ultra-fast send wrapper using wrapt."""
|
|
386
|
+
data = args[0] if len(args) > 0 else kwargs.get("data", b"")
|
|
387
|
+
return patched_send(instance, data)
|
|
388
|
+
|
|
389
|
+
def instrumented_getresponse(wrapped, instance, args, kwargs):
|
|
390
|
+
"""Ultra-fast getresponse wrapper using wrapt."""
|
|
391
|
+
return patched_getresponse(instance)
|
|
392
|
+
|
|
393
|
+
wrapt.wrap_function_wrapper(_hc.HTTPConnection, "send", instrumented_send)
|
|
394
|
+
wrapt.wrap_function_wrapper(
|
|
395
|
+
_hc.HTTPConnection, "getresponse", instrumented_getresponse
|
|
396
|
+
)
|
|
397
|
+
else:
|
|
398
|
+
_hc.HTTPConnection.send = patched_send
|
|
399
|
+
_hc.HTTPConnection.getresponse = patched_getresponse
|
|
400
|
+
|
|
401
|
+
if SF_DEBUG:
|
|
402
|
+
try:
|
|
403
|
+
print("[http_client] Patched send/getresponse for capture", log=False)
|
|
404
|
+
except TypeError:
|
|
405
|
+
print("[http_client] Patched send/getresponse for capture")
|
|
406
|
+
else:
|
|
407
|
+
reason = (
|
|
408
|
+
"LD_PRELOAD handles capture"
|
|
409
|
+
if preload_active
|
|
410
|
+
else "C extension not available"
|
|
411
|
+
)
|
|
412
|
+
if SF_DEBUG:
|
|
413
|
+
try:
|
|
414
|
+
print(
|
|
415
|
+
f"[http_client] Skipped send/getresponse patches ({reason})",
|
|
416
|
+
log=False,
|
|
417
|
+
)
|
|
418
|
+
except TypeError:
|
|
419
|
+
print(f"[http_client] Skipped send/getresponse patches ({reason})")
|