From 2250454721005f643b192e2152c2374e40f6385f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 17 Aug 2015 13:43:12 -0700 Subject: [PATCH 1/4] forgot to expose status and trailers for unary call --- .../Grpc.Core/AsyncClientStreamingCall.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs index bf020cd6274..fb9b562c77b 100644 --- a/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs +++ b/src/csharp/Grpc.Core/AsyncClientStreamingCall.cs @@ -88,6 +88,24 @@ namespace Grpc.Core return responseAsync.GetAwaiter(); } + /// + /// Gets the call status if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Status GetStatus() + { + return getStatusFunc(); + } + + /// + /// Gets the call trailing metadata if the call has already finished. + /// Throws InvalidOperationException otherwise. + /// + public Metadata GetTrailers() + { + return getTrailersFunc(); + } + /// /// Provides means to cleanup after the call. /// If the call has already finished normally (request stream has been completed and call result has been received), doesn't do anything. From 9e14414415d75bca155e5a85a8b7ac226027459f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 17 Aug 2015 14:58:09 -0700 Subject: [PATCH 2/4] refactor auth interceptors --- src/csharp/Grpc.Auth/AuthInterceptors.cs | 84 +++++++++++++ src/csharp/Grpc.Auth/Grpc.Auth.csproj | 42 +++---- src/csharp/Grpc.Auth/OAuth2Interceptors.cs | 115 ------------------ .../Grpc.Core/AsyncDuplexStreamingCall.cs | 2 - .../Grpc.Core/AsyncServerStreamingCall.cs | 2 - src/csharp/Grpc.Core/ClientBase.cs | 16 +-- .../Grpc.IntegrationTesting/InteropClient.cs | 10 +- 7 files changed, 112 insertions(+), 159 deletions(-) create mode 100644 src/csharp/Grpc.Auth/AuthInterceptors.cs delete mode 100644 src/csharp/Grpc.Auth/OAuth2Interceptors.cs diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/AuthInterceptors.cs new file mode 100644 index 00000000000..5a5ca7edc71 --- /dev/null +++ b/src/csharp/Grpc.Auth/AuthInterceptors.cs @@ -0,0 +1,84 @@ +#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 Google.Apis.Auth.OAuth2; +using Grpc.Core; +using Grpc.Core.Utils; + +namespace Grpc.Auth +{ + /// + /// Factory methods to create authorization interceptors. + /// + public static class AuthInterceptors + { + private const string AuthorizationHeader = "Authorization"; + private const string Schema = "Bearer"; + + /// + /// Creates interceptor that will obtain access token from any credential type that implements + /// ITokenAccess. (e.g. GoogleCredential). + /// + public static HeaderInterceptor FromCredential(ITokenAccess credential) + { + return new HeaderInterceptor((authUri, metadata) => + { + // TODO(jtattermusch): Rethink synchronous wait to obtain the result. + var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None) + .ConfigureAwait(false).GetAwaiter().GetResult(); + metadata.Add(CreateBearerTokenHeader(accessToken)); + }); + } + + /// + /// Creates OAuth2 interceptor that will use given access token as authorization. + /// + /// OAuth2 access token. + public static HeaderInterceptor FromAccessToken(string accessToken) + { + Preconditions.CheckNotNull(accessToken); + return new HeaderInterceptor((authUri, metadata) => + { + metadata.Add(CreateBearerTokenHeader(accessToken)); + }); + } + + private static Metadata.Entry CreateBearerTokenHeader(string accessToken) + { + return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken); + } + } +} diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj index 930a34b0c33..4fb087d4a34 100644 --- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj +++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj @@ -3,8 +3,6 @@ Debug AnyCPU - 10.0.0 - 2.0 {AE21D0EE-9A2C-4C15-AB7F-5224EED5B0EA} Library Grpc.Auth @@ -41,57 +39,47 @@ C:\keys\Grpc.snk - - False + + + + + ..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll - - False + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll - - False + ..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll - - False + ..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll - - False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - - False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - - False + ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - - False + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - - - - - False + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - - False + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - Version.cs - + diff --git a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs b/src/csharp/Grpc.Auth/OAuth2Interceptors.cs deleted file mode 100644 index d628a83246d..00000000000 --- a/src/csharp/Grpc.Auth/OAuth2Interceptors.cs +++ /dev/null @@ -1,115 +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.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Security.Cryptography.X509Certificates; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; - -using Google.Apis.Auth.OAuth2; -using Google.Apis.Util; -using Grpc.Core; -using Grpc.Core.Utils; - -namespace Grpc.Auth -{ - public static class OAuth2Interceptors - { - /// - /// Creates OAuth2 interceptor that will obtain access token from GoogleCredentials. - /// - public static MetadataInterceptorDelegate FromCredential(GoogleCredential googleCredential) - { - var interceptor = new OAuth2Interceptor(googleCredential, SystemClock.Default); - return new MetadataInterceptorDelegate(interceptor.InterceptHeaders); - } - - /// - /// Creates OAuth2 interceptor that will use given OAuth2 token. - /// - /// - /// - public static MetadataInterceptorDelegate FromAccessToken(string oauth2Token) - { - Preconditions.CheckNotNull(oauth2Token); - return new MetadataInterceptorDelegate((authUri, metadata) => - { - metadata.Add(OAuth2Interceptor.CreateBearerTokenHeader(oauth2Token)); - }); - } - - /// - /// Injects OAuth2 authorization header into initial metadata (= request headers). - /// - private class OAuth2Interceptor - { - private const string AuthorizationHeader = "Authorization"; - private const string Schema = "Bearer"; - - private ITokenAccess credential; - private IClock clock; - - public OAuth2Interceptor(ITokenAccess credential, IClock clock) - { - this.credential = credential; - this.clock = clock; - } - - /// - /// Gets access token and requests refreshing it if is going to expire soon. - /// - /// - /// - public string GetAccessToken(string authUri, CancellationToken cancellationToken) - { - // TODO(jtattermusch): Rethink synchronous wait to obtain the result. - return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken: cancellationToken).GetAwaiter().GetResult(); - } - - public void InterceptHeaders(string authUri, Metadata metadata) - { - var accessToken = GetAccessToken(authUri, CancellationToken.None); - metadata.Add(CreateBearerTokenHeader(accessToken)); - } - - public static Metadata.Entry CreateBearerTokenHeader(string accessToken) - { - return new Metadata.Entry(AuthorizationHeader, Schema + " " + accessToken); - } - } - } -} diff --git a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs index 0979de606f7..183c84216a0 100644 --- a/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs +++ b/src/csharp/Grpc.Core/AsyncDuplexStreamingCall.cs @@ -32,8 +32,6 @@ #endregion using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; namespace Grpc.Core { diff --git a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs index 380efcdb0e2..ab2049f2695 100644 --- a/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs +++ b/src/csharp/Grpc.Core/AsyncServerStreamingCall.cs @@ -32,8 +32,6 @@ #endregion using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; namespace Grpc.Core { diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index f240d777b9c..751d2d260fe 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -32,15 +32,15 @@ #endregion using System; -using System.Collections.Generic; using System.Text.RegularExpressions; - -using Grpc.Core.Internal; -using Grpc.Core.Utils; +using System.Threading.Tasks; namespace Grpc.Core { - public delegate void MetadataInterceptorDelegate(string authUri, Metadata metadata); + /// + /// Interceptor for call headers. + /// + public delegate void HeaderInterceptor(string authUri, Metadata metadata); /// /// Base class for client-side stubs. @@ -60,10 +60,10 @@ namespace Grpc.Core } /// - /// Can be used to register a custom header (initial metadata) interceptor. - /// The delegate each time before a new call on this client is started. + /// Can be used to register a custom header (request metadata) interceptor. + /// The interceptor is invoked each time a new call on this client is started. /// - public MetadataInterceptorDelegate HeaderInterceptor + public HeaderInterceptor HeaderInterceptor { get; set; diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 385ca920862..1047f2efc10 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -308,7 +308,7 @@ namespace Grpc.IntegrationTesting Console.WriteLine("running service_account_creds"); var credential = await GoogleCredential.GetApplicationDefaultAsync(); credential = credential.CreateScoped(new[] { AuthScope }); - client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); var request = SimpleRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) @@ -332,7 +332,7 @@ namespace Grpc.IntegrationTesting Console.WriteLine("running compute_engine_creds"); var credential = await GoogleCredential.GetApplicationDefaultAsync(); Assert.IsFalse(credential.IsCreateScopedRequired); - client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); var request = SimpleRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) @@ -357,7 +357,7 @@ namespace Grpc.IntegrationTesting var credential = await GoogleCredential.GetApplicationDefaultAsync(); // check this a credential with scope support, but don't add the scope. Assert.IsTrue(credential.IsCreateScopedRequired); - client.HeaderInterceptor = OAuth2Interceptors.FromCredential(credential); + client.HeaderInterceptor = AuthInterceptors.FromCredential(credential); var request = SimpleRequest.CreateBuilder() .SetResponseType(PayloadType.COMPRESSABLE) @@ -381,7 +381,7 @@ namespace Grpc.IntegrationTesting ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); string oauth2Token = await credential.GetAccessTokenForRequestAsync(); - client.HeaderInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); + client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token); var request = SimpleRequest.CreateBuilder() .SetFillUsername(true) @@ -401,7 +401,7 @@ namespace Grpc.IntegrationTesting ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { AuthScope }); string oauth2Token = await credential.GetAccessTokenForRequestAsync(); - var headerInterceptor = OAuth2Interceptors.FromAccessToken(oauth2Token); + var headerInterceptor = AuthInterceptors.FromAccessToken(oauth2Token); var request = SimpleRequest.CreateBuilder() .SetFillUsername(true) From e7178527ffb4ff5c38685fdd08de24a9b82f2fe4 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 17 Aug 2015 14:59:14 -0700 Subject: [PATCH 3/4] fix comment --- src/csharp/Grpc.Core/ChannelOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs index 0cb2953f2c2..ad54b46ad59 100644 --- a/src/csharp/Grpc.Core/ChannelOptions.cs +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -71,7 +71,7 @@ namespace Grpc.Core /// Creates a channel option with an integer value. /// /// Name. - /// String value. + /// Integer value. public ChannelOption(string name, int intValue) { this.type = OptionType.Integer; From e396b8dbeac9dbe3a426cf13f4e7d1ce9a558e2a Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 17 Aug 2015 15:02:23 -0700 Subject: [PATCH 4/4] add method info to auth interceptor --- src/csharp/Grpc.Auth/AuthInterceptors.cs | 4 +-- src/csharp/Grpc.Core/ClientBase.cs | 4 +-- src/csharp/Grpc.Core/Method.cs | 29 ++++++++++++++++++- .../Grpc.IntegrationTesting/InteropClient.cs | 2 +- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/AuthInterceptors.cs index 5a5ca7edc71..61338f7f0e2 100644 --- a/src/csharp/Grpc.Auth/AuthInterceptors.cs +++ b/src/csharp/Grpc.Auth/AuthInterceptors.cs @@ -54,7 +54,7 @@ namespace Grpc.Auth /// public static HeaderInterceptor FromCredential(ITokenAccess credential) { - return new HeaderInterceptor((authUri, metadata) => + return new HeaderInterceptor((method, authUri, metadata) => { // TODO(jtattermusch): Rethink synchronous wait to obtain the result. var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None) @@ -70,7 +70,7 @@ namespace Grpc.Auth public static HeaderInterceptor FromAccessToken(string accessToken) { Preconditions.CheckNotNull(accessToken); - return new HeaderInterceptor((authUri, metadata) => + return new HeaderInterceptor((method, authUri, metadata) => { metadata.Add(CreateBearerTokenHeader(accessToken)); }); diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs index 751d2d260fe..7bc100ca603 100644 --- a/src/csharp/Grpc.Core/ClientBase.cs +++ b/src/csharp/Grpc.Core/ClientBase.cs @@ -40,7 +40,7 @@ namespace Grpc.Core /// /// Interceptor for call headers. /// - public delegate void HeaderInterceptor(string authUri, Metadata metadata); + public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata); /// /// Base class for client-side stubs. @@ -107,7 +107,7 @@ namespace Grpc.Core options = options.WithHeaders(new Metadata()); } var authUri = authUriBase != null ? authUriBase + method.ServiceName : null; - interceptor(authUri, options.Headers); + interceptor(method, authUri, options.Headers); } return new CallInvocationDetails(channel, method, Host, options); } diff --git a/src/csharp/Grpc.Core/Method.cs b/src/csharp/Grpc.Core/Method.cs index 4c208b4a263..4c53285893e 100644 --- a/src/csharp/Grpc.Core/Method.cs +++ b/src/csharp/Grpc.Core/Method.cs @@ -54,10 +54,37 @@ namespace Grpc.Core DuplexStreaming } + /// + /// A non-generic representation of a remote method. + /// + public interface IMethod + { + /// + /// Gets the type of the method. + /// + MethodType Type { get; } + + /// + /// Gets the name of the service to which this method belongs. + /// + string ServiceName { get; } + + /// + /// Gets the unqualified name of the method. + /// + string Name { get; } + + /// + /// Gets the fully qualified name of the method. On the server side, methods are dispatched + /// based on this name. + /// + string FullName { get; } + } + /// /// A description of a remote method. /// - public class Method + public class Method : IMethod { readonly MethodType type; readonly string serviceName; diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 1047f2efc10..f4b0a1028f9 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -409,7 +409,7 @@ namespace Grpc.IntegrationTesting .Build(); var headers = new Metadata(); - headerInterceptor("", headers); + headerInterceptor(null, "", headers); var response = client.UnaryCall(request, headers: headers); Assert.AreEqual(AuthScopeResponse, response.OauthScope);