diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/surface/call.c | 3 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 1 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs | 14 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/PInvokeTest.cs | 4 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/PerformanceTest.cs | 8 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/SanityTest.cs | 125 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ShutdownTest.cs | 11 | ||||
-rw-r--r-- | src/csharp/Grpc.Core/Server.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj | 3 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs | 54 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs | 7 | ||||
-rw-r--r-- | src/csharp/Grpc.IntegrationTesting/packages.config | 1 | ||||
-rw-r--r-- | src/csharp/tests.json | 45 | ||||
-rw-r--r-- | src/node/interop/async_delay_queue.js | 79 | ||||
-rw-r--r-- | src/node/interop/interop_server.js | 23 | ||||
-rw-r--r-- | src/php/ext/grpc/package.xml | 41 |
17 files changed, 373 insertions, 50 deletions
diff --git a/src/core/surface/call.c b/src/core/surface/call.c index d170dbf68e..55aad9e8ac 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -1131,7 +1131,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, } gpr_mu_unlock(&call->mu); post_batch_completion(exec_ctx, bctl); - return GRPC_CALL_OK; + error = GRPC_CALL_OK; + goto done; } /* rewrite batch ops into a transport op */ diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index a171855ee0..475c792347 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -91,6 +91,7 @@ <Compile Include="ContextPropagationTest.cs" /> <Compile Include="MetadataTest.cs" /> <Compile Include="PerformanceTest.cs" /> + <Compile Include="SanityTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs index 48b5d78ca9..74f7f2497a 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -167,18 +167,18 @@ namespace Grpc.Core.Internal.Tests () => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))); } - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void NowBenchmark() { // approx Timespec.Now latency <33ns BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; }); } - - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void PreciseNowBenchmark() { // approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC) diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 073c502daf..af55cb0566 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -59,6 +59,8 @@ namespace Grpc.Core.Tests [Test] public void CompletionQueueCreateDestroyBenchmark() { + GrpcEnvironment.AddRef(); // completion queue requires gRPC environment being initialized. + BenchmarkUtil.RunBenchmark( 10, 10, () => @@ -66,6 +68,8 @@ namespace Grpc.Core.Tests CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); cq.Dispose(); }); + + GrpcEnvironment.Release(); } /// <summary> diff --git a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs index 5516cd3377..6815839992 100644 --- a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs +++ b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs @@ -67,10 +67,10 @@ namespace Grpc.Core.Tests channel.ShutdownAsync().Wait(); server.ShutdownAsync().Wait(); } - - // Test attribute commented out to prevent running as part of the default test suite. - //[Test] - //[Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void UnaryCallPerformance() { var profiler = new BasicProfiler(); diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs new file mode 100644 index 0000000000..343ab1e85a --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs @@ -0,0 +1,125 @@ +#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.Reflection; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class SanityTest + { + /// <summary> + /// Because we depend on a native library, sometimes when things go wrong, the + /// entire NUnit test process crashes. To be able to track down problems better, + /// the NUnit tests are run by run_tests.py script in a separate process per test class. + /// The list of tests to run is stored in src/csharp/tests.json. + /// This test checks that the tests.json file is up to date by discovering all the + /// existing NUnit tests in all test assemblies and comparing to contents of tests.json. + /// </summary> + [Test] + public void TestsJsonUpToDate() + { + var testClasses = DiscoverAllTestClasses(); + string testsJson = GetTestsJson(); + + // we don't have a JSON parser at hand, but check that the test class + // name is contained in the file instead. + foreach (var className in testClasses) { + Assert.IsTrue(testsJson.Contains(className), + string.Format("Test class \"{0}\" is missing in C# tests.json file", className)); + } + } + + /// <summary> + /// Gets list of all test classes obtained by inspecting all the test assemblies. + /// </summary> + private List<string> DiscoverAllTestClasses() + { + var assemblies = GetTestAssemblies(); + + var testClasses = new List<string>(); + foreach (var assembly in assemblies) + { + foreach (var t in assembly.GetTypes()) + { + foreach (var m in t.GetMethods()) + { + var attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true); + if (attributes.Length > 0) + { + testClasses.Add(t.FullName); + break; + } + + } + } + } + testClasses.Sort(); + return testClasses; + } + + private string GetTestsJson() + { + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testsJsonFile = Path.Combine(assemblyDir, "..", "..", "..", "tests.json"); + + return File.ReadAllText(testsJsonFile); + } + + private List<Assembly> GetTestAssemblies() + { + var result = new List<Assembly>(); + var executingAssembly = Assembly.GetExecutingAssembly(); + + result.Add(executingAssembly); + + var otherAssemblies = new[] { + "Grpc.Examples.Tests", + "Grpc.HealthCheck.Tests", + "Grpc.IntegrationTesting" + }; + foreach (var assemblyName in otherAssemblies) + { + var location = executingAssembly.Location.Replace("Grpc.Core.Tests", assemblyName); + result.Add(Assembly.LoadFrom(location)); + } + return result; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs index a2be7ddd5e..10d666d109 100644 --- a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs +++ b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs @@ -61,17 +61,20 @@ namespace Grpc.Core.Tests } [Test] - public async Task AbandonedCall() + public async Task AbandonedCall_ServerKillAsync() { + var readyToShutdown = new TaskCompletionSource<object>(); helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => { + readyToShutdown.SetResult(null); await requestStream.ToListAsync(); }); - var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(1)))); + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + await readyToShutdown.Task; // make sure handler is running - channel.ShutdownAsync().Wait(); - server.ShutdownAsync().Wait(); + await channel.ShutdownAsync(); // channel.ShutdownAsync() works even if there's a pending call. + await server.KillAsync(); // server.ShutdownAsync() would hang waiting for the call to finish. } } } diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 0fadabe554..d120f95fdf 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -171,6 +171,8 @@ namespace Grpc.Core handle.CancelAllCalls(); await shutdownTcs.Task.ConfigureAwait(false); DisposeHandle(); + + await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false); } internal void AddCallReference(object call) diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index c48ac71630..b0d920a34c 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -41,6 +41,9 @@ <Reference Include="CommandLine"> <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath> </Reference> + <Reference Include="Moq"> + <HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath> + </Reference> <Reference Include="nunit.framework"> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> </Reference> diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 5facb87971..18168f9970 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -140,12 +140,14 @@ namespace Grpc.IntegrationTesting } [Test] + [Ignore("TODO: see #4427")] public async Task StatusCodeAndMessage() { await InteropClient.RunStatusCodeAndMessageAsync(client); } [Test] + [Ignore("TODO: see #4427")] public void UnimplementedMethod() { InteropClient.RunUnimplementedMethod(UnimplementedService.NewClient(channel)); diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs index 35230f48c1..1c8bfed1f6 100644 --- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs @@ -40,6 +40,7 @@ using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Testing; +using Moq; using NUnit.Framework; namespace Grpc.IntegrationTesting @@ -50,37 +51,37 @@ namespace Grpc.IntegrationTesting Server server; Channel channel; TestService.ITestServiceClient client; + List<ChannelOption> options; + Mock<TestService.ITestService> serviceMock; + AsyncAuthInterceptor asyncAuthInterceptor; - [TestFixtureSetUp] + [SetUp] public void Init() { - var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) }); + serviceMock = new Mock<TestService.ITestService>(); + serviceMock.Setup(m => m.UnaryCall(It.IsAny<SimpleRequest>(), It.IsAny<ServerCallContext>())) + .Returns(new Func<SimpleRequest, ServerCallContext, Task<SimpleResponse>>(UnaryCallHandler)); + server = new Server { - Services = { TestService.BindService(new TestServiceImpl()) }, - Ports = { { Host, ServerPort.PickUnused, serverCredentials } } + Services = { TestService.BindService(serviceMock.Object) }, + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } } }; server.Start(); - var options = new List<ChannelOption> + options = new List<ChannelOption> { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => + asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => { - await Task.Delay(100); // make sure the operation is asynchronous. + await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous. metadata.Add("authorization", "SECRET_TOKEN"); }); - - var clientCredentials = ChannelCredentials.Create( - new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)), - CallCredentials.FromInterceptor(asyncAuthInterceptor)); - channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); - client = TestService.NewClient(channel); } - [TestFixtureTearDown] + [TearDown] public void Cleanup() { channel.ShutdownAsync().Wait(); @@ -90,8 +91,29 @@ namespace Grpc.IntegrationTesting [Test] public void MetadataCredentials() { - var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); - Assert.AreEqual(10, response.Payload.Body.Length); + var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), + CallCredentials.FromInterceptor(asyncAuthInterceptor)); + channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options); + client = TestService.NewClient(channel); + + client.UnaryCall(new SimpleRequest {}); + } + + [Test] + public void MetadataCredentials_PerCall() + { + channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options); + client = TestService.NewClient(channel); + + var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor); + client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials)); + } + + private Task<SimpleResponse> UnaryCallHandler(SimpleRequest request, ServerCallContext context) + { + var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value; + Assert.AreEqual("SECRET_TOKEN", authToken); + return Task.FromResult(new SimpleResponse()); } } } diff --git a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs index 2b51526c88..3dd91b7948 100644 --- a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs @@ -75,9 +75,10 @@ namespace Grpc.IntegrationTesting serverRunner.StopAsync().Wait(); } - // Test attribute commented out to prevent running as part of the default test suite. - //[Test] - //[Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public async Task ClientServerRunner() { var config = new ClientConfig diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index bdb3dadf44..5c59af1b7d 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -11,6 +11,7 @@ <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="Moq" version="4.2.1510.2205" targetFramework="net45" /> <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/tests.json b/src/csharp/tests.json new file mode 100644 index 0000000000..4aa93668ad --- /dev/null +++ b/src/csharp/tests.json @@ -0,0 +1,45 @@ +{ + "assemblies": [ + "Grpc.Core.Tests", + "Grpc.Examples.Tests", + "Grpc.HealthCheck.Tests", + "Grpc.IntegrationTesting" + ], + "tests": [ + "Grpc.Core.Internal.Tests.AsyncCallTest", + "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest", + "Grpc.Core.Internal.Tests.CompletionQueueEventTest", + "Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest", + "Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest", + "Grpc.Core.Internal.Tests.TimespecTest", + "Grpc.Core.Tests.CallCredentialsTest", + "Grpc.Core.Tests.CallOptionsTest", + "Grpc.Core.Tests.ChannelCredentialsTest", + "Grpc.Core.Tests.ChannelOptionsTest", + "Grpc.Core.Tests.ChannelTest", + "Grpc.Core.Tests.ClientServerTest", + "Grpc.Core.Tests.CompressionTest", + "Grpc.Core.Tests.ContextPropagationTest", + "Grpc.Core.Tests.GrpcEnvironmentTest", + "Grpc.Core.Tests.MarshallingErrorsTest", + "Grpc.Core.Tests.MetadataTest", + "Grpc.Core.Tests.NUnitVersionTest", + "Grpc.Core.Tests.PerformanceTest", + "Grpc.Core.Tests.PInvokeTest", + "Grpc.Core.Tests.ResponseHeadersTest", + "Grpc.Core.Tests.SanityTest", + "Grpc.Core.Tests.ServerTest", + "Grpc.Core.Tests.ShutdownTest", + "Grpc.Core.Tests.TimeoutsTest", + "Grpc.Core.Tests.UserAgentStringTest", + "Math.Tests.MathClientServerTest", + "Grpc.HealthCheck.Tests.HealthClientServerTest", + "Grpc.HealthCheck.Tests.HealthServiceImplTest", + "Grpc.IntegrationTesting.HeaderInterceptorTest", + "Grpc.IntegrationTesting.HistogramTest", + "Grpc.IntegrationTesting.InteropClientServerTest", + "Grpc.IntegrationTesting.MetadataCredentialsTest", + "Grpc.IntegrationTesting.RunnerClientServerTest", + "Grpc.IntegrationTesting.SslCredentialsTest" + ] +}
\ No newline at end of file diff --git a/src/node/interop/async_delay_queue.js b/src/node/interop/async_delay_queue.js new file mode 100644 index 0000000000..2bd3ca4da3 --- /dev/null +++ b/src/node/interop/async_delay_queue.js @@ -0,0 +1,79 @@ +/* + * + * 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. + * + */ + +'use strict'; + +var _ = require('lodash'); + +/** + * This class represents a queue of callbacks that must happen sequentially, each + * with a specific delay after the previous event. + */ +function AsyncDelayQueue() { + this.queue = []; + + this.callback_pending = false; +} + +/** + * Run the next callback after its corresponding delay, if there are any + * remaining. + */ +AsyncDelayQueue.prototype.runNext = function() { + var next = this.queue.shift(); + var continueCallback = _.bind(this.runNext, this); + if (next) { + this.callback_pending = true; + setTimeout(function() { + next.callback(continueCallback); + }, next.delay); + } else { + this.callback_pending = false; + } +}; + +/** + * Add a callback to be called with a specific delay after now or after the + * current last item in the queue or current pending callback, whichever is + * latest. + * @param {function(function())} callback The callback + * @param {Number} The delay to apply, in milliseconds + */ +AsyncDelayQueue.prototype.add = function(callback, delay) { + this.queue.push({callback: callback, delay: delay}); + if (!this.callback_pending) { + this.runNext(); + } +}; + +module.exports = AsyncDelayQueue; diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 5321005c86..9526b5d183 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -36,6 +36,7 @@ var fs = require('fs'); var path = require('path'); var _ = require('lodash'); +var AsyncDelayQueue = require('./async_delay_queue'); var grpc = require('..'); var testProto = grpc.load({ root: __dirname + '/../../..', @@ -155,6 +156,7 @@ function handleStreamingInput(call, callback) { */ function handleStreamingOutput(call) { echoHeader(call); + var delay_queue = new AsyncDelayQueue(); var req = call.request; if (req.response_status) { var status = req.response_status; @@ -163,9 +165,15 @@ function handleStreamingOutput(call) { return; } _.each(req.response_parameters, function(resp_param) { - call.write({payload: getPayload(req.response_type, resp_param.size)}); + delay_queue.add(function(next) { + call.write({payload: getPayload(req.response_type, resp_param.size)}); + next(); + }, resp_param.interval_us); + }); + delay_queue.add(function(next) { + call.end(getEchoTrailer(call)); + next(); }); - call.end(getEchoTrailer(call)); } /** @@ -175,6 +183,7 @@ function handleStreamingOutput(call) { */ function handleFullDuplex(call) { echoHeader(call); + var delay_queue = new AsyncDelayQueue(); call.on('data', function(value) { if (value.response_status) { var status = value.response_status; @@ -183,11 +192,17 @@ function handleFullDuplex(call) { return; } _.each(value.response_parameters, function(resp_param) { - call.write({payload: getPayload(value.response_type, resp_param.size)}); + delay_queue.add(function(next) { + call.write({payload: getPayload(value.response_type, resp_param.size)}); + next(); + }, resp_param.interval_us); }); }); call.on('end', function() { - call.end(getEchoTrailer(call)); + delay_queue.add(function(next) { + call.end(getEchoTrailer(call)); + next(); + }); }); } diff --git a/src/php/ext/grpc/package.xml b/src/php/ext/grpc/package.xml index a2e3e2826a..79a0a79d3a 100644 --- a/src/php/ext/grpc/package.xml +++ b/src/php/ext/grpc/package.xml @@ -10,11 +10,11 @@ <email>grpc-packages@google.com</email> <active>yes</active> </lead> - <date>2015-10-21</date> - <time>17:04:32</time> + <date>2015-12-15</date> + <time>13:49:29</time> <version> - <release>0.6.1</release> - <api>0.6.0</api> + <release>0.7.0</release> + <api>0.7.0</api> </version> <stability> <release>beta</release> @@ -22,19 +22,21 @@ </stability> <license>BSD</license> <notes> -- fixed undefined constant fatal error when run with apache/nginx #2275 +- Breaking change to Credentials class (removed) #3765 +- Replaced by ChannelCredentials and CallCredentials class #3765 +- New plugin based metadata auth API #4394 </notes> <contents> <dir baseinstalldir="/" name="/"> <file baseinstalldir="/" md5sum="6f19828fb869b7b8a590cbb76b4f996d" name="byte_buffer.c" role="src" /> <file baseinstalldir="/" md5sum="c8de0f819499c48adfc8d7f472c0196b" name="byte_buffer.h" role="src" /> - <file baseinstalldir="/" md5sum="d64c9005993de02abac55664b0b9e0b2" name="call.c" role="src" /> - <file baseinstalldir="/" md5sum="26acbf04c30162c2d2aca4688bb2aec8" name="call.h" role="src" /> - <file baseinstalldir="/" md5sum="6fa13d260dfde216f795225644f04e7a" name="call_credentials.c" role="src" /> - <file baseinstalldir="/" md5sum="e45269975f9a30fd349a90daf6b31aa2" name="call_credentials.h" role="src" /> - <file baseinstalldir="/" md5sum="0779db3b196c98081b2260ceec22cd4d" name="channel.c" role="src" /> + <file baseinstalldir="/" md5sum="ee7eb7757f9e6f0e36f8f616b6bd0af5" name="call.c" role="src" /> + <file baseinstalldir="/" md5sum="44c56bd9912d2538cbd6059e3e0452b6" name="call.h" role="src" /> + <file baseinstalldir="/" md5sum="ff90f6c03ed44b5f4170bf3259a6704e" name="call_credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="3c3860e1d84f43cb6b2fbaa8d2ae1ab7" name="call_credentials.h" role="src" /> + <file baseinstalldir="/" md5sum="00b44de389fbafa68afe8173045940af" name="channel.c" role="src" /> <file baseinstalldir="/" md5sum="ed4b00c0cf3702b115d0cfa87450dc09" name="channel.h" role="src" /> - <file baseinstalldir="/" md5sum="1b40f50fa6184ad7d24a961ac76151ff" name="channel_credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="d6e32503492b22bca1baf119d6c5e373" name="channel_credentials.c" role="src" /> <file baseinstalldir="/" md5sum="a86250e03f610ce6c2c7595a84e08821" name="channel_credentials.h" role="src" /> <file baseinstalldir="/" md5sum="55ab7a42f9dd9bfc7e28a61cfc5fca63" name="completion_queue.c" role="src" /> <file baseinstalldir="/" md5sum="f10b5bb232d74a6878e829e2e76cdaa2" name="completion_queue.h" role="src" /> @@ -130,5 +132,22 @@ Update to wrap gRPC C Core version 0.10.0 - fixed undefined constant fatal error when run with apache/nginx #2275 </notes> </release> + <release> + <version> + <release>0.7.0</release> + <api>0.7.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2015-12-15</date> + <license>BSD</license> + <notes> +- Breaking change to Credentials class (removed) #3765 +- Replaced by ChannelCredentials and CallCredentials class #3765 +- New plugin based metadata auth API #4394 + </notes> + </release> </changelog> </package> |