1 #region Copyright notice and license 2 // Copyright 2015 gRPC authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 #endregion 16 using System; 17 using System.Runtime.InteropServices; 18 using System.Threading; 19 using System.Threading.Tasks; 20 21 using Grpc.Core.Logging; 22 using Grpc.Core.Utils; 23 24 namespace Grpc.Core.Internal 25 { NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)26 internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy); 27 28 internal class NativeMetadataCredentialsPlugin 29 { 30 const string GetMetadataExceptionStatusMsg = "Exception occurred in metadata credentials plugin."; 31 const string GetMetadataExceptionLogMsg = GetMetadataExceptionStatusMsg + " This is likely not a problem with gRPC itself. Please verify that the code supplying the metadata (usually an authentication token) works correctly."; 32 static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeMetadataCredentialsPlugin>(); 33 static readonly NativeMethods Native = NativeMethods.Get(); 34 35 AsyncAuthInterceptor interceptor; 36 GCHandle gcHandle; 37 NativeMetadataInterceptor nativeInterceptor; 38 CallCredentialsSafeHandle credentials; 39 NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor)40 public NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor) 41 { 42 this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, "interceptor"); 43 this.nativeInterceptor = NativeMetadataInterceptorHandler; 44 45 // Make sure the callback doesn't get garbage collected until it is destroyed. 46 this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal); 47 this.credentials = Native.grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor); 48 } 49 50 public CallCredentialsSafeHandle Credentials 51 { 52 get { return credentials; } 53 } 54 NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)55 private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) 56 { 57 if (isDestroy) 58 { 59 gcHandle.Free(); 60 return; 61 } 62 63 try 64 { 65 var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr), Marshal.PtrToStringAnsi(methodNamePtr)); 66 // Make a guarantee that credentials_notify_from_plugin is invoked async to be compliant with c-core API. 67 ThreadPool.QueueUserWorkItem(async (stateInfo) => await GetMetadataAsync(context, callbackPtr, userDataPtr)); 68 } 69 catch (Exception e) 70 { 71 // eat the exception, we must not throw when inside callback from native code. 72 Logger.Error(e, "Exception occurred while invoking native metadata interceptor handler."); 73 } 74 } 75 GetMetadataAsync(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr)76 private async Task GetMetadataAsync(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr) 77 { 78 try 79 { 80 var metadata = new Metadata(); 81 await interceptor(context, metadata).ConfigureAwait(false); 82 83 using (var metadataArray = MetadataArraySafeHandle.Create(metadata)) 84 { 85 Native.grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null); 86 } 87 } 88 catch (Exception e) 89 { 90 string detail = GetMetadataExceptionStatusMsg + " " + e.ToString(); 91 Native.grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, detail); 92 Logger.Error(e, GetMetadataExceptionLogMsg); 93 } 94 } 95 } 96 } 97