Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 3e6303b

Browse files
author
Cesar Blum Silveira
authored
Fix flakiness in connection reset logging tests.
1 parent f090b7a commit 3e6303b

File tree

1 file changed

+59
-24
lines changed

1 file changed

+59
-24
lines changed

test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
3030
public class RequestTests
3131
{
3232
private const int _connectionStartedEventId = 1;
33+
private const int _connectionKeepAliveEventId = 9;
3334
private const int _connectionResetEventId = 19;
3435
private const int _semaphoreWaitTimeout = 2500;
3536

@@ -241,7 +242,8 @@ public void CanUpgradeRequestWithConnectionKeepAliveUpgradeHeader()
241242
[Fact]
242243
public async Task ConnectionResetPriorToRequestIsLoggedAsDebug()
243244
{
244-
var connectionStartedTcs = new TaskCompletionSource<object>();
245+
var connectionStarted = new SemaphoreSlim(0);
246+
var connectionReset = new SemaphoreSlim(0);
245247

246248
var mockLogger = new Mock<ILogger>();
247249
mockLogger
@@ -251,7 +253,13 @@ public async Task ConnectionResetPriorToRequestIsLoggedAsDebug()
251253
.Setup(logger => logger.Log(LogLevel.Debug, _connectionStartedEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()))
252254
.Callback(() =>
253255
{
254-
connectionStartedTcs.SetResult(null);
256+
connectionStarted.Release();
257+
});
258+
mockLogger
259+
.Setup(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()))
260+
.Callback(() =>
261+
{
262+
connectionReset.Release();
255263
});
256264

257265
var mockLoggerFactory = new Mock<ILoggerFactory>();
@@ -266,10 +274,7 @@ public async Task ConnectionResetPriorToRequestIsLoggedAsDebug()
266274
.UseLoggerFactory(mockLoggerFactory.Object)
267275
.UseKestrel()
268276
.UseUrls($"http://127.0.0.1:0")
269-
.Configure(app => app.Run(context =>
270-
{
271-
return TaskCache.CompletedTask;
272-
}));
277+
.Configure(app => app.Run(context => TaskCache.CompletedTask));
273278

274279
using (var host = builder.Build())
275280
{
@@ -280,23 +285,42 @@ public async Task ConnectionResetPriorToRequestIsLoggedAsDebug()
280285
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
281286

282287
// Wait until connection is established
283-
await connectionStartedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
288+
await connectionStarted.WaitAsync(TimeSpan.FromSeconds(10));
284289

285290
// Force a reset
286291
socket.LingerState = new LingerOption(true, 0);
287292
}
288-
}
289293

290-
mockLogger.Verify(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()));
294+
// If the reset is correctly logged as Debug, the wait below should complete shortly.
295+
// This check MUST come before disposing the server, otherwise there's a race where the RST
296+
// is still in flight when the connection is aborted, leading to the reset never being received
297+
// and therefore not logged.
298+
await connectionReset.WaitAsync(TimeSpan.FromSeconds(10));
299+
}
291300
}
292301

293302
[Fact]
294303
public async Task ConnectionResetBetweenRequestsIsLoggedAsDebug()
295304
{
305+
var requestDone = new SemaphoreSlim(0);
306+
var connectionReset = new SemaphoreSlim(0);
307+
296308
var mockLogger = new Mock<ILogger>();
297309
mockLogger
298310
.Setup(logger => logger.IsEnabled(It.IsAny<LogLevel>()))
299311
.Returns(true);
312+
mockLogger
313+
.Setup(logger => logger.Log(LogLevel.Debug, _connectionKeepAliveEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()))
314+
.Callback(() =>
315+
{
316+
requestDone.Release();
317+
});
318+
mockLogger
319+
.Setup(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()))
320+
.Callback(() =>
321+
{
322+
connectionReset.Release();
323+
});
300324

301325
var mockLoggerFactory = new Mock<ILoggerFactory>();
302326
mockLoggerFactory
@@ -306,17 +330,12 @@ public async Task ConnectionResetBetweenRequestsIsLoggedAsDebug()
306330
.Setup(factory => factory.CreateLogger(It.IsNotIn(new[] { "Microsoft.AspNetCore.Server.Kestrel" })))
307331
.Returns(Mock.Of<ILogger>());
308332

309-
var connectionStartedTcs = new TaskCompletionSource<object>();
310333

311334
var builder = new WebHostBuilder()
312335
.UseLoggerFactory(mockLoggerFactory.Object)
313336
.UseKestrel()
314337
.UseUrls($"http://127.0.0.1:0")
315-
.Configure(app => app.Run(context =>
316-
{
317-
connectionStartedTcs.SetResult(null);
318-
return TaskCache.CompletedTask;
319-
}));
338+
.Configure(app => app.Run(context => TaskCache.CompletedTask));
320339

321340
using (var host = builder.Build())
322341
{
@@ -327,24 +346,36 @@ public async Task ConnectionResetBetweenRequestsIsLoggedAsDebug()
327346
socket.Connect(new IPEndPoint(IPAddress.Loopback, host.GetPort()));
328347
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
329348

330-
// Wait until connection is established
331-
await connectionStartedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
349+
// Wait until request is done being processed
350+
await requestDone.WaitAsync(TimeSpan.FromSeconds(10));
332351

333352
// Force a reset
334353
socket.LingerState = new LingerOption(true, 0);
335354
}
336-
}
337355

338-
mockLogger.Verify(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()));
356+
// If the reset is correctly logged as Debug, the wait below should complete shortly.
357+
// This check MUST come before disposing the server, otherwise there's a race where the RST
358+
// is still in flight when the connection is aborted, leading to the reset never being received
359+
// and therefore not logged.
360+
await connectionReset.WaitAsync(TimeSpan.FromSeconds(10));
361+
}
339362
}
340363

341364
[Fact]
342365
public async Task ConnectionResetMidRequestIsLoggedAsDebug()
343366
{
367+
var connectionReset = new SemaphoreSlim(0);
368+
344369
var mockLogger = new Mock<ILogger>();
345370
mockLogger
346371
.Setup(logger => logger.IsEnabled(It.IsAny<LogLevel>()))
347372
.Returns(true);
373+
mockLogger
374+
.Setup(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()))
375+
.Callback(() =>
376+
{
377+
connectionReset.Release();
378+
});
348379

349380
var mockLoggerFactory = new Mock<ILoggerFactory>();
350381
mockLoggerFactory
@@ -354,15 +385,15 @@ public async Task ConnectionResetMidRequestIsLoggedAsDebug()
354385
.Setup(factory => factory.CreateLogger(It.IsNotIn(new[] { "Microsoft.AspNetCore.Server.Kestrel" })))
355386
.Returns(Mock.Of<ILogger>());
356387

357-
var connectionStartedTcs = new TaskCompletionSource<object>();
388+
var requestStarted = new SemaphoreSlim(0);
358389

359390
var builder = new WebHostBuilder()
360391
.UseLoggerFactory(mockLoggerFactory.Object)
361392
.UseKestrel()
362393
.UseUrls($"http://127.0.0.1:0")
363394
.Configure(app => app.Run(async context =>
364395
{
365-
connectionStartedTcs.SetResult(null);
396+
requestStarted.Release();
366397
await context.Request.Body.ReadAsync(new byte[1], 0, 1);
367398
}));
368399

@@ -376,14 +407,18 @@ public async Task ConnectionResetMidRequestIsLoggedAsDebug()
376407
socket.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
377408

378409
// Wait until connection is established
379-
await connectionStartedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10));
410+
await requestStarted.WaitAsync(TimeSpan.FromSeconds(10));
380411

381412
// Force a reset
382413
socket.LingerState = new LingerOption(true, 0);
383414
}
384-
}
385415

386-
mockLogger.Verify(logger => logger.Log(LogLevel.Debug, _connectionResetEventId, It.IsAny<object>(), null, It.IsAny<Func<object, Exception, string>>()));
416+
// If the reset is correctly logged as Debug, the wait below should complete shortly.
417+
// This check MUST come before disposing the server, otherwise there's a race where the RST
418+
// is still in flight when the connection is aborted, leading to the reset never being received
419+
// and therefore not logged.
420+
await connectionReset.WaitAsync(TimeSpan.FromSeconds(10));
421+
}
387422
}
388423

389424
[Fact]

0 commit comments

Comments
 (0)