diff options
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r-- | src/csharp/Grpc.Core/Channel.cs | 108 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ChannelOptions.cs | 4 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ChannelState.cs | 69 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Grpc.Core.csproj | 9 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Grpc.Core.nuspec | 1 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/AsyncCall.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs | 31 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs | 7 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/KeyCertificatePair.cs | 1 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Metadata.cs | 1 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Server.cs | 175 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ServerCredentials.cs | 28 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ServerPort.cs | 120 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/ServerServiceDefinition.cs | 12 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/packages.config | 1 |
15 files changed, 474 insertions, 95 deletions
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index 18e6f2fda5..0b69610443 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,21 +47,23 @@ namespace Grpc.Core /// </summary> public class Channel : IDisposable { + static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Channel>(); + readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; readonly List<ChannelOption> options; - readonly string target; bool disposed; /// <summary> /// 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. /// </summary> - /// <param name="host">The DNS name of IP address of the host.</param> + /// <param name="host">The name or IP address of the host.</param> /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> public Channel(string host, Credentials credentials, IEnumerable<ChannelOption> options = null) { + Preconditions.CheckNotNull(host); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); @@ -76,14 +80,13 @@ namespace Grpc.Core this.handle = ChannelSafeHandle.CreateInsecure(host, nativeChannelArgs); } } - this.target = GetOverridenTarget(host, this.options); } /// <summary> /// Creates a channel that connects to a specific host and port. /// </summary> - /// <param name="host">DNS name or IP address</param> - /// <param name="port">the port</param> + /// <param name="host">The name or IP address of the host.</param> + /// <param name="port">The port.</param> /// <param name="credentials">Credentials to secure the channel.</param> /// <param name="options">Channel options.</param> public Channel(string host, int port, Credentials credentials, IEnumerable<ChannelOption> options = null) : @@ -91,20 +94,82 @@ namespace Grpc.Core { } - public void Dispose() + /// <summary> + /// Gets current connectivity state of this channel. + /// </summary> + public ChannelState State { - Dispose(true); - GC.SuppressFinalize(this); + get + { + return handle.CheckConnectivityState(false); + } + } + + /// <summary> + /// Returned tasks completes once channel state has become different from + /// given lastObservedState. + /// If deadline is reached or and error occurs, returned task is cancelled. + /// </summary> + 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<object>(); + 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; } - internal string Target + /// <summary> Address of the remote endpoint in URI format.</summary> + public string Target { get { - return target; + return handle.GetTarget(); + } + } + + /// <summary> + /// 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. + /// </summary> + 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); } } + /// <summary> + /// Destroys the underlying channel. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + internal ChannelSafeHandle Handle { get @@ -159,26 +224,5 @@ namespace Grpc.Core // TODO(jtattermusch): it would be useful to also provide .NET/mono version. return string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion); } - - /// <summary> - /// Look for SslTargetNameOverride option and return its value instead of originalTarget - /// if found. - /// </summary> - private static string GetOverridenTarget(string originalTarget, IEnumerable<ChannelOption> options) - { - if (options == null) - { - return originalTarget; - } - foreach (var option in options) - { - if (option.Type == ChannelOption.OptionType.String - && option.Name == ChannelOptions.SslTargetNameOverride) - { - return option.StringValue; - } - } - return originalTarget; - } } } diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs index 9fe03d2805..1e0f90287a 100644 --- a/src/csharp/Grpc.Core/ChannelOptions.cs +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -30,7 +30,6 @@ #endregion using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -135,6 +134,9 @@ namespace Grpc.Core /// <summary>Initial sequence number for http2 transports</summary> public const string Http2InitialSequenceNumber = "grpc.http2.initial_sequence_number"; + /// <summary>Default authority for calls.</summary> + public const string DefaultAuthority = "grpc.default_authority"; + /// <summary>Primary user agent: goes at the start of the user-agent metadata</summary> public const string PrimaryUserAgentString = "grpc.primary_user_agent"; 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 +{ + /// <summary> + /// Connectivity state of a channel. + /// Based on grpc_connectivity_state from grpc/grpc.h + /// </summary> + public enum ChannelState + { + /// <summary> + /// Channel is idle + /// </summary> + Idle, + + /// <summary> + /// Channel is connecting + /// </summary> + Connecting, + + /// <summary> + /// Channel is ready for work + /// </summary> + Ready, + + /// <summary> + /// Channel has seen a failure but expects to recover + /// </summary> + TransientFailure, + + /// <summary> + /// Channel has seen a failure that it cannot recover from + /// </summary> + FatalFailure + } +} diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 940a6b8ac0..17add77164 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -44,9 +44,6 @@ <Reference Include="System.Interactive.Async"> <HintPath>..\packages\Ix-Async.1.2.3\lib\net45\System.Interactive.Async.dll</HintPath> </Reference> - <Reference Include="System.Collections.Immutable"> - <HintPath>..\packages\System.Collections.Immutable.1.1.36\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="AsyncDuplexStreamingCall.cs" /> @@ -55,6 +52,7 @@ <Compile Include="IServerStreamWriter.cs" /> <Compile Include="IAsyncStreamWriter.cs" /> <Compile Include="IAsyncStreamReader.cs" /> + <Compile Include="ServerPort.cs" /> <Compile Include="Version.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="RpcException.cs" /> @@ -115,6 +113,7 @@ <Compile Include="Logging\ILogger.cs" /> <Compile Include="Logging\ConsoleLogger.cs" /> <Compile Include="Internal\NativeLogRedirector.cs" /> + <Compile Include="ChannelState.cs" /> </ItemGroup> <ItemGroup> <None Include="Grpc.Core.nuspec" /> @@ -145,7 +144,5 @@ </Target> <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.2.2\build\portable-net45\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.9\build\portable-net45\grpc.dependencies.zlib.redist.targets')" /> - <ItemGroup> - <Folder Include="Logging\" /> - </ItemGroup> + <ItemGroup /> </Project>
\ No newline at end of file diff --git a/src/csharp/Grpc.Core/Grpc.Core.nuspec b/src/csharp/Grpc.Core/Grpc.Core.nuspec index 086776f69d..fe49efc7ec 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.nuspec +++ b/src/csharp/Grpc.Core/Grpc.Core.nuspec @@ -15,7 +15,6 @@ <copyright>Copyright 2015, Google Inc.</copyright> <tags>gRPC RPC Protocol HTTP/2</tags> <dependencies> - <dependency id="System.Collections.Immutable" version="1.1.36" /> <dependency id="Ix-Async" version="1.2.3" /> <dependency id="grpc.native.csharp_ext" version="$GrpcNativeCsharpExtVersion$" /> </dependencies> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index bfcb9366a1..48f466460f 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, channel.Target, deadline); + var call = channel.Handle.CreateCall(channel.CompletionRegistry, cq, methodName, null, deadline); channel.Environment.DebugStats.ActiveClientCalls.Increment(); InitializeInternal(call); } diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs index 20815efbd3..7324ebdf57 100644 --- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs @@ -50,6 +50,16 @@ namespace Grpc.Core.Internal 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 CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_channel_destroy(IntPtr channel); private ChannelSafeHandle() @@ -73,6 +83,27 @@ 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); + } + + 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/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs index 59238a452c..37a4f5256b 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -42,7 +42,7 @@ namespace Grpc.Core.Internal internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid { [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] - static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs); + static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth); [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_server_credentials_release(IntPtr credentials); @@ -51,12 +51,13 @@ namespace Grpc.Core.Internal { } - public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray) + public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, bool forceClientAuth) { Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length); return grpcsharp_ssl_server_credentials_create(pemRootCerts, keyCertPairCertChainArray, keyCertPairPrivateKeyArray, - new UIntPtr((ulong)keyCertPairCertChainArray.Length)); + new UIntPtr((ulong)keyCertPairCertChainArray.Length), + forceClientAuth); } protected override bool ReleaseHandle() diff --git a/src/csharp/Grpc.Core/KeyCertificatePair.cs b/src/csharp/Grpc.Core/KeyCertificatePair.cs index 7cea18618e..5def15a656 100644 --- a/src/csharp/Grpc.Core/KeyCertificatePair.cs +++ b/src/csharp/Grpc.Core/KeyCertificatePair.cs @@ -33,7 +33,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using Grpc.Core.Internal; using Grpc.Core.Utils; diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 2f308cbb11..6fd0a7109d 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -32,7 +32,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; using System.Collections.Specialized; using System.Runtime.InteropServices; using System.Text; diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 3217547cc4..eb5b043d1c 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; @@ -48,18 +48,17 @@ namespace Grpc.Core /// </summary> public class Server { - /// <summary> - /// Pass this value as port to have the server choose an unused listening port for you. - /// </summary> - public const int PickUnusedPort = 0; - static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>(); + readonly ServiceDefinitionCollection serviceDefinitions; + readonly ServerPortCollection ports; readonly GrpcEnvironment environment; readonly List<ChannelOption> options; readonly ServerSafeHandle handle; readonly object myLock = new object(); + readonly List<ServerServiceDefinition> serviceDefinitionsList = new List<ServerServiceDefinition>(); + readonly List<ServerPort> serverPortList = new List<ServerPort>(); readonly Dictionary<string, IServerCallHandler> callHandlers = new Dictionary<string, IServerCallHandler>(); readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>(); @@ -72,6 +71,8 @@ namespace Grpc.Core /// <param name="options">Channel options.</param> public Server(IEnumerable<ChannelOption> options = null) { + this.serviceDefinitions = new ServiceDefinitionCollection(this); + this.ports = new ServerPortCollection(this); this.environment = GrpcEnvironment.GetInstance(); this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); using (var channelArgs = ChannelOptions.CreateChannelArgs(this.options)) @@ -81,47 +82,26 @@ namespace Grpc.Core } /// <summary> - /// 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. /// </summary> - 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; } } /// <summary> - /// 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. /// </summary> - /// <returns>The port on which server will be listening.</returns> - /// <param name="host">the host</param> - /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> - 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; } } @@ -190,6 +170,50 @@ namespace Grpc.Core } /// <summary> + /// Adds a service definition. + /// </summary> + 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); + } + } + + /// <summary> + /// Adds a listening port. + /// </summary> + 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; + } + } + + /// <summary> /// Allows one new RPC call to be received by server. /// </summary> private void AllowOneRpc() @@ -249,5 +273,82 @@ namespace Grpc.Core { shutdownTcs.SetResult(null); } + + /// <summary> + /// Collection of service definitions. + /// </summary> + public class ServiceDefinitionCollection : IEnumerable<ServerServiceDefinition> + { + readonly Server server; + + internal ServiceDefinitionCollection(Server server) + { + this.server = server; + } + + /// <summary> + /// Adds a service definition to the server. This is how you register + /// handlers for a service with the server. Only call this before Start(). + /// </summary> + public void Add(ServerServiceDefinition serviceDefinition) + { + server.AddServiceDefinitionInternal(serviceDefinition); + } + + public IEnumerator<ServerServiceDefinition> GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serviceDefinitionsList.GetEnumerator(); + } + } + + /// <summary> + /// Collection of server ports. + /// </summary> + public class ServerPortCollection : IEnumerable<ServerPort> + { + readonly Server server; + + internal ServerPortCollection(Server server) + { + this.server = server; + } + + /// <summary> + /// Adds a new port on which server should listen. + /// Only call this before Start(). + /// <returns>The port on which server will be listening.</returns> + /// </summary> + public int Add(ServerPort serverPort) + { + return server.AddPortInternal(serverPort); + } + + /// <summary> + /// Adds a new port on which server should listen. + /// <returns>The port on which server will be listening.</returns> + /// </summary> + /// <param name="host">the host</param> + /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> + /// <param name="credentials">credentials to use to secure this port.</param> + public int Add(string host, int port, ServerCredentials credentials) + { + return Add(new ServerPort(host, port, credentials)); + } + + public IEnumerator<ServerPort> GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return server.serverPortList.GetEnumerator(); + } + } } } diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs index 32ed4b78a1..c11a1ede08 100644 --- a/src/csharp/Grpc.Core/ServerCredentials.cs +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -33,7 +33,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using Grpc.Core.Internal; using Grpc.Core.Utils; @@ -80,18 +79,26 @@ namespace Grpc.Core { readonly IList<KeyCertificatePair> keyCertificatePairs; readonly string rootCertificates; + readonly bool forceClientAuth; /// <summary> /// Creates server-side SSL credentials. /// </summary> - /// <param name="rootCertificates">PEM encoded client root certificates used to authenticate client.</param> /// <param name="keyCertificatePairs">Key-certificates to use.</param> - public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs, string rootCertificates) + /// <param name="rootCertificates">PEM encoded client root certificates used to authenticate client.</param> + /// <param name="forceClientAuth">If true, client will be rejected unless it proves its unthenticity using against rootCertificates.</param> + public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs, string rootCertificates, bool forceClientAuth) { this.keyCertificatePairs = new List<KeyCertificatePair>(keyCertificatePairs).AsReadOnly(); Preconditions.CheckArgument(this.keyCertificatePairs.Count > 0, "At least one KeyCertificatePair needs to be provided"); + if (forceClientAuth) + { + Preconditions.CheckNotNull(rootCertificates, + "Cannot force client authentication unless you provide rootCertificates."); + } this.rootCertificates = rootCertificates; + this.forceClientAuth = forceClientAuth; } /// <summary> @@ -100,7 +107,7 @@ namespace Grpc.Core /// using client root certificates. /// </summary> /// <param name="keyCertificatePairs">Key-certificates to use.</param> - public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs) : this(keyCertificatePairs, null) + public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs) : this(keyCertificatePairs, null, false) { } @@ -126,6 +133,17 @@ namespace Grpc.Core } } + /// <summary> + /// If true, the authenticity of client check will be enforced. + /// </summary> + public bool ForceClientAuthentication + { + get + { + return this.forceClientAuth; + } + } + internal override ServerCredentialsSafeHandle ToNativeCredentials() { int count = keyCertificatePairs.Count; @@ -136,7 +154,7 @@ namespace Grpc.Core certChains[i] = keyCertificatePairs[i].CertificateChain; keys[i] = keyCertificatePairs[i].PrivateKey; } - return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys); + return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys, forceClientAuth); } } } 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 +{ + /// <summary> + /// A port exposed by a server. + /// </summary> + public class ServerPort + { + /// <summary> + /// 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 <see cref="BoundPort"/> property. + /// </summary> + public const int PickUnused = 0; + + readonly string host; + readonly int port; + readonly ServerCredentials credentials; + readonly int boundPort; + + /// <summary> + /// Creates a new port on which server should listen. + /// </summary> + /// <returns>The port on which server will be listening.</returns> + /// <param name="host">the host</param> + /// <param name="port">the port. If zero, an unused port is chosen automatically.</param> + /// <param name="credentials">credentials to use to secure this port.</param> + public ServerPort(string host, int port, ServerCredentials credentials) + { + this.host = Preconditions.CheckNotNull(host); + this.port = port; + this.credentials = Preconditions.CheckNotNull(credentials); + } + + /// <summary> + /// Creates a port from an existing <c>ServerPort</c> instance and boundPort value. + /// </summary> + internal ServerPort(ServerPort serverPort, int boundPort) + { + this.host = serverPort.host; + this.port = serverPort.port; + this.credentials = serverPort.credentials; + this.boundPort = boundPort; + } + + /// <value>The host.</value> + public string Host + { + get + { + return host; + } + } + + /// <value>The port.</value> + public int Port + { + get + { + return port; + } + } + + /// <value>The server credentials.</value> + public ServerCredentials Credentials + { + get + { + return credentials; + } + } + + /// <value> + /// The port actually bound by the server. This is useful if you let server + /// pick port automatically. <see cref="PickUnused"/> + /// </value> + public int BoundPort + { + get + { + return boundPort; + } + } + } +} diff --git a/src/csharp/Grpc.Core/ServerServiceDefinition.cs b/src/csharp/Grpc.Core/ServerServiceDefinition.cs index b180186c12..a00d156e52 100644 --- a/src/csharp/Grpc.Core/ServerServiceDefinition.cs +++ b/src/csharp/Grpc.Core/ServerServiceDefinition.cs @@ -33,7 +33,7 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; +using System.Collections.ObjectModel; using Grpc.Core.Internal; namespace Grpc.Core @@ -43,14 +43,14 @@ namespace Grpc.Core /// </summary> public class ServerServiceDefinition { - readonly ImmutableDictionary<string, IServerCallHandler> callHandlers; + readonly ReadOnlyDictionary<string, IServerCallHandler> callHandlers; - private ServerServiceDefinition(ImmutableDictionary<string, IServerCallHandler> callHandlers) + private ServerServiceDefinition(Dictionary<string, IServerCallHandler> callHandlers) { - this.callHandlers = callHandlers; + this.callHandlers = new ReadOnlyDictionary<string, IServerCallHandler>(callHandlers); } - internal ImmutableDictionary<string, IServerCallHandler> CallHandlers + internal IDictionary<string, IServerCallHandler> CallHandlers { get { @@ -115,7 +115,7 @@ namespace Grpc.Core public ServerServiceDefinition Build() { - return new ServerServiceDefinition(callHandlers.ToImmutableDictionary()); + return new ServerServiceDefinition(callHandlers); } } } diff --git a/src/csharp/Grpc.Core/packages.config b/src/csharp/Grpc.Core/packages.config index 6cdcdf2656..9b12b9b096 100644 --- a/src/csharp/Grpc.Core/packages.config +++ b/src/csharp/Grpc.Core/packages.config @@ -3,5 +3,4 @@ <package id="grpc.dependencies.openssl.redist" version="1.0.2.2" targetFramework="net45" /> <package id="grpc.dependencies.zlib.redist" version="1.2.8.9" targetFramework="net45" /> <package id="Ix-Async" version="1.2.3" targetFramework="net45" /> - <package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" /> </packages>
\ No newline at end of file |