-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
credentials, transport, grpc : add a call option to override the :authority header on a per-RPC basis #8068
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #8068 +/- ##
==========================================
+ Coverage 82.32% 82.36% +0.04%
==========================================
Files 387 387
Lines 39064 39091 +27
==========================================
+ Hits 32159 32197 +38
+ Misses 5593 5584 -9
+ Partials 1312 1310 -2
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall lgtm. Just few minor comments. I think we can combine some individual into t-tests.
credentials/credentials.go
Outdated
@@ -120,6 +120,14 @@ type AuthInfo interface { | |||
AuthType() string | |||
} | |||
|
|||
// AuthorityValidator defines an interface for validating the authority used to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to mention "defines an interface". Can just say "AuthorityValidator validates the authority....."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
internal/transport/http2_client.go
Outdated
// precedence to determine the :authority header. Any value in Host field of | ||
// CallHdr is overwritten. But before overriding, we validate the authority | ||
// string against the peer certificates and fail the RPC with `UNAVAILABLE` | ||
// status code if eirther of the condition fails. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
internal/transport/http2_client.go
Outdated
if !ok { | ||
return nil, &NewStreamError{Err: status.Error(codes.Unavailable, fmt.Sprintf("credentials type %s does not implement the AuthorityValidator interface", t.authInfo.AuthType())), AllowTransparentRetry: false} | ||
} | ||
err := auth.ValidateAuthority(callHdr.Authority) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can combine this with if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// | ||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a | ||
// later release. | ||
func CallAuthority(auth string) CallOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we call it PerRPCAuthority? Similar to PerRPCCredentials?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We went through this in the design doc. We had consensus on CallAuthority
.
// | ||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a | ||
// later release. | ||
type AuthorityOverrideCallOption struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we name above PerRPCAuthority, AuthorityOverrideCallOption can change to PerRPCAuthorityCallOption
credentials/credentials_ext_test.go
Outdated
defer cancel() | ||
|
||
_, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(tt.expectedAuth)) | ||
if tt.expectRPCError { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/expect/want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
} | ||
} | ||
|
||
func (s) TestTLSCredsWithNoAuthorityOverride(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the tests tls_ext_test.go should already be testing this right? Does this need to be here? or it should just be another test case in the above TestAuthorityCallOptionsWithTLSCreds
// Perform a test RPC with a specified call authority. | ||
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) | ||
defer cancel() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nix new line
} | ||
|
||
// FakeCredsNoAuthValidator is a test credential that does not implement AuthorityValidator. | ||
type FakeCredsNoAuthValidator struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the FakeCreds can be at the top
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename this testCreds
. We use the term fake when we use a test double that actually provides a fake implementation of something that the code under test depends on.
Also, the AuthInfo types need to be named such that it is clear from their names that they either implement the AuthorityValidator
interface or not.
credentials/credentials_ext_test.go
Outdated
// TestCallOptionWithNoAuthorityValidator tests the CallAuthority call option | ||
// with custom credentials that do not implement AuthorityValidator and verifies | ||
// that it fails with `UNAVAILABLE` status code. | ||
func (s) TestCallOptionWithNoAuthorityValidator(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same. This can be another test case in test with CustomCreds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
Also, don't feel strongly but may be we can utilize the setup in tls_ext_test or move the tls tests to tls_ext_test.go |
@@ -71,6 +71,10 @@ func (info) AuthType() string { | |||
return "insecure" | |||
} | |||
|
|||
func (info) ValidateAuthority(_ string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can get rid of the _
variable name as this method contains only one parameter.
return nil | ||
} | ||
} | ||
return fmt.Errorf("credentials: failed to verify authority %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The err
variable will only contain the last error here. Should we instead include all errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Also, the error message could be something like
fmt.Errorf(credentials: invalid authority %q: %s", authority, err)
@@ -120,6 +120,13 @@ type AuthInfo interface { | |||
AuthType() string | |||
} | |||
|
|||
// AuthorityValidator validates the authority used to override the `:authority` | |||
// header. A struct implementing AuthInfo should also implement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/A struct implementing AuthInfo should/Implementations of AuthInfo should/
// header. A struct implementing AuthInfo should also implement | ||
// AuthorityValidator if the credentials need to support per-RPC authority overrides. | ||
type AuthorityValidator interface { | ||
ValidateAuthority(authority string) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document what this authority means and what the return value indicates.
// AuthorityValidator validates the authority used to override the `:authority` | ||
// header. A struct implementing AuthInfo should also implement | ||
// AuthorityValidator if the credentials need to support per-RPC authority overrides. | ||
type AuthorityValidator interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I think we should focus on documenting a couple of things here:
- that this is an option interface
- will be invoked only when the appropriate call option is specified by the application
Something to this effect could work (might need some cleanup)
// AuthorityValidator validates `:authority` header overrides. This is an
// optional interface for transport credentials to implement.
type AuthorityValidator interface {
// ValidateAuthority validates the authority used to override the
// `:authority` header. If implemented by the AuthInfo type returned by the
// transport credentials, this method will be invoked if the client
// application attempts to override the `:authority` header by using the
// CallAuthority call option.
ValidateAuthority(authority string) error
}
return nil | ||
} | ||
} | ||
return fmt.Errorf("credentials: failed to verify authority %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Also, the error message could be something like
fmt.Errorf(credentials: invalid authority %q: %s", authority, err)
// | ||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a | ||
// later release. | ||
func CallAuthority(auth string) CallOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We went through this in the design doc. We had consensus on CallAuthority
.
// | ||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a | ||
// later release. | ||
func CallAuthority(auth string) CallOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: s/auth/authority/
Please note that this is part of our public API. Parameter names also serve as documentation. So using good parameter names is critical.
@@ -365,6 +366,36 @@ func (o MaxRecvMsgSizeCallOption) before(c *callInfo) error { | |||
} | |||
func (o MaxRecvMsgSizeCallOption) after(*callInfo, *csAttempt) {} | |||
|
|||
// CallAuthority returns a CallOption which sets the authority to override the | |||
// `:authority` pseudoheader in a RPC. If this is set, the RPC will use only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only place where we use the term pseudoheader
. Either use it everywhere (like in the dosctrings of the AuthorityValidator
interface), or don't use it here. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about something more simpler and direct
"CallAuthority creates a CallOption that sets the HTTP/2 :authority header of an RPC to the specified value."
} | ||
|
||
// FakeCredsNoAuthValidator is a test credential that does not implement AuthorityValidator. | ||
type FakeCredsNoAuthValidator struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename this testCreds
. We use the term fake when we use a test double that actually provides a fake implementation of something that the code under test depends on.
Also, the AuthInfo types need to be named such that it is clear from their names that they either implement the AuthorityValidator
interface or not.
// TestAuthInfo implements the AuthInfo interface. | ||
type TestAuthInfo struct{} | ||
|
||
// AuthType returns the authentication type. | ||
func (TestAuthInfo) AuthType() string { return "test" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The definition of this type is sandwiched between the implementation of the test creds type. Please move it to be separate.
} | ||
|
||
// TestAuthInfo implements the AuthInfo interface. | ||
type TestAuthInfo struct{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this have to be exported?
|
||
// ServerHandshake performs the server-side handshake. | ||
// Returns a test AuthInfo object to satisfy the interface requirements. | ||
func (c *FakeCredsWithAuthValidator) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, we can have a single testCreds
type that contains a boolean field that indicates whether or not it should return an AuthInfo
that implements AuthorityValidator
or not. We don't need two test credentials types here.
name string | ||
creds credentials.TransportCredentials | ||
expectedAuth string | ||
wantRPCError bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. You can directly use the status that you want the RPC to return.
Fixes: #5361
RELEASE NOTES:
CallAuthority
callOption that can be used to overwrite the http:authority
header on per-RPC basis.AuthorityValidator
interface which needs to be implemented by credentials that want to allow authority overwrite.AuthorityValidator
interface for Insecure and TLS credentials.