implement C# wrapping for plugin API

pull/3468/head
Jan Tattermusch 9 years ago
parent 4d1fc55261
commit 5bd75d789c
  1. 52
      src/csharp/Grpc.Core/Credentials.cs
  2. 1
      src/csharp/Grpc.Core/Grpc.Core.csproj
  3. 8
      src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
  4. 112
      src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
  5. 1
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  6. 100
      src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
  7. 41
      src/csharp/ext/grpc_csharp_ext.c

@ -32,10 +32,17 @@
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
// TODO: rename
public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
/// <summary>
/// Client-side credentials. Used for creation of a secure channel.
/// </summary>
@ -135,4 +142,49 @@ namespace Grpc.Core
return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
}
}
/// <summary>
/// Client-side credentials that delegate metadata based auth to an interceptor.
/// </summary>
public partial class MetadataCredentials : Credentials
{
readonly AsyncAuthInterceptor interceptor;
public MetadataCredentials(AsyncAuthInterceptor interceptor)
{
this.interceptor = interceptor;
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor);
return plugin.Credentials;
}
}
public sealed class CompositeCredentials : Credentials
{
readonly List<Credentials> credentials;
public CompositeCredentials(params Credentials[] credentials)
{
Preconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
this.credentials = new List<Credentials>(credentials);
}
public static CompositeCredentials Create(params Credentials[] credentials)
{
return new CompositeCredentials(credentials);
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
var nativeComposite = credentials[0].ToNativeCredentials();
for (int i = 1; i < credentials.Count; i++)
{
nativeComposite = CredentialsSafeHandle.CreateComposite(nativeComposite, credentials[i].ToNativeCredentials());
}
return nativeComposite;
}
}
}

@ -49,6 +49,7 @@
<Compile Include="AsyncDuplexStreamingCall.cs" />
<Compile Include="AsyncServerStreamingCall.cs" />
<Compile Include="IClientStreamWriter.cs" />
<Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" />
<Compile Include="Internal\INativeCall.cs" />
<Compile Include="IServerStreamWriter.cs" />
<Compile Include="IAsyncStreamWriter.cs" />

@ -43,6 +43,9 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern CredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
[DllImport("grpc_csharp_ext.dll")]
static extern CredentialsSafeHandle grpcsharp_composite_credentials_create(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_credentials_release(IntPtr credentials);
@ -69,6 +72,11 @@ namespace Grpc.Core.Internal
}
}
public static CredentialsSafeHandle CreateComposite(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2)
{
return grpcsharp_composite_credentials_create(creds1, creds2);
}
protected override bool ReleaseHandle()
{
grpcsharp_credentials_release(handle);

@ -0,0 +1,112 @@
#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.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Logging;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy);
internal class NativeMetadataCredentialsPlugin
{
const string GetMetadataExceptionMsg = "Exception occured in metadata credentials plugin.";
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeMetadataCredentialsPlugin>();
[DllImport("grpc_csharp_ext.dll")]
static extern CredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor);
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
AsyncAuthInterceptor interceptor;
GCHandle gcHandle;
NativeMetadataInterceptor nativeInterceptor;
CredentialsSafeHandle credentials;
public NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor)
{
this.interceptor = Preconditions.CheckNotNull(interceptor, "interceptor");
this.nativeInterceptor = NativeMetadataInterceptorHandler;
// Make sure the callback doesn't get garbage collected until it is destroyed.
this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal);
this.credentials = grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor);
}
public CredentialsSafeHandle Credentials
{
get { return credentials; }
}
private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)
{
if (isDestroy)
{
gcHandle.Free();
return;
}
try
{
string serviceUrl = Marshal.PtrToStringAnsi(serviceUrlPtr);
StartGetMetadata(serviceUrl, callbackPtr, userDataPtr);
}
catch (Exception e)
{
grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, null, StatusCode.Unknown, GetMetadataExceptionMsg);
Logger.Error(e, GetMetadataExceptionMsg);
}
}
private async void StartGetMetadata(string serviceUrl, IntPtr callbackPtr, IntPtr userDataPtr)
{
try
{
var metadata = new Metadata();
await interceptor(serviceUrl, metadata);
using (var metadataArray = MetadataArraySafeHandle.Create(metadata))
{
grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null);
}
}
catch (Exception e)
{
grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, null, StatusCode.Unknown, GetMetadataExceptionMsg);
Logger.Error(e, GetMetadataExceptionMsg);
}
}
}
}

@ -96,6 +96,7 @@
<Compile Include="Empty.cs" />
<Compile Include="Messages.cs" />
<Compile Include="InteropClientServerTest.cs" />
<Compile Include="MetadataCredentialsTest.cs" />
<Compile Include="TestServiceImpl.cs" />
<Compile Include="InteropServer.cs" />
<Compile Include="InteropClient.cs" />

@ -0,0 +1,100 @@
#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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
using NUnit.Framework;
namespace Grpc.IntegrationTesting
{
public class MetadataCredentialsTest
{
const string Host = "localhost";
Server server;
Channel channel;
TestService.ITestServiceClient client;
[TestFixtureSetUp]
public void Init()
{
var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(
File.ReadAllText(TestCredentials.ServerCertChainPath),
File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) });
server = new Server
{
Services = { TestService.BindService(new TestServiceImpl()) },
Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
};
server.Start();
var options = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
var asyncAuthInterceptor = new AsyncAuthInterceptor(async (authUri, metadata) =>
{
await Task.Delay(100); // make sure the operation is asynchronous.
metadata.Add("authorization", "SECRET_TOKEN");
});
var clientCredentials = CompositeCredentials.Create(
new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)),
new MetadataCredentials(asyncAuthInterceptor)
);
channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
client = TestService.NewClient(channel);
}
[TestFixtureTearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
}
[Test]
public void MetadataCredentials()
{
var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 });
Assert.AreEqual(10, response.Payload.Body.Length);
}
}
}

@ -68,7 +68,7 @@ grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) {
/*
* Helper to maintain lifetime of batch op inputs and store batch op outputs.
*/
typedef struct gprcsharp_batch_context {
typedef struct grpcsharp_batch_context {
grpc_metadata_array send_initial_metadata;
grpc_byte_buffer *send_message;
struct {
@ -892,6 +892,45 @@ grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr,
return grpc_server_add_secure_http2_port(server, addr, creds);
}
GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_composite_credentials_create(
grpc_credentials *creds1,
grpc_credentials *creds2) {
return grpc_composite_credentials_create(creds1, creds2, NULL);
}
/* Metadata credentials plugin */
GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
void *callback_ptr, void *user_data, grpc_metadata_array *metadata,
grpc_status_code status, const char *error_details) {
grpc_credentials_plugin_metadata_cb cb = (grpc_credentials_plugin_metadata_cb)callback_ptr;
cb(user_data, metadata->metadata, metadata->count, status, error_details);
}
typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)(
void *state, const char *service_url, void *callback_ptr,
void *user_data, gpr_int32 is_destroy);
static void grpcsharp_get_metadata_handler(void *state, const char *service_url,
grpc_credentials_plugin_metadata_cb cb, void *user_data) {
grpcsharp_metadata_interceptor_func interceptor = (grpcsharp_metadata_interceptor_func)state;
interceptor(state, service_url, (void*)cb, user_data, 0);
}
static void grpcsharp_metadata_credentials_destroy_handler(void *state) {
grpcsharp_metadata_interceptor_func interceptor = (grpcsharp_metadata_interceptor_func)state;
interceptor(state, NULL, NULL, NULL, 1);
}
GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_metadata_credentials_create_from_plugin(
grpcsharp_metadata_interceptor_func metadata_interceptor) {
grpc_metadata_credentials_plugin plugin;
plugin.get_metadata = grpcsharp_get_metadata_handler;
plugin.destroy = grpcsharp_metadata_credentials_destroy_handler;
plugin.state = metadata_interceptor;
return grpc_metadata_credentials_create_from_plugin(plugin, NULL);
}
/* Logging */
typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line,

Loading…
Cancel
Save