Skip to content

extproc: implement ClientFilter and ClientFilterBuilder interface#9086

Open
eshitachandwani wants to merge 16 commits into
grpc:masterfrom
eshitachandwani:build_client_interceptor
Open

extproc: implement ClientFilter and ClientFilterBuilder interface#9086
eshitachandwani wants to merge 16 commits into
grpc:masterfrom
eshitachandwani:build_client_interceptor

Conversation

@eshitachandwani
Copy link
Copy Markdown
Member

@eshitachandwani eshitachandwani commented Apr 22, 2026

This PR is part of implementing A93: xds-ext-proc

This PR adds the builder that implements the ClientFilter and ClientFilterBuilder interface. That includes creating the interceptor config from the base and the override config. It also includes making the ext_proc channel.

THis PR has a placeholder function for converting grpcService to channel The function will be implemented later when implementing gRFC A102.

The interceptor struct has a resolver.ClientInterceptor embedded for now, but that will be removed in a later PR when we implement the resolver.ClientInterceptor interface. Since the filter will not be registered and no one is calling the resolver.ClientInterceptor function fro extproc filter , it will not panic.

#ext-proc-a93

RELEASE NOTES: None

@eshitachandwani eshitachandwani added this to the 1.82 Release milestone Apr 22, 2026
@eshitachandwani eshitachandwani added the Type: Feature New features or improvements in behavior label Apr 22, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 90.47619% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.19%. Comparing base (037c6ef) to head (1db23e1).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
internal/xds/httpfilter/extproc/ext_proc.go 86.20% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9086      +/-   ##
==========================================
- Coverage   83.20%   83.19%   -0.02%     
==========================================
  Files         417      417              
  Lines       33648    33698      +50     
==========================================
+ Hits        27998    28035      +37     
- Misses       4235     4244       +9     
- Partials     1415     1419       +4     
Files with missing lines Coverage Δ
internal/xds/httpfilter/extproc/config.go 78.94% <100.00%> (+10.94%) ⬆️
internal/xds/httpfilter/extproc/ext_proc.go 76.92% <86.20%> (+4.70%) ⬆️

... and 22 files with indirect coverage changes

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

@eshitachandwani
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the Envoy external processing filter, providing configuration parsing logic and the infrastructure to build a client interceptor. Feedback addresses a critical uninitialized function variable that would cause a panic during filter construction and suggests a performance optimization for slice allocation when converting string matchers.

Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go Outdated
eshitachandwani and others added 3 commits April 26, 2026 22:33
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@eshitachandwani
Copy link
Copy Markdown
Member Author

As per offline discussion , changed override config to use the new optional type and changed the functions and tests accordingly.

@eshitachandwani
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic Option[T] type for optional values and establishes the foundational configuration and builder logic for the xDS external processing (extproc) HTTP filter. Feedback highlights critical concerns regarding resource management, specifically the creation of unmanaged grpc.ClientConn instances that could lead to leaks and performance issues. Additionally, the interceptor struct currently embeds an uninitialized interface, which would result in a nil pointer dereference panic if invoked.

Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/ext_proc.go
@easwars
Copy link
Copy Markdown
Contributor

easwars commented May 12, 2026

@eshitachandwani
There is a whole lot of overlap with 9073, in which I had a bunch of comments. So, moving this back to you until the former is merged.

@easwars easwars assigned eshitachandwani and unassigned easwars and mbissa May 12, 2026
@eshitachandwani
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the BuildClientFilter and BuildClientInterceptor methods for the extproc HTTP filter, along with logic to merge base and override configurations. The review identified several critical issues: embedding the resolver.ClientInterceptor interface without implementing its methods will cause a nil pointer dereference panic when invoked; creating a new grpc.ClientConn for every interceptor is inefficient and risks resource exhaustion; and the configuration merging logic performs a shallow copy of slices, which could lead to unintended side effects if the shared underlying arrays are modified.

Comment thread internal/xds/httpfilter/extproc/ext_proc.go
Comment thread internal/xds/httpfilter/extproc/ext_proc.go Outdated
Comment thread internal/xds/httpfilter/extproc/config.go
Comment on lines +51 to +53
createExtProcChannel = func(xdsresource.GRPCServiceConfig) (*grpc.ClientConn, error) {
return nil, fmt.Errorf("dialing external processing server not implemented")
}
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.

Nit: Please consider having this method return a (grpc.ClientConnInterface, func(), error) instead of (*grpc.ClientConn, error). We only care about the methods in the grpc.ClientConnInterface and Close. The latter will be satisfied by the second return value func(). The reason for making the return type be the interface is that it allows the implementer to get fancy and return a wrapped ClientConn if required.

Copy link
Copy Markdown
Member Author

@eshitachandwani eshitachandwani May 22, 2026

Choose a reason for hiding this comment

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

Done. Changed it to return (grpc.ClientConnInterface, func() error, error) . I changed it to func() error since clientconn.Close is a function that returns error.

if err != nil {
return nil, fmt.Errorf("extproc: failed to create client: %v", err)
}
extClient := v3procservicepb.NewExternalProcessorClient(cc)
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.

Why do we have to create the stub here and pass it as a separate field when we are anyways passing the cc?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I am not sure what you mean. We need the stub to create a new RPC every time NewStream is called. We actually so not need clientconn if we have the cancel function. So changed it to have the cancel function and the v3procservicepb.ExternalProcessorClient stub.

resolver.ClientInterceptor
config baseConfig
extClient v3procservicepb.ExternalProcessorClient
cc *grpc.ClientConn
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.

Same here. Make this to be of the interface type and store the cancel func as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If we have the cancel function and the v3procservicepb.ExternalProcessorClient stub , we do not need the interface. Changed it to such.


func (s) TestBuildClientInterceptor(t *testing.T) {
origCreateExtProcChannel := createExtProcChannel
defer func() { createExtProcChannel = origCreateExtProcChannel }()
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.

Nit/Optional: Personally I find it more readable when it is:

origFoo := foo
foo := overriddenFoo
defer func() { foo = origFoo} ()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

cfg httpfilter.FilterConfig
override httpfilter.FilterConfig
wantConfig baseConfig
wantErr string
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.

I don't see much value in wantErr being a string here. Why not just a bool?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just always thought comparing the error strings is a better test but since its the same error message every time , I guess it does not add value here. But I have added a new failure case for when channel creation fails , so retaining the wantErr string here. Let me know if we should change it?

Comment on lines +774 to +776
if !ok {
t.Fatalf("BuildClientInterceptor() returned %T, want *interceptor", intptr)
}
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.

Is this ever possible? Why not just do ic := intptr.(*interceptor)? And if there is a bug in the code that ends up causing a different interceptor type to be returned, the test will panic, which is fine.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
intptr, err := f.BuildClientInterceptor(tc.cfg, tc.override)
if tc.wantErr == "" {
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.

I think this test logic is still reasonably complicated for a table driven test. Please consider splitting into success and failure cases.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

}, nil
}

type interceptor struct {
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.

Please call this clientInterceptor. We will have a serverInterceptor soonish.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

@easwars easwars assigned eshitachandwani and unassigned easwars May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants