Merge branch 'master' into node_interop_compliance

pull/3746/head
murgatroid99 9 years ago
commit 2e58fafe55
  1. 3
      BUILD
  2. 1
      build.yaml
  3. 3
      examples/php/README.md
  4. 2
      gRPC.podspec
  5. 2
      src/core/channel/compress_filter.c
  6. 2
      src/core/compression/algorithm.c
  7. 1
      src/core/surface/byte_buffer.c
  8. 27
      src/core/surface/call.c
  9. 11
      src/core/surface/call.h
  10. 65
      src/core/surface/call_test_only.h
  11. 24
      src/csharp/Grpc.Auth/AuthInterceptors.cs
  12. 1
      src/csharp/Grpc.Auth/Grpc.Auth.csproj
  13. 93
      src/csharp/Grpc.Auth/GrpcCredentials.cs
  14. 25
      src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
  15. 73
      src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
  16. 10
      src/csharp/Grpc.Core.Tests/ChannelTest.cs
  17. 52
      src/csharp/Grpc.Core.Tests/FakeCredentials.cs
  18. 4
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  19. 2
      src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
  20. 4
      src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs
  21. 2
      src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
  22. 142
      src/csharp/Grpc.Core/CallCredentials.cs
  23. 16
      src/csharp/Grpc.Core/CallOptions.cs
  24. 4
      src/csharp/Grpc.Core/Channel.cs
  25. 238
      src/csharp/Grpc.Core/ChannelCredentials.cs
  26. 31
      src/csharp/Grpc.Core/ClientBase.cs
  27. 138
      src/csharp/Grpc.Core/Credentials.cs
  28. 4
      src/csharp/Grpc.Core/Grpc.Core.csproj
  29. 2
      src/csharp/Grpc.Core/Grpc.Core.nuspec
  30. 10
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  31. 8
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  32. 6
      src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
  33. 8
      src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
  34. 113
      src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
  35. 2
      src/csharp/Grpc.Examples.MathClient/MathClient.cs
  36. 2
      src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
  37. 2
      src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
  38. 49
      src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
  39. 11
      src/csharp/Grpc.IntegrationTesting.Client/packages.config
  40. 49
      src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
  41. 11
      src/csharp/Grpc.IntegrationTesting.Server/packages.config
  42. 5
      src/csharp/Grpc.IntegrationTesting/Empty.cs
  43. 4
      src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
  44. 97
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  45. 7
      src/csharp/Grpc.IntegrationTesting/InteropServer.cs
  46. 446
      src/csharp/Grpc.IntegrationTesting/Messages.cs
  47. 97
      src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
  48. 37
      src/csharp/Grpc.IntegrationTesting/Test.cs
  49. 188
      src/csharp/Grpc.IntegrationTesting/TestGrpc.cs
  50. 132
      src/csharp/Grpc.IntegrationTesting/proto/messages.proto
  51. 71
      src/csharp/Grpc.IntegrationTesting/proto/test.proto
  52. 4
      src/csharp/build_packages.bat
  53. 66
      src/csharp/ext/grpc_csharp_ext.c
  54. 2
      src/csharp/generate_proto_csharp.sh
  55. 5
      src/objective-c/README.md
  56. 10
      src/objective-c/RxLibrary/GRXWriter.m
  57. 30
      src/php/ext/grpc/package.xml
  58. 17
      src/php/ext/grpc/php_grpc.c
  59. 22
      src/ruby/lib/grpc/generic/client_stub.rb
  60. 14
      src/ruby/spec/generic/client_stub_spec.rb
  61. 11
      test/core/end2end/tests/compressed_payload.c
  62. 2
      test/cpp/interop/client.cc
  63. 10
      test/cpp/interop/client_helper.cc
  64. 6
      test/cpp/interop/client_helper.h
  65. 6
      test/cpp/interop/server_helper.cc
  66. 16
      test/cpp/qps/driver.cc
  67. 1
      tools/doxygen/Doxyfile.core.internal
  68. 10
      tools/gce_setup/grpc_docker.sh
  69. 9
      tools/http2_interop/README.md
  70. 6
      tools/http2_interop/doc.go
  71. 11
      tools/http2_interop/frame.go
  72. 109
      tools/http2_interop/frameheader.go
  73. 245
      tools/http2_interop/http2interop.go
  74. 50
      tools/http2_interop/http2interop_test.go
  75. 65
      tools/http2_interop/ping.go
  76. 109
      tools/http2_interop/settings.go
  77. 54
      tools/http2_interop/unknownframe.go
  78. 14
      tools/jenkins/build_docker_and_run_tests.sh
  79. 11
      tools/jenkins/build_interop_image.sh
  80. 1
      tools/jenkins/grpc_interop_node/Dockerfile
  81. 3
      tools/jenkins/grpc_interop_node/build_interop.sh
  82. 10
      tools/jenkins/run_jenkins.sh
  83. 12
      tools/run_tests/build_csharp.sh
  84. 2
      tools/run_tests/build_ruby.sh
  85. 34
      tools/run_tests/dockerjob.py
  86. 6
      tools/run_tests/jobset.py
  87. 21
      tools/run_tests/pre_build_c.bat
  88. 22
      tools/run_tests/pre_build_csharp.bat
  89. 41
      tools/run_tests/pre_build_csharp.sh
  90. 39
      tools/run_tests/pre_build_ruby.sh
  91. 50
      tools/run_tests/run_interop_tests.py
  92. 18
      tools/run_tests/run_tests.py
  93. 4
      tools/run_tests/sources_and_headers.json
  94. 2
      vsprojects/nuget_package/README.md
  95. 6
      vsprojects/nuget_package/grpc.native.csharp.nuspec
  96. 0
      vsprojects/nuget_package/grpc.native.csharp.props
  97. 0
      vsprojects/nuget_package/grpc.native.csharp.targets
  98. 1
      vsprojects/vcxproj/grpc/grpc.vcxproj
  99. 3
      vsprojects/vcxproj/grpc/grpc.vcxproj.filters
  100. 1
      vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
  101. Some files were not shown because too many files have changed in this diff Show More

@ -223,6 +223,7 @@ cc_library(
"src/core/surface/api_trace.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.h",
"src/core/surface/completion_queue.h",
"src/core/surface/event_string.h",
@ -509,6 +510,7 @@ cc_library(
"src/core/surface/api_trace.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.h",
"src/core/surface/completion_queue.h",
"src/core/surface/event_string.h",
@ -1296,6 +1298,7 @@ objc_library(
"src/core/surface/api_trace.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.h",
"src/core/surface/completion_queue.h",
"src/core/surface/event_string.h",

@ -183,6 +183,7 @@ filegroups:
- src/core/surface/api_trace.h
- src/core/surface/byte_buffer_queue.h
- src/core/surface/call.h
- src/core/surface/call_test_only.h
- src/core/surface/channel.h
- src/core/surface/completion_queue.h
- src/core/surface/event_string.h

@ -8,7 +8,7 @@ This requires PHP 5.5 or greater.
INSTALL
-------
- On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][]. Run the following command to install gRPC.
- On Mac OS X, install [homebrew][]. Run the following command to install gRPC.
```sh
$ curl -fsSL https://goo.gl/getgrpc | bash -s php
@ -59,7 +59,6 @@ TUTORIAL
You can find a more detailed tutorial in [gRPC Basics: PHP][]
[homebrew]:http://brew.sh
[linuxbrew]:https://github.com/Homebrew/linuxbrew#installation
[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
[Node]:https://github.com/grpc/grpc/tree/master/examples/node
[gRPC Basics: PHP]:http://www.grpc.io/docs/tutorials/basic/php.html

@ -227,6 +227,7 @@ Pod::Spec.new do |s|
'src/core/surface/api_trace.h',
'src/core/surface/byte_buffer_queue.h',
'src/core/surface/call.h',
'src/core/surface/call_test_only.h',
'src/core/surface/channel.h',
'src/core/surface/completion_queue.h',
'src/core/surface/event_string.h',
@ -518,6 +519,7 @@ Pod::Spec.new do |s|
'src/core/surface/api_trace.h',
'src/core/surface/byte_buffer_queue.h',
'src/core/surface/call.h',
'src/core/surface/call_test_only.h',
'src/core/surface/channel.h',
'src/core/surface/completion_queue.h',
'src/core/surface/event_string.h',

@ -242,7 +242,7 @@ static void process_send_ops(grpc_call_element *elem,
GPR_ASSERT(calld->remaining_slice_bytes > 0);
/* Increase input ref count, gpr_slice_buffer_add takes ownership. */
gpr_slice_buffer_add(&calld->slices, gpr_slice_ref(sop->data.slice));
GPR_ASSERT(GPR_SLICE_LENGTH(sop->data.slice) >=
GPR_ASSERT(GPR_SLICE_LENGTH(sop->data.slice) <=
calld->remaining_slice_bytes);
calld->remaining_slice_bytes -=
(gpr_uint32)GPR_SLICE_LENGTH(sop->data.slice);

@ -101,6 +101,7 @@ grpc_compression_algorithm grpc_compression_algorithm_for_level(
default:
/* we shouldn't be making it here */
abort();
return GRPC_COMPRESS_NONE;
}
}
@ -116,6 +117,7 @@ grpc_compression_level grpc_compression_level_for_algorithm(
}
}
abort();
return GRPC_COMPRESS_LEVEL_NONE;
}
void grpc_compression_options_init(grpc_compression_options *opts) {

@ -97,4 +97,5 @@ size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) {
}
gpr_log(GPR_ERROR, "should never reach here");
abort();
return 0;
}

@ -436,6 +436,7 @@ static grpc_cq_completion *allocate_completion(grpc_call *call) {
}
gpr_log(GPR_ERROR, "should never reach here");
abort();
return NULL;
}
static void done_completion(grpc_exec_ctx *exec_ctx, void *call,
@ -526,9 +527,13 @@ static void set_compression_algorithm(grpc_call *call,
call->compression_algorithm = algo;
}
grpc_compression_algorithm grpc_call_get_compression_algorithm(
const grpc_call *call) {
return call->compression_algorithm;
grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
grpc_call *call) {
grpc_compression_algorithm algorithm;
gpr_mu_lock(&call->mu);
algorithm = call->compression_algorithm;
gpr_mu_unlock(&call->mu);
return algorithm;
}
static void set_encodings_accepted_by_peer(
@ -562,12 +567,20 @@ static void set_encodings_accepted_by_peer(
}
}
gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call) {
return call->encodings_accepted_by_peer;
gpr_uint32 grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) {
gpr_uint32 encodings_accepted_by_peer;
gpr_mu_lock(&call->mu);
encodings_accepted_by_peer = call->encodings_accepted_by_peer;
gpr_mu_unlock(&call->mu);
return encodings_accepted_by_peer;
}
gpr_uint32 grpc_call_get_message_flags(const grpc_call *call) {
return call->incoming_message_flags;
gpr_uint32 grpc_call_test_only_get_message_flags(grpc_call *call) {
gpr_uint32 flags;
gpr_mu_lock(&call->mu);
flags = call->incoming_message_flags;
gpr_mu_unlock(&call->mu);
return flags;
}
static void set_status_details(grpc_call *call, status_source source,

@ -169,17 +169,6 @@ void *grpc_call_context_get(grpc_call *call, grpc_context_index elem);
gpr_uint8 grpc_call_is_client(grpc_call *call);
grpc_compression_algorithm grpc_call_get_compression_algorithm(
const grpc_call *call);
gpr_uint32 grpc_call_get_message_flags(const grpc_call *call);
/** Returns a bitset for the encodings (compression algorithms) supported by \a
* call's peer.
*
* To be indexed by grpc_compression_algorithm enum values. */
gpr_uint32 grpc_call_get_encodings_accepted_by_peer(grpc_call *call);
#ifdef __cplusplus
}
#endif

@ -0,0 +1,65 @@
/*
*
* 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.
*
*/
#ifndef GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H
#define GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H
#include <grpc/grpc.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Return the compression algorithm from \a call.
*
* \warning This function should \b only be used in test code. */
grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm(
grpc_call *call);
/** Return the message flags from \a call.
*
* \warning This function should \b only be used in test code. */
gpr_uint32 grpc_call_test_only_get_message_flags(grpc_call *call);
/** Returns a bitset for the encodings (compression algorithms) supported by \a
* call's peer.
*
* To be indexed by grpc_compression_algorithm enum values. */
gpr_uint32 grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H */

@ -41,8 +41,8 @@ using Grpc.Core.Utils;
namespace Grpc.Auth
{
/// <summary>
/// Factory methods to create authorization interceptors. Interceptors created can be registered with gRPC client classes (autogenerated client stubs that
/// inherit from <see cref="Grpc.Core.ClientBase"/>).
/// Factory methods to create authorization interceptors.
/// <seealso cref="GrpcCredentials"/>
/// </summary>
public static class AuthInterceptors
{
@ -50,31 +50,29 @@ namespace Grpc.Auth
private const string Schema = "Bearer";
/// <summary>
/// Creates interceptor that will obtain access token from any credential type that implements
/// Creates an <see cref="AsyncAuthInterceptor"/> that will obtain access token from any credential type that implements
/// <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <returns>The header interceptor.</returns>
public static HeaderInterceptor FromCredential(ITokenAccess credential)
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromCredential(ITokenAccess credential)
{
return new HeaderInterceptor((method, authUri, metadata) =>
return new AsyncAuthInterceptor(async (authUri, metadata) =>
{
// TODO(jtattermusch): Rethink synchronous wait to obtain the result.
var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None)
.ConfigureAwait(false).GetAwaiter().GetResult();
var accessToken = await credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None).ConfigureAwait(false);
metadata.Add(CreateBearerTokenHeader(accessToken));
});
}
/// <summary>
/// Creates OAuth2 interceptor that will use given access token as authorization.
/// Creates an <see cref="AsyncAuthInterceptor"/> that will use given access token as authorization.
/// </summary>
/// <param name="accessToken">OAuth2 access token.</param>
/// <returns>The header interceptor.</returns>
public static HeaderInterceptor FromAccessToken(string accessToken)
/// <returns>The interceptor.</returns>
public static AsyncAuthInterceptor FromAccessToken(string accessToken)
{
Preconditions.CheckNotNull(accessToken);
return new HeaderInterceptor((method, authUri, metadata) =>
return new AsyncAuthInterceptor(async (authUri, metadata) =>
{
metadata.Add(CreateBearerTokenHeader(accessToken));
});

@ -78,6 +78,7 @@
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="GrpcCredentials.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AuthInterceptors.cs" />
</ItemGroup>

@ -0,0 +1,93 @@
#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
{
/// <summary>
/// Factory methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes.
/// </summary>
public static class GrpcCredentials
{
/// <summary>
/// Creates a <see cref="MetadataCredentials"/> instance that will obtain access tokens
/// from any credential that implements <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <returns>The <c>MetadataCredentials</c> instance.</returns>
public static MetadataCredentials Create(ITokenAccess credential)
{
return new MetadataCredentials(AuthInterceptors.FromCredential(credential));
}
/// <summary>
/// Convenience method to create a <see cref="ChannelCredentials"/> instance from
/// <c>ITokenAccess</c> credential and <c>SslCredentials</c> instance.
/// </summary>
/// <param name="credential">The credential to use to obtain access tokens.</param>
/// <param name="sslCredentials">The <c>SslCredentials</c> instance.</param>
/// <returns>The channel credentials for access token based auth over a secure channel.</returns>
public static ChannelCredentials Create(ITokenAccess credential, SslCredentials sslCredentials)
{
return ChannelCredentials.Create(sslCredentials, Create(credential));
}
/// <summary>
/// Creates an instance of <see cref="MetadataCredentials"/> that will use given access token to authenticate
/// with a gRPC service.
/// </summary>
/// <param name="accessToken">OAuth2 access token.</param>
/// /// <returns>The <c>MetadataCredentials</c> instance.</returns>
public static MetadataCredentials FromAccessToken(string accessToken)
{
return new MetadataCredentials(AuthInterceptors.FromAccessToken(accessToken));
}
/// <summary>
/// Converts a <c>ITokenAccess</c> object into a <see cref="MetadataCredentials"/> object supported
/// by gRPC.
/// </summary>
/// <param name="credential"></param>
/// <returns></returns>
public static MetadataCredentials ToGrpcCredentials(this ITokenAccess credential)
{
return GrpcCredentials.Create(credential);
}
}
}

@ -32,6 +32,10 @@
#endregion
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
@ -39,24 +43,23 @@ using NUnit.Framework;
namespace Grpc.Core.Tests
{
public class ClientBaseTest
public class CallCredentialsTest
{
[Test]
public void GetAuthUriBase_Valid()
public void CallCredentials_ComposeAtLeastTwo()
{
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com/"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com:443/"));
Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com:443/"));
Assert.Throws(typeof(ArgumentException), () => CallCredentials.Compose(new FakeCallCredentials()));
}
[Test]
public void GetAuthUriBase_Invalid()
public void CallCredentials_ToNativeCredentials()
{
Assert.IsNull(ClientBase.GetAuthUriBase("some.googleapi.com:"));
Assert.IsNull(ClientBase.GetAuthUriBase("https://some.googleapi.com/"));
Assert.IsNull(ClientBase.GetAuthUriBase("dns://some.googleapi.com:443")); // just two slashes
Assert.IsNull(ClientBase.GetAuthUriBase(""));
var composite = CallCredentials.Compose(
new MetadataCredentials(async (uri, m) => { await Task.Delay(1); }),
new MetadataCredentials(async (uri, m) => { await Task.Delay(2); }));
using (var nativeComposite = composite.ToNativeCredentials())
{
}
}
}
}

@ -0,0 +1,73 @@
#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.Runtime.InteropServices;
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 ChannelCredentialsTest
{
[Test]
public void InsecureCredentials_IsNonComposable()
{
Assert.IsFalse(ChannelCredentials.Insecure.IsComposable);
}
[Test]
public void ChannelCredentials_CreateComposite()
{
var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials());
Assert.IsFalse(composite.IsComposable);
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials()));
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null));
// forbid composing non-composable
Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
}
[Test]
public void ChannelCredentials_CreateWrapped()
{
ChannelCredentials.Create(new FakeCallCredentials());
}
}
}

@ -44,13 +44,13 @@ namespace Grpc.Core.Tests
[Test]
public void Constructor_RejectsInvalidParams()
{
Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure));
Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, ChannelCredentials.Insecure));
}
[Test]
public void State_IdleAfterCreation()
{
var channel = new Channel("localhost", Credentials.Insecure);
var channel = new Channel("localhost", ChannelCredentials.Insecure);
Assert.AreEqual(ChannelState.Idle, channel.State);
channel.ShutdownAsync().Wait();
}
@ -58,7 +58,7 @@ namespace Grpc.Core.Tests
[Test]
public void WaitForStateChangedAsync_InvalidArgument()
{
var channel = new Channel("localhost", Credentials.Insecure);
var channel = new Channel("localhost", ChannelCredentials.Insecure);
Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure));
channel.ShutdownAsync().Wait();
}
@ -66,7 +66,7 @@ namespace Grpc.Core.Tests
[Test]
public void ResolvedTarget()
{
var channel = new Channel("127.0.0.1", Credentials.Insecure);
var channel = new Channel("127.0.0.1", ChannelCredentials.Insecure);
Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1"));
channel.ShutdownAsync().Wait();
}
@ -74,7 +74,7 @@ namespace Grpc.Core.Tests
[Test]
public void Shutdown_AllowedOnlyOnce()
{
var channel = new Channel("localhost", Credentials.Insecure);
var channel = new Channel("localhost", ChannelCredentials.Insecure);
channel.ShutdownAsync().Wait();
Assert.Throws(typeof(InvalidOperationException), () => channel.ShutdownAsync().GetAwaiter().GetResult());
}

@ -1,3 +1,4 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
@ -28,16 +29,45 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
#endregion
package grpc.testing;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
// An empty message that you can re-use to avoid defining duplicated empty
// messages in your project. A typical example is to use it as argument or the
// return value of a service API. For instance:
//
// service Foo {
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
// };
//
message Empty {}
namespace Grpc.Core.Tests
{
internal class FakeChannelCredentials : ChannelCredentials
{
readonly bool composable;
public FakeChannelCredentials(bool composable)
{
this.composable = composable;
}
internal override bool IsComposable
{
get { return composable; }
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return null;
}
}
internal class FakeCallCredentials : CallCredentials
{
internal override CredentialsSafeHandle ToNativeCredentials()
{
return null;
}
}
}

@ -63,8 +63,10 @@
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
<Compile Include="ClientBaseTest.cs" />
<Compile Include="CallCredentialsTest.cs" />
<Compile Include="FakeCredentials.cs" />
<Compile Include="MarshallingErrorsTest.cs" />
<Compile Include="ChannelCredentialsTest.cs" />
<Compile Include="ShutdownTest.cs" />
<Compile Include="Internal\AsyncCallTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

@ -49,7 +49,7 @@ namespace Grpc.Core.Internal.Tests
[SetUp]
public void Init()
{
channel = new Channel("localhost", Credentials.Insecure);
channel = new Channel("localhost", ChannelCredentials.Insecure);
fakeCall = new FakeNativeCall();

@ -119,7 +119,7 @@ namespace Grpc.Core.Tests
[Test]
public void RequestParsingError_UnaryRequest()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult("RESPONSE");
});
@ -161,7 +161,7 @@ namespace Grpc.Core.Tests
{
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
CollectionAssert.AreEqual(new [] {"A", "B"}, await requestStream.ToListAsync());
CollectionAssert.AreEqual(new[] { "A", "B" }, await requestStream.ToListAsync());
return "RESPONSE";
});
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());

@ -154,7 +154,7 @@ namespace Grpc.Core.Tests
{
if (channel == null)
{
channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure);
channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure);
}
return channel;
}

@ -0,0 +1,142 @@
#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.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Client-side call credentials. Provide authorization with per-call granularity.
/// </summary>
public abstract class CallCredentials
{
/// <summary>
/// Composes multiple multiple <c>CallCredentials</c> objects into
/// a single <c>CallCredentials</c> object.
/// </summary>
/// <param name="credentials">credentials to compose</param>
/// <returns>The new <c>CompositeCallCredentials</c></returns>
public static CallCredentials Compose(params CallCredentials[] credentials)
{
return new CompositeCallCredentials(credentials);
}
/// <summary>
/// Creates native object for the credentials.
/// </summary>
/// <returns>The native credentials.</returns>
internal abstract CredentialsSafeHandle ToNativeCredentials();
}
/// <summary>
/// Asynchronous authentication interceptor for <see cref="MetadataCredentials"/>.
/// </summary>
/// <param name="authUri">URL of a service to which current remote call needs to authenticate</param>
/// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
/// <returns></returns>
public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
/// <summary>
/// Client-side credentials that delegate metadata based auth to an interceptor.
/// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c>
/// </summary>
public class MetadataCredentials : CallCredentials
{
readonly AsyncAuthInterceptor interceptor;
/// <summary>
/// Initializes a new instance of <c>MetadataCredentials</c> class.
/// </summary>
/// <param name="interceptor">authentication interceptor</param>
public MetadataCredentials(AsyncAuthInterceptor interceptor)
{
this.interceptor = interceptor;
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor);
return plugin.Credentials;
}
}
/// <summary>
/// Credentials that allow composing multiple credentials objects into one <see cref="CallCredentials"/> object.
/// </summary>
internal sealed class CompositeCallCredentials : CallCredentials
{
readonly List<CallCredentials> credentials;
/// <summary>
/// Initializes a new instance of <c>CompositeCallCredentials</c> class.
/// The resulting credentials object will be composite of all the credentials specified as parameters.
/// </summary>
/// <param name="credentials">credentials to compose</param>
public CompositeCallCredentials(params CallCredentials[] credentials)
{
Preconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
this.credentials = new List<CallCredentials>(credentials);
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return ToNativeRecursive(0);
}
// Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier.
// In practice, we won't usually see composites from more than two credentials anyway.
private CredentialsSafeHandle ToNativeRecursive(int startIndex)
{
if (startIndex == credentials.Count - 1)
{
return credentials[startIndex].ToNativeCredentials();
}
using (var cred1 = credentials[startIndex].ToNativeCredentials())
using (var cred2 = ToNativeRecursive(startIndex + 1))
{
var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2);
if (nativeComposite.IsInvalid)
{
throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
}
return nativeComposite;
}
}
}
}

@ -49,6 +49,7 @@ namespace Grpc.Core
CancellationToken cancellationToken;
WriteOptions writeOptions;
ContextPropagationToken propagationToken;
CallCredentials credentials;
/// <summary>
/// Creates a new instance of <c>CallOptions</c> struct.
@ -58,14 +59,16 @@ namespace Grpc.Core
/// <param name="cancellationToken">Can be used to request cancellation of the call.</param>
/// <param name="writeOptions">Write options that will be used for this call.</param>
/// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param>
/// <param name="credentials">Credentials to use for this call.</param>
public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken),
WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null)
WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null, CallCredentials credentials = null)
{
this.headers = headers;
this.deadline = deadline;
this.cancellationToken = cancellationToken;
this.writeOptions = writeOptions;
this.propagationToken = propagationToken;
this.credentials = credentials;
}
/// <summary>
@ -114,6 +117,17 @@ namespace Grpc.Core
}
}
/// <summary>
/// Credentials to use for this call.
/// </summary>
public CallCredentials Credentials
{
get
{
return this.credentials;
}
}
/// <summary>
/// Returns new instance of <see cref="CallOptions"/> with
/// <c>Headers</c> set to the value provided. Values of all other fields are preserved.

@ -68,7 +68,7 @@ namespace Grpc.Core
/// <param name="target">Target of the channel.</param>
/// <param name="credentials">Credentials to secure the channel.</param>
/// <param name="options">Channel options.</param>
public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null)
public Channel(string target, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null)
{
this.target = Preconditions.CheckNotNull(target, "target");
this.environment = GrpcEnvironment.AddRef();
@ -96,7 +96,7 @@ namespace Grpc.Core
/// <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) :
public Channel(string host, int port, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null) :
this(string.Format("{0}:{1}", host, port), credentials, options)
{
}

@ -0,0 +1,238 @@
#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.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Client-side channel credentials. Used for creation of a secure channel.
/// </summary>
public abstract class ChannelCredentials
{
static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl();
/// <summary>
/// Returns instance of credentials that provides no security and
/// will result in creating an unsecure channel with no encryption whatsoever.
/// </summary>
public static ChannelCredentials Insecure
{
get
{
return InsecureInstance;
}
}
/// <summary>
/// Creates a new instance of <c>ChannelCredentials</c> class by composing
/// given channel credentials with call credentials.
/// </summary>
/// <param name="channelCredentials">Channel credentials.</param>
/// <param name="callCredentials">Call credentials.</param>
/// <returns>The new composite <c>ChannelCredentials</c></returns>
public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)
{
return new CompositeChannelCredentials(channelCredentials, callCredentials);
}
/// <summary>
/// Creates a new instance of <c>ChannelCredentials</c> by wrapping
/// an instance of <c>CallCredentials</c>.
/// </summary>
/// <param name="callCredentials">Call credentials.</param>
/// <returns>The <c>ChannelCredentials</c> wrapping given call credentials.</returns>
public static ChannelCredentials Create(CallCredentials callCredentials)
{
return new WrappedCallCredentials(callCredentials);
}
/// <summary>
/// Creates native object for the credentials. May return null if insecure channel
/// should be created.
/// </summary>
/// <returns>The native credentials.</returns>
internal abstract CredentialsSafeHandle ToNativeCredentials();
/// <summary>
/// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
/// </summary>
internal virtual bool IsComposable
{
get { return false; }
}
private sealed class InsecureCredentialsImpl : ChannelCredentials
{
internal override CredentialsSafeHandle ToNativeCredentials()
{
return null;
}
}
}
/// <summary>
/// Client-side SSL credentials.
/// </summary>
public sealed class SslCredentials : ChannelCredentials
{
readonly string rootCertificates;
readonly KeyCertificatePair keyCertificatePair;
/// <summary>
/// Creates client-side SSL credentials loaded from
/// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
/// If that fails, gets the roots certificates from a well known place on disk.
/// </summary>
public SslCredentials() : this(null, null)
{
}
/// <summary>
/// Creates client-side SSL credentials from
/// a string containing PEM encoded root certificates.
/// </summary>
public SslCredentials(string rootCertificates) : this(rootCertificates, null)
{
}
/// <summary>
/// Creates client-side SSL credentials.
/// </summary>
/// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
/// <param name="keyCertificatePair">a key certificate pair.</param>
public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
{
this.rootCertificates = rootCertificates;
this.keyCertificatePair = keyCertificatePair;
}
/// <summary>
/// PEM encoding of the server root certificates.
/// </summary>
public string RootCertificates
{
get
{
return this.rootCertificates;
}
}
/// <summary>
/// Client side key and certificate pair.
/// If null, client will not use key and certificate pair.
/// </summary>
public KeyCertificatePair KeyCertificatePair
{
get
{
return this.keyCertificatePair;
}
}
// Composing composite makes no sense.
internal override bool IsComposable
{
get { return true; }
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
}
}
/// <summary>
/// Credentials that allow composing one <see cref="ChannelCredentials"/> object and
/// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>.
/// </summary>
internal sealed class CompositeChannelCredentials : ChannelCredentials
{
readonly ChannelCredentials channelCredentials;
readonly CallCredentials callCredentials;
/// <summary>
/// Initializes a new instance of <c>CompositeChannelCredentials</c> class.
/// The resulting credentials object will be composite of all the credentials specified as parameters.
/// </summary>
/// <param name="channelCredentials">channelCredentials to compose</param>
/// <param name="callCredentials">channelCredentials to compose</param>
public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
{
this.channelCredentials = Preconditions.CheckNotNull(channelCredentials);
this.callCredentials = Preconditions.CheckNotNull(callCredentials);
Preconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition.");
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
using (var cred1 = channelCredentials.ToNativeCredentials())
using (var cred2 = callCredentials.ToNativeCredentials())
{
var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2);
if (nativeComposite.IsInvalid)
{
throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
}
return nativeComposite;
}
}
}
/// <summary>
/// Credentials wrapping <see cref="CallCredentials"/> as <see cref="ChannelCredentials"/>.
/// </summary>
internal sealed class WrappedCallCredentials : ChannelCredentials
{
readonly CallCredentials callCredentials;
/// <summary>
/// Wraps instance of <c>CallCredentials</c> as <c>ChannelCredentials</c>.
/// </summary>
/// <param name="callCredentials">credentials to wrap</param>
public WrappedCallCredentials(CallCredentials callCredentials)
{
this.callCredentials = Preconditions.CheckNotNull(callCredentials);
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return callCredentials.ToNativeCredentials();
}
}
}

@ -40,18 +40,17 @@ namespace Grpc.Core
/// <summary>
/// Interceptor for call headers.
/// </summary>
public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata);
/// <remarks>Header interceptor is no longer to recommented way to perform authentication.
/// For header (initial metadata) based auth such as OAuth2 or JWT access token, use <see cref="MetadataCredentials"/>.
/// </remarks>
public delegate void HeaderInterceptor(IMethod method, Metadata metadata);
/// <summary>
/// Base class for client-side stubs.
/// </summary>
public abstract class ClientBase
{
// Regex for removal of the optional DNS scheme, trailing port, and trailing backslash
static readonly Regex ChannelTargetPattern = new Regex(@"^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$");
readonly Channel channel;
readonly string authUriBase;
/// <summary>
/// Initializes a new instance of <c>ClientBase</c> class.
@ -60,13 +59,14 @@ namespace Grpc.Core
public ClientBase(Channel channel)
{
this.channel = channel;
this.authUriBase = GetAuthUriBase(channel.Target);
}
/// <summary>
/// Can be used to register a custom header (request metadata) interceptor.
/// Can be used to register a custom header interceptor.
/// The interceptor is invoked each time a new call on this client is started.
/// It is not recommented to use header interceptor to add auth headers to RPC calls.
/// </summary>
/// <seealso cref="HeaderInterceptor"/>
public HeaderInterceptor HeaderInterceptor
{
get;
@ -115,24 +115,9 @@ namespace Grpc.Core
{
options = options.WithHeaders(new Metadata());
}
var authUri = authUriBase != null ? authUriBase + method.ServiceName : null;
interceptor(method, authUri, options.Headers);
interceptor(method, options.Headers);
}
return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options);
}
/// <summary>
/// Creates Auth URI base from channel's target (the one passed at channel creation).
/// Fully-qualified service name is to be appended to this.
/// </summary>
internal static string GetAuthUriBase(string target)
{
var match = ChannelTargetPattern.Match(target);
if (!match.Success)
{
return null;
}
return "https://" + match.Groups[2].Value + "/";
}
}
}

@ -1,138 +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;
namespace Grpc.Core
{
/// <summary>
/// Client-side credentials. Used for creation of a secure channel.
/// </summary>
public abstract class Credentials
{
static readonly Credentials InsecureInstance = new InsecureCredentialsImpl();
/// <summary>
/// Returns instance of credential that provides no security and
/// will result in creating an unsecure channel with no encryption whatsoever.
/// </summary>
public static Credentials Insecure
{
get
{
return InsecureInstance;
}
}
/// <summary>
/// Creates native object for the credentials. May return null if insecure channel
/// should be created.
/// </summary>
/// <returns>The native credentials.</returns>
internal abstract CredentialsSafeHandle ToNativeCredentials();
private sealed class InsecureCredentialsImpl : Credentials
{
internal override CredentialsSafeHandle ToNativeCredentials()
{
return null;
}
}
}
/// <summary>
/// Client-side SSL credentials.
/// </summary>
public sealed class SslCredentials : Credentials
{
readonly string rootCertificates;
readonly KeyCertificatePair keyCertificatePair;
/// <summary>
/// Creates client-side SSL credentials loaded from
/// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
/// If that fails, gets the roots certificates from a well known place on disk.
/// </summary>
public SslCredentials() : this(null, null)
{
}
/// <summary>
/// Creates client-side SSL credentials from
/// a string containing PEM encoded root certificates.
/// </summary>
public SslCredentials(string rootCertificates) : this(rootCertificates, null)
{
}
/// <summary>
/// Creates client-side SSL credentials.
/// </summary>
/// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
/// <param name="keyCertificatePair">a key certificate pair.</param>
public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
{
this.rootCertificates = rootCertificates;
this.keyCertificatePair = keyCertificatePair;
}
/// <summary>
/// PEM encoding of the server root certificates.
/// </summary>
public string RootCertificates
{
get
{
return this.rootCertificates;
}
}
/// <summary>
/// Client side key and certificate pair.
/// If null, client will not use key and certificate pair.
/// </summary>
public KeyCertificatePair KeyCertificatePair
{
get
{
return this.keyCertificatePair;
}
}
internal override CredentialsSafeHandle ToNativeCredentials()
{
return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
}
}
}

@ -48,7 +48,9 @@
<ItemGroup>
<Compile Include="AsyncDuplexStreamingCall.cs" />
<Compile Include="AsyncServerStreamingCall.cs" />
<Compile Include="CallCredentials.cs" />
<Compile Include="IClientStreamWriter.cs" />
<Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" />
<Compile Include="Internal\INativeCall.cs" />
<Compile Include="IServerStreamWriter.cs" />
<Compile Include="IAsyncStreamWriter.cs" />
@ -79,7 +81,7 @@
<Compile Include="Utils\AsyncStreamExtensions.cs" />
<Compile Include="Utils\BenchmarkUtil.cs" />
<Compile Include="Internal\CredentialsSafeHandle.cs" />
<Compile Include="Credentials.cs" />
<Compile Include="ChannelCredentials.cs" />
<Compile Include="Internal\ChannelArgsSafeHandle.cs" />
<Compile Include="Internal\AsyncCompletion.cs" />
<Compile Include="Internal\AsyncCallBase.cs" />

@ -16,7 +16,7 @@
<tags>gRPC RPC Protocol HTTP/2</tags>
<dependencies>
<dependency id="Ix-Async" version="1.2.3" />
<dependency id="grpc.native.csharp_ext" version="$GrpcNativeCsharpExtVersion$" />
<dependency id="grpc.native.csharp" version="$GrpcNativeCsharpVersion$" />
</dependencies>
</metadata>
<files>

@ -344,9 +344,13 @@ namespace Grpc.Core.Internal
var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
parentCall, ContextPropagationToken.DefaultMask, cq,
details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value));
var credentials = details.Options.Credentials;
using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null)
{
return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
parentCall, ContextPropagationToken.DefaultMask, cq,
details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials);
}
}
// Make sure that once cancellationToken for this call is cancelled, Cancel() will be called.

@ -98,6 +98,9 @@ namespace Grpc.Core.Internal
static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call,
BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_set_credentials(CallSafeHandle call, CredentialsSafeHandle credentials);
[DllImport("grpc_csharp_ext.dll")]
static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call);
@ -113,6 +116,11 @@ namespace Grpc.Core.Internal
this.completionRegistry = completionRegistry;
}
public void SetCredentials(CredentialsSafeHandle credentials)
{
grpcsharp_call_set_credentials(this, credentials).CheckOk();
}
public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
{
var ctx = BatchContextSafeHandle.Create();

@ -82,9 +82,13 @@ namespace Grpc.Core.Internal
return grpcsharp_secure_channel_create(credentials, target, channelArgs);
}
public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials)
{
var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
if (credentials != null)
{
result.SetCredentials(credentials);
}
result.SetCompletionRegistry(registry);
return result;
}

@ -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,113 @@
#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, MetadataArraySafeHandle.Create(Metadata.Empty), 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, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg);
Logger.Error(e, GetMetadataExceptionMsg);
}
}
}
}

@ -39,7 +39,7 @@ namespace Math
{
public static void Main(string[] args)
{
var channel = new Channel("127.0.0.1", 23456, Credentials.Insecure);
var channel = new Channel("127.0.0.1", 23456, ChannelCredentials.Insecure);
Math.IMathClient client = new Math.MathClient(channel);
MathExamples.DivExample(client);

@ -61,7 +61,7 @@ namespace Math.Tests
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
client = Math.NewClient(channel);
}

@ -63,7 +63,7 @@ namespace Grpc.HealthCheck.Tests
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
};
server.Start();
channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
client = Grpc.Health.V1Alpha.Health.NewClient(channel);
}

@ -9,6 +9,7 @@
<AssemblyName>Grpc.IntegrationTesting.Client</AssemblyName>
<StartupObject>Grpc.IntegrationTesting.Client.Program</StartupObject>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>6d22e68f</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -38,7 +39,47 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Primitives">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">
@ -60,5 +101,13 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
</Project>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>

@ -9,6 +9,7 @@
<AssemblyName>Grpc.IntegrationTesting.Server</AssemblyName>
<StartupObject>Grpc.IntegrationTesting.Server.Program</StartupObject>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<NuGetPackageImportStamp>d9ee8e52</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -38,7 +39,47 @@
<AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions">
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Primitives">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Grpc.Core\Version.cs">
@ -60,5 +101,13 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
</Project>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
<package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>

@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: empty.proto
// source: test/proto/empty.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
@ -23,7 +23,8 @@ namespace Grpc.Testing {
static Empty() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5YgZwcm90bzM="));
"ChZ0ZXN0L3Byb3RvL2VtcHR5LnByb3RvEgxncnBjLnRlc3RpbmciBwoFRW1w",
"dHliBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {

@ -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" />
@ -118,9 +119,6 @@
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="proto\test.proto" />
<None Include="proto\empty.proto" />
<None Include="proto\messages.proto" />
<None Include="data\README">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

@ -33,20 +33,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
using Google.Apis.Auth.OAuth2;
using Google.Protobuf;
using Grpc.Auth;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using CommandLine.Text;
using System.IO;
namespace Grpc.IntegrationTesting
{
@ -66,11 +67,13 @@ namespace Grpc.IntegrationTesting
[Option("test_case", DefaultValue = "large_unary")]
public string TestCase { get; set; }
[Option("use_tls")]
public bool UseTls { get; set; }
// Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls)
[Option("use_tls", DefaultValue = false)]
public bool? UseTls { get; set; }
[Option("use_test_ca")]
public bool UseTestCa { get; set; }
// Deliberately using nullable bool type to allow --use_test_ca=true syntax (as opposed to --use_test_ca)
[Option("use_test_ca", DefaultValue = false)]
public bool? UseTestCa { get; set; }
[Option("default_service_account", Required = false)]
public string DefaultServiceAccount { get; set; }
@ -116,7 +119,7 @@ namespace Grpc.IntegrationTesting
private async Task Run()
{
var credentials = options.UseTls ? TestCredentials.CreateTestClientCredentials(options.UseTestCa) : Credentials.Insecure;
var credentials = await CreateCredentialsAsync();
List<ChannelOption> channelOptions = null;
if (!string.IsNullOrEmpty(options.ServerHostOverride))
@ -132,6 +135,26 @@ namespace Grpc.IntegrationTesting
await channel.ShutdownAsync();
}
private async Task<ChannelCredentials> CreateCredentialsAsync()
{
var credentials = options.UseTls.Value ? TestCredentials.CreateTestClientCredentials(options.UseTestCa.Value) : ChannelCredentials.Insecure;
if (options.TestCase == "jwt_token_creds")
{
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsTrue(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
}
if (options.TestCase == "compute_engine_creds")
{
var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(googleCredential.IsCreateScopedRequired);
credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
}
return credentials;
}
private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options)
{
switch (options.TestCase)
@ -155,16 +178,16 @@ namespace Grpc.IntegrationTesting
await RunEmptyStreamAsync(client);
break;
case "compute_engine_creds":
await RunComputeEngineCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope);
RunComputeEngineCreds(client, options.DefaultServiceAccount, options.OAuthScope);
break;
case "jwt_token_creds":
await RunJwtTokenCredsAsync(client, options.DefaultServiceAccount);
RunJwtTokenCreds(client);
break;
case "oauth2_auth_token":
await RunOAuth2AuthTokenAsync(client, options.DefaultServiceAccount, options.OAuthScope);
await RunOAuth2AuthTokenAsync(client, options.OAuthScope);
break;
case "per_rpc_creds":
await RunPerRpcCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope);
await RunPerRpcCredsAsync(client, options.OAuthScope);
break;
case "cancel_after_begin":
await RunCancelAfterBeginAsync(client);
@ -318,13 +341,10 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
public static void RunComputeEngineCreds(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
{
Console.WriteLine("running compute_engine_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsFalse(credential.IsCreateScopedRequired);
client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
var request = new SimpleRequest
{
ResponseType = PayloadType.COMPRESSABLE,
@ -334,6 +354,7 @@ namespace Grpc.IntegrationTesting
FillOauthScope = true
};
// not setting credentials here because they were set on channel already
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
@ -344,13 +365,10 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount)
public static void RunJwtTokenCreds(TestService.TestServiceClient client)
{
Console.WriteLine("running jwt_token_creds");
var credential = await GoogleCredential.GetApplicationDefaultAsync();
Assert.IsTrue(credential.IsCreateScopedRequired);
client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
var request = new SimpleRequest
{
ResponseType = PayloadType.COMPRESSABLE,
@ -359,53 +377,50 @@ namespace Grpc.IntegrationTesting
FillUsername = true,
};
// not setting credentials here because they were set on channel already
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
Assert.AreEqual(314159, response.Payload.Body.Length);
Assert.AreEqual(defaultServiceAccount, response.Username);
Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
Console.WriteLine("Passed!");
}
public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
public static async Task RunOAuth2AuthTokenAsync(TestService.TestServiceClient client, string oauthScope)
{
Console.WriteLine("running oauth2_auth_token");
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
string oauth2Token = await credential.GetAccessTokenForRequestAsync();
client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
var credentials = GrpcCredentials.FromAccessToken(oauth2Token);
var request = new SimpleRequest
{
FillUsername = true,
FillOauthScope = true
};
var response = client.UnaryCall(request);
var response = client.UnaryCall(request, new CallOptions(credentials: credentials));
Assert.False(string.IsNullOrEmpty(response.OauthScope));
Assert.True(oauthScope.Contains(response.OauthScope));
Assert.AreEqual(defaultServiceAccount, response.Username);
Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
Console.WriteLine("Passed!");
}
public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string oauthScope)
{
Console.WriteLine("running per_rpc_creds");
ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
string accessToken = await credential.GetAccessTokenForRequestAsync();
var headerInterceptor = AuthInterceptors.FromAccessToken(accessToken);
ITokenAccess googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
var credentials = GrpcCredentials.Create(googleCredential);
var request = new SimpleRequest
{
FillUsername = true,
};
var headers = new Metadata();
headerInterceptor(null, "", headers);
var response = client.UnaryCall(request, headers: headers);
var response = client.UnaryCall(request, new CallOptions(credentials: credentials));
Assert.AreEqual(defaultServiceAccount, response.Username);
Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username);
Console.WriteLine("Passed!");
}
@ -485,5 +500,17 @@ namespace Grpc.IntegrationTesting
{
return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
}
// extracts the client_email field from service account file used for auth test cases
private static string GetEmailFromServiceAccountFile()
{
string keyFile = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
Assert.IsNotNull(keyFile);
var jobject = JObject.Parse(File.ReadAllText(keyFile));
string email = jobject.GetValue("client_email").Value<string>();
Assert.IsTrue(email.Length > 0); // spec requires nonempty client email.
return email;
}
}
}

@ -54,8 +54,9 @@ namespace Grpc.IntegrationTesting
[Option("port", DefaultValue = 8070)]
public int Port { get; set; }
[Option("use_tls")]
public bool UseTls { get; set; }
// Deliberately using nullable bool type to allow --use_tls=true syntax (as opposed to --use_tls)
[Option("use_tls", DefaultValue = false)]
public bool? UseTls { get; set; }
[HelpOption]
public string GetUsage()
@ -99,7 +100,7 @@ namespace Grpc.IntegrationTesting
string host = "0.0.0.0";
int port = options.Port;
if (options.UseTls)
if (options.UseTls.Value)
{
server.Ports.Add(host, port, TestCredentials.CreateTestServerCredentials());
}

@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: messages.proto
// source: test/proto/messages.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
@ -21,37 +21,48 @@ namespace Grpc.Testing {
static Messages() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"Cg5tZXNzYWdlcy5wcm90bxIMZ3JwYy50ZXN0aW5nIkAKB1BheWxvYWQSJwoE",
"dHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIMCgRib2R5",
"GAIgASgMIrEBCg1TaW1wbGVSZXF1ZXN0EjAKDXJlc3BvbnNlX3R5cGUYASAB",
"KA4yGS5ncnBjLnRlc3RpbmcuUGF5bG9hZFR5cGUSFQoNcmVzcG9uc2Vfc2l6",
"ZRgCIAEoBRImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxv",
"YWQSFQoNZmlsbF91c2VybmFtZRgEIAEoCBIYChBmaWxsX29hdXRoX3Njb3Bl",
"GAUgASgIIl8KDlNpbXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5n",
"cnBjLnRlc3RpbmcuUGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0",
"aF9zY29wZRgDIAEoCSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYK",
"B3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJl",
"YW1pbmdJbnB1dENhbGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRf",
"c2l6ZRgBIAEoBSI3ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEo",
"BRITCgtpbnRlcnZhbF91cxgCIAEoBSK1AQoaU3RyZWFtaW5nT3V0cHV0Q2Fs",
"bFJlcXVlc3QSMAoNcmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGlu",
"Zy5QYXlsb2FkVHlwZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAu",
"Z3JwYy50ZXN0aW5nLlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMg",
"ASgLMhUuZ3JwYy50ZXN0aW5nLlBheWxvYWQiRQobU3RyZWFtaW5nT3V0cHV0",
"Q2FsbFJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3Rpbmcu",
"UGF5bG9hZCo/CgtQYXlsb2FkVHlwZRIQCgxDT01QUkVTU0FCTEUQABISCg5V",
"TkNPTVBSRVNTQUJMRRABEgoKBlJBTkRPTRACYgZwcm90bzM="));
"Chl0ZXN0L3Byb3RvL21lc3NhZ2VzLnByb3RvEgxncnBjLnRlc3RpbmciQAoH",
"UGF5bG9hZBInCgR0eXBlGAEgASgOMhkuZ3JwYy50ZXN0aW5nLlBheWxvYWRU",
"eXBlEgwKBGJvZHkYAiABKAwiKwoKRWNob1N0YXR1cxIMCgRjb2RlGAEgASgF",
"Eg8KB21lc3NhZ2UYAiABKAkioQIKDVNpbXBsZVJlcXVlc3QSMAoNcmVzcG9u",
"c2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlwZRIVCg1y",
"ZXNwb25zZV9zaXplGAIgASgFEiYKB3BheWxvYWQYAyABKAsyFS5ncnBjLnRl",
"c3RpbmcuUGF5bG9hZBIVCg1maWxsX3VzZXJuYW1lGAQgASgIEhgKEGZpbGxf",
"b2F1dGhfc2NvcGUYBSABKAgSOwoUcmVzcG9uc2VfY29tcHJlc3Npb24YBiAB",
"KA4yHS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEKD3Jlc3BvbnNl",
"X3N0YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3RhdHVzIl8KDlNp",
"bXBsZVJlc3BvbnNlEiYKB3BheWxvYWQYASABKAsyFS5ncnBjLnRlc3Rpbmcu",
"UGF5bG9hZBIQCgh1c2VybmFtZRgCIAEoCRITCgtvYXV0aF9zY29wZRgDIAEo",
"CSJDChlTdHJlYW1pbmdJbnB1dENhbGxSZXF1ZXN0EiYKB3BheWxvYWQYASAB",
"KAsyFS5ncnBjLnRlc3RpbmcuUGF5bG9hZCI9ChpTdHJlYW1pbmdJbnB1dENh",
"bGxSZXNwb25zZRIfChdhZ2dyZWdhdGVkX3BheWxvYWRfc2l6ZRgBIAEoBSI3",
"ChJSZXNwb25zZVBhcmFtZXRlcnMSDAoEc2l6ZRgBIAEoBRITCgtpbnRlcnZh",
"bF91cxgCIAEoBSKlAgoaU3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QSMAoN",
"cmVzcG9uc2VfdHlwZRgBIAEoDjIZLmdycGMudGVzdGluZy5QYXlsb2FkVHlw",
"ZRI9ChNyZXNwb25zZV9wYXJhbWV0ZXJzGAIgAygLMiAuZ3JwYy50ZXN0aW5n",
"LlJlc3BvbnNlUGFyYW1ldGVycxImCgdwYXlsb2FkGAMgASgLMhUuZ3JwYy50",
"ZXN0aW5nLlBheWxvYWQSOwoUcmVzcG9uc2VfY29tcHJlc3Npb24YBiABKA4y",
"HS5ncnBjLnRlc3RpbmcuQ29tcHJlc3Npb25UeXBlEjEKD3Jlc3BvbnNlX3N0",
"YXR1cxgHIAEoCzIYLmdycGMudGVzdGluZy5FY2hvU3RhdHVzIkUKG1N0cmVh",
"bWluZ091dHB1dENhbGxSZXNwb25zZRImCgdwYXlsb2FkGAEgASgLMhUuZ3Jw",
"Yy50ZXN0aW5nLlBheWxvYWQiMwoNUmVjb25uZWN0SW5mbxIOCgZwYXNzZWQY",
"ASABKAgSEgoKYmFja29mZl9tcxgCIAMoBSo/CgtQYXlsb2FkVHlwZRIQCgxD",
"T01QUkVTU0FCTEUQABISCg5VTkNPTVBSRVNTQUJMRRABEgoKBlJBTkRPTRAC",
"KjIKD0NvbXByZXNzaW9uVHlwZRIICgROT05FEAASCAoER1pJUBABEgsKB0RF",
"RkxBVEUQAmIGcHJvdG8z"));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), }, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(new[] {typeof(global::Grpc.Testing.PayloadType), typeof(global::Grpc.Testing.CompressionType), }, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.Payload), new[]{ "Type", "Body" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleRequest), new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.EchoStatus), new[]{ "Code", "Message" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleRequest), new[]{ "ResponseType", "ResponseSize", "Payload", "FillUsername", "FillOauthScope", "ResponseCompression", "ResponseStatus" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.SimpleResponse), new[]{ "Payload", "Username", "OauthScope" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallRequest), new[]{ "Payload" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingInputCallResponse), new[]{ "AggregatedPayloadSize" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ResponseParameters), new[]{ "Size", "IntervalUs" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), new[]{ "ResponseType", "ResponseParameters", "Payload" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), new[]{ "Payload" }, null, null, null)
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallRequest), new[]{ "ResponseType", "ResponseParameters", "Payload", "ResponseCompression", "ResponseStatus" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.StreamingOutputCallResponse), new[]{ "Payload" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Grpc.Testing.ReconnectInfo), new[]{ "Passed", "BackoffMs" }, null, null, null)
}));
}
#endregion
@ -64,6 +75,12 @@ namespace Grpc.Testing {
RANDOM = 2,
}
public enum CompressionType {
NONE = 0,
GZIP = 1,
DEFLATE = 2,
}
#endregion
#region Messages
@ -195,13 +212,141 @@ namespace Grpc.Testing {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class EchoStatus : pb::IMessage<EchoStatus> {
private static readonly pb::MessageParser<EchoStatus> _parser = new pb::MessageParser<EchoStatus>(() => new EchoStatus());
public static pb::MessageParser<EchoStatus> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[1]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public EchoStatus() {
OnConstruction();
}
partial void OnConstruction();
public EchoStatus(EchoStatus other) : this() {
code_ = other.code_;
message_ = other.message_;
}
public EchoStatus Clone() {
return new EchoStatus(this);
}
public const int CodeFieldNumber = 1;
private int code_;
public int Code {
get { return code_; }
set {
code_ = value;
}
}
public const int MessageFieldNumber = 2;
private string message_ = "";
public string Message {
get { return message_; }
set {
message_ = pb::Preconditions.CheckNotNull(value, "value");
}
}
public override bool Equals(object other) {
return Equals(other as EchoStatus);
}
public bool Equals(EchoStatus other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (Code != other.Code) return false;
if (Message != other.Message) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (Code != 0) hash ^= Code.GetHashCode();
if (Message.Length != 0) hash ^= Message.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (Code != 0) {
output.WriteRawTag(8);
output.WriteInt32(Code);
}
if (Message.Length != 0) {
output.WriteRawTag(18);
output.WriteString(Message);
}
}
public int CalculateSize() {
int size = 0;
if (Code != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Code);
}
if (Message.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Message);
}
return size;
}
public void MergeFrom(EchoStatus other) {
if (other == null) {
return;
}
if (other.Code != 0) {
Code = other.Code;
}
if (other.Message.Length != 0) {
Message = other.Message;
}
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 8: {
Code = input.ReadInt32();
break;
}
case 18: {
Message = input.ReadString();
break;
}
}
}
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class SimpleRequest : pb::IMessage<SimpleRequest> {
private static readonly pb::MessageParser<SimpleRequest> _parser = new pb::MessageParser<SimpleRequest>(() => new SimpleRequest());
public static pb::MessageParser<SimpleRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[1]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[2]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -220,6 +365,8 @@ namespace Grpc.Testing {
Payload = other.payload_ != null ? other.Payload.Clone() : null;
fillUsername_ = other.fillUsername_;
fillOauthScope_ = other.fillOauthScope_;
responseCompression_ = other.responseCompression_;
ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null;
}
public SimpleRequest Clone() {
@ -271,6 +418,24 @@ namespace Grpc.Testing {
}
}
public const int ResponseCompressionFieldNumber = 6;
private global::Grpc.Testing.CompressionType responseCompression_ = global::Grpc.Testing.CompressionType.NONE;
public global::Grpc.Testing.CompressionType ResponseCompression {
get { return responseCompression_; }
set {
responseCompression_ = value;
}
}
public const int ResponseStatusFieldNumber = 7;
private global::Grpc.Testing.EchoStatus responseStatus_;
public global::Grpc.Testing.EchoStatus ResponseStatus {
get { return responseStatus_; }
set {
responseStatus_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as SimpleRequest);
}
@ -287,6 +452,8 @@ namespace Grpc.Testing {
if (!object.Equals(Payload, other.Payload)) return false;
if (FillUsername != other.FillUsername) return false;
if (FillOauthScope != other.FillOauthScope) return false;
if (ResponseCompression != other.ResponseCompression) return false;
if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false;
return true;
}
@ -297,6 +464,8 @@ namespace Grpc.Testing {
if (payload_ != null) hash ^= Payload.GetHashCode();
if (FillUsername != false) hash ^= FillUsername.GetHashCode();
if (FillOauthScope != false) hash ^= FillOauthScope.GetHashCode();
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) hash ^= ResponseCompression.GetHashCode();
if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode();
return hash;
}
@ -325,6 +494,14 @@ namespace Grpc.Testing {
output.WriteRawTag(40);
output.WriteBool(FillOauthScope);
}
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
output.WriteRawTag(48);
output.WriteEnum((int) ResponseCompression);
}
if (responseStatus_ != null) {
output.WriteRawTag(58);
output.WriteMessage(ResponseStatus);
}
}
public int CalculateSize() {
@ -344,6 +521,12 @@ namespace Grpc.Testing {
if (FillOauthScope != false) {
size += 1 + 1;
}
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression);
}
if (responseStatus_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus);
}
return size;
}
@ -369,6 +552,15 @@ namespace Grpc.Testing {
if (other.FillOauthScope != false) {
FillOauthScope = other.FillOauthScope;
}
if (other.ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
ResponseCompression = other.ResponseCompression;
}
if (other.responseStatus_ != null) {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
}
ResponseStatus.MergeFrom(other.ResponseStatus);
}
}
public void MergeFrom(pb::CodedInputStream input) {
@ -401,6 +593,17 @@ namespace Grpc.Testing {
FillOauthScope = input.ReadBool();
break;
}
case 48: {
responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum();
break;
}
case 58: {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
}
input.ReadMessage(responseStatus_);
break;
}
}
}
}
@ -413,7 +616,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<SimpleResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[2]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[3]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -573,7 +776,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingInputCallRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[3]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[4]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -681,7 +884,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingInputCallResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[4]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[5]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -783,7 +986,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<ResponseParameters> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[5]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[6]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -911,7 +1114,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingOutputCallRequest> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[6]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[7]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -928,6 +1131,8 @@ namespace Grpc.Testing {
responseType_ = other.responseType_;
responseParameters_ = other.responseParameters_.Clone();
Payload = other.payload_ != null ? other.Payload.Clone() : null;
responseCompression_ = other.responseCompression_;
ResponseStatus = other.responseStatus_ != null ? other.ResponseStatus.Clone() : null;
}
public StreamingOutputCallRequest Clone() {
@ -960,6 +1165,24 @@ namespace Grpc.Testing {
}
}
public const int ResponseCompressionFieldNumber = 6;
private global::Grpc.Testing.CompressionType responseCompression_ = global::Grpc.Testing.CompressionType.NONE;
public global::Grpc.Testing.CompressionType ResponseCompression {
get { return responseCompression_; }
set {
responseCompression_ = value;
}
}
public const int ResponseStatusFieldNumber = 7;
private global::Grpc.Testing.EchoStatus responseStatus_;
public global::Grpc.Testing.EchoStatus ResponseStatus {
get { return responseStatus_; }
set {
responseStatus_ = value;
}
}
public override bool Equals(object other) {
return Equals(other as StreamingOutputCallRequest);
}
@ -974,6 +1197,8 @@ namespace Grpc.Testing {
if (ResponseType != other.ResponseType) return false;
if(!responseParameters_.Equals(other.responseParameters_)) return false;
if (!object.Equals(Payload, other.Payload)) return false;
if (ResponseCompression != other.ResponseCompression) return false;
if (!object.Equals(ResponseStatus, other.ResponseStatus)) return false;
return true;
}
@ -982,6 +1207,8 @@ namespace Grpc.Testing {
if (ResponseType != global::Grpc.Testing.PayloadType.COMPRESSABLE) hash ^= ResponseType.GetHashCode();
hash ^= responseParameters_.GetHashCode();
if (payload_ != null) hash ^= Payload.GetHashCode();
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) hash ^= ResponseCompression.GetHashCode();
if (responseStatus_ != null) hash ^= ResponseStatus.GetHashCode();
return hash;
}
@ -999,6 +1226,14 @@ namespace Grpc.Testing {
output.WriteRawTag(26);
output.WriteMessage(Payload);
}
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
output.WriteRawTag(48);
output.WriteEnum((int) ResponseCompression);
}
if (responseStatus_ != null) {
output.WriteRawTag(58);
output.WriteMessage(ResponseStatus);
}
}
public int CalculateSize() {
@ -1010,6 +1245,12 @@ namespace Grpc.Testing {
if (payload_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(Payload);
}
if (ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ResponseCompression);
}
if (responseStatus_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(ResponseStatus);
}
return size;
}
@ -1027,6 +1268,15 @@ namespace Grpc.Testing {
}
Payload.MergeFrom(other.Payload);
}
if (other.ResponseCompression != global::Grpc.Testing.CompressionType.NONE) {
ResponseCompression = other.ResponseCompression;
}
if (other.responseStatus_ != null) {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
}
ResponseStatus.MergeFrom(other.ResponseStatus);
}
}
public void MergeFrom(pb::CodedInputStream input) {
@ -1051,6 +1301,17 @@ namespace Grpc.Testing {
input.ReadMessage(payload_);
break;
}
case 48: {
responseCompression_ = (global::Grpc.Testing.CompressionType) input.ReadEnum();
break;
}
case 58: {
if (responseStatus_ == null) {
responseStatus_ = new global::Grpc.Testing.EchoStatus();
}
input.ReadMessage(responseStatus_);
break;
}
}
}
}
@ -1063,7 +1324,7 @@ namespace Grpc.Testing {
public static pb::MessageParser<StreamingOutputCallResponse> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[7]; }
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[8]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
@ -1165,6 +1426,127 @@ namespace Grpc.Testing {
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class ReconnectInfo : pb::IMessage<ReconnectInfo> {
private static readonly pb::MessageParser<ReconnectInfo> _parser = new pb::MessageParser<ReconnectInfo>(() => new ReconnectInfo());
public static pb::MessageParser<ReconnectInfo> Parser { get { return _parser; } }
public static pbr::MessageDescriptor Descriptor {
get { return global::Grpc.Testing.Messages.Descriptor.MessageTypes[9]; }
}
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
public ReconnectInfo() {
OnConstruction();
}
partial void OnConstruction();
public ReconnectInfo(ReconnectInfo other) : this() {
passed_ = other.passed_;
backoffMs_ = other.backoffMs_.Clone();
}
public ReconnectInfo Clone() {
return new ReconnectInfo(this);
}
public const int PassedFieldNumber = 1;
private bool passed_;
public bool Passed {
get { return passed_; }
set {
passed_ = value;
}
}
public const int BackoffMsFieldNumber = 2;
private static readonly pb::FieldCodec<int> _repeated_backoffMs_codec
= pb::FieldCodec.ForInt32(18);
private readonly pbc::RepeatedField<int> backoffMs_ = new pbc::RepeatedField<int>();
public pbc::RepeatedField<int> BackoffMs {
get { return backoffMs_; }
}
public override bool Equals(object other) {
return Equals(other as ReconnectInfo);
}
public bool Equals(ReconnectInfo other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (Passed != other.Passed) return false;
if(!backoffMs_.Equals(other.backoffMs_)) return false;
return true;
}
public override int GetHashCode() {
int hash = 1;
if (Passed != false) hash ^= Passed.GetHashCode();
hash ^= backoffMs_.GetHashCode();
return hash;
}
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) {
if (Passed != false) {
output.WriteRawTag(8);
output.WriteBool(Passed);
}
backoffMs_.WriteTo(output, _repeated_backoffMs_codec);
}
public int CalculateSize() {
int size = 0;
if (Passed != false) {
size += 1 + 1;
}
size += backoffMs_.CalculateSize(_repeated_backoffMs_codec);
return size;
}
public void MergeFrom(ReconnectInfo other) {
if (other == null) {
return;
}
if (other.Passed != false) {
Passed = other.Passed;
}
backoffMs_.Add(other.backoffMs_);
}
public void MergeFrom(pb::CodedInputStream input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
input.SkipLastField();
break;
case 8: {
Passed = input.ReadBool();
break;
}
case 18:
case 16: {
backoffMs_.AddEntriesFrom(input, _repeated_backoffMs_codec);
break;
}
}
}
}
}
#endregion
}

@ -0,0 +1,97 @@
#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 = ChannelCredentials.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);
}
}
}

@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test.proto
// source: test/proto/test.proto
#pragma warning disable 1591, 0612, 3021
#region Designer generated code
@ -21,21 +21,26 @@ namespace Grpc.Testing {
static Test() {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"Cgp0ZXN0LnByb3RvEgxncnBjLnRlc3RpbmcaC2VtcHR5LnByb3RvGg5tZXNz",
"YWdlcy5wcm90bzK7BAoLVGVzdFNlcnZpY2USNQoJRW1wdHlDYWxsEhMuZ3Jw",
"Yy50ZXN0aW5nLkVtcHR5GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5",
"Q2FsbBIbLmdycGMudGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0",
"aW5nLlNpbXBsZVJlc3BvbnNlEmwKE1N0cmVhbWluZ091dHB1dENhbGwSKC5n",
"cnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBj",
"LnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3Ry",
"ZWFtaW5nSW5wdXRDYWxsEicuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0",
"Q2FsbFJlcXVlc3QaKC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxs",
"UmVzcG9uc2UoARJpCg5GdWxsRHVwbGV4Q2FsbBIoLmdycGMudGVzdGluZy5T",
"dHJlYW1pbmdPdXRwdXRDYWxsUmVxdWVzdBopLmdycGMudGVzdGluZy5TdHJl",
"YW1pbmdPdXRwdXRDYWxsUmVzcG9uc2UoATABEmkKDkhhbGZEdXBsZXhDYWxs",
"EiguZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXF1ZXN0Giku",
"Z3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZSgBMAFi",
"BnByb3RvMw=="));
"ChV0ZXN0L3Byb3RvL3Rlc3QucHJvdG8SDGdycGMudGVzdGluZxoWdGVzdC9w",
"cm90by9lbXB0eS5wcm90bxoZdGVzdC9wcm90by9tZXNzYWdlcy5wcm90bzK7",
"BAoLVGVzdFNlcnZpY2USNQoJRW1wdHlDYWxsEhMuZ3JwYy50ZXN0aW5nLkVt",
"cHR5GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5Q2FsbBIbLmdycGMu",
"dGVzdGluZy5TaW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0aW5nLlNpbXBsZVJl",
"c3BvbnNlEmwKE1N0cmVhbWluZ091dHB1dENhbGwSKC5ncnBjLnRlc3Rpbmcu",
"U3RyZWFtaW5nT3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBjLnRlc3RpbmcuU3Ry",
"ZWFtaW5nT3V0cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3RyZWFtaW5nSW5wdXRD",
"YWxsEicuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0Q2FsbFJlcXVlc3Qa",
"KC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxsUmVzcG9uc2UoARJp",
"Cg5GdWxsRHVwbGV4Q2FsbBIoLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRw",
"dXRDYWxsUmVxdWVzdBopLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRD",
"YWxsUmVzcG9uc2UoATABEmkKDkhhbGZEdXBsZXhDYWxsEiguZ3JwYy50ZXN0",
"aW5nLlN0cmVhbWluZ091dHB1dENhbGxSZXF1ZXN0GikuZ3JwYy50ZXN0aW5n",
"LlN0cmVhbWluZ091dHB1dENhbGxSZXNwb25zZSgBMAEyVQoUVW5pbXBsZW1l",
"bnRlZFNlcnZpY2USPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRlc3Rp",
"bmcuRW1wdHkaEy5ncnBjLnRlc3RpbmcuRW1wdHkyfwoQUmVjb25uZWN0U2Vy",
"dmljZRIxCgVTdGFydBITLmdycGMudGVzdGluZy5FbXB0eRoTLmdycGMudGVz",
"dGluZy5FbXB0eRI4CgRTdG9wEhMuZ3JwYy50ZXN0aW5nLkVtcHR5GhsuZ3Jw",
"Yy50ZXN0aW5nLlJlY29ubmVjdEluZm9iBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
new pbr::FileDescriptor[] { global::Grpc.Testing.Proto.Empty.Descriptor, global::Grpc.Testing.Messages.Descriptor, },
new pbr::GeneratedCodeInfo(null, null));

@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: test.proto
// source: test/proto/test.proto
#region Designer generated code
using System;
@ -207,5 +207,191 @@ namespace Grpc.Testing {
}
}
public static class UnimplementedService
{
static readonly string __ServiceName = "grpc.testing.UnimplementedService";
static readonly Marshaller<global::Grpc.Testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom);
static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_UnimplementedCall = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
MethodType.Unary,
__ServiceName,
"UnimplementedCall",
__Marshaller_Empty,
__Marshaller_Empty);
// service descriptor
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Grpc.Testing.Test.Descriptor.Services[1]; }
}
// client interface
public interface IUnimplementedServiceClient
{
global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options);
AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options);
}
// server-side interface
public interface IUnimplementedService
{
Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context);
}
// client stub
public class UnimplementedServiceClient : ClientBase, IUnimplementedServiceClient
{
public UnimplementedServiceClient(Channel channel) : base(channel)
{
}
public global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_UnimplementedCall, new CallOptions(headers, deadline, cancellationToken));
return Calls.BlockingUnaryCall(call, request);
}
public global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_UnimplementedCall, options);
return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_UnimplementedCall, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_UnimplementedCall, options);
return Calls.AsyncUnaryCall(call, request);
}
}
// creates service definition that can be registered with a server
public static ServerServiceDefinition BindService(IUnimplementedService serviceImpl)
{
return ServerServiceDefinition.CreateBuilder(__ServiceName)
.AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build();
}
// creates a new client
public static UnimplementedServiceClient NewClient(Channel channel)
{
return new UnimplementedServiceClient(channel);
}
}
public static class ReconnectService
{
static readonly string __ServiceName = "grpc.testing.ReconnectService";
static readonly Marshaller<global::Grpc.Testing.Empty> __Marshaller_Empty = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.Empty.Parser.ParseFrom);
static readonly Marshaller<global::Grpc.Testing.ReconnectInfo> __Marshaller_ReconnectInfo = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Testing.ReconnectInfo.Parser.ParseFrom);
static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_Start = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>(
MethodType.Unary,
__ServiceName,
"Start",
__Marshaller_Empty,
__Marshaller_Empty);
static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo> __Method_Stop = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.ReconnectInfo>(
MethodType.Unary,
__ServiceName,
"Stop",
__Marshaller_Empty,
__Marshaller_ReconnectInfo);
// service descriptor
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Grpc.Testing.Test.Descriptor.Services[2]; }
}
// client interface
public interface IReconnectServiceClient
{
global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options);
AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options);
global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, CallOptions options);
AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken));
AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, CallOptions options);
}
// server-side interface
public interface IReconnectService
{
Task<global::Grpc.Testing.Empty> Start(global::Grpc.Testing.Empty request, ServerCallContext context);
Task<global::Grpc.Testing.ReconnectInfo> Stop(global::Grpc.Testing.Empty request, ServerCallContext context);
}
// client stub
public class ReconnectServiceClient : ClientBase, IReconnectServiceClient
{
public ReconnectServiceClient(Channel channel) : base(channel)
{
}
public global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_Start, new CallOptions(headers, deadline, cancellationToken));
return Calls.BlockingUnaryCall(call, request);
}
public global::Grpc.Testing.Empty Start(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_Start, options);
return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_Start, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_Start, options);
return Calls.AsyncUnaryCall(call, request);
}
public global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_Stop, new CallOptions(headers, deadline, cancellationToken));
return Calls.BlockingUnaryCall(call, request);
}
public global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_Stop, options);
return Calls.BlockingUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))
{
var call = CreateCall(__Method_Stop, new CallOptions(headers, deadline, cancellationToken));
return Calls.AsyncUnaryCall(call, request);
}
public AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, CallOptions options)
{
var call = CreateCall(__Method_Stop, options);
return Calls.AsyncUnaryCall(call, request);
}
}
// creates service definition that can be registered with a server
public static ServerServiceDefinition BindService(IReconnectService serviceImpl)
{
return ServerServiceDefinition.CreateBuilder(__ServiceName)
.AddMethod(__Method_Start, serviceImpl.Start)
.AddMethod(__Method_Stop, serviceImpl.Stop).Build();
}
// creates a new client
public static ReconnectServiceClient NewClient(Channel channel)
{
return new ReconnectServiceClient(channel);
}
}
}
#endregion

@ -1,132 +0,0 @@
// 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.
// Message definitions to be used by integration test service definitions.
syntax = "proto3";
package grpc.testing;
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
PayloadType type = 1;
// Primary contents of payload.
bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 response_size = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
// Whether SimpleResponse should include username.
bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
string username = 2;
// OAuth scope.
string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
Payload payload = 1;
}

@ -1,71 +0,0 @@
// 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.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
import "empty.proto";
import "messages.proto";
package grpc.testing;
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
// One request followed by one response.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}

@ -20,9 +20,9 @@ endlocal
@call ..\..\vsprojects\build_plugins.bat || goto :error
%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp_ext.nuspec -Version %CORE_VERSION% || goto :error
%NUGET% pack ..\..\vsprojects\nuget_package\grpc.native.csharp.nuspec -Version %CORE_VERSION% || goto :error
%NUGET% pack Grpc.Auth\Grpc.Auth.nuspec -Symbols -Version %VERSION% || goto :error
%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpExtVersion=%CORE_VERSION% || goto :error
%NUGET% pack Grpc.Core\Grpc.Core.nuspec -Symbols -Version %VERSION% -Properties GrpcNativeCsharpVersion=%CORE_VERSION% || goto :error
%NUGET% pack Grpc.HealthCheck\Grpc.HealthCheck.nuspec -Symbols -Version %VERSION_WITH_BETA% -Properties ProtobufVersion=%PROTOBUF_VERSION% || goto :error
%NUGET% pack Grpc.Tools.nuspec -Version %VERSION% || goto :error
%NUGET% pack Grpc.nuspec -Version %VERSION% || goto :error

@ -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 {
@ -665,16 +665,16 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call,
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata(
grpc_call *call, grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[0].flags = 0;
ops[0].reserved = NULL;
grpc_call *call, grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[0].flags = 0;
ops[0].reserved = NULL;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
NULL);
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
NULL);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
@ -785,6 +785,11 @@ grpcsharp_call_send_initial_metadata(grpc_call *call,
NULL);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_set_credentials(grpc_call *call,
grpc_credentials *creds) {
return grpc_call_set_credentials(call, creds);
}
/* Server */
GPR_EXPORT grpc_server *GPR_CALLTYPE
@ -892,6 +897,47 @@ 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(
grpc_credentials_plugin_metadata_cb cb,
void *user_data, grpc_metadata_array *metadata,
grpc_status_code status, const char *error_details) {
cb(user_data, metadata->metadata, metadata->count, status, error_details);
}
typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)(
void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb,
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)(gpr_intptr)state;
interceptor(state, service_url, cb, user_data, 0);
}
static void grpcsharp_metadata_credentials_destroy_handler(void *state) {
grpcsharp_metadata_interceptor_func interceptor =
(grpcsharp_metadata_interceptor_func)(gpr_intptr)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 = (void*)(gpr_intptr)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,

@ -42,7 +42,7 @@ $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \
-I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$INTEROP_DIR --grpc_out=$INTEROP_DIR \
-I $INTEROP_DIR/proto $INTEROP_DIR/proto/*.proto
-I ../.. ../../test/proto/*.proto
$PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \
-I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto

@ -17,7 +17,7 @@ services.
<a name="install"></a>
## Install protoc with the gRPC plugin
On Mac OS X, install [homebrew][]. On Linux, install [linuxbrew][].
On Mac OS X, install [homebrew][].
Run the following command to install _protoc_ and the gRPC _protoc_ plugin:
```sh
@ -153,7 +153,7 @@ _protoc_, in which case no system modification nor renaming is necessary.
<a name="no-cocoapods"></a>
### Integrate the generated gRPC library without using Cocoapods
You need to compile the generated `.pbpbjc.*` files (the enums and messages) without ARC support,
You need to compile the generated `.pbobjc.*` files (the enums and messages) without ARC support,
and the generated `.pbrpc.*` files (the services) with ARC support. The generated code depends on
v0.5+ of the Objective-C gRPC runtime library and v3.0.0-alpha-3+ of the Objective-C Protobuf
runtime library.
@ -168,7 +168,6 @@ Objective-C Protobuf runtime library.
[Protocol Buffers]:https://developers.google.com/protocol-buffers/
[homebrew]:http://brew.sh
[linuxbrew]:https://github.com/Homebrew/linuxbrew
[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
[example Podfile]:https://github.com/grpc/grpc/blob/master/src/objective-c/examples/Sample/Podfile
[sample app]: https://github.com/grpc/grpc/tree/master/src/objective-c/examples/Sample

@ -35,4 +35,14 @@
@implementation GRXWriter
- (void)startWithWriteable:(id<GRXWriteable>)writeable {
NSAssert(NO, @"Missing base implementation for %@", NSStringFromSelector(_cmd));
[self doesNotRecognizeSelector:_cmd];
}
- (void)finishWithError:(NSError *)errorOrNil {
NSAssert(NO, @"Missing base implementation for %@", NSStringFromSelector(_cmd));
[self doesNotRecognizeSelector:_cmd];
}
@end

@ -10,10 +10,10 @@
<email>grpc-packages@google.com</email>
<active>yes</active>
</lead>
<date>2015-09-24</date>
<time>09:51:04</time>
<date>2015-10-07</date>
<time>13:40:54</time>
<version>
<release>0.6.0</release>
<release>0.6.1</release>
<api>0.6.0</api>
</version>
<stability>
@ -22,12 +22,7 @@
</stability>
<license>BSD</license>
<notes>
- support per message compression disable
- expose per-call host override option
- expose connectivity API
- expose channel target and call peer
- add user-agent
- update to wrap gRPC C core library beta version 0.11.0
- fixed undefined constant fatal error when run with apache/nginx #2275
</notes>
<contents>
<dir baseinstalldir="/" name="/">
@ -44,7 +39,7 @@
<file baseinstalldir="/" md5sum="6988d6e97c19c8f8e3eb92371cf8246b" name="credentials.h" role="src" />
<file baseinstalldir="/" md5sum="38a1bc979d810c36ebc2a52d4b7b5319" name="CREDITS" role="doc" />
<file baseinstalldir="/" md5sum="3f35b472bbdef5a788cd90617d7d0847" name="LICENSE" role="doc" />
<file baseinstalldir="/" md5sum="6a550516a1423def0786851c76f87c85" name="php_grpc.c" role="src" />
<file baseinstalldir="/" md5sum="b77f1f3941aaf7a21090b493e9f26037" name="php_grpc.c" role="src" />
<file baseinstalldir="/" md5sum="673b07859d9f69232f8a754c56780686" name="php_grpc.h" role="src" />
<file baseinstalldir="/" md5sum="7533a6d3ea02c78cad23a9651de0825d" name="README.md" role="doc" />
<file baseinstalldir="/" md5sum="3e4e960454ebb2fc7b78a840493f5315" name="server.c" role="src" />
@ -118,5 +113,20 @@ Update to wrap gRPC C Core version 0.10.0
- update to wrap gRPC C core library beta version 0.11.0
</notes>
</release>
<release>
<version>
<release>0.6.1</release>
<api>0.6.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<date>2015-10-07</date>
<license>BSD</license>
<notes>
- fixed undefined constant fatal error when run with apache/nginx #2275
</notes>
</release>
</changelog>
</package>

@ -150,7 +150,7 @@ PHP_MINIT_FUNCTION(grpc) {
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT",
GRPC_STATUS_INVALID_ARGUMENT,
CONST_CS | CONST_PERSISTENT);
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED",
GRPC_STATUS_DEADLINE_EXCEEDED,
CONST_CS | CONST_PERSISTENT);
@ -173,7 +173,8 @@ PHP_MINIT_FUNCTION(grpc) {
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE,
REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE",
GRPC_STATUS_OUT_OF_RANGE,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED",
GRPC_STATUS_UNIMPLEMENTED,
@ -202,7 +203,8 @@ PHP_MINIT_FUNCTION(grpc) {
GRPC_OP_RECV_INITIAL_METADATA,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE",
GRPC_OP_RECV_MESSAGE, CONST_CS | CONST_PERSISTENT);
GRPC_OP_RECV_MESSAGE,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT",
GRPC_OP_RECV_STATUS_ON_CLIENT,
CONST_CS | CONST_PERSISTENT);
@ -212,11 +214,14 @@ PHP_MINIT_FUNCTION(grpc) {
/* Register connectivity state constants */
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE",
GRPC_CHANNEL_IDLE, CONST_CS | CONST_PERSISTENT);
GRPC_CHANNEL_IDLE,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING",
GRPC_CHANNEL_CONNECTING, CONST_CS | CONST_PERSISTENT);
GRPC_CHANNEL_CONNECTING,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY",
GRPC_CHANNEL_READY, CONST_CS | CONST_PERSISTENT);
GRPC_CHANNEL_READY,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE",
GRPC_CHANNEL_TRANSIENT_FAILURE,
CONST_CS | CONST_PERSISTENT);

@ -176,8 +176,7 @@ module GRPC
deadline: deadline,
timeout: timeout,
parent: parent)
kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri)
md = update_metadata(kw, method)
return c.request_response(req, **md) unless return_op
# return the operation view of the active_call; define #execute as a
@ -244,8 +243,7 @@ module GRPC
deadline: deadline,
timeout: timeout,
parent: parent)
kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri)
md = update_metadata(kw, method)
return c.client_streamer(requests, **md) unless return_op
# return the operation view of the active_call; define #execute as a
@ -322,8 +320,7 @@ module GRPC
deadline: deadline,
timeout: timeout,
parent: parent)
kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri)
md = update_metadata(kw, method)
return c.server_streamer(req, **md, &blk) unless return_op
# return the operation view of the active_call; define #execute
@ -439,8 +436,7 @@ module GRPC
deadline: deadline,
timeout: timeout,
parent: parent)
kw_with_jwt_uri = self.class.update_with_jwt_aud_uri(kw, @host, method)
md = @update_metadata.nil? ? kw : @update_metadata.call(kw_with_jwt_uri)
md = update_metadata(kw, method)
return c.bidi_streamer(requests, **md, &blk) unless return_op
# return the operation view of the active_call; define #execute
@ -454,6 +450,16 @@ module GRPC
private
def update_metadata(kw, method)
return kw if @update_metadata.nil?
just_jwt_uri = self.class.update_with_jwt_aud_uri({}, @host, method)
updated = @update_metadata.call(just_jwt_uri)
# keys should be lowercase
updated = Hash[updated.each_pair.map { |k, v| [k.downcase, v] }]
kw.merge(updated)
end
# Creates a new active stub
#
# @param method [string] the method being called.

@ -159,6 +159,20 @@ describe 'ClientStub' do
th.join
end
it 'should downcase the keys provided by the metadata updater' do
server_port = create_test_server
host = "localhost:#{server_port}"
th = run_request_response(@sent_msg, @resp, @pass,
k1: 'downcased-key-v1', k2: 'v2')
update_md = proc do |md|
md[:K1] = 'downcased-key-v1'
md
end
stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md)
expect(get_response(stub)).to eq(@resp)
th.join
end
it 'should send a request when configured using an override channel' do
server_port = create_test_server
alt_host = "localhost:#{server_port}"

@ -46,7 +46,7 @@
#include "test/core/end2end/cq_verifier.h"
#include "src/core/channel/channel_args.h"
#include "src/core/channel/compress_filter.h"
#include "src/core/surface/call.h"
#include "src/core/surface/call_test_only.h"
enum { TIMEOUT = 200000 };
@ -196,12 +196,13 @@ static void request_with_payload_template(
cq_expect_completion(cqv, tag(101), 1);
cq_verify(cqv);
GPR_ASSERT(GPR_BITCOUNT(grpc_call_get_encodings_accepted_by_peer(s)) == 3);
GPR_ASSERT(GPR_BITGET(grpc_call_get_encodings_accepted_by_peer(s),
GPR_ASSERT(
GPR_BITCOUNT(grpc_call_test_only_get_encodings_accepted_by_peer(s)) == 3);
GPR_ASSERT(GPR_BITGET(grpc_call_test_only_get_encodings_accepted_by_peer(s),
GRPC_COMPRESS_NONE) != 0);
GPR_ASSERT(GPR_BITGET(grpc_call_get_encodings_accepted_by_peer(s),
GPR_ASSERT(GPR_BITGET(grpc_call_test_only_get_encodings_accepted_by_peer(s),
GRPC_COMPRESS_DEFLATE) != 0);
GPR_ASSERT(GPR_BITGET(grpc_call_get_encodings_accepted_by_peer(s),
GPR_ASSERT(GPR_BITGET(grpc_call_test_only_get_encodings_accepted_by_peer(s),
GRPC_COMPRESS_GZIP) != 0);
op = ops;

@ -46,7 +46,7 @@
#include "test/cpp/util/test_config.h"
DEFINE_bool(use_tls, false, "Whether to use tls.");
DEFINE_bool(use_prod_roots, false, "True to use SSL roots for google");
DEFINE_bool(use_test_ca, false, "False to use SSL roots for google");
DEFINE_int32(server_port, 0, "Server port.");
DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
DEFINE_string(server_host_override, "foo.test.google.fr",

@ -52,7 +52,7 @@
#include "test/cpp/util/create_test_channel.h"
DECLARE_bool(use_tls);
DECLARE_bool(use_prod_roots);
DECLARE_bool(use_test_ca);
DECLARE_int32(server_port);
DECLARE_string(server_host);
DECLARE_string(server_host_override);
@ -102,7 +102,7 @@ std::shared_ptr<Channel> CreateChannelForTestCase(
GPR_ASSERT(FLAGS_use_tls);
creds = GoogleComputeEngineCredentials();
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_use_tls, FLAGS_use_prod_roots, creds);
FLAGS_use_tls, !FLAGS_use_test_ca, creds);
} else if (test_case == "jwt_token_creds") {
std::shared_ptr<Credentials> creds;
GPR_ASSERT(FLAGS_use_tls);
@ -111,15 +111,15 @@ std::shared_ptr<Channel> CreateChannelForTestCase(
creds =
ServiceAccountJWTAccessCredentials(json_key, token_lifetime.count());
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_use_tls, FLAGS_use_prod_roots, creds);
FLAGS_use_tls, !FLAGS_use_test_ca, creds);
} else if (test_case == "oauth2_auth_token") {
grpc::string raw_token = GetOauth2AccessToken();
std::shared_ptr<Credentials> creds = AccessTokenCredentials(raw_token);
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_use_tls, FLAGS_use_prod_roots, creds);
FLAGS_use_tls, !FLAGS_use_test_ca, creds);
} else {
return CreateTestChannel(host_port, FLAGS_server_host_override,
FLAGS_use_tls, FLAGS_use_prod_roots);
FLAGS_use_tls, !FLAGS_use_test_ca);
}
}

@ -38,7 +38,7 @@
#include <grpc++/channel.h>
#include "src/core/surface/call.h"
#include "src/core/surface/call_test_only.h"
namespace grpc {
namespace testing {
@ -57,11 +57,11 @@ class InteropClientContextInspector {
// Inspector methods, able to peek inside ClientContext, follow.
grpc_compression_algorithm GetCallCompressionAlgorithm() const {
return grpc_call_get_compression_algorithm(context_.call_);
return grpc_call_test_only_get_compression_algorithm(context_.call_);
}
gpr_uint32 GetMessageFlags() const {
return grpc_call_get_message_flags(context_.call_);
return grpc_call_test_only_get_message_flags(context_.call_);
}
private:

@ -38,7 +38,7 @@
#include <gflags/gflags.h>
#include <grpc++/security/server_credentials.h>
#include "src/core/surface/call.h"
#include "src/core/surface/call_test_only.h"
#include "test/core/end2end/data/ssl_test_data.h"
DECLARE_bool(use_tls);
@ -65,11 +65,11 @@ InteropServerContextInspector::InteropServerContextInspector(
grpc_compression_algorithm
InteropServerContextInspector::GetCallCompressionAlgorithm() const {
return grpc_call_get_compression_algorithm(context_.call_);
return grpc_call_test_only_get_compression_algorithm(context_.call_);
}
gpr_uint32 InteropServerContextInspector::GetEncodingsAcceptedByClient() const {
return grpc_call_get_encodings_accepted_by_peer(context_.call_);
return grpc_call_test_only_get_encodings_accepted_by_peer(context_.call_);
}
std::shared_ptr<const AuthContext>

@ -82,9 +82,12 @@ static deque<string> get_hosts(const string& name) {
namespace runsc {
// ClientContext allocator
static ClientContext* AllocContext(list<ClientContext>* contexts) {
template <class T>
static ClientContext* AllocContext(list<ClientContext>* contexts, T deadline) {
contexts->emplace_back();
return &contexts->back();
auto context = &contexts->back();
context->set_deadline(deadline);
return context;
}
struct ServerData {
@ -147,6 +150,11 @@ std::unique_ptr<ScenarioResult> RunScenario(
// Trim to just what we need
workers.resize(num_clients + num_servers);
gpr_timespec deadline =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_from_seconds(
warmup_seconds + benchmark_seconds + 20, GPR_TIMESPAN));
// Start servers
using runsc::ServerData;
// servers is array rather than std::vector to avoid gcc-4.4 issues
@ -160,7 +168,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
result_server_config.set_host(workers[i]);
*args.mutable_setup() = server_config;
servers[i].stream =
servers[i].stub->RunServer(runsc::AllocContext(&contexts));
servers[i].stub->RunServer(runsc::AllocContext(&contexts, deadline));
GPR_ASSERT(servers[i].stream->Write(args));
ServerStatus init_status;
GPR_ASSERT(servers[i].stream->Read(&init_status));
@ -188,7 +196,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
result_client_config.set_host(workers[i + num_servers]);
*args.mutable_setup() = client_config;
clients[i].stream =
clients[i].stub->RunTest(runsc::AllocContext(&contexts));
clients[i].stub->RunTest(runsc::AllocContext(&contexts, deadline));
GPR_ASSERT(clients[i].stream->Write(args));
ClientStatus init_status;
GPR_ASSERT(clients[i].stream->Read(&init_status));

@ -858,6 +858,7 @@ src/core/statistics/census_rpc_stats.h \
src/core/surface/api_trace.h \
src/core/surface/byte_buffer_queue.h \
src/core/surface/call.h \
src/core/surface/call_test_only.h \
src/core/surface/channel.h \
src/core/surface/completion_queue.h \
src/core/surface/event_string.h \

@ -1477,7 +1477,7 @@ grpc_cloud_prod_auth_compute_engine_creds_gen_node_cmd() {
# cmd=$($grpc_gen_test_cmd $flags)
grpc_interop_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls --use_test_ca";
local the_cmd="$cmd_prefix $test_script $@";
echo $the_cmd
}
@ -1489,7 +1489,7 @@ grpc_interop_gen_cxx_cmd() {
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls --use_prod_roots";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls";
local gfe_flags=$(_grpc_prod_gfe_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
echo $the_cmd
@ -1502,7 +1502,7 @@ grpc_cloud_prod_gen_cxx_cmd() {
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_auth_service_account_creds_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls --use_prod_roots";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls";
local gfe_flags=$(_grpc_prod_gfe_flags)
local added_gfe_flags=$(_grpc_svc_acc_test_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $added_gfe_flags $@";
@ -1516,7 +1516,7 @@ grpc_cloud_prod_auth_service_account_creds_gen_cxx_cmd() {
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_auth_compute_engine_creds_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls --use_prod_roots";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls";
local gfe_flags=$(_grpc_prod_gfe_flags)
local added_gfe_flags=$(_grpc_gce_test_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $added_gfe_flags $@";
@ -1530,7 +1530,7 @@ grpc_cloud_prod_auth_compute_engine_creds_gen_cxx_cmd() {
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_auth_jwt_token_creds_gen_cxx_cmd() {
local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls --use_prod_roots";
local test_script="/var/local/git/grpc/bins/opt/interop_client --use_tls";
local gfe_flags=$(_grpc_prod_gfe_flags)
local added_gfe_flags=$(_grpc_jwt_token_test_flags)
local the_cmd="$cmd_prefix $test_script $gfe_flags $added_gfe_flags $@";

@ -0,0 +1,9 @@
HTTP/2 Interop Tests
====
This is a suite of tests that check a server to see if it plays nicely with other HTTP/2 clients. To run, just type:
`go test -spec :1234`
Where ":1234" is the ip:port of a running server.

@ -0,0 +1,6 @@
// http2interop project doc.go
/*
http2interop document
*/
package http2interop

@ -0,0 +1,11 @@
package http2interop
import (
"io"
)
type Frame interface {
GetHeader() *FrameHeader
ParsePayload(io.Reader) error
MarshalBinary() ([]byte, error)
}

@ -0,0 +1,109 @@
package http2interop
import (
"encoding/binary"
"fmt"
"io"
)
type FrameHeader struct {
Length int
Type FrameType
Flags byte
Reserved Reserved
StreamID
}
type Reserved bool
func (r Reserved) String() string {
if r {
return "R"
}
return ""
}
func (fh *FrameHeader) Parse(r io.Reader) error {
buf := make([]byte, 9)
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
return fh.UnmarshalBinary(buf)
}
func (fh *FrameHeader) UnmarshalBinary(b []byte) error {
if len(b) != 9 {
return fmt.Errorf("Invalid frame header length %d", len(b))
}
*fh = FrameHeader{
Length: int(b[0])<<16 | int(b[1])<<8 | int(b[2]),
Type: FrameType(b[3]),
Flags: b[4],
Reserved: Reserved(b[5]>>7 == 1),
StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff),
}
return nil
}
func (fh *FrameHeader) MarshalBinary() ([]byte, error) {
buf := make([]byte, 9, 9+fh.Length)
if fh.Length > 0xFFFFFF || fh.Length < 0 {
return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length)
}
if fh.StreamID < 0 {
return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID)
}
buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length)
buf[3] = byte(fh.Type)
buf[4] = fh.Flags
binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID))
return buf, nil
}
type StreamID int32
type FrameType byte
func (ft FrameType) String() string {
switch ft {
case DataFrameType:
return "DATA"
case HeadersFrameType:
return "HEADERS"
case PriorityFrameType:
return "PRIORITY"
case ResetStreamFrameType:
return "RST_STREAM"
case SettingsFrameType:
return "SETTINGS"
case PushPromiseFrameType:
return "PUSH_PROMISE"
case PingFrameType:
return "PING"
case GoAwayFrameType:
return "GOAWAY"
case WindowUpdateFrameType:
return "WINDOW_UPDATE"
case ContinuationFrameType:
return "CONTINUATION"
default:
return fmt.Sprintf("UNKNOWN(%d)", byte(ft))
}
}
// Types
const (
DataFrameType FrameType = 0
HeadersFrameType FrameType = 1
PriorityFrameType FrameType = 2
ResetStreamFrameType FrameType = 3
SettingsFrameType FrameType = 4
PushPromiseFrameType FrameType = 5
PingFrameType FrameType = 6
GoAwayFrameType FrameType = 7
WindowUpdateFrameType FrameType = 8
ContinuationFrameType FrameType = 9
)

@ -0,0 +1,245 @@
package http2interop
import (
"crypto/tls"
"fmt"
"io"
"log"
)
const (
Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
)
func parseFrame(r io.Reader) (Frame, error) {
fh := FrameHeader{}
if err := fh.Parse(r); err != nil {
return nil, err
}
var f Frame
switch fh.Type {
case PingFrameType:
f = &PingFrame{
Header: fh,
}
case SettingsFrameType:
f = &SettingsFrame{
Header: fh,
}
default:
f = &UnknownFrame{
Header: fh,
}
}
if err := f.ParsePayload(r); err != nil {
return nil, err
}
return f, nil
}
func streamFrame(w io.Writer, f Frame) error {
raw, err := f.MarshalBinary()
if err != nil {
return err
}
if _, err := w.Write(raw); err != nil {
return err
}
return nil
}
func getHttp2Conn(addr string) (*tls.Conn, error) {
config := &tls.Config{
InsecureSkipVerify: true,
NextProtos: []string{"h2"},
}
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
return nil, err
}
return conn, nil
}
func testClientShortSettings(addr string, length int) error {
c, err := getHttp2Conn(addr)
if err != nil {
return err
}
defer c.Close()
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
// Bad, settings, non multiple of 6
sf := &UnknownFrame{
Header: FrameHeader{
Type: SettingsFrameType,
},
Data: make([]byte, length),
}
if err := streamFrame(c, sf); err != nil {
return err
}
for {
frame, err := parseFrame(c)
if err != nil {
return err
}
log.Println(frame)
}
return nil
}
func testClientPrefaceWithStreamId(addr string) error {
c, err := getHttp2Conn(addr)
if err != nil {
return err
}
defer c.Close()
// Good so far
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
// Bad, settings do not have ids
sf := &SettingsFrame{
Header: FrameHeader{
StreamID: 1,
},
}
if err := streamFrame(c, sf); err != nil {
return err
}
for {
frame, err := parseFrame(c)
if err != nil {
return err
}
log.Println(frame)
}
return nil
}
func testUnknownFrameType(addr string) error {
c, err := getHttp2Conn(addr)
if err != nil {
return err
}
defer c.Close()
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
// Send some settings, which are part of the client preface
sf := &SettingsFrame{}
if err := streamFrame(c, sf); err != nil {
return err
}
// Write a bunch of invalid frame types.
for ft := ContinuationFrameType + 1; ft != 0; ft++ {
fh := &UnknownFrame{
Header: FrameHeader{
Type: ft,
},
}
if err := streamFrame(c, fh); err != nil {
return err
}
}
pf := &PingFrame{
Data: []byte("01234567"),
}
if err := streamFrame(c, pf); err != nil {
return err
}
for {
frame, err := parseFrame(c)
if err != nil {
return err
}
if npf, ok := frame.(*PingFrame); !ok {
continue
} else {
if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 {
return fmt.Errorf("Bad ping %+v", *npf)
}
return nil
}
}
return nil
}
func testShortPreface(addr string, prefacePrefix string) error {
c, err := getHttp2Conn(addr)
if err != nil {
return err
}
defer c.Close()
if _, err := c.Write([]byte(prefacePrefix)); err != nil {
return err
}
buf := make([]byte, 256)
for ; err == nil; _, err = c.Read(buf) {
}
// TODO: maybe check for a GOAWAY?
return err
}
func testTLSMaxVersion(addr string, version uint16) error {
config := &tls.Config{
InsecureSkipVerify: true,
NextProtos: []string{"h2"},
MaxVersion: version,
}
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
return err
}
defer conn.Close()
buf := make([]byte, 256)
if n, err := conn.Read(buf); err != nil {
if n != 0 {
return fmt.Errorf("Expected no bytes to be read, but was %d", n)
}
return err
}
return nil
}
func testTLSApplicationProtocol(addr string) error {
config := &tls.Config{
InsecureSkipVerify: true,
NextProtos: []string{"h2c"},
}
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
return err
}
defer conn.Close()
buf := make([]byte, 256)
if n, err := conn.Read(buf); err != nil {
if n != 0 {
return fmt.Errorf("Expected no bytes to be read, but was %d", n)
}
return err
}
return nil
}

@ -0,0 +1,50 @@
package http2interop
import (
"crypto/tls"
"flag"
"io"
"os"
"testing"
)
var (
serverSpec = flag.String("spec", ":50051", "The server spec to test")
)
func TestShortPreface(t *testing.T) {
for i := 0; i < len(Preface)-1; i++ {
if err := testShortPreface(*serverSpec, Preface[:i]+"X"); err != io.EOF {
t.Error("Expected an EOF but was", err)
}
}
}
func TestUnknownFrameType(t *testing.T) {
if err := testUnknownFrameType(*serverSpec); err != nil {
t.Fatal(err)
}
}
func TestTLSApplicationProtocol(t *testing.T) {
if err := testTLSApplicationProtocol(*serverSpec); err != io.EOF {
t.Fatal("Expected an EOF but was", err)
}
}
func TestTLSMaxVersion(t *testing.T) {
if err := testTLSMaxVersion(*serverSpec, tls.VersionTLS11); err != io.EOF {
t.Fatal("Expected an EOF but was", err)
}
}
func TestClientPrefaceWithStreamId(t *testing.T) {
if err := testClientPrefaceWithStreamId(*serverSpec); err != io.EOF {
t.Fatal("Expected an EOF but was", err)
}
}
func TestMain(m *testing.M) {
flag.Parse()
os.Exit(m.Run())
}

@ -0,0 +1,65 @@
package http2interop
import (
"fmt"
"io"
)
type PingFrame struct {
Header FrameHeader
Data []byte
}
const (
PING_ACK = 0x01
)
func (f *PingFrame) GetHeader() *FrameHeader {
return &f.Header
}
func (f *PingFrame) ParsePayload(r io.Reader) error {
raw := make([]byte, f.Header.Length)
if _, err := io.ReadFull(r, raw); err != nil {
return err
}
return f.UnmarshalPayload(raw)
}
func (f *PingFrame) UnmarshalPayload(raw []byte) error {
if f.Header.Length != len(raw) {
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw))
}
if f.Header.Length != 8 {
return fmt.Errorf("Invalid Payload length %d", f.Header.Length)
}
f.Data = []byte(string(raw))
return nil
}
func (f *PingFrame) MarshalPayload() ([]byte, error) {
if len(f.Data) != 8 {
return nil, fmt.Errorf("Invalid Payload length %d", len(f.Data))
}
return []byte(string(f.Data)), nil
}
func (f *PingFrame) MarshalBinary() ([]byte, error) {
payload, err := f.MarshalPayload()
if err != nil {
return nil, err
}
f.Header.Length = len(payload)
f.Header.Type = PingFrameType
header, err := f.Header.MarshalBinary()
if err != nil {
return nil, err
}
header = append(header, payload...)
return header, nil
}

@ -0,0 +1,109 @@
package http2interop
import (
"encoding/binary"
"fmt"
"io"
)
const (
SETTINGS_ACK = 1
)
type SettingsFrame struct {
Header FrameHeader
Params []SettingsParameter
}
type SettingsIdentifier uint16
const (
SettingsHeaderTableSize SettingsIdentifier = 1
SettingsEnablePush SettingsIdentifier = 2
SettingsMaxConcurrentStreams SettingsIdentifier = 3
SettingsInitialWindowSize SettingsIdentifier = 4
SettingsMaxFrameSize SettingsIdentifier = 5
SettingsMaxHeaderListSize SettingsIdentifier = 6
)
func (si SettingsIdentifier) String() string {
switch si {
case SettingsHeaderTableSize:
return "HEADER_TABLE_SIZE"
case SettingsEnablePush:
return "ENABLE_PUSH"
case SettingsMaxConcurrentStreams:
return "MAX_CONCURRENT_STREAMS"
case SettingsInitialWindowSize:
return "INITIAL_WINDOW_SIZE"
case SettingsMaxFrameSize:
return "MAX_FRAME_SIZE"
case SettingsMaxHeaderListSize:
return "MAX_HEADER_LIST_SIZE"
default:
return fmt.Sprintf("UNKNOWN(%d)", uint16(si))
}
}
type SettingsParameter struct {
Identifier SettingsIdentifier
Value uint32
}
func (f *SettingsFrame) GetHeader() *FrameHeader {
return &f.Header
}
func (f *SettingsFrame) ParsePayload(r io.Reader) error {
raw := make([]byte, f.Header.Length)
if _, err := io.ReadFull(r, raw); err != nil {
return err
}
return f.UnmarshalPayload(raw)
}
func (f *SettingsFrame) UnmarshalPayload(raw []byte) error {
if f.Header.Length != len(raw) {
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw))
}
if f.Header.Length%6 != 0 {
return fmt.Errorf("Invalid Payload length %d", f.Header.Length)
}
f.Params = make([]SettingsParameter, 0, f.Header.Length/6)
for i := 0; i < len(raw); i += 6 {
f.Params = append(f.Params, SettingsParameter{
Identifier: SettingsIdentifier(binary.BigEndian.Uint16(raw[i : i+2])),
Value: binary.BigEndian.Uint32(raw[i+2 : i+6]),
})
}
return nil
}
func (f *SettingsFrame) MarshalPayload() ([]byte, error) {
raw := make([]byte, 0, len(f.Params)*6)
for i, p := range f.Params {
binary.BigEndian.PutUint16(raw[i*6:i*6+2], uint16(p.Identifier))
binary.BigEndian.PutUint32(raw[i*6+2:i*6+6], p.Value)
}
return raw, nil
}
func (f *SettingsFrame) MarshalBinary() ([]byte, error) {
payload, err := f.MarshalPayload()
if err != nil {
return nil, err
}
f.Header.Length = len(payload)
f.Header.Type = SettingsFrameType
header, err := f.Header.MarshalBinary()
if err != nil {
return nil, err
}
header = append(header, payload...)
return header, nil
}

@ -0,0 +1,54 @@
package http2interop
import (
"fmt"
"io"
)
type UnknownFrame struct {
Header FrameHeader
Data []byte
}
func (f *UnknownFrame) GetHeader() *FrameHeader {
return &f.Header
}
func (f *UnknownFrame) ParsePayload(r io.Reader) error {
raw := make([]byte, f.Header.Length)
if _, err := io.ReadFull(r, raw); err != nil {
return err
}
return f.UnmarshalPayload(raw)
}
func (f *UnknownFrame) UnmarshalPayload(raw []byte) error {
if f.Header.Length != len(raw) {
return fmt.Errorf("Invalid Payload length %d != %d", f.Header.Length, len(raw))
}
f.Data = []byte(string(raw))
return nil
}
func (f *UnknownFrame) MarshalPayload() ([]byte, error) {
return []byte(string(f.Data)), nil
}
func (f *UnknownFrame) MarshalBinary() ([]byte, error) {
f.Header.Length = len(f.Data)
buf, err := f.Header.MarshalBinary()
if err != nil {
return nil, err
}
payload, err := f.MarshalPayload()
if err != nil {
return nil, err
}
buf = append(buf, payload...)
return buf, nil
}

@ -53,8 +53,8 @@ DOCKER_IMAGE_NAME=grpc_jenkins_slave${docker_suffix}_`sha1sum tools/jenkins/grpc
# Make sure docker image has been built. Should be instantaneous if so.
docker build -t $DOCKER_IMAGE_NAME tools/jenkins/grpc_jenkins_slave$docker_suffix
# Make sure the CID file is gone.
rm -f docker.cid
# Choose random name for docker container
CONTAINER_NAME="run_tests_$(uuidgen)"
# Run tests inside docker
docker run \
@ -70,23 +70,21 @@ docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/bin/docker \
-w /var/local/git/grpc \
--cidfile=docker.cid \
--name=$CONTAINER_NAME \
$DOCKER_IMAGE_NAME \
bash -l /var/local/jenkins/grpc/tools/jenkins/docker_run_tests.sh || DOCKER_FAILED="true"
DOCKER_CID=`cat docker.cid`
if [ "$XML_REPORT" != "" ]
then
docker cp "$DOCKER_CID:/var/local/git/grpc/$XML_REPORT" $git_root
docker cp "$CONTAINER_NAME:/var/local/git/grpc/$XML_REPORT" $git_root
fi
docker cp "$DOCKER_CID:/var/local/git/grpc/reports.zip" $git_root || true
docker cp "$CONTAINER_NAME:/var/local/git/grpc/reports.zip" $git_root || true
unzip $git_root/reports.zip -d $git_root || true
rm -f reports.zip
# remove the container, possibly killing it first
docker rm -f $DOCKER_CID || true
docker rm -f $CONTAINER_NAME || true
if [ "$DOCKER_FAILED" != "" ] && [ "$XML_REPORT" == "" ]
then

@ -77,7 +77,7 @@ docker build -t $BASE_IMAGE --force-rm=true tools/jenkins/$BASE_NAME || exit $?
# Create a local branch so the child Docker script won't complain
git branch -f jenkins-docker
CIDFILE=`mktemp -u --suffix=.cid`
CONTAINER_NAME="build_${BASE_NAME}_$(uuidgen)"
# Prepare image for interop tests, commit it on success.
(docker run \
@ -85,17 +85,14 @@ CIDFILE=`mktemp -u --suffix=.cid`
-i $TTY_FLAG \
$MOUNT_ARGS \
-v /tmp/ccache:/tmp/ccache \
--cidfile=$CIDFILE \
--name=$CONTAINER_NAME \
$BASE_IMAGE \
bash -l /var/local/jenkins/grpc/tools/jenkins/$BASE_NAME/build_interop.sh \
&& docker commit `cat $CIDFILE` $INTEROP_IMAGE \
&& docker commit $CONTAINER_NAME $INTEROP_IMAGE \
&& echo "Successfully built image $INTEROP_IMAGE")
EXITCODE=$?
# remove intermediate container, possibly killing it first
docker rm -f `cat $CIDFILE`
# remove the cidfile
rm -rf `cat $CIDFILE`
docker rm -f $CONTAINER_NAME
exit $EXITCODE

@ -48,6 +48,7 @@ RUN apt-get update && apt-get install -y \
libc6-dbg \
libc6-dev \
libgtest-dev \
libssl-dev \
libtool \
make \
strace \

@ -45,5 +45,4 @@ make install-certs
# build Node interop client & server
npm install -g node-gyp
make install_c -C /var/local/git/grpc
(cd src/node && npm install && node-gyp rebuild)
(npm install && node-gyp rebuild)

@ -63,10 +63,6 @@ then
# Prevent msbuild from picking up "platform" env variable, which would break the build
unset platform
# TODO(jtattermusch): integrate nuget restore in a nicer way.
/cygdrive/c/nuget/nuget.exe restore vsprojects/grpc.sln
/cygdrive/c/nuget/nuget.exe restore src/csharp/Grpc.sln
python tools/run_tests/run_tests.py -t -l $language -x report.xml $@ || true
elif [ "$platform" == "macos" ]
@ -90,3 +86,9 @@ else
echo "Unknown platform $platform"
exit 1
fi
if [ ! -e reports/index.html ]
then
mkdir -p reports
echo 'No reports generated.' > reports/index.html
fi

@ -37,14 +37,6 @@ else
MSBUILD_CONFIG="Release"
fi
# change to gRPC repo root
cd $(dirname $0)/../..
cd $(dirname $0)/../../src/csharp
root=`pwd`
if [ -n "$NUGET" ]
then
$NUGET restore src/csharp/Grpc.sln
fi
xbuild /p:Configuration=$MSBUILD_CONFIG src/csharp/Grpc.sln
xbuild /p:Configuration=$MSBUILD_CONFIG Grpc.sln

@ -37,6 +37,4 @@ export GRPC_CONFIG=${CONFIG:-opt}
cd $(dirname $0)/../../src/ruby
rm -rf ./tmp
bundle install
rake compile:grpc

@ -38,18 +38,15 @@ import subprocess
_DEVNULL = open(os.devnull, 'w')
def wait_for_file(filepath, timeout_seconds=15):
"""Wait until given file exists and returns its content."""
started = time.time()
while time.time() - started < timeout_seconds:
if os.path.isfile(filepath):
with open(filepath, 'r') as f:
content = f.read()
# make sure we don't return empty content
if content:
return content
time.sleep(1)
raise Exception('Failed to read file %s.' % filepath)
def random_name(base_name):
"""Randomizes given base name."""
return '%s_%s' % (base_name, uuid.uuid4())
def docker_kill(cid):
"""Kills a docker container. Returns True if successful."""
return subprocess.call(['docker','kill', str(cid)]) == 0
def docker_mapped_port(cid, port):
@ -92,23 +89,16 @@ class DockerJob:
def __init__(self, spec):
self._spec = spec
self._job = jobset.Job(spec, bin_hash=None, newline_on_success=True, travis=True, add_env={}, xml_report=None)
self._cidfile = spec.cidfile
self._cid = None
def cid(self):
"""Gets cid of this container"""
if not self._cid:
self._cid = wait_for_file(self._cidfile)
return self._cid
self._container_name = spec.container_name
def mapped_port(self, port):
return docker_mapped_port(self.cid(), port)
return docker_mapped_port(self._container_name, port)
def kill(self, suppress_failure=False):
"""Sends kill signal to the container."""
if suppress_failure:
self._job.suppress_failure_message()
return subprocess.call(['docker','kill', self.cid()]) == 0
return docker_kill(self._container_name)
def is_running(self):
"""Polls a job and returns True if given job is still running."""

@ -135,13 +135,14 @@ class JobSpec(object):
def __init__(self, cmdline, shortname=None, environ=None, hash_targets=None,
cwd=None, shell=False, timeout_seconds=5*60, flake_retries=0,
timeout_retries=0):
timeout_retries=0, kill_handler=None):
"""
Arguments:
cmdline: a list of arguments to pass as the command line
environ: a dictionary of environment variables to set in the child process
hash_targets: which files to include in the hash representing the jobs version
(or empty, indicating the job should not be hashed)
kill_handler: a handler that will be called whenever job.kill() is invoked
"""
if environ is None:
environ = {}
@ -156,6 +157,7 @@ class JobSpec(object):
self.timeout_seconds = timeout_seconds
self.flake_retries = flake_retries
self.timeout_retries = timeout_retries
self.kill_handler = kill_handler
def identity(self):
return '%r %r %r' % (self.cmdline, self.environ, self.hash_targets)
@ -254,6 +256,8 @@ class Job(object):
def kill(self):
if self._state == _RUNNING:
self._state = _KILLED
if self._spec.kill_handler:
self._spec.kill_handler(self)
self._process.terminate()
def suppress_failure_message(self):

@ -0,0 +1,21 @@
@rem Performs nuget restore step for C/C++.
setlocal
@rem enter repo root
cd /d %~dp0\..\..
@rem Location of nuget.exe
set NUGET=C:\nuget\nuget.exe
if exist %NUGET% (
%NUGET% restore vsprojects/grpc.sln || goto :error
)
endlocal
goto :EOF
:error
echo Failed!
exit /b %errorlevel%

@ -0,0 +1,22 @@
@rem Performs nuget restore step for C#.
setlocal
@rem enter repo root
cd /d %~dp0\..\..
@rem Location of nuget.exe
set NUGET=C:\nuget\nuget.exe
if exist %NUGET% (
%NUGET% restore vsprojects/grpc_csharp_ext.sln || goto :error
%NUGET% restore src/csharp/Grpc.sln || goto :error
)
endlocal
goto :EOF
:error
echo Failed!
exit /b %errorlevel%

@ -0,0 +1,41 @@
#!/bin/bash
# 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.
set -ex
# cd to gRPC csharp directory
cd $(dirname $0)/../../src/csharp
root=`pwd`
if [ -n "$NUGET" ]
then
$NUGET restore Grpc.sln
fi

@ -0,0 +1,39 @@
#!/bin/bash
# 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.
set -ex
export GRPC_CONFIG=${CONFIG:-opt}
# change to grpc's ruby directory
cd $(dirname $0)/../../src/ruby
bundle install

@ -62,7 +62,6 @@ _CLOUD_TO_CLOUD_BASE_ARGS = [
_SSL_CERT_ENV = { 'SSL_CERT_FILE':'/usr/local/share/grpc/roots.pem' }
# TODO(jtattermusch) unify usage of --use_tls and --use_tls=true
# TODO(jtattermusch) unify usage of --use_prod_roots and --use_test_ca
# TODO(jtattermusch) go uses --tls_ca_file instead of --use_test_ca
@ -75,11 +74,11 @@ class CXXLanguage:
def cloud_to_prod_args(self):
return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
['--use_tls=true','--use_prod_roots'])
['--use_tls=true'])
def cloud_to_cloud_args(self):
return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
['--use_tls=true'])
['--use_tls=true', '--use_test_ca=true'])
def cloud_to_prod_env(self):
return {}
@ -100,17 +99,17 @@ class CSharpLanguage:
def cloud_to_prod_args(self):
return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
['--use_tls'])
['--use_tls=true'])
def cloud_to_cloud_args(self):
return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
['--use_tls', '--use_test_ca'])
['--use_tls=true', '--use_test_ca=true'])
def cloud_to_prod_env(self):
return _SSL_CERT_ENV
def server_args(self):
return ['mono', 'Grpc.IntegrationTesting.Server.exe', '--use_tls']
return ['mono', 'Grpc.IntegrationTesting.Server.exe', '--use_tls=true']
def __str__(self):
return 'csharp'
@ -312,23 +311,39 @@ def add_auth_options(language, test_case, cmdline, env):
if test_case in ['per_rpc_creds', 'oauth2_auth_token']:
cmdline += [oauth_scope_arg]
if test_case == 'oauth2_auth_token' and language == 'c++':
# C++ oauth2 test uses GCE creds and thus needs to know the default account
cmdline += [default_account_arg]
if test_case == 'compute_engine_creds':
cmdline += [oauth_scope_arg, default_account_arg]
return (cmdline, env)
def _job_kill_handler(job):
if job._spec.container_name:
dockerjob.docker_kill(job._spec.container_name)
def cloud_to_prod_jobspec(language, test_case, docker_image=None, auth=False):
"""Creates jobspec for cloud-to-prod interop test"""
cmdline = language.cloud_to_prod_args() + ['--test_case=%s' % test_case]
cwd = language.client_cwd
environ = language.cloud_to_prod_env()
container_name = None
if auth:
cmdline, environ = add_auth_options(language, test_case, cmdline, environ)
cmdline = bash_login_cmdline(cmdline)
if docker_image:
cmdline = docker_run_cmdline(cmdline, image=docker_image, cwd=cwd, environ=environ)
container_name = dockerjob.random_name('interop_client_%s' % language)
cmdline = docker_run_cmdline(cmdline,
image=docker_image,
cwd=cwd,
environ=environ,
docker_args=['--net=host',
'--name', container_name])
cwd = None
environ = None
@ -340,7 +355,9 @@ def cloud_to_prod_jobspec(language, test_case, docker_image=None, auth=False):
shortname="%s:%s:%s" % (suite_name, language, test_case),
timeout_seconds=2*60,
flake_retries=5 if args.allow_flakes else 0,
timeout_retries=2 if args.allow_flakes else 0)
timeout_retries=2 if args.allow_flakes else 0,
kill_handler=_job_kill_handler)
test_job.container_name = container_name
return test_job
@ -353,11 +370,14 @@ def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
'--server_port=%s' % server_port ])
cwd = language.client_cwd
if docker_image:
container_name = dockerjob.random_name('interop_client_%s' % language)
cmdline = docker_run_cmdline(cmdline,
image=docker_image,
cwd=cwd,
docker_args=['--net=host'])
docker_args=['--net=host',
'--name', container_name])
cwd = None
test_job = jobset.JobSpec(
cmdline=cmdline,
cwd=cwd,
@ -365,25 +385,27 @@ def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
test_case),
timeout_seconds=2*60,
flake_retries=5 if args.allow_flakes else 0,
timeout_retries=2 if args.allow_flakes else 0)
timeout_retries=2 if args.allow_flakes else 0,
kill_handler=_job_kill_handler)
test_job.container_name = container_name
return test_job
def server_jobspec(language, docker_image):
"""Create jobspec for running a server"""
cidfile = tempfile.mktemp()
container_name = dockerjob.random_name('interop_server_%s' % language)
cmdline = bash_login_cmdline(language.server_args() +
['--port=%s' % _DEFAULT_SERVER_PORT])
docker_cmdline = docker_run_cmdline(cmdline,
image=docker_image,
cwd=language.server_cwd,
docker_args=['-p', str(_DEFAULT_SERVER_PORT),
'--cidfile', cidfile])
'--name', container_name])
server_job = jobset.JobSpec(
cmdline=docker_cmdline,
shortname="interop_server:%s" % language,
shortname="interop_server_%s" % language,
timeout_seconds=30*60)
server_job.cidfile = cidfile
server_job.container_name = container_name
return server_job

@ -167,7 +167,10 @@ class CLanguage(object):
return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]
def pre_build_steps(self):
return []
if self.platform == 'windows':
return [['tools\\run_tests\\pre_build_c.bat']]
else:
return []
def build_steps(self):
return []
@ -284,7 +287,7 @@ class RubyLanguage(object):
environ=_FORCE_ENVIRON_FOR_WRAPPERS)]
def pre_build_steps(self):
return []
return [['tools/run_tests/pre_build_ruby.sh']]
def make_targets(self):
return ['static_c']
@ -321,7 +324,10 @@ class CSharpLanguage(object):
for assembly in assemblies]
def pre_build_steps(self):
return []
if self.platform == 'windows':
return [['tools\\run_tests\\pre_build_csharp.bat']]
else:
return [['tools/run_tests/pre_build_csharp.sh']]
def make_targets(self):
# For Windows, this target doesn't really build anything,
@ -612,7 +618,7 @@ for l in languages:
set(l.make_targets()))
build_steps = list(set(
jobset.JobSpec(cmdline, environ={'CONFIG': cfg})
jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, flake_retries=5)
for cfg in build_configs
for l in languages
for cmdline in l.pre_build_steps()))
@ -620,7 +626,7 @@ if make_targets:
make_commands = itertools.chain.from_iterable(make_jobspec(cfg, list(targets), makefile) for cfg in build_configs for (makefile, targets) in make_targets.iteritems())
build_steps.extend(set(make_commands))
build_steps.extend(set(
jobset.JobSpec(cmdline, environ={'CONFIG': cfg})
jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, timeout_seconds=10*60)
for cfg in build_configs
for l in languages
for cmdline in l.build_steps()))
@ -731,7 +737,7 @@ def _build_and_run(
check_cancelled, newline_on_success, travis, cache, xml_report=None):
"""Do one pass of building & running tests."""
# build latest sequentially
if not jobset.run(build_steps, maxjobs=1,
if not jobset.run(build_steps, maxjobs=1, stop_on_failure=True,
newline_on_success=newline_on_success, travis=travis):
return 1

@ -12365,6 +12365,7 @@
"src/core/surface/api_trace.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.h",
"src/core/surface/completion_queue.h",
"src/core/surface/event_string.h",
@ -12607,6 +12608,7 @@
"src/core/surface/call.h",
"src/core/surface/call_details.c",
"src/core/surface/call_log_batch.c",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.c",
"src/core/surface/channel.h",
"src/core/surface/channel_connectivity.c",
@ -12861,6 +12863,7 @@
"src/core/surface/api_trace.h",
"src/core/surface/byte_buffer_queue.h",
"src/core/surface/call.h",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.h",
"src/core/surface/completion_queue.h",
"src/core/surface/event_string.h",
@ -13073,6 +13076,7 @@
"src/core/surface/call.h",
"src/core/surface/call_details.c",
"src/core/surface/call_log_batch.c",
"src/core/surface/call_test_only.h",
"src/core/surface/channel.c",
"src/core/surface/channel.h",
"src/core/surface/channel_connectivity.c",

@ -16,7 +16,7 @@ Build all flavors of gRPC C# extension and package them as a NuGet package.
```
buildall.bat
nuget pack grpc.native.csharp_ext.nuspec
nuget pack grpc.native.csharp.nuspec
```
When building the NuGet package, ignore the "Assembly outside lib folder" warnings (they DLLs are not assemblies, they are native libraries).

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>grpc.native.csharp_ext</id>
<id>grpc.native.csharp</id>
<version>$version$</version>
<authors>Google Inc.</authors>
<owners>grpc-packages</owners>
@ -20,8 +20,8 @@
</dependencies>
</metadata>
<files>
<file src="grpc.native.csharp_ext.props" target="\build\portable-net45\grpc.native.csharp_ext.props" />
<file src="grpc.native.csharp_ext.targets" target="\build\portable-net45\grpc.native.csharp_ext.targets" />
<file src="grpc.native.csharp.props" target="\build\portable-net45\grpc.native.csharp.props" />
<file src="grpc.native.csharp.targets" target="\build\portable-net45\grpc.native.csharp.targets" />
<file src="output\v100\Win32\Release\grpc_csharp_ext.dll" target="/build/native/bin/v100\Win32\Release\grpc_csharp_ext.dll" />
<file src="output\v120\Win32\Release\grpc_csharp_ext.dll" target="/build/native/bin/v120\Win32\Release\grpc_csharp_ext.dll" />
<file src="output\v100\Win32\Debug\grpc_csharp_ext.dll" target="/build/native/bin/v100\Win32\Debug\grpc_csharp_ext.dll" />

@ -344,6 +344,7 @@
<ClInclude Include="..\..\..\src\core\surface\api_trace.h" />
<ClInclude Include="..\..\..\src\core\surface\byte_buffer_queue.h" />
<ClInclude Include="..\..\..\src\core\surface\call.h" />
<ClInclude Include="..\..\..\src\core\surface\call_test_only.h" />
<ClInclude Include="..\..\..\src\core\surface\channel.h" />
<ClInclude Include="..\..\..\src\core\surface\completion_queue.h" />
<ClInclude Include="..\..\..\src\core\surface\event_string.h" />

@ -743,6 +743,9 @@
<ClInclude Include="..\..\..\src\core\surface\call.h">
<Filter>src\core\surface</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\surface\call_test_only.h">
<Filter>src\core\surface</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\surface\channel.h">
<Filter>src\core\surface</Filter>
</ClInclude>

@ -323,6 +323,7 @@
<ClInclude Include="..\..\..\src\core\surface\api_trace.h" />
<ClInclude Include="..\..\..\src\core\surface\byte_buffer_queue.h" />
<ClInclude Include="..\..\..\src\core\surface\call.h" />
<ClInclude Include="..\..\..\src\core\surface\call_test_only.h" />
<ClInclude Include="..\..\..\src\core\surface\channel.h" />
<ClInclude Include="..\..\..\src\core\surface\completion_queue.h" />
<ClInclude Include="..\..\..\src\core\surface\event_string.h" />

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save