diff --git a/internal/transport/http2_client.go b/internal/transport/http2_client.go index b2db61a1f86d..b5a895a1e5ba 100644 --- a/internal/transport/http2_client.go +++ b/internal/transport/http2_client.go @@ -946,6 +946,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s LocalAddr: t.localAddr, Compression: callHdr.SendCompress, Header: header, + Authority: callHdr.Host, }) } if transportDrainRequired { diff --git a/stats/stats.go b/stats/stats.go index 10bf998aa5be..c6996724c95c 100644 --- a/stats/stats.go +++ b/stats/stats.go @@ -190,6 +190,9 @@ type OutHeader struct { Compression string // Header contains the header metadata sent. Header metadata.MD + // Authority is the :authority pseudo-header sent for the RPC. + // It is valid only if Client is true. + Authority string // The following fields are valid only if Client is true. // FullMethod is the full RPC method string, i.e., /package.service/method. diff --git a/stats/stats_test.go b/stats/stats_test.go index cc33062260af..86faba1bdd7d 100644 --- a/stats/stats_test.go +++ b/stats/stats_test.go @@ -294,10 +294,11 @@ const ( ) type rpcConfig struct { - count int // Number of requests and responses for streaming RPCs. - success bool // Whether the RPC should succeed or return error. - failfast bool - callType rpcType // Type of RPC. + count int // Number of requests and responses for streaming RPCs. + success bool // Whether the RPC should succeed or return error. + failfast bool + authority string // Authority override for the RPC. + callType rpcType // Type of RPC. } func (te *test) doUnaryCall(c *rpcConfig) (*testpb.SimpleRequest, *testpb.SimpleResponse, error) { @@ -315,7 +316,11 @@ func (te *test) doUnaryCall(c *rpcConfig) (*testpb.SimpleRequest, *testpb.Simple req = &testpb.SimpleRequest{Payload: idToPayload(errorID)} } - resp, err = tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testMetadata), req, grpc.WaitForReady(!c.failfast)) + callOpts := []grpc.CallOption{grpc.WaitForReady(!c.failfast)} + if c.authority != "" { + callOpts = append(callOpts, grpc.CallAuthority(c.authority)) + } + resp, err = tc.UnaryCall(metadata.NewOutgoingContext(tCtx, testMetadata), req, callOpts...) return req, resp, err } @@ -427,6 +432,7 @@ type expectedData struct { isServerStream bool serverAddr string compression string + authority string reqIdx int requests []proto.Message respIdx int @@ -620,6 +626,13 @@ func checkOutHeader(t *testing.T, d *gotData, e *expectedData) { if st.RemoteAddr.String() != e.serverAddr { t.Fatalf("st.RemoteAddr = %v, want %v", st.RemoteAddr, e.serverAddr) } + wantAuthority := e.authority + if wantAuthority == "" { + wantAuthority = e.serverAddr + } + if st.Authority != wantAuthority { + t.Fatalf("st.Authority = %s, want %s", st.Authority, wantAuthority) + } // additional headers might be injected so instead of testing equality, test that all the // expected headers keys have the expected header values. for key := range testMetadata { @@ -1210,6 +1223,7 @@ func testClientStats(t *testing.T, tc *testConfig, cc *rpcConfig, checkFuncs map err: err, isClientStream: isClientStream, isServerStream: isServerStream, + authority: cc.authority, } h.mu.Lock() @@ -1230,6 +1244,23 @@ func (s) TestClientStatsUnaryRPC(t *testing.T) { }) } +func (s) TestClientStatsUnaryRPCAuthority(t *testing.T) { + testClientStats(t, &testConfig{compress: ""}, &rpcConfig{ + success: true, + failfast: false, + authority: "authority-override.example.com", + callType: unaryRPC, + }, map[int]*checkFuncWithCount{ + begin: {checkBegin, 1}, + outHeader: {checkOutHeader, 1}, + outPayload: {checkOutPayload, 1}, + inHeader: {checkInHeader, 1}, + inPayload: {checkInPayload, 1}, + inTrailer: {checkInTrailer, 1}, + end: {checkEnd, 1}, + }) +} + func (s) TestClientStatsUnaryRPCError(t *testing.T) { testClientStats(t, &testConfig{compress: ""}, &rpcConfig{success: false, failfast: false, callType: unaryRPC}, map[int]*checkFuncWithCount{ begin: {checkBegin, 1},