feat(fetch_azure): adapt fetch HttpClient to Azure's HttpClient#494
Draft
martintmk wants to merge 22 commits into
Draft
feat(fetch_azure): adapt fetch HttpClient to Azure's HttpClient#494martintmk wants to merge 22 commits into
martintmk wants to merge 22 commits into
Conversation
Add a new fetch_azure crate providing FetchHttpClient, an adapter that implements typespec_client_core::http::HttpClient on top of a fetch::HttpClient. This lets the Azure SDK for Rust use fetch as its HTTP transport. The adapter converts a typespec Request into a fetch request (method, uri, headers, and bytes or seekable-stream body), executes it through the fetch client, and maps the fetch response back into an AsyncRawResponse with a streamed body. A new_http_client helper returns an Arc<dyn HttpClient> for convenience. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #494 +/- ##
=======================================
Coverage 99.9% 100.0%
=======================================
Files 336 338 +2
Lines 24829 24932 +103
=======================================
+ Hits 24818 24932 +114
+ Misses 11 0 -11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Avoid the possessive form of the SDK acronym, which Hunspell flags, in the to_headers doc comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Incorporate the best ideas from the internal azure_core adapter to reduce allocations and improve error classification: - split request building (mapped to DataConversion errors) from execution (mapped to Io errors) via layered::Service, instead of the combined builder .fetch() path - add an empty-body fast path that reuses fetch's shared empty body instead of allocating - stream the response body via HttpBody::into_stream, dropping the http-body-util dependency - forward seekable request-stream read errors with a descriptive message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add examples/azure_transport.rs showing how to adapt a Tokio-based fetch client into an Arc<dyn HttpClient> Azure SDK transport and issue a request through the typespec HttpClient trait. Enable the tokio and rustls features on the fetch dev-dependency so the example can build a real client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add integration tests for the previously-uncovered branches so the crate reaches full line coverage: - request build failure maps to a DataConversion error - non-UTF8 response header values are skipped - a failing seekable request-body stream surfaces through the body error map - a failing response body surfaces through the response error map Adds async-trait and futures dev-dependencies for the erroring SeekableStream helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bundle a runtime abstraction alongside the transport: SpawnerRuntime implements azure_core::async_runtime::AsyncRuntime on top of an anyspawn::Spawner (spawn via the spawner, sleep on its blocking pool, and yield), with a new_async_runtime helper returning Arc<dyn AsyncRuntime>. Switch the crate from typespec_client_core to azure_core (which re-exports the same typespec http and async_runtime traits) so both abstractions come from one Azure SDK crate. Add anyspawn and azure_core dependencies and drop the direct typespec_client_core dependency. Add integration tests for spawn, abort, sleep, yield, the dyn-runtime helper, and the From/inner round trip; lib.rs remains at 100% coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SpawnerRuntime now holds a tick::Clock alongside the spawner and implements AsyncRuntime::sleep via Clock::delay, instead of blocking a spawner thread with std::thread::sleep. Constructors and new_async_runtime take the clock; spawner() and clock() accessors replace inner()/into_inner(), and From now accepts a (Spawner, Clock) tuple. lib.rs remains at 100% coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The adapter implements azure_core::http::HttpClient, so AzureHttpClient reads more naturally than FetchHttpClient. Pure rename; no behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martintmk
commented
Jun 12, 2026
martintmk
commented
Jun 12, 2026
martintmk
commented
Jun 12, 2026
Drop the new_http_client free function in favor of From impls: From<fetch::HttpClient> for AzureHttpClient (unchanged) and a new From<AzureHttpClient> for Arc<dyn HttpClient>, so callers write AzureHttpClient::from(client).into(). A direct From<fetch::HttpClient> for Arc<dyn HttpClient> is not possible under the orphan rules, so the Arc conversion goes through the local type. lib.rs remains at 100% coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address PR review feedback: - move AzureHttpClient into a client module - move the runtime into a runtime module and rename SpawnerRuntime to Runtime - fix cancellation: spawn tasks wrapped in futures::future::Abortable and abort via AbortHandle so aborting wakes pending waiters, instead of the flag-based approach that left parked awaiters hanging lib.rs is now a thin module root; client.rs and runtime.rs are each at 100% coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martintmk
commented
Jun 12, 2026
Address PR review feedback: - drop AzureHttpClient::inner and into_inner (not needed publicly) - drop the new_async_runtime free function; add From<Runtime> for Arc<dyn AsyncRuntime> to mirror the client's From-based Arc conversion - split integration tests into tests/client.rs and tests/runtime.rs client.rs and runtime.rs remain at 100% coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an optional �zure-identity feature under which Runtime implements �zure_identity::Executor, running developer-credential commands on the spawner's blocking pool. Add a tokio-based blob-listing example that wires the transport, executor, and DeveloperToolsCredential together. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The CI examples runner executes every example and requires exit 0. The blob_list example needs a live Storage account and developer sign-in, so it now skips gracefully (printing a message and returning Ok) when \AZURE_STORAGE_SERVICE_ENDPOINT\ is unset, while still running the full flow when configured. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark to_fetch_body with mutants::skip: the empty-body fast path is observationally equivalent to the general bytes path (both yield a zero-length body), so the is_empty() guard is an equivalent mutant. Bound the abort cancellation test with a timeout so a no-op abort fails fast (caught) instead of hanging until the mutation-test timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the AsyncRuntime adapter (Runtime) and the azure_identity::Executor impl out of fetch_azure into a dedicated anyspawn_azure crate. fetch_azure now provides only the AzureHttpClient transport; anyspawn_azure owns the spawner/clock-backed runtime and the optional azure-identity executor. Also drop the From<(Spawner, Clock)> for Runtime conversion (callers use Runtime::new) and keep the blob_list example in fetch_azure, now dev-depending on anyspawn_azure for the executor. Register both crates in the root README and CHANGELOG. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fetch_azure no longer depends on anyspawn after the runtime extraction, so the crate-doc reference to it must be a plain code span rather than an intra-doc link (which fails the docs build under -D warnings). Regenerate the README accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martintmk
commented
Jun 12, 2026
Both crate libraries used azure_core only to reach the HttpClient and AsyncRuntime traits, which azure_core re-exports from typespec_client_core (the allowed_external_types lists already targeted typespec_client_core). Depend on typespec_client_core directly in both libraries (fetch_azure enables its \http\ feature). azure_core is retained only as a fetch_azure dev-dependency for the blob_list example, which uses Azure-specific ClientOptions/Transport/credentials. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martintmk
commented
Jun 12, 2026
martintmk
commented
Jun 12, 2026
Per review, rename the transport adapter to \etch_azure::HttpClient\ so it reads naturally alongside \�nyspawn_azure::Runtime\. Since the struct now shares its name with the \ ypespec_client_core::http::HttpClient\ trait it implements, the trait is imported under the \HttpClientTrait\ alias where it must be named (impl site, \Arc<dyn ...>\). Updates the lib/struct docs, examples, and tests accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror the existing \From<Runtime> for Arc<dyn AsyncRuntime>\ with a feature-gated \From<Runtime> for Arc<dyn azure_identity::Executor>\ so a Runtime can be handed to credentials as a boxed executor, with a test covering the conversion. Also enable the \http\ feature on anyspawn_azure's typespec_client_core dependency: typespec_client_core's always-compiled \stream\ module imports the http-gated \crate::http\, so the crate does not build without it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
martintmk
commented
Jun 12, 2026
Add \#![cfg_attr(docsrs, feature(doc_cfg))]\ to fetch's crate root so feature-gated items render with their feature requirements on docs.rs, matching fetch_azure and anyspawn_azure. The attribute is gated on the \docsrs\ cfg, so it is inert on stable builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Miri cannot run tokio's runtime or spawn OS processes, so the runtime and transport integration tests fail under \cargo miri test\. Gate both test files with \#![cfg(not(miri))]\ (matching anyspawn's tokio test files); neither crate has unsafe code, so Miri loses no UB coverage. Also fix a stale doc reference to fetch_azure::Runtime in the moved runtime tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Under Miri the cfg(not(miri)) guard strips each test crate's contents, including its module doc, leaving an empty crate that trips the denied missing_docs lint. Add an allow(missing_docs) attribute before the cfg guard (matching anyspawn's tokio test files) so the empty Miri build compiles cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two crates that adapt Oxidizer building blocks to the Azure SDK abstractions, built directly on
typespec_client_core1.0.0 (the crate that defines theHttpClientandAsyncRuntimetraits thatazure_corere-exports):fetch_azure—AzureHttpClientimplementstypespec_client_core::http::HttpClienton top of afetch::HttpClienttransport.anyspawn_azure—Runtimeimplementstypespec_client_core::async_runtime::AsyncRuntimeon top of ananyspawn::Spawner(spawning) and atick::Clock(sleeping). With the optionalazure-identityfeature it also implementsazure_identity::Executor, running developer-credential commands on the spawner.fetchis depended on without enabling any of its features.Details
AzureHttpClientconverts a typespecRequest(method, URI, headers, and bytes or seekable-stream body) into afetchrequest, executes it, and maps thefetchresponse back into anAsyncRawResponsewith a streamed body. Empty bodies and byte bodies avoid copies; non-UTF-8 response headers are skipped (matching the built-inreqwesttransport). Build errors map toDataConversion; transport errors toIo.From<fetch::HttpClient>andFrom<AzureHttpClient>intoArc<dyn HttpClient>;From<Runtime>intoArc<dyn AsyncRuntime>.Runtimespawns tasks wrapped infutures::future::Abortableso cancellation wakes pending waiters;sleepusestick::Clock::delay.azure_transport(transport round-trip) andblob_list(transport +anyspawn_azureexecutor +DeveloperToolsCredential, listing blobs on tokio).client.rsandruntime.rsare each at 100% line/region coverage.This mirrors the in-tree
reqwestadapter shipped bytypespec_client_core.