handle Http2Stream add/remove better (dotnet/corefx#37505)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Wed, 8 May 2019 21:28:13 +0000 (14:28 -0700)
committerGitHub <noreply@github.com>
Wed, 8 May 2019 21:28:13 +0000 (14:28 -0700)
* handle sream add/remove better

* abort connection only if it is not already disposed

* feedback from review

Commit migrated from https://github.com/dotnet/corefx/commit/184bbc41f7e51b025e7a3c041895af8d83bc6864

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs

index 18ebd60ac4c43b1eef887c614a18dcde18ec01ad..b85233f76308700ff72422aac06cc69c4d4c3f5e 100644 (file)
@@ -260,7 +260,12 @@ namespace System.Net.Http
             }
             catch (Exception e)
             {
-                Abort(e);
+                if (NetEventSource.IsEnabled) Trace($"ProcessIncomingFramesAsync: {e.Message}");
+
+                if (!_disposed)
+                {
+                    Abort(e);
+                }
             }
         }
 
@@ -720,7 +725,7 @@ namespace System.Net.Http
             }
 
             // If the connection has been aborted, then fail now instead of trying to send more data.
-            if (IsAborted())
+            if (_disposed)
             {
                 throw new IOException(SR.net_http_request_aborted);
             }
@@ -1120,22 +1125,18 @@ namespace System.Net.Http
             _outgoingBuffer.Commit(FrameHeader.Size);
         }
 
+         /// <summary>Abort all streams and cause further processing to fail.</summary>
+         /// <param name="abortException">Exception causing Abort to be called.</param>
         private void Abort(Exception abortException)
         {
             // The connection has failed, e.g. failed IO or a connection-level frame error.
-            // Abort all streams and cause further processing to fail.
-            if (!IsAborted())
+            if (_abortException == null)
             {
                 _abortException = abortException;
             }
             AbortStreams(0, abortException);
         }
 
-        private bool IsAborted()
-        {
-            return _disposed;
-        }
-
         /// <summary>Gets whether the connection exceeded any of the connection limits.</summary>
         /// <param name="nowTicks">The current tick count.  Passed in to amortize the cost of calling Environment.TickCount.</param>
         /// <param name="connectionLifetime">How long a connection can be open to be considered reusable.</param>
@@ -1323,7 +1324,7 @@ namespace System.Net.Http
         private enum FrameFlags : byte
         {
             None = 0,
-            
+
             // Some frame types define bits differently.  Define them all here for simplicity.
 
             EndStream =     0b00000001,
@@ -1401,8 +1402,15 @@ namespace System.Net.Http
         {
             lock (SyncObject)
             {
-                if (_disposed || _nextStream == MaxStreamId)
+                if (_nextStream == MaxStreamId || _disposed)
                 {
+                    if (_abortException != null)
+                    {
+                        // Aborted because protocol error or IO.
+                        throw new ObjectDisposedException(nameof(Http2Connection));
+                    }
+
+                    // We run out of IDs or we have race condition between receiving GOAWAY and processing requests.
                     // Throw a retryable request exception. This will cause retry logic to kick in
                     // and perform another connection attempt. The user should never see this exception.
                     throw new HttpRequestException(null, null, allowRetry: true);
@@ -1427,7 +1435,10 @@ namespace System.Net.Http
             {
                 if (!_httpStreams.Remove(http2Stream.StreamId, out Http2Stream removed))
                 {
-                    Debug.Fail("_httpStreams.Remove failed");
+                    // Stream can be removed from background ProcessIncomingFramesAsync() when endOfStream is set
+                    // or when we hit various error conditions in SendAsync() call.
+                    // Skip logic below if stream was already removed.
+                    return;
                 }
 
                 _concurrentStreams.AdjustCredit(1);