File: Http1\Http1HttpProtocolFeatureCollectionTests.cs
Web Access
Project: src\src\Servers\Kestrel\Core\test\Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj (Microsoft.AspNetCore.Server.Kestrel.Core.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO.Pipelines;
using System.Linq;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.InternalTesting;
using Moq;
using Xunit;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests;
 
public class Http1HttpProtocolFeatureCollectionTests
{
    private readonly TestHttp1Connection _http1Connection;
    private readonly HttpConnectionContext _httpConnectionContext;
    private readonly IFeatureCollection _collection;
 
    public Http1HttpProtocolFeatureCollectionTests()
    {
        var connectionContext = Mock.Of<ConnectionContext>();
        var metricsContext = TestContextFactory.CreateMetricsContext(connectionContext);
 
        var connectionFeatures = new FeatureCollection();
        connectionFeatures.Set<IConnectionMetricsContextFeature>(new TestConnectionMetricsContextFeature { MetricsContext = metricsContext });
 
        var context = TestContextFactory.CreateHttpConnectionContext(
            connectionContext: connectionContext,
            serviceContext: new TestServiceContext(),
            transport: Mock.Of<IDuplexPipe>(),
            connectionFeatures: connectionFeatures,
            timeoutControl: Mock.Of<ITimeoutControl>(),
            metricsContext: metricsContext);
 
        _httpConnectionContext = context;
        _http1Connection = new TestHttp1Connection(context);
        _http1Connection.Reset();
        _collection = _http1Connection;
    }
 
    [Fact]
    public void FeaturesStartAsSelf()
    {
        var featureCount = GetFeaturesCount();
 
        Assert.NotEqual(0, featureCount);
    }
 
    [Fact]
    public void FeaturesCanBeAssignedTo()
    {
        var featureCount = SetFeaturesToNonDefault();
        Assert.NotEqual(0, featureCount);
 
        featureCount = 0;
        foreach (var feature in _collection)
        {
            Type type = feature.Key;
            if (type.IsAssignableFrom(typeof(HttpProtocol)))
            {
                Assert.Same(_collection[type], feature.Value);
                Assert.NotSame(_collection[type], _collection);
                featureCount++;
            }
        }
 
        Assert.NotEqual(0, featureCount);
    }
 
    [Fact]
    public void FeaturesResetToSelf()
    {
        var featuresAssigned = SetFeaturesToNonDefault();
        _http1Connection.ResetFeatureCollection();
        var featuresReset = GetFeaturesCount();
 
        Assert.Equal(featuresAssigned, featuresReset);
    }
 
    [Fact]
    public void FeaturesByGenericSameAsByType()
    {
        var featuresAssigned = SetFeaturesToNonDefault();
 
        CompareGenericGetterToIndexer();
 
        _http1Connection.ResetFeatureCollection();
        var featuresReset = GetFeaturesCount();
 
        Assert.Equal(featuresAssigned, featuresReset);
    }
 
    [Fact]
    public void FeaturesSetByTypeSameAsGeneric()
    {
        _collection[typeof(IHttpRequestFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpRequestBodyDetectionFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpResponseFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpResponseBodyFeature)] = CreateHttp1Connection();
        _collection[typeof(IRequestBodyPipeFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpRequestIdentifierFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpRequestLifetimeFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpRequestTrailersFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpConnectionFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpMaxRequestBodySizeFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpMinRequestBodyDataRateFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpMinResponseDataRateFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpBodyControlFeature)] = CreateHttp1Connection();
        _collection[typeof(IRouteValuesFeature)] = CreateHttp1Connection();
        _collection[typeof(IEndpointFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpExtendedConnectFeature)] = CreateHttp1Connection();
        _collection[typeof(IHttpUpgradeFeature)] = CreateHttp1Connection();
        _collection[typeof(IPersistentStateFeature)] = CreateHttp1Connection();
#pragma warning disable CA2252 // WebTransport is a preview feature
        _collection.Set<IHttpWebTransportFeature>(CreateHttp1Connection());
#pragma warning restore CA2252 // WebTransport is a preview feature
 
        CompareGenericGetterToIndexer();
 
        EachHttpProtocolFeatureSetAndUnique();
    }
 
    [Fact]
    public void FeaturesSetByGenericSameAsByType()
    {
        _collection.Set<IHttpRequestFeature>(CreateHttp1Connection());
        _collection.Set<IHttpRequestBodyDetectionFeature>(CreateHttp1Connection());
        _collection.Set<IHttpResponseFeature>(CreateHttp1Connection());
        _collection.Set<IHttpResponseBodyFeature>(CreateHttp1Connection());
        _collection.Set<IRequestBodyPipeFeature>(CreateHttp1Connection());
        _collection.Set<IHttpRequestIdentifierFeature>(CreateHttp1Connection());
        _collection.Set<IHttpRequestLifetimeFeature>(CreateHttp1Connection());
        _collection.Set<IHttpRequestTrailersFeature>(CreateHttp1Connection());
        _collection.Set<IHttpConnectionFeature>(CreateHttp1Connection());
        _collection.Set<IHttpMaxRequestBodySizeFeature>(CreateHttp1Connection());
        _collection.Set<IHttpMinRequestBodyDataRateFeature>(CreateHttp1Connection());
        _collection.Set<IHttpMinResponseDataRateFeature>(CreateHttp1Connection());
        _collection.Set<IHttpBodyControlFeature>(CreateHttp1Connection());
        _collection.Set<IRouteValuesFeature>(CreateHttp1Connection());
        _collection.Set<IEndpointFeature>(CreateHttp1Connection());
        _collection.Set<IHttpExtendedConnectFeature>(CreateHttp1Connection());
        _collection.Set<IHttpUpgradeFeature>(CreateHttp1Connection());
        _collection.Set<IPersistentStateFeature>(CreateHttp1Connection());
#pragma warning disable CA2252 // WebTransport is a preview feature
        _collection.Set<IHttpWebTransportFeature>(CreateHttp1Connection());
#pragma warning restore CA2252 // WebTransport is a preview feature
 
        CompareGenericGetterToIndexer();
 
        EachHttpProtocolFeatureSetAndUnique();
    }
 
    [Fact]
    public void Http1HasIHttpMinResponseDataRateFeature()
    {
        Assert.NotNull(_collection.Get<IHttpMinResponseDataRateFeature>());
    }
 
    [Fact]
    public void SetExtraFeatureAsNull()
    {
        _collection[typeof(string)] = null;
        Assert.Equal(0, _collection.Count(kv => kv.Key == typeof(string)));
 
        _collection[typeof(string)] = "A string";
        Assert.Equal(1, _collection.Count(kv => kv.Key == typeof(string)));
 
        _collection[typeof(string)] = null;
        Assert.Equal(0, _collection.Count(kv => kv.Key == typeof(string)));
    }
 
    private void CompareGenericGetterToIndexer()
    {
        Assert.Same(_collection.Get<IHttpRequestFeature>(), _collection[typeof(IHttpRequestFeature)]);
        Assert.Same(_collection.Get<IHttpRequestBodyDetectionFeature>(), _collection[typeof(IHttpRequestBodyDetectionFeature)]);
        Assert.Same(_collection.Get<IHttpResponseFeature>(), _collection[typeof(IHttpResponseFeature)]);
        Assert.Same(_collection.Get<IHttpResponseBodyFeature>(), _collection[typeof(IHttpResponseBodyFeature)]);
        Assert.Same(_collection.Get<IRequestBodyPipeFeature>(), _collection[typeof(IRequestBodyPipeFeature)]);
        Assert.Same(_collection.Get<IHttpRequestIdentifierFeature>(), _collection[typeof(IHttpRequestIdentifierFeature)]);
        Assert.Same(_collection.Get<IHttpRequestLifetimeFeature>(), _collection[typeof(IHttpRequestLifetimeFeature)]);
        Assert.Same(_collection.Get<IHttpConnectionFeature>(), _collection[typeof(IHttpConnectionFeature)]);
        Assert.Same(_collection.Get<IHttpMaxRequestBodySizeFeature>(), _collection[typeof(IHttpMaxRequestBodySizeFeature)]);
        Assert.Same(_collection.Get<IHttpMinRequestBodyDataRateFeature>(), _collection[typeof(IHttpMinRequestBodyDataRateFeature)]);
        Assert.Same(_collection.Get<IHttpMinResponseDataRateFeature>(), _collection[typeof(IHttpMinResponseDataRateFeature)]);
        Assert.Same(_collection.Get<IHttpBodyControlFeature>(), _collection[typeof(IHttpBodyControlFeature)]);
    }
 
    private int EachHttpProtocolFeatureSetAndUnique()
    {
        var featureCount = 0;
        foreach (var item in _collection)
        {
            var type = item.Key;
            if (type.IsAssignableFrom(typeof(HttpProtocol)))
            {
                var matches = _collection.Where(kv => ReferenceEquals(kv.Value, item.Value)).ToList();
                try
                {
                    Assert.Single(matches);
                }
                catch (Exception ex)
                {
                    throw new Exception($"Error for feature {type}.", ex);
                }
 
                featureCount++;
            }
        }
 
        Assert.NotEqual(0, featureCount);
 
        return featureCount;
    }
 
    public int GetFeaturesCount()
    {
        var featureCount = 0;
        foreach (var featureIter in _collection)
        {
            Type type = featureIter.Key;
            if (type.IsAssignableFrom(typeof(HttpProtocol)))
            {
                var featureLookup = _collection[type];
                Assert.Same(featureLookup, featureIter.Value);
                Assert.Same(featureLookup, _collection);
                featureCount++;
            }
        }
        return featureCount;
    }
 
    private int SetFeaturesToNonDefault()
    {
        int featureCount = 0;
        foreach (var feature in _collection)
        {
            Type type = feature.Key;
            if (type.IsAssignableFrom(typeof(HttpProtocol)))
            {
                _collection[type] = CreateHttp1Connection();
                featureCount++;
            }
        }
 
        var protocolFeaturesCount = EachHttpProtocolFeatureSetAndUnique();
 
        Assert.Equal(protocolFeaturesCount, featureCount);
 
        return featureCount;
    }
 
    private Http1Connection CreateHttp1Connection() => new TestHttp1Connection(_httpConnectionContext);
}