Skip to content

Support CallHttpAsync in Standalone SDK #696

@YunchuWang

Description

@YunchuWang

Support built in CallHttpAsync in Standalone SDK

Summary

Add built-in BuiltIn::HttpActivity support to the standalone worker SDK (Microsoft.DurableTask.Worker) so that CallHttpAsync works when using durabletask-dotnet to connect to sidecar (e.g.,, azuremanaged backend).

Today, CallHttpAsync only works when the Azure Functions Durable Task extension host is present, because the host intercepts the BuiltIn::HttpActivity task in TaskHttpActivityShim before it reaches the worker. In standalone mode, the activity dispatches to the worker, which has no registration for it and fails with a non-retriable ActivityTaskNotFound error.

Motivation

The standalone SDK (Microsoft.DurableTask.Client + Microsoft.DurableTask.Worker) is increasingly used outside Azure Functions — with , Dapr Workflow, and dts backend. HTTP calls are one of the most common orchestration primitives. Requiring users to hand-roll their own HTTP activity to achieve what CallHttpAsync provides out of the box in Azure Functions creates an unnecessary capability gap.

Current behavior (standalone + dts backend)

// This compiles only if you reference Worker.Extensions.DurableTask (Azure Functions package)
// and fails at runtime because nobody handles the built-in activity:
var response = await context.CallHttpAsync(new DurableHttpRequest(HttpMethod.Get, new Uri("https://example.com")));
// → TaskFailedException: ActivityTaskNotFound — "No activity task named 'BuiltIn::HttpActivity' was found."

Even calling it manually without the extension method produces the same failure:

await context.CallActivityAsync<DurableHttpResponse>("BuiltIn::HttpActivity", request);
// → same ActivityTaskNotFound error

Expected behavior

CallHttpAsync (or an equivalent API) should work out of the box with any backend, not just Azure Functions.

Proposed Design

Auto-register a built-in HTTP activity implementation inside DurableTaskWorker that executes the HTTP call locally using HttpClient. This mirrors the approach used by TaskHttpActivityShim in the Functions host, but lives in the SDK itself.

Key changes

  1. New internal class BuiltInHttpActivity in Microsoft.DurableTask.Worker — implements ITaskActivity, uses HttpClient to execute the DurableHttpRequest and return a DurableHttpResponse.

  2. Auto-registration in DurableTaskWorkerBuilder.Build() — inject BuiltInHttpActivity for the name "BuiltIn::HttpActivity" unless the user has explicitly registered their own activity with that name.

  3. Move CallHttpAsync extension method and HTTP types to the core SDK — the CallHttpAsync extension method, DurableHttpRequest, and DurableHttpResponse currently live in the Azure Functions extension package. They should be available to standalone SDK users without requiring a dependency on Worker.Extensions.DurableTask.

  4. User override — if a user registers their own "BuiltIn::HttpActivity" factory, the auto-registration yields to the user's implementation.

What to support in v1

Feature Support Notes
HTTP methods (GET/POST/PUT/DELETE/PATCH) Yes Core functionality
Custom headers Yes Passed through to HttpRequestMessage
Request body Yes String content
Response status + headers + body Yes Mapped to DurableHttpResponse
Timeout Yes From DurableHttpRequest.Timeout
AsynchronousPatternEnabled (202 polling) No (v2) Complex loop — return 202 as-is with a log warning
TokenSource / Managed Identity No (v2) Requires Azure.Identity dependency
HttpRetryOptions No (v2) Durable Task already supports retries via TaskOptions at the activity level

Architecture

Orchestrator → CallHttpAsync(request)
  → CallActivityAsync("BuiltIn::HttpActivity", request)
    → sidecar dispatches to worker
      → DurableTaskWorker activity lookup
        → BuiltInHttpActivity.RunAsync()
          → HttpClient.SendAsync(...)
          → return DurableHttpResponse

This is the same flow as any user-defined activity, except the SDK pre-registers it.

Alternatives Considered

  1. Document "just write your own HTTP activity" — shifts burden to every standalone user. Boilerplate for a universal primitive.

  2. Move it to a separate NuGet package (Microsoft.DurableTask.Worker.Http) — possible but adds packaging overhead for a feature that should be first-class.

Additional Context

  • The Java SDK (durabletask-java) has the same gap — DurableHttp.callHttp() schedules BuiltIn::HttpActivity but the standalone worker has no handler. A parallel enhancement is being proposed there.
  • The Python SDK (durabletask-python) would benefit from the same pattern.

Metadata

Metadata

Assignees

Labels

EnhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions