Flush response headers #3031

This commit is contained in:
Chris Ross (ASP.NET) 2018-10-19 10:14:19 -07:00
parent 23a4e11261
commit 277a5502fd
3 changed files with 54 additions and 30 deletions

View File

@ -104,19 +104,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
}
public Task FlushAsync(IHttpOutputAborter outputAborter, CancellationToken cancellationToken)
{
lock (_writeLock)
{
if (_completed)
{
return Task.CompletedTask;
}
return _flusher.FlushAsync(outputAborter, cancellationToken);
}
}
public Task Write100ContinueAsync(int streamId)
{
lock (_writeLock)
@ -171,9 +158,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
throw new InvalidOperationException(hex.Message, hex); // Report the error to the user if this was the first write.
}
}
_ = _flusher.FlushAsync();
}
public Task WriteResponseTrailers(int streamId, HttpResponseTrailers headers)
public Task WriteResponseTrailersAsync(int streamId, HttpResponseTrailers headers)
{
lock (_writeLock)
{

View File

@ -28,7 +28,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly object _dataWriterLock = new object();
private readonly Pipe _dataPipe;
private readonly Task _dataWriteProcessingTask;
private bool _startedWritingDataFrames;
private bool _completed;
private bool _disposed;
@ -101,18 +100,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
if (_startedWritingDataFrames)
{
// If there's already been response data written to the stream, just wait for that. Any header
// should be in front of the data frames in the connection pipe. Trailers could change things.
return _flusher.FlushAsync(this, cancellationToken);
}
else
{
// Flushing the connection pipe ensures headers already in the pipe are flushed even if no data
// frames have been written.
return _frameWriter.FlushAsync(this, cancellationToken);
}
return _flusher.FlushAsync(this, cancellationToken);
}
}
@ -160,8 +148,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
_startedWritingDataFrames = true;
_dataPipe.Writer.Write(data);
return _flusher.FlushAsync(this, cancellationToken);
}
@ -212,7 +198,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
await _frameWriter.WriteDataAsync(_streamId, _flowControl, _stream.MinResponseDataRate, readResult.Buffer, endStream: false);
}
await _frameWriter.WriteResponseTrailers(_streamId, _stream.Trailers);
await _frameWriter.WriteResponseTrailersAsync(_streamId, _stream.Trailers);
}
else
{

View File

@ -2083,5 +2083,54 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
}
[Fact]
public async Task ResponseHeaders_NotBlockedByFlowControl()
{
var headers = new[]
{
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
};
await InitializeConnectionAsync(context =>
{
return context.Response.WriteAsync("hello world");
});
_clientSettings.InitialWindowSize = 0;
await SendSettingsAsync();
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1);
await SendWindowUpdateAsync(1, 11);
await ExpectAsync(Http2FrameType.DATA,
withLength: 11,
withFlags: (byte)Http2DataFrameFlags.NONE,
withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA,
withLength: 0,
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
withStreamId: 1);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
_hpackDecoder.Decode(headersFrame.PayloadSequence, endHeaders: false, handler: this);
Assert.Equal(2, _decodedHeaders.Count);
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
}
}
}