Skip to content

api: accept canonical gRPC URI form on the gRPC endpoint - #1817

Open
lrascao wants to merge 4 commits into
dapr:masterfrom
lrascao:canonical-grpc-format
Open

api: accept canonical gRPC URI form on the gRPC endpoint#1817
lrascao wants to merge 4 commits into
dapr:masterfrom
lrascao:canonical-grpc-format

Conversation

@lrascao

@lrascao lrascao commented May 15, 2026

Copy link
Copy Markdown

Description

The Dapr SDKs warn that http/https are deprecated as gRPC URI schemes — the prefix denotes a name resolver, not a transport.

  • Add DaprDefaults.NormalizeGrpcEndpoint. Returns http/https unchanged; rewrites dns://host:port?tls=true to https://host:port and ?tls=false to http://host:port before the URI reaches GrpcChannel.ForAddress (which is HttpClient-backed and does not understand the dns scheme). Throws on dns:// without a parseable tls=true|false and on any other scheme.
  • DaprClientBuilder.Build and DaprGenericClientBuilder .BuildDaprClientDependencies now normalize the gRPC endpoint up front and feed the normalized URI to both the http/https switch and GrpcChannel.ForAddress. The redundant scheme check on the gRPC path is gone; HTTP endpoint validation is unchanged.
  • GetDefaultGrpcEndpoint normalizes the env-var value before handing it to BuildEndpoint (otherwise UriBuilder would strip the ?tls= query while reassembling).
  • UseGrpcEndpoint docstrings now mention the canonical form.
  • Tests: unit coverage for NormalizeGrpcEndpoint (incl. case-insensitive TLS=True, missing tls, unparseable tls, unsupported scheme), env-var path via DAPR_GRPC_ENDPOINT=dns://..., and dns acceptance / dns- without-tls rejection on both DaprClientBuilder and DaprJobsClientBuilder (which exercises the shared generic builder used by Workflow/Messaging/AI/etc). HTTP endpoints and InvocationHandler are unchanged.

Checklist

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation

The Dapr SDKs warn that http/https are deprecated as gRPC URI schemes —
the prefix denotes a name resolver, not a transport.

- Add `DaprDefaults.NormalizeGrpcEndpoint`. Returns http/https unchanged;
  rewrites `dns://host:port?tls=true` to `https://host:port` and
  `?tls=false` to `http://host:port` before the URI reaches
  `GrpcChannel.ForAddress` (which is HttpClient-backed and does not
  understand the dns scheme). Throws on `dns://` without a parseable
  `tls=true|false` and on any other scheme.
- `DaprClientBuilder.Build` and `DaprGenericClientBuilder
  .BuildDaprClientDependencies` now normalize the gRPC endpoint up front
  and feed the normalized URI to both the http/https switch and
  `GrpcChannel.ForAddress`. The redundant scheme check on the gRPC path
  is gone; HTTP endpoint validation is unchanged.
- `GetDefaultGrpcEndpoint` normalizes the env-var value before handing
  it to `BuildEndpoint` (otherwise `UriBuilder` would strip the `?tls=`
  query while reassembling).
- `UseGrpcEndpoint` docstrings now mention the canonical form.
- Tests: unit coverage for `NormalizeGrpcEndpoint` (incl. case-insensitive
  `TLS=True`, missing tls, unparseable tls, unsupported scheme), env-var
  path via `DAPR_GRPC_ENDPOINT=dns://...`, and dns acceptance / dns-
  without-tls rejection on both `DaprClientBuilder` and
  `DaprJobsClientBuilder` (which exercises the shared generic builder
  used by Workflow/Messaging/AI/etc).
HTTP endpoints and `InvocationHandler` are unchanged.

Signed-off-by: Luis Rascao <luis.rascao@gmail.com>
@lrascao
lrascao requested review from a team as code owners May 15, 2026 09:03
@WhitWaldo

Copy link
Copy Markdown
Contributor

@lrascao Could you please add some integration tests demonstrating that this doesn't hit a wall when it encounters the runtime? You're right that from the client side, there's more than can be done here (as you did), but for the most part, the SDK is generally just a wrapper for functionality exposed by the runtime. So while yes, the client using HttpClient doesn't care what the prefix is, I don't know that's going to be true of the runtime that is then connected to.

@WhitWaldo

Copy link
Copy Markdown
Contributor

@lrascao Also, the warning about HTTP is because the SDKs are transitioning to only connect to the runtime via gRPC, so the intent is that developers have some heads up that they need to specify a gRPC endpoint/port for their apps so callbacks work (if using those building blocks).

Copilot AI 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.

Pull request overview

This PR updates the Dapr .NET SDK client builders to accept a “canonical” gRPC DNS-style endpoint (dns://...?...) by normalizing it into an http:// or https:// URI before creating the GrpcChannel, and adds unit tests to cover the new behavior.

Changes:

  • Added DaprDefaults.NormalizeGrpcEndpoint to rewrite dns://host:port?tls=<bool> into http(s)://host:port (and reject unsupported/malformed inputs).
  • Updated DaprClientBuilder.Build and DaprGenericClientBuilder.BuildDaprClientDependencies to normalize gRPC endpoints before scheme checks and GrpcChannel.ForAddress.
  • Added/updated unit tests covering the normalization logic and builder acceptance/rejection of the DNS form.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/Dapr.Jobs.Test/DaprJobsClientBuilderTests.cs Updates scheme validation expectations and adds acceptance/rejection tests for dns://...?... endpoints.
test/Dapr.Common.Test/DaprDefaultTest.cs Adds unit tests for NormalizeGrpcEndpoint and env-var handling of DNS-form gRPC endpoints.
test/Dapr.AspNetCore.Test/DaprClientBuilderTest.cs Updates scheme validation expectations and adds acceptance/rejection tests for dns://...?... endpoints.
src/Dapr.Common/DaprGenericClientBuilder.cs Normalizes gRPC endpoint before creating the channel and removes redundant scheme validation.
src/Dapr.Common/DaprDefaults.cs Introduces NormalizeGrpcEndpoint and normalizes env-var gRPC endpoints prior to BuildEndpoint.
src/Dapr.Client/DaprClientBuilder.cs Normalizes gRPC endpoint before creating the channel and removes redundant scheme validation.
Comments suppressed due to low confidence (2)

src/Dapr.Client/DaprClientBuilder.cs:174

  • The comment above the Http2UnencryptedSupport switch is both misspelled (“maksecure”) and misleading: this switch enables unencrypted/plaintext HTTP/2 support for gRPC over http://, not “secure” calls. Updating it will avoid confusion about why the switch is set only for the http scheme.
        if (grpcEndpoint.Scheme.Equals(Uri.UriSchemeHttp))
        {
            // Set correct switch to maksecure gRPC service calls. This switch must be set before creating the GrpcChannel.
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

src/Dapr.Common/DaprGenericClientBuilder.cs:199

  • The comment above the Http2UnencryptedSupport switch says it is for “secure gRPC service calls”, but the switch name and behavior are the opposite: it enables unencrypted/plaintext HTTP/2 support for http:// endpoints. Adjusting this comment will make the intent clearer and prevent future refactors from removing it incorrectly.
        if (grpcEndpoint.Scheme.Equals(Uri.UriSchemeHttp))
        {
            // Set correct switch to make secure gRPC service calls. This switch must be set before creating the GrpcChannel.
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
        }

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

Comment on lines +121 to +133
if (uri.Scheme == DnsScheme)
{
var useTls = TryParseTlsQuery(uri.Query);
if (useTls is null)
{
throw new InvalidOperationException(
$"The gRPC endpoint '{endpoint}' uses the 'dns' scheme but is missing or has an invalid 'tls' " +
"query parameter. Use the canonical gRPC URI form 'dns://host:port?tls=true' or 'dns://host:port?tls=false'.");
}

var scheme = useTls.Value ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
return new UriBuilder { Scheme = scheme, Host = uri.Host, Port = uri.Port }.ToString();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants