Verify request Content-Length #2733
This commit is contained in:
parent
f7ce86c8d7
commit
b62bb641b2
|
|
@ -560,4 +560,10 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
||||||
<data name="Http2StreamErrorAfterHeaders" xml:space="preserve">
|
<data name="Http2StreamErrorAfterHeaders" xml:space="preserve">
|
||||||
<value>An error occured after the response headers were sent, a reset is being sent.</value>
|
<value>An error occured after the response headers were sent, a reset is being sent.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Http2StreamErrorLessDataThanLength" xml:space="preserve">
|
||||||
|
<value>Less data received than specified in the Content-Length header.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Http2StreamErrorMoreDataThanLength" xml:space="preserve">
|
||||||
|
<value>More data received than specified in the Content-Length header.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -73,6 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
private Http2Stream _currentHeadersStream;
|
private Http2Stream _currentHeadersStream;
|
||||||
private RequestHeaderParsingState _requestHeaderParsingState;
|
private RequestHeaderParsingState _requestHeaderParsingState;
|
||||||
private PseudoHeaderFields _parsedPseudoHeaderFields;
|
private PseudoHeaderFields _parsedPseudoHeaderFields;
|
||||||
|
private Http2HeadersFrameFlags _headerFlags;
|
||||||
private bool _isMethodConnect;
|
private bool _isMethodConnect;
|
||||||
private readonly object _stateLock = new object();
|
private readonly object _stateLock = new object();
|
||||||
private int _highestOpenedStreamId;
|
private int _highestOpenedStreamId;
|
||||||
|
|
@ -517,12 +518,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
TimeoutControl = this,
|
TimeoutControl = this,
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM)
|
|
||||||
{
|
|
||||||
_currentHeadersStream.OnEndStreamReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentHeadersStream.Reset();
|
_currentHeadersStream.Reset();
|
||||||
|
_headerFlags = _incomingFrame.HeadersFlags;
|
||||||
|
|
||||||
var endHeaders = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS;
|
var endHeaders = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS;
|
||||||
await DecodeHeadersAsync(application, endHeaders, _incomingFrame.HeadersPayload);
|
await DecodeHeadersAsync(application, endHeaders, _incomingFrame.HeadersPayload);
|
||||||
|
|
@ -825,6 +822,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
|
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This must be initialized before we offload the request or else we may start processing request body frames without it.
|
||||||
|
_currentHeadersStream.InputRemaining = _currentHeadersStream.RequestHeaders.ContentLength;
|
||||||
|
|
||||||
|
// This must wait until we've received all of the headers so we can verify the content-length.
|
||||||
|
if ((_headerFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM)
|
||||||
|
{
|
||||||
|
_currentHeadersStream.OnEndStreamReceived();
|
||||||
|
}
|
||||||
|
|
||||||
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
|
_streams[_incomingFrame.StreamId] = _currentHeadersStream;
|
||||||
// Must not allow app code to block the connection handling loop.
|
// Must not allow app code to block the connection handling loop.
|
||||||
ThreadPool.UnsafeQueueUserWorkItem(state =>
|
ThreadPool.UnsafeQueueUserWorkItem(state =>
|
||||||
|
|
@ -840,6 +846,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
_currentHeadersStream = null;
|
_currentHeadersStream = null;
|
||||||
_requestHeaderParsingState = RequestHeaderParsingState.Ready;
|
_requestHeaderParsingState = RequestHeaderParsingState.Ready;
|
||||||
_parsedPseudoHeaderFields = PseudoHeaderFields.None;
|
_parsedPseudoHeaderFields = PseudoHeaderFields.None;
|
||||||
|
_headerFlags = Http2HeadersFrameFlags.NONE;
|
||||||
_isMethodConnect = false;
|
_isMethodConnect = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
public int StreamId => _context.StreamId;
|
public int StreamId => _context.StreamId;
|
||||||
|
|
||||||
|
public long? InputRemaining { get; internal set; }
|
||||||
|
|
||||||
public bool RequestBodyStarted { get; private set; }
|
public bool RequestBodyStarted { get; private set; }
|
||||||
public bool EndStreamReceived => (_completionState & StreamCompletionFlags.EndStreamReceived) == StreamCompletionFlags.EndStreamReceived;
|
public bool EndStreamReceived => (_completionState & StreamCompletionFlags.EndStreamReceived) == StreamCompletionFlags.EndStreamReceived;
|
||||||
|
private bool IsAborted => (_completionState & StreamCompletionFlags.Aborted) == StreamCompletionFlags.Aborted;
|
||||||
|
|
||||||
public override bool IsUpgradableRequest => false;
|
public override bool IsUpgradableRequest => false;
|
||||||
|
|
||||||
|
|
@ -263,8 +266,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
public Task OnDataAsync(Http2Frame dataFrame)
|
public Task OnDataAsync(Http2Frame dataFrame)
|
||||||
{
|
{
|
||||||
// TODO: content-length accounting
|
|
||||||
|
|
||||||
// Since padding isn't buffered, immediately count padding bytes as read for flow control purposes.
|
// Since padding isn't buffered, immediately count padding bytes as read for flow control purposes.
|
||||||
if (dataFrame.DataHasPadding)
|
if (dataFrame.DataHasPadding)
|
||||||
{
|
{
|
||||||
|
|
@ -288,6 +289,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
_inputFlowControl.Advance(payload.Count);
|
_inputFlowControl.Advance(payload.Count);
|
||||||
|
|
||||||
|
if (IsAborted)
|
||||||
|
{
|
||||||
|
// Ignore data frames for aborted streams, but only after counting them for purposes of connection level flow control.
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This check happens after flow control so that when we throw and abort, the byte count is returned to the connection
|
||||||
|
// level accounting.
|
||||||
|
if (InputRemaining.HasValue)
|
||||||
|
{
|
||||||
|
// https://tools.ietf.org/html/rfc7540#section-8.1.2.6
|
||||||
|
if (payload.Count > InputRemaining.Value)
|
||||||
|
{
|
||||||
|
throw new Http2StreamErrorException(StreamId, CoreStrings.Http2StreamErrorMoreDataThanLength, Http2ErrorCode.PROTOCOL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputRemaining -= payload.Count;
|
||||||
|
}
|
||||||
|
|
||||||
RequestBodyPipe.Writer.Write(payload);
|
RequestBodyPipe.Writer.Write(payload);
|
||||||
var flushTask = RequestBodyPipe.Writer.FlushAsync();
|
var flushTask = RequestBodyPipe.Writer.FlushAsync();
|
||||||
|
|
||||||
|
|
@ -306,6 +326,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
public void OnEndStreamReceived()
|
public void OnEndStreamReceived()
|
||||||
{
|
{
|
||||||
|
if (InputRemaining.HasValue)
|
||||||
|
{
|
||||||
|
// https://tools.ietf.org/html/rfc7540#section-8.1.2.6
|
||||||
|
if (InputRemaining.Value != 0)
|
||||||
|
{
|
||||||
|
throw new Http2StreamErrorException(StreamId, CoreStrings.Http2StreamErrorLessDataThanLength, Http2ErrorCode.PROTOCOL_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TryApplyCompletionFlag(StreamCompletionFlags.EndStreamReceived);
|
TryApplyCompletionFlag(StreamCompletionFlags.EndStreamReceived);
|
||||||
|
|
||||||
RequestBodyPipe.Writer.Complete();
|
RequestBodyPipe.Writer.Complete();
|
||||||
|
|
|
||||||
|
|
@ -2072,6 +2072,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
internal static string FormatHttp2StreamErrorAfterHeaders()
|
internal static string FormatHttp2StreamErrorAfterHeaders()
|
||||||
=> GetString("Http2StreamErrorAfterHeaders");
|
=> GetString("Http2StreamErrorAfterHeaders");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Less data received than specified in the Content-Length header.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Http2StreamErrorLessDataThanLength
|
||||||
|
{
|
||||||
|
get => GetString("Http2StreamErrorLessDataThanLength");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Less data received than specified in the Content-Length header.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatHttp2StreamErrorLessDataThanLength()
|
||||||
|
=> GetString("Http2StreamErrorLessDataThanLength");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// More data received than specified in the Content-Length header.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Http2StreamErrorMoreDataThanLength
|
||||||
|
{
|
||||||
|
get => GetString("Http2StreamErrorMoreDataThanLength");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// More data received than specified in the Content-Length header.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatHttp2StreamErrorMoreDataThanLength()
|
||||||
|
=> GetString("Http2StreamErrorMoreDataThanLength");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -43,11 +42,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
|
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
|
private MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
|
||||||
private readonly DuplexPipe.DuplexPipePair _pair;
|
private DuplexPipe.DuplexPipePair _pair;
|
||||||
private readonly TestApplicationErrorLogger _logger;
|
private readonly TestApplicationErrorLogger _logger;
|
||||||
private readonly Http2ConnectionContext _connectionContext;
|
private Http2ConnectionContext _connectionContext;
|
||||||
private readonly Http2Connection _connection;
|
private Http2Connection _connection;
|
||||||
private readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
|
private readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
|
||||||
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
||||||
private readonly HPackDecoder _hpackDecoder;
|
private readonly HPackDecoder _hpackDecoder;
|
||||||
|
|
@ -69,24 +68,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
|
||||||
public Http2StreamTests()
|
public Http2StreamTests()
|
||||||
{
|
{
|
||||||
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
|
|
||||||
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
|
|
||||||
// it easier to verify request frames are processed correctly immediately after sending the them.
|
|
||||||
var inputPipeOptions = new PipeOptions(
|
|
||||||
pool: _memoryPool,
|
|
||||||
readerScheduler: PipeScheduler.Inline,
|
|
||||||
writerScheduler: PipeScheduler.ThreadPool,
|
|
||||||
useSynchronizationContext: false
|
|
||||||
);
|
|
||||||
var outputPipeOptions = new PipeOptions(
|
|
||||||
pool: _memoryPool,
|
|
||||||
readerScheduler: PipeScheduler.ThreadPool,
|
|
||||||
writerScheduler: PipeScheduler.Inline,
|
|
||||||
useSynchronizationContext: false
|
|
||||||
);
|
|
||||||
|
|
||||||
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
|
|
||||||
|
|
||||||
_noopApplication = context => Task.CompletedTask;
|
_noopApplication = context => Task.CompletedTask;
|
||||||
|
|
||||||
_echoMethod = context =>
|
_echoMethod = context =>
|
||||||
|
|
@ -179,6 +160,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
|
||||||
_logger = new TestApplicationErrorLogger();
|
_logger = new TestApplicationErrorLogger();
|
||||||
|
|
||||||
|
InitializeConnectionFields(KestrelMemoryPool.Create());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeConnectionFields(MemoryPool<byte> memoryPool)
|
||||||
|
{
|
||||||
|
_memoryPool = memoryPool;
|
||||||
|
|
||||||
|
// Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
|
||||||
|
// Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
|
||||||
|
// it easier to verify request frames are processed correctly immediately after sending the them.
|
||||||
|
var inputPipeOptions = new PipeOptions(
|
||||||
|
pool: _memoryPool,
|
||||||
|
readerScheduler: PipeScheduler.Inline,
|
||||||
|
writerScheduler: PipeScheduler.ThreadPool,
|
||||||
|
useSynchronizationContext: false
|
||||||
|
);
|
||||||
|
var outputPipeOptions = new PipeOptions(
|
||||||
|
pool: _memoryPool,
|
||||||
|
readerScheduler: PipeScheduler.ThreadPool,
|
||||||
|
writerScheduler: PipeScheduler.Inline,
|
||||||
|
useSynchronizationContext: false
|
||||||
|
);
|
||||||
|
|
||||||
|
_pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
|
||||||
|
|
||||||
_connectionContext = new Http2ConnectionContext
|
_connectionContext = new Http2ConnectionContext
|
||||||
{
|
{
|
||||||
ConnectionFeatures = new FeatureCollection(),
|
ConnectionFeatures = new FeatureCollection(),
|
||||||
|
|
@ -756,21 +762,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContentLength_Response_FirstWriteMoreBytesWritten_Throws_Sends500()
|
public async Task ContentLength_Received_SingleDataFrame_Verified()
|
||||||
{
|
{
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
};
|
};
|
||||||
await InitializeConnectionAsync(async context =>
|
await InitializeConnectionAsync(async context =>
|
||||||
{
|
{
|
||||||
context.Response.ContentLength = 11;
|
var buffer = new byte[100];
|
||||||
await context.Response.WriteAsync("hello, world"); // 12
|
var read = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);
|
||||||
|
Assert.Equal(12, read);
|
||||||
});
|
});
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[12].AsSpan(), endStream: true);
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
||||||
withLength: 55,
|
withLength: 55,
|
||||||
|
|
@ -781,47 +790,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||||
withStreamId: 1);
|
withStreamId: 1);
|
||||||
|
|
||||||
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too many bytes written (12 of 11).") ?? false);
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
|
||||||
|
|
||||||
Assert.Equal(3, _decodedHeaders.Count);
|
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
|
||||||
Assert.Equal("500", _decodedHeaders[HeaderNames.Status]);
|
|
||||||
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ContentLength_Response_MoreBytesWritten_ThrowsAndResetsStream()
|
|
||||||
{
|
|
||||||
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(async context =>
|
|
||||||
{
|
|
||||||
context.Response.ContentLength = 11;
|
|
||||||
await context.Response.WriteAsync("hello,");
|
|
||||||
await context.Response.WriteAsync(" world");
|
|
||||||
});
|
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
|
||||||
withLength: 56,
|
|
||||||
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
|
||||||
withStreamId: 1);
|
|
||||||
await ExpectAsync(Http2FrameType.DATA,
|
|
||||||
withLength: 6,
|
|
||||||
withFlags: (byte)Http2DataFrameFlags.NONE,
|
|
||||||
withStreamId: 1);
|
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(1, Http2ErrorCode.INTERNAL_ERROR, "Response Content-Length mismatch: too many bytes written (12 of 11).");
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
||||||
|
|
@ -829,25 +797,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
Assert.Equal(3, _decodedHeaders.Count);
|
Assert.Equal(3, _decodedHeaders.Count);
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
||||||
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
||||||
Assert.Equal("11", _decodedHeaders[HeaderNames.ContentLength]);
|
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ContentLength_Response_NoBytesWritten_Sends500()
|
public async Task ContentLength_ReceivedInContinuation_SingleDataFrame_Verified()
|
||||||
{
|
{
|
||||||
var headers = new[]
|
await InitializeConnectionAsync(async context =>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
var buffer = new byte[100];
|
||||||
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
var read = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);
|
||||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
Assert.Equal(12, read);
|
||||||
};
|
|
||||||
await InitializeConnectionAsync(context =>
|
|
||||||
{
|
|
||||||
context.Response.ContentLength = 11;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>("a", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("b", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("c", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("d", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[12].AsSpan(), endStream: true);
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
||||||
withLength: 55,
|
withLength: 55,
|
||||||
|
|
@ -858,46 +833,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||||
withStreamId: 1);
|
withStreamId: 1);
|
||||||
|
|
||||||
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains("Response Content-Length mismatch: too few bytes written (0 of 11).") ?? false);
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
|
||||||
|
|
||||||
Assert.Equal(3, _decodedHeaders.Count);
|
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
|
||||||
Assert.Equal("500", _decodedHeaders[HeaderNames.Status]);
|
|
||||||
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task ContentLength_Response_TooFewBytesWritten_Resets()
|
|
||||||
{
|
|
||||||
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 =>
|
|
||||||
{
|
|
||||||
context.Response.ContentLength = 11;
|
|
||||||
return context.Response.WriteAsync("hello,");
|
|
||||||
});
|
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
|
||||||
withLength: 56,
|
|
||||||
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
|
||||||
withStreamId: 1);
|
|
||||||
await ExpectAsync(Http2FrameType.DATA,
|
|
||||||
withLength: 6,
|
|
||||||
withFlags: (byte)Http2DataFrameFlags.NONE,
|
|
||||||
withStreamId: 1);
|
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(1, Http2ErrorCode.INTERNAL_ERROR, "Response Content-Length mismatch: too few bytes written (6 of 11).");
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
||||||
|
|
@ -905,24 +840,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
Assert.Equal(3, _decodedHeaders.Count);
|
Assert.Equal(3, _decodedHeaders.Count);
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
||||||
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
||||||
Assert.Equal("11", _decodedHeaders[HeaderNames.ContentLength]);
|
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ApplicationExeption_BeforeFirstWrite_Sends500()
|
public async Task ContentLength_Received_MultipleDataFrame_Verified()
|
||||||
{
|
{
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
};
|
};
|
||||||
await InitializeConnectionAsync(context =>
|
await InitializeConnectionAsync(async context =>
|
||||||
{
|
{
|
||||||
throw new Exception("App Faulted");
|
var buffer = new byte[100];
|
||||||
|
var read = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);
|
||||||
|
var total = read;
|
||||||
|
while (read > 0)
|
||||||
|
{
|
||||||
|
read = await context.Request.Body.ReadAsync(buffer, total, buffer.Length - total);
|
||||||
|
total += read;
|
||||||
|
}
|
||||||
|
Assert.Equal(12, total);
|
||||||
});
|
});
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[1].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[3].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[8].AsSpan(), endStream: true);
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
||||||
withLength: 55,
|
withLength: 55,
|
||||||
|
|
@ -933,53 +881,186 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||||
withStreamId: 1);
|
withStreamId: 1);
|
||||||
|
|
||||||
Assert.Contains(_logger.Messages, m => (m.Exception?.Message.Contains("App Faulted") ?? false) && m.LogLevel == LogLevel.Error);
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
||||||
|
|
||||||
Assert.Equal(3, _decodedHeaders.Count);
|
Assert.Equal(3, _decodedHeaders.Count);
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
||||||
Assert.Equal("500", _decodedHeaders[HeaderNames.Status]);
|
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
||||||
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
Assert.Equal("0", _decodedHeaders[HeaderNames.ContentLength]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ApplicationExeption_AfterFirstWrite_Resets()
|
public async Task ContentLength_Received_NoDataFrames_Reset()
|
||||||
{
|
{
|
||||||
var headers = new[]
|
var headers = new[]
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
};
|
};
|
||||||
await InitializeConnectionAsync(async context =>
|
await InitializeConnectionAsync(_noopApplication);
|
||||||
{
|
|
||||||
await context.Response.WriteAsync("hello,");
|
|
||||||
throw new Exception("App Faulted");
|
|
||||||
});
|
|
||||||
|
|
||||||
await StartStreamAsync(1, headers, endStream: true);
|
await StartStreamAsync(1, headers, endStream: true);
|
||||||
|
|
||||||
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorLessDataThanLength);
|
||||||
withLength: 37,
|
|
||||||
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
|
|
||||||
withStreamId: 1);
|
|
||||||
await ExpectAsync(Http2FrameType.DATA,
|
|
||||||
withLength: 6,
|
|
||||||
withFlags: (byte)Http2DataFrameFlags.NONE,
|
|
||||||
withStreamId: 1);
|
|
||||||
|
|
||||||
await WaitForStreamErrorAsync(1, Http2ErrorCode.INTERNAL_ERROR, "App Faulted");
|
|
||||||
|
|
||||||
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
}
|
||||||
|
|
||||||
_hpackDecoder.Decode(headersFrame.HeadersPayload, endHeaders: false, handler: this);
|
[Fact]
|
||||||
|
public async Task ContentLength_ReceivedInContinuation_NoDataFrames_Reset()
|
||||||
|
{
|
||||||
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>("a", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("b", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("c", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>("d", _largeHeaderValue),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await InitializeConnectionAsync(_noopApplication);
|
||||||
|
|
||||||
Assert.Equal(2, _decodedHeaders.Count);
|
await StartStreamAsync(1, headers, endStream: true);
|
||||||
Assert.Contains("date", _decodedHeaders.Keys, StringComparer.OrdinalIgnoreCase);
|
|
||||||
Assert.Equal("200", _decodedHeaders[HeaderNames.Status]);
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorLessDataThanLength);
|
||||||
|
|
||||||
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContentLength_Received_SingleDataFrameOverSize_Reset()
|
||||||
|
{
|
||||||
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await InitializeConnectionAsync(async context =>
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ConnectionAbortedException>(async () =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[100];
|
||||||
|
while (await context.Request.Body.ReadAsync(buffer, 0, buffer.Length) > 0) { }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[13].AsSpan(), endStream: true);
|
||||||
|
|
||||||
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorMoreDataThanLength);
|
||||||
|
|
||||||
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContentLength_Received_SingleDataFrameUnderSize_Reset()
|
||||||
|
{
|
||||||
|
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
||||||
|
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
||||||
|
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
||||||
|
Dispose();
|
||||||
|
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
||||||
|
|
||||||
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await InitializeConnectionAsync(async context =>
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ConnectionAbortedException>(async () =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[100];
|
||||||
|
while (await context.Request.Body.ReadAsync(buffer, 0, buffer.Length) > 0) { }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[11].AsSpan(), endStream: true);
|
||||||
|
|
||||||
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorLessDataThanLength);
|
||||||
|
|
||||||
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContentLength_Received_MultipleDataFramesOverSize_Reset()
|
||||||
|
{
|
||||||
|
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
||||||
|
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
||||||
|
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
||||||
|
Dispose();
|
||||||
|
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
||||||
|
|
||||||
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await InitializeConnectionAsync(async context =>
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ConnectionAbortedException>(async () =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[100];
|
||||||
|
while (await context.Request.Body.ReadAsync(buffer, 0, buffer.Length) > 0) { }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[1].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[2].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[10].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[2].AsSpan(), endStream: true);
|
||||||
|
|
||||||
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorMoreDataThanLength);
|
||||||
|
|
||||||
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContentLength_Received_MultipleDataFramesUnderSize_Reset()
|
||||||
|
{
|
||||||
|
// I hate doing this, but it avoids exceptions from MemoryPool.Dipose() in debug mode. The problem is since
|
||||||
|
// the stream's ProcessRequestsAsync loop is never awaited by the connection, it's not really possible to
|
||||||
|
// observe when all the blocks are returned. This can be removed after we implement graceful shutdown.
|
||||||
|
Dispose();
|
||||||
|
InitializeConnectionFields(new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), allowLateReturn: true));
|
||||||
|
|
||||||
|
var headers = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Method, "POST"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||||
|
new KeyValuePair<string, string>(HeaderNames.ContentLength, "12"),
|
||||||
|
};
|
||||||
|
await InitializeConnectionAsync(async context =>
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ConnectionAbortedException>(async () =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[100];
|
||||||
|
while (await context.Request.Body.ReadAsync(buffer, 0, buffer.Length) > 0) { }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await StartStreamAsync(1, headers, endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[1].AsSpan(), endStream: false);
|
||||||
|
await SendDataAsync(1, new byte[2].AsSpan(), endStream: true);
|
||||||
|
|
||||||
|
await WaitForStreamErrorAsync(1, Http2ErrorCode.PROTOCOL_ERROR, CoreStrings.Http2StreamErrorLessDataThanLength);
|
||||||
|
|
||||||
|
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1410,8 +1491,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
|
||||||
if (expectedErrorMessage != null)
|
if (expectedErrorMessage != null)
|
||||||
{
|
{
|
||||||
var message = Assert.Single(_logger.Messages, m => m.Exception is ConnectionAbortedException);
|
Assert.Contains(_logger.Messages, m => m.Exception?.Message.Contains(expectedErrorMessage) ?? false);
|
||||||
Assert.Contains(expectedErrorMessage, message.Exception.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#if NETCOREAPP2_2
|
#if NETCOREAPP2_2
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -56,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var dataset = new TheoryData<H2SpecTestCase>();
|
var dataset = new TheoryData<H2SpecTestCase>();
|
||||||
var toSkip = new[] { "hpack/4.2/1", "http2/5.1/8", "http2/8.1.2.6/1", "http2/8.1.2.6/2" };
|
var toSkip = new[] { "hpack/4.2/1", "http2/5.1/8" };
|
||||||
|
|
||||||
foreach (var testcase in H2SpecCommands.EnumerateTestCases())
|
foreach (var testcase in H2SpecCommands.EnumerateTestCases())
|
||||||
{
|
{
|
||||||
|
|
@ -123,9 +124,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
|
|
||||||
private void ConfigureHelloWorld(IApplicationBuilder app)
|
private void ConfigureHelloWorld(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.Run(context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
return context.Request.Body.CopyToAsync(context.Response.Body);
|
// Read the whole request body to check for errors.
|
||||||
|
await context.Request.Body.CopyToAsync(Stream.Null);
|
||||||
|
await context.Response.WriteAsync("Hello World");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ namespace Microsoft.AspNetCore.Testing
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue