}
}
- 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);
}
}
- await _connection.SendEndStreamAsync(_streamId, _requestBodyCancellationToken).ConfigureAwait(false);
-
if (NetEventSource.IsEnabled) Trace($"Finished sending request body.");
}
catch (Exception e)
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}");
_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.