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.

Files changed (132) hide show
  1. sf_veritas/__init__.py +20 -0
  2. sf_veritas/_sffastlog.c +889 -0
  3. sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
  4. sf_veritas/_sffastnet.c +924 -0
  5. sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
  6. sf_veritas/_sffastnetworkrequest.c +730 -0
  7. sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
  8. sf_veritas/_sffuncspan.c +2155 -0
  9. sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
  10. sf_veritas/_sffuncspan_config.c +617 -0
  11. sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
  12. sf_veritas/_sfheadercheck.c +341 -0
  13. sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
  14. sf_veritas/_sfnetworkhop.c +1451 -0
  15. sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
  16. sf_veritas/_sfservice.c +1175 -0
  17. sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
  18. sf_veritas/_sfteepreload.c +5167 -0
  19. sf_veritas/app_config.py +49 -0
  20. sf_veritas/cli.py +336 -0
  21. sf_veritas/constants.py +10 -0
  22. sf_veritas/custom_excepthook.py +304 -0
  23. sf_veritas/custom_log_handler.py +129 -0
  24. sf_veritas/custom_output_wrapper.py +144 -0
  25. sf_veritas/custom_print.py +146 -0
  26. sf_veritas/django_app.py +5 -0
  27. sf_veritas/env_vars.py +186 -0
  28. sf_veritas/exception_handling_middleware.py +18 -0
  29. sf_veritas/exception_metaclass.py +69 -0
  30. sf_veritas/fast_frame_info.py +116 -0
  31. sf_veritas/fast_network_hop.py +293 -0
  32. sf_veritas/frame_tools.py +112 -0
  33. sf_veritas/funcspan_config_loader.py +556 -0
  34. sf_veritas/function_span_profiler.py +1174 -0
  35. sf_veritas/import_hook.py +62 -0
  36. sf_veritas/infra_details/__init__.py +3 -0
  37. sf_veritas/infra_details/get_infra_details.py +24 -0
  38. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  39. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  40. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  41. sf_veritas/infra_details/running_on/__init__.py +17 -0
  42. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  43. sf_veritas/interceptors.py +497 -0
  44. sf_veritas/libsfnettee.so +0 -0
  45. sf_veritas/local_env_detect.py +118 -0
  46. sf_veritas/package_metadata.py +6 -0
  47. sf_veritas/patches/__init__.py +0 -0
  48. sf_veritas/patches/concurrent_futures.py +19 -0
  49. sf_veritas/patches/constants.py +1 -0
  50. sf_veritas/patches/exceptions.py +82 -0
  51. sf_veritas/patches/multiprocessing.py +32 -0
  52. sf_veritas/patches/network_libraries/__init__.py +76 -0
  53. sf_veritas/patches/network_libraries/aiohttp.py +281 -0
  54. sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
  55. sf_veritas/patches/network_libraries/http_client.py +419 -0
  56. sf_veritas/patches/network_libraries/httpcore.py +515 -0
  57. sf_veritas/patches/network_libraries/httplib2.py +204 -0
  58. sf_veritas/patches/network_libraries/httpx.py +515 -0
  59. sf_veritas/patches/network_libraries/niquests.py +211 -0
  60. sf_veritas/patches/network_libraries/pycurl.py +385 -0
  61. sf_veritas/patches/network_libraries/requests.py +633 -0
  62. sf_veritas/patches/network_libraries/tornado.py +341 -0
  63. sf_veritas/patches/network_libraries/treq.py +270 -0
  64. sf_veritas/patches/network_libraries/urllib_request.py +468 -0
  65. sf_veritas/patches/network_libraries/utils.py +398 -0
  66. sf_veritas/patches/os.py +17 -0
  67. sf_veritas/patches/threading.py +218 -0
  68. sf_veritas/patches/web_frameworks/__init__.py +54 -0
  69. sf_veritas/patches/web_frameworks/aiohttp.py +793 -0
  70. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +317 -0
  71. sf_veritas/patches/web_frameworks/blacksheep.py +527 -0
  72. sf_veritas/patches/web_frameworks/bottle.py +502 -0
  73. sf_veritas/patches/web_frameworks/cherrypy.py +678 -0
  74. sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
  75. sf_veritas/patches/web_frameworks/django.py +944 -0
  76. sf_veritas/patches/web_frameworks/eve.py +395 -0
  77. sf_veritas/patches/web_frameworks/falcon.py +926 -0
  78. sf_veritas/patches/web_frameworks/fastapi.py +724 -0
  79. sf_veritas/patches/web_frameworks/flask.py +520 -0
  80. sf_veritas/patches/web_frameworks/klein.py +501 -0
  81. sf_veritas/patches/web_frameworks/litestar.py +551 -0
  82. sf_veritas/patches/web_frameworks/pyramid.py +428 -0
  83. sf_veritas/patches/web_frameworks/quart.py +824 -0
  84. sf_veritas/patches/web_frameworks/robyn.py +697 -0
  85. sf_veritas/patches/web_frameworks/sanic.py +857 -0
  86. sf_veritas/patches/web_frameworks/starlette.py +723 -0
  87. sf_veritas/patches/web_frameworks/strawberry.py +813 -0
  88. sf_veritas/patches/web_frameworks/tornado.py +481 -0
  89. sf_veritas/patches/web_frameworks/utils.py +91 -0
  90. sf_veritas/print_override.py +13 -0
  91. sf_veritas/regular_data_transmitter.py +409 -0
  92. sf_veritas/request_interceptor.py +401 -0
  93. sf_veritas/request_utils.py +550 -0
  94. sf_veritas/server_status.py +1 -0
  95. sf_veritas/shutdown_flag.py +11 -0
  96. sf_veritas/subprocess_startup.py +3 -0
  97. sf_veritas/test_cli.py +145 -0
  98. sf_veritas/thread_local.py +970 -0
  99. sf_veritas/timeutil.py +114 -0
  100. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  101. sf_veritas/transmitter.py +132 -0
  102. sf_veritas/types.py +47 -0
  103. sf_veritas/unified_interceptor.py +1580 -0
  104. sf_veritas/utils.py +39 -0
  105. sf_veritas-0.10.3.dist-info/METADATA +97 -0
  106. sf_veritas-0.10.3.dist-info/RECORD +132 -0
  107. sf_veritas-0.10.3.dist-info/WHEEL +5 -0
  108. sf_veritas-0.10.3.dist-info/entry_points.txt +2 -0
  109. sf_veritas-0.10.3.dist-info/top_level.txt +1 -0
  110. sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  111. sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  112. sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
  113. sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  114. sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  115. sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
  116. sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  117. sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  118. sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  119. sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  120. sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
  121. sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  122. sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  123. sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  124. sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  125. sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  126. sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  127. sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  128. sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
  129. sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
  130. sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
  131. sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  132. 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})")