Skip to content

refactor: native HTTP client#18792

Open
P403n1x87 wants to merge 19 commits into
mainfrom
chore/use-native-http-client
Open

refactor: native HTTP client#18792
P403n1x87 wants to merge 19 commits into
mainfrom
chore/use-native-http-client

Conversation

@P403n1x87

Copy link
Copy Markdown
Contributor

Description

We refactor the HTTP connection classes to use the new native HTTP connection. This way we can avoid the use of the http.client from the standard library, increasing isolation from the runtime.

@P403n1x87 P403n1x87 added the changelog/no-changelog A changelog entry is not required for this PR. label Jun 29, 2026
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codeowners resolved as

ddtrace/aiguard/_api_client.py                                          @DataDog/asm-python
ddtrace/internal/http.py                                                @DataDog/apm-core-python
ddtrace/llmobs/_prompts/manager.py                                      @DataDog/ml-observability
tests/aiguard/api/test_api_client.py                                    @DataDog/asm-python
tests/llmobs/test_prompts.py                                            @DataDog/ml-observability

We refactor the HTTP connection classes to use the new native HTTP
connection. This way we can avoid the use of the http.client from the
standard library, increasing isolation from the runtime.
@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch from 8e0dfbb to 044993d Compare June 29, 2026 16:37
@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

🚦 19 Pipeline jobs failed

System Tests | tracer-release / End-to-end #2 / uds-flask 2   View in Datadog   GitHub Actions

🧪 2 Tests failed

tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag.test_ai_guard_event[uds-flask] from system_tests_suite   View in Datadog
assert 500 == 200
 +  where 500 = HttpResponse(status_code:500, headers:{'Server': 'gunicorn', 'Date': 'Fri, 17 Jul 2026 11:11:03 GMT', 'Connection': 'k...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n).status_code
 +    where HttpResponse(status_code:500, headers:{'Server': 'gunicorn', 'Date': 'Fri, 17 Jul 2026 11:11:03 GMT', 'Connection': 'k...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n) = <tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag object at 0x7ffbced3a9c0>.r

self = <tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag object at 0x7ffbced3a9c0>

    def test_ai_guard_event(self):
        """Test AI Guard sets ai_guard.event:true tag in the local root span of the trace."""
>       assert self.r.status_code == 200
E       assert 500 == 200
...
tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities.test_tag_probabilities[uds-flask] from system_tests_suite   View in Datadog
assert 500 == 200
 +  where 500 = HttpResponse(status_code:500, headers:{'Server': 'gunicorn', 'Date': 'Fri, 17 Jul 2026 11:11:03 GMT', 'Connection': 'k...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n).status_code
 +    where HttpResponse(status_code:500, headers:{'Server': 'gunicorn', 'Date': 'Fri, 17 Jul 2026 11:11:03 GMT', 'Connection': 'k...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n) = <tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities object at 0x7ffbced3a1e0>.r

self = <tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities object at 0x7ffbced3a1e0>

    def test_tag_probabilities(self):
        """Test AI Guard returns and stores tag probabilities.
        Verifies the SDK response exposes tag probabilities and the ai_guard meta struct keeps the
        same probability map received from the AI Guard REST API.
...

System Tests | tracer-release / End-to-end #2 / uwsgi-poc 2   View in Datadog   GitHub Actions

🧪 2 Tests failed

tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag.test_ai_guard_event[uwsgi-poc] from system_tests_suite   View in Datadog
assert 500 == 200
 +  where 500 = HttpResponse(status_code:500, headers:{'Content-Type': 'application/json', 'Content-Length': '162'}, text:{"error":"Un...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n).status_code
 +    where HttpResponse(status_code:500, headers:{'Content-Type': 'application/json', 'Content-Length': '162'}, text:{"error":"Un...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n) = <tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag object at 0x7f9564eb91c0>.r

self = <tests.ai_guard.test_ai_guard_sdk.Test_AIGuardEvent_Tag object at 0x7f9564eb91c0>

    def test_ai_guard_event(self):
        """Test AI Guard sets ai_guard.event:true tag in the local root span of the trace."""
>       assert self.r.status_code == 200
E       assert 500 == 200
...
tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities.test_tag_probabilities[uwsgi-poc] from system_tests_suite   View in Datadog
assert 500 == 200
 +  where 500 = HttpResponse(status_code:500, headers:{'Content-Type': 'application/json', 'Content-Length': '162'}, text:{"error":"Un...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n).status_code
 +    where HttpResponse(status_code:500, headers:{'Content-Type': 'application/json', 'Content-Length': '162'}, text:{"error":"Un...service: error sending request for url (http://vcr_cassettes:8300/vcr/aiguard/evaluate)","type":"AIGuardClientError"}\n) = <tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities object at 0x7f9564eb8ad0>.r

self = <tests.ai_guard.test_ai_guard_sdk.Test_Tag_Probabilities object at 0x7f9564eb8ad0>

    def test_tag_probabilities(self):
        """Test AI Guard returns and stores tag probabilities.
        Verifies the SDK response exposes tag probabilities and the ai_guard meta struct keeps the
        same probability map received from the AI Guard REST API.
...

System Tests | tracer-release / End-to-end #8 / uwsgi-poc 8   View in Datadog   GitHub Actions

🧪 2 Tests failed

tests.test_telemetry.Test_Metric_Generation_Enabled.test_metric_tracers_spans_created[uwsgi-poc] from system_tests_suite   View in Datadog
AssertionError: No telemetry data received for metric tracers.spans_created
assert (0 != 0 or 1 == 0)
 +  where 0 = len([])

self = <tests.test_telemetry.Test_Metric_Generation_Enabled object at 0x7fee3c4178c0>

    def test_metric_tracers_spans_created(self):
>       self.assert_count_metric("tracers", "spans_created", expect_at_least=1)

tests/test_telemetry.py:1036: 
...
tests.test_telemetry.Test_Metric_Generation_Enabled.test_metric_tracers_spans_finished[uwsgi-poc] from system_tests_suite   View in Datadog
AssertionError: No telemetry data received for metric tracers.spans_finished
assert (0 != 0 or 1 == 0)
 +  where 0 = len([])

self = <tests.test_telemetry.Test_Metric_Generation_Enabled object at 0x7fee3c241160>

    def test_metric_tracers_spans_finished(self):
>       self.assert_count_metric("tracers", "spans_finished", expect_at_least=1)

tests/test_telemetry.py:1039: 
...

View all 19 failed jobs.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 75cf8e7 | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Jun 29, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-17 11:20:17

Comparing candidate commit 75cf8e7 in PR branch chore/use-native-http-client with baseline commit 608332b in branch main.

Found 0 performance improvements and 5 performance regressions! Performance is the same for 615 metrics, 11 unstable metrics.

scenario:iastaspects-swapcase_aspect

  • 🟥 execution_time [+35.493µs; +43.248µs] or [+10.316%; +12.569%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+97.420µs; +103.489µs] or [+22.833%; +24.255%]

scenario:span-start

  • 🟥 execution_time [+1.696ms; +1.896ms] or [+10.876%; +12.160%]

scenario:telemetryaddmetric-1-count-metric-1-times

  • 🟥 execution_time [+339.807ns; +378.933ns] or [+16.051%; +17.900%]

scenario:tracer-small

  • 🟥 execution_time [+25.725µs; +28.307µs] or [+7.228%; +7.954%]

@P403n1x87
P403n1x87 marked this pull request as ready for review June 30, 2026 10:57
@P403n1x87
P403n1x87 requested review from a team as code owners June 30, 2026 10:57
@P403n1x87
P403n1x87 requested a review from rachelyangdog June 30, 2026 10:57

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e3da075fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ddtrace/internal/http.py Outdated
Comment thread ddtrace/internal/http.py Outdated
@P403n1x87
P403n1x87 requested a review from a team as a code owner June 30, 2026 14:19
@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch from ecaa757 to c3c115b Compare June 30, 2026 14:32

@yannham yannham left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as far as I can tell, although my Python fu is limited - I came to review the Rust part but it's rather thin 😅

@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch from c3c115b to fd75c45 Compare June 30, 2026 18:48
@P403n1x87
P403n1x87 requested a review from a team as a code owner June 30, 2026 18:48
@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch 3 times, most recently from 1be8581 to 8aaf9ec Compare July 1, 2026 15:59

@emmettbutler emmettbutler left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great

@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch from 8aaf9ec to fdc4361 Compare July 2, 2026 10:01
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jul 10, 2026

Copy link
Copy Markdown

Circular import analysis

⚠️ Existing circular imports

There are 49 circular imports that already exist on the base branch and have not been changed by this PR.

Show existing cycles (showing 5 of 49 shortest)
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.aiokafka -> ddtrace.internal.datastreams
ddtrace.internal.core -> ddtrace._trace.span -> ddtrace.internal.core
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.google_cloud_pubsub -> ddtrace.internal.datastreams
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.kafka -> ddtrace.internal.datastreams
ddtrace.internal.datastreams -> ddtrace.internal.datastreams.kombu -> ddtrace.internal.datastreams

To see all cycles, download the cycles-base.json and cycles-pr.json artifacts from this CI job and run:

uv run --script scripts/import-analysis/cycles.py compare cycles-base.json cycles-pr.json

Comment thread ddtrace/internal/http.py Outdated
Comment thread ddtrace/internal/http.py
Comment thread src/native/Cargo.toml
Comment thread tests/internal/test_auto.py
Comment thread tests/telemetry/test_telemetry.py
@P403n1x87

Copy link
Copy Markdown
Contributor Author

requires #19005

@P403n1x87
P403n1x87 force-pushed the chore/use-native-http-client branch from 9ed6273 to f6fbf1f Compare July 16, 2026 14:01

@christophe-papazian christophe-papazian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two potential blockers found during review, both on production transport paths. Details inline. Not requesting changes — flagging for confirmation, since each is reproducible.

Comment thread ddtrace/internal/writer/writer.py Outdated
Comment thread ddtrace/internal/http.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors ddtrace’s internal HTTP transport to use the native Rust HTTP client end-to-end (including UDS), reducing reliance on http.client/Python sockets and updating writers/tests to match the new behavior.

Changes:

  • Introduces a native-backed ddtrace.internal.http connection/response layer with client pooling and fork-safety cache clearing.
  • Removes the legacy UDS http.client-based implementation and updates internal callers/writers to handle URL path prefixes explicitly where needed.
  • Updates tests to mock at the native-connection layer and adjusts expectations for error types and header normalization; adds crypto-provider initialization for rustls.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/tracer/test_writer.py Updates UDS tests to use HTTPConnection(unix://…) and expands expected exception types for native client errors.
tests/tracer/test_endpoint_config.py Switches endpoint-config tests to mock ddtrace.internal.http.HTTPResponse and removes http.client-specific mocking.
tests/telemetry/test_writer.py Replaces httpretty usage with NativeHTTPConnection.getresponse mocking and normalizes header lookups.
tests/telemetry/test_telemetry.py Makes header assertions case-insensitive to match native client normalization.
tests/llmobs/test_llmobs_span_agentless_writer.py Adds regression test for preserving intake URL path prefixes; updates timeout test to patch HTTPConnection.
tests/llmobs/test_llmobs_service.py Updates fixture patch target from get_connection to HTTPConnection.
tests/internal/test_utils_http.py Replaces socket-level mocking with NativeHTTPConnection.getresponse mocking for connector tests.
tests/internal/test_auto.py Adjusts gevent/socket-patching expectations to assert no Python-socket dependency in the new connection.
src/native/http_client/client.rs Adds one-time rustls crypto-provider installation before client creation.
src/native/Cargo.toml Adds explicit rustls dependency with ring provider support.
ddtrace/llmobs/_writer.py Uses HTTPConnection directly and updates request targets to avoid losing base-path prefixes.
ddtrace/llmobs/_http.py Replaces LLMObs-local connection logic with re-exports from internal HTTP utilities (behavior change).
ddtrace/internal/writer/writer.py Adds intake base-path handling so writer requests include configured HTTP path prefixes (but not unix socket paths).
ddtrace/internal/utils/http.py Simplifies get_connection() to return the native HTTPConnection and explicitly strips URL paths.
ddtrace/internal/uds.py Removes the legacy UDSHTTPConnection implementation.
ddtrace/internal/telemetry/writer.py Updates return type annotations to use internal HTTPResponse.
ddtrace/internal/native_runtime.py Refactors singleton creation to functools.cache.
ddtrace/internal/http.py Implements the native-backed HTTPConnection/HTTPResponse wrappers with pooled client caching and container header injection.
ddtrace/internal/ci_visibility/_api_client.py Updates connection typing to use HTTPConnection instead of the removed union type.
ddtrace/bootstrap/cloning.py Removes pre-imports of the deleted UDS module; adjusts imports during module cleanup.
ddtrace/aiguard/_api_client.py Reworks request execution to build native HTTP connections and send path-only requests.
benchmarks/suitespec.yml Removes the deleted UDS module from the benchmark component list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ddtrace/internal/http.py Outdated
Comment thread ddtrace/aiguard/_api_client.py
Comment thread ddtrace/llmobs/_http.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/no-changelog A changelog entry is not required for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants