diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs index 40447854f47..3b186c82796 100644 --- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs @@ -32,19 +32,21 @@ namespace Grpc.IntegrationTesting public class MetadataCredentialsTest { const string Host = "localhost"; + + FakeTestService serviceImpl; Server server; Channel channel; TestService.TestServiceClient client; List options; - AsyncAuthInterceptor asyncAuthInterceptor; [SetUp] public void Init() { + serviceImpl = new FakeTestService(); // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) { - Services = { TestService.BindService(new FakeTestService()) }, + Services = { TestService.BindService(serviceImpl) }, Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } } }; server.Start(); @@ -53,12 +55,6 @@ namespace Grpc.IntegrationTesting { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - - asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => - { - await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous. - metadata.Add("authorization", "SECRET_TOKEN"); - }); } [TearDown] @@ -69,8 +65,21 @@ namespace Grpc.IntegrationTesting } [Test] - public void MetadataCredentials() + public void MetadataCredentials_Channel() { + serviceImpl.UnaryCallHandler = (req, context) => + { + var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value; + Assert.AreEqual("SECRET_TOKEN", authToken); + return Task.FromResult(new SimpleResponse()); + }; + + var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => + { + await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous. + metadata.Add("authorization", "SECRET_TOKEN"); + }); + var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), CallCredentials.FromInterceptor(asyncAuthInterceptor)); channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options); @@ -82,6 +91,19 @@ namespace Grpc.IntegrationTesting [Test] public void MetadataCredentials_PerCall() { + serviceImpl.UnaryCallHandler = (req, context) => + { + var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value; + Assert.AreEqual("SECRET_TOKEN", authToken); + return Task.FromResult(new SimpleResponse()); + }; + + var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => + { + await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous. + metadata.Add("authorization", "SECRET_TOKEN"); + }); + channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options); client = new TestService.TestServiceClient(channel); @@ -89,9 +111,53 @@ namespace Grpc.IntegrationTesting client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials)); } + [Test] + public void MetadataCredentials_BothChannelAndPerCall() + { + serviceImpl.UnaryCallHandler = (req, context) => + { + var firstAuth = context.RequestHeaders.First((entry) => entry.Key == "first_authorization").Value; + Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth); + var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value; + Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth); + // both values of "duplicate_authorization" are sent + Assert.AreEqual("value1", context.RequestHeaders.First((entry) => entry.Key == "duplicate_authorization").Value); + Assert.AreEqual("value2", context.RequestHeaders.Last((entry) => entry.Key == "duplicate_authorization").Value); + return Task.FromResult(new SimpleResponse()); + }; + + var channelCallCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => { + metadata.Add("first_authorization", "FIRST_SECRET_TOKEN"); + metadata.Add("duplicate_authorization", "value1"); + return TaskUtils.CompletedTask; + })); + var perCallCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => { + metadata.Add("second_authorization", "SECOND_SECRET_TOKEN"); + metadata.Add("duplicate_authorization", "value2"); + return TaskUtils.CompletedTask; + })); + + var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), channelCallCredentials); + channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options); + client = new TestService.TestServiceClient(channel); + + client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: perCallCredentials)); + } + [Test] public async Task MetadataCredentials_Composed() { + serviceImpl.StreamingOutputCallHandler = async (req, responseStream, context) => + { + var firstAuth = context.RequestHeaders.Last((entry) => entry.Key == "first_authorization").Value; + Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth); + var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value; + Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth); + var thirdAuth = context.RequestHeaders.First((entry) => entry.Key == "third_authorization").Value; + Assert.AreEqual("THIRD_SECRET_TOKEN", thirdAuth); + await responseStream.WriteAsync(new StreamingOutputCallResponse()); + }; + var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => { // Attempt to exercise the case where async callback is inlineable/synchronously-runnable. metadata.Add("first_authorization", "FIRST_SECRET_TOKEN"); @@ -117,6 +183,15 @@ namespace Grpc.IntegrationTesting [Test] public async Task MetadataCredentials_ComposedPerCall() { + serviceImpl.StreamingOutputCallHandler = async (req, responseStream, context) => + { + var firstAuth = context.RequestHeaders.Last((entry) => entry.Key == "first_authorization").Value; + Assert.AreEqual("FIRST_SECRET_TOKEN", firstAuth); + var secondAuth = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value; + Assert.AreEqual("SECOND_SECRET_TOKEN", secondAuth); + await responseStream.WriteAsync(new StreamingOutputCallResponse()); + }; + channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options); var client = new TestService.TestServiceClient(channel); var first = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => { @@ -127,12 +202,8 @@ namespace Grpc.IntegrationTesting metadata.Add("second_authorization", "SECOND_SECRET_TOKEN"); return TaskUtils.CompletedTask; })); - var third = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => { - metadata.Add("third_authorization", "THIRD_SECRET_TOKEN"); - return TaskUtils.CompletedTask; - })); var call = client.StreamingOutputCall(new StreamingOutputCallRequest{ }, - new CallOptions(credentials: CallCredentials.Compose(first, second, third))); + new CallOptions(credentials: CallCredentials.Compose(first, second))); Assert.IsTrue(await call.ResponseStream.MoveNext()); Assert.IsFalse(await call.ResponseStream.MoveNext()); } @@ -140,14 +211,17 @@ namespace Grpc.IntegrationTesting [Test] public void MetadataCredentials_InterceptorLeavesMetadataEmpty() { + serviceImpl.UnaryCallHandler = (req, context) => + { + var authHeaderCount = context.RequestHeaders.Count((entry) => entry.Key == "authorization"); + Assert.AreEqual(0, authHeaderCount); + return Task.FromResult(new SimpleResponse()); + }; var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) => TaskUtils.CompletedTask))); channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options); client = new TestService.TestServiceClient(channel); - - var ex = Assert.Throws(() => client.UnaryCall(new SimpleRequest { })); - // StatusCode.Unknown as the server-side handler throws an exception after not receiving the authorization header. - Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); + client.UnaryCall(new SimpleRequest { }); } [Test] @@ -169,22 +243,26 @@ namespace Grpc.IntegrationTesting private class FakeTestService : TestService.TestServiceBase { + public UnaryServerMethod UnaryCallHandler; + + public ServerStreamingServerMethod StreamingOutputCallHandler; + public override Task UnaryCall(SimpleRequest request, ServerCallContext context) { - var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value; - Assert.AreEqual("SECRET_TOKEN", authToken); - return Task.FromResult(new SimpleResponse()); + if (UnaryCallHandler != null) + { + return UnaryCallHandler(request, context); + } + return base.UnaryCall(request, context); } - public override async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter responseStream, ServerCallContext context) + public override Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter responseStream, ServerCallContext context) { - var first = context.RequestHeaders.First((entry) => entry.Key == "first_authorization").Value; - Assert.AreEqual("FIRST_SECRET_TOKEN", first); - var second = context.RequestHeaders.First((entry) => entry.Key == "second_authorization").Value; - Assert.AreEqual("SECOND_SECRET_TOKEN", second); - var third = context.RequestHeaders.First((entry) => entry.Key == "third_authorization").Value; - Assert.AreEqual("THIRD_SECRET_TOKEN", third); - await responseStream.WriteAsync(new StreamingOutputCallResponse()); + if (StreamingOutputCallHandler != null) + { + return StreamingOutputCallHandler(request, responseStream, context); + } + return base.StreamingOutputCall(request, responseStream, context); } } }