Skip to content

internal/resolver: add target URI parsing with resolver validation#8876

Open
HueCodes wants to merge 1 commit intogrpc:masterfrom
HueCodes:validate-target-uri
Open

internal/resolver: add target URI parsing with resolver validation#8876
HueCodes wants to merge 1 commit intogrpc:masterfrom
HueCodes:validate-target-uri

Conversation

@HueCodes
Copy link

@HueCodes HueCodes commented Feb 2, 2026

Fixes #8747

Add resolver validation to ParseTarget in grpcutil. The function
parses a gRPC target URI string and verifies that a resolver is
registered for the parsed scheme.

Revert clientconn.go to url.Parse since it uses cc.getResolver()
for custom dial-option resolvers. Remove the redundant resolver.Get
check from rls/config.go.

RELEASE NOTES: n/a

@codecov
Copy link

codecov bot commented Feb 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.34%. Comparing base (2abe1f0) to head (623f036).
⚠️ Report is 49 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8876      +/-   ##
==========================================
+ Coverage   83.17%   83.34%   +0.16%     
==========================================
  Files         414      418       +4     
  Lines       32751    32940     +189     
==========================================
+ Hits        27241    27453     +212     
+ Misses       4091     4078      -13     
+ Partials     1419     1409      -10     
Files with missing lines Coverage Δ
balancer/rls/config.go 86.46% <100.00%> (+0.85%) ⬆️
clientconn.go 90.17% <100.00%> (-0.63%) ⬇️
internal/resolver/target.go 100.00% <100.00%> (ø)

... and 54 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@arjan-bal arjan-bal self-assigned this Feb 3, 2026
@arjan-bal arjan-bal self-requested a review February 3, 2026 06:27
@arjan-bal arjan-bal added this to the 1.80 Release milestone Feb 3, 2026
@arjan-bal arjan-bal added the Type: Feature New features or improvements in behavior label Feb 3, 2026
@arjan-bal
Copy link
Contributor

Hi @HueCodes, thanks for picking up this issue.

To ensure we have a reusable API, we should update existing call sites to use the new implementation. Specifically:

  1. ClientConn:

    grpc-go/clientconn.go

    Lines 1802 to 1827 in b7b1cce

    parsedTarget, err := parseTarget(cc.target)
    if err == nil {
    rb = cc.getResolver(parsedTarget.URL.Scheme)
    if rb != nil {
    cc.parsedTarget = parsedTarget
    cc.resolverBuilder = rb
    return nil
    }
    }
    // We are here because the user's dial target did not contain a scheme or
    // specified an unregistered scheme. We should fallback to the default
    // scheme, except when a custom dialer is specified in which case, we should
    // always use passthrough scheme. For either case, we need to respect any overridden
    // global defaults set by the user.
    defScheme := cc.dopts.defaultScheme
    if internal.UserSetDefaultScheme {
    defScheme = resolver.GetDefaultScheme()
    }
    canonicalTarget := defScheme + ":///" + cc.target
    parsedTarget, err = parseTarget(canonicalTarget)
    if err != nil {
    return err
    }
  2. Route Lookup Service:
    parsedTarget, err := url.Parse(lookupService)
    if err != nil {
    // url.Parse() fails if scheme is missing. Retry with default scheme.
    parsedTarget, err = url.Parse(resolver.GetDefaultScheme() + ":///" + lookupService)
    if err != nil {
    return nil, fmt.Errorf("rls: invalid target URI in lookup_service %s", lookupService)
    }
    }

Both of these locations share common behavior:

  1. If parsing fails, they attempt to append the default scheme and re-parse.
  2. They return a url.URL object.

To handle this, the new function would need to accept a default scheme param and return a url.Url as the result. I think we should design the API to handle these two operations internally. This would allow us to refactor the existing code to call the new API as well. @easwars, what do you think?

@arjan-bal arjan-bal assigned easwars and unassigned arjan-bal Feb 3, 2026
@HueCodes
Copy link
Author

HueCodes commented Feb 3, 2026

Thanks for the response! I will work on fixing this today when I am off work.

@easwars
Copy link
Contributor

easwars commented Feb 3, 2026

Agree with the comments by @arjan-bal.

Another place where we would have to use the new API is here:

if sc.serverURI == "" {

Currently, that code only verifies that the server_uri is not empty. But we need to ensure that it is a valid URI. I think this will cause a whole bunch of unit tests to fail because we simply pass a non-empty string for this value in a lot of tests. So, this could also be done as a follow-up if it ends up touching too many test files. Thanks.

@HueCodes
Copy link
Author

HueCodes commented Feb 4, 2026

Thanks for taking the time to review this. I appreciate it. I addressed the issues:

  • Added ParseTarget function that accepts a default scheme parameter and returns *url.URL
  • Updated clientconn.go and balancer/rls/config.go to use the new function
  • Left xds/bootstrap for a follow-up as suggested, since it may touch many test files

Let me know if there are any other changes you would like me to make.

@arjan-bal arjan-bal assigned HueCodes and unassigned arjan-bal Feb 5, 2026
@HueCodes HueCodes changed the title grpcutil: add ValidateTargetURI function grpcutil: add target URI parsing with resolver validation Feb 7, 2026
@eshitachandwani eshitachandwani assigned arjan-bal and unassigned HueCodes Feb 9, 2026
name string
target string
defaultScheme string
wantScheme string
Copy link
Contributor

Choose a reason for hiding this comment

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

In addition to the scheme, I think we should also validate the host and the path (via Target.Endpoint()) since they're the most important components for gRPC. I've left a separate comment about returning a resolver.Target instead of a URL.

@arjan-bal arjan-bal assigned HueCodes and unassigned arjan-bal Feb 11, 2026
HueCodes added a commit to HueCodes/grpc-go that referenced this pull request Feb 13, 2026
Move ParseTarget to resolver package per PR grpc#8876 review feedback.
Add ParseTargetWithRegistry to support custom resolver registries.
Return resolver.Target instead of *url.URL for better API ergonomics.

Changes:
- Add resolver.ParseTarget using global resolver registry
- Add resolver.ParseTargetWithRegistry accepting custom registry function
- Return resolver.Target instead of *url.URL
- Use %v instead of %w in error messages per review
- Update balancer/rls/config.go to use resolver.ParseTarget
- Simplify clientconn.initParsedTargetAndResolverBuilder
- Remove internal/grpcutil/target.go and tests
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't realize that adding the function here would make the ParseTarget public. The issue explicitly mentions that the API should be internal. Can you please move the function to the internal/resolver package?

// Package resolver provides internal resolver-related functionality.
package resolver

Apologies for the back and forth.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for taking the time to review my code. I am learning a ton as this is very helpful. I should be done tonight and will push the changes then.

@arjan-bal arjan-bal assigned HueCodes and unassigned arjan-bal Feb 18, 2026
@HueCodes HueCodes force-pushed the validate-target-uri branch 2 times, most recently from 48e43ac to 3a28eea Compare February 20, 2026 05:50
Fixes grpc#8747

Add ParseTarget to internal/resolver. The function parses a gRPC target
string into a resolver.Target and verifies that a resolver is registered
for the parsed scheme using a caller-supplied lookup function.

Scheme lookup rules:
- Registered scheme (hierarchical or opaque form): accepted immediately.
- Unregistered scheme in hierarchical URI (scheme://...): rejected; the
  caller chose this scheme explicitly, so no silent fallback occurs.
- Opaque URI (e.g. host:port) or empty-scheme URI with an unregistered
  scheme: retried by prepending defaultScheme + ":///" if provided.

Accepting a builder func instead of calling resolver.Get directly lets
clientconn.go pass cc.getResolver, which also checks resolvers
registered via dial options.

Update clientconn.go and balancer/rls/config.go to use ParseTarget,
removing the duplicate parsing and resolver-lookup logic from both.

RELEASE NOTES: n/a
@HueCodes HueCodes force-pushed the validate-target-uri branch from 3a28eea to 623f036 Compare February 24, 2026 06:06
@mbissa mbissa modified the milestones: 1.80 Release, 1.81 Release Mar 6, 2026
@arjan-bal arjan-bal assigned arjan-bal and unassigned HueCodes Mar 17, 2026
@arjan-bal arjan-bal added Type: Internal Cleanup Refactors, etc and removed Type: Feature New features or improvements in behavior labels Mar 17, 2026
Copy link
Contributor

@arjan-bal arjan-bal left a comment

Choose a reason for hiding this comment

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

Almost looks good, some minor comments.

Comment on lines 1813 to 1816
defScheme := cc.dopts.defaultScheme
if internal.UserSetDefaultScheme {
defScheme = resolver.GetDefaultScheme()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we move the computation of the default scheme higher up and pass the default scheme to the first call to iresolver.ParseTarget? That should allow us to get rid of the second ParseTarget below.

errContain string
}{
{
name: "known scheme resolves",
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Could you replace spaces with underscores (_) in the subtest names? The Go test harness automatically rewrites spaces, which makes it difficult to search the logs or run specific subtests via the test filter (-run flag) when debugging.

Comment on lines +50 to +54
if u.Opaque == "" {
// Unregistered scheme in hierarchical URI form (scheme://...): the
// caller explicitly chose this scheme; do not silently fall back.
return resolver.Target{}, fmt.Errorf("no resolver registered for scheme %q in target %q", u.Scheme, target)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this check isn't present in the current implementation. Is there a strong reason to add this? If no, I would suggest maintaining the existing behaviour.

@arjan-bal arjan-bal assigned HueCodes and unassigned arjan-bal Mar 17, 2026
@arjan-bal arjan-bal changed the title grpcutil: add target URI parsing with resolver validation internal/resolver: add target URI parsing with resolver validation Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an internal API to validate a target URI

4 participants