From dbd557c9657f144c327b6e56f5289685a83c33ee Mon Sep 17 00:00:00 2001 From: "Chris Ross (ASP.NET)" Date: Tue, 6 Mar 2018 20:25:51 -0800 Subject: [PATCH] Exclude the upgrade feature if the OS does not support it #427 --- .../StandardFeatureCollection.cs | 12 ++- .../OSDontSkipConditionAttribute.cs | 99 +++++++++++++++++++ .../OpaqueUpgradeTests.cs | 26 +++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OSDontSkipConditionAttribute.cs diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs b/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs index 0830de0fec..2adf51d1fc 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/StandardFeatureCollection.cs @@ -22,7 +22,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys // { typeof(ITlsTokenBindingFeature), ctx => ctx.GetTlsTokenBindingFeature() }, TODO: https://github.com/aspnet/HttpSysServer/issues/231 { typeof(IHttpBufferingFeature), _identityFunc }, { typeof(IHttpRequestLifetimeFeature), _identityFunc }, - { typeof(IHttpUpgradeFeature), _identityFunc }, { typeof(IHttpAuthenticationFeature), _identityFunc }, { typeof(IHttpRequestIdentifierFeature), _identityFunc }, { typeof(RequestContext), ctx => ctx.RequestContext }, @@ -32,6 +31,17 @@ namespace Microsoft.AspNetCore.Server.HttpSys private readonly FeatureContext _featureContext; + static StandardFeatureCollection() + { + if (ComNetOS.IsWin8orLater) + { + // Only add the upgrade feature if it stands a chance of working. + // SignalR uses the presence of the feature to detect feature support. + // https://github.com/aspnet/HttpSysServer/issues/427 + _featureFuncLookup[typeof(IHttpUpgradeFeature)] = _identityFunc; + } + } + public StandardFeatureCollection(FeatureContext featureContext) { _featureContext = featureContext; diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OSDontSkipConditionAttribute.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OSDontSkipConditionAttribute.cs new file mode 100644 index 0000000000..c657240a3e --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OSDontSkipConditionAttribute.cs @@ -0,0 +1,99 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + // Skip except on a specific OS and version + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)] + public class OSDontSkipConditionAttribute : Attribute, ITestCondition + { + private readonly OperatingSystems _includedOperatingSystem; + private readonly IEnumerable _includedVersions; + private readonly OperatingSystems _osPlatform; + private readonly string _osVersion; + + public OSDontSkipConditionAttribute(OperatingSystems operatingSystem, params string[] versions) : + this( + operatingSystem, + GetCurrentOS(), + GetCurrentOSVersion(), + versions) + { + } + + // to enable unit testing + internal OSDontSkipConditionAttribute( + OperatingSystems operatingSystem, OperatingSystems osPlatform, string osVersion, params string[] versions) + { + _includedOperatingSystem = operatingSystem; + _includedVersions = versions ?? Enumerable.Empty(); + _osPlatform = osPlatform; + _osVersion = osVersion; + } + + public bool IsMet + { + get + { + var currentOSInfo = new OSInfo() + { + OperatingSystem = _osPlatform, + Version = _osVersion, + }; + + var skip = (_includedOperatingSystem & currentOSInfo.OperatingSystem) != currentOSInfo.OperatingSystem; + if (!skip && _includedVersions.Any()) + { + skip = !_includedVersions.Any(inc => _osVersion.StartsWith(inc, StringComparison.OrdinalIgnoreCase)); + } + + // Since a test would be excuted only if 'IsMet' is true, return false if we want to skip + return !skip; + } + } + + public string SkipReason { get; set; } = "Test cannot run on this operating system."; + + static private OperatingSystems GetCurrentOS() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return OperatingSystems.Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return OperatingSystems.Linux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return OperatingSystems.MacOSX; + } + throw new PlatformNotSupportedException(); + } + + static private string GetCurrentOSVersion() + { + // currently not used on other OS's + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.OSVersion.Version.ToString(); + } + else + { + return string.Empty; + } + } + + private class OSInfo + { + public OperatingSystems OperatingSystem { get; set; } + + public string Version { get; set; } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs index c0fdda0dd2..609bc59fdd 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/OpaqueUpgradeTests.cs @@ -17,6 +17,32 @@ namespace Microsoft.AspNetCore.Server.HttpSys { public class OpaqueUpgradeTests { + [ConditionalFact] + [OSDontSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)] + public async Task OpaqueUpgrade_DownLevel_FeatureIsAbsent() + { + using (Utilities.CreateHttpServer(out var address, httpContext => + { + try + { + var opaqueFeature = httpContext.Features.Get(); + Assert.Null(opaqueFeature); + } + catch (Exception ex) + { + return httpContext.Response.WriteAsync(ex.ToString()); + } + return Task.FromResult(0); + })) + { + HttpResponseMessage response = await SendRequestAsync(address); + Assert.Equal(200, (int)response.StatusCode); + Assert.False(response.Headers.TransferEncodingChunked.HasValue, "Chunked"); + Assert.Equal(0, response.Content.Headers.ContentLength); + Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync()); + } + } + [ConditionalFact] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)] public async Task OpaqueUpgrade_SupportKeys_Present()