Skip to content

Commit 80cb82e

Browse files
authored
Flush request body stream at start of client and duplex streamin… (#418)
1 parent 6e9cead commit 80cb82e

File tree

6 files changed

+71
-26
lines changed

6 files changed

+71
-26
lines changed

build_and_test.sh

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@ source activate.sh
1919

2020
echo "Building solution"
2121

22-
dotnet build
22+
dotnet build -c Release
2323

2424
echo "Testing solution"
2525

2626
test_projects=( $( ls test/**/*Tests.csproj ) )
2727

2828
for test_project in "${test_projects[@]}"
2929
do
30-
# Capturing test diagnostic logs because of hanging build
31-
# https://github.com/grpc/grpc-dotnet/pull/363
32-
dotnet test $test_project --no-build --diag:artifacts/${test_project##*/}.log.txt
30+
# "dotnet test" is hanging when it writes to console for an unknown reason
31+
# Tracking issue at https://github.com/microsoft/vstest/issues/2080
32+
# Write test output to a text file and then write the text file to console as a workaround
33+
{
34+
dotnet test $test_project -c Release -v n --no-build &> ${test_project##*/}.log.txt &&
35+
echo "Success" &&
36+
cat ${test_project##*/}.log.txt
37+
} || {
38+
echo "Failure" &&
39+
cat ${test_project##*/}.log.txt &&
40+
exit 1
41+
}
3342
done
3443

3544
echo "Finished"

src/Grpc.Net.Client/Internal/GrpcCall.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,17 @@ private HttpContentClientStreamWriter<TRequest, TResponse> CreateWriter(HttpRequ
609609
_writeCompleteTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
610610

611611
message.Content = new PushStreamContent(
612-
(stream) =>
612+
async stream =>
613613
{
614+
// Immediately flush request stream to send headers
615+
// https://github.com/dotnet/corefx/issues/39586#issuecomment-516210081
616+
await stream.FlushAsync().ConfigureAwait(false);
617+
618+
// Pass request stream to writer
614619
_writeStreamTcs.TrySetResult(stream);
615-
return _writeCompleteTcs.Task;
620+
621+
// Wait for the writer to report it is complete
622+
await _writeCompleteTcs.Task.ConfigureAwait(false);
616623
},
617624
GrpcProtocolConstants.GrpcContentTypeHeaderValue);
618625

test/FunctionalTests/Client/EventSourceTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public async Task UnaryMethod_SuccessfulCall_PollingCountersUpdatedCorrectly()
5252

5353
async Task<HelloReply> UnarySuccess(HelloRequest request, ServerCallContext context)
5454
{
55-
await tcs.Task;
55+
await tcs.Task.DefaultTimeout();
5656

5757
return new HelloReply();
5858
}
@@ -119,7 +119,7 @@ public async Task UnaryMethod_ErrorCall_PollingCountersUpdatedCorrectly()
119119

120120
async Task<HelloReply> UnaryError(HelloRequest request, ServerCallContext context)
121121
{
122-
await tcs.Task;
122+
await tcs.Task.DefaultTimeout();
123123

124124
throw new Exception("Error!");
125125
}
@@ -233,13 +233,13 @@ public async Task DuplexStreamingMethod_Success_PollingCountersUpdatedCorrectly(
233233
{
234234
async Task DuplexStreamingMethod(IAsyncStreamReader<HelloRequest> requestStream, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
235235
{
236-
while (await requestStream.MoveNext())
236+
while (await requestStream.MoveNext().DefaultTimeout())
237237
{
238238

239239
}
240240

241-
await responseStream.WriteAsync(new HelloReply { Message = "Message 1" });
242-
await responseStream.WriteAsync(new HelloReply { Message = "Message 2" });
241+
await responseStream.WriteAsync(new HelloReply { Message = "Message 1" }).DefaultTimeout();
242+
await responseStream.WriteAsync(new HelloReply { Message = "Message 2" }).DefaultTimeout();
243243
}
244244

245245
// Arrange
@@ -267,11 +267,11 @@ async Task DuplexStreamingMethod(IAsyncStreamReader<HelloRequest> requestStream,
267267
["messages-received"] = 0,
268268
}).DefaultTimeout();
269269

270-
await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 1" });
271-
await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 2" });
272-
await call.RequestStream.CompleteAsync();
270+
await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 1" }).DefaultTimeout();
271+
await call.RequestStream.WriteAsync(new HelloRequest { Name = "Name 2" }).DefaultTimeout();
272+
await call.RequestStream.CompleteAsync().DefaultTimeout();
273273

274-
while (await call.ResponseStream.MoveNext())
274+
while (await call.ResponseStream.MoveNext().DefaultTimeout())
275275
{
276276
}
277277

test/FunctionalTests/Client/StreamingTests.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,25 @@ public async Task DuplexStream_SendLargeFileBatchedAndRecieveLargeFileBatched_Su
5555
const int BatchSize = 1024 * 64; // 64 KB
5656

5757
var writeCount = Math.Min(data.Length - sent, BatchSize);
58-
var finalWrite = sent + writeCount == data.Length;
58+
5959
await call.RequestStream.WriteAsync(new DataMessage
6060
{
61-
Data = ByteString.CopyFrom(data, sent, writeCount),
62-
FinalSegment = finalWrite
61+
Data = ByteString.CopyFrom(data, sent, writeCount)
6362
}).DefaultTimeout();
6463

6564
sent += writeCount;
65+
66+
Logger.LogInformation($"Sent {sent} bytes");
6667
}
6768

69+
await call.RequestStream.CompleteAsync().DefaultTimeout();
70+
6871
var ms = new MemoryStream();
6972
while (await call.ResponseStream.MoveNext(CancellationToken.None).DefaultTimeout())
7073
{
7174
ms.Write(call.ResponseStream.Current.Data.Span);
75+
76+
Logger.LogInformation($"Received {ms.Length} bytes");
7277
}
7378

7479
// Assert
@@ -121,6 +126,7 @@ public async Task ClientStream_SendLargeFileBatchedAndRecieveLargeFileBatched_Su
121126
}
122127

123128
[Test]
129+
[Ignore("Waiting on fix from https://github.com/dotnet/corefx/issues/39586")]
124130
public async Task DuplexStream_SendToUnimplementedMethod_ThrowError()
125131
{
126132
SetExpectedErrorsFilter(writeContext =>
@@ -157,5 +163,25 @@ await call.RequestStream.WriteAsync(new UnimplementeDataMessage
157163
// Assert
158164
Assert.AreEqual(StatusCode.Cancelled, ex.StatusCode);
159165
}
166+
167+
[Test]
168+
[Ignore("Waiting on fix from https://github.com/dotnet/corefx/issues/39586")]
169+
public async Task DuplexStream_SendToUnimplementedMethodAfterResponseReceived_Hang()
170+
{
171+
// Arrange
172+
var client = GrpcClient.Create<UnimplementedService.UnimplementedServiceClient>(Fixture.Client, LoggerFactory);
173+
174+
for (int i = 0; i < 1000; i++)
175+
{
176+
Logger.LogInformation($"ITERATION {i}");
177+
178+
// Act
179+
var call = client.DuplexData();
180+
181+
// Response will only be headers so the call is "done" on the server side
182+
await call.ResponseHeadersAsync.DefaultTimeout();
183+
await call.RequestStream.CompleteAsync();
184+
}
185+
}
160186
}
161187
}

testassets/FunctionalTestsWebsite/Services/StreamService.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@
2222
using System.Threading.Tasks;
2323
using Google.Protobuf;
2424
using Grpc.Core;
25+
using Microsoft.Extensions.Logging;
2526
using Streaming;
2627

2728
namespace FunctionalTestsWebsite.Services
2829
{
2930
public class StreamService : Streaming.StreamService.StreamServiceBase
3031
{
32+
private readonly ILogger _logger;
33+
34+
public StreamService(ILoggerFactory loggerFactory)
35+
{
36+
_logger = loggerFactory.CreateLogger<StreamService>();
37+
}
38+
3139
public override async Task DuplexData(
3240
IAsyncStreamReader<DataMessage> requestStream,
3341
IServerStreamWriter<DataMessage> responseStream,
@@ -38,10 +46,7 @@ public override async Task DuplexData(
3846
while (await requestStream.MoveNext(CancellationToken.None))
3947
{
4048
ms.Write(requestStream.Current.Data.Span);
41-
if (requestStream.Current.FinalSegment)
42-
{
43-
break;
44-
}
49+
_logger.LogInformation($"Received {ms.Length} bytes");
4550
}
4651

4752
// Write back to client in batches
@@ -52,14 +57,13 @@ public override async Task DuplexData(
5257
const int BatchSize = 1024 * 64; // 64 KB
5358

5459
var writeCount = Math.Min(data.Length - sent, BatchSize);
55-
var finalWrite = sent + writeCount == data.Length;
5660
await responseStream.WriteAsync(new DataMessage
5761
{
58-
Data = ByteString.CopyFrom(data, sent, writeCount),
59-
FinalSegment = finalWrite
62+
Data = ByteString.CopyFrom(data, sent, writeCount)
6063
});
6164

6265
sent += writeCount;
66+
_logger.LogInformation($"Sent {sent} bytes");
6367
}
6468
}
6569

testassets/Proto/streaming.proto

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ service StreamService {
2424
message DataMessage {
2525
bytes data = 1;
2626
int32 serverDelayMilliseconds = 2;
27-
bool finalSegment = 3;
2827
}
2928

3029
message DataComplete {

0 commit comments

Comments
 (0)