From ca56fee980cba5bcbcd26f83d194af2277258ae8 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Wed, 15 Jan 2020 16:26:32 -0800 Subject: [PATCH] Missed conflicts --- eng/Baseline.Designer.props | 4 - .../ArchiveBaseline.2.1.15.txt | 1 - .../ArchiveBaseline.2.1.15.txt | 1 - .../src/Internal/HttpConnectionContext.cs | 42 ------- .../src/Internal/HttpConnectionDispatcher.cs | 97 ---------------- .../src/Internal/HttpConnectionManager.cs | 33 ------ .../test/HttpConnectionDispatcherTests.cs | 104 ------------------ .../Tests.Utils/VerifyNoErrorsScope.cs | 26 ----- 8 files changed, 308 deletions(-) delete mode 100644 src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.15.txt delete mode 100644 src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.15.txt diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index b0493f1b1a..0550859109 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,11 +2,7 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) -<<<<<<< HEAD 3.1.1 -======= - 2.1.15 ->>>>>>> release/2.1 diff --git a/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.15.txt b/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.15.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.15.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.15.txt b/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.15.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.15.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs index 1d617a3f51..b496aa370e 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs @@ -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 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) diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs index df6ce3b676..c40a80b9ce 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionDispatcher.cs @@ -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(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; } diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs index 85c9701242..b0f4b079fb 100644 --- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs +++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs @@ -32,10 +32,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal private readonly ILogger _logger; private readonly ILogger _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); diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs index 6fa5ed0a8a..33d72eddc2 100644 --- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs +++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs @@ -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(); - var builder = new ConnectionBuilder(services.BuildServiceProvider()); - builder.UseConnectionHandler(); - 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(); - var messageTcs1 = new TaskCompletionSource(); - var waitForMessageTcs2 = new TaskCompletionSource(); - var messageTcs2 = new TaskCompletionSource(); - 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() { diff --git a/src/SignalR/common/testassets/Tests.Utils/VerifyNoErrorsScope.cs b/src/SignalR/common/testassets/Tests.Utils/VerifyNoErrorsScope.cs index 0e3c8f47a4..7aa1c4544f 100644 --- a/src/SignalR/common/testassets/Tests.Utils/VerifyNoErrorsScope.cs +++ b/src/SignalR/common/testassets/Tests.Utils/VerifyNoErrorsScope.cs @@ -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();