fix race in request body completion
authorGeoff Kizer <geoffrek>
Tue, 23 Jul 2019 20:18:56 +0000 (13:18 -0700)
committerGeoff Kizer <geoffrek>
Tue, 23 Jul 2019 20:19:35 +0000 (13:19 -0700)
Commit migrated from https://github.com/dotnet/corefx/commit/29328b71b65abe3a199f0daf62f6ee71ca916e78

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

index aff3bbc..c95fdc9 100644 (file)
@@ -1263,9 +1263,9 @@ namespace System.Net.Http
             }
         }
 
-        private async Task SendEndStreamAsync(int streamId, CancellationToken cancellationToken)
+        private async Task SendEndStreamAsync(int streamId)
         {
-            Memory<byte> writeBuffer = await StartWriteAsync(FrameHeader.Size, cancellationToken).ConfigureAwait(false);
+            Memory<byte> writeBuffer = await StartWriteAsync(FrameHeader.Size).ConfigureAwait(false);
             if (NetEventSource.IsEnabled) Trace(streamId, "Started writing.");
 
             FrameHeader frameHeader = new FrameHeader(0, FrameType.Data, FrameFlags.EndStream, streamId);
index 6b45233..9039453 100644 (file)
@@ -163,8 +163,6 @@ namespace System.Net.Http
                         }
                     }
 
-                    await _connection.SendEndStreamAsync(_streamId, _requestBodyCancellationToken).ConfigureAwait(false);
-
                     if (NetEventSource.IsEnabled) Trace($"Finished sending request body.");
                 }
                 catch (Exception e)
@@ -184,6 +182,9 @@ namespace System.Net.Http
                     throw;
                 }
 
+                // Before we send the EndStream, mark the request as completed.
+                // This avoids races where the server may close the connection after finishing all requests
+                // and we get an OnReset callback before the request is marked as completed.
                 lock (SyncObject)
                 {
                     Debug.Assert(_requestCompletionState == StreamCompletionState.InProgress, $"Request already completed with state={_requestCompletionState}");
@@ -191,6 +192,10 @@ namespace System.Net.Http
                     _requestCompletionState = StreamCompletionState.Completed;
                     CheckForCompletion();
                 }
+
+                // Send EndStream asynchronously and without cancellation.
+                // If this fails, it means that the connection is aborting and we will be reset.
+                _connection.LogExceptions(_connection.SendEndStreamAsync(_streamId));
             }
 
             // Delay sending request body if we sent Expect: 100-continue.