From 0175e18133bf8cd74363ef08b8f3485038e3a36d Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 3 Aug 2015 18:10:19 -0700 Subject: tolerate occasional StatusCode.Internal on timeout --- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 010ffd898a..a09273b846 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -134,7 +134,8 @@ namespace Grpc.Core.Tests } catch (RpcException e) { - Assert.AreEqual(StatusCode.DeadlineExceeded, e.Status.StatusCode); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } } @@ -151,7 +152,8 @@ namespace Grpc.Core.Tests } catch (RpcException e) { - Assert.AreEqual(StatusCode.DeadlineExceeded, e.Status.StatusCode); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } } @@ -168,7 +170,8 @@ namespace Grpc.Core.Tests } catch (RpcException e) { - Assert.AreEqual(StatusCode.DeadlineExceeded, e.Status.StatusCode); + // We can't guarantee the status code is always DeadlineExceeded. See issue #2685. + Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } Assert.AreEqual("CANCELLED", stringFromServerHandlerTcs.Task.Result); } -- cgit v1.2.3 From d8bbdeae42ce6ae6077e4e5b4b4f4c673acecf57 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 22 Jul 2015 12:51:06 -0700 Subject: Added channel state API --- src/csharp/Grpc.Auth/OAuth2Interceptors.cs | 2 - src/csharp/Grpc.Core.Tests/ChannelTest.cs | 82 ++++++++++++++++++++++ src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 24 +++++++ src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 + src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs | 4 +- src/csharp/Grpc.Core/Channel.cs | 74 +++++++++++++++++-- src/csharp/Grpc.Core/ChannelState.cs | 69 ++++++++++++++++++ src/csharp/Grpc.Core/Grpc.Core.csproj | 1 + src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs | 20 ++++++ src/csharp/ext/grpc_csharp_ext.c | 12 ++++ 10 files changed, 280 insertions(+), 9 deletions(-) create mode 100644 src/csharp/Grpc.Core.Tests/ChannelTest.cs create mode 100644 src/csharp/Grpc.Core/ChannelState.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs index c785ca5a16..cc9d2c175f 100644 --- a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs +++ b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs @@ -119,7 +119,5 @@ namespace Grpc.Auth return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken); } } - - } } diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs new file mode 100644 index 0000000000..bfe001b292 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -0,0 +1,82 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ChannelTest + { + [TestFixtureTearDown] + public void CleanupClass() + { + GrpcEnvironment.Shutdown(); + } + + [Test] + public void Constructor_RejectsInvalidParams() + { + Assert.Throws(typeof(NullReferenceException), () => new Channel(null, Credentials.Insecure)); + } + + [Test] + public void State_IdleAfterCreation() + { + using (var channel = new Channel("localhost", Credentials.Insecure)) + { + Assert.AreEqual(ChannelState.Idle, channel.State); + } + } + + [Test] + public void WaitForStateChangedAsync_InvalidArgument() + { + using (var channel = new Channel("localhost", Credentials.Insecure)) + { + Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure)); + } + } + + [Test] + public void Dispose_IsIdempotent() + { + var channel = new Channel("localhost", Credentials.Insecure); + channel.Dispose(); + channel.Dispose(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 540fe756c0..35924868ca 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -276,6 +276,30 @@ namespace Grpc.Core.Tests Assert.IsTrue(peer.Contains(Host)); } + [Test] + public async Task Channel_WaitForStateChangedAsync() + { + Assert.Throws(typeof(TaskCanceledException), + async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10))); + + var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); + + var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + await Calls.AsyncUnaryCall(internalCall, "abc", CancellationToken.None); + + await stateChangedTask; + Assert.AreEqual(ChannelState.Ready, channel.State); + } + + [Test] + public async Task Channel_ConnectAsync() + { + await channel.ConnectAsync(); + Assert.AreEqual(ChannelState.Ready, channel.State); + await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000)); + Assert.AreEqual(ChannelState.Ready, channel.State); + } + private static async Task EchoHandler(string request, ServerCallContext context) { foreach (Metadata.Entry metadataEntry in context.RequestHeaders) diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 242a60d098..f2bf459dc5 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -76,6 +76,7 @@ + diff --git a/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs index 600df1a18d..3fa6ad09c0 100644 --- a/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs +++ b/src/csharp/Grpc.Core.Tests/NUnitVersionTest.cs @@ -70,10 +70,8 @@ namespace Grpc.Core.Tests [Test] public async Task NUnitVersionTest2() { - testRunCount ++; + testRunCount++; await Task.Delay(10); } - - } } diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 18e6f2fda5..8a71afa01a 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -37,6 +37,8 @@ using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; +using Grpc.Core.Logging; +using Grpc.Core.Utils; namespace Grpc.Core { @@ -45,6 +47,8 @@ namespace Grpc.Core /// public class Channel : IDisposable { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; readonly List options; @@ -53,13 +57,14 @@ namespace Grpc.Core /// /// Creates a channel that connects to a specific host. - /// Port will default to 80 for an unsecure channel and to 443 a secure channel. + /// Port will default to 80 for an unsecure channel and to 443 for a secure channel. /// - /// The DNS name of IP address of the host. + /// The name or IP address of the host. /// Credentials to secure the channel. /// Channel options. public Channel(string host, Credentials credentials, IEnumerable options = null) { + Preconditions.CheckNotNull(host); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List(options) : new List(); @@ -82,8 +87,8 @@ namespace Grpc.Core /// /// Creates a channel that connects to a specific host and port. /// - /// DNS name or IP address - /// the port + /// The name or IP address of the host. + /// The port. /// Credentials to secure the channel. /// Channel options. public Channel(string host, int port, Credentials credentials, IEnumerable options = null) : @@ -91,6 +96,67 @@ namespace Grpc.Core { } + /// + /// Gets current connectivity state of this channel. + /// + public ChannelState State + { + get + { + return handle.CheckConnectivityState(false); + } + } + + /// + /// Returned tasks completes once channel state has become different from + /// given lastObservedState. + /// If deadline is reached or and error occurs, returned task is cancelled. + /// + public Task WaitForStateChangedAsync(ChannelState lastObservedState, DateTime? deadline = null) + { + Preconditions.CheckArgument(lastObservedState != ChannelState.FatalFailure, + "FatalFailure is a terminal state. No further state changes can occur."); + var tcs = new TaskCompletionSource(); + var deadlineTimespec = deadline.HasValue ? Timespec.FromDateTime(deadline.Value) : Timespec.InfFuture; + var handler = new BatchCompletionDelegate((success, ctx) => + { + if (success) + { + tcs.SetResult(null); + } + else + { + tcs.SetCanceled(); + } + }); + handle.WatchConnectivityState(lastObservedState, deadlineTimespec, environment.CompletionQueue, environment.CompletionRegistry, handler); + return tcs.Task; + } + + /// + /// Allows explicitly requesting channel to connect without starting an RPC. + /// Returned task completes once state Ready was seen. If the deadline is reached, + /// or channel enters the FatalFailure state, the task is cancelled. + /// There is no need to call this explicitly unless your use case requires that. + /// Starting an RPC on a new channel will request connection implicitly. + /// + public async Task ConnectAsync(DateTime? deadline = null) + { + var currentState = handle.CheckConnectivityState(true); + while (currentState != ChannelState.Ready) + { + if (currentState == ChannelState.FatalFailure) + { + throw new OperationCanceledException("Channel has reached FatalFailure state."); + } + await WaitForStateChangedAsync(currentState, deadline); + currentState = handle.CheckConnectivityState(false); + } + } + + /// + /// Destroys the underlying channel. + /// public void Dispose() { Dispose(true); diff --git a/src/csharp/Grpc.Core/ChannelState.cs b/src/csharp/Grpc.Core/ChannelState.cs new file mode 100644 index 0000000000..d293b98f75 --- /dev/null +++ b/src/csharp/Grpc.Core/ChannelState.cs @@ -0,0 +1,69 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; + +namespace Grpc.Core +{ + /// + /// Connectivity state of a channel. + /// Based on grpc_connectivity_state from grpc/grpc.h + /// + public enum ChannelState + { + /// + /// Channel is idle + /// + Idle, + + /// + /// Channel is connecting + /// + Connecting, + + /// + /// Channel is ready for work + /// + Ready, + + /// + /// Channel has seen a failure but expects to recover + /// + TransientFailure, + + /// + /// Channel has seen a failure that it cannot recover from + /// + FatalFailure + } +} diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 940a6b8ac0..641b54baba 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -115,6 +115,7 @@ + diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index 20815efbd3..c017560b56 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -49,6 +49,13 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, + Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_channel_destroy(IntPtr channel); @@ -73,6 +80,19 @@ namespace Grpc.Core.Internal return result; } + public ChannelState CheckConnectivityState(bool tryToConnect) + { + return grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0); + } + + public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, + CompletionRegistry completionRegistry, BatchCompletionDelegate callback) + { + var ctx = BatchContextSafeHandle.Create(); + completionRegistry.RegisterBatchCompletion(ctx, callback); + grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx); + } + protected override bool ReleaseHandle() { grpcsharp_channel_destroy(handle); diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 49a0471042..0e3a83d244 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -382,6 +382,18 @@ grpcsharp_channel_create_call(grpc_channel *channel, grpc_completion_queue *cq, return grpc_channel_create_call(channel, cq, method, host, deadline); } +GPR_EXPORT grpc_connectivity_state GPR_CALLTYPE +grpcsharp_channel_check_connectivity_state(grpc_channel *channel, gpr_int32 try_to_connect) { + return grpc_channel_check_connectivity_state(channel, try_to_connect); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, grpcsharp_batch_context *ctx) { + grpc_channel_watch_connectivity_state(channel, last_observed_state, NULL, + deadline, cq, ctx); +} + /* Channel args */ GPR_EXPORT grpc_channel_args *GPR_CALLTYPE -- cgit v1.2.3 From dead905b87c159ce8af7252c17f05cb3e40e7826 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sat, 1 Aug 2015 21:34:31 -0700 Subject: expose Channel.Target property --- src/csharp/Grpc.Core.Tests/ChannelTest.cs | 9 +++++++++ src/csharp/Grpc.Core/Channel.cs | 9 +++++++++ src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs | 11 +++++++++++ src/csharp/ext/grpc_csharp_ext.c | 4 ++++ 4 files changed, 33 insertions(+) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs index bfe001b292..60b45176e5 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -71,6 +71,15 @@ namespace Grpc.Core.Tests } } + [Test] + public void Target() + { + using (var channel = new Channel("127.0.0.1", Credentials.Insecure)) + { + Assert.IsTrue(channel.Target.Contains("127.0.0.1")); + } + } + [Test] public void Dispose_IsIdempotent() { diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 04fea8c924..0b69610443 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -131,6 +131,15 @@ namespace Grpc.Core return tcs.Task; } + /// Address of the remote endpoint in URI format. + public string Target + { + get + { + return handle.GetTarget(); + } + } + /// /// Allows explicitly requesting channel to connect without starting an RPC. /// Returned task completes once state Ready was seen. If the deadline is reached, diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index c017560b56..7324ebdf57 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -56,6 +56,9 @@ namespace Grpc.Core.Internal static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + [DllImport("grpc_csharp_ext.dll")] + static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_channel_destroy(IntPtr channel); @@ -93,6 +96,14 @@ namespace Grpc.Core.Internal grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx); } + public string GetTarget() + { + using (var cstring = grpcsharp_channel_get_target(this)) + { + return cstring.GetValue(); + } + } + protected override bool ReleaseHandle() { grpcsharp_channel_destroy(handle); diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 0e3a83d244..8e2a5f0269 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -394,6 +394,10 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_watch_connectivity_state( deadline, cq, ctx); } +GPR_EXPORT char *GPR_CALLTYPE grpcsharp_channel_get_target(grpc_channel *channel) { + return grpc_channel_get_target(channel); +} + /* Channel args */ GPR_EXPORT grpc_channel_args *GPR_CALLTYPE -- cgit v1.2.3 From 021df8a7f23c00216d1a544b34d4da2222054f0a Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 4 Aug 2015 20:31:11 -0700 Subject: changed way service definitions are added to the server --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 6 +- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 6 +- src/csharp/Grpc.Core/Server.cs | 68 ++++++++++++++++++---- src/csharp/Grpc.Examples.MathServer/MathServer.cs | 6 +- .../Grpc.Examples.Tests/MathClientServerTests.cs | 6 +- .../HealthClientServerTest.cs | 6 +- .../InteropClientServerTest.cs | 6 +- .../Grpc.IntegrationTesting/InteropServer.cs | 6 +- .../Grpc.IntegrationTesting/SslCredentialsTest.cs | 6 +- 9 files changed, 89 insertions(+), 27 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 35924868ca..c051fffbdd 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -77,8 +77,10 @@ namespace Grpc.Core.Tests [SetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(ServiceDefinition); + server = new Server() + { + Services = { ServiceDefinition } + }; int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); channel = new Channel(Host, port, Credentials.Insecure); diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index a09273b846..9125bcc6f8 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -70,8 +70,10 @@ namespace Grpc.Core.Tests [SetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(ServiceDefinition); + server = new Server() + { + Services = { ServiceDefinition } + }; int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); channel = new Channel(Host, port, Credentials.Insecure); diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 3217547cc4..059ff7a2f5 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -32,7 +32,7 @@ #endregion using System; -using System.Collections.Concurrent; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; @@ -55,11 +55,13 @@ namespace Grpc.Core static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); + readonly ServiceDefinitionCollection serviceDefinitions; readonly GrpcEnvironment environment; readonly List options; readonly ServerSafeHandle handle; readonly object myLock = new object(); + readonly List serviceDefinitionsList = new List(); readonly Dictionary callHandlers = new Dictionary(); readonly TaskCompletionSource shutdownTcs = new TaskCompletionSource(); @@ -72,6 +74,7 @@ namespace Grpc.Core /// Channel options. public Server(IEnumerable options = null) { + this.serviceDefinitions = new ServiceDefinitionCollection(this); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List(options) : new List(); using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options)) @@ -81,19 +84,14 @@ namespace Grpc.Core } /// - /// Adds a service definition to the server. This is how you register - /// handlers for a service with the server. - /// Only call this before Start(). + /// Services that will be exported by the server once started. Register a service with this + /// server by adding its definition to this collection. /// - public void AddServiceDefinition(ServerServiceDefinition serviceDefinition) + public ServiceDefinitionCollection Services { - lock (myLock) + get { - Preconditions.CheckState(!startRequested); - foreach (var entry in serviceDefinition.CallHandlers) - { - callHandlers.Add(entry.Key, entry.Value); - } + return serviceDefinitions; } } @@ -189,6 +187,22 @@ namespace Grpc.Core handle.Dispose(); } + /// + /// Adds a service definition. + /// + private void AddServiceDefinitionInternal(ServerServiceDefinition serviceDefinition) + { + lock (myLock) + { + Preconditions.CheckState(!startRequested); + foreach (var entry in serviceDefinition.CallHandlers) + { + callHandlers.Add(entry.Key, entry.Value); + } + serviceDefinitionsList.Add(serviceDefinition); + } + } + /// /// Allows one new RPC call to be received by server. /// @@ -249,5 +263,37 @@ namespace Grpc.Core { shutdownTcs.SetResult(null); } + + /// + /// Collection of service definitions. + /// + public class ServiceDefinitionCollection : IEnumerable + { + readonly Server server; + + internal ServiceDefinitionCollection(Server server) + { + this.server = server; + } + + /// + /// Adds a service definition to the server. This is how you register + /// handlers for a service with the server. Only call this before Start(). + /// + public void Add(ServerServiceDefinition serviceDefinition) + { + server.AddServiceDefinitionInternal(serviceDefinition); + } + + public IEnumerator GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + } } } diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs index 468eefbe3e..4d6b43e5d3 100644 --- a/src/csharp/Grpc.Examples.MathServer/MathServer.cs +++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs @@ -42,8 +42,10 @@ namespace math { string host = "0.0.0.0"; - Server server = new Server(); - server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); + Server server = new Server() + { + Services = { Math.BindService(new MathServiceImpl()) }, + }; int port = server.AddPort(host, 23456, ServerCredentials.Insecure); server.Start(); diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index 242d29a9a5..080e733523 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -54,8 +54,10 @@ namespace math.Tests [TestFixtureSetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(Math.BindService(new MathServiceImpl())); + server = new Server() + { + Services = { Math.BindService(new MathServiceImpl()) } + }; int port = server.AddPort(host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); channel = new Channel(host, port, Credentials.Insecure); diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs index 9d89698a8f..50b1908fc8 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -57,8 +57,10 @@ namespace Grpc.HealthCheck.Tests { serviceImpl = new HealthServiceImpl(); - server = new Server(); - server.AddServiceDefinition(Grpc.Health.V1Alpha.Health.BindService(serviceImpl)); + server = new Server() + { + Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) } + }; int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); channel = new Channel(Host, port, Credentials.Insecure); diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 2756ce97aa..ab38fc8858 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -55,8 +55,10 @@ namespace Grpc.IntegrationTesting [TestFixtureSetUp] public void Init() { - server = new Server(); - server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); + server = new Server() + { + Services = { TestService.BindService(new TestServiceImpl()) } + }; int port = server.AddPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials()); server.Start(); diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index bf6947e09d..05058d6aad 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -88,8 +88,10 @@ namespace Grpc.IntegrationTesting private void Run() { - var server = new Server(); - server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); + var server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) } + }; string host = "0.0.0.0"; int port = options.port.Value; diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs index 3069dceced..7c553d5fa0 100644 --- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -65,8 +65,10 @@ namespace Grpc.IntegrationTesting var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true); var clientCredentials = new SslCredentials(rootCert, keyCertPair); - server = new Server(); - server.AddServiceDefinition(TestService.BindService(new TestServiceImpl())); + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) } + }; int port = server.AddPort(host, Server.PickUnusedPort, serverCredentials); server.Start(); -- cgit v1.2.3 From 31ba0632247993b02b552294691a354ee5d0ef08 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 4 Aug 2015 22:02:55 -0700 Subject: changed the way ports are added to the server --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 8 +- src/csharp/Grpc.Core.Tests/ServerTest.cs | 39 ++++++- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 8 +- src/csharp/Grpc.Core/Grpc.Core.csproj | 1 + src/csharp/Grpc.Core/Server.cs | 107 +++++++++++++----- src/csharp/Grpc.Core/ServerPort.cs | 120 +++++++++++++++++++++ src/csharp/Grpc.Examples.MathServer/MathServer.cs | 11 +- .../Grpc.Examples.Tests/MathClientServerTests.cs | 18 ++-- .../HealthClientServerTest.cs | 8 +- .../InteropClientServerTest.cs | 12 ++- .../Grpc.IntegrationTesting/InteropServer.cs | 4 +- .../Grpc.IntegrationTesting/SslCredentialsTest.cs | 9 +- 12 files changed, 277 insertions(+), 68 deletions(-) create mode 100644 src/csharp/Grpc.Core/ServerPort.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index c051fffbdd..bf7cc3fbf3 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -77,13 +77,13 @@ namespace Grpc.Core.Tests [SetUp] public void Init() { - server = new Server() + server = new Server { - Services = { ServiceDefinition } + Services = { ServiceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; - int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); - channel = new Channel(Host, port, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); } [TearDown] diff --git a/src/csharp/Grpc.Core.Tests/ServerTest.cs b/src/csharp/Grpc.Core.Tests/ServerTest.cs index ba9efae871..485006ebac 100644 --- a/src/csharp/Grpc.Core.Tests/ServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ServerTest.cs @@ -32,6 +32,7 @@ #endregion using System; +using System.Linq; using Grpc.Core; using Grpc.Core.Internal; using Grpc.Core.Utils; @@ -44,11 +45,45 @@ namespace Grpc.Core.Tests [Test] public void StartAndShutdownServer() { - Server server = new Server(); - server.AddPort("localhost", Server.PickUnusedPort, ServerCredentials.Insecure); + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; server.Start(); server.ShutdownAsync().Wait(); GrpcEnvironment.Shutdown(); } + + [Test] + public void PickUnusedPort() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + + var boundPort = server.Ports.Single(); + Assert.AreEqual(0, boundPort.Port); + Assert.Greater(boundPort.BoundPort, 0); + + server.Start(); + server.ShutdownAsync(); + GrpcEnvironment.Shutdown(); + } + + [Test] + public void CannotModifyAfterStarted() + { + Server server = new Server + { + Ports = { new ServerPort("localhost", ServerPort.PickUnused, ServerCredentials.Insecure) } + }; + server.Start(); + Assert.Throws(typeof(InvalidOperationException), () => server.Ports.Add("localhost", 9999, ServerCredentials.Insecure)); + Assert.Throws(typeof(InvalidOperationException), () => server.Services.Add(ServerServiceDefinition.CreateBuilder("serviceName").Build())); + + server.ShutdownAsync().Wait(); + GrpcEnvironment.Shutdown(); + } } } diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 9125bcc6f8..d84801fbac 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -70,13 +70,13 @@ namespace Grpc.Core.Tests [SetUp] public void Init() { - server = new Server() + server = new Server { - Services = { ServiceDefinition } + Services = { ServiceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; - int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); - channel = new Channel(Host, port, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); stringFromServerHandlerTcs = new TaskCompletionSource(); } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index ea06c0f10d..17add77164 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -52,6 +52,7 @@ + diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 059ff7a2f5..eb5b043d1c 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -48,20 +48,17 @@ namespace Grpc.Core /// public class Server { - /// - /// Pass this value as port to have the server choose an unused listening port for you. - /// - public const int PickUnusedPort = 0; - static readonly ILogger Logger = GrpcEnvironment.Logger.ForType(); readonly ServiceDefinitionCollection serviceDefinitions; + readonly ServerPortCollection ports; readonly GrpcEnvironment environment; readonly List options; readonly ServerSafeHandle handle; readonly object myLock = new object(); readonly List serviceDefinitionsList = new List(); + readonly List serverPortList = new List(); readonly Dictionary callHandlers = new Dictionary(); readonly TaskCompletionSource shutdownTcs = new TaskCompletionSource(); @@ -75,6 +72,7 @@ namespace Grpc.Core public Server(IEnumerable options = null) { this.serviceDefinitions = new ServiceDefinitionCollection(this); + this.ports = new ServerPortCollection(this); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List(options) : new List(); using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options)) @@ -96,30 +94,14 @@ namespace Grpc.Core } /// - /// Add a port on which server should listen. - /// Only call this before Start(). + /// Ports on which the server will listen once started. Register a port with this + /// server by adding its definition to this collection. /// - /// The port on which server will be listening. - /// the host - /// the port. If zero, an unused port is chosen automatically. - public int AddPort(string host, int port, ServerCredentials credentials) + public ServerPortCollection Ports { - lock (myLock) + get { - Preconditions.CheckNotNull(credentials); - Preconditions.CheckState(!startRequested); - var address = string.Format("{0}:{1}", host, port); - using (var nativeCredentials = credentials.ToNativeCredentials()) - { - if (nativeCredentials != null) - { - return handle.AddSecurePort(address, nativeCredentials); - } - else - { - return handle.AddInsecurePort(address); - } - } + return ports; } } @@ -203,6 +185,34 @@ namespace Grpc.Core } } + /// + /// Adds a listening port. + /// + private int AddPortInternal(ServerPort serverPort) + { + lock (myLock) + { + Preconditions.CheckNotNull(serverPort.Credentials); + Preconditions.CheckState(!startRequested); + var address = string.Format("{0}:{1}", serverPort.Host, serverPort.Port); + int boundPort; + using (var nativeCredentials = serverPort.Credentials.ToNativeCredentials()) + { + if (nativeCredentials != null) + { + boundPort = handle.AddSecurePort(address, nativeCredentials); + } + else + { + boundPort = handle.AddInsecurePort(address); + } + } + var newServerPort = new ServerPort(serverPort, boundPort); + this.serverPortList.Add(newServerPort); + return boundPort; + } + } + /// /// Allows one new RPC call to be received by server. /// @@ -295,5 +305,50 @@ namespace Grpc.Core return server.serviceDefinitionsList.GetEnumerator(); } } + + /// + /// Collection of server ports. + /// + public class ServerPortCollection : IEnumerable + { + readonly Server server; + + internal ServerPortCollection(Server server) + { + this.server = server; + } + + /// + /// Adds a new port on which server should listen. + /// Only call this before Start(). + /// The port on which server will be listening. + /// + public int Add(ServerPort serverPort) + { + return server.AddPortInternal(serverPort); + } + + /// + /// Adds a new port on which server should listen. + /// The port on which server will be listening. + /// + /// the host + /// the port. If zero, an unused port is chosen automatically. + /// credentials to use to secure this port. + public int Add(string host, int port, ServerCredentials credentials) + { + return Add(new ServerPort(host, port, credentials)); + } + + public IEnumerator GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + } } } diff --git a/src/csharp/Grpc.Core/ServerPort.cs b/src/csharp/Grpc.Core/ServerPort.cs new file mode 100644 index 0000000000..55e4bd0062 --- /dev/null +++ b/src/csharp/Grpc.Core/ServerPort.cs @@ -0,0 +1,120 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; + +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// A port exposed by a server. + /// + public class ServerPort + { + /// + /// Pass this value as port to have the server choose an unused listening port for you. + /// Ports added to a server will contain the bound port in their property. + /// + public const int PickUnused = 0; + + readonly string host; + readonly int port; + readonly ServerCredentials credentials; + readonly int boundPort; + + /// + /// Creates a new port on which server should listen. + /// + /// The port on which server will be listening. + /// the host + /// the port. If zero, an unused port is chosen automatically. + /// credentials to use to secure this port. + public ServerPort(string host, int port, ServerCredentials credentials) + { + this.host = Preconditions.CheckNotNull(host); + this.port = port; + this.credentials = Preconditions.CheckNotNull(credentials); + } + + /// + /// Creates a port from an existing ServerPort instance and boundPort value. + /// + internal ServerPort(ServerPort serverPort, int boundPort) + { + this.host = serverPort.host; + this.port = serverPort.port; + this.credentials = serverPort.credentials; + this.boundPort = boundPort; + } + + /// The host. + public string Host + { + get + { + return host; + } + } + + /// The port. + public int Port + { + get + { + return port; + } + } + + /// The server credentials. + public ServerCredentials Credentials + { + get + { + return credentials; + } + } + + /// + /// The port actually bound by the server. This is useful if you let server + /// pick port automatically. + /// + public int BoundPort + { + get + { + return boundPort; + } + } + } +} diff --git a/src/csharp/Grpc.Examples.MathServer/MathServer.cs b/src/csharp/Grpc.Examples.MathServer/MathServer.cs index 4d6b43e5d3..5f7e717b0c 100644 --- a/src/csharp/Grpc.Examples.MathServer/MathServer.cs +++ b/src/csharp/Grpc.Examples.MathServer/MathServer.cs @@ -38,18 +38,19 @@ namespace math { class MainClass { + const string Host = "0.0.0.0"; + const int Port = 23456; + public static void Main(string[] args) { - string host = "0.0.0.0"; - - Server server = new Server() + Server server = new Server { Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, Port, ServerCredentials.Insecure } } }; - int port = server.AddPort(host, 23456, ServerCredentials.Insecure); server.Start(); - Console.WriteLine("MathServer listening on port " + port); + Console.WriteLine("MathServer listening on port " + Port); Console.WriteLine("Press any key to stop the server..."); Console.ReadKey(); diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs index 080e733523..08aece7ef2 100644 --- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs +++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Grpc.Core; @@ -46,7 +47,7 @@ namespace math.Tests /// public class MathClientServerTest { - string host = "localhost"; + const string Host = "localhost"; Server server; Channel channel; Math.MathClient client; @@ -54,21 +55,14 @@ namespace math.Tests [TestFixtureSetUp] public void Init() { - server = new Server() + server = new Server { - Services = { Math.BindService(new MathServiceImpl()) } + Services = { Math.BindService(new MathServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; - int port = server.AddPort(host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); - channel = new Channel(host, port, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); client = Math.NewClient(channel); - - // TODO(jtattermusch): get rid of the custom header here once we have dedicated tests - // for header support. - client.HeaderInterceptor = (metadata) => - { - metadata.Add(new Metadata.Entry("custom-header", "abcdef")); - }; } [TestFixtureTearDown] diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs index 50b1908fc8..024377e216 100644 --- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs +++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs @@ -57,13 +57,13 @@ namespace Grpc.HealthCheck.Tests { serviceImpl = new HealthServiceImpl(); - server = new Server() + server = new Server { - Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) } + Services = { Grpc.Health.V1Alpha.Health.BindService(serviceImpl) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } }; - int port = server.AddPort(Host, Server.PickUnusedPort, ServerCredentials.Insecure); server.Start(); - channel = new Channel(Host, port, Credentials.Insecure); + channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); client = Grpc.Health.V1Alpha.Health.NewClient(channel); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index ab38fc8858..6fa721bc1c 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using grpc.testing; @@ -47,7 +48,7 @@ namespace Grpc.IntegrationTesting /// public class InteropClientServerTest { - string host = "localhost"; + const string Host = "localhost"; Server server; Channel channel; TestService.ITestServiceClient client; @@ -55,18 +56,19 @@ namespace Grpc.IntegrationTesting [TestFixtureSetUp] public void Init() { - server = new Server() + server = new Server { - Services = { TestService.BindService(new TestServiceImpl()) } + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateTestServerCredentials() } } }; - int port = server.AddPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials()); server.Start(); var options = new List { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options); + int port = server.Ports.Single().BoundPort; + channel = new Channel(Host, port, TestCredentials.CreateTestClientCredentials(true), options); client = TestService.NewClient(channel); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index 05058d6aad..504fd11857 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -97,11 +97,11 @@ namespace Grpc.IntegrationTesting int port = options.port.Value; if (options.useTls) { - server.AddPort(host, port, TestCredentials.CreateTestServerCredentials()); + server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials()); } else { - server.AddPort(host, options.port.Value, ServerCredentials.Insecure); + server.Ports.Add(host, options.port.Value, ServerCredentials.Insecure); } Console.WriteLine("Running server on " + string.Format("{0}:{1}", host, port)); server.Start(); diff --git a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs index 7c553d5fa0..1c398eb84e 100644 --- a/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using grpc.testing; @@ -49,7 +50,7 @@ namespace Grpc.IntegrationTesting /// public class SslCredentialsTest { - string host = "localhost"; + const string Host = "localhost"; Server server; Channel channel; TestService.ITestServiceClient client; @@ -67,9 +68,9 @@ namespace Grpc.IntegrationTesting server = new Server { - Services = { TestService.BindService(new TestServiceImpl()) } + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, serverCredentials } } }; - int port = server.AddPort(host, Server.PickUnusedPort, serverCredentials); server.Start(); var options = new List @@ -77,7 +78,7 @@ namespace Grpc.IntegrationTesting new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - channel = new Channel(host, port, clientCredentials, options); + channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); client = TestService.NewClient(channel); } -- cgit v1.2.3 From 5e10f18376e07fd43c12bc2a14ccf3a0e0682660 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 00:13:02 -0700 Subject: introduce CallContext --- src/compiler/csharp_generator.cc | 76 +++++++++++++++++++--- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 63 +++++++++--------- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 24 +++---- src/csharp/Grpc.Core/Call.cs | 28 ++------ src/csharp/Grpc.Core/CallContext.cs | 89 ++++++++++++++++++++++++++ src/csharp/Grpc.Core/Calls.cs | 38 +++++------ src/csharp/Grpc.Core/Channel.cs | 16 ----- src/csharp/Grpc.Core/ClientBase.cs | 10 ++- src/csharp/Grpc.Core/Grpc.Core.csproj | 1 + src/csharp/Grpc.Core/Internal/AsyncCall.cs | 2 +- src/csharp/Grpc.Core/ServerCallContext.cs | 15 ++--- 11 files changed, 238 insertions(+), 124 deletions(-) create mode 100644 src/csharp/Grpc.Core/CallContext.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index e0c1bcda19..efd39e8ac5 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -273,6 +273,13 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { "methodname", method->name(), "request", GetClassName(method->input_type()), "response", GetClassName(method->output_type())); + + // overload taking CallContext as a param + out->Print( + "$response$ $methodname$($request$ request, CallContext context);\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); } std::string method_name = method->name(); @@ -284,6 +291,13 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { "methodname", method_name, "request_maybe", GetMethodRequestParamMaybe(method), "returntype", GetMethodReturnTypeClient(method)); + + // overload taking CallContext as a param + out->Print( + "$returntype$ $methodname$($request_maybe$CallContext context);\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); } out->Outdent(); out->Print("}\n"); @@ -340,10 +354,25 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { GetClassName(method->output_type())); out->Print("{\n"); out->Indent(); - out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers, deadline);\n", + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, new CallContext(headers, deadline, cancellationToken));\n", + "servicenamefield", GetServiceNameFieldName(), "methodfield", + GetMethodFieldName(method)); + out->Print("return Calls.BlockingUnaryCall(call, request);\n"); + out->Outdent(); + out->Print("}\n"); + + // overload taking CallContext as a param + out->Print( + "public $response$ $methodname$($request$ request, CallContext context)\n", + "methodname", method->name(), "request", + GetClassName(method->input_type()), "response", + GetClassName(method->output_type())); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, context);\n", "servicenamefield", GetServiceNameFieldName(), "methodfield", GetMethodFieldName(method)); - out->Print("return Calls.BlockingUnaryCall(call, request, cancellationToken);\n"); + out->Print("return Calls.BlockingUnaryCall(call, request);\n"); out->Outdent(); out->Print("}\n"); } @@ -359,26 +388,57 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { GetMethodReturnTypeClient(method)); out->Print("{\n"); out->Indent(); - out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers, deadline);\n", + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, new CallContext(headers, deadline, cancellationToken));\n", "servicenamefield", GetServiceNameFieldName(), "methodfield", GetMethodFieldName(method)); switch (GetMethodType(method)) { case METHODTYPE_NO_STREAMING: - out->Print("return Calls.AsyncUnaryCall(call, request, cancellationToken);\n"); + out->Print("return Calls.AsyncUnaryCall(call, request);\n"); break; case METHODTYPE_CLIENT_STREAMING: - out->Print("return Calls.AsyncClientStreamingCall(call, cancellationToken);\n"); + out->Print("return Calls.AsyncClientStreamingCall(call);\n"); break; case METHODTYPE_SERVER_STREAMING: out->Print( - "return Calls.AsyncServerStreamingCall(call, request, cancellationToken);\n"); + "return Calls.AsyncServerStreamingCall(call, request);\n"); break; case METHODTYPE_BIDI_STREAMING: - out->Print("return Calls.AsyncDuplexStreamingCall(call, cancellationToken);\n"); + out->Print("return Calls.AsyncDuplexStreamingCall(call);\n"); break; default: GOOGLE_LOG(FATAL)<< "Can't get here."; - } + } + out->Outdent(); + out->Print("}\n"); + + // overload taking CallContext as a param + out->Print( + "public $returntype$ $methodname$($request_maybe$CallContext context)\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method), "returntype", + GetMethodReturnTypeClient(method)); + out->Print("{\n"); + out->Indent(); + out->Print("var call = CreateCall($servicenamefield$, $methodfield$, context);\n", + "servicenamefield", GetServiceNameFieldName(), "methodfield", + GetMethodFieldName(method)); + switch (GetMethodType(method)) { + case METHODTYPE_NO_STREAMING: + out->Print("return Calls.AsyncUnaryCall(call, request);\n"); + break; + case METHODTYPE_CLIENT_STREAMING: + out->Print("return Calls.AsyncClientStreamingCall(call);\n"); + break; + case METHODTYPE_SERVER_STREAMING: + out->Print( + "return Calls.AsyncServerStreamingCall(call, request);\n"); + break; + case METHODTYPE_BIDI_STREAMING: + out->Print("return Calls.AsyncDuplexStreamingCall(call);\n"); + break; + default: + GOOGLE_LOG(FATAL)<< "Can't get here."; + } out->Outdent(); out->Print("}\n"); } diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index bf7cc3fbf3..d289ded6bd 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -102,17 +102,17 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None)); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "THROW", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "THROW"); Assert.Fail(); } catch (RpcException e) @@ -124,10 +124,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -139,10 +139,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -152,20 +152,20 @@ namespace Grpc.Core.Tests } [Test] - public void AsyncUnaryCall() + public async Task AsyncUnaryCall() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - var result = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None).ResponseAsync.Result; + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var result = await Calls.AsyncUnaryCall(internalCall, "ABC"); Assert.AreEqual("ABC", result); } [Test] public async Task AsyncUnaryCall_ServerHandlerThrows() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); try { - await Calls.AsyncUnaryCall(internalCall, "THROW", CancellationToken.None); + await Calls.AsyncUnaryCall(internalCall, "THROW"); Assert.Fail(); } catch (RpcException e) @@ -177,8 +177,8 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall() { - var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty); - var call = Calls.AsyncClientStreamingCall(internalCall, CancellationToken.None); + var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, new CallContext()); + var call = Calls.AsyncClientStreamingCall(internalCall); await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); @@ -187,10 +187,9 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall_CancelAfterBegin() { - var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, Metadata.Empty); - var cts = new CancellationTokenSource(); - var call = Calls.AsyncClientStreamingCall(internalCall, cts.Token); + var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, new CallContext(cancellationToken: cts.Token)); + var call = Calls.AsyncClientStreamingCall(internalCall); // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. await Task.Delay(1000); @@ -214,8 +213,8 @@ namespace Grpc.Core.Tests new Metadata.Entry("ascii-header", "abcdefg"), new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var internalCall = new Call(ServiceName, EchoMethod, channel, headers); - var call = Calls.AsyncUnaryCall(internalCall, "ABC", CancellationToken.None); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext(headers: headers)); + var call = Calls.AsyncUnaryCall(internalCall, "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -235,25 +234,25 @@ namespace Grpc.Core.Tests { channel.Dispose(); - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC", CancellationToken.None)); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC")); } [Test] public void UnaryCallPerformance() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); BenchmarkUtil.RunBenchmark(100, 100, - () => { Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken)); }); + () => { Calls.BlockingUnaryCall(internalCall, "ABC"); }); } [Test] public void UnknownMethodHandler() { - var internalCall = new Call(ServiceName, NonexistentMethod, channel, Metadata.Empty); + var internalCall = new Call(ServiceName, NonexistentMethod, channel, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "ABC", default(CancellationToken)); + Calls.BlockingUnaryCall(internalCall, "ABC"); Assert.Fail(); } catch (RpcException e) @@ -265,16 +264,16 @@ namespace Grpc.Core.Tests [Test] public void UserAgentStringPresent() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT", CancellationToken.None); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } [Test] public void PeerInfoPresent() { - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - string peer = Calls.BlockingUnaryCall(internalCall, "RETURN-PEER", CancellationToken.None); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + string peer = Calls.BlockingUnaryCall(internalCall, "RETURN-PEER"); Assert.IsTrue(peer.Contains(Host)); } @@ -286,8 +285,8 @@ namespace Grpc.Core.Tests var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); - var internalCall = new Call(ServiceName, EchoMethod, channel, Metadata.Empty); - await Calls.AsyncUnaryCall(internalCall, "abc", CancellationToken.None); + var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + await Calls.AsyncUnaryCall(internalCall, "abc"); await stateChangedTask; Assert.AreEqual(ChannelState.Ready, channel.State); diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index d84801fbac..2dea8d06e1 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -98,12 +98,12 @@ namespace Grpc.Core.Tests public void InfiniteDeadline() { // no deadline specified, check server sees infinite deadline - var internalCall = new Call(ServiceName, TestMethod, channel, Metadata.Empty); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE", CancellationToken.None)); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE")); // DateTime.MaxValue deadline specified, check server sees infinite deadline - var internalCall2 = new Call(ServiceName, TestMethod, channel, Metadata.Empty, DateTime.MaxValue); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall2, "RETURN_DEADLINE", CancellationToken.None)); + var internalCall2 = new Call(ServiceName, TestMethod, channel, new CallContext()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall2, "RETURN_DEADLINE")); } [Test] @@ -112,9 +112,9 @@ namespace Grpc.Core.Tests var remainingTimeClient = TimeSpan.FromDays(7); var deadline = DateTime.UtcNow + remainingTimeClient; Thread.Sleep(1000); - var internalCall = new Call(ServiceName, TestMethod, channel, Metadata.Empty, deadline); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); - var serverDeadlineTicksString = Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE", CancellationToken.None); + var serverDeadlineTicksString = Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE"); var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); // A fairly relaxed check that the deadline set by client and deadline seen by server @@ -127,11 +127,11 @@ namespace Grpc.Core.Tests public void DeadlineInThePast() { var deadline = DateTime.MinValue; - var internalCall = new Call(ServiceName, TestMethod, channel, Metadata.Empty, deadline); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "TIMEOUT", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "TIMEOUT"); Assert.Fail(); } catch (RpcException e) @@ -145,11 +145,11 @@ namespace Grpc.Core.Tests public void DeadlineExceededStatusOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(ServiceName, TestMethod, channel, Metadata.Empty, deadline); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); try { - Calls.BlockingUnaryCall(internalCall, "TIMEOUT", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "TIMEOUT"); Assert.Fail(); } catch (RpcException e) @@ -163,11 +163,11 @@ namespace Grpc.Core.Tests public void ServerReceivesCancellationOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(ServiceName, TestMethod, channel, Metadata.Empty, deadline); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); try { - Calls.BlockingUnaryCall(internalCall, "CHECK_CANCELLATION_RECEIVED", CancellationToken.None); + Calls.BlockingUnaryCall(internalCall, "CHECK_CANCELLATION_RECEIVED"); Assert.Fail(); } catch (RpcException e) diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/Call.cs index 94c5e26082..f9d1fde548 100644 --- a/src/csharp/Grpc.Core/Call.cs +++ b/src/csharp/Grpc.Core/Call.cs @@ -46,22 +46,14 @@ namespace Grpc.Core readonly Marshaller requestMarshaller; readonly Marshaller responseMarshaller; readonly Channel channel; - readonly Metadata headers; - readonly DateTime deadline; + readonly CallContext context; - public Call(string serviceName, Method method, Channel channel, Metadata headers) - : this(serviceName, method, channel, headers, DateTime.MaxValue) - { - } - - public Call(string serviceName, Method method, Channel channel, Metadata headers, DateTime deadline) + public Call(string serviceName, Method method, Channel channel, CallContext context) { this.name = method.GetFullName(serviceName); this.requestMarshaller = method.RequestMarshaller; this.responseMarshaller = method.ResponseMarshaller; - this.channel = Preconditions.CheckNotNull(channel); - this.headers = Preconditions.CheckNotNull(headers); - this.deadline = deadline; + this.context = context; } public Channel Channel @@ -84,21 +76,13 @@ namespace Grpc.Core } /// - /// Headers to send at the beginning of the call. + /// Call context. /// - public Metadata Headers - { - get - { - return headers; - } - } - - public DateTime Deadline + public CallContext Context { get { - return this.deadline; + return context; } } diff --git a/src/csharp/Grpc.Core/CallContext.cs b/src/csharp/Grpc.Core/CallContext.cs new file mode 100644 index 0000000000..2787d3f5b3 --- /dev/null +++ b/src/csharp/Grpc.Core/CallContext.cs @@ -0,0 +1,89 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Threading; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Context for calls made by client. + /// + public class CallContext + { + readonly Metadata headers; + readonly DateTime deadline; + readonly CancellationToken cancellationToken; + + /// + /// Creates a new call context. + /// + /// Headers to be sent with the call. + /// Deadline for the call to finish. null means no deadline. + /// Can be used to request cancellation of the call. + public CallContext(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // TODO(jtattermusch): consider only creating metadata object once it's really needed. + this.headers = headers != null ? headers : new Metadata(); + this.deadline = deadline.HasValue ? deadline.Value : DateTime.MaxValue; + this.cancellationToken = cancellationToken; + } + + /// + /// Headers to send at the beginning of the call. + /// + public Metadata Headers + { + get { return headers; } + } + + /// + /// Call deadline. + /// + public DateTime Deadline + { + get { return deadline; } + } + + /// + /// Token that can be used for cancelling the call. + /// + public CancellationToken CancellationToken + { + get { return cancellationToken; } + } + } +} diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index 054fc27491..f3c363bda2 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -43,59 +43,59 @@ namespace Grpc.Core /// public static class Calls { - public static TResponse BlockingUnaryCall(Call call, TRequest req, CancellationToken token) + public static TResponse BlockingUnaryCall(Call call, TRequest req) where TRequest : class where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); // TODO(jtattermusch): this gives a race that cancellation can be requested before the call even starts. - RegisterCancellationCallback(asyncCall, token); - return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Headers, call.Deadline); + RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); + return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Context.Headers, call.Context.Deadline); } - public static AsyncUnaryCall AsyncUnaryCall(Call call, TRequest req, CancellationToken token) + public static AsyncUnaryCall AsyncUnaryCall(Call call, TRequest req) where TRequest : class where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline)); - var asyncResult = asyncCall.UnaryCallAsync(req, call.Headers, call.Deadline); - RegisterCancellationCallback(asyncCall, token); + asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); + var asyncResult = asyncCall.UnaryCallAsync(req, call.Context.Headers, call.Context.Deadline); + RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); return new AsyncUnaryCall(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncServerStreamingCall AsyncServerStreamingCall(Call call, TRequest req, CancellationToken token) + public static AsyncServerStreamingCall AsyncServerStreamingCall(Call call, TRequest req) where TRequest : class where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline)); - asyncCall.StartServerStreamingCall(req, call.Headers, call.Deadline); - RegisterCancellationCallback(asyncCall, token); + asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); + asyncCall.StartServerStreamingCall(req, call.Context.Headers, call.Context.Deadline); + RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var responseStream = new ClientResponseStream(asyncCall); return new AsyncServerStreamingCall(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncClientStreamingCall AsyncClientStreamingCall(Call call, CancellationToken token) + public static AsyncClientStreamingCall AsyncClientStreamingCall(Call call) where TRequest : class where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline)); - var resultTask = asyncCall.ClientStreamingCallAsync(call.Headers, call.Deadline); - RegisterCancellationCallback(asyncCall, token); + asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); + var resultTask = asyncCall.ClientStreamingCallAsync(call.Context.Headers, call.Context.Deadline); + RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(Call call, CancellationToken token) + public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(Call call) where TRequest : class where TResponse : class { var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.CompletionQueue, call.Name, Timespec.FromDateTime(call.Deadline)); - asyncCall.StartDuplexStreamingCall(call.Headers, call.Deadline); - RegisterCancellationCallback(asyncCall, token); + asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); + asyncCall.StartDuplexStreamingCall(call.Context.Headers, call.Context.Deadline); + RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); var responseStream = new ClientResponseStream(asyncCall); return new AsyncDuplexStreamingCall(requestStream, responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 0b69610443..9273ea4582 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -178,22 +178,6 @@ namespace Grpc.Core } } - internal CompletionQueueSafeHandle CompletionQueue - { - get - { - return this.environment.CompletionQueue; - } - } - - internal CompletionRegistry CompletionRegistry - { - get - { - return this.environment.CompletionRegistry; - } - } - internal GrpcEnvironment Environment { get diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index fd3473128a..55e3f33b3e 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -76,19 +76,17 @@ namespace Grpc.Core /// /// Creates a new call to given method. /// - protected Call CreateCall(string serviceName, Method method, Metadata metadata, DateTime? deadline) + protected Call CreateCall(string serviceName, Method method, CallContext context) where TRequest : class where TResponse : class { var interceptor = HeaderInterceptor; if (interceptor != null) { - metadata = metadata ?? new Metadata(); - interceptor(metadata); - metadata.Freeze(); + interceptor(context.Headers); + context.Headers.Freeze(); } - return new Call(serviceName, method, channel, - metadata ?? Metadata.Empty, deadline ?? DateTime.MaxValue); + return new Call(serviceName, method, channel, context); } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 17add77164..a282d57d99 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -48,6 +48,7 @@ + diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 48f466460f..f84c4b4633 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -67,7 +67,7 @@ namespace Grpc.Core.Internal public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName, Timespec deadline) { this.channel = channel; - var call = channel.Handle.CreateCall(channel.CompletionRegistry, cq, methodName, null, deadline); + var call = channel.Handle.CreateCall(channel.Environment.CompletionRegistry, cq, methodName, null, deadline); channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); } diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs index 0c48adaea5..032b1390db 100644 --- a/src/csharp/Grpc.Core/ServerCallContext.cs +++ b/src/csharp/Grpc.Core/ServerCallContext.cs @@ -65,7 +65,7 @@ namespace Grpc.Core this.cancellationToken = cancellationToken; } - /// Name of method called in this RPC. + /// Name of method called in this RPC. public string Method { get @@ -74,7 +74,7 @@ namespace Grpc.Core } } - /// Name of host called in this RPC. + /// Name of host called in this RPC. public string Host { get @@ -83,7 +83,7 @@ namespace Grpc.Core } } - /// Address of the remote endpoint in URI format. + /// Address of the remote endpoint in URI format. public string Peer { get @@ -92,7 +92,7 @@ namespace Grpc.Core } } - /// Deadline for this RPC. + /// Deadline for this RPC. public DateTime Deadline { get @@ -101,7 +101,7 @@ namespace Grpc.Core } } - /// Initial metadata sent by client. + /// Initial metadata sent by client. public Metadata RequestHeaders { get @@ -110,8 +110,7 @@ namespace Grpc.Core } } - // TODO(jtattermusch): support signalling cancellation. - /// Cancellation token signals when call is cancelled. + ///Cancellation token signals when call is cancelled. public CancellationToken CancellationToken { get @@ -120,7 +119,7 @@ namespace Grpc.Core } } - /// Trailers to send back to client after RPC finishes. + /// Trailers to send back to client after RPC finishes. public Metadata ResponseTrailers { get -- cgit v1.2.3 From 542e21cbe08191f6709d0dc6e44367c231fb3072 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 02:25:33 -0700 Subject: refactoring AsyncCall --- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 3 +- src/csharp/Grpc.Core/Call.cs | 1 + src/csharp/Grpc.Core/Calls.cs | 29 +++++++------- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 61 +++++++++++++++++------------- 4 files changed, 52 insertions(+), 42 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 2dea8d06e1..f90a46368c 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -126,8 +126,7 @@ namespace Grpc.Core.Tests [Test] public void DeadlineInThePast() { - var deadline = DateTime.MinValue; - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext()); + var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: DateTime.MinValue)); try { diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/Call.cs index f9d1fde548..577c17b931 100644 --- a/src/csharp/Grpc.Core/Call.cs +++ b/src/csharp/Grpc.Core/Call.cs @@ -53,6 +53,7 @@ namespace Grpc.Core this.name = method.GetFullName(serviceName); this.requestMarshaller = method.RequestMarshaller; this.responseMarshaller = method.ResponseMarshaller; + this.channel = channel; this.context = context; } diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index f3c363bda2..ef6636587e 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -47,19 +47,20 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + var asyncCall = new AsyncCall(call.Channel, call.Name, null, call.Context, + call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); // TODO(jtattermusch): this gives a race that cancellation can be requested before the call even starts. RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); - return asyncCall.UnaryCall(call.Channel, call.Name, req, call.Context.Headers, call.Context.Deadline); + return asyncCall.UnaryCall(req); } public static AsyncUnaryCall AsyncUnaryCall(Call call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); - var asyncResult = asyncCall.UnaryCallAsync(req, call.Context.Headers, call.Context.Deadline); + var asyncCall = new AsyncCall(call.Channel, call.Name, null, call.Context, + call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + var asyncResult = asyncCall.UnaryCallAsync(req); RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); return new AsyncUnaryCall(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } @@ -68,9 +69,9 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); - asyncCall.StartServerStreamingCall(req, call.Context.Headers, call.Context.Deadline); + var asyncCall = new AsyncCall(call.Channel, call.Name, null, call.Context, + call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.StartServerStreamingCall(req); RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var responseStream = new ClientResponseStream(asyncCall); return new AsyncServerStreamingCall(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); @@ -80,9 +81,9 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); - var resultTask = asyncCall.ClientStreamingCallAsync(call.Context.Headers, call.Context.Deadline); + var asyncCall = new AsyncCall(call.Channel, call.Name, null, call.Context, + call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + var resultTask = asyncCall.ClientStreamingCallAsync(); RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); @@ -92,9 +93,9 @@ namespace Grpc.Core where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); - asyncCall.Initialize(call.Channel, call.Channel.Environment.CompletionQueue, call.Name, Timespec.FromDateTime(call.Context.Deadline)); - asyncCall.StartDuplexStreamingCall(call.Context.Headers, call.Context.Deadline); + var asyncCall = new AsyncCall(call.Channel, call.Name, null, call.Context, + call.RequestMarshaller.Serializer, call.ResponseMarshaller.Deserializer); + asyncCall.StartDuplexStreamingCall(); RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); var responseStream = new ClientResponseStream(asyncCall); diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index f84c4b4633..ff3e99d30d 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -50,7 +50,10 @@ namespace Grpc.Core.Internal { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); - Channel channel; + readonly Channel channel; + readonly string method; + readonly string host; + readonly CallContext context; // Completion of a pending unary response if not null. TaskCompletionSource unaryResponseTcs; @@ -60,26 +63,20 @@ namespace Grpc.Core.Internal bool readObserverCompleted; // True if readObserver has already been completed. - public AsyncCall(Func serializer, Func deserializer) : base(serializer, deserializer) - { - } - - public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName, Timespec deadline) + public AsyncCall(Channel channel, string method, string host, CallContext context, Func serializer, Func deserializer) : base(serializer, deserializer) { this.channel = channel; - var call = channel.Handle.CreateCall(channel.Environment.CompletionRegistry, cq, methodName, null, deadline); - channel.Environment.DebugStats.ActiveClientCalls.Increment(); - InitializeInternal(call); + this.method = Preconditions.CheckNotNull(method); + this.host = host; // null host means default host will be used by C-core. + this.context = context; } // TODO: this method is not Async, so it shouldn't be in AsyncCall class, but // it is reusing fair amount of code in this class, so we are leaving it here. - // TODO: for other calls, you need to call Initialize, this methods calls initialize - // on its own, so there's a usage inconsistency. /// /// Blocking unary request - unary response call. /// - public TResponse UnaryCall(Channel channel, string methodName, TRequest msg, Metadata headers, DateTime deadline) + public TResponse UnaryCall(TRequest msg) { using (CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create()) { @@ -89,13 +86,14 @@ namespace Grpc.Core.Internal lock (myLock) { - Initialize(channel, cq, methodName, Timespec.FromDateTime(deadline)); + Preconditions.CheckState(!started); + Initialize(cq); started = true; halfcloseRequested = true; readingDone = true; } - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) { using (var ctx = BatchContextSafeHandle.Create()) { @@ -129,11 +127,12 @@ namespace Grpc.Core.Internal /// /// Starts a unary request - unary response call. /// - public Task UnaryCallAsync(TRequest msg, Metadata headers, DateTime deadline) + public Task UnaryCallAsync(TRequest msg) { lock (myLock) { - Preconditions.CheckNotNull(call); + Preconditions.CheckState(!started); + Initialize(channel.Environment.CompletionQueue); started = true; halfcloseRequested = true; @@ -142,7 +141,7 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) { call.StartUnary(payload, HandleUnaryResponse, metadataArray); } @@ -154,17 +153,18 @@ namespace Grpc.Core.Internal /// Starts a streamed request - unary response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// - public Task ClientStreamingCallAsync(Metadata headers, DateTime deadline) + public Task ClientStreamingCallAsync() { lock (myLock) { - Preconditions.CheckNotNull(call); + Preconditions.CheckState(!started); + Initialize(channel.Environment.CompletionQueue); started = true; readingDone = true; unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) { call.StartClientStreaming(HandleUnaryResponse, metadataArray); } @@ -176,11 +176,12 @@ namespace Grpc.Core.Internal /// /// Starts a unary request - streamed response call. /// - public void StartServerStreamingCall(TRequest msg, Metadata headers, DateTime deadline) + public void StartServerStreamingCall(TRequest msg) { lock (myLock) { - Preconditions.CheckNotNull(call); + Preconditions.CheckState(!started); + Initialize(channel.Environment.CompletionQueue); started = true; halfcloseRequested = true; @@ -188,7 +189,7 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) { call.StartServerStreaming(payload, HandleFinished, metadataArray); } @@ -199,15 +200,16 @@ namespace Grpc.Core.Internal /// Starts a streaming request - streaming response call. /// Use StartSendMessage and StartSendCloseFromClient to stream requests. /// - public void StartDuplexStreamingCall(Metadata headers, DateTime deadline) + public void StartDuplexStreamingCall() { lock (myLock) { - Preconditions.CheckNotNull(call); + Preconditions.CheckState(!started); + Initialize(channel.Environment.CompletionQueue); started = true; - using (var metadataArray = MetadataArraySafeHandle.Create(headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) { call.StartDuplexStreaming(HandleFinished, metadataArray); } @@ -312,6 +314,13 @@ namespace Grpc.Core.Internal channel.Environment.DebugStats.ActiveClientCalls.Decrement(); } + private void Initialize(CompletionQueueSafeHandle cq) + { + var call = channel.Handle.CreateCall(channel.Environment.CompletionRegistry, cq, method, host, Timespec.FromDateTime(context.Deadline)); + channel.Environment.DebugStats.ActiveClientCalls.Increment(); + InitializeInternal(call); + } + /// /// Handler for unary response completion. /// -- cgit v1.2.3 From a9ddd02dae4c4bff9d0c261afda43592e0d589e0 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 03:04:58 -0700 Subject: add servicename field to method --- src/compiler/csharp_generator.cc | 2 ++ src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 39 ++++++++++++++------------ src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 15 +++++----- src/csharp/Grpc.Core/Method.cs | 28 +++++++++++++++--- 4 files changed, 55 insertions(+), 29 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index efd39e8ac5..9ef30b817d 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -246,6 +246,8 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { out->Indent(); out->Print("$methodtype$,\n", "methodtype", GetCSharpMethodType(GetMethodType(method))); + out->Print("$servicenamefield$,\n", "servicenamefield", + GetServiceNameFieldName()); out->Print("\"$methodname$\",\n", "methodname", method->name()); out->Print("$requestmarshaller$,\n", "requestmarshaller", GetMarshallerFieldName(method->input_type())); diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index d289ded6bd..9e7acab7ed 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -50,19 +50,22 @@ namespace Grpc.Core.Tests static readonly Method EchoMethod = new Method( MethodType.Unary, - "/tests.Test/Echo", + "tests.Test", + "Echo", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method ConcatAndEchoMethod = new Method( MethodType.ClientStreaming, - "/tests.Test/ConcatAndEcho", + "tests.Test", + "ConcatAndEcho", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method NonexistentMethod = new Method( MethodType.Unary, - "/tests.Test/NonexistentMethod", + "tests.Test", + "NonexistentMethod", Marshallers.StringMarshaller, Marshallers.StringMarshaller); @@ -102,14 +105,14 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); try { Calls.BlockingUnaryCall(internalCall, "THROW"); @@ -124,7 +127,7 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); try { Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED"); @@ -139,7 +142,7 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); try { Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED"); @@ -154,7 +157,7 @@ namespace Grpc.Core.Tests [Test] public async Task AsyncUnaryCall() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); var result = await Calls.AsyncUnaryCall(internalCall, "ABC"); Assert.AreEqual("ABC", result); } @@ -162,7 +165,7 @@ namespace Grpc.Core.Tests [Test] public async Task AsyncUnaryCall_ServerHandlerThrows() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); try { await Calls.AsyncUnaryCall(internalCall, "THROW"); @@ -177,7 +180,7 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall() { - var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, ConcatAndEchoMethod, new CallContext()); var call = Calls.AsyncClientStreamingCall(internalCall); await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); @@ -188,7 +191,7 @@ namespace Grpc.Core.Tests public async Task ClientStreamingCall_CancelAfterBegin() { var cts = new CancellationTokenSource(); - var internalCall = new Call(ServiceName, ConcatAndEchoMethod, channel, new CallContext(cancellationToken: cts.Token)); + var internalCall = new Call(channel, ConcatAndEchoMethod, new CallContext(cancellationToken: cts.Token)); var call = Calls.AsyncClientStreamingCall(internalCall); // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. @@ -213,7 +216,7 @@ namespace Grpc.Core.Tests new Metadata.Entry("ascii-header", "abcdefg"), new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext(headers: headers)); + var internalCall = new Call(channel, EchoMethod, new CallContext(headers: headers)); var call = Calls.AsyncUnaryCall(internalCall, "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -234,14 +237,14 @@ namespace Grpc.Core.Tests { channel.Dispose(); - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC")); } [Test] public void UnaryCallPerformance() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); BenchmarkUtil.RunBenchmark(100, 100, () => { Calls.BlockingUnaryCall(internalCall, "ABC"); }); } @@ -249,7 +252,7 @@ namespace Grpc.Core.Tests [Test] public void UnknownMethodHandler() { - var internalCall = new Call(ServiceName, NonexistentMethod, channel, new CallContext()); + var internalCall = new Call(channel, NonexistentMethod, new CallContext()); try { Calls.BlockingUnaryCall(internalCall, "ABC"); @@ -264,7 +267,7 @@ namespace Grpc.Core.Tests [Test] public void UserAgentStringPresent() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } @@ -272,7 +275,7 @@ namespace Grpc.Core.Tests [Test] public void PeerInfoPresent() { - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); string peer = Calls.BlockingUnaryCall(internalCall, "RETURN-PEER"); Assert.IsTrue(peer.Contains(Host)); } @@ -285,7 +288,7 @@ namespace Grpc.Core.Tests var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); - var internalCall = new Call(ServiceName, EchoMethod, channel, new CallContext()); + var internalCall = new Call(channel, EchoMethod, new CallContext()); await Calls.AsyncUnaryCall(internalCall, "abc"); await stateChangedTask; diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index f90a46368c..e5fb2e5404 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -53,7 +53,8 @@ namespace Grpc.Core.Tests static readonly Method TestMethod = new Method( MethodType.Unary, - "/tests.Test/Test", + "tests.Test", + "Test", Marshallers.StringMarshaller, Marshallers.StringMarshaller); @@ -98,11 +99,11 @@ namespace Grpc.Core.Tests public void InfiniteDeadline() { // no deadline specified, check server sees infinite deadline - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext()); + var internalCall = new Call(channel, TestMethod, new CallContext()); Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE")); // DateTime.MaxValue deadline specified, check server sees infinite deadline - var internalCall2 = new Call(ServiceName, TestMethod, channel, new CallContext()); + var internalCall2 = new Call(channel, TestMethod, new CallContext()); Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall2, "RETURN_DEADLINE")); } @@ -112,7 +113,7 @@ namespace Grpc.Core.Tests var remainingTimeClient = TimeSpan.FromDays(7); var deadline = DateTime.UtcNow + remainingTimeClient; Thread.Sleep(1000); - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); + var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); var serverDeadlineTicksString = Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE"); var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); @@ -126,7 +127,7 @@ namespace Grpc.Core.Tests [Test] public void DeadlineInThePast() { - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: DateTime.MinValue)); + var internalCall = new Call(channel, TestMethod, new CallContext(deadline: DateTime.MinValue)); try { @@ -144,7 +145,7 @@ namespace Grpc.Core.Tests public void DeadlineExceededStatusOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); + var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); try { @@ -162,7 +163,7 @@ namespace Grpc.Core.Tests public void ServerReceivesCancellationOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(ServiceName, TestMethod, channel, new CallContext(deadline: deadline)); + var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); try { diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs index 77d36191c3..cc047ac9f8 100644 --- a/src/csharp/Grpc.Core/Method.cs +++ b/src/csharp/Grpc.Core/Method.cs @@ -53,16 +53,20 @@ namespace Grpc.Core public class Method { readonly MethodType type; + readonly string serviceName; readonly string name; readonly Marshaller requestMarshaller; readonly Marshaller responseMarshaller; + readonly string fullName; - public Method(MethodType type, string name, Marshaller requestMarshaller, Marshaller responseMarshaller) + public Method(MethodType type, string serviceName, string name, Marshaller requestMarshaller, Marshaller responseMarshaller) { this.type = type; - this.name = name; - this.requestMarshaller = requestMarshaller; - this.responseMarshaller = responseMarshaller; + this.serviceName = Preconditions.CheckNotNull(serviceName); + this.name = Preconditions.CheckNotNull(name); + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); + this.fullName = GetFullName(serviceName); } public MethodType Type @@ -72,6 +76,14 @@ namespace Grpc.Core return this.type; } } + + public string ServiceName + { + get + { + return this.serviceName; + } + } public string Name { @@ -97,6 +109,14 @@ namespace Grpc.Core } } + public string FullName + { + get + { + return this.fullName; + } + } + /// /// Gets full name of the method including the service name. /// -- cgit v1.2.3 From 5cb5ceda2c146c165253160e969b7f04469dd81c Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 04:05:57 -0700 Subject: refactoring client side calls --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 60 ++++++------- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 24 ++--- src/csharp/Grpc.Core/Call.cs | 98 -------------------- src/csharp/Grpc.Core/CallInvocationDetails.cs | 119 +++++++++++++++++++++++++ src/csharp/Grpc.Core/Calls.cs | 39 +++----- src/csharp/Grpc.Core/ClientBase.cs | 4 +- src/csharp/Grpc.Core/Grpc.Core.csproj | 2 +- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 63 +++++++------ 8 files changed, 211 insertions(+), 198 deletions(-) delete mode 100644 src/csharp/Grpc.Core/Call.cs create mode 100644 src/csharp/Grpc.Core/CallInvocationDetails.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 9e7acab7ed..ab9dae6dd6 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -105,17 +105,17 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); - Assert.AreEqual("ABC", Calls.BlockingUnaryCall(internalCall, "ABC")); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "THROW"); + Calls.BlockingUnaryCall(callDetails, "THROW"); Assert.Fail(); } catch (RpcException e) @@ -127,10 +127,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "THROW_UNAUTHENTICATED"); + Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -142,10 +142,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "SET_UNAUTHENTICATED"); + Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED"); Assert.Fail(); } catch (RpcException e) @@ -157,18 +157,18 @@ namespace Grpc.Core.Tests [Test] public async Task AsyncUnaryCall() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); - var result = await Calls.AsyncUnaryCall(internalCall, "ABC"); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var result = await Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", result); } [Test] public async Task AsyncUnaryCall_ServerHandlerThrows() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); try { - await Calls.AsyncUnaryCall(internalCall, "THROW"); + await Calls.AsyncUnaryCall(callDetails, "THROW"); Assert.Fail(); } catch (RpcException e) @@ -180,8 +180,8 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall() { - var internalCall = new Call(channel, ConcatAndEchoMethod, new CallContext()); - var call = Calls.AsyncClientStreamingCall(internalCall); + var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallContext()); + var call = Calls.AsyncClientStreamingCall(callDetails); await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); @@ -191,8 +191,8 @@ namespace Grpc.Core.Tests public async Task ClientStreamingCall_CancelAfterBegin() { var cts = new CancellationTokenSource(); - var internalCall = new Call(channel, ConcatAndEchoMethod, new CallContext(cancellationToken: cts.Token)); - var call = Calls.AsyncClientStreamingCall(internalCall); + var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallContext(cancellationToken: cts.Token)); + var call = Calls.AsyncClientStreamingCall(callDetails); // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. await Task.Delay(1000); @@ -216,8 +216,8 @@ namespace Grpc.Core.Tests new Metadata.Entry("ascii-header", "abcdefg"), new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var internalCall = new Call(channel, EchoMethod, new CallContext(headers: headers)); - var call = Calls.AsyncUnaryCall(internalCall, "ABC"); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext(headers: headers)); + var call = Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -237,25 +237,25 @@ namespace Grpc.Core.Tests { channel.Dispose(); - var internalCall = new Call(channel, EchoMethod, new CallContext()); - Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(internalCall, "ABC")); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCallPerformance() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); BenchmarkUtil.RunBenchmark(100, 100, - () => { Calls.BlockingUnaryCall(internalCall, "ABC"); }); + () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); } [Test] public void UnknownMethodHandler() { - var internalCall = new Call(channel, NonexistentMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, NonexistentMethod, new CallContext()); try { - Calls.BlockingUnaryCall(internalCall, "ABC"); + Calls.BlockingUnaryCall(callDetails, "ABC"); Assert.Fail(); } catch (RpcException e) @@ -267,16 +267,16 @@ namespace Grpc.Core.Tests [Test] public void UserAgentStringPresent() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); - string userAgent = Calls.BlockingUnaryCall(internalCall, "RETURN-USER-AGENT"); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } [Test] public void PeerInfoPresent() { - var internalCall = new Call(channel, EchoMethod, new CallContext()); - string peer = Calls.BlockingUnaryCall(internalCall, "RETURN-PEER"); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER"); Assert.IsTrue(peer.Contains(Host)); } @@ -288,8 +288,8 @@ namespace Grpc.Core.Tests var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); - var internalCall = new Call(channel, EchoMethod, new CallContext()); - await Calls.AsyncUnaryCall(internalCall, "abc"); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + await Calls.AsyncUnaryCall(callDetails, "abc"); await stateChangedTask; Assert.AreEqual(ChannelState.Ready, channel.State); diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index e5fb2e5404..54cd024670 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -99,12 +99,12 @@ namespace Grpc.Core.Tests public void InfiniteDeadline() { // no deadline specified, check server sees infinite deadline - var internalCall = new Call(channel, TestMethod, new CallContext()); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE")); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE")); // DateTime.MaxValue deadline specified, check server sees infinite deadline - var internalCall2 = new Call(channel, TestMethod, new CallContext()); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(internalCall2, "RETURN_DEADLINE")); + var callDetails2 = new CallInvocationDetails(channel, TestMethod, new CallContext()); + Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE")); } [Test] @@ -113,9 +113,9 @@ namespace Grpc.Core.Tests var remainingTimeClient = TimeSpan.FromDays(7); var deadline = DateTime.UtcNow + remainingTimeClient; Thread.Sleep(1000); - var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); - var serverDeadlineTicksString = Calls.BlockingUnaryCall(internalCall, "RETURN_DEADLINE"); + var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"); var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); // A fairly relaxed check that the deadline set by client and deadline seen by server @@ -127,11 +127,11 @@ namespace Grpc.Core.Tests [Test] public void DeadlineInThePast() { - var internalCall = new Call(channel, TestMethod, new CallContext(deadline: DateTime.MinValue)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: DateTime.MinValue)); try { - Calls.BlockingUnaryCall(internalCall, "TIMEOUT"); + Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); Assert.Fail(); } catch (RpcException e) @@ -145,11 +145,11 @@ namespace Grpc.Core.Tests public void DeadlineExceededStatusOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); try { - Calls.BlockingUnaryCall(internalCall, "TIMEOUT"); + Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); Assert.Fail(); } catch (RpcException e) @@ -163,11 +163,11 @@ namespace Grpc.Core.Tests public void ServerReceivesCancellationOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var internalCall = new Call(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); try { - Calls.BlockingUnaryCall(internalCall, "CHECK_CANCELLATION_RECEIVED"); + Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED"); Assert.Fail(); } catch (RpcException e) diff --git a/src/csharp/Grpc.Core/Call.cs b/src/csharp/Grpc.Core/Call.cs deleted file mode 100644 index 00ccb9d1b5..0000000000 --- a/src/csharp/Grpc.Core/Call.cs +++ /dev/null @@ -1,98 +0,0 @@ -#region Copyright notice and license - -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; -using Grpc.Core.Internal; -using Grpc.Core.Utils; - -namespace Grpc.Core -{ - /// - /// Abstraction of a call to be invoked on a client. - /// - public class Call - { - readonly Channel channel; - readonly Method method; - readonly string host; - readonly CallContext context; - - public Call(Channel channel, Method method, CallContext context) - : this(channel, method, null, context) - { - } - - public Call(Channel channel, Method method, string host, CallContext context) - { - this.channel = Preconditions.CheckNotNull(channel); - this.method = Preconditions.CheckNotNull(method); - this.host = host; - this.context = Preconditions.CheckNotNull(context); - } - - public Channel Channel - { - get - { - return this.channel; - } - } - - public Method Method - { - get - { - return this.method; - } - } - - public string Host - { - get - { - return this.host; - } - } - - /// - /// Call context. - /// - public CallContext Context - { - get - { - return context; - } - } - } -} diff --git a/src/csharp/Grpc.Core/CallInvocationDetails.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs new file mode 100644 index 0000000000..6678b7f430 --- /dev/null +++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs @@ -0,0 +1,119 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// + /// Details about a client-side call to be invoked. + /// + public class CallInvocationDetails + { + readonly Channel channel; + readonly string method; + readonly string host; + readonly Marshaller requestMarshaller; + readonly Marshaller responseMarshaller; + readonly CallContext context; + + + public CallInvocationDetails(Channel channel, Method method, CallContext context) : + this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, context) + { + } + + public CallInvocationDetails(Channel channel, string method, string host, Marshaller requestMarshaller, Marshaller responseMarshaller, CallContext context) + { + this.channel = Preconditions.CheckNotNull(channel); + this.method = Preconditions.CheckNotNull(method); + this.host = host; + this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); + this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); + this.context = Preconditions.CheckNotNull(context); + } + + public Channel Channel + { + get + { + return this.channel; + } + } + + public string Method + { + get + { + return this.method; + } + } + + public string Host + { + get + { + return this.host; + } + } + + public Marshaller RequestMarshaller + { + get + { + return this.requestMarshaller; + } + } + + public Marshaller ResponseMarshaller + { + get + { + return this.responseMarshaller; + } + } + + /// + /// Call context. + /// + public CallContext Context + { + get + { + return context; + } + } + } +} diff --git a/src/csharp/Grpc.Core/Calls.cs b/src/csharp/Grpc.Core/Calls.cs index f975bcde22..00a8cabf82 100644 --- a/src/csharp/Grpc.Core/Calls.cs +++ b/src/csharp/Grpc.Core/Calls.cs @@ -43,71 +43,52 @@ namespace Grpc.Core /// public static class Calls { - public static TResponse BlockingUnaryCall(Call call, TRequest req) + public static TResponse BlockingUnaryCall(CallInvocationDetails call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.Channel, call.Method.FullName, call.Host, call.Context, - call.Method.RequestMarshaller.Serializer, call.Method.ResponseMarshaller.Deserializer); - // TODO(jtattermusch): this gives a race that cancellation can be requested before the call even starts. - RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); + var asyncCall = new AsyncCall(call); return asyncCall.UnaryCall(req); } - public static AsyncUnaryCall AsyncUnaryCall(Call call, TRequest req) + public static AsyncUnaryCall AsyncUnaryCall(CallInvocationDetails call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.Channel, call.Method.FullName, call.Host, call.Context, - call.Method.RequestMarshaller.Serializer, call.Method.ResponseMarshaller.Deserializer); + var asyncCall = new AsyncCall(call); var asyncResult = asyncCall.UnaryCallAsync(req); - RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); return new AsyncUnaryCall(asyncResult, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncServerStreamingCall AsyncServerStreamingCall(Call call, TRequest req) + public static AsyncServerStreamingCall AsyncServerStreamingCall(CallInvocationDetails call, TRequest req) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.Channel, call.Method.FullName, call.Host, call.Context, - call.Method.RequestMarshaller.Serializer, call.Method.ResponseMarshaller.Deserializer); + var asyncCall = new AsyncCall(call); asyncCall.StartServerStreamingCall(req); - RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var responseStream = new ClientResponseStream(asyncCall); return new AsyncServerStreamingCall(responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncClientStreamingCall AsyncClientStreamingCall(Call call) + public static AsyncClientStreamingCall AsyncClientStreamingCall(CallInvocationDetails call) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.Channel, call.Method.FullName, call.Host, call.Context, - call.Method.RequestMarshaller.Serializer, call.Method.ResponseMarshaller.Deserializer); + var asyncCall = new AsyncCall(call); var resultTask = asyncCall.ClientStreamingCallAsync(); - RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); return new AsyncClientStreamingCall(requestStream, resultTask, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(Call call) + public static AsyncDuplexStreamingCall AsyncDuplexStreamingCall(CallInvocationDetails call) where TRequest : class where TResponse : class { - var asyncCall = new AsyncCall(call.Channel, call.Method.FullName, call.Host, call.Context, - call.Method.RequestMarshaller.Serializer, call.Method.ResponseMarshaller.Deserializer); + var asyncCall = new AsyncCall(call); asyncCall.StartDuplexStreamingCall(); - RegisterCancellationCallback(asyncCall, call.Context.CancellationToken); var requestStream = new ClientRequestStream(asyncCall); var responseStream = new ClientResponseStream(asyncCall); return new AsyncDuplexStreamingCall(requestStream, responseStream, asyncCall.GetStatus, asyncCall.GetTrailers, asyncCall.Cancel); } - - private static void RegisterCancellationCallback(AsyncCall asyncCall, CancellationToken token) - { - if (token.CanBeCanceled) - { - token.Register(() => asyncCall.Cancel()); - } - } } } diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index e9a9686694..5cebe4e7b9 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -76,7 +76,7 @@ namespace Grpc.Core /// /// Creates a new call to given method. /// - protected Call CreateCall(Method method, CallContext context) + protected CallInvocationDetails CreateCall(Method method, CallContext context) where TRequest : class where TResponse : class { @@ -86,7 +86,7 @@ namespace Grpc.Core interceptor(context.Headers); context.Headers.Freeze(); } - return new Call(channel, method, null, context); + return new CallInvocationDetails(channel, method, context); } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index a282d57d99..9370a0b2f5 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -58,7 +58,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index ff3e99d30d..939c24acaf 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -50,10 +50,7 @@ namespace Grpc.Core.Internal { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType>(); - readonly Channel channel; - readonly string method; - readonly string host; - readonly CallContext context; + readonly CallInvocationDetails callDetails; // Completion of a pending unary response if not null. TaskCompletionSource unaryResponseTcs; @@ -63,12 +60,10 @@ namespace Grpc.Core.Internal bool readObserverCompleted; // True if readObserver has already been completed. - public AsyncCall(Channel channel, string method, string host, CallContext context, Func serializer, Func deserializer) : base(serializer, deserializer) + public AsyncCall(CallInvocationDetails callDetails) + : base(callDetails.RequestMarshaller.Serializer, callDetails.ResponseMarshaller.Deserializer) { - this.channel = channel; - this.method = Preconditions.CheckNotNull(method); - this.host = host; // null host means default host will be used by C-core. - this.context = context; + this.callDetails = callDetails; } // TODO: this method is not Async, so it shouldn't be in AsyncCall class, but @@ -87,13 +82,14 @@ namespace Grpc.Core.Internal lock (myLock) { Preconditions.CheckState(!started); - Initialize(cq); started = true; + Initialize(cq); + halfcloseRequested = true; readingDone = true; } - using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) { using (var ctx = BatchContextSafeHandle.Create()) { @@ -132,16 +128,17 @@ namespace Grpc.Core.Internal lock (myLock) { Preconditions.CheckState(!started); - Initialize(channel.Environment.CompletionQueue); - started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + halfcloseRequested = true; readingDone = true; byte[] payload = UnsafeSerialize(msg); unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) { call.StartUnary(payload, HandleUnaryResponse, metadataArray); } @@ -158,13 +155,14 @@ namespace Grpc.Core.Internal lock (myLock) { Preconditions.CheckState(!started); - Initialize(channel.Environment.CompletionQueue); - started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + readingDone = true; unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) { call.StartClientStreaming(HandleUnaryResponse, metadataArray); } @@ -181,15 +179,16 @@ namespace Grpc.Core.Internal lock (myLock) { Preconditions.CheckState(!started); - Initialize(channel.Environment.CompletionQueue); - started = true; + + Initialize(callDetails.Channel.Environment.CompletionQueue); + halfcloseRequested = true; halfclosed = true; // halfclose not confirmed yet, but it will be once finishedHandler is called. byte[] payload = UnsafeSerialize(msg); - using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) { call.StartServerStreaming(payload, HandleFinished, metadataArray); } @@ -205,11 +204,11 @@ namespace Grpc.Core.Internal lock (myLock) { Preconditions.CheckState(!started); - Initialize(channel.Environment.CompletionQueue); - started = true; - using (var metadataArray = MetadataArraySafeHandle.Create(context.Headers)) + Initialize(callDetails.Channel.Environment.CompletionQueue); + + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) { call.StartDuplexStreaming(HandleFinished, metadataArray); } @@ -311,14 +310,26 @@ namespace Grpc.Core.Internal protected override void OnReleaseResources() { - channel.Environment.DebugStats.ActiveClientCalls.Decrement(); + callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Decrement(); } private void Initialize(CompletionQueueSafeHandle cq) { - var call = channel.Handle.CreateCall(channel.Environment.CompletionRegistry, cq, method, host, Timespec.FromDateTime(context.Deadline)); - channel.Environment.DebugStats.ActiveClientCalls.Increment(); + var call = callDetails.Channel.Handle.CreateCall(callDetails.Channel.Environment.CompletionRegistry, cq, + callDetails.Method, callDetails.Host, Timespec.FromDateTime(callDetails.Context.Deadline)); + callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); + RegisterCancellationCallback(); + } + + // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called. + private void RegisterCancellationCallback() + { + var token = callDetails.Context.CancellationToken; + if (token.CanBeCanceled) + { + token.Register(() => this.Cancel()); + } } /// -- cgit v1.2.3 From 5c371f83763b9f2328a45eccd093ef7a60c37e72 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 14:55:17 -0700 Subject: renamed CallContext to CallOptions --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 30 ++++----- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 12 ++-- src/csharp/Grpc.Core/CallContext.cs | 89 -------------------------- src/csharp/Grpc.Core/CallInvocationDetails.cs | 20 +++--- src/csharp/Grpc.Core/CallOptions.cs | 2 +- src/csharp/Grpc.Core/ClientBase.cs | 8 +-- src/csharp/Grpc.Core/Grpc.Core.csproj | 2 +- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 14 ++-- 8 files changed, 42 insertions(+), 135 deletions(-) delete mode 100644 src/csharp/Grpc.Core/CallContext.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index ab9dae6dd6..2aac4e3dec 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -105,14 +105,14 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); try { Calls.BlockingUnaryCall(callDetails, "THROW"); @@ -127,7 +127,7 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); try { Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED"); @@ -142,7 +142,7 @@ namespace Grpc.Core.Tests [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); try { Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED"); @@ -157,7 +157,7 @@ namespace Grpc.Core.Tests [Test] public async Task AsyncUnaryCall() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); var result = await Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", result); } @@ -165,7 +165,7 @@ namespace Grpc.Core.Tests [Test] public async Task AsyncUnaryCall_ServerHandlerThrows() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); try { await Calls.AsyncUnaryCall(callDetails, "THROW"); @@ -180,7 +180,7 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall() { - var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallOptions()); var call = Calls.AsyncClientStreamingCall(callDetails); await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); @@ -191,7 +191,7 @@ namespace Grpc.Core.Tests public async Task ClientStreamingCall_CancelAfterBegin() { var cts = new CancellationTokenSource(); - var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallContext(cancellationToken: cts.Token)); + var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallOptions(cancellationToken: cts.Token)); var call = Calls.AsyncClientStreamingCall(callDetails); // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. @@ -216,7 +216,7 @@ namespace Grpc.Core.Tests new Metadata.Entry("ascii-header", "abcdefg"), new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext(headers: headers)); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions(headers: headers)); var call = Calls.AsyncUnaryCall(callDetails, "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -237,14 +237,14 @@ namespace Grpc.Core.Tests { channel.Dispose(); - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC")); } [Test] public void UnaryCallPerformance() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); BenchmarkUtil.RunBenchmark(100, 100, () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); } @@ -252,7 +252,7 @@ namespace Grpc.Core.Tests [Test] public void UnknownMethodHandler() { - var callDetails = new CallInvocationDetails(channel, NonexistentMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, NonexistentMethod, new CallOptions()); try { Calls.BlockingUnaryCall(callDetails, "ABC"); @@ -267,7 +267,7 @@ namespace Grpc.Core.Tests [Test] public void UserAgentStringPresent() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } @@ -275,7 +275,7 @@ namespace Grpc.Core.Tests [Test] public void PeerInfoPresent() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER"); Assert.IsTrue(peer.Contains(Host)); } @@ -288,7 +288,7 @@ namespace Grpc.Core.Tests var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); await Calls.AsyncUnaryCall(callDetails, "abc"); await stateChangedTask; diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 54cd024670..14819b85e1 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -99,11 +99,11 @@ namespace Grpc.Core.Tests public void InfiniteDeadline() { // no deadline specified, check server sees infinite deadline - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext()); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions()); Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE")); // DateTime.MaxValue deadline specified, check server sees infinite deadline - var callDetails2 = new CallInvocationDetails(channel, TestMethod, new CallContext()); + var callDetails2 = new CallInvocationDetails(channel, TestMethod, new CallOptions()); Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE")); } @@ -113,7 +113,7 @@ namespace Grpc.Core.Tests var remainingTimeClient = TimeSpan.FromDays(7); var deadline = DateTime.UtcNow + remainingTimeClient; Thread.Sleep(1000); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"); var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); @@ -127,7 +127,7 @@ namespace Grpc.Core.Tests [Test] public void DeadlineInThePast() { - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: DateTime.MinValue)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue)); try { @@ -145,7 +145,7 @@ namespace Grpc.Core.Tests public void DeadlineExceededStatusOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); try { @@ -163,7 +163,7 @@ namespace Grpc.Core.Tests public void ServerReceivesCancellationOnTimeout() { var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallContext(deadline: deadline)); + var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); try { diff --git a/src/csharp/Grpc.Core/CallContext.cs b/src/csharp/Grpc.Core/CallContext.cs deleted file mode 100644 index 2787d3f5b3..0000000000 --- a/src/csharp/Grpc.Core/CallContext.cs +++ /dev/null @@ -1,89 +0,0 @@ -#region Copyright notice and license - -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; -using System.Threading; - -using Grpc.Core.Internal; -using Grpc.Core.Utils; - -namespace Grpc.Core -{ - /// - /// Context for calls made by client. - /// - public class CallContext - { - readonly Metadata headers; - readonly DateTime deadline; - readonly CancellationToken cancellationToken; - - /// - /// Creates a new call context. - /// - /// Headers to be sent with the call. - /// Deadline for the call to finish. null means no deadline. - /// Can be used to request cancellation of the call. - public CallContext(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) - { - // TODO(jtattermusch): consider only creating metadata object once it's really needed. - this.headers = headers != null ? headers : new Metadata(); - this.deadline = deadline.HasValue ? deadline.Value : DateTime.MaxValue; - this.cancellationToken = cancellationToken; - } - - /// - /// Headers to send at the beginning of the call. - /// - public Metadata Headers - { - get { return headers; } - } - - /// - /// Call deadline. - /// - public DateTime Deadline - { - get { return deadline; } - } - - /// - /// Token that can be used for cancelling the call. - /// - public CancellationToken CancellationToken - { - get { return cancellationToken; } - } - } -} diff --git a/src/csharp/Grpc.Core/CallInvocationDetails.cs b/src/csharp/Grpc.Core/CallInvocationDetails.cs index 6678b7f430..eb23a3a209 100644 --- a/src/csharp/Grpc.Core/CallInvocationDetails.cs +++ b/src/csharp/Grpc.Core/CallInvocationDetails.cs @@ -47,22 +47,21 @@ namespace Grpc.Core readonly string host; readonly Marshaller requestMarshaller; readonly Marshaller responseMarshaller; - readonly CallContext context; + readonly CallOptions options; - - public CallInvocationDetails(Channel channel, Method method, CallContext context) : - this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, context) + public CallInvocationDetails(Channel channel, Method method, CallOptions options) : + this(channel, method.FullName, null, method.RequestMarshaller, method.ResponseMarshaller, options) { } - public CallInvocationDetails(Channel channel, string method, string host, Marshaller requestMarshaller, Marshaller responseMarshaller, CallContext context) + public CallInvocationDetails(Channel channel, string method, string host, Marshaller requestMarshaller, Marshaller responseMarshaller, CallOptions options) { this.channel = Preconditions.CheckNotNull(channel); this.method = Preconditions.CheckNotNull(method); this.host = host; this.requestMarshaller = Preconditions.CheckNotNull(requestMarshaller); this.responseMarshaller = Preconditions.CheckNotNull(responseMarshaller); - this.context = Preconditions.CheckNotNull(context); + this.options = Preconditions.CheckNotNull(options); } public Channel Channel @@ -104,15 +103,12 @@ namespace Grpc.Core return this.responseMarshaller; } } - - /// - /// Call context. - /// - public CallContext Context + + public CallOptions Options { get { - return context; + return options; } } } diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs index f54d455357..8e9739335f 100644 --- a/src/csharp/Grpc.Core/CallOptions.cs +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -49,7 +49,7 @@ namespace Grpc.Core readonly CancellationToken cancellationToken; /// - /// Creates a new call context. + /// Creates a new instance of CallOptions. /// /// Headers to be sent with the call. /// Deadline for the call to finish. null means no deadline. diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index 5cebe4e7b9..88494bb4ac 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -76,17 +76,17 @@ namespace Grpc.Core /// /// Creates a new call to given method. /// - protected CallInvocationDetails CreateCall(Method method, CallContext context) + protected CallInvocationDetails CreateCall(Method method, CallOptions options) where TRequest : class where TResponse : class { var interceptor = HeaderInterceptor; if (interceptor != null) { - interceptor(context.Headers); - context.Headers.Freeze(); + interceptor(options.Headers); + options.Headers.Freeze(); } - return new CallInvocationDetails(channel, method, context); + return new CallInvocationDetails(channel, method, options); } } } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 9370a0b2f5..52defd1965 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -48,7 +48,6 @@ - @@ -115,6 +114,7 @@ + diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 939c24acaf..414b5c4282 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -89,7 +89,7 @@ namespace Grpc.Core.Internal readingDone = true; } - using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { using (var ctx = BatchContextSafeHandle.Create()) { @@ -138,7 +138,7 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartUnary(payload, HandleUnaryResponse, metadataArray); } @@ -162,7 +162,7 @@ namespace Grpc.Core.Internal readingDone = true; unaryResponseTcs = new TaskCompletionSource(); - using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartClientStreaming(HandleUnaryResponse, metadataArray); } @@ -188,7 +188,7 @@ namespace Grpc.Core.Internal byte[] payload = UnsafeSerialize(msg); - using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartServerStreaming(payload, HandleFinished, metadataArray); } @@ -208,7 +208,7 @@ namespace Grpc.Core.Internal Initialize(callDetails.Channel.Environment.CompletionQueue); - using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Context.Headers)) + using (var metadataArray = MetadataArraySafeHandle.Create(callDetails.Options.Headers)) { call.StartDuplexStreaming(HandleFinished, metadataArray); } @@ -316,7 +316,7 @@ namespace Grpc.Core.Internal private void Initialize(CompletionQueueSafeHandle cq) { var call = callDetails.Channel.Handle.CreateCall(callDetails.Channel.Environment.CompletionRegistry, cq, - callDetails.Method, callDetails.Host, Timespec.FromDateTime(callDetails.Context.Deadline)); + callDetails.Method, callDetails.Host, Timespec.FromDateTime(callDetails.Options.Deadline)); callDetails.Channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); RegisterCancellationCallback(); @@ -325,7 +325,7 @@ namespace Grpc.Core.Internal // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called. private void RegisterCancellationCallback() { - var token = callDetails.Context.CancellationToken; + var token = callDetails.Options.CancellationToken; if (token.CanBeCanceled) { token.Register(() => this.Cancel()); -- cgit v1.2.3 From 3cd76d6ef229eed45bd148952118b39a3d3899ee Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 5 Aug 2015 15:14:35 -0700 Subject: fix tests --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 8 ++++---- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 2aac4e3dec..64ea21800f 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -46,25 +46,25 @@ namespace Grpc.Core.Tests public class ClientServerTest { const string Host = "127.0.0.1"; - const string ServiceName = "/tests.Test"; + const string ServiceName = "tests.Test"; static readonly Method EchoMethod = new Method( MethodType.Unary, - "tests.Test", + ServiceName, "Echo", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method ConcatAndEchoMethod = new Method( MethodType.ClientStreaming, - "tests.Test", + ServiceName, "ConcatAndEcho", Marshallers.StringMarshaller, Marshallers.StringMarshaller); static readonly Method NonexistentMethod = new Method( MethodType.Unary, - "tests.Test", + ServiceName, "NonexistentMethod", Marshallers.StringMarshaller, Marshallers.StringMarshaller); diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 14819b85e1..fc395b0acd 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -49,11 +49,11 @@ namespace Grpc.Core.Tests public class TimeoutsTest { const string Host = "localhost"; - const string ServiceName = "/tests.Test"; + const string ServiceName = "tests.Test"; static readonly Method TestMethod = new Method( MethodType.Unary, - "tests.Test", + ServiceName, "Test", Marshallers.StringMarshaller, Marshallers.StringMarshaller); -- cgit v1.2.3 From 5b0b392cc3e02d7014b918250d6dd1d946a68d46 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 19:07:14 -0700 Subject: introduced MockServiceHelper to ease testing --- src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 + src/csharp/Grpc.Core.Tests/MockServiceHelper.cs | 244 ++++++++++++++++++++++ src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 124 ++++------- 3 files changed, 287 insertions(+), 82 deletions(-) create mode 100644 src/csharp/Grpc.Core.Tests/MockServiceHelper.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index f2bf459dc5..55d0c98d44 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -77,6 +77,7 @@ + diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs new file mode 100644 index 0000000000..25afa30bba --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -0,0 +1,244 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Allows setting up a mock service in the client-server tests easily. + /// + public class MockServiceHelper + { + public const string ServiceName = "tests.Test"; + + public static readonly Method UnaryMethod = new Method( + MethodType.Unary, + ServiceName, + "Unary", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + public static readonly Method ClientStreamingMethod = new Method( + MethodType.ClientStreaming, + ServiceName, + "ClientStreaming", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + public static readonly Method ServerStreamingMethod = new Method( + MethodType.ServerStreaming, + ServiceName, + "ServerStreaming", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + public static readonly Method DuplexStreamingMethod = new Method( + MethodType.DuplexStreaming, + ServiceName, + "DuplexStreaming", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + readonly string host; + readonly ServerServiceDefinition serviceDefinition; + + UnaryServerMethod unaryHandler; + ClientStreamingServerMethod clientStreamingHandler; + ServerStreamingServerMethod serverStreamingHandler; + DuplexStreamingServerMethod duplexStreamingHandler; + + Server server; + Channel channel; + + public MockServiceHelper(string host = null) + { + this.host = host ?? "localhost"; + + serviceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName) + .AddMethod(UnaryMethod, (request, context) => unaryHandler(request, context)) + .AddMethod(ClientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context)) + .AddMethod(ServerStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context)) + .AddMethod(DuplexStreamingMethod, (requestStream, responseStream, context) => duplexStreamingHandler(requestStream, responseStream, context)) + .Build(); + + var defaultStatus = new Status(StatusCode.Unknown, "Default mock implementation. Please provide your own."); + + unaryHandler = new UnaryServerMethod(async (request, context) => + { + context.Status = defaultStatus; + return ""; + }); + + clientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + context.Status = defaultStatus; + return ""; + }); + + serverStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + context.Status = defaultStatus; + }); + + duplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + context.Status = defaultStatus; + }); + } + + /// + /// Returns the default server for this service and creates one if not yet created. + /// + public Server GetServer() + { + if (server == null) + { + server = new Server + { + Services = { serviceDefinition }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + } + return server; + } + + /// + /// Returns the default channel for this service and creates one if not yet created. + /// + public Channel GetChannel() + { + if (channel == null) + { + channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure); + } + return channel; + } + + public CallInvocationDetails CreateUnaryCall(CallOptions options = null) + { + options = options ?? new CallOptions(); + return new CallInvocationDetails(channel, UnaryMethod, options); + } + + public CallInvocationDetails CreateClientStreamingCall(CallOptions options = null) + { + options = options ?? new CallOptions(); + return new CallInvocationDetails(channel, ClientStreamingMethod, options); + } + + public CallInvocationDetails CreateServerStreamingCall(CallOptions options = null) + { + options = options ?? new CallOptions(); + return new CallInvocationDetails(channel, ServerStreamingMethod, options); + } + + public CallInvocationDetails CreateDuplexStreamingCall(CallOptions options = null) + { + options = options ?? new CallOptions(); + return new CallInvocationDetails(channel, DuplexStreamingMethod, options); + } + + public string Host + { + get + { + return this.host; + } + } + + public ServerServiceDefinition ServiceDefinition + { + get + { + return this.serviceDefinition; + } + } + + public UnaryServerMethod UnaryHandler + { + get + { + return this.unaryHandler; + } + set + { + unaryHandler = value; + } + } + + public ClientStreamingServerMethod ClientStreamingHandler + { + get + { + return this.clientStreamingHandler; + } + set + { + clientStreamingHandler = value; + } + } + + public ServerStreamingServerMethod ServerStreamingHandler + { + get + { + return this.serverStreamingHandler; + } + set + { + serverStreamingHandler = value; + } + } + + public DuplexStreamingServerMethod DuplexStreamingHandler + { + get + { + return this.duplexStreamingHandler; + } + set + { + duplexStreamingHandler = value; + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index fc395b0acd..239fc95cb6 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -48,38 +48,15 @@ namespace Grpc.Core.Tests /// public class TimeoutsTest { - const string Host = "localhost"; - const string ServiceName = "tests.Test"; - - static readonly Method TestMethod = new Method( - MethodType.Unary, - ServiceName, - "Test", - Marshallers.StringMarshaller, - Marshallers.StringMarshaller); - - static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName) - .AddMethod(TestMethod, TestMethodHandler) - .Build(); - - // provides a way how to retrieve an out-of-band result value from server handler - static TaskCompletionSource stringFromServerHandlerTcs; - + MockServiceHelper helper = new MockServiceHelper(); Server server; Channel channel; [SetUp] public void Init() { - server = new Server - { - Services = { ServiceDefinition }, - Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } - }; - server.Start(); - channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); - - stringFromServerHandlerTcs = new TaskCompletionSource(); + server = helper.GetServer(); + channel = helper.GetChannel(); } [TearDown] @@ -98,40 +75,44 @@ namespace Grpc.Core.Tests [Test] public void InfiniteDeadline() { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + Assert.AreEqual(DateTime.MaxValue, context.Deadline); + return "PASS"; + }); + // no deadline specified, check server sees infinite deadline - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions()); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE")); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); // DateTime.MaxValue deadline specified, check server sees infinite deadline - var callDetails2 = new CallInvocationDetails(channel, TestMethod, new CallOptions()); - Assert.AreEqual("DATETIME_MAXVALUE", Calls.BlockingUnaryCall(callDetails2, "RETURN_DEADLINE")); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MaxValue)), "abc")); } [Test] public void DeadlineTransferredToServer() { - var remainingTimeClient = TimeSpan.FromDays(7); - var deadline = DateTime.UtcNow + remainingTimeClient; - Thread.Sleep(1000); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); - - var serverDeadlineTicksString = Calls.BlockingUnaryCall(callDetails, "RETURN_DEADLINE"); - var serverDeadline = new DateTime(long.Parse(serverDeadlineTicksString), DateTimeKind.Utc); - - // A fairly relaxed check that the deadline set by client and deadline seen by server - // are in agreement. C core takes care of the work with transferring deadline over the wire, - // so we don't need an exact check here. - Assert.IsTrue(Math.Abs((deadline - serverDeadline).TotalMilliseconds) < 5000); + var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7); + + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + // A fairly relaxed check that the deadline set by client and deadline seen by server + // are in agreement. C core takes care of the work with transferring deadline over the wire, + // so we don't need an exact check here. + Assert.IsTrue(Math.Abs((clientDeadline - context.Deadline).TotalMilliseconds) < 5000); + return "PASS"; + }); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: clientDeadline)), "abc"); } [Test] public void DeadlineInThePast() { - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: DateTime.MinValue)); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + await Task.Delay(60000); + return "FAIL"; + }); try { - Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc"); Assert.Fail(); } catch (RpcException e) @@ -144,12 +125,14 @@ namespace Grpc.Core.Tests [Test] public void DeadlineExceededStatusOnTimeout() { - var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + await Task.Delay(60000); + return "FAIL"; + }); try { - Calls.BlockingUnaryCall(callDetails, "TIMEOUT"); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"); Assert.Fail(); } catch (RpcException e) @@ -162,12 +145,20 @@ namespace Grpc.Core.Tests [Test] public void ServerReceivesCancellationOnTimeout() { - var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)); - var callDetails = new CallInvocationDetails(channel, TestMethod, new CallOptions(deadline: deadline)); + string receivedCancellation = "NO"; + + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + // wait until cancellation token is fired. + var tcs = new TaskCompletionSource(); + context.CancellationToken.Register(() => { tcs.SetResult(null); }); + await tcs.Task; + receivedCancellation = "YES"; + return ""; + }); try { - Calls.BlockingUnaryCall(callDetails, "CHECK_CANCELLATION_RECEIVED"); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"); Assert.Fail(); } catch (RpcException e) @@ -175,38 +166,7 @@ namespace Grpc.Core.Tests // We can't guarantee the status code is always DeadlineExceeded. See issue #2685. Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } - Assert.AreEqual("CANCELLED", stringFromServerHandlerTcs.Task.Result); - } - - private static async Task TestMethodHandler(string request, ServerCallContext context) - { - if (request == "TIMEOUT") - { - await Task.Delay(60000); - return ""; - } - - if (request == "RETURN_DEADLINE") - { - if (context.Deadline == DateTime.MaxValue) - { - return "DATETIME_MAXVALUE"; - } - - return context.Deadline.Ticks.ToString(); - } - - if (request == "CHECK_CANCELLATION_RECEIVED") - { - // wait until cancellation token is fired. - var tcs = new TaskCompletionSource(); - context.CancellationToken.Register(() => { tcs.SetResult(null); }); - await tcs.Task; - stringFromServerHandlerTcs.SetResult("CANCELLED"); - return ""; - } - - return ""; + Assert.AreEqual("YES", receivedCancellation); } } } -- cgit v1.2.3 From a4291e7073a40777bfe8845bd926612a76e154f6 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 19:13:31 -0700 Subject: fixing tests --- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 239fc95cb6..51709813bf 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -48,14 +48,17 @@ namespace Grpc.Core.Tests /// public class TimeoutsTest { - MockServiceHelper helper = new MockServiceHelper(); + MockServiceHelper helper; Server server; Channel channel; [SetUp] public void Init() { + helper = new MockServiceHelper(); + server = helper.GetServer(); + server.Start(); channel = helper.GetChannel(); } @@ -145,6 +148,7 @@ namespace Grpc.Core.Tests [Test] public void ServerReceivesCancellationOnTimeout() { + object myLock = new object(); string receivedCancellation = "NO"; helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { @@ -152,7 +156,10 @@ namespace Grpc.Core.Tests var tcs = new TaskCompletionSource(); context.CancellationToken.Register(() => { tcs.SetResult(null); }); await tcs.Task; - receivedCancellation = "YES"; + lock (myLock) + { + receivedCancellation = "YES"; + } return ""; }); @@ -166,7 +173,11 @@ namespace Grpc.Core.Tests // We can't guarantee the status code is always DeadlineExceeded. See issue #2685. Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } - Assert.AreEqual("YES", receivedCancellation); + + lock (myLock) + { + Assert.AreEqual("YES", receivedCancellation); + } } } } -- cgit v1.2.3 From 0abb84746ce3f35bb859c0b5a88afa5cff5e2ef0 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 20:28:44 -0700 Subject: big facelift of test code --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 282 ++++++++++--------------- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 39 +--- 2 files changed, 117 insertions(+), 204 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 64ea21800f..eb9cd7cf0c 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -46,47 +46,18 @@ namespace Grpc.Core.Tests public class ClientServerTest { const string Host = "127.0.0.1"; - const string ServiceName = "tests.Test"; - - static readonly Method EchoMethod = new Method( - MethodType.Unary, - ServiceName, - "Echo", - Marshallers.StringMarshaller, - Marshallers.StringMarshaller); - - static readonly Method ConcatAndEchoMethod = new Method( - MethodType.ClientStreaming, - ServiceName, - "ConcatAndEcho", - Marshallers.StringMarshaller, - Marshallers.StringMarshaller); - - static readonly Method NonexistentMethod = new Method( - MethodType.Unary, - ServiceName, - "NonexistentMethod", - Marshallers.StringMarshaller, - Marshallers.StringMarshaller); - - static readonly ServerServiceDefinition ServiceDefinition = ServerServiceDefinition.CreateBuilder(ServiceName) - .AddMethod(EchoMethod, EchoHandler) - .AddMethod(ConcatAndEchoMethod, ConcatAndEchoHandler) - .Build(); + MockServiceHelper helper; Server server; Channel channel; [SetUp] public void Init() { - server = new Server - { - Services = { ServiceDefinition }, - Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } - }; + helper = new MockServiceHelper(Host); + server = helper.GetServer(); server.Start(); - channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure); + channel = helper.GetChannel(); } [TearDown] @@ -103,86 +74,79 @@ namespace Grpc.Core.Tests } [Test] - public void UnaryCall() + public async Task UnaryCall() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - Assert.AreEqual("ABC", Calls.BlockingUnaryCall(callDetails, "ABC")); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return request; + }); + + Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC")); + + Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC")); } [Test] public void UnaryCall_ServerHandlerThrows() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - try + helper.UnaryHandler = new UnaryServerMethod((request, context) => { - Calls.BlockingUnaryCall(callDetails, "THROW"); - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); - } + throw new Exception("This was thrown on purpose by a test"); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); + + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode); } [Test] public void UnaryCall_ServerHandlerThrowsRpcException() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - try - { - Calls.BlockingUnaryCall(callDetails, "THROW_UNAUTHENTICATED"); - Assert.Fail(); - } - catch (RpcException e) + helper.UnaryHandler = new UnaryServerMethod((request, context) => { - Assert.AreEqual(StatusCode.Unauthenticated, e.Status.StatusCode); - } + throw new RpcException(new Status(StatusCode.Unauthenticated, "")); + }); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); + + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); } [Test] public void UnaryCall_ServerHandlerSetsStatus() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - try - { - Calls.BlockingUnaryCall(callDetails, "SET_UNAUTHENTICATED"); - Assert.Fail(); - } - catch (RpcException e) + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { - Assert.AreEqual(StatusCode.Unauthenticated, e.Status.StatusCode); - } - } + context.Status = new Status(StatusCode.Unauthenticated, ""); + return ""; + }); - [Test] - public async Task AsyncUnaryCall() - { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - var result = await Calls.AsyncUnaryCall(callDetails, "ABC"); - Assert.AreEqual("ABC", result); - } + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); - [Test] - public async Task AsyncUnaryCall_ServerHandlerThrows() - { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - try - { - await Calls.AsyncUnaryCall(callDetails, "THROW"); - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); - } + var ex2 = Assert.Throws(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); + Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); } [Test] public async Task ClientStreamingCall() { - var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallOptions()); - var call = Calls.AsyncClientStreamingCall(callDetails); + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + string result = ""; + await requestStream.ForEach(async (request) => + { + result += request; + }); + await Task.Delay(100); + return result; + }); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); await call.RequestStream.WriteAll(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); } @@ -190,34 +154,46 @@ namespace Grpc.Core.Tests [Test] public async Task ClientStreamingCall_CancelAfterBegin() { + var barrier = new TaskCompletionSource(); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + barrier.SetResult(null); + await requestStream.ToList(); + return ""; + }); + var cts = new CancellationTokenSource(); - var callDetails = new CallInvocationDetails(channel, ConcatAndEchoMethod, new CallOptions(cancellationToken: cts.Token)); - var call = Calls.AsyncClientStreamingCall(callDetails); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); - // TODO(jtattermusch): we need this to ensure call has been initiated once we cancel it. - await Task.Delay(1000); + await barrier.Task; // make sure the handler has started. cts.Cancel(); - try - { - await call.ResponseAsync; - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Cancelled, e.Status.StatusCode); - } + var ex = Assert.Throws(async () => await call.ResponseAsync); + Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode); } [Test] public void AsyncUnaryCall_EchoMetadata() { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + foreach (Metadata.Entry metadataEntry in context.RequestHeaders) + { + if (metadataEntry.Key != "user-agent") + { + context.ResponseTrailers.Add(metadataEntry); + } + } + return ""; + }); + var headers = new Metadata { new Metadata.Entry("ascii-header", "abcdefg"), new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions(headers: headers)); - var call = Calls.AsyncUnaryCall(callDetails, "ABC"); + var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC"); Assert.AreEqual("ABC", call.ResponseAsync.Result); @@ -236,15 +212,13 @@ namespace Grpc.Core.Tests public void UnaryCall_DisposedChannel() { channel.Dispose(); - - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(callDetails, "ABC")); + Assert.Throws(typeof(ObjectDisposedException), () => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC")); } [Test] public void UnaryCallPerformance() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); + var callDetails = helper.CreateUnaryCall(); BenchmarkUtil.RunBenchmark(100, 100, () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); } @@ -252,44 +226,57 @@ namespace Grpc.Core.Tests [Test] public void UnknownMethodHandler() { - var callDetails = new CallInvocationDetails(channel, NonexistentMethod, new CallOptions()); - try - { - Calls.BlockingUnaryCall(callDetails, "ABC"); - Assert.Fail(); - } - catch (RpcException e) - { - Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode); - } + var nonexistentMethod = new Method( + MethodType.Unary, + MockServiceHelper.ServiceName, + "NonExistentMethod", + Marshallers.StringMarshaller, + Marshallers.StringMarshaller); + + var callDetails = new CallInvocationDetails(channel, nonexistentMethod, new CallOptions()); + + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(callDetails, "abc")); + Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode); } [Test] public void UserAgentStringPresent() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - string userAgent = Calls.BlockingUnaryCall(callDetails, "RETURN-USER-AGENT"); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value; + }); + + string userAgent = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); } [Test] public void PeerInfoPresent() { - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - string peer = Calls.BlockingUnaryCall(callDetails, "RETURN-PEER"); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return context.Peer; + }); + + string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); Assert.IsTrue(peer.Contains(Host)); } [Test] public async Task Channel_WaitForStateChangedAsync() { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + return request; + }); + Assert.Throws(typeof(TaskCanceledException), async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10))); var stateChangedTask = channel.WaitForStateChangedAsync(channel.State); - var callDetails = new CallInvocationDetails(channel, EchoMethod, new CallOptions()); - await Calls.AsyncUnaryCall(callDetails, "abc"); + await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"); await stateChangedTask; Assert.AreEqual(ChannelState.Ready, channel.State); @@ -300,62 +287,9 @@ namespace Grpc.Core.Tests { await channel.ConnectAsync(); Assert.AreEqual(ChannelState.Ready, channel.State); + await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(1000)); Assert.AreEqual(ChannelState.Ready, channel.State); } - - private static async Task EchoHandler(string request, ServerCallContext context) - { - foreach (Metadata.Entry metadataEntry in context.RequestHeaders) - { - if (metadataEntry.Key != "user-agent") - { - context.ResponseTrailers.Add(metadataEntry); - } - } - - if (request == "RETURN-USER-AGENT") - { - return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value; - } - - if (request == "RETURN-PEER") - { - return context.Peer; - } - - if (request == "THROW") - { - throw new Exception("This was thrown on purpose by a test"); - } - - if (request == "THROW_UNAUTHENTICATED") - { - throw new RpcException(new Status(StatusCode.Unauthenticated, "")); - } - - if (request == "SET_UNAUTHENTICATED") - { - context.Status = new Status(StatusCode.Unauthenticated, ""); - } - - return request; - } - - private static async Task ConcatAndEchoHandler(IAsyncStreamReader requestStream, ServerCallContext context) - { - string result = ""; - await requestStream.ForEach(async (request) => - { - if (request == "THROW") - { - throw new Exception("This was thrown on purpose by a test"); - } - result += request; - }); - // simulate processing takes some time. - await Task.Delay(250); - return result; - } } } diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index 51709813bf..a52020cf40 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -113,16 +113,9 @@ namespace Grpc.Core.Tests return "FAIL"; }); - try - { - Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc"); - Assert.Fail(); - } - catch (RpcException e) - { - // We can't guarantee the status code always DeadlineExceeded. See issue #2685. - Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); - } + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.MinValue)), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } [Test] @@ -133,16 +126,9 @@ namespace Grpc.Core.Tests return "FAIL"; }); - try - { - Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"); - Assert.Fail(); - } - catch (RpcException e) - { - // We can't guarantee the status code always DeadlineExceeded. See issue #2685. - Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); - } + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); } [Test] @@ -163,16 +149,9 @@ namespace Grpc.Core.Tests return ""; }); - try - { - Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc"); - Assert.Fail(); - } - catch (RpcException e) - { - // We can't guarantee the status code is always DeadlineExceeded. See issue #2685. - Assert.Contains(e.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); - } + var ex = Assert.Throws(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(new CallOptions(deadline: DateTime.UtcNow.Add(TimeSpan.FromSeconds(5)))), "abc")); + // We can't guarantee the status code always DeadlineExceeded. See issue #2685. + Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); lock (myLock) { -- cgit v1.2.3 From 2615f39b208efec60619ee431e17acbf4d60a458 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 20:41:26 -0700 Subject: fixing tests --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 9 ++++++--- src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 15 ++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index eb9cd7cf0c..08c80bbe53 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -174,7 +174,7 @@ namespace Grpc.Core.Tests } [Test] - public void AsyncUnaryCall_EchoMetadata() + public async Task AsyncUnaryCall_EchoMetadata() { helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { @@ -194,8 +194,7 @@ namespace Grpc.Core.Tests new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), }; var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC"); - - Assert.AreEqual("ABC", call.ResponseAsync.Result); + await call; Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); @@ -218,6 +217,10 @@ namespace Grpc.Core.Tests [Test] public void UnaryCallPerformance() { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + return request; + }); + var callDetails = helper.CreateUnaryCall(); BenchmarkUtil.RunBenchmark(100, 100, () => { Calls.BlockingUnaryCall(callDetails, "ABC"); }); diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index a52020cf40..ead0b1854b 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -132,20 +132,16 @@ namespace Grpc.Core.Tests } [Test] - public void ServerReceivesCancellationOnTimeout() + public async Task ServerReceivesCancellationOnTimeout() { - object myLock = new object(); - string receivedCancellation = "NO"; + var serverReceivedCancellationTcs = new TaskCompletionSource(); helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { // wait until cancellation token is fired. var tcs = new TaskCompletionSource(); context.CancellationToken.Register(() => { tcs.SetResult(null); }); await tcs.Task; - lock (myLock) - { - receivedCancellation = "YES"; - } + serverReceivedCancellationTcs.SetResult(true); return ""; }); @@ -153,10 +149,7 @@ namespace Grpc.Core.Tests // We can't guarantee the status code always DeadlineExceeded. See issue #2685. Assert.Contains(ex.Status.StatusCode, new[] { StatusCode.DeadlineExceeded, StatusCode.Internal }); - lock (myLock) - { - Assert.AreEqual("YES", receivedCancellation); - } + Assert.IsTrue(await serverReceivedCancellationTcs.Task); } } } -- cgit v1.2.3 From c75c57c5af620491d0043047533fa0e2f078b09f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 22:07:40 -0700 Subject: added ResponseHeadersTest, fixed stylecop issues --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 3 +- src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 + src/csharp/Grpc.Core.Tests/MockServiceHelper.cs | 4 + src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs | 139 ++++++++++++++++++++++ src/csharp/Grpc.Core.Tests/TimeoutsTest.cs | 15 ++- src/csharp/Grpc.Core/Internal/AsyncCall.cs | 2 +- src/csharp/Grpc.Core/Internal/AsyncCallServer.cs | 3 +- src/csharp/Grpc.Core/WriteOptions.cs | 1 - 8 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 08c80bbe53..f56fb744a6 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -217,7 +217,8 @@ namespace Grpc.Core.Tests [Test] public void UnaryCallPerformance() { - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { return request; }); diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 55d0c98d44..4692d958a0 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -78,6 +78,7 @@ + diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs index 25afa30bba..b642286b11 100644 --- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -199,6 +199,7 @@ namespace Grpc.Core.Tests { return this.unaryHandler; } + set { unaryHandler = value; @@ -211,6 +212,7 @@ namespace Grpc.Core.Tests { return this.clientStreamingHandler; } + set { clientStreamingHandler = value; @@ -223,6 +225,7 @@ namespace Grpc.Core.Tests { return this.serverStreamingHandler; } + set { serverStreamingHandler = value; @@ -235,6 +238,7 @@ namespace Grpc.Core.Tests { return this.duplexStreamingHandler; } + set { duplexStreamingHandler = value; diff --git a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs new file mode 100644 index 0000000000..b024488549 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs @@ -0,0 +1,139 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + /// + /// Tests for response headers support. + /// + public class ResponseHeadersTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + Metadata headers; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + + headers = new Metadata + { + new Metadata.Entry("ascii-header", "abcdefg"), + }; + } + + [TearDown] + public void Cleanup() + { + channel.Dispose(); + server.ShutdownAsync().Wait(); + } + + [TestFixtureTearDown] + public void CleanupClass() + { + GrpcEnvironment.Shutdown(); + } + + [Test] + public void WriteResponseHeaders_NullNotAllowed() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.Throws(typeof(NullReferenceException), async () => await context.WriteResponseHeadersAsync(null)); + return "PASS"; + }); + + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + + [Test] + public void WriteResponseHeaders_AllowedOnlyOnce() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + await context.WriteResponseHeadersAsync(headers); + try + { + await context.WriteResponseHeadersAsync(headers); + Assert.Fail(); + } + catch (InvalidOperationException expected) + { + } + return "PASS"; + }); + + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + + [Test] + public async Task WriteResponseHeaders_NotAllowedAfterWrite() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod(async (request, responseStream, context) => + { + await responseStream.WriteAsync("A"); + try + { + await context.WriteResponseHeadersAsync(headers); + Assert.Fail(); + } + catch (InvalidOperationException expected) + { + } + await responseStream.WriteAsync("B"); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); + var responses = await call.ResponseStream.ToList(); + CollectionAssert.AreEqual(new[] { "A", "B" }, responses); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs index ead0b1854b..d875d601b9 100644 --- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs +++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs @@ -78,7 +78,8 @@ namespace Grpc.Core.Tests [Test] public void InfiniteDeadline() { - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { Assert.AreEqual(DateTime.MaxValue, context.Deadline); return "PASS"; }); @@ -95,7 +96,8 @@ namespace Grpc.Core.Tests { var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7); - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { // A fairly relaxed check that the deadline set by client and deadline seen by server // are in agreement. C core takes care of the work with transferring deadline over the wire, // so we don't need an exact check here. @@ -108,7 +110,8 @@ namespace Grpc.Core.Tests [Test] public void DeadlineInThePast() { - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { await Task.Delay(60000); return "FAIL"; }); @@ -121,7 +124,8 @@ namespace Grpc.Core.Tests [Test] public void DeadlineExceededStatusOnTimeout() { - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { await Task.Delay(60000); return "FAIL"; }); @@ -136,7 +140,8 @@ namespace Grpc.Core.Tests { var serverReceivedCancellationTcs = new TaskCompletionSource(); - helper.UnaryHandler = new UnaryServerMethod(async (request, context) => { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { // wait until cancellation token is fired. var tcs = new TaskCompletionSource(); context.CancellationToken.Register(() => { tcs.SetResult(null); }); diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index c8c2449ee6..df5c07e4c4 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -279,7 +279,7 @@ namespace Grpc.Core.Internal } } - public CallInvocationDetails Details + public CallInvocationDetails Details { get { diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 9eac7f7b61..1704b9afbf 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -107,10 +107,11 @@ namespace Grpc.Core.Internal { lock (myLock) { + Preconditions.CheckNotNull(headers, "metadata"); Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); Preconditions.CheckState(!initialMetadataSent, "Response headers can only be sent once per call."); - Preconditions.CheckState(streamingWritesCounter > 0, "Response headers can only be sent before the first write starts."); + Preconditions.CheckState(streamingWritesCounter == 0, "Response headers can only be sent before the first write starts."); CheckSendingAllowed(); Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); diff --git a/src/csharp/Grpc.Core/WriteOptions.cs b/src/csharp/Grpc.Core/WriteOptions.cs index ec4a7dd8cd..7ef3189d76 100644 --- a/src/csharp/Grpc.Core/WriteOptions.cs +++ b/src/csharp/Grpc.Core/WriteOptions.cs @@ -54,7 +54,6 @@ namespace Grpc.Core NoCompress = 0x2 } - /// /// Options for write operations. /// -- cgit v1.2.3 From 5321d49b51e00d42af730dddeb8c85f12afeb8ea Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Fri, 7 Aug 2015 23:21:27 -0700 Subject: fixed writeOptions and added test --- src/csharp/Grpc.Core.Tests/CompressionTest.cs | 128 +++++++++++++++++++++ src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 + src/csharp/Grpc.Core/CallOptions.cs | 1 + src/csharp/Grpc.Core/IAsyncStreamWriter.cs | 4 - src/csharp/Grpc.Core/Internal/AsyncCall.cs | 8 +- src/csharp/Grpc.Core/Internal/AsyncCallServer.cs | 8 +- src/csharp/Grpc.Core/Internal/CallSafeHandle.cs | 30 ++--- .../Grpc.Core/Internal/ClientRequestStream.cs | 2 +- .../Grpc.Core/Internal/ServerResponseStream.cs | 4 +- src/csharp/ext/grpc_csharp_ext.c | 29 +++-- 10 files changed, 170 insertions(+), 45 deletions(-) create mode 100644 src/csharp/Grpc.Core.Tests/CompressionTest.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/CompressionTest.cs b/src/csharp/Grpc.Core.Tests/CompressionTest.cs new file mode 100644 index 0000000000..492369968e --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/CompressionTest.cs @@ -0,0 +1,128 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class CompressionTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.Dispose(); + server.ShutdownAsync().Wait(); + } + + [TestFixtureTearDown] + public void CleanupClass() + { + GrpcEnvironment.Shutdown(); + } + + [Test] + public void WriteOptions_Unary() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + return request; + }); + + var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress)); + Calls.BlockingUnaryCall(helper.CreateUnaryCall(callOptions), "abc"); + } + + [Test] + public async Task WriteOptions_DuplexStreaming() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod(async (requestStream, responseStream, context) => + { + await requestStream.ToList(); + + context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + + await context.WriteResponseHeadersAsync(new Metadata { new Metadata.Entry("ascii-header", "abcdefg") }); + + await responseStream.WriteAsync("X"); + + responseStream.WriteOptions = null; + await responseStream.WriteAsync("Y"); + + responseStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + await responseStream.WriteAsync("Z"); + }); + + var callOptions = new CallOptions(writeOptions: new WriteOptions(WriteFlags.NoCompress)); + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(callOptions)); + + // check that write options from call options are propagated to request stream. + Assert.IsTrue((call.RequestStream.WriteOptions.Flags & WriteFlags.NoCompress) != 0); + + call.RequestStream.WriteOptions = new WriteOptions(); + await call.RequestStream.WriteAsync("A"); + + call.RequestStream.WriteOptions = null; + await call.RequestStream.WriteAsync("B"); + + call.RequestStream.WriteOptions = new WriteOptions(WriteFlags.NoCompress); + await call.RequestStream.WriteAsync("C"); + + await call.RequestStream.CompleteAsync(); + + await call.ResponseStream.ToList(); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 4692d958a0..58fa7c645f 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -79,6 +79,7 @@ + diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs index e8d0b0647f..a08986d77e 100644 --- a/src/csharp/Grpc.Core/CallOptions.cs +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -63,6 +63,7 @@ namespace Grpc.Core // TODO(jtattermusch): allow null value of deadline? this.deadline = deadline.HasValue ? deadline.Value : DateTime.MaxValue; this.cancellationToken = cancellationToken; + this.writeOptions = writeOptions; } /// diff --git a/src/csharp/Grpc.Core/IAsyncStreamWriter.cs b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs index b554b6e266..4e2acb9c71 100644 --- a/src/csharp/Grpc.Core/IAsyncStreamWriter.cs +++ b/src/csharp/Grpc.Core/IAsyncStreamWriter.cs @@ -56,10 +56,6 @@ namespace Grpc.Core /// If null, default options will be used. /// Once set, this property maintains its value across subsequent /// writes. - /// Internally, closing the stream is on client and sending - /// status from server is treated as a write, so write options - /// are also applied to these operations. - /// /// The write options. WriteOptions WriteOptions { get; set; } } diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index df5c07e4c4..dee31c670e 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -165,7 +165,7 @@ namespace Grpc.Core.Internal unaryResponseTcs = new TaskCompletionSource(); using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartClientStreaming(HandleUnaryResponse, metadataArray, GetWriteFlagsForCall()); + call.StartClientStreaming(HandleUnaryResponse, metadataArray); } return unaryResponseTcs.Task; @@ -211,7 +211,7 @@ namespace Grpc.Core.Internal using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers)) { - call.StartDuplexStreaming(HandleFinished, metadataArray, GetWriteFlagsForCall()); + call.StartDuplexStreaming(HandleFinished, metadataArray); } } } @@ -239,14 +239,14 @@ namespace Grpc.Core.Internal /// Only one pending send action is allowed at any given time. /// completionDelegate is called when the operation finishes. /// - public void StartSendCloseFromClient(WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + public void StartSendCloseFromClient(AsyncCompletionDelegate completionDelegate) { lock (myLock) { Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null"); CheckSendingAllowed(); - call.StartSendCloseFromClient(HandleHalfclosed, writeFlags); + call.StartSendCloseFromClient(HandleHalfclosed); halfcloseRequested = true; sendCompletionDelegate = completionDelegate; diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs index 1704b9afbf..3710a65d6b 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs @@ -103,7 +103,7 @@ namespace Grpc.Core.Internal /// to make things simpler. /// completionDelegate is invoked upon completion. /// - public void StartSendInitialMetadata(Metadata headers, WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + public void StartSendInitialMetadata(Metadata headers, AsyncCompletionDelegate completionDelegate) { lock (myLock) { @@ -118,7 +118,7 @@ namespace Grpc.Core.Internal using (var metadataArray = MetadataArraySafeHandle.Create(headers)) { - call.StartSendInitialMetadata(HandleSendFinished, metadataArray, writeFlags); + call.StartSendInitialMetadata(HandleSendFinished, metadataArray); } this.initialMetadataSent = true; @@ -131,7 +131,7 @@ namespace Grpc.Core.Internal /// Only one pending send action is allowed at any given time. /// completionDelegate is called when the operation finishes. /// - public void StartSendStatusFromServer(Status status, Metadata trailers, WriteFlags writeFlags, AsyncCompletionDelegate completionDelegate) + public void StartSendStatusFromServer(Status status, Metadata trailers, AsyncCompletionDelegate completionDelegate) { lock (myLock) { @@ -140,7 +140,7 @@ namespace Grpc.Core.Internal using (var metadataArray = MetadataArraySafeHandle.Create(trailers)) { - call.StartSendStatusFromServer(HandleHalfclosed, status, metadataArray, writeFlags, !initialMetadataSent); + call.StartSendStatusFromServer(HandleHalfclosed, status, metadataArray, !initialMetadataSent); } halfcloseRequested = true; readingDone = true; diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 02502a6f01..1b9d0abbc4 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -57,7 +57,7 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags); + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call, @@ -66,7 +66,7 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags); + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call, @@ -74,11 +74,11 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call, - BatchContextSafeHandle ctx, WriteFlags writeFlags); + BatchContextSafeHandle ctx); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call, - BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags, bool sendEmptyInitialMetadata); + BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata); [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call, @@ -90,7 +90,7 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags); + BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); [DllImport("grpc_csharp_ext.dll")] static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); @@ -121,11 +121,11 @@ namespace Grpc.Core.Internal .CheckOk(); } - public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); completionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_call_start_client_streaming(this, ctx, metadataArray, writeFlags).CheckOk(); + grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk(); } public void StartServerStreaming(BatchCompletionDelegate callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) @@ -135,11 +135,11 @@ namespace Grpc.Core.Internal grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk(); } - public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); completionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray, writeFlags).CheckOk(); + grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk(); } public void StartSendMessage(BatchCompletionDelegate callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata) @@ -149,18 +149,18 @@ namespace Grpc.Core.Internal grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk(); } - public void StartSendCloseFromClient(BatchCompletionDelegate callback, WriteFlags writeFlags) + public void StartSendCloseFromClient(BatchCompletionDelegate callback) { var ctx = BatchContextSafeHandle.Create(); completionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_call_send_close_from_client(this, ctx, writeFlags).CheckOk(); + grpcsharp_call_send_close_from_client(this, ctx).CheckOk(); } - public void StartSendStatusFromServer(BatchCompletionDelegate callback, Status status, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags, bool sendEmptyInitialMetadata) + public void StartSendStatusFromServer(BatchCompletionDelegate callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata) { var ctx = BatchContextSafeHandle.Create(); completionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, writeFlags, sendEmptyInitialMetadata).CheckOk(); + grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk(); } public void StartReceiveMessage(BatchCompletionDelegate callback) @@ -177,11 +177,11 @@ namespace Grpc.Core.Internal grpcsharp_call_start_serverside(this, ctx).CheckOk(); } - public void StartSendInitialMetadata(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags) + public void StartSendInitialMetadata(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray) { var ctx = BatchContextSafeHandle.Create(); completionRegistry.RegisterBatchCompletion(ctx, callback); - grpcsharp_call_send_initial_metadata(this, ctx, metadataArray, writeFlags).CheckOk(); + grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk(); } public void Cancel() diff --git a/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs b/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs index dd7f4256c4..013f00ff6f 100644 --- a/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs +++ b/src/csharp/Grpc.Core/Internal/ClientRequestStream.cs @@ -58,7 +58,7 @@ namespace Grpc.Core.Internal public Task CompleteAsync() { var taskSource = new AsyncCompletionTaskSource(); - call.StartSendCloseFromClient(GetWriteFlags(), taskSource.CompletionDelegate); + call.StartSendCloseFromClient(taskSource.CompletionDelegate); return taskSource.Task; } diff --git a/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs b/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs index 5dcd5a7220..03e39efc02 100644 --- a/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs +++ b/src/csharp/Grpc.Core/Internal/ServerResponseStream.cs @@ -60,14 +60,14 @@ namespace Grpc.Core.Internal public Task WriteStatusAsync(Status status, Metadata trailers) { var taskSource = new AsyncCompletionTaskSource(); - call.StartSendStatusFromServer(status, trailers, GetWriteFlags(), taskSource.CompletionDelegate); + call.StartSendStatusFromServer(status, trailers, taskSource.CompletionDelegate); return taskSource.Task; } public Task WriteResponseHeadersAsync(Metadata responseHeaders) { var taskSource = new AsyncCompletionTaskSource(); - call.StartSendInitialMetadata(responseHeaders, GetWriteFlags(), taskSource.CompletionDelegate); + call.StartSendInitialMetadata(responseHeaders, taskSource.CompletionDelegate); return taskSource.Task; } diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index cb138064e1..5d17360d6a 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -506,7 +506,7 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx, ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); @@ -514,7 +514,7 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx, ops[1].flags = write_flags; ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - ops[2].flags = write_flags; + ops[2].flags = 0; ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -542,7 +542,7 @@ grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_client_streaming(grpc_call *call, grpcsharp_batch_context *ctx, - grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) { + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[4]; ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; @@ -551,7 +551,7 @@ grpcsharp_call_start_client_streaming(grpc_call *call, ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -587,7 +587,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; ops[1].op = GRPC_OP_SEND_MESSAGE; ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len); @@ -595,7 +595,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( ops[1].flags = write_flags; ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - ops[2].flags = write_flags; + ops[2].flags = 0; ops[3].op = GRPC_OP_RECV_INITIAL_METADATA; ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -619,7 +619,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming( GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_duplex_streaming(grpc_call *call, grpcsharp_batch_context *ctx, - grpc_metadata_array *initial_metadata, gpr_uint32 write_flags) { + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[3]; ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; @@ -628,7 +628,7 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call, ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; ops[1].op = GRPC_OP_RECV_INITIAL_METADATA; ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata); @@ -671,11 +671,11 @@ grpcsharp_call_send_message(grpc_call *call, grpcsharp_batch_context *ctx, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_close_from_client(grpc_call *call, - grpcsharp_batch_context *ctx, gpr_uint32 write_flags) { + grpcsharp_batch_context *ctx) { /* TODO: don't use magic number */ grpc_op ops[1]; ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; - ops[0].flags = write_flags; + ops[0].flags = 0; return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx); } @@ -683,7 +683,7 @@ grpcsharp_call_send_close_from_client(grpc_call *call, GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server( grpc_call *call, grpcsharp_batch_context *ctx, grpc_status_code status_code, const char *status_details, grpc_metadata_array *trailing_metadata, - gpr_uint32 write_flags, gpr_int32 send_empty_initial_metadata) { + gpr_int32 send_empty_initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[2]; size_t nops = send_empty_initial_metadata ? 2 : 1; @@ -697,7 +697,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server( ctx->send_status_from_server.trailing_metadata.count; ops[0].data.send_status_from_server.trailing_metadata = ctx->send_status_from_server.trailing_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; ops[1].op = GRPC_OP_SEND_INITIAL_METADATA; ops[1].data.send_initial_metadata.count = 0; ops[1].data.send_initial_metadata.metadata = NULL; @@ -731,8 +731,7 @@ grpcsharp_call_start_serverside(grpc_call *call, grpcsharp_batch_context *ctx) { GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_initial_metadata(grpc_call *call, grpcsharp_batch_context *ctx, - grpc_metadata_array *initial_metadata, - gpr_uint32 write_flags) { + grpc_metadata_array *initial_metadata) { /* TODO: don't use magic number */ grpc_op ops[1]; ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; @@ -741,7 +740,7 @@ grpcsharp_call_send_initial_metadata(grpc_call *call, ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count; ops[0].data.send_initial_metadata.metadata = ctx->send_initial_metadata.metadata; - ops[0].flags = write_flags; + ops[0].flags = 0; return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx); } -- cgit v1.2.3 From 410c473c2b0187ac2ae77ff5a9f4faa06a67f81e Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sat, 8 Aug 2015 00:02:07 -0700 Subject: make intializer for metadata even nicer --- src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 4 ++-- src/csharp/Grpc.Core.Tests/CompressionTest.cs | 2 +- .../Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs | 8 ++++---- src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs | 5 +---- src/csharp/Grpc.Core/Metadata.cs | 10 ++++++++++ 5 files changed, 18 insertions(+), 11 deletions(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index f56fb744a6..c5fc85b3fe 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -190,8 +190,8 @@ namespace Grpc.Core.Tests var headers = new Metadata { - new Metadata.Entry("ascii-header", "abcdefg"), - new Metadata.Entry("binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff }), + { "ascii-header", "abcdefg" }, + { "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } } }; var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC"); await call; diff --git a/src/csharp/Grpc.Core.Tests/CompressionTest.cs b/src/csharp/Grpc.Core.Tests/CompressionTest.cs index 492369968e..ac0c3d6b5f 100644 --- a/src/csharp/Grpc.Core.Tests/CompressionTest.cs +++ b/src/csharp/Grpc.Core.Tests/CompressionTest.cs @@ -94,7 +94,7 @@ namespace Grpc.Core.Tests context.WriteOptions = new WriteOptions(WriteFlags.NoCompress); - await context.WriteResponseHeadersAsync(new Metadata { new Metadata.Entry("ascii-header", "abcdefg") }); + await context.WriteResponseHeadersAsync(new Metadata { { "ascii-header", "abcdefg" } }); await responseStream.WriteAsync("X"); diff --git a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs index 46469113c5..33534fdd3c 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/MetadataArraySafeHandleTest.cs @@ -53,8 +53,8 @@ namespace Grpc.Core.Internal.Tests { var metadata = new Metadata { - new Metadata.Entry("host", "somehost"), - new Metadata.Entry("header2", "header value"), + { "host", "somehost" }, + { "header2", "header value" }, }; var nativeMetadata = MetadataArraySafeHandle.Create(metadata); nativeMetadata.Dispose(); @@ -65,8 +65,8 @@ namespace Grpc.Core.Internal.Tests { var metadata = new Metadata { - new Metadata.Entry("host", "somehost"), - new Metadata.Entry("header2", "header value"), + { "host", "somehost" }, + { "header2", "header value" } }; var nativeMetadata = MetadataArraySafeHandle.Create(metadata); diff --git a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs index b024488549..8925041ba4 100644 --- a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs +++ b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs @@ -63,10 +63,7 @@ namespace Grpc.Core.Tests server.Start(); channel = helper.GetChannel(); - headers = new Metadata - { - new Metadata.Entry("ascii-header", "abcdefg"), - }; + headers = new Metadata { { "ascii-header", "abcdefg" } }; } [TearDown] diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 6fd0a7109d..a58dbdbc93 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -114,6 +114,16 @@ namespace Grpc.Core entries.Add(item); } + public void Add(string key, string value) + { + Add(new Entry(key, value)); + } + + public void Add(string key, byte[] valueBytes) + { + Add(new Entry(key, valueBytes)); + } + public void Clear() { CheckWriteable(); -- cgit v1.2.3 From dad17243bbd42c2e0635f87143ed92ad3ecc3ea0 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Sat, 8 Aug 2015 23:27:04 -0700 Subject: added tests --- .../Grpc.Core.Tests/ContextPropagationTest.cs | 122 +++++++++++++++++++++ src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 + src/csharp/Grpc.Core/ContextPropagationToken.cs | 2 +- 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs new file mode 100644 index 0000000000..a7f5075874 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs @@ -0,0 +1,122 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class ContextPropagationTest + { + MockServiceHelper helper; + Server server; + Channel channel; + + [SetUp] + public void Init() + { + helper = new MockServiceHelper(); + + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + } + + [TearDown] + public void Cleanup() + { + channel.Dispose(); + server.ShutdownAsync().Wait(); + } + + [TestFixtureTearDown] + public void CleanupClass() + { + GrpcEnvironment.Shutdown(); + } + + [Test] + public async Task PropagateCancellation() + { + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + // check that we didn't obtain the default cancellation token. + Assert.IsTrue(context.CancellationToken.CanBeCanceled); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + var propagationToken = context.CreatePropagationToken(); + Assert.IsNotNull(propagationToken.ParentCall); + + var callOptions = new CallOptions(propagationToken: propagationToken); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var cts = new CancellationTokenSource(); + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } + + [Test] + public async Task PropagateDeadline() + { + var deadline = DateTime.UtcNow.AddDays(7); + helper.UnaryHandler = new UnaryServerMethod(async (request, context) => + { + Assert.IsTrue(context.Deadline < deadline.AddMinutes(1)); + Assert.IsTrue(context.Deadline > deadline.AddMinutes(-1)); + return "PASS"; + }); + + helper.ClientStreamingHandler = new ClientStreamingServerMethod(async (requestStream, context) => + { + var callOptions = new CallOptions(propagationToken: context.CreatePropagationToken()); + return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + }); + + var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(deadline: deadline))); + await call.RequestStream.CompleteAsync(); + Assert.AreEqual("PASS", await call); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 58fa7c645f..97ee0454bb 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -80,6 +80,7 @@ + diff --git a/src/csharp/Grpc.Core/ContextPropagationToken.cs b/src/csharp/Grpc.Core/ContextPropagationToken.cs index e765947766..b6ea5115a4 100644 --- a/src/csharp/Grpc.Core/ContextPropagationToken.cs +++ b/src/csharp/Grpc.Core/ContextPropagationToken.cs @@ -52,7 +52,7 @@ namespace Grpc.Core /// /// Default propagation mask used by C core. /// - const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags) 0xffff; + const ContextPropagationFlags DefaultCoreMask = (ContextPropagationFlags)0xffff; /// /// Default propagation mask used by C# - we want to propagate deadline -- cgit v1.2.3 From 1338798897378d8d05ab2a4633f25bcd63cfdc87 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 10 Aug 2015 11:13:28 -0700 Subject: fix method signature and add test --- src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs | 8 ++++++++ src/csharp/Grpc.Core/GrpcEnvironment.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src/csharp/Grpc.Core.Tests') diff --git a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs index 9ae12776f3..4ed93c7eca 100644 --- a/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs +++ b/src/csharp/Grpc.Core.Tests/GrpcEnvironmentTest.cs @@ -69,5 +69,13 @@ namespace Grpc.Core.Tests Assert.IsFalse(object.ReferenceEquals(env1, env2)); } + + [Test] + public void GetCoreVersionString() + { + var coreVersion = GrpcEnvironment.GetCoreVersionString(); + var parts = coreVersion.Split('.'); + Assert.AreEqual(4, parts.Length); + } } } diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index 29a6ea7872..1bb83c9962 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -169,7 +169,7 @@ namespace Grpc.Core /// /// Gets version of gRPC C core. /// - internal string GetCoreVersionString() + internal static string GetCoreVersionString() { var ptr = grpcsharp_version_string(); // the pointer is not owned return Marshal.PtrToStringAnsi(ptr); -- cgit v1.2.3