Missed conflicts

This commit is contained in:
Justin Kotalik 2020-01-15 16:26:32 -08:00
parent 4dd0a6cd27
commit ca56fee980
8 changed files with 0 additions and 308 deletions

View File

@ -2,11 +2,7 @@
<Project>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<<<<<<< HEAD
<AspNetCoreBaselineVersion>3.1.1</AspNetCoreBaselineVersion>
=======
<AspNetCoreBaselineVersion>2.1.15</AspNetCoreBaselineVersion>
>>>>>>> release/2.1
</PropertyGroup>
<!-- Package: AspNetCoreRuntime.3.0.x64-->
<PropertyGroup Condition=" '$(PackageId)' == 'AspNetCoreRuntime.3.0.x64' ">

View File

@ -33,10 +33,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
{
private static long _tenSeconds = TimeSpan.FromSeconds(10).Ticks;
<<<<<<< HEAD
private readonly object _stateLock = new object();
=======
>>>>>>> release/2.1
private readonly object _itemsLock = new object();
private readonly object _heartbeatLock = new object();
private List<(Action<object> handler, object state)> _heartbeatHandlers;
@ -49,10 +46,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
private bool _activeSend;
private long _startedSendTime;
private readonly object _sendingLock = new object();
<<<<<<< HEAD
=======
>>>>>>> release/2.1
internal CancellationToken SendingToken { get; private set; }
// This tcs exists so that multiple calls to DisposeAsync all wait asynchronously
@ -299,7 +292,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Wait for either to finish
var result = await Task.WhenAny(applicationTask, transportTask);
<<<<<<< HEAD
// If the application is complete, complete the transport pipe (it's the pipe to the transport)
if (result == applicationTask)
{
@ -341,25 +333,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
Transport?.Output.Complete();
Transport?.Input.Complete();
=======
// Normally it isn't safe to try and acquire this lock because the Send can hold onto it for a long time if there is backpressure
// It is safe to wait for this lock now because the Send will be in one of 4 states
// 1. In the middle of a write which is in the middle of being canceled by the CancelPendingFlush above, when it throws
// an OperationCanceledException it will complete the PipeWriter which will make any other Send waiting on the lock
// throw an InvalidOperationException if they call Write
// 2. About to write and see that there is a pending cancel from the CancelPendingFlush, go to 1 to see what happens
// 3. Enters the Send and sees the Dispose state from DisposeAndRemoveAsync and releases the lock
// 4. No Send in progress
await WriteLock.WaitAsync();
try
{
// Complete the applications read loop
Application?.Output.Complete(transportTask.Exception?.InnerException);
}
finally
{
WriteLock.Release();
>>>>>>> release/2.1
}
Application?.Input.CancelPendingRead();
@ -401,7 +374,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
}
}
<<<<<<< HEAD
internal bool TryActivatePersistentConnection(
ConnectionDelegate connectionDelegate,
IHttpTransport transport,
@ -582,8 +554,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
await connectionDelegate(this);
}
=======
>>>>>>> release/2.1
internal void StartSendCancellation()
{
lock (_sendingLock)
@ -593,18 +563,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
_sendCts = new CancellationTokenSource();
SendingToken = _sendCts.Token;
}
<<<<<<< HEAD
=======
>>>>>>> release/2.1
_startedSendTime = DateTime.UtcNow.Ticks;
_activeSend = true;
}
}
<<<<<<< HEAD
=======
>>>>>>> release/2.1
internal void TryCancelSend(long currentTicks)
{
lock (_sendingLock)
@ -618,10 +580,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
}
}
}
<<<<<<< HEAD
=======
>>>>>>> release/2.1
internal void StopSendCancellation()
{
lock (_sendingLock)

View File

@ -142,11 +142,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
connection.SupportedFormats = TransferFormat.Text;
// We only need to provide the Input channel since writing to the application is handled through /send.
<<<<<<< HEAD
var sse = new ServerSentEventsServerTransport(connection.Application.Input, connection.ConnectionId, connection, _loggerFactory);
=======
var sse = new ServerSentEventsTransport(connection.Application.Input, connection.ConnectionId, connection, _loggerFactory);
>>>>>>> release/2.1
await DoPersistentConnection(connectionDelegate, sse, context, connection);
}
@ -195,83 +191,9 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
if (!await connection.CancelPreviousPoll(context))
{
<<<<<<< HEAD
// Connection closed. It's already set the response status code.
return;
}
=======
if (connection.Status == HttpConnectionStatus.Disposed)
{
Log.ConnectionDisposed(_logger, connection.ConnectionId);
// The connection was disposed
context.Response.StatusCode = StatusCodes.Status404NotFound;
context.Response.ContentType = "text/plain";
return;
}
if (connection.Status == HttpConnectionStatus.Active)
{
var existing = connection.GetHttpContext();
Log.ConnectionAlreadyActive(_logger, connection.ConnectionId, existing.TraceIdentifier);
}
using (connection.Cancellation)
{
// Cancel the previous request
connection.Cancellation?.Cancel();
try
{
// Wait for the previous request to drain
await connection.PreviousPollTask;
}
catch (OperationCanceledException)
{
// Previous poll canceled due to connection closing, close this poll too
context.Response.ContentType = "text/plain";
context.Response.StatusCode = StatusCodes.Status204NoContent;
return;
}
connection.PreviousPollTask = currentRequestTcs.Task;
}
// Mark the connection as active
connection.Status = HttpConnectionStatus.Active;
// Raise OnConnected for new connections only since polls happen all the time
if (connection.ApplicationTask == null)
{
Log.EstablishedConnection(_logger);
connection.ApplicationTask = ExecuteApplication(connectionDelegate, connection);
context.Response.ContentType = "application/octet-stream";
// This request has no content
context.Response.ContentLength = 0;
// On the first poll, we flush the response immediately to mark the poll as "initialized" so future
// requests can be made safely
connection.TransportTask = context.Response.Body.FlushAsync();
}
else
{
Log.ResumingConnection(_logger);
// REVIEW: Performance of this isn't great as this does a bunch of per request allocations
connection.Cancellation = new CancellationTokenSource();
var timeoutSource = new CancellationTokenSource();
var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(connection.Cancellation.Token, context.RequestAborted, timeoutSource.Token);
// Dispose these tokens when the request is over
context.Response.RegisterForDispose(timeoutSource);
context.Response.RegisterForDispose(tokenSource);
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Input, _loggerFactory, connection);
>>>>>>> release/2.1
// Create a new Tcs every poll to keep track of the poll finishing, so we can properly wait on previous polls
var currentRequestTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -314,23 +236,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
connection.MarkInactive();
}
}
<<<<<<< HEAD
else if (resultTask.IsFaulted || resultTask.IsCanceled)
=======
else if (connection.TransportTask.IsFaulted || connection.TransportTask.IsCanceled)
{
// Cancel current request to release any waiting poll and let dispose aquire the lock
currentRequestTcs.TrySetCanceled();
// We should be able to safely dispose because there's no more data being written
// We don't need to wait for close here since we've already waited for both sides
await _manager.DisposeAndRemoveAsync(connection, closeGracefully: false);
// Don't poll again if we've removed the connection completely
pollAgain = false;
}
else if (context.Response.StatusCode == StatusCodes.Status204NoContent)
>>>>>>> release/2.1
{
// Cancel current request to release any waiting poll and let dispose acquire the lock
currentRequestTcs.TrySetCanceled();
@ -538,7 +444,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Other code isn't guaranteed to be able to acquire the lock before another write
// even if CancelPendingFlush is called, and the other write could hang if there is backpressure
connection.Application.Output.Complete();
<<<<<<< HEAD
return;
}
catch (IOException ex)
@ -548,8 +453,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "text/plain";
=======
>>>>>>> release/2.1
return;
}

View File

@ -32,10 +32,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
private readonly ILogger<HttpConnectionManager> _logger;
private readonly ILogger<HttpConnectionContext> _connectionLogger;
private readonly bool _useSendTimeout = true;
<<<<<<< HEAD
private readonly TimeSpan _disconnectTimeout;
=======
>>>>>>> release/2.1
public HttpConnectionManager(ILoggerFactory loggerFactory, IHostApplicationLifetime appLifetime)
: this(loggerFactory, appLifetime, Options.Create(new ConnectionOptions() { DisconnectTimeout = ConnectionOptionsSetup.DefaultDisconectTimeout }))
@ -56,15 +53,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Register these last as the callbacks could run immediately
appLifetime.ApplicationStarted.Register(() => Start());
appLifetime.ApplicationStopping.Register(() => CloseConnections());
<<<<<<< HEAD
=======
_nextHeartbeat = new TimerAwaitable(_heartbeatTickRate, _heartbeatTickRate);
if (AppContext.TryGetSwitch("Microsoft.AspNetCore.Http.Connections.DoNotUseSendTimeout", out var timeoutDisabled))
{
_useSendTimeout = !timeoutDisabled;
}
>>>>>>> release/2.1
}
public void Start()
@ -173,31 +161,10 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Capture the connection state
var lastSeenUtc = connection.LastSeenUtcIfInactive;
<<<<<<< HEAD
var utcNow = DateTimeOffset.UtcNow;
// Once the decision has been made to dispose we don't check the status again
// But don't clean up connections while the debugger is attached.
if (!Debugger.IsAttached && lastSeenUtc.HasValue && (utcNow - lastSeenUtc.Value).TotalSeconds > _disconnectTimeout.TotalSeconds)
=======
await connection.StateLock.WaitAsync();
try
{
// Capture the connection state
status = connection.Status;
lastSeenUtc = connection.LastSeenUtc;
}
finally
{
connection.StateLock.Release();
}
var utcNow = DateTimeOffset.UtcNow;
// Once the decision has been made to dispose we don't check the status again
// But don't clean up connections while the debugger is attached.
if (!Debugger.IsAttached && status == HttpConnectionStatus.Inactive && (utcNow - lastSeenUtc).TotalSeconds > 5)
>>>>>>> release/2.1
{
Log.ConnectionTimedOut(_logger, connection.ConnectionId);
HttpConnectionsEventSource.Log.ConnectionTimedOut(connection.ConnectionId);

View File

@ -1951,110 +1951,6 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
}
}
[Fact]
public async Task DeleteEndpointTerminatesLongPollingWithHangingApplication()
{
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{
var manager = CreateConnectionManager(loggerFactory);
var pipeOptions = new PipeOptions(pauseWriterThreshold: 2, resumeWriterThreshold: 1);
var connection = manager.CreateConnection(pipeOptions, pipeOptions);
connection.TransportType = HttpTransportType.LongPolling;
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
var context = MakeRequest("/foo", connection);
var services = new ServiceCollection();
services.AddSingleton<NeverEndingConnectionHandler>();
var builder = new ConnectionBuilder(services.BuildServiceProvider());
builder.UseConnectionHandler<NeverEndingConnectionHandler>();
var app = builder.Build();
var options = new HttpConnectionDispatcherOptions();
var pollTask = dispatcher.ExecuteAsync(context, options, app);
Assert.True(pollTask.IsCompleted);
// Now send the second poll
pollTask = dispatcher.ExecuteAsync(context, options, app);
// Issue the delete request and make sure the poll completes
var deleteContext = new DefaultHttpContext();
deleteContext.Request.Path = "/foo";
deleteContext.Request.QueryString = new QueryString($"?id={connection.ConnectionId}");
deleteContext.Request.Method = "DELETE";
Assert.False(pollTask.IsCompleted);
await dispatcher.ExecuteAsync(deleteContext, options, app).OrTimeout();
await pollTask.OrTimeout();
// Verify that transport shuts down
await connection.TransportTask.OrTimeout();
// Verify the response from the DELETE request
Assert.Equal(StatusCodes.Status202Accepted, deleteContext.Response.StatusCode);
Assert.Equal("text/plain", deleteContext.Response.ContentType);
Assert.Equal(HttpConnectionStatus.Disposed, connection.Status);
// Verify the connection not removed because application is hanging
Assert.True(manager.TryGetConnection(connection.ConnectionId, out _));
}
}
[Fact]
public async Task PollCanReceiveFinalMessageAfterAppCompletes()
{
using (StartVerifiableLog(out var loggerFactory, LogLevel.Debug))
{
var transportType = HttpTransportType.LongPolling;
var manager = CreateConnectionManager(loggerFactory);
var dispatcher = new HttpConnectionDispatcher(manager, loggerFactory);
var connection = manager.CreateConnection();
connection.TransportType = transportType;
var waitForMessageTcs1 = new TaskCompletionSource<object>();
var messageTcs1 = new TaskCompletionSource<object>();
var waitForMessageTcs2 = new TaskCompletionSource<object>();
var messageTcs2 = new TaskCompletionSource<object>();
ConnectionDelegate connectionDelegate = async c =>
{
await waitForMessageTcs1.Task.OrTimeout();
await c.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Message1")).OrTimeout();
messageTcs1.TrySetResult(null);
await waitForMessageTcs2.Task.OrTimeout();
await c.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Message2")).OrTimeout();
messageTcs2.TrySetResult(null);
};
{
var options = new HttpConnectionDispatcherOptions();
var context = MakeRequest("/foo", connection);
await dispatcher.ExecuteAsync(context, options, connectionDelegate).OrTimeout();
// second poll should have data
waitForMessageTcs1.SetResult(null);
await messageTcs1.Task.OrTimeout();
var ms = new MemoryStream();
context.Response.Body = ms;
// Now send the second poll
await dispatcher.ExecuteAsync(context, options, connectionDelegate).OrTimeout();
Assert.Equal("Message1", Encoding.UTF8.GetString(ms.ToArray()));
waitForMessageTcs2.SetResult(null);
await messageTcs2.Task.OrTimeout();
context = MakeRequest("/foo", connection);
ms.Seek(0, SeekOrigin.Begin);
context.Response.Body = ms;
// This is the third poll which gets the final message after the app is complete
await dispatcher.ExecuteAsync(context, options, connectionDelegate).OrTimeout();
Assert.Equal("Message2", Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
[Fact]
public async Task NegotiateDoesNotReturnWebSocketsWhenNotAvailable()
{

View File

@ -32,32 +32,6 @@ namespace Microsoft.AspNetCore.SignalR.Tests
var results = _sink.GetLogs().Where(w => w.Write.LogLevel >= LogLevel.Error).ToList();
#if NETCOREAPP2_1 || NETCOREAPP2_2 || NET461
// -- Remove this code after 2.2 --
// This section of code is resolving test flakiness caused by a race in LongPolling
// The race has been resolved in version 3.0
// The below code tries to find is a DELETE request has arrived from the client before removing error logs associated with the race
// We do this because we don't want to hide any actual issues, but we feel confident that looking for DELETE first wont hide any real problems
var foundDelete = false;
var allLogs = _sink.GetLogs();
foreach (var log in allLogs)
{
if (foundDelete == false && log.Write.Message.Contains("Request starting") && log.Write.Message.Contains("DELETE"))
{
foundDelete = true;
}
if (foundDelete)
{
if ((log.Write.EventId.Name == "LongPollingTerminated" || log.Write.EventId.Name == "ApplicationError" || log.Write.EventId.Name == "FailedDispose")
&& log.Write.Exception?.Message.Contains("Reading is not allowed after reader was completed.") == true)
{
results.Remove(log);
}
}
}
#endif
if (_expectedErrorsFilter != null)
{
results = results.Where(w => !_expectedErrorsFilter(w.Write)).ToList();