Merge branch 'release/2.2'
# Conflicts: # src/Kestrel.Core/CoreStrings.resx # src/Kestrel.Core/Properties/CoreStrings.Designer.cs # test/Kestrel.InMemory.FunctionalTests/Http2/Http2StreamTests.cs
This commit is contained in:
commit
50d238c6f2
|
|
@ -43,7 +43,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = memoryPool,
|
||||
TimeoutControl = new MockTimeoutControl(),
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = memoryPool,
|
||||
TimeoutControl = new MockTimeoutControl(),
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ServiceContext = serviceContext,
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = _memoryPool,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ServiceContext = serviceContext,
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = memoryPool,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ServiceContext = serviceContext,
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = _memoryPool,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport,
|
||||
TimeoutControl = new MockTimeoutControl()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -188,7 +188,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ServiceContext = serviceContext,
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = memoryPool,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = _memoryPool,
|
||||
TimeoutControl = new MockTimeoutControl(),
|
||||
Application = _pair.Application,
|
||||
Transport = _pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
|
|
@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Connections
|
|||
{
|
||||
// We expect this to be overridden, but this helps maintain back compat
|
||||
// with implementations of ConnectionContext that predate the addition of
|
||||
// ConnectioContext.Abort()
|
||||
// ConnectionContext.Abort()
|
||||
Features.Get<IConnectionLifetimeFeature>()?.Abort();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -168,9 +168,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
|
|||
_transport.Input.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -557,13 +557,19 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
|||
<data name="Http2StreamErrorSchemeMismatch" xml:space="preserve">
|
||||
<value>The request :scheme header '{requestScheme}' does not match the transport scheme '{transportScheme}'.</value>
|
||||
</data>
|
||||
<data name="Http2StreamErrorAfterHeaders" xml:space="preserve">
|
||||
<value>An error occured after the response headers were sent, a reset is being sent.</value>
|
||||
</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>
|
||||
<data name="Http2StreamErrorAfterHeaders" xml:space="preserve">
|
||||
<value>An error occured after the response headers were sent, a reset is being sent.</value>
|
||||
</data>
|
||||
<data name="Http2ErrorMaxStreams" xml:space="preserve">
|
||||
<value>A new stream was refused because this connection has reached its stream limit.</value>
|
||||
</data>
|
||||
<data name="GreaterThanZeroRequired" xml:space="preserve">
|
||||
<value>A value greater than zero is required.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Limits only applicable to HTTP/2 connections.
|
||||
/// </summary>
|
||||
public class Http2Limits
|
||||
{
|
||||
private int _maxStreamsPerConnection = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused.
|
||||
/// <para>
|
||||
/// Defaults to 100
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public int MaxStreamsPerConnection
|
||||
{
|
||||
get => _maxStreamsPerConnection;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanZeroRequired);
|
||||
}
|
||||
_maxStreamsPerConnection = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,11 @@ using System.Buffers;
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO.Pipelines;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Connections.Abstractions;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||
|
|
@ -28,6 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
protected readonly long _keepAliveTicks;
|
||||
private readonly long _requestHeadersTimeoutTicks;
|
||||
|
||||
private int _requestAborted;
|
||||
private volatile bool _requestTimedOut;
|
||||
private uint _requestCount;
|
||||
|
||||
|
|
@ -61,6 +60,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
public override bool IsUpgradableRequest => _upgradeAvailable;
|
||||
|
||||
/// <summary>
|
||||
/// Immediately kill the connection and poison the request body stream with an error.
|
||||
/// </summary>
|
||||
public void Abort(ConnectionAbortedException abortReason)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _requestAborted, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort output prior to calling OnIOCompleted() to give the transport the chance to complete the input
|
||||
// with the correct error and message.
|
||||
Output.Abort(abortReason);
|
||||
|
||||
OnInputOrOutputCompleted();
|
||||
|
||||
PoisonRequestBodyStream(abortReason);
|
||||
}
|
||||
|
||||
protected override void ApplicationAbort()
|
||||
{
|
||||
Log.ApplicationAbortedConnection(ConnectionId, TraceIdentifier);
|
||||
Abort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedByApplication));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the request processing loop between requests.
|
||||
/// Called on all active connections when the server wants to initiate a shutdown
|
||||
|
|
|
|||
|
|
@ -21,6 +21,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
public IPEndPoint LocalEndPoint { get; set; }
|
||||
public ITimeoutControl TimeoutControl { get; set; }
|
||||
public IDuplexPipe Transport { get; set; }
|
||||
public IDuplexPipe Application { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,7 +399,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
private const int MaxChunkPrefixBytes = 10;
|
||||
|
||||
private long _inputLength;
|
||||
private long _consumedBytes;
|
||||
|
||||
private Mode _mode = Mode.Prefix;
|
||||
|
||||
|
|
@ -490,16 +489,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
return _mode == Mode.Complete;
|
||||
}
|
||||
|
||||
private void AddAndCheckConsumedBytes(long consumedBytes)
|
||||
{
|
||||
_consumedBytes += consumedBytes;
|
||||
|
||||
if (_consumedBytes > _context.MaxRequestBodySize)
|
||||
{
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseChunkedPrefix(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
|
||||
{
|
||||
consumed = buffer.Start;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.IO;
|
|||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
|
|
@ -230,10 +229,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
ApplicationAbort();
|
||||
}
|
||||
|
||||
protected virtual void ApplicationAbort()
|
||||
{
|
||||
Log.ApplicationAbortedConnection(ConnectionId, TraceIdentifier);
|
||||
Abort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedByApplication));
|
||||
}
|
||||
protected abstract void ApplicationAbort();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ using Microsoft.AspNetCore.Hosting.Server;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
|
|
@ -42,7 +41,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
private Stack<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||
private Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||
|
||||
private int _requestAborted;
|
||||
private volatile int _ioCompleted;
|
||||
private CancellationTokenSource _abortedCts;
|
||||
private CancellationToken? _manuallySetRequestAbortToken;
|
||||
|
|
@ -385,6 +383,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
}
|
||||
|
||||
protected virtual void OnErrorAfterResponseStarted()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual bool BeginRead(out ValueTask<ReadResult> awaitable)
|
||||
{
|
||||
awaitable = default;
|
||||
|
|
@ -425,23 +427,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
ServiceContext.Scheduler.Schedule(state => ((HttpProtocol)state).CancelRequestAbortedToken(), this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately kill the connection and poison the request and response streams with an error if there is one.
|
||||
/// </summary>
|
||||
public virtual void Abort(ConnectionAbortedException abortReason)
|
||||
protected void PoisonRequestBodyStream(Exception abortReason)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _requestAborted, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_streams?.Abort(abortReason);
|
||||
|
||||
// Abort output prior to calling OnIOCompleted() to give the transport the chance to
|
||||
// complete the input with the correct error and message.
|
||||
Output.Abort(abortReason);
|
||||
|
||||
OnInputOrOutputCompleted();
|
||||
}
|
||||
|
||||
public void OnHeader(Span<byte> name, Span<byte> value)
|
||||
|
|
@ -1032,7 +1020,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
if (HasResponseStarted)
|
||||
{
|
||||
ErrorAfterResponseStarted();
|
||||
// We can no longer change the response, so we simply close the connection.
|
||||
_keepAlive = false;
|
||||
OnErrorAfterResponseStarted();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
@ -1057,12 +1047,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
return WriteSuffix();
|
||||
}
|
||||
|
||||
protected virtual void ErrorAfterResponseStarted()
|
||||
{
|
||||
// We can no longer change the response, so we simply close the connection.
|
||||
_keepAlive = false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private async Task ProduceEndAwaited()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
private readonly HttpProtocol _context;
|
||||
|
||||
private bool _send100Continue = true;
|
||||
private long _consumedBytes;
|
||||
|
||||
protected MessageBody(HttpProtocol context)
|
||||
{
|
||||
|
|
@ -168,6 +169,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
}
|
||||
|
||||
protected void AddAndCheckConsumedBytes(long consumedBytes)
|
||||
{
|
||||
_consumedBytes += consumedBytes;
|
||||
|
||||
if (_consumedBytes > _context.MaxRequestBodySize)
|
||||
{
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
private class ForZeroContentLength : MessageBody
|
||||
{
|
||||
public ForZeroContentLength(bool keepAlive)
|
||||
|
|
|
|||
|
|
@ -86,8 +86,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
public Http2Connection(Http2ConnectionContext context)
|
||||
{
|
||||
_context = context;
|
||||
_frameWriter = new Http2FrameWriter(context.Transport.Output, context.Application.Input, _outputFlowControl, this, context.ConnectionId, context.ServiceContext.Log);
|
||||
_frameWriter = new Http2FrameWriter(context.Transport.Output, context.ConnectionContext, _outputFlowControl, this, context.ConnectionId, context.ServiceContext.Log);
|
||||
_hpackDecoder = new HPackDecoder((int)_serverSettings.HeaderTableSize);
|
||||
_serverSettings.MaxConcurrentStreams = (uint)context.ServiceContext.ServerOptions.Limits.Http2.MaxStreamsPerConnection;
|
||||
}
|
||||
|
||||
public string ConnectionId => _context.ConnectionId;
|
||||
|
|
@ -98,6 +99,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public IFeatureCollection ConnectionFeatures => _context.ConnectionFeatures;
|
||||
|
||||
internal Http2PeerSettings ServerSettings => _serverSettings;
|
||||
|
||||
public void OnInputOrOutputCompleted()
|
||||
{
|
||||
lock (_stateLock)
|
||||
|
|
@ -172,7 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
if (_state != Http2ConnectionState.Closed)
|
||||
{
|
||||
await _frameWriter.WriteSettingsAsync(_serverSettings);
|
||||
await _frameWriter.WriteSettingsAsync(_serverSettings.GetNonProtocolDefaults());
|
||||
}
|
||||
|
||||
while (_state != Http2ConnectionState.Closed)
|
||||
|
|
@ -201,7 +204,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
catch (Http2StreamErrorException ex)
|
||||
{
|
||||
Log.Http2StreamError(ConnectionId, ex);
|
||||
AbortStream(_incomingFrame.StreamId, new ConnectionAbortedException(ex.Message, ex));
|
||||
AbortStream(_incomingFrame.StreamId, new IOException(ex.Message, ex));
|
||||
await _frameWriter.WriteRstStreamAsync(ex.StreamId, ex.ErrorCode);
|
||||
}
|
||||
finally
|
||||
|
|
@ -269,7 +272,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
foreach (var stream in _streams.Values)
|
||||
{
|
||||
stream.Abort(connectionError);
|
||||
stream.Abort(new IOException(CoreStrings.Http2StreamAborted, connectionError));
|
||||
}
|
||||
|
||||
await _streamsCompleted.Task;
|
||||
|
|
@ -583,7 +586,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
|
||||
ThrowIfIncomingFrameSentToIdleStream();
|
||||
AbortStream(_incomingFrame.StreamId, new ConnectionAbortedException(CoreStrings.Http2StreamResetByClient));
|
||||
AbortStream(_incomingFrame.StreamId, new IOException(CoreStrings.Http2StreamResetByClient));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
|
@ -617,10 +620,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
try
|
||||
{
|
||||
// ParseFrame will not parse an InitialWindowSize > int.MaxValue.
|
||||
// int.MaxValue is the largest allowed windows size.
|
||||
var previousInitialWindowSize = (int)_clientSettings.InitialWindowSize;
|
||||
|
||||
_clientSettings.ParseFrame(_incomingFrame);
|
||||
_clientSettings.Update(_incomingFrame.GetSettings());
|
||||
|
||||
var ackTask = _frameWriter.WriteSettingsAckAsync(); // Ack before we update the windows, they could send data immediately.
|
||||
|
||||
|
|
@ -838,6 +841,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (_streams.Count >= _serverSettings.MaxConcurrentStreams)
|
||||
{
|
||||
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMaxStreams, Http2ErrorCode.REFUSED_STREAM);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
|
@ -885,7 +893,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
private void AbortStream(int streamId, ConnectionAbortedException error)
|
||||
private void AbortStream(int streamId, IOException error)
|
||||
{
|
||||
if (_streams.TryGetValue(streamId, out var stream))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
|
|
@ -11,13 +12,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
public class Http2ConnectionContext
|
||||
{
|
||||
public string ConnectionId { get; set; }
|
||||
public ConnectionContext ConnectionContext { get; set; }
|
||||
public ServiceContext ServiceContext { get; set; }
|
||||
public IFeatureCollection ConnectionFeatures { get; set; }
|
||||
public MemoryPool<byte> MemoryPool { get; set; }
|
||||
public IPEndPoint LocalEndPoint { get; set; }
|
||||
public IPEndPoint RemoteEndPoint { get; set; }
|
||||
|
||||
public IDuplexPipe Transport { get; set; }
|
||||
public IDuplexPipe Application { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public partial class Http2Frame
|
||||
{
|
||||
private const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value.
|
||||
|
||||
public Http2SettingsFrameFlags SettingsFlags
|
||||
{
|
||||
get => (Http2SettingsFrameFlags)Flags;
|
||||
set => Flags = (byte)value;
|
||||
}
|
||||
|
||||
public void PrepareSettings(Http2SettingsFrameFlags flags, Http2PeerSettings settings = null)
|
||||
public int SettingsCount
|
||||
{
|
||||
var settingCount = settings?.Count() ?? 0;
|
||||
get => Length / SettingSize;
|
||||
set => Length = value * SettingSize;
|
||||
}
|
||||
|
||||
Length = 6 * settingCount;
|
||||
public IList<Http2PeerSetting> GetSettings()
|
||||
{
|
||||
var settings = new Http2PeerSetting[SettingsCount];
|
||||
for (int i = 0; i < settings.Length; i++)
|
||||
{
|
||||
settings[i] = GetSetting(i);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private Http2PeerSetting GetSetting(int index)
|
||||
{
|
||||
var offset = index * SettingSize;
|
||||
var payload = Payload.Slice(offset);
|
||||
var id = (Http2SettingsParameter)BinaryPrimitives.ReadUInt16BigEndian(payload);
|
||||
var value = BinaryPrimitives.ReadUInt32BigEndian(payload.Slice(2));
|
||||
|
||||
return new Http2PeerSetting(id, value);
|
||||
}
|
||||
|
||||
public void PrepareSettings(Http2SettingsFrameFlags flags, IList<Http2PeerSetting> settings = null)
|
||||
{
|
||||
var settingCount = settings?.Count ?? 0;
|
||||
SettingsCount = settingCount;
|
||||
Type = Http2FrameType.SETTINGS;
|
||||
SettingsFlags = flags;
|
||||
StreamId = 0;
|
||||
|
||||
if (settings != null)
|
||||
for (int i = 0; i < settingCount; i++)
|
||||
{
|
||||
Span<byte> payload = Payload;
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
payload[0] = (byte)((ushort)setting.Parameter >> 8);
|
||||
payload[1] = (byte)(ushort)setting.Parameter;
|
||||
payload[2] = (byte)(setting.Value >> 24);
|
||||
payload[3] = (byte)(setting.Value >> 16);
|
||||
payload[4] = (byte)(setting.Value >> 8);
|
||||
payload[5] = (byte)setting.Value;
|
||||
payload = payload.Slice(6);
|
||||
}
|
||||
SetSetting(i, settings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSetting(int index, Http2PeerSetting setting)
|
||||
{
|
||||
var offset = index * SettingSize;
|
||||
var payload = Payload.Slice(offset);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(payload, (ushort)setting.Parameter);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(payload.Slice(2), setting.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
private readonly object _writeLock = new object();
|
||||
private readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
||||
private readonly PipeWriter _outputWriter;
|
||||
private readonly PipeReader _outputReader;
|
||||
private bool _aborted;
|
||||
private readonly ConnectionContext _connectionContext;
|
||||
private readonly OutputFlowControl _connectionOutputFlowControl;
|
||||
private readonly string _connectionId;
|
||||
private readonly IKestrelTrace _log;
|
||||
|
|
@ -36,15 +37,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public Http2FrameWriter(
|
||||
PipeWriter outputPipeWriter,
|
||||
PipeReader outputPipeReader,
|
||||
ConnectionContext connectionContext,
|
||||
OutputFlowControl connectionOutputFlowControl,
|
||||
ITimeoutControl timeoutControl,
|
||||
string connectionId,
|
||||
IKestrelTrace log)
|
||||
{
|
||||
_outputWriter = outputPipeWriter;
|
||||
_outputReader = outputPipeReader;
|
||||
|
||||
_connectionContext = connectionContext;
|
||||
_connectionOutputFlowControl = connectionOutputFlowControl;
|
||||
_connectionId = connectionId;
|
||||
_log = log;
|
||||
|
|
@ -66,11 +66,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
public void Abort(ConnectionAbortedException ex)
|
||||
public void Abort(ConnectionAbortedException error)
|
||||
{
|
||||
// TODO: Really abort the connection using the ConnectionContex like Http1OutputProducer.
|
||||
_outputReader.CancelPendingRead();
|
||||
Complete();
|
||||
lock (_writeLock)
|
||||
{
|
||||
if (_aborted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_aborted = true;
|
||||
_connectionContext.Abort(error);
|
||||
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
|
||||
public Task FlushAsync(IHttpOutputProducer outputProducer, CancellationToken cancellationToken)
|
||||
|
|
@ -239,7 +248,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
|
||||
// This awaitable releases continuations in FIFO order when the window updates.
|
||||
// It should be very rare for a continuation to run without any availability.
|
||||
// It should be very rare for a continuation to run without any availability.
|
||||
if (availabilityAwaitable != null)
|
||||
{
|
||||
await availabilityAwaitable;
|
||||
|
|
@ -270,12 +279,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
public Task WriteSettingsAsync(Http2PeerSettings settings)
|
||||
public Task WriteSettingsAsync(IList<Http2PeerSetting> settings)
|
||||
{
|
||||
lock (_writeLock)
|
||||
{
|
||||
// TODO: actually send settings
|
||||
_outgoingFrame.PrepareSettings(Http2SettingsFrameFlags.NONE);
|
||||
_outgoingFrame.PrepareSettings(Http2SettingsFrameFlags.NONE, settings);
|
||||
return WriteFrameUnsynchronizedAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
_context = context;
|
||||
}
|
||||
|
||||
protected override void OnReadStarting()
|
||||
{
|
||||
// Note ContentLength or MaxRequestBodySize may be null
|
||||
if (_context.RequestHeaders.ContentLength > _context.MaxRequestBodySize)
|
||||
{
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnReadStarted()
|
||||
{
|
||||
// Produce 100-continue if no request body data for the stream has arrived yet.
|
||||
|
|
@ -28,6 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
protected override void OnDataRead(int bytesRead)
|
||||
{
|
||||
_context.OnDataRead(bytesRead);
|
||||
AddAndCheckConsumedBytes(bytesRead);
|
||||
}
|
||||
|
||||
protected override Task OnConsumeAsync() => Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public class Http2PeerSettings : IEnumerable<Http2PeerSetting>
|
||||
public class Http2PeerSettings
|
||||
{
|
||||
// Note these are protocol defaults, not Kestrel defaults.
|
||||
public const uint DefaultHeaderTableSize = 4096;
|
||||
public const bool DefaultEnablePush = true;
|
||||
public const uint DefaultMaxConcurrentStreams = uint.MaxValue;
|
||||
|
|
@ -28,20 +28,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public uint MaxHeaderListSize { get; set; } = DefaultMaxHeaderListSize;
|
||||
|
||||
public void ParseFrame(Http2Frame frame)
|
||||
// TODO: Return the diff so we can react
|
||||
public void Update(IList<Http2PeerSetting> settings)
|
||||
{
|
||||
var settingsCount = frame.Length / 6;
|
||||
|
||||
for (var i = 0; i < settingsCount; i++)
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var offset = i * 6;
|
||||
var id = (Http2SettingsParameter)((frame.Payload[offset] << 8) | frame.Payload[offset + 1]);
|
||||
var value = (uint)((frame.Payload[offset + 2] << 24)
|
||||
| (frame.Payload[offset + 3] << 16)
|
||||
| (frame.Payload[offset + 4] << 8)
|
||||
| frame.Payload[offset + 5]);
|
||||
var value = setting.Value;
|
||||
|
||||
switch (id)
|
||||
switch (setting.Parameter)
|
||||
{
|
||||
case Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE:
|
||||
HeaderTableSize = value;
|
||||
|
|
@ -91,16 +85,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Http2PeerSetting> GetEnumerator()
|
||||
// Gets the settings that are different from the protocol defaults (as opposed to the server defaults).
|
||||
internal IList<Http2PeerSetting> GetNonProtocolDefaults()
|
||||
{
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, HeaderTableSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, EnablePush ? 1u : 0);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, MaxConcurrentStreams);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, InitialWindowSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE, MaxFrameSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, MaxHeaderListSize);
|
||||
}
|
||||
var list = new List<Http2PeerSetting>(1);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
if (HeaderTableSize != DefaultHeaderTableSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, HeaderTableSize));
|
||||
}
|
||||
|
||||
if (EnablePush != DefaultEnablePush)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, EnablePush ? 1u : 0));
|
||||
}
|
||||
|
||||
if (MaxConcurrentStreams != DefaultMaxConcurrentStreams)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, MaxConcurrentStreams));
|
||||
}
|
||||
|
||||
if (InitialWindowSize != DefaultInitialWindowSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, InitialWindowSize));
|
||||
}
|
||||
|
||||
if (MaxFrameSize != DefaultMaxFrameSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE, MaxFrameSize));
|
||||
}
|
||||
|
||||
if (MaxHeaderListSize != DefaultMaxHeaderListSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, MaxHeaderListSize));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
return _context.FrameWriter.TryUpdateStreamWindow(_outputFlowControl, bytes);
|
||||
}
|
||||
|
||||
public override void Abort(ConnectionAbortedException abortReason)
|
||||
public void Abort(IOException abortReason)
|
||||
{
|
||||
if (!TryApplyCompletionFlag(StreamCompletionFlags.Aborted))
|
||||
{
|
||||
|
|
@ -362,10 +362,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
AbortCore(abortReason);
|
||||
}
|
||||
|
||||
protected override void ErrorAfterResponseStarted()
|
||||
protected override void OnErrorAfterResponseStarted()
|
||||
{
|
||||
// We can no longer change the response, send a Reset instead.
|
||||
base.ErrorAfterResponseStarted();
|
||||
var abortReason = new ConnectionAbortedException(CoreStrings.Http2StreamErrorAfterHeaders);
|
||||
ResetAndAbort(abortReason, Http2ErrorCode.INTERNAL_ERROR);
|
||||
}
|
||||
|
|
@ -391,12 +390,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
AbortCore(abortReason);
|
||||
}
|
||||
|
||||
private void AbortCore(ConnectionAbortedException abortReason)
|
||||
private void AbortCore(Exception abortReason)
|
||||
{
|
||||
base.Abort(abortReason);
|
||||
// Call OnIOCompleted() which closes the output prior to poisoning the request body stream or pipe to
|
||||
// ensure that an app that completes early due to the abort doesn't result in header frames being sent.
|
||||
OnInputOrOutputCompleted();
|
||||
|
||||
// Unblock the request body.
|
||||
RequestBodyPipe.Writer.Complete(new IOException(CoreStrings.Http2StreamAborted, abortReason));
|
||||
PoisonRequestBodyStream(abortReason);
|
||||
RequestBodyPipe.Writer.Complete(abortReason);
|
||||
|
||||
_inputFlowControl.Abort();
|
||||
}
|
||||
|
|
@ -420,7 +422,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
var lastCompletionState = _completionState;
|
||||
_completionState |= completionState;
|
||||
|
||||
if (ShoulStopTrackingStream(_completionState) && !ShoulStopTrackingStream(lastCompletionState))
|
||||
if (ShouldStopTrackingStream(_completionState) && !ShouldStopTrackingStream(lastCompletionState))
|
||||
{
|
||||
_context.StreamLifetimeHandler.OnStreamCompleted(StreamId);
|
||||
}
|
||||
|
|
@ -429,7 +431,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
private static bool ShoulStopTrackingStream(StreamCompletionFlags completionState)
|
||||
private static bool ShouldStopTrackingStream(StreamCompletionFlags completionState)
|
||||
{
|
||||
// This could be a single condition, but I think it reads better as two if's.
|
||||
if ((completionState & StreamCompletionFlags.RequestProcessingEnded) == StreamCompletionFlags.RequestProcessingEnded)
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
// _adaptedTransport must be set prior to adding the connection to the manager in order
|
||||
// to allow the connection to be aported prior to protocol selection.
|
||||
_adaptedTransport = _context.Transport;
|
||||
var application = _context.Application;
|
||||
|
||||
|
||||
if (_context.ConnectionAdapters.Count > 0)
|
||||
|
|
@ -147,14 +146,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
case HttpProtocols.Http1:
|
||||
// _http1Connection must be initialized before adding the connection to the connection manager
|
||||
requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application);
|
||||
requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport);
|
||||
_protocolSelectionState = ProtocolSelectionState.Selected;
|
||||
break;
|
||||
case HttpProtocols.Http2:
|
||||
// _http2Connection must be initialized before yielding control to the transport thread,
|
||||
// to prevent a race condition where _http2Connection.Abort() is called just as
|
||||
// _http2Connection is about to be initialized.
|
||||
requestProcessor = CreateHttp2Connection(_adaptedTransport, application);
|
||||
requestProcessor = CreateHttp2Connection(_adaptedTransport);
|
||||
_protocolSelectionState = ProtocolSelectionState.Selected;
|
||||
break;
|
||||
case HttpProtocols.None:
|
||||
|
|
@ -199,13 +198,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
}
|
||||
|
||||
// For testing only
|
||||
internal void Initialize(IDuplexPipe transport, IDuplexPipe application)
|
||||
internal void Initialize(IDuplexPipe transport)
|
||||
{
|
||||
_requestProcessor = _http1Connection = CreateHttp1Connection(transport, application);
|
||||
_requestProcessor = _http1Connection = CreateHttp1Connection(transport);
|
||||
_protocolSelectionState = ProtocolSelectionState.Selected;
|
||||
}
|
||||
|
||||
private Http1Connection CreateHttp1Connection(IDuplexPipe transport, IDuplexPipe application)
|
||||
private Http1Connection CreateHttp1Connection(IDuplexPipe transport)
|
||||
{
|
||||
return new Http1Connection(new Http1ConnectionContext
|
||||
{
|
||||
|
|
@ -217,22 +216,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
ServiceContext = _context.ServiceContext,
|
||||
ConnectionContext = _context.ConnectionContext,
|
||||
TimeoutControl = this,
|
||||
Transport = transport,
|
||||
Application = application
|
||||
Transport = transport
|
||||
});
|
||||
}
|
||||
|
||||
private Http2Connection CreateHttp2Connection(IDuplexPipe transport, IDuplexPipe application)
|
||||
private Http2Connection CreateHttp2Connection(IDuplexPipe transport)
|
||||
{
|
||||
return new Http2Connection(new Http2ConnectionContext
|
||||
{
|
||||
ConnectionId = _context.ConnectionId,
|
||||
ConnectionContext = _context.ConnectionContext,
|
||||
ServiceContext = _context.ServiceContext,
|
||||
ConnectionFeatures = _context.ConnectionFeatures,
|
||||
MemoryPool = MemoryPool,
|
||||
LocalEndPoint = LocalEndPoint,
|
||||
RemoteEndPoint = RemoteEndPoint,
|
||||
Application = application,
|
||||
Transport = transport
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public IPEndPoint LocalEndPoint { get; set; }
|
||||
public IPEndPoint RemoteEndPoint { get; set; }
|
||||
public IDuplexPipe Transport { get; set; }
|
||||
public IDuplexPipe Application { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
// We need the transport feature so that we can cancel the output reader that the transport is using
|
||||
// This is a bit of a hack but it preserves the existing semantics
|
||||
var applicationFeature = connectionContext.Features.Get<IApplicationTransportFeature>();
|
||||
var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>();
|
||||
|
||||
var httpConnectionId = Interlocked.Increment(ref _lastHttpConnectionId);
|
||||
|
|
@ -52,8 +51,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
ConnectionFeatures = connectionContext.Features,
|
||||
MemoryPool = memoryPoolFeature.MemoryPool,
|
||||
ConnectionAdapters = _connectionAdapters,
|
||||
Transport = connectionContext.Transport,
|
||||
Application = applicationFeature.Application
|
||||
Transport = connectionContext.Transport
|
||||
};
|
||||
|
||||
var connectionFeature = connectionContext.Features.Get<IHttpConnectionFeature>();
|
||||
|
|
|
|||
|
|
@ -251,6 +251,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limits only applicable to HTTP/2 connections.
|
||||
/// </summary>
|
||||
public Http2Limits Http2 { get; } = new Http2Limits();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request body minimum data rate in bytes/second.
|
||||
/// Setting this property to null indicates no minimum data rate should be enforced.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
|
|||
[assembly: InternalsVisibleTo("InMemory.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Sockets.BindTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Libuv.BindTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Kestrel.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("PlatformBenchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -2058,20 +2058,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatHttp2StreamErrorSchemeMismatch(object requestScheme, object transportScheme)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("Http2StreamErrorSchemeMismatch", "requestScheme", "transportScheme"), requestScheme, transportScheme);
|
||||
|
||||
/// <summary>
|
||||
/// An error occured after the response headers were sent, a reset is being sent.
|
||||
/// </summary>
|
||||
internal static string Http2StreamErrorAfterHeaders
|
||||
{
|
||||
get => GetString("Http2StreamErrorAfterHeaders");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occured after the response headers were sent, a reset is being sent.
|
||||
/// </summary>
|
||||
internal static string FormatHttp2StreamErrorAfterHeaders()
|
||||
=> GetString("Http2StreamErrorAfterHeaders");
|
||||
|
||||
/// <summary>
|
||||
/// Less data received than specified in the Content-Length header.
|
||||
/// </summary>
|
||||
|
|
@ -2100,6 +2086,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatHttp2StreamErrorMoreDataThanLength()
|
||||
=> GetString("Http2StreamErrorMoreDataThanLength");
|
||||
|
||||
/// <summary>
|
||||
/// An error occured after the response headers were sent, a reset is being sent.
|
||||
/// </summary>
|
||||
internal static string Http2StreamErrorAfterHeaders
|
||||
{
|
||||
get => GetString("Http2StreamErrorAfterHeaders");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error occured after the response headers were sent, a reset is being sent.
|
||||
/// </summary>
|
||||
internal static string FormatHttp2StreamErrorAfterHeaders()
|
||||
=> GetString("Http2StreamErrorAfterHeaders");
|
||||
|
||||
/// <summary>
|
||||
/// A new stream was refused because this connection has reached its stream limit.
|
||||
/// </summary>
|
||||
internal static string Http2ErrorMaxStreams
|
||||
{
|
||||
get => GetString("Http2ErrorMaxStreams");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A new stream was refused because this connection has reached its stream limit.
|
||||
/// </summary>
|
||||
internal static string FormatHttp2ErrorMaxStreams()
|
||||
=> GetString("Http2ErrorMaxStreams");
|
||||
|
||||
/// <summary>
|
||||
/// A value greater than zero is required.
|
||||
/// </summary>
|
||||
internal static string GreaterThanZeroRequired
|
||||
{
|
||||
get => GetString("GreaterThanZeroRequired");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value greater than zero is required.
|
||||
/// </summary>
|
||||
internal static string FormatGreaterThanZeroRequired()
|
||||
=> GetString("GreaterThanZeroRequired");
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("InMemory.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
ConnectionFeatures = connectionFeatures,
|
||||
MemoryPool = _pipelineFactory,
|
||||
TimeoutControl = _timeoutControl.Object,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
ConnectionFeatures = connectionFeatures,
|
||||
MemoryPool = _memoryPool,
|
||||
HttpConnectionId = long.MinValue,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport,
|
||||
ServiceContext = new TestServiceContext
|
||||
{
|
||||
|
|
@ -66,7 +65,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockDebugger = new Mock<IDebugger>();
|
||||
mockDebugger.SetupGet(g => g.IsAttached).Returns(true);
|
||||
_httpConnection.Debugger = mockDebugger.Object;
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
|
||||
var now = DateTimeOffset.Now;
|
||||
_httpConnection.Tick(now);
|
||||
|
|
@ -113,7 +112,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
_httpConnectionContext.ServiceContext.Log = logger;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -140,7 +139,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -182,7 +181,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -259,7 +258,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -327,7 +326,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
// Initialize timestamp
|
||||
|
|
@ -389,7 +388,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
|
||||
var startTime = systemClock.UtcNow;
|
||||
|
|
@ -430,7 +429,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
@ -464,7 +463,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
@ -506,7 +505,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
@ -555,7 +554,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var mockLogger = new Mock<IKestrelTrace>();
|
||||
_httpConnectionContext.ServiceContext.Log = mockLogger.Object;
|
||||
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application);
|
||||
_httpConnection.Initialize(_httpConnectionContext.Transport);
|
||||
_httpConnection.Http1Connection.Reset();
|
||||
_httpConnection.Http1Connection.RequestAborted.Register(() =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = _memoryPool,
|
||||
TimeoutControl = _timeoutControl.Object,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
ServiceContext = new TestServiceContext(),
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
MemoryPool = memoryPool,
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport,
|
||||
TimeoutControl = null
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Microsoft.AspNetCore.Server.Kestrel.Core.Tests</AssemblyName>
|
||||
<AssemblyName>Core.Tests</AssemblyName>
|
||||
<RootNamespace>Microsoft.AspNetCore.Server.Kestrel.Core.Tests</RootNamespace>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
|
@ -18,10 +18,11 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -422,12 +422,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var options = new PipeOptions(pool: memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
|
||||
var pair = DuplexPipe.CreateConnectionPair(options, options);
|
||||
var transport = pair.Transport;
|
||||
var application = pair.Application;
|
||||
var http1ConnectionContext = new Http1ConnectionContext
|
||||
{
|
||||
ServiceContext = new TestServiceContext(),
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
Application = application,
|
||||
Transport = transport,
|
||||
MemoryPool = memoryPool,
|
||||
TimeoutControl = Mock.Of<ITimeoutControl>()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
|
||||
[assembly: ShortClassName]
|
||||
|
|
@ -38,7 +38,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
ServiceContext = new TestServiceContext(),
|
||||
ConnectionContext = Mock.Of<ConnectionContext>(),
|
||||
ConnectionFeatures = connectionFeatures,
|
||||
Application = Application,
|
||||
Transport = Transport,
|
||||
MemoryPool = _memoryPool,
|
||||
TimeoutControl = Mock.Of<ITimeoutControl>()
|
||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
private async Task TestBadRequest(string request, string expectedResponseStatusCode, string expectedExceptionMessage, string expectedAllowHeader = null)
|
||||
{
|
||||
BadHttpRequestException loggedException = null;
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(trace => trace.IsEnabled(LogLevel.Information))
|
||||
.Returns(true);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,868 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||
{
|
||||
public class Http2TestBase : TestApplicationErrorLoggerLoggedTest, IDisposable, IHttpHeadersHandler
|
||||
{
|
||||
protected static readonly string _largeHeaderValue = new string('a', HPackDecoder.MaxStringOctets);
|
||||
|
||||
protected static readonly IEnumerable<KeyValuePair<string, string>> _browserRequestHeaders = new[]
|
||||
{
|
||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Path, "/"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
|
||||
new KeyValuePair<string, string>("user-agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0"),
|
||||
new KeyValuePair<string, string>("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
|
||||
new KeyValuePair<string, string>("accept-language", "en-US,en;q=0.5"),
|
||||
new KeyValuePair<string, string>("accept-encoding", "gzip, deflate, br"),
|
||||
new KeyValuePair<string, string>("upgrade-insecure-requests", "1"),
|
||||
};
|
||||
|
||||
private readonly MemoryPool<byte> _memoryPool = KestrelMemoryPool.Create();
|
||||
internal readonly DuplexPipe.DuplexPipePair _pair;
|
||||
|
||||
protected readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
|
||||
protected readonly HPackEncoder _hpackEncoder = new HPackEncoder();
|
||||
protected readonly HPackDecoder _hpackDecoder;
|
||||
|
||||
protected readonly ConcurrentDictionary<int, TaskCompletionSource<object>> _runningStreams = new ConcurrentDictionary<int, TaskCompletionSource<object>>();
|
||||
protected readonly Dictionary<string, string> _receivedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
protected readonly Dictionary<string, string> _decodedHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
protected readonly HashSet<int> _abortedStreamIds = new HashSet<int>();
|
||||
protected readonly object _abortedStreamIdsLock = new object();
|
||||
protected readonly TaskCompletionSource<object> _closingStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
protected readonly TaskCompletionSource<object> _closedStateReached = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
protected readonly RequestDelegate _noopApplication;
|
||||
protected readonly RequestDelegate _readHeadersApplication;
|
||||
protected readonly RequestDelegate _readTrailersApplication;
|
||||
protected readonly RequestDelegate _bufferingApplication;
|
||||
protected readonly RequestDelegate _echoApplication;
|
||||
protected readonly RequestDelegate _echoWaitForAbortApplication;
|
||||
protected readonly RequestDelegate _largeHeadersApplication;
|
||||
protected readonly RequestDelegate _waitForAbortApplication;
|
||||
protected readonly RequestDelegate _waitForAbortFlushingApplication;
|
||||
protected readonly RequestDelegate _waitForAbortWithDataApplication;
|
||||
protected readonly RequestDelegate _echoMethod;
|
||||
protected readonly RequestDelegate _echoHost;
|
||||
protected readonly RequestDelegate _echoPath;
|
||||
|
||||
protected Http2ConnectionContext _connectionContext;
|
||||
protected Http2Connection _connection;
|
||||
protected Task _connectionTask;
|
||||
|
||||
public Http2TestBase()
|
||||
{
|
||||
// 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);
|
||||
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize);
|
||||
|
||||
_noopApplication = context => Task.CompletedTask;
|
||||
|
||||
_readHeadersApplication = context =>
|
||||
{
|
||||
foreach (var header in context.Request.Headers)
|
||||
{
|
||||
_receivedHeaders[header.Key] = header.Value.ToString();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_readTrailersApplication = async context =>
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
// Consuming the entire request body guarantees trailers will be available
|
||||
await context.Request.Body.CopyToAsync(ms);
|
||||
}
|
||||
|
||||
foreach (var header in context.Request.Headers)
|
||||
{
|
||||
_receivedHeaders[header.Key] = header.Value.ToString();
|
||||
}
|
||||
};
|
||||
|
||||
_bufferingApplication = async context =>
|
||||
{
|
||||
var data = new List<byte>();
|
||||
var buffer = new byte[1024];
|
||||
var received = 0;
|
||||
|
||||
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
data.AddRange(new ArraySegment<byte>(buffer, 0, received));
|
||||
}
|
||||
|
||||
await context.Response.Body.WriteAsync(data.ToArray(), 0, data.Count);
|
||||
};
|
||||
|
||||
_echoApplication = async context =>
|
||||
{
|
||||
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
|
||||
var received = 0;
|
||||
|
||||
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
await context.Response.Body.WriteAsync(buffer, 0, received);
|
||||
}
|
||||
};
|
||||
|
||||
_echoWaitForAbortApplication = async context =>
|
||||
{
|
||||
var buffer = new byte[Http2Frame.MinAllowedMaxFrameSize];
|
||||
var received = 0;
|
||||
|
||||
while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
await context.Response.Body.WriteAsync(buffer, 0, received);
|
||||
}
|
||||
|
||||
var sem = new SemaphoreSlim(0);
|
||||
|
||||
context.RequestAborted.Register(() =>
|
||||
{
|
||||
sem.Release();
|
||||
});
|
||||
|
||||
await sem.WaitAsync().DefaultTimeout();
|
||||
};
|
||||
|
||||
_largeHeadersApplication = context =>
|
||||
{
|
||||
foreach (var name in new[] { "a", "b", "c", "d", "e", "f", "g", "h" })
|
||||
{
|
||||
context.Response.Headers[name] = _largeHeaderValue;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_waitForAbortApplication = async context =>
|
||||
{
|
||||
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
|
||||
var sem = new SemaphoreSlim(0);
|
||||
|
||||
context.RequestAborted.Register(() =>
|
||||
{
|
||||
lock (_abortedStreamIdsLock)
|
||||
{
|
||||
_abortedStreamIds.Add(streamIdFeature.StreamId);
|
||||
}
|
||||
|
||||
sem.Release();
|
||||
});
|
||||
|
||||
await sem.WaitAsync().DefaultTimeout();
|
||||
|
||||
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
|
||||
};
|
||||
|
||||
_waitForAbortFlushingApplication = async context =>
|
||||
{
|
||||
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
|
||||
var sem = new SemaphoreSlim(0);
|
||||
|
||||
context.RequestAborted.Register(() =>
|
||||
{
|
||||
lock (_abortedStreamIdsLock)
|
||||
{
|
||||
_abortedStreamIds.Add(streamIdFeature.StreamId);
|
||||
}
|
||||
|
||||
sem.Release();
|
||||
});
|
||||
|
||||
await sem.WaitAsync().DefaultTimeout();
|
||||
|
||||
await context.Response.Body.FlushAsync();
|
||||
|
||||
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
|
||||
};
|
||||
|
||||
_waitForAbortWithDataApplication = async context =>
|
||||
{
|
||||
var streamIdFeature = context.Features.Get<IHttp2StreamIdFeature>();
|
||||
var sem = new SemaphoreSlim(0);
|
||||
|
||||
context.RequestAborted.Register(() =>
|
||||
{
|
||||
lock (_abortedStreamIdsLock)
|
||||
{
|
||||
_abortedStreamIds.Add(streamIdFeature.StreamId);
|
||||
}
|
||||
|
||||
sem.Release();
|
||||
});
|
||||
|
||||
await sem.WaitAsync().DefaultTimeout();
|
||||
|
||||
await context.Response.Body.WriteAsync(new byte[10], 0, 10);
|
||||
|
||||
_runningStreams[streamIdFeature.StreamId].TrySetResult(null);
|
||||
};
|
||||
|
||||
_echoMethod = context =>
|
||||
{
|
||||
context.Response.Headers["Method"] = context.Request.Method;
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_echoHost = context =>
|
||||
{
|
||||
context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_echoPath = context =>
|
||||
{
|
||||
context.Response.Headers["path"] = context.Request.Path.ToString();
|
||||
context.Response.Headers["rawtarget"] = context.Features.Get<IHttpRequestFeature>().RawTarget;
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
base.Initialize(methodInfo, testMethodArguments, testOutputHelper);
|
||||
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(m => m.Http2ConnectionClosing(It.IsAny<string>()))
|
||||
.Callback(() => _closingStateReached.SetResult(null));
|
||||
mockKestrelTrace
|
||||
.Setup(m => m.Http2ConnectionClosed(It.IsAny<string>(), It.IsAny<int>()))
|
||||
.Callback(() => _closedStateReached.SetResult(null));
|
||||
|
||||
_connectionContext = new Http2ConnectionContext
|
||||
{
|
||||
ConnectionContext = Mock.Of<ConnectionContext>(),
|
||||
ConnectionFeatures = new FeatureCollection(),
|
||||
ServiceContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object),
|
||||
MemoryPool = _memoryPool,
|
||||
Transport = _pair.Transport
|
||||
};
|
||||
|
||||
_connection = new Http2Connection(_connectionContext);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_pair.Application.Input.Complete();
|
||||
_pair.Application.Output.Complete();
|
||||
_pair.Transport.Input.Complete();
|
||||
_pair.Transport.Output.Complete();
|
||||
_memoryPool.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
void IHttpHeadersHandler.OnHeader(Span<byte> name, Span<byte> value)
|
||||
{
|
||||
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetAsciiStringNonNullCharacters();
|
||||
}
|
||||
|
||||
protected async Task InitializeConnectionAsync(RequestDelegate application)
|
||||
{
|
||||
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application));
|
||||
|
||||
await SendPreambleAsync().ConfigureAwait(false);
|
||||
await SendSettingsAsync();
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 6,
|
||||
withFlags: 0,
|
||||
withStreamId: 0);
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2SettingsFrameFlags.ACK,
|
||||
withStreamId: 0);
|
||||
}
|
||||
|
||||
protected async Task StartStreamAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, bool endStream)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_runningStreams[streamId] = tcs;
|
||||
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
|
||||
var done = _hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
|
||||
frame.Length = length;
|
||||
|
||||
if (done)
|
||||
{
|
||||
frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
|
||||
}
|
||||
|
||||
if (endStream)
|
||||
{
|
||||
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
|
||||
}
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
|
||||
done = _hpackEncoder.Encode(frame.HeadersPayload, out length);
|
||||
frame.Length = length;
|
||||
|
||||
if (done)
|
||||
{
|
||||
frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
|
||||
}
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task SendHeadersWithPaddingAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, bool endStream)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_runningStreams[streamId] = tcs;
|
||||
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED, streamId);
|
||||
frame.HeadersPadLength = padLength;
|
||||
|
||||
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
|
||||
|
||||
frame.Length = 1 + length + padLength;
|
||||
frame.Payload.Slice(1 + length).Fill(0);
|
||||
|
||||
if (endStream)
|
||||
{
|
||||
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
|
||||
}
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected async Task SendHeadersWithPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte priority, int streamDependency, bool endStream)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_runningStreams[streamId] = tcs;
|
||||
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PRIORITY, streamId);
|
||||
frame.HeadersPriority = priority;
|
||||
frame.HeadersStreamDependency = streamDependency;
|
||||
|
||||
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
|
||||
|
||||
frame.Length = 5 + length;
|
||||
|
||||
if (endStream)
|
||||
{
|
||||
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
|
||||
}
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected async Task SendHeadersWithPaddingAndPriorityAsync(int streamId, IEnumerable<KeyValuePair<string, string>> headers, byte padLength, byte priority, int streamDependency, bool endStream)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
_runningStreams[streamId] = tcs;
|
||||
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.PADDED | Http2HeadersFrameFlags.PRIORITY, streamId);
|
||||
frame.HeadersPadLength = padLength;
|
||||
frame.HeadersPriority = priority;
|
||||
frame.HeadersStreamDependency = streamDependency;
|
||||
|
||||
_hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);
|
||||
|
||||
frame.Length = 6 + length + padLength;
|
||||
frame.Payload.Slice(6 + length).Fill(0);
|
||||
|
||||
if (endStream)
|
||||
{
|
||||
frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
|
||||
}
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task WaitForAllStreamsAsync()
|
||||
{
|
||||
return Task.WhenAll(_runningStreams.Values.Select(tcs => tcs.Task)).DefaultTimeout();
|
||||
}
|
||||
|
||||
protected Task SendAsync(ReadOnlySpan<byte> span)
|
||||
{
|
||||
var writableBuffer = _pair.Application.Output;
|
||||
writableBuffer.Write(span);
|
||||
return FlushAsync(writableBuffer);
|
||||
}
|
||||
|
||||
protected static async Task FlushAsync(PipeWriter writableBuffer)
|
||||
{
|
||||
await writableBuffer.FlushAsync();
|
||||
}
|
||||
|
||||
protected Task SendPreambleAsync() => SendAsync(new ArraySegment<byte>(Http2Connection.ClientPreface));
|
||||
|
||||
protected Task SendSettingsAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendSettingsAckWithInvalidLengthAsync(int length)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.ACK);
|
||||
frame.Length = length;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendSettingsWithInvalidStreamIdAsync(int streamId)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
frame.StreamId = streamId;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendSettingsWithInvalidLengthAsync(int length)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
frame.Length = length;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendSettingsWithInvalidParameterValueAsync(Http2SettingsParameter parameter, uint value)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
|
||||
frame.Length = 6;
|
||||
|
||||
frame.Payload[0] = (byte)((ushort)parameter >> 8);
|
||||
frame.Payload[1] = (byte)(ushort)parameter;
|
||||
frame.Payload[2] = (byte)(value >> 24);
|
||||
frame.Payload[3] = (byte)(value >> 16);
|
||||
frame.Payload[4] = (byte)(value >> 8);
|
||||
frame.Payload[5] = (byte)value;
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendPushPromiseFrameAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.Length = 0;
|
||||
frame.Type = Http2FrameType.PUSH_PROMISE;
|
||||
frame.StreamId = 1;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected async Task<bool> SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, IEnumerable<KeyValuePair<string, string>> headers)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareHeaders(flags, streamId);
|
||||
var done = _hpackEncoder.BeginEncode(headers, frame.Payload, out var length);
|
||||
frame.Length = length;
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
protected Task SendHeadersAsync(int streamId, Http2HeadersFrameFlags flags, byte[] headerBlock)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareHeaders(flags, streamId);
|
||||
frame.Length = headerBlock.Length;
|
||||
headerBlock.CopyTo(frame.HeadersPayload);
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidHeadersFrameAsync(int streamId, int frameLength, byte padLength)
|
||||
{
|
||||
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
|
||||
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.PADDED, streamId);
|
||||
frame.Payload[0] = padLength;
|
||||
|
||||
// Set length last so .Payload can be written to
|
||||
frame.Length = frameLength;
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendIncompleteHeadersFrameAsync(int streamId)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareHeaders(Http2HeadersFrameFlags.END_HEADERS, streamId);
|
||||
frame.Length = 3;
|
||||
|
||||
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
|
||||
// with an incomplete new name
|
||||
frame.Payload[0] = 0;
|
||||
frame.Payload[1] = 2;
|
||||
frame.Payload[2] = (byte)'a';
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected async Task<bool> SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareContinuation(flags, streamId);
|
||||
var done = _hpackEncoder.Encode(frame.Payload, out var length);
|
||||
frame.Length = length;
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
protected async Task SendContinuationAsync(int streamId, Http2ContinuationFrameFlags flags, byte[] payload)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareContinuation(flags, streamId);
|
||||
frame.Length = payload.Length;
|
||||
payload.CopyTo(frame.Payload);
|
||||
|
||||
await SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendEmptyContinuationFrameAsync(int streamId, Http2ContinuationFrameFlags flags)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareContinuation(flags, streamId);
|
||||
frame.Length = 0;
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendIncompleteContinuationFrameAsync(int streamId)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareContinuation(Http2ContinuationFrameFlags.END_HEADERS, streamId);
|
||||
frame.Length = 3;
|
||||
|
||||
// Set up an incomplete Literal Header Field w/ Incremental Indexing frame,
|
||||
// with an incomplete new name
|
||||
frame.Payload[0] = 0;
|
||||
frame.Payload[1] = 2;
|
||||
frame.Payload[2] = (byte)'a';
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendDataAsync(int streamId, Span<byte> data, bool endStream)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareData(streamId);
|
||||
frame.Length = data.Length;
|
||||
frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
|
||||
data.CopyTo(frame.DataPayload);
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendDataWithPaddingAsync(int streamId, Span<byte> data, byte padLength, bool endStream)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareData(streamId, padLength);
|
||||
frame.Length = data.Length + 1 + padLength;
|
||||
data.CopyTo(frame.DataPayload);
|
||||
|
||||
if (endStream)
|
||||
{
|
||||
frame.DataFlags |= Http2DataFrameFlags.END_STREAM;
|
||||
}
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidDataFrameAsync(int streamId, int frameLength, byte padLength)
|
||||
{
|
||||
Assert.True(padLength >= frameLength, $"{nameof(padLength)} must be greater than or equal to {nameof(frameLength)} to create an invalid frame.");
|
||||
|
||||
var frame = new Http2Frame();
|
||||
|
||||
frame.PrepareData(streamId);
|
||||
frame.DataFlags = Http2DataFrameFlags.PADDED;
|
||||
frame.Payload[0] = padLength;
|
||||
|
||||
// Set length last so .Payload can be written to
|
||||
frame.Length = frameLength;
|
||||
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendPingAsync(Http2PingFrameFlags flags)
|
||||
{
|
||||
var pingFrame = new Http2Frame();
|
||||
pingFrame.PreparePing(flags);
|
||||
return SendAsync(pingFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendPingWithInvalidLengthAsync(int length)
|
||||
{
|
||||
var pingFrame = new Http2Frame();
|
||||
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
|
||||
pingFrame.Length = length;
|
||||
return SendAsync(pingFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendPingWithInvalidStreamIdAsync(int streamId)
|
||||
{
|
||||
Assert.NotEqual(0, streamId);
|
||||
|
||||
var pingFrame = new Http2Frame();
|
||||
pingFrame.PreparePing(Http2PingFrameFlags.NONE);
|
||||
pingFrame.StreamId = streamId;
|
||||
return SendAsync(pingFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendPriorityAsync(int streamId, int streamDependency = 0)
|
||||
{
|
||||
var priorityFrame = new Http2Frame();
|
||||
priorityFrame.PreparePriority(streamId, streamDependency: streamDependency, exclusive: false, weight: 0);
|
||||
return SendAsync(priorityFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidPriorityFrameAsync(int streamId, int length)
|
||||
{
|
||||
var priorityFrame = new Http2Frame();
|
||||
priorityFrame.PreparePriority(streamId, streamDependency: 0, exclusive: false, weight: 0);
|
||||
priorityFrame.Length = length;
|
||||
return SendAsync(priorityFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendRstStreamAsync(int streamId)
|
||||
{
|
||||
var rstStreamFrame = new Http2Frame();
|
||||
rstStreamFrame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
|
||||
return SendAsync(rstStreamFrame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidRstStreamFrameAsync(int streamId, int length)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareRstStream(streamId, Http2ErrorCode.CANCEL);
|
||||
frame.Length = length;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendGoAwayAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidGoAwayFrameAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareGoAway(0, Http2ErrorCode.NO_ERROR);
|
||||
frame.StreamId = 1;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendWindowUpdateAsync(int streamId, int sizeIncrement)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareWindowUpdate(streamId, sizeIncrement);
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendInvalidWindowUpdateAsync(int streamId, int sizeIncrement, int length)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareWindowUpdate(streamId, sizeIncrement);
|
||||
frame.Length = length;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected Task SendUnknownFrameTypeAsync(int streamId, int frameType)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.StreamId = streamId;
|
||||
frame.Type = (Http2FrameType)frameType;
|
||||
frame.Length = 0;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
protected async Task<Http2Frame> ReceiveFrameAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var result = await _pair.Application.Input.ReadAsync().AsTask().DefaultTimeout();
|
||||
var buffer = result.Buffer;
|
||||
var consumed = buffer.Start;
|
||||
var examined = buffer.End;
|
||||
|
||||
try
|
||||
{
|
||||
Assert.True(buffer.Length > 0);
|
||||
|
||||
if (Http2FrameReader.ReadFrame(buffer, frame, 16_384, out consumed, out examined))
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
throw new IOException("The reader completed without returning a frame.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pair.Application.Input.AdvanceTo(consumed, examined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task<Http2Frame> ExpectAsync(Http2FrameType type, int withLength, byte withFlags, int withStreamId)
|
||||
{
|
||||
var frame = await ReceiveFrameAsync();
|
||||
|
||||
Assert.Equal(type, frame.Type);
|
||||
Assert.Equal(withLength, frame.Length);
|
||||
Assert.Equal(withFlags, frame.Flags);
|
||||
Assert.Equal(withStreamId, frame.StreamId);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
protected Task StopConnectionAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
|
||||
{
|
||||
_pair.Application.Output.Complete();
|
||||
|
||||
return WaitForConnectionStopAsync(expectedLastStreamId, ignoreNonGoAwayFrames);
|
||||
}
|
||||
|
||||
protected Task WaitForConnectionStopAsync(int expectedLastStreamId, bool ignoreNonGoAwayFrames)
|
||||
{
|
||||
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR, expectedErrorMessage: null);
|
||||
}
|
||||
|
||||
protected void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
||||
{
|
||||
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
|
||||
Assert.Equal(8, frame.Length);
|
||||
Assert.Equal(0, frame.Flags);
|
||||
Assert.Equal(0, frame.StreamId);
|
||||
Assert.Equal(expectedLastStreamId, frame.GoAwayLastStreamId);
|
||||
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
|
||||
}
|
||||
|
||||
protected async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
|
||||
where TException : Exception
|
||||
{
|
||||
var frame = await ReceiveFrameAsync();
|
||||
|
||||
if (ignoreNonGoAwayFrames)
|
||||
{
|
||||
while (frame.Type != Http2FrameType.GOAWAY)
|
||||
{
|
||||
frame = await ReceiveFrameAsync();
|
||||
}
|
||||
}
|
||||
|
||||
VerifyGoAway(frame, expectedLastStreamId, expectedErrorCode);
|
||||
|
||||
if (expectedErrorMessage != null)
|
||||
{
|
||||
var message = Assert.Single(TestApplicationErrorLogger.Messages, m => m.Exception is TException);
|
||||
Assert.Contains(expectedErrorMessage, message.Exception.Message);
|
||||
}
|
||||
|
||||
await _connectionTask;
|
||||
_pair.Application.Output.Complete();
|
||||
}
|
||||
|
||||
protected async Task WaitForStreamErrorAsync(int expectedStreamId, Http2ErrorCode expectedErrorCode, string expectedErrorMessage)
|
||||
{
|
||||
var frame = await ReceiveFrameAsync();
|
||||
|
||||
Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
|
||||
Assert.Equal(4, frame.Length);
|
||||
Assert.Equal(0, frame.Flags);
|
||||
Assert.Equal(expectedStreamId, frame.StreamId);
|
||||
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
|
||||
|
||||
if (expectedErrorMessage != null)
|
||||
{
|
||||
Assert.Contains(TestApplicationErrorLogger.Messages, m => m.Exception?.Message.Contains(expectedErrorMessage) ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void VerifyDecodedRequestHeaders(IEnumerable<KeyValuePair<string, string>> expectedHeaders)
|
||||
{
|
||||
foreach (var header in expectedHeaders)
|
||||
{
|
||||
Assert.True(_receivedHeaders.TryGetValue(header.Key, out var value), header.Key);
|
||||
Assert.Equal(header.Value, value, ignoreCase: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,8 +37,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
[Fact]
|
||||
public Task Server_Http2Only_Cleartext_Success()
|
||||
{
|
||||
// Expect a SETTINGS frame (type 0x4) with no payload and no flags
|
||||
return TestSuccess(HttpProtocols.Http2, Encoding.ASCII.GetString(Http2Connection.ClientPreface), "\x00\x00\x00\x04\x00\x00\x00\x00\x00");
|
||||
// Expect a SETTINGS frame (type 0x4) with default settings
|
||||
return TestSuccess(HttpProtocols.Http2, Encoding.ASCII.GetString(Http2Connection.ClientPreface),
|
||||
"\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x64");
|
||||
}
|
||||
|
||||
private async Task TestSuccess(HttpProtocols serverProtocols, string request, string expectedResponse)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\shared\*.cs" LinkBase="shared" />
|
||||
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
const string response = "hello, world";
|
||||
|
||||
var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(trace => trace.ConnectionHeadResponseBodyWrite(It.IsAny<string>(), response.Length))
|
||||
.Callback<string, long>((connectionId, count) => logTcs.SetResult(null));
|
||||
|
|
@ -610,7 +610,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
public async Task WhenAppWritesLessThanContentLengthErrorLogged()
|
||||
{
|
||||
var logTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var mockTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockTrace = new Mock<IKestrelTrace>();
|
||||
mockTrace
|
||||
.Setup(trace => trace.ApplicationError(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<InvalidOperationException>()))
|
||||
.Callback<string, string, Exception>((connectionId, requestId, ex) =>
|
||||
|
|
@ -663,7 +663,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
public async Task WhenAppWritesLessThanContentLengthButRequestIsAbortedErrorNotLogged()
|
||||
{
|
||||
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var mockTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockTrace = new Mock<IKestrelTrace>();
|
||||
|
||||
using (var server = new TestServer(async httpContext =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
|
||||
<ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -318,8 +318,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
.Callback<LogLevel, EventId, object, Exception, Func<object, Exception, string>>((logLevel, eventId, state, exception, formatter) =>
|
||||
{
|
||||
Logger.Log(logLevel, eventId, state, exception, formatter);
|
||||
var log = $"Log {logLevel}[{eventId}]: {formatter(state, exception)} {exception}";
|
||||
TestOutputHelper.WriteLine(log);
|
||||
|
||||
if (eventId.Id == _connectionResetEventId)
|
||||
{
|
||||
|
|
@ -707,10 +705,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets")))
|
||||
.Returns(mockLogger.Object);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var testContext = new TestServiceContext(mockLoggerFactory.Object)
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
var testContext = new TestServiceContext(mockLoggerFactory.Object, mockKestrelTrace.Object)
|
||||
{
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -765,11 +762,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var readTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var appStartedTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var testContext = new TestServiceContext()
|
||||
{
|
||||
Log = mockKestrelTrace.Object,
|
||||
};
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object);
|
||||
|
||||
var scratchBuffer = new byte[4096];
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
|
|
@ -250,7 +249,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var clientClosedConnection = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var writeTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
var mockLogger = new Mock<ILogger>();
|
||||
mockLogger
|
||||
.Setup(logger => logger.IsEnabled(It.IsAny<LogLevel>()))
|
||||
|
|
@ -276,9 +275,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
"Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets")))
|
||||
.Returns(mockLogger.Object);
|
||||
|
||||
var testContext = new TestServiceContext(mockLoggerFactory.Object)
|
||||
var testContext = new TestServiceContext(mockLoggerFactory.Object, mockKestrelTrace.Object)
|
||||
{
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -470,7 +468,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
|
||||
|
|
@ -478,10 +476,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
.Setup(trace => trace.ConnectionStop(It.IsAny<string>()))
|
||||
.Callback(() => connectionStopMessageLogged.SetResult(null));
|
||||
|
||||
var testContext = new TestServiceContext
|
||||
var testContext = new TestServiceContext(loggerFactory, mockKestrelTrace.Object)
|
||||
{
|
||||
LoggerFactory = loggerFactory,
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -539,7 +535,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
await responseRateTimeoutMessageLogged.Task.DefaultTimeout();
|
||||
await connectionStopMessageLogged.Task.DefaultTimeout();
|
||||
await appFuncCompleted.Task.DefaultTimeout();
|
||||
await AssertStreamAborted(connection.Reader.BaseStream, chunkSize * chunks);
|
||||
await AssertStreamAborted(connection.Stream, chunkSize * chunks);
|
||||
|
||||
sw.Stop();
|
||||
logger.LogInformation("Connection was aborted after {totalMilliseconds}ms.", sw.ElapsedMilliseconds);
|
||||
|
|
@ -562,7 +558,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var aborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
|
||||
|
|
@ -616,7 +612,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
using (var sslStream = new SslStream(connection.Reader.BaseStream, false, (sender, cert, chain, errors) => true, null))
|
||||
using (var sslStream = new SslStream(connection.Stream, false, (sender, cert, chain, errors) => true, null))
|
||||
{
|
||||
await sslStream.AuthenticateAsClientAsync("localhost", new X509CertificateCollection(), SslProtocols.Tls12 | SslProtocols.Tls11, false);
|
||||
|
||||
|
|
@ -628,7 +624,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
await connectionStopMessageLogged.Task.DefaultTimeout();
|
||||
await appFuncCompleted.Task.DefaultTimeout();
|
||||
|
||||
await AssertStreamAborted(connection.Reader.BaseStream, chunkSize * chunks);
|
||||
await AssertStreamAborted(connection.Stream, chunkSize * chunks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -647,7 +643,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var requestAborted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var copyToAsyncCts = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
mockKestrelTrace
|
||||
.Setup(trace => trace.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback(() => responseRateTimeoutMessageLogged.SetResult(null));
|
||||
|
|
@ -655,10 +651,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
.Setup(trace => trace.ConnectionStop(It.IsAny<string>()))
|
||||
.Callback(() => connectionStopMessageLogged.SetResult(null));
|
||||
|
||||
var testContext = new TestServiceContext
|
||||
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
|
||||
{
|
||||
LoggerFactory = LoggerFactory,
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -734,11 +728,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
var requestAborted = false;
|
||||
var appFuncCompleted = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
|
||||
var testContext = new TestServiceContext
|
||||
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
|
||||
{
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -783,7 +776,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
// Make sure consuming a single chunk exceeds the 2 second timeout.
|
||||
var targetBytesPerSecond = chunkSize / 4;
|
||||
await AssertStreamCompleted(connection.Reader.BaseStream, minTotalOutputSize, targetBytesPerSecond);
|
||||
await AssertStreamCompleted(connection.Stream, minTotalOutputSize, targetBytesPerSecond);
|
||||
await appFuncCompleted.Task.DefaultTimeout();
|
||||
|
||||
mockKestrelTrace.Verify(t => t.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
|
|
@ -803,11 +796,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
var headerStringValues = new StringValues(Enumerable.Repeat(headerValue, headerCount).ToArray());
|
||||
|
||||
var requestAborted = false;
|
||||
var mockKestrelTrace = new Mock<KestrelTrace>(Logger) { CallBase = true };
|
||||
var mockKestrelTrace = new Mock<IKestrelTrace>();
|
||||
|
||||
var testContext = new TestServiceContext
|
||||
var testContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object)
|
||||
{
|
||||
Log = mockKestrelTrace.Object,
|
||||
ServerOptions =
|
||||
{
|
||||
Limits =
|
||||
|
|
@ -860,7 +852,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
// Make sure consuming a single set of response headers exceeds the 2 second timeout.
|
||||
var targetBytesPerSecond = responseSize / 4;
|
||||
await AssertStreamCompleted(connection.Reader.BaseStream, minTotalOutputSize, targetBytesPerSecond);
|
||||
await AssertStreamCompleted(connection.Stream, minTotalOutputSize, targetBytesPerSecond);
|
||||
|
||||
mockKestrelTrace.Verify(t => t.ResponseMininumDataRateNotSatisfied(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
mockKestrelTrace.Verify(t => t.ConnectionStop(It.IsAny<string>()), Times.Once());
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
<Compile Include="..\Kestrel.Transport.BindTests\**\*.cs" />
|
||||
<Compile Include="..\Kestrel.Transport.Libuv.FunctionalTests\TransportSelector.cs" />
|
||||
<Compile Include="..\shared\*.cs" LinkBase="shared" />
|
||||
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
|
||||
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="..\Kestrel.Transport.FunctionalTests\**\*.cs" />
|
||||
<Compile Include="..\shared\*.cs" LinkBase="shared" />
|
||||
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
|
||||
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -743,7 +743,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
|||
ConnectionFeatures = connectionFeatures,
|
||||
MemoryPool = _memoryPool,
|
||||
TimeoutControl = Mock.Of<ITimeoutControl>(),
|
||||
Application = pair.Application,
|
||||
Transport = pair.Transport
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
<Compile Include="..\Kestrel.Transport.BindTests\**\*.cs" />
|
||||
<Compile Include="..\Kestrel.Transport.Sockets.FunctionalTests\TransportSelector.cs" />
|
||||
<Compile Include="..\shared\*.cs" LinkBase="shared" />
|
||||
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
|
||||
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="..\Kestrel.Transport.FunctionalTests\**\*.cs" />
|
||||
<Compile Include="..\shared\*.cs" LinkBase="shared" />
|
||||
<Compile Include="..\shared\FunctionalTestHelpers\*.cs" LinkBase="shared\FunctionalTestHelpers" />
|
||||
<Compile Include="..\shared\TransportTestHelpers\*.cs" LinkBase="shared\TransportTestHelpers" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" LinkBase="shared\TestCertificates" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
|
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
|
|
@ -85,11 +86,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
.Build();
|
||||
|
||||
_host.Start();
|
||||
|
||||
Context.Log.LogDebug($"TestServer is listening on port {Port}");
|
||||
}
|
||||
|
||||
public IPEndPoint EndPoint => _listenOptions.IPEndPoint;
|
||||
public int Port => _listenOptions.IPEndPoint.Port;
|
||||
public AddressFamily AddressFamily => _listenOptions.IPEndPoint.AddressFamily;
|
||||
// Avoid NullReferenceException in the CanListenToOpenTcpSocketHandle test
|
||||
public int Port => _listenOptions.IPEndPoint?.Port ?? 0;
|
||||
|
||||
public TestServiceContext Context { get; }
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
public TestConnection CreateConnection()
|
||||
{
|
||||
return new TestConnection(Port, AddressFamily);
|
||||
return new TestConnection(Port, _listenOptions.IPEndPoint.AddressFamily);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken token = default)
|
||||
|
|
|
|||
Loading…
Reference in New Issue