From a8c8ddbb451f81cf88024f1925067df2ddc365ff Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 6 Nov 2018 13:11:45 -0800 Subject: [PATCH 01/57] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Logging/tree/8270c545224e8734d7297e54edef5c584ee82f01 --- src/Testing/src/AssemblyTestLog.cs | 305 ++++++++++++++++++ ...Microsoft.Extensions.Logging.Testing.props | 8 + src/Testing/test/AssemblyTestLogTests.cs | 207 ++++++++++++ src/Testing/test/LoggedTestXunitTests.cs | 142 ++++++++ src/Testing/test/TestTestOutputHelper.cs | 36 +++ 5 files changed, 698 insertions(+) create mode 100644 src/Testing/src/AssemblyTestLog.cs create mode 100644 src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props create mode 100644 src/Testing/test/AssemblyTestLogTests.cs create mode 100644 src/Testing/test/LoggedTestXunitTests.cs create mode 100644 src/Testing/test/TestTestOutputHelper.cs diff --git a/src/Testing/src/AssemblyTestLog.cs b/src/Testing/src/AssemblyTestLog.cs new file mode 100644 index 0000000000..97a67b11fa --- /dev/null +++ b/src/Testing/src/AssemblyTestLog.cs @@ -0,0 +1,305 @@ +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Serilog.Extensions.Logging; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing +{ + public class AssemblyTestLog : IDisposable + { + public static readonly string OutputDirectoryEnvironmentVariableName = "ASPNETCORE_TEST_LOG_DIR"; + private static readonly string MaxPathLengthEnvironmentVariableName = "ASPNETCORE_TEST_LOG_MAXPATH"; + private static readonly string LogFileExtension = ".log"; + private static readonly int MaxPathLength = GetMaxPathLength(); + private static char[] InvalidFileChars = new char[] + { + '\"', '<', '>', '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31, ':', '*', '?', '\\', '/', ' ', (char)127 + }; + + private static readonly object _lock = new object(); + private static readonly Dictionary _logs = new Dictionary(); + + private readonly ILoggerFactory _globalLoggerFactory; + private readonly ILogger _globalLogger; + private readonly string _baseDirectory; + private readonly string _assemblyName; + private readonly IServiceProvider _serviceProvider; + + private static int GetMaxPathLength() + { + var maxPathString = Environment.GetEnvironmentVariable(MaxPathLengthEnvironmentVariableName); + var defaultMaxPath = 245; + return string.IsNullOrEmpty(maxPathString) ? defaultMaxPath : int.Parse(maxPathString); + } + + private AssemblyTestLog(ILoggerFactory globalLoggerFactory, ILogger globalLogger, string baseDirectory, string assemblyName, IServiceProvider serviceProvider) + { + _globalLoggerFactory = globalLoggerFactory; + _globalLogger = globalLogger; + _baseDirectory = baseDirectory; + _assemblyName = assemblyName; + _serviceProvider = serviceProvider; + } + + public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, [CallerMemberName] string testName = null) => + StartTestLog(output, className, out loggerFactory, LogLevel.Debug, testName); + + public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null) => + StartTestLog(output, className, out loggerFactory, minLogLevel, out var _, testName); + + internal IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, out string resolvedTestName, [CallerMemberName] string testName = null) + { + var serviceProvider = CreateLoggerServices(output, className, minLogLevel, out resolvedTestName, testName); + var factory = serviceProvider.GetRequiredService(); + loggerFactory = factory; + var logger = loggerFactory.CreateLogger("TestLifetime"); + + var stopwatch = Stopwatch.StartNew(); + + var scope = logger.BeginScope("Test: {testName}", testName); + + _globalLogger.LogInformation("Starting test {testName}", testName); + logger.LogInformation("Starting test {testName}", testName); + + return new Disposable(() => + { + stopwatch.Stop(); + _globalLogger.LogInformation("Finished test {testName} in {duration}s", testName, stopwatch.Elapsed.TotalSeconds); + logger.LogInformation("Finished test {testName} in {duration}s", testName, stopwatch.Elapsed.TotalSeconds); + scope.Dispose(); + factory.Dispose(); + (serviceProvider as IDisposable)?.Dispose(); + }); + } + + public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) => + CreateLoggerFactory(output, className, LogLevel.Trace, testName); + + public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, LogLevel minLogLevel, [CallerMemberName] string testName = null) + { + return CreateLoggerServices(output, className, minLogLevel, out var _, testName).GetRequiredService(); + } + + public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, out string normalizedTestName, [CallerMemberName] string testName = null) + { + normalizedTestName = string.Empty; + + // Try to shorten the class name using the assembly name + if (className.StartsWith(_assemblyName + ".")) + { + className = className.Substring(_assemblyName.Length + 1); + } + + SerilogLoggerProvider serilogLoggerProvider = null; + if (!string.IsNullOrEmpty(_baseDirectory)) + { + var testOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_assemblyName, _baseDirectory), className); + testName = RemoveIllegalFileChars(testName); + + if (testOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength) + { + _globalLogger.LogWarning($"Test name {testName} is too long. Please shorten test name."); + + // Shorten the test name by removing the middle portion of the testname + var testNameLength = MaxPathLength - testOutputDirectory.Length - LogFileExtension.Length; + + if (testNameLength <= 0) + { + throw new InvalidOperationException("Output file path could not be constructed due to max path length restrictions. Please shorten test assembly, class or method names."); + } + + testName = testName.Substring(0, testNameLength / 2) + testName.Substring(testName.Length - testNameLength / 2, testNameLength / 2); + + _globalLogger.LogWarning($"To prevent long paths test name was shortened to {testName}."); + } + + var testOutputFile = Path.Combine(testOutputDirectory, $"{testName}{LogFileExtension}"); + + if (File.Exists(testOutputFile)) + { + _globalLogger.LogWarning($"Output log file {testOutputFile} already exists. Please try to keep log file names unique."); + + for (var i = 0; i < 1000; i++) + { + testOutputFile = Path.Combine(testOutputDirectory, $"{testName}.{i}{LogFileExtension}"); + + if (!File.Exists(testOutputFile)) + { + _globalLogger.LogWarning($"To resolve log file collision, the enumerated file {testOutputFile} will be used."); + testName = $"{testName}.{i}"; + break; + } + } + } + + normalizedTestName = testName; + serilogLoggerProvider = ConfigureFileLogging(testOutputFile); + } + + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => + { + builder.SetMinimumLevel(minLogLevel); + + if (output != null) + { + builder.AddXunit(output, minLogLevel); + } + + if (serilogLoggerProvider != null) + { + // Use a factory so that the container will dispose it + builder.Services.AddSingleton(_ => serilogLoggerProvider); + } + }); + + return serviceCollection.BuildServiceProvider(); + } + + public static AssemblyTestLog Create(string assemblyName, string baseDirectory) + { + SerilogLoggerProvider serilogLoggerProvider = null; + var globalLogDirectory = GetAssemblyBaseDirectory(assemblyName, baseDirectory); + if (!string.IsNullOrEmpty(globalLogDirectory)) + { + var globalLogFileName = Path.Combine(globalLogDirectory, "global.log"); + serilogLoggerProvider = ConfigureFileLogging(globalLogFileName); + } + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddLogging(builder => + { + // Global logging, when it's written, is expected to be outputted. So set the log level to minimum. + builder.SetMinimumLevel(LogLevel.Trace); + + if (serilogLoggerProvider != null) + { + // Use a factory so that the container will dispose it + builder.Services.AddSingleton(_ => serilogLoggerProvider); + } + }); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + var loggerFactory = serviceProvider.GetRequiredService(); + + var logger = loggerFactory.CreateLogger("GlobalTestLog"); + logger.LogInformation($"Global Test Logging initialized. Set the '{OutputDirectoryEnvironmentVariableName}' Environment Variable in order to create log files on disk."); + return new AssemblyTestLog(loggerFactory, logger, baseDirectory, assemblyName, serviceProvider); + } + + public static AssemblyTestLog ForAssembly(Assembly assembly) + { + lock (_lock) + { + if (!_logs.TryGetValue(assembly, out var log)) + { + var assemblyName = assembly.GetName().Name; + var baseDirectory = Environment.GetEnvironmentVariable(OutputDirectoryEnvironmentVariableName); + log = Create(assemblyName, baseDirectory); + _logs[assembly] = log; + + // Try to clear previous logs + var assemblyBaseDirectory = GetAssemblyBaseDirectory(assemblyName, baseDirectory); + if (Directory.Exists(assemblyBaseDirectory)) + { + try + { + Directory.Delete(assemblyBaseDirectory, recursive: true); + } + catch {} + } + } + return log; + } + } + + private static string GetAssemblyBaseDirectory(string assemblyName, string baseDirectory) + { + if (!string.IsNullOrEmpty(baseDirectory)) + { + return Path.Combine(baseDirectory, assemblyName, RuntimeInformation.FrameworkDescription.TrimStart('.')); + } + return string.Empty; + } + + private static SerilogLoggerProvider ConfigureFileLogging(string fileName) + { + var dir = Path.GetDirectoryName(fileName); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + + var serilogger = new LoggerConfiguration() + .Enrich.FromLogContext() + .MinimumLevel.Verbose() + .WriteTo.File(fileName, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{SourceContext}] [{Level}] {Message}{NewLine}{Exception}", flushToDiskInterval: TimeSpan.FromSeconds(1), shared: true) + .CreateLogger(); + return new SerilogLoggerProvider(serilogger, dispose: true); + } + + private static string RemoveIllegalFileChars(string s) + { + var sb = new StringBuilder(); + + foreach (var c in s) + { + if (InvalidFileChars.Contains(c)) + { + if (sb.Length > 0 && sb[sb.Length - 1] != '_') + { + sb.Append('_'); + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + public void Dispose() + { + (_serviceProvider as IDisposable)?.Dispose(); + _globalLoggerFactory.Dispose(); + } + + private class Disposable : IDisposable + { + private Action _action; + + public Disposable(Action action) + { + _action = action; + } + + public void Dispose() + { + _action(); + } + } + } +} diff --git a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props new file mode 100644 index 0000000000..f98e3e13b5 --- /dev/null +++ b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props @@ -0,0 +1,8 @@ + + + + <_Parameter1>Microsoft.Extensions.Logging.Testing.LoggedTestFramework + <_Parameter2>Microsoft.Extensions.Logging.Testing + + + \ No newline at end of file diff --git a/src/Testing/test/AssemblyTestLogTests.cs b/src/Testing/test/AssemblyTestLogTests.cs new file mode 100644 index 0000000000..0efadb4367 --- /dev/null +++ b/src/Testing/test/AssemblyTestLogTests.cs @@ -0,0 +1,207 @@ +// 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.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Extensions.Logging.Testing.Tests +{ + public class AssemblyTestLogTests : LoggedTest + { + private static readonly Assembly ThisAssembly = typeof(AssemblyTestLog).GetTypeInfo().Assembly; + + [Fact] + public void FullClassNameUsedWhenShortClassNameAttributeNotSpecified() + { + Assert.Equal(GetType().FullName, ResolvedTestClassName); + } + + [Fact] + public void ForAssembly_ReturnsSameInstanceForSameAssembly() + { + Assert.Same( + AssemblyTestLog.ForAssembly(ThisAssembly), + AssemblyTestLog.ForAssembly(ThisAssembly)); + } + + [Fact] + public void TestLogWritesToITestOutputHelper() + { + var output = new TestTestOutputHelper(); + var assemblyLog = AssemblyTestLog.Create("NonExistant.Test.Assembly", baseDirectory: null); + + using (assemblyLog.StartTestLog(output, "NonExistant.Test.Class", out var loggerFactory)) + { + var logger = loggerFactory.CreateLogger("TestLogger"); + logger.LogInformation("Information!"); + + // Trace is disabled by default + logger.LogTrace("Trace!"); + } + + Assert.Equal(@"[TIMESTAMP] TestLifetime Information: Starting test TestLogWritesToITestOutputHelper +[TIMESTAMP] TestLogger Information: Information! +[TIMESTAMP] TestLifetime Information: Finished test TestLogWritesToITestOutputHelper in DURATION +", MakeConsistent(output.Output), ignoreLineEndingDifferences: true); + } + + [Fact] + private Task TestLogEscapesIllegalFileNames() => + RunTestLogFunctionalTest((tempDir) => + { + var illegalTestName = "Testing-https://localhost:5000"; + var escapedTestName = "Testing-https_localhost_5000"; + using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", baseDirectory: tempDir)) + using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, resolvedTestName: out var resolvedTestname, testName: illegalTestName)) + { + Assert.Equal(escapedTestName, resolvedTestname); + } + }); + + [Fact] + public Task TestLogWritesToGlobalLogFile() => + RunTestLogFunctionalTest((tempDir) => + { + // Because this test writes to a file, it is a functional test and should be logged + // but it's also testing the test logging facility. So this is pretty meta ;) + var logger = LoggerFactory.CreateLogger("Test"); + + using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + { + logger.LogInformation("Created test log in {baseDirectory}", tempDir); + + using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) + { + var testLogger = testLoggerFactory.CreateLogger("TestLogger"); + testLogger.LogInformation("Information!"); + testLogger.LogTrace("Trace!"); + } + } + + logger.LogInformation("Finished test log in {baseDirectory}", tempDir); + + var globalLogPath = Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "global.log"); + var testLog = Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.log"); + + Assert.True(File.Exists(globalLogPath), $"Expected global log file {globalLogPath} to exist"); + Assert.True(File.Exists(testLog), $"Expected test log file {testLog} to exist"); + + var globalLogContent = MakeConsistent(File.ReadAllText(globalLogPath)); + var testLogContent = MakeConsistent(File.ReadAllText(testLog)); + + Assert.Equal(@"[GlobalTestLog] [Information] Global Test Logging initialized. Set the 'ASPNETCORE_TEST_LOG_DIR' Environment Variable in order to create log files on disk. +[GlobalTestLog] [Information] Starting test ""FakeTestName"" +[GlobalTestLog] [Information] Finished test ""FakeTestName"" in DURATION +", globalLogContent, ignoreLineEndingDifferences: true); + Assert.Equal(@"[TestLifetime] [Information] Starting test ""FakeTestName"" +[TestLogger] [Information] Information! +[TestLogger] [Verbose] Trace! +[TestLifetime] [Information] Finished test ""FakeTestName"" in DURATION +", testLogContent, ignoreLineEndingDifferences: true); + }); + + [Fact] + public Task TestLogTruncatesTestNameToAvoidLongPaths() => + RunTestLogFunctionalTest((tempDir) => + { + var longTestName = new string('0', 50) + new string('1', 50) + new string('2', 50) + new string('3', 50) + new string('4', 50); + var logger = LoggerFactory.CreateLogger("Test"); + using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + { + logger.LogInformation("Created test log in {baseDirectory}", tempDir); + + using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: longTestName)) + { + testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!"); + } + } + logger.LogInformation("Finished test log in {baseDirectory}", tempDir); + + var testLogFiles = new DirectoryInfo(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass")).EnumerateFiles(); + var testLog = Assert.Single(testLogFiles); + var testFileName = Path.GetFileNameWithoutExtension(testLog.Name); + + // The first half of the file comes from the beginning of the test name passed to the logger + Assert.Equal(longTestName.Substring(0, testFileName.Length / 2), testFileName.Substring(0, testFileName.Length / 2)); + // The last half of the file comes from the ending of the test name passed to the logger + Assert.Equal(longTestName.Substring(longTestName.Length - testFileName.Length / 2, testFileName.Length / 2), testFileName.Substring(testFileName.Length - testFileName.Length / 2, testFileName.Length / 2)); + }); + + [Fact] + public Task TestLogEnumerateFilenamesToAvoidCollisions() => + RunTestLogFunctionalTest((tempDir) => + { + var logger = LoggerFactory.CreateLogger("Test"); + using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + { + logger.LogInformation("Created test log in {baseDirectory}", tempDir); + + for (var i = 0; i < 10; i++) + { + using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) + { + testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!"); + } + } + } + logger.LogInformation("Finished test log in {baseDirectory}", tempDir); + + // The first log file exists + Assert.True(File.Exists(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.log"))); + + // Subsequent files exist + for (var i = 0; i < 9; i++) + { + Assert.True(File.Exists(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.{i}.log"))); + } + }); + + private static readonly Regex TimestampRegex = new Regex(@"\d+-\d+-\d+T\d+:\d+:\d+"); + private static readonly Regex DurationRegex = new Regex(@"[^ ]+s$"); + + private async Task RunTestLogFunctionalTest(Action action, [CallerMemberName] string testName = null) + { + var tempDir = Path.Combine(Path.GetTempPath(), $"TestLogging_{Guid.NewGuid().ToString("N")}"); + try + { + action(tempDir); + } + finally + { + if (Directory.Exists(tempDir)) + { + try + { + Directory.Delete(tempDir, recursive: true); + } + catch + { + await Task.Delay(100); + Directory.Delete(tempDir, recursive: true); + } + } + } + } + + private static string MakeConsistent(string input) + { + return string.Join(Environment.NewLine, input.Split(new[] { Environment.NewLine }, StringSplitOptions.None) + .Select(line => + { + var strippedPrefix = line.IndexOf("[") >= 0 ? line.Substring(line.IndexOf("[")) : line; + + var strippedDuration = + DurationRegex.Replace(strippedPrefix, "DURATION"); + var strippedTimestamp = TimestampRegex.Replace(strippedDuration, "TIMESTAMP"); + return strippedTimestamp; + })); + } + } +} diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs new file mode 100644 index 0000000000..31fd6d631f --- /dev/null +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -0,0 +1,142 @@ +// 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.AspNetCore.Testing.xunit; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing.Tests +{ + [ShortClassName] + public class LoggedTestXunitTests : TestLoggedTest + { + private readonly ITestOutputHelper _output; + + public LoggedTestXunitTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void ShortClassNameUsedWhenShortClassNameAttributeSpecified() + { + Assert.Equal(GetType().Name, ResolvedTestClassName); + } + + [Fact] + public void LoggedTestTestOutputHelperSameInstanceAsInjectedConstructorArg() + { + Assert.Same(_output, TestOutputHelper); + } + + [Fact] + public void LoggedFactInitializesLoggedTestProperties() + { + Assert.NotNull(Logger); + Assert.NotNull(LoggerFactory); + Assert.NotNull(TestSink); + Assert.NotNull(TestOutputHelper); + } + + [Theory] + [InlineData("Hello world")] + public void LoggedTheoryInitializesLoggedTestProperties(string argument) + { + Assert.NotNull(Logger); + Assert.NotNull(LoggerFactory); + Assert.NotNull(TestSink); + Assert.NotNull(TestOutputHelper); + // Use the test argument + Assert.NotNull(argument); + } + + [ConditionalFact] + public void ConditionalLoggedFactGetsInitializedLoggerFactory() + { + Assert.NotNull(Logger); + Assert.NotNull(LoggerFactory); + Assert.NotNull(TestSink); + Assert.NotNull(TestOutputHelper); + } + + [ConditionalTheory] + [InlineData("Hello world")] + public void LoggedConditionalTheoryInitializesLoggedTestProperties(string argument) + { + Assert.NotNull(Logger); + Assert.NotNull(LoggerFactory); + Assert.NotNull(TestSink); + Assert.NotNull(TestOutputHelper); + // Use the test argument + Assert.NotNull(argument); + } + + [Fact] + [LogLevel(LogLevel.Information)] + public void LoggedFactFilteredByLogLevel() + { + Logger.LogInformation("Information"); + Logger.LogDebug("Debug"); + + var message = Assert.Single(TestSink.Writes); + Assert.Equal(LogLevel.Information, message.LogLevel); + Assert.Equal("Information", message.Formatter(message.State, null)); + } + + [Theory] + [InlineData("Hello world")] + [LogLevel(LogLevel.Information)] + public void LoggedTheoryFilteredByLogLevel(string argument) + { + Logger.LogInformation("Information"); + Logger.LogDebug("Debug"); + + var message = Assert.Single(TestSink.Writes); + Assert.Equal(LogLevel.Information, message.LogLevel); + Assert.Equal("Information", message.Formatter(message.State, null)); + + // Use the test argument + Assert.NotNull(argument); + } + + [Fact] + public void AddTestLoggingUpdatedWhenLoggerFactoryIsSet() + { + var loggerFactory = new LoggerFactory(); + var serviceCollection = new ServiceCollection(); + + LoggerFactory = loggerFactory; + AddTestLogging(serviceCollection); + + Assert.Same(loggerFactory, serviceCollection.BuildServiceProvider().GetRequiredService()); + } + + [ConditionalTheory] + [EnvironmentVariableSkipCondition("ASPNETCORE_TEST_LOG_DIR", "")] // The test name is only generated when logging is enabled via the environment variable + [InlineData(null)] + public void LoggedTheoryNullArgumentsAreEscaped(string argument) + { + Assert.NotNull(LoggerFactory); + Assert.Equal($"{nameof(LoggedTheoryNullArgumentsAreEscaped)}_null", ResolvedTestMethodName); + // Use the test argument + Assert.Null(argument); + } + + [Fact] + public void AdditionalSetupInvoked() + { + Assert.True(SetupInvoked); + } + } + + public class TestLoggedTest : LoggedTest + { + public bool SetupInvoked { get; private set; } = false; + + public override void AdditionalSetup() + { + SetupInvoked = true; + } + } +} diff --git a/src/Testing/test/TestTestOutputHelper.cs b/src/Testing/test/TestTestOutputHelper.cs new file mode 100644 index 0000000000..7043fe4ed2 --- /dev/null +++ b/src/Testing/test/TestTestOutputHelper.cs @@ -0,0 +1,36 @@ +// 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.Text; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing.Tests +{ + public class TestTestOutputHelper : ITestOutputHelper + { + private StringBuilder _output = new StringBuilder(); + + public bool Throw { get; set; } + + public string Output => _output.ToString(); + + public void WriteLine(string message) + { + if (Throw) + { + throw new Exception("Boom!"); + } + _output.AppendLine(message); + } + + public void WriteLine(string format, params object[] args) + { + if (Throw) + { + throw new Exception("Boom!"); + } + _output.AppendLine(string.Format(format, args)); + } + } +} From fd100ade9e885b4a8adced9414434cde0bf35b7a Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 6 Nov 2018 15:28:23 -0800 Subject: [PATCH 02/57] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Logging/tree/5381f42ded1f41a3b0960bba799aed53da411401 --- src/Testing/src/AssemblyTestLog.cs | 119 +++++++++++------- src/Testing/src/LoggedTest/ILoggedTest.cs | 23 ++++ src/Testing/src/LoggedTest/LoggedTest.cs | 24 ++++ src/Testing/src/LoggedTest/LoggedTestBase.cs | 82 ++++++++++++ .../src/TestFrameworkFileLoggerAttribute.cs | 20 +++ ...Microsoft.Extensions.Logging.Testing.props | 27 +++- src/Testing/test/AssemblyTestLogTests.cs | 71 ++++++----- src/Testing/test/LoggedTestXunitTests.cs | 20 ++- 8 files changed, 304 insertions(+), 82 deletions(-) create mode 100644 src/Testing/src/LoggedTest/ILoggedTest.cs create mode 100644 src/Testing/src/LoggedTest/LoggedTest.cs create mode 100644 src/Testing/src/LoggedTest/LoggedTestBase.cs create mode 100644 src/Testing/src/TestFrameworkFileLoggerAttribute.cs diff --git a/src/Testing/src/AssemblyTestLog.cs b/src/Testing/src/AssemblyTestLog.cs index 97a67b11fa..e84df52554 100644 --- a/src/Testing/src/AssemblyTestLog.cs +++ b/src/Testing/src/AssemblyTestLog.cs @@ -8,10 +8,11 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using Microsoft.Extensions.DependencyInjection; using Serilog; +using Serilog.Core; +using Serilog.Events; using Serilog.Extensions.Logging; using Xunit.Abstractions; @@ -19,7 +20,6 @@ namespace Microsoft.Extensions.Logging.Testing { public class AssemblyTestLog : IDisposable { - public static readonly string OutputDirectoryEnvironmentVariableName = "ASPNETCORE_TEST_LOG_DIR"; private static readonly string MaxPathLengthEnvironmentVariableName = "ASPNETCORE_TEST_LOG_MAXPATH"; private static readonly string LogFileExtension = ".log"; private static readonly int MaxPathLength = GetMaxPathLength(); @@ -38,7 +38,7 @@ namespace Microsoft.Extensions.Logging.Testing private readonly ILoggerFactory _globalLoggerFactory; private readonly ILogger _globalLogger; private readonly string _baseDirectory; - private readonly string _assemblyName; + private readonly Assembly _assembly; private readonly IServiceProvider _serviceProvider; private static int GetMaxPathLength() @@ -48,12 +48,12 @@ namespace Microsoft.Extensions.Logging.Testing return string.IsNullOrEmpty(maxPathString) ? defaultMaxPath : int.Parse(maxPathString); } - private AssemblyTestLog(ILoggerFactory globalLoggerFactory, ILogger globalLogger, string baseDirectory, string assemblyName, IServiceProvider serviceProvider) + private AssemblyTestLog(ILoggerFactory globalLoggerFactory, ILogger globalLogger, string baseDirectory, Assembly assembly, IServiceProvider serviceProvider) { _globalLoggerFactory = globalLoggerFactory; _globalLogger = globalLogger; _baseDirectory = baseDirectory; - _assemblyName = assemblyName; + _assembly = assembly; _serviceProvider = serviceProvider; } @@ -61,11 +61,12 @@ namespace Microsoft.Extensions.Logging.Testing StartTestLog(output, className, out loggerFactory, LogLevel.Debug, testName); public IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null) => - StartTestLog(output, className, out loggerFactory, minLogLevel, out var _, testName); + StartTestLog(output, className, out loggerFactory, minLogLevel, out var _, out var _, testName); - internal IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, out string resolvedTestName, [CallerMemberName] string testName = null) + internal IDisposable StartTestLog(ITestOutputHelper output, string className, out ILoggerFactory loggerFactory, LogLevel minLogLevel, out string resolvedTestName, out string logOutputDirectory, [CallerMemberName] string testName = null) { - var serviceProvider = CreateLoggerServices(output, className, minLogLevel, out resolvedTestName, testName); + var logStart = DateTimeOffset.UtcNow; + var serviceProvider = CreateLoggerServices(output, className, minLogLevel, out resolvedTestName, out logOutputDirectory, testName, logStart); var factory = serviceProvider.GetRequiredService(); loggerFactory = factory; var logger = loggerFactory.CreateLogger("TestLifetime"); @@ -75,7 +76,7 @@ namespace Microsoft.Extensions.Logging.Testing var scope = logger.BeginScope("Test: {testName}", testName); _globalLogger.LogInformation("Starting test {testName}", testName); - logger.LogInformation("Starting test {testName}", testName); + logger.LogInformation("Starting test {testName} at {logStart}", testName, logStart.ToString("s")); return new Disposable(() => { @@ -88,36 +89,39 @@ namespace Microsoft.Extensions.Logging.Testing }); } - public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null) => - CreateLoggerFactory(output, className, LogLevel.Trace, testName); + public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, [CallerMemberName] string testName = null, DateTimeOffset? logStart = null) + => CreateLoggerFactory(output, className, LogLevel.Trace, testName, logStart); - public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, LogLevel minLogLevel, [CallerMemberName] string testName = null) - { - return CreateLoggerServices(output, className, minLogLevel, out var _, testName).GetRequiredService(); - } + public ILoggerFactory CreateLoggerFactory(ITestOutputHelper output, string className, LogLevel minLogLevel, [CallerMemberName] string testName = null, DateTimeOffset? logStart = null) + => CreateLoggerServices(output, className, minLogLevel, out var _, out var _, testName, logStart).GetRequiredService(); - public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, out string normalizedTestName, [CallerMemberName] string testName = null) + public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, out string normalizedTestName, [CallerMemberName] string testName = null, DateTimeOffset? logStart = null) + => CreateLoggerServices(output, className, minLogLevel, out normalizedTestName, out var _, testName, logStart); + + public IServiceProvider CreateLoggerServices(ITestOutputHelper output, string className, LogLevel minLogLevel, out string normalizedTestName, out string logOutputDirectory, [CallerMemberName] string testName = null, DateTimeOffset? logStart = null) { normalizedTestName = string.Empty; + logOutputDirectory = string.Empty; + var assemblyName = _assembly.GetName().Name; // Try to shorten the class name using the assembly name - if (className.StartsWith(_assemblyName + ".")) + if (className.StartsWith(assemblyName + ".")) { - className = className.Substring(_assemblyName.Length + 1); + className = className.Substring(assemblyName.Length + 1); } SerilogLoggerProvider serilogLoggerProvider = null; if (!string.IsNullOrEmpty(_baseDirectory)) { - var testOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_assemblyName, _baseDirectory), className); + logOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_baseDirectory, _assembly), className); testName = RemoveIllegalFileChars(testName); - if (testOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength) + if (logOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength) { _globalLogger.LogWarning($"Test name {testName} is too long. Please shorten test name."); // Shorten the test name by removing the middle portion of the testname - var testNameLength = MaxPathLength - testOutputDirectory.Length - LogFileExtension.Length; + var testNameLength = MaxPathLength - logOutputDirectory.Length - LogFileExtension.Length; if (testNameLength <= 0) { @@ -129,7 +133,7 @@ namespace Microsoft.Extensions.Logging.Testing _globalLogger.LogWarning($"To prevent long paths test name was shortened to {testName}."); } - var testOutputFile = Path.Combine(testOutputDirectory, $"{testName}{LogFileExtension}"); + var testOutputFile = Path.Combine(logOutputDirectory, $"{testName}{LogFileExtension}"); if (File.Exists(testOutputFile)) { @@ -137,7 +141,7 @@ namespace Microsoft.Extensions.Logging.Testing for (var i = 0; i < 1000; i++) { - testOutputFile = Path.Combine(testOutputDirectory, $"{testName}.{i}{LogFileExtension}"); + testOutputFile = Path.Combine(logOutputDirectory, $"{testName}.{i}{LogFileExtension}"); if (!File.Exists(testOutputFile)) { @@ -149,7 +153,7 @@ namespace Microsoft.Extensions.Logging.Testing } normalizedTestName = testName; - serilogLoggerProvider = ConfigureFileLogging(testOutputFile); + serilogLoggerProvider = ConfigureFileLogging(testOutputFile, logStart); } var serviceCollection = new ServiceCollection(); @@ -159,7 +163,7 @@ namespace Microsoft.Extensions.Logging.Testing if (output != null) { - builder.AddXunit(output, minLogLevel); + builder.AddXunit(output, minLogLevel, logStart); } if (serilogLoggerProvider != null) @@ -172,14 +176,19 @@ namespace Microsoft.Extensions.Logging.Testing return serviceCollection.BuildServiceProvider(); } + // For back compat public static AssemblyTestLog Create(string assemblyName, string baseDirectory) + => Create(Assembly.Load(new AssemblyName(assemblyName)), baseDirectory); + + public static AssemblyTestLog Create(Assembly assembly, string baseDirectory) { + var logStart = DateTimeOffset.UtcNow; SerilogLoggerProvider serilogLoggerProvider = null; - var globalLogDirectory = GetAssemblyBaseDirectory(assemblyName, baseDirectory); + var globalLogDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly); if (!string.IsNullOrEmpty(globalLogDirectory)) { var globalLogFileName = Path.Combine(globalLogDirectory, "global.log"); - serilogLoggerProvider = ConfigureFileLogging(globalLogFileName); + serilogLoggerProvider = ConfigureFileLogging(globalLogFileName, logStart); } var serviceCollection = new ServiceCollection(); @@ -200,8 +209,11 @@ namespace Microsoft.Extensions.Logging.Testing var loggerFactory = serviceProvider.GetRequiredService(); var logger = loggerFactory.CreateLogger("GlobalTestLog"); - logger.LogInformation($"Global Test Logging initialized. Set the '{OutputDirectoryEnvironmentVariableName}' Environment Variable in order to create log files on disk."); - return new AssemblyTestLog(loggerFactory, logger, baseDirectory, assemblyName, serviceProvider); + logger.LogInformation("Global Test Logging initialized at {logStart}. " + + "Configure the output directory via 'LoggingTestingFileLoggingDirectory' MSBuild property " + + "or set 'LoggingTestingDisableFileLogging' to 'true' to disable file logging.", + logStart.ToString("s")); + return new AssemblyTestLog(loggerFactory, logger, baseDirectory, assembly, serviceProvider); } public static AssemblyTestLog ForAssembly(Assembly assembly) @@ -210,13 +222,13 @@ namespace Microsoft.Extensions.Logging.Testing { if (!_logs.TryGetValue(assembly, out var log)) { - var assemblyName = assembly.GetName().Name; - var baseDirectory = Environment.GetEnvironmentVariable(OutputDirectoryEnvironmentVariableName); - log = Create(assemblyName, baseDirectory); + var baseDirectory = GetFileLoggerAttribute(assembly).BaseDirectory; + + log = Create(assembly, baseDirectory); _logs[assembly] = log; // Try to clear previous logs - var assemblyBaseDirectory = GetAssemblyBaseDirectory(assemblyName, baseDirectory); + var assemblyBaseDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly); if (Directory.Exists(assemblyBaseDirectory)) { try @@ -230,16 +242,18 @@ namespace Microsoft.Extensions.Logging.Testing } } - private static string GetAssemblyBaseDirectory(string assemblyName, string baseDirectory) - { - if (!string.IsNullOrEmpty(baseDirectory)) - { - return Path.Combine(baseDirectory, assemblyName, RuntimeInformation.FrameworkDescription.TrimStart('.')); - } - return string.Empty; - } + private static string GetAssemblyBaseDirectory(string baseDirectory, Assembly assembly) + => string.IsNullOrEmpty(baseDirectory) + ? string.Empty + : Path.Combine(baseDirectory, assembly.GetName().Name, GetFileLoggerAttribute(assembly).TFM); - private static SerilogLoggerProvider ConfigureFileLogging(string fileName) + private static TestFrameworkFileLoggerAttribute GetFileLoggerAttribute(Assembly assembly) + => assembly.GetCustomAttribute() + ?? throw new InvalidOperationException($"No {nameof(TestFrameworkFileLoggerAttribute)} found on the assembly {assembly.GetName().Name}. " + + "The attribute is added via msbuild properties of the Microsoft.Extensions.Logging.Testing. " + + "Please ensure the msbuild property is imported or a direct reference to Microsoft.Extensions.Logging.Testing is added."); + + private static SerilogLoggerProvider ConfigureFileLogging(string fileName, DateTimeOffset? logStart) { var dir = Path.GetDirectoryName(fileName); if (!Directory.Exists(dir)) @@ -254,8 +268,9 @@ namespace Microsoft.Extensions.Logging.Testing var serilogger = new LoggerConfiguration() .Enrich.FromLogContext() + .Enrich.With(new AssemblyLogTimestampOffsetEnricher(logStart)) .MinimumLevel.Verbose() - .WriteTo.File(fileName, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{SourceContext}] [{Level}] {Message}{NewLine}{Exception}", flushToDiskInterval: TimeSpan.FromSeconds(1), shared: true) + .WriteTo.File(fileName, outputTemplate: "[{TimestampOffset}] [{SourceContext}] [{Level}] {Message:l}{NewLine}{Exception}", flushToDiskInterval: TimeSpan.FromSeconds(1), shared: true) .CreateLogger(); return new SerilogLoggerProvider(serilogger, dispose: true); } @@ -287,6 +302,24 @@ namespace Microsoft.Extensions.Logging.Testing _globalLoggerFactory.Dispose(); } + private class AssemblyLogTimestampOffsetEnricher : ILogEventEnricher + { + private DateTimeOffset? _logStart; + + public AssemblyLogTimestampOffsetEnricher(DateTimeOffset? logStart) + { + _logStart = logStart; + } + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + => logEvent.AddPropertyIfAbsent( + propertyFactory.CreateProperty( + "TimestampOffset", + _logStart.HasValue + ? $"{(DateTimeOffset.UtcNow - _logStart.Value).TotalSeconds.ToString("N3")}s" + : DateTimeOffset.UtcNow.ToString("s"))); + } + private class Disposable : IDisposable { private Action _action; diff --git a/src/Testing/src/LoggedTest/ILoggedTest.cs b/src/Testing/src/LoggedTest/ILoggedTest.cs new file mode 100644 index 0000000000..a563cbdaf9 --- /dev/null +++ b/src/Testing/src/LoggedTest/ILoggedTest.cs @@ -0,0 +1,23 @@ +// 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.Reflection; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing +{ + public interface ILoggedTest : IDisposable + { + ILogger Logger { get; } + + ILoggerFactory LoggerFactory { get; } + + ITestOutputHelper TestOutputHelper { get; } + + // For back compat + IDisposable StartLog(out ILoggerFactory loggerFactory, LogLevel minLogLevel, string testName); + + void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper); + } +} diff --git a/src/Testing/src/LoggedTest/LoggedTest.cs b/src/Testing/src/LoggedTest/LoggedTest.cs new file mode 100644 index 0000000000..64a9adec06 --- /dev/null +++ b/src/Testing/src/LoggedTest/LoggedTest.cs @@ -0,0 +1,24 @@ +// 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.Reflection; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing +{ + public class LoggedTest : LoggedTestBase + { + // Obsolete but keeping for back compat + public LoggedTest(ITestOutputHelper output = null) : base (output) { } + + public ITestSink TestSink { get; set; } + + public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + { + base.Initialize(methodInfo, testMethodArguments, testOutputHelper); + + TestSink = new TestSink(); + LoggerFactory.AddProvider(new TestLoggerProvider(TestSink)); + } + } +} diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs new file mode 100644 index 0000000000..f714a632a4 --- /dev/null +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -0,0 +1,82 @@ +// 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.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.Logging.Testing +{ + public class LoggedTestBase : ILoggedTest + { + private IDisposable _testLog; + + // Obsolete but keeping for back compat + public LoggedTestBase(ITestOutputHelper output = null) + { + TestOutputHelper = output; + } + + // Internal for testing + internal string ResolvedTestClassName { get; set; } + + internal RetryContext RetryContext { get; set; } + + public string ResolvedLogOutputDirectory { get; set; } + + public string ResolvedTestMethodName { get; set; } + + public ILogger Logger { get; set; } + + public ILoggerFactory LoggerFactory { get; set; } + + public ITestOutputHelper TestOutputHelper { get; set; } + + public void AddTestLogging(IServiceCollection services) => services.AddSingleton(LoggerFactory); + + // For back compat + public IDisposable StartLog(out ILoggerFactory loggerFactory, [CallerMemberName] string testName = null) => StartLog(out loggerFactory, LogLevel.Debug, testName); + + // For back compat + public IDisposable StartLog(out ILoggerFactory loggerFactory, LogLevel minLogLevel, [CallerMemberName] string testName = null) + { + return AssemblyTestLog.ForAssembly(GetType().GetTypeInfo().Assembly).StartTestLog(TestOutputHelper, GetType().FullName, out loggerFactory, minLogLevel, testName); + } + + public virtual void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + { + TestOutputHelper = testOutputHelper; + + var classType = GetType(); + var logLevelAttribute = methodInfo.GetCustomAttribute(); + var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}"); + + var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute() + ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); + // internal for testing + ResolvedTestClassName = useShortClassName == null ? classType.FullName : classType.Name; + + _testLog = AssemblyTestLog + .ForAssembly(classType.GetTypeInfo().Assembly) + .StartTestLog( + TestOutputHelper, + ResolvedTestClassName, + out var loggerFactory, + logLevelAttribute?.LogLevel ?? LogLevel.Debug, + out var resolvedTestName, + out var logOutputDirectory, + testName); + + ResolvedLogOutputDirectory = logOutputDirectory; + ResolvedTestMethodName = resolvedTestName; + + LoggerFactory = loggerFactory; + Logger = loggerFactory.CreateLogger(classType); + } + + public virtual void Dispose() => _testLog.Dispose(); + } +} diff --git a/src/Testing/src/TestFrameworkFileLoggerAttribute.cs b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs new file mode 100644 index 0000000000..32d8f30584 --- /dev/null +++ b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs @@ -0,0 +1,20 @@ +// 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.Extensions.Logging.Testing +{ + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class TestFrameworkFileLoggerAttribute : Attribute + { + public TestFrameworkFileLoggerAttribute(string tfm, string baseDirectory = null) + { + TFM = tfm; + BaseDirectory = baseDirectory; + } + + public string TFM { get; } + public string BaseDirectory { get; } + } +} \ No newline at end of file diff --git a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props index f98e3e13b5..0d2585146c 100644 --- a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props +++ b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props @@ -1,8 +1,23 @@  - - - <_Parameter1>Microsoft.Extensions.Logging.Testing.LoggedTestFramework - <_Parameter2>Microsoft.Extensions.Logging.Testing - - + + + $(ASPNETCORE_TEST_LOG_DIR) + $(RepositoryRoot)artifacts\logs\ + + + + + + <_Parameter1>Microsoft.Extensions.Logging.Testing.LoggedTestFramework + <_Parameter2>Microsoft.Extensions.Logging.Testing + + + + <_Parameter1>$(TargetFramework) + <_Parameter2 Condition="'$(LoggingTestingDisableFileLogging)' != 'true'">$(LoggingTestingFileLoggingDirectory) + + + \ No newline at end of file diff --git a/src/Testing/test/AssemblyTestLogTests.cs b/src/Testing/test/AssemblyTestLogTests.cs index 0efadb4367..20f597defc 100644 --- a/src/Testing/test/AssemblyTestLogTests.cs +++ b/src/Testing/test/AssemblyTestLogTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using Xunit; @@ -15,7 +14,9 @@ namespace Microsoft.Extensions.Logging.Testing.Tests { public class AssemblyTestLogTests : LoggedTest { - private static readonly Assembly ThisAssembly = typeof(AssemblyTestLog).GetTypeInfo().Assembly; + private static readonly Assembly ThisAssembly = typeof(AssemblyTestLogTests).GetTypeInfo().Assembly; + private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name; + private static readonly string TFM = new DirectoryInfo(AppContext.BaseDirectory).Name; [Fact] public void FullClassNameUsedWhenShortClassNameAttributeNotSpecified() @@ -35,7 +36,7 @@ namespace Microsoft.Extensions.Logging.Testing.Tests public void TestLogWritesToITestOutputHelper() { var output = new TestTestOutputHelper(); - var assemblyLog = AssemblyTestLog.Create("NonExistant.Test.Assembly", baseDirectory: null); + var assemblyLog = AssemblyTestLog.Create(ThisAssemblyName, baseDirectory: null); using (assemblyLog.StartTestLog(output, "NonExistant.Test.Class", out var loggerFactory)) { @@ -46,20 +47,23 @@ namespace Microsoft.Extensions.Logging.Testing.Tests logger.LogTrace("Trace!"); } - Assert.Equal(@"[TIMESTAMP] TestLifetime Information: Starting test TestLogWritesToITestOutputHelper -[TIMESTAMP] TestLogger Information: Information! -[TIMESTAMP] TestLifetime Information: Finished test TestLogWritesToITestOutputHelper in DURATION -", MakeConsistent(output.Output), ignoreLineEndingDifferences: true); + var testLogContent = MakeConsistent(output.Output); + + Assert.Equal( +@"[OFFSET] TestLifetime Information: Starting test TestLogWritesToITestOutputHelper at TIMESTAMP +[OFFSET] TestLogger Information: Information! +[OFFSET] TestLifetime Information: Finished test TestLogWritesToITestOutputHelper in DURATION +", testLogContent, ignoreLineEndingDifferences: true); } [Fact] private Task TestLogEscapesIllegalFileNames() => RunTestLogFunctionalTest((tempDir) => { - var illegalTestName = "Testing-https://localhost:5000"; - var escapedTestName = "Testing-https_localhost_5000"; - using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", baseDirectory: tempDir)) - using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, resolvedTestName: out var resolvedTestname, testName: illegalTestName)) + var illegalTestName = "T:e/s//t"; + var escapedTestName = "T_e_s_t"; + using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, baseDirectory: tempDir)) + using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, resolvedTestName: out var resolvedTestname, out var _, testName: illegalTestName)) { Assert.Equal(escapedTestName, resolvedTestname); } @@ -73,11 +77,11 @@ namespace Microsoft.Extensions.Logging.Testing.Tests // but it's also testing the test logging facility. So this is pretty meta ;) var logger = LoggerFactory.CreateLogger("Test"); - using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir)) { logger.LogInformation("Created test log in {baseDirectory}", tempDir); - using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) + using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) { var testLogger = testLoggerFactory.CreateLogger("TestLogger"); testLogger.LogInformation("Information!"); @@ -87,8 +91,8 @@ namespace Microsoft.Extensions.Logging.Testing.Tests logger.LogInformation("Finished test log in {baseDirectory}", tempDir); - var globalLogPath = Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "global.log"); - var testLog = Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.log"); + var globalLogPath = Path.Combine(tempDir, ThisAssemblyName, TFM, "global.log"); + var testLog = Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", "FakeTestName.log"); Assert.True(File.Exists(globalLogPath), $"Expected global log file {globalLogPath} to exist"); Assert.True(File.Exists(testLog), $"Expected test log file {testLog} to exist"); @@ -96,14 +100,16 @@ namespace Microsoft.Extensions.Logging.Testing.Tests var globalLogContent = MakeConsistent(File.ReadAllText(globalLogPath)); var testLogContent = MakeConsistent(File.ReadAllText(testLog)); - Assert.Equal(@"[GlobalTestLog] [Information] Global Test Logging initialized. Set the 'ASPNETCORE_TEST_LOG_DIR' Environment Variable in order to create log files on disk. -[GlobalTestLog] [Information] Starting test ""FakeTestName"" -[GlobalTestLog] [Information] Finished test ""FakeTestName"" in DURATION + Assert.Equal( +@"[OFFSET] [GlobalTestLog] [Information] Global Test Logging initialized at TIMESTAMP. Configure the output directory via 'LoggingTestingFileLoggingDirectory' MSBuild property or set 'LoggingTestingDisableFileLogging' to 'true' to disable file logging. +[OFFSET] [GlobalTestLog] [Information] Starting test FakeTestName +[OFFSET] [GlobalTestLog] [Information] Finished test FakeTestName in DURATION ", globalLogContent, ignoreLineEndingDifferences: true); - Assert.Equal(@"[TestLifetime] [Information] Starting test ""FakeTestName"" -[TestLogger] [Information] Information! -[TestLogger] [Verbose] Trace! -[TestLifetime] [Information] Finished test ""FakeTestName"" in DURATION + Assert.Equal( +@"[OFFSET] [TestLifetime] [Information] Starting test FakeTestName at TIMESTAMP +[OFFSET] [TestLogger] [Information] Information! +[OFFSET] [TestLogger] [Verbose] Trace! +[OFFSET] [TestLifetime] [Information] Finished test FakeTestName in DURATION ", testLogContent, ignoreLineEndingDifferences: true); }); @@ -113,18 +119,18 @@ namespace Microsoft.Extensions.Logging.Testing.Tests { var longTestName = new string('0', 50) + new string('1', 50) + new string('2', 50) + new string('3', 50) + new string('4', 50); var logger = LoggerFactory.CreateLogger("Test"); - using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir)) { logger.LogInformation("Created test log in {baseDirectory}", tempDir); - using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: longTestName)) + using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: longTestName)) { testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!"); } } logger.LogInformation("Finished test log in {baseDirectory}", tempDir); - var testLogFiles = new DirectoryInfo(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass")).EnumerateFiles(); + var testLogFiles = new DirectoryInfo(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass")).EnumerateFiles(); var testLog = Assert.Single(testLogFiles); var testFileName = Path.GetFileNameWithoutExtension(testLog.Name); @@ -139,13 +145,13 @@ namespace Microsoft.Extensions.Logging.Testing.Tests RunTestLogFunctionalTest((tempDir) => { var logger = LoggerFactory.CreateLogger("Test"); - using (var testAssemblyLog = AssemblyTestLog.Create("FakeTestAssembly", tempDir)) + using (var testAssemblyLog = AssemblyTestLog.Create(ThisAssemblyName, tempDir)) { logger.LogInformation("Created test log in {baseDirectory}", tempDir); for (var i = 0; i < 10; i++) { - using (testAssemblyLog.StartTestLog(output: null, className: "FakeTestAssembly.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) + using (testAssemblyLog.StartTestLog(output: null, className: $"{ThisAssemblyName}.FakeTestClass", loggerFactory: out var testLoggerFactory, minLogLevel: LogLevel.Trace, testName: "FakeTestName")) { testLoggerFactory.CreateLogger("TestLogger").LogInformation("Information!"); } @@ -154,16 +160,17 @@ namespace Microsoft.Extensions.Logging.Testing.Tests logger.LogInformation("Finished test log in {baseDirectory}", tempDir); // The first log file exists - Assert.True(File.Exists(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.log"))); + Assert.True(File.Exists(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", "FakeTestName.log"))); // Subsequent files exist for (var i = 0; i < 9; i++) { - Assert.True(File.Exists(Path.Combine(tempDir, "FakeTestAssembly", RuntimeInformation.FrameworkDescription.TrimStart('.'), "FakeTestClass", $"FakeTestName.{i}.log"))); + Assert.True(File.Exists(Path.Combine(tempDir, ThisAssemblyName, TFM, "FakeTestClass", $"FakeTestName.{i}.log"))); } }); private static readonly Regex TimestampRegex = new Regex(@"\d+-\d+-\d+T\d+:\d+:\d+"); + private static readonly Regex TimestampOffsetRegex = new Regex(@"\d+\.\d+s"); private static readonly Regex DurationRegex = new Regex(@"[^ ]+s$"); private async Task RunTestLogFunctionalTest(Action action, [CallerMemberName] string testName = null) @@ -197,10 +204,10 @@ namespace Microsoft.Extensions.Logging.Testing.Tests { var strippedPrefix = line.IndexOf("[") >= 0 ? line.Substring(line.IndexOf("[")) : line; - var strippedDuration = - DurationRegex.Replace(strippedPrefix, "DURATION"); + var strippedDuration = DurationRegex.Replace(strippedPrefix, "DURATION"); var strippedTimestamp = TimestampRegex.Replace(strippedDuration, "TIMESTAMP"); - return strippedTimestamp; + var strippedTimestampOffset = TimestampOffsetRegex.Replace(strippedTimestamp, "OFFSET"); + return strippedTimestampOffset; })); } } diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs index 31fd6d631f..d1d8581193 100644 --- a/src/Testing/test/LoggedTestXunitTests.cs +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -1,6 +1,7 @@ // 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.Reflection; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -130,12 +131,29 @@ namespace Microsoft.Extensions.Logging.Testing.Tests } } + public class LoggedTestXunitInitializationTests : TestLoggedTest + { + [Fact] + public void ITestOutputHelperInitializedByDefault() + { + Assert.True(ITestOutputHelperIsInitialized); + } + } + public class TestLoggedTest : LoggedTest { public bool SetupInvoked { get; private set; } = false; + public bool ITestOutputHelperIsInitialized { get; private set; } = false; - public override void AdditionalSetup() + public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { + base.Initialize(methodInfo, testMethodArguments, testOutputHelper); + + try + { + TestOutputHelper.WriteLine("Test"); + ITestOutputHelperIsInitialized = true; + } catch { } SetupInvoked = true; } } From e7cca176e589b9102cf43365f2bca3d356fb29aa Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 6 Nov 2018 16:58:30 -0800 Subject: [PATCH 03/57] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Logging/tree/f7d8e4e0537eaab54dcf28c2b148b82688a3d62d --- src/Testing/src/LoggedTest/LoggedTestBase.cs | 4 +- src/Testing/test/LoggedTestXunitTests.cs | 46 +++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index f714a632a4..c3a4e78931 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -51,7 +51,9 @@ namespace Microsoft.Extensions.Logging.Testing TestOutputHelper = testOutputHelper; var classType = GetType(); - var logLevelAttribute = methodInfo.GetCustomAttribute(); + var logLevelAttribute = methodInfo.GetCustomAttribute() + ?? methodInfo.DeclaringType.GetCustomAttribute() + ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}"); var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute() diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs index d1d8581193..507453a242 100644 --- a/src/Testing/test/LoggedTestXunitTests.cs +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -1,6 +1,7 @@ // 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.Linq; using System.Reflection; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; @@ -9,6 +10,7 @@ using Xunit.Abstractions; namespace Microsoft.Extensions.Logging.Testing.Tests { + [LogLevel(LogLevel.Debug)] [ShortClassName] public class LoggedTestXunitTests : TestLoggedTest { @@ -75,7 +77,7 @@ namespace Microsoft.Extensions.Logging.Testing.Tests [Fact] [LogLevel(LogLevel.Information)] - public void LoggedFactFilteredByLogLevel() + public void LoggedFactFilteredByMethodLogLevel() { Logger.LogInformation("Information"); Logger.LogDebug("Debug"); @@ -85,6 +87,17 @@ namespace Microsoft.Extensions.Logging.Testing.Tests Assert.Equal("Information", message.Formatter(message.State, null)); } + [Fact] + public void LoggedFactFilteredByClassLogLevel() + { + Logger.LogDebug("Debug"); + Logger.LogTrace("Trace"); + + var message = Assert.Single(TestSink.Writes); + Assert.Equal(LogLevel.Debug, message.LogLevel); + Assert.Equal("Debug", message.Formatter(message.State, null)); + } + [Theory] [InlineData("Hello world")] [LogLevel(LogLevel.Information)] @@ -129,6 +142,37 @@ namespace Microsoft.Extensions.Logging.Testing.Tests { Assert.True(SetupInvoked); } + + [Fact] + public void MessageWrittenEventInvoked() + { + WriteContext context = null; + TestSink.MessageLogged += ctx => context = ctx; + Logger.LogInformation("Information"); + Assert.Equal(TestSink.Writes.Single(), context); + } + + [Fact] + public void ScopeStartedEventInvoked() + { + BeginScopeContext context = null; + TestSink.ScopeStarted += ctx => context = ctx; + using (Logger.BeginScope("Scope")) {} + Assert.Equal(TestSink.Scopes.Single(), context); + } + } + + public class LoggedTestXunitLogLevelTests : LoggedTest + { + [Fact] + public void LoggedFactFilteredByAssemblyLogLevel() + { + Logger.LogTrace("Trace"); + + var message = Assert.Single(TestSink.Writes); + Assert.Equal(LogLevel.Trace, message.LogLevel); + Assert.Equal("Trace", message.Formatter(message.State, null)); + } } public class LoggedTestXunitInitializationTests : TestLoggedTest From 688914bb70063b97dcb2facdbc1ab91980406ece Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 17 Dec 2018 09:18:26 -0800 Subject: [PATCH 04/57] Capture LoggedTest.Initialize exception and re-trow in Dispose (#770) --- src/Testing/src/LoggedTest/LoggedTestBase.cs | 64 ++++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index c3a4e78931..72de6a87c3 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using Microsoft.Extensions.DependencyInjection; using Xunit.Abstractions; @@ -12,6 +13,8 @@ namespace Microsoft.Extensions.Logging.Testing { public class LoggedTestBase : ILoggedTest { + private ExceptionDispatchInfo _initializationException; + private IDisposable _testLog; // Obsolete but keeping for back compat @@ -48,37 +51,48 @@ namespace Microsoft.Extensions.Logging.Testing public virtual void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { - TestOutputHelper = testOutputHelper; + try + { + TestOutputHelper = testOutputHelper; - var classType = GetType(); - var logLevelAttribute = methodInfo.GetCustomAttribute() - ?? methodInfo.DeclaringType.GetCustomAttribute() - ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); - var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}"); + var classType = GetType(); + var logLevelAttribute = methodInfo.GetCustomAttribute() + ?? methodInfo.DeclaringType.GetCustomAttribute() + ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); + var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}"); - var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute() - ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); - // internal for testing - ResolvedTestClassName = useShortClassName == null ? classType.FullName : classType.Name; + var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute() + ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); + // internal for testing + ResolvedTestClassName = useShortClassName == null ? classType.FullName : classType.Name; - _testLog = AssemblyTestLog - .ForAssembly(classType.GetTypeInfo().Assembly) - .StartTestLog( - TestOutputHelper, - ResolvedTestClassName, - out var loggerFactory, - logLevelAttribute?.LogLevel ?? LogLevel.Debug, - out var resolvedTestName, - out var logOutputDirectory, - testName); + _testLog = AssemblyTestLog + .ForAssembly(classType.GetTypeInfo().Assembly) + .StartTestLog( + TestOutputHelper, + ResolvedTestClassName, + out var loggerFactory, + logLevelAttribute?.LogLevel ?? LogLevel.Debug, + out var resolvedTestName, + out var logOutputDirectory, + testName); - ResolvedLogOutputDirectory = logOutputDirectory; - ResolvedTestMethodName = resolvedTestName; + ResolvedLogOutputDirectory = logOutputDirectory; + ResolvedTestMethodName = resolvedTestName; - LoggerFactory = loggerFactory; - Logger = loggerFactory.CreateLogger(classType); + LoggerFactory = loggerFactory; + Logger = loggerFactory.CreateLogger(classType); + } + catch (Exception e) + { + _initializationException = ExceptionDispatchInfo.Capture(e); + } } - public virtual void Dispose() => _testLog.Dispose(); + public virtual void Dispose() + { + _initializationException?.Throw(); + _testLog.Dispose(); + } } } From b51be2c25045ba50909822f971425da764fed2fd Mon Sep 17 00:00:00 2001 From: John Luo Date: Mon, 7 Jan 2019 13:42:22 -0500 Subject: [PATCH 05/57] Add attribute to allow LoggedTest to collect dump on failure (#905) * Add attribute for collecting dumps for failed LoggedTest --- src/Testing/src/CollectDumpAttribute.cs | 18 +++++ .../DumpCollector/DumpCollector.Windows.cs | 76 +++++++++++++++++++ .../src/DumpCollector/DumpCollector.cs | 20 +++++ 3 files changed, 114 insertions(+) create mode 100644 src/Testing/src/CollectDumpAttribute.cs create mode 100644 src/Testing/src/DumpCollector/DumpCollector.Windows.cs create mode 100644 src/Testing/src/DumpCollector/DumpCollector.cs diff --git a/src/Testing/src/CollectDumpAttribute.cs b/src/Testing/src/CollectDumpAttribute.cs new file mode 100644 index 0000000000..8a6aa84bac --- /dev/null +++ b/src/Testing/src/CollectDumpAttribute.cs @@ -0,0 +1,18 @@ +// 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.Extensions.Logging.Testing +{ + /// + /// Capture the memory dump upon test failure. + /// + /// + /// This currently only works in Windows environments + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class CollectDumpAttribute : Attribute + { + } +} diff --git a/src/Testing/src/DumpCollector/DumpCollector.Windows.cs b/src/Testing/src/DumpCollector/DumpCollector.Windows.cs new file mode 100644 index 0000000000..8d4168c20c --- /dev/null +++ b/src/Testing/src/DumpCollector/DumpCollector.Windows.cs @@ -0,0 +1,76 @@ +// 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.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace Microsoft.Extensions.Logging.Testing +{ + public static partial class DumpCollector + { + private static class Windows + { + internal static void Collect(Process process, string outputFile) + { + // Open the file for writing + using (var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) + { + // Dump the process! + var exceptionInfo = new NativeMethods.MINIDUMP_EXCEPTION_INFORMATION(); + if (!NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, NativeMethods.MINIDUMP_TYPE.MiniDumpWithFullMemory, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero)) + { + var err = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(err); + } + } + } + + private static class NativeMethods + { + [DllImport("Dbghelp.dll")] + public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE DumpType, ref MINIDUMP_EXCEPTION_INFORMATION ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct MINIDUMP_EXCEPTION_INFORMATION + { + public uint ThreadId; + public IntPtr ExceptionPointers; + public int ClientPointers; + } + + [Flags] + public enum MINIDUMP_TYPE : uint + { + MiniDumpNormal = 0, + MiniDumpWithDataSegs = 1 << 0, + MiniDumpWithFullMemory = 1 << 1, + MiniDumpWithHandleData = 1 << 2, + MiniDumpFilterMemory = 1 << 3, + MiniDumpScanMemory = 1 << 4, + MiniDumpWithUnloadedModules = 1 << 5, + MiniDumpWithIndirectlyReferencedMemory = 1 << 6, + MiniDumpFilterModulePaths = 1 << 7, + MiniDumpWithProcessThreadData = 1 << 8, + MiniDumpWithPrivateReadWriteMemory = 1 << 9, + MiniDumpWithoutOptionalData = 1 << 10, + MiniDumpWithFullMemoryInfo = 1 << 11, + MiniDumpWithThreadInfo = 1 << 12, + MiniDumpWithCodeSegs = 1 << 13, + MiniDumpWithoutAuxiliaryState = 1 << 14, + MiniDumpWithFullAuxiliaryState = 1 << 15, + MiniDumpWithPrivateWriteCopyMemory = 1 << 16, + MiniDumpIgnoreInaccessibleMemory = 1 << 17, + MiniDumpWithTokenInformation = 1 << 18, + MiniDumpWithModuleHeaders = 1 << 19, + MiniDumpFilterTriage = 1 << 20, + MiniDumpWithAvxXStateContext = 1 << 21, + MiniDumpWithIptTrace = 1 << 22, + MiniDumpValidTypeFlags = (-1) ^ ((~1) << 22) + } + } + } + } +} diff --git a/src/Testing/src/DumpCollector/DumpCollector.cs b/src/Testing/src/DumpCollector/DumpCollector.cs new file mode 100644 index 0000000000..67043ed827 --- /dev/null +++ b/src/Testing/src/DumpCollector/DumpCollector.cs @@ -0,0 +1,20 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +namespace Microsoft.Extensions.Logging.Testing +{ + public static partial class DumpCollector + { + public static void Collect(Process process, string fileName) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Windows.Collect(process, fileName); + } + // No implementations yet for macOS and Linux + } + } +} From c43d3b823e37de499c3113351575ed47366638d6 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 29 Jan 2019 18:34:54 -0800 Subject: [PATCH 06/57] Cleanup conversion to Arcade (#1014) * Remove obsolete targets, properties, and scripts * Replace IsProductComponent with IsShipping * Undo bad merge to version.props * Update documentation, and put workarounds into a common file * Replace usages of RepositoryRoot with RepoRoot * Remove API baselines * Remove unnecessary restore feeds and split workarounds into two files * Enable PR checks on all branches, and disable autocancel --- .../src/build/Microsoft.Extensions.Logging.Testing.props | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props index 0d2585146c..c503c32d40 100644 --- a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props +++ b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props @@ -1,8 +1,9 @@  + $(RepositoryRoot) $(ASPNETCORE_TEST_LOG_DIR) - $(RepositoryRoot)artifacts\logs\ + $(RepoRoot)artifacts\log\ - \ No newline at end of file + From 262262569a3d6f204330ce38b801ede9b7bf77d0 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Wed, 6 Mar 2019 15:19:11 -0800 Subject: [PATCH 07/57] add FlakyAttribute to mark flaky tests (#1222) part of aspnet/AspNetCore#8237 --- src/Testing/src/LoggedTest/LoggedTestBase.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index 72de6a87c3..492de61cb6 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -91,6 +91,14 @@ namespace Microsoft.Extensions.Logging.Testing public virtual void Dispose() { + if(_testLog == null) + { + // It seems like sometimes the MSBuild goop that adds the test framework can end up in a bad state and not actually add it + // Not sure yet why that happens but the exception isn't clear so I'm adding this error so we can detect it better. + // -anurse + throw new InvalidOperationException("LoggedTest base class was used but nothing initialized it! The test framework may not be enabled. Try cleaning your 'obj' directory."); + } + _initializationException?.Throw(); _testLog.Dispose(); } From f57e591af3e3fd0500fb1e51b5be8a4fb5a3562a Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Tue, 9 Apr 2019 14:03:12 -0700 Subject: [PATCH 08/57] Add Repeat attribute (#1375) --- src/Testing/src/LoggedTest/LoggedTestBase.cs | 2 +- src/Testing/test/LoggedTestXunitTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index 492de61cb6..94cdf82257 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -26,7 +26,7 @@ namespace Microsoft.Extensions.Logging.Testing // Internal for testing internal string ResolvedTestClassName { get; set; } - internal RetryContext RetryContext { get; set; } + internal RepeatContext RepeatContext { get; set; } public string ResolvedLogOutputDirectory { get; set; } diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs index 507453a242..520ffaaa9e 100644 --- a/src/Testing/test/LoggedTestXunitTests.cs +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Linq; From ce392fa4f7a2e6b8eb186d64e0e6eb7d07c3a2c4 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Thu, 15 Aug 2019 09:12:53 -0700 Subject: [PATCH 09/57] Cleanup to skip/flaky attributes (#2186) --- src/Testing/test/LoggedTestXunitTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs index 520ffaaa9e..ab9ee746c3 100644 --- a/src/Testing/test/LoggedTestXunitTests.cs +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; -using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Xunit; using Xunit.Abstractions; From 221985c254773e10da453c4d4b1381677b275a71 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Mon, 16 Sep 2019 13:33:09 -0700 Subject: [PATCH 10/57] Refactor xUnit extensibility Adds our own hook for before/after logic that's more usable, called `ITestMethodLifecycle`. This provides access to a context object including the information about the test and the output helper. This can be implemented by attributes or by the class itself. The goal (and result) of this, is that we have a single *test executor* extensibility point that provides all of the features we need. We should use this everywhere we need features xUnit doesn't have. Adding a new extensibility point (`ITestMethodLifecycle`) allows us to do this without turning all of these features into a giant monolith. --- Also updated our existing extensibility to use this new hook. I did as much cleanup as a could to remove duplication from logging and keep it loosly coupled. I didn't want to tease this apart completely because the scope of this PR is already pretty large. --- src/Testing/src/AssemblyTestLog.cs | 55 ++++--------------- src/Testing/src/CollectDumpAttribute.cs | 23 +++++++- src/Testing/src/LoggedTest/ILoggedTest.cs | 3 +- src/Testing/src/LoggedTest/LoggedTest.cs | 5 +- src/Testing/src/LoggedTest/LoggedTestBase.cs | 41 +++++++++----- .../src/TestFrameworkFileLoggerAttribute.cs | 13 ++--- ...Microsoft.Extensions.Logging.Testing.props | 4 +- src/Testing/test/AssemblyTestLogTests.cs | 8 +-- src/Testing/test/LoggedTestXunitTests.cs | 16 +----- 9 files changed, 76 insertions(+), 92 deletions(-) diff --git a/src/Testing/src/AssemblyTestLog.cs b/src/Testing/src/AssemblyTestLog.cs index e84df52554..3c598b67d5 100644 --- a/src/Testing/src/AssemblyTestLog.cs +++ b/src/Testing/src/AssemblyTestLog.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Core; @@ -23,14 +24,6 @@ namespace Microsoft.Extensions.Logging.Testing private static readonly string MaxPathLengthEnvironmentVariableName = "ASPNETCORE_TEST_LOG_MAXPATH"; private static readonly string LogFileExtension = ".log"; private static readonly int MaxPathLength = GetMaxPathLength(); - private static char[] InvalidFileChars = new char[] - { - '\"', '<', '>', '|', '\0', - (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, - (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, - (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, - (char)31, ':', '*', '?', '\\', '/', ' ', (char)127 - }; private static readonly object _lock = new object(); private static readonly Dictionary _logs = new Dictionary(); @@ -113,8 +106,8 @@ namespace Microsoft.Extensions.Logging.Testing SerilogLoggerProvider serilogLoggerProvider = null; if (!string.IsNullOrEmpty(_baseDirectory)) { - logOutputDirectory = Path.Combine(GetAssemblyBaseDirectory(_baseDirectory, _assembly), className); - testName = RemoveIllegalFileChars(testName); + logOutputDirectory = Path.Combine(_baseDirectory, className); + testName = TestFileOutputContext.RemoveIllegalFileChars(testName); if (logOutputDirectory.Length + testName.Length + LogFileExtension.Length >= MaxPathLength) { @@ -184,10 +177,10 @@ namespace Microsoft.Extensions.Logging.Testing { var logStart = DateTimeOffset.UtcNow; SerilogLoggerProvider serilogLoggerProvider = null; - var globalLogDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly); - if (!string.IsNullOrEmpty(globalLogDirectory)) + if (!string.IsNullOrEmpty(baseDirectory)) { - var globalLogFileName = Path.Combine(globalLogDirectory, "global.log"); + baseDirectory = TestFileOutputContext.GetAssemblyBaseDirectory(assembly, baseDirectory); + var globalLogFileName = Path.Combine(baseDirectory, "global.log"); serilogLoggerProvider = ConfigureFileLogging(globalLogFileName, logStart); } @@ -222,31 +215,26 @@ namespace Microsoft.Extensions.Logging.Testing { if (!_logs.TryGetValue(assembly, out var log)) { - var baseDirectory = GetFileLoggerAttribute(assembly).BaseDirectory; + var baseDirectory = TestFileOutputContext.GetOutputDirectory(assembly); log = Create(assembly, baseDirectory); _logs[assembly] = log; - // Try to clear previous logs - var assemblyBaseDirectory = GetAssemblyBaseDirectory(baseDirectory, assembly); - if (Directory.Exists(assemblyBaseDirectory)) + // Try to clear previous logs, continue if it fails. + var assemblyBaseDirectory = TestFileOutputContext.GetAssemblyBaseDirectory(assembly); + if (!string.IsNullOrEmpty(assemblyBaseDirectory)) { try { Directory.Delete(assemblyBaseDirectory, recursive: true); } - catch {} + catch { } } } return log; } } - private static string GetAssemblyBaseDirectory(string baseDirectory, Assembly assembly) - => string.IsNullOrEmpty(baseDirectory) - ? string.Empty - : Path.Combine(baseDirectory, assembly.GetName().Name, GetFileLoggerAttribute(assembly).TFM); - private static TestFrameworkFileLoggerAttribute GetFileLoggerAttribute(Assembly assembly) => assembly.GetCustomAttribute() ?? throw new InvalidOperationException($"No {nameof(TestFrameworkFileLoggerAttribute)} found on the assembly {assembly.GetName().Name}. " @@ -275,27 +263,6 @@ namespace Microsoft.Extensions.Logging.Testing return new SerilogLoggerProvider(serilogger, dispose: true); } - private static string RemoveIllegalFileChars(string s) - { - var sb = new StringBuilder(); - - foreach (var c in s) - { - if (InvalidFileChars.Contains(c)) - { - if (sb.Length > 0 && sb[sb.Length - 1] != '_') - { - sb.Append('_'); - } - } - else - { - sb.Append(c); - } - } - return sb.ToString(); - } - public void Dispose() { (_serviceProvider as IDisposable)?.Dispose(); diff --git a/src/Testing/src/CollectDumpAttribute.cs b/src/Testing/src/CollectDumpAttribute.cs index 8a6aa84bac..012a5c8fa1 100644 --- a/src/Testing/src/CollectDumpAttribute.cs +++ b/src/Testing/src/CollectDumpAttribute.cs @@ -2,6 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; namespace Microsoft.Extensions.Logging.Testing { @@ -12,7 +17,23 @@ namespace Microsoft.Extensions.Logging.Testing /// This currently only works in Windows environments /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class CollectDumpAttribute : Attribute + public class CollectDumpAttribute : Attribute, ITestMethodLifecycle { + public Task OnTestStartAsync(TestContext context, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public Task OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken) + { + if (exception != null) + { + var path = Path.Combine(context.FileOutput.TestClassOutputDirectory, context.FileOutput.GetUniqueFileName(context.FileOutput.TestName, ".dmp")); + var process = Process.GetCurrentProcess(); + DumpCollector.Collect(process, path); + } + + return Task.CompletedTask; + } } } diff --git a/src/Testing/src/LoggedTest/ILoggedTest.cs b/src/Testing/src/LoggedTest/ILoggedTest.cs index a563cbdaf9..a906ae84a2 100644 --- a/src/Testing/src/LoggedTest/ILoggedTest.cs +++ b/src/Testing/src/LoggedTest/ILoggedTest.cs @@ -3,6 +3,7 @@ using System; using System.Reflection; +using Microsoft.AspNetCore.Testing; using Xunit.Abstractions; namespace Microsoft.Extensions.Logging.Testing @@ -18,6 +19,6 @@ namespace Microsoft.Extensions.Logging.Testing // For back compat IDisposable StartLog(out ILoggerFactory loggerFactory, LogLevel minLogLevel, string testName); - void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper); + void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper); } } diff --git a/src/Testing/src/LoggedTest/LoggedTest.cs b/src/Testing/src/LoggedTest/LoggedTest.cs index 64a9adec06..d108ffb7e8 100644 --- a/src/Testing/src/LoggedTest/LoggedTest.cs +++ b/src/Testing/src/LoggedTest/LoggedTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; +using Microsoft.AspNetCore.Testing; using Xunit.Abstractions; namespace Microsoft.Extensions.Logging.Testing @@ -13,9 +14,9 @@ namespace Microsoft.Extensions.Logging.Testing public ITestSink TestSink { get; set; } - public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { - base.Initialize(methodInfo, testMethodArguments, testOutputHelper); + base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper); TestSink = new TestSink(); LoggerFactory.AddProvider(new TestLoggerProvider(TestSink)); diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index 94cdf82257..324b855319 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -6,12 +6,16 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Serilog; using Xunit.Abstractions; namespace Microsoft.Extensions.Logging.Testing { - public class LoggedTestBase : ILoggedTest + public class LoggedTestBase : ILoggedTest, ITestMethodLifecycle { private ExceptionDispatchInfo _initializationException; @@ -23,11 +27,11 @@ namespace Microsoft.Extensions.Logging.Testing TestOutputHelper = output; } + protected TestContext Context { get; private set; } + // Internal for testing internal string ResolvedTestClassName { get; set; } - internal RepeatContext RepeatContext { get; set; } - public string ResolvedLogOutputDirectory { get; set; } public string ResolvedTestMethodName { get; set; } @@ -49,7 +53,7 @@ namespace Microsoft.Extensions.Logging.Testing return AssemblyTestLog.ForAssembly(GetType().GetTypeInfo().Assembly).StartTestLog(TestOutputHelper, GetType().FullName, out loggerFactory, minLogLevel, testName); } - public virtual void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + public virtual void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { try { @@ -59,25 +63,22 @@ namespace Microsoft.Extensions.Logging.Testing var logLevelAttribute = methodInfo.GetCustomAttribute() ?? methodInfo.DeclaringType.GetCustomAttribute() ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); - var testName = testMethodArguments.Aggregate(methodInfo.Name, (a, b) => $"{a}-{(b ?? "null")}"); - var useShortClassName = methodInfo.DeclaringType.GetCustomAttribute() - ?? methodInfo.DeclaringType.Assembly.GetCustomAttribute(); // internal for testing - ResolvedTestClassName = useShortClassName == null ? classType.FullName : classType.Name; + ResolvedTestClassName = context.FileOutput.TestClassName; _testLog = AssemblyTestLog .ForAssembly(classType.GetTypeInfo().Assembly) .StartTestLog( TestOutputHelper, - ResolvedTestClassName, + context.FileOutput.TestClassName, out var loggerFactory, logLevelAttribute?.LogLevel ?? LogLevel.Debug, out var resolvedTestName, - out var logOutputDirectory, - testName); + out var logDirectory, + context.FileOutput.TestName); - ResolvedLogOutputDirectory = logOutputDirectory; + ResolvedLogOutputDirectory = logDirectory; ResolvedTestMethodName = resolvedTestName; LoggerFactory = loggerFactory; @@ -91,7 +92,7 @@ namespace Microsoft.Extensions.Logging.Testing public virtual void Dispose() { - if(_testLog == null) + if (_testLog == null) { // It seems like sometimes the MSBuild goop that adds the test framework can end up in a bad state and not actually add it // Not sure yet why that happens but the exception isn't clear so I'm adding this error so we can detect it better. @@ -102,5 +103,19 @@ namespace Microsoft.Extensions.Logging.Testing _initializationException?.Throw(); _testLog.Dispose(); } + + Task ITestMethodLifecycle.OnTestStartAsync(TestContext context, CancellationToken cancellationToken) + { + + Context = context; + + Initialize(context, context.TestMethod, context.MethodArguments, context.Output); + return Task.CompletedTask; + } + + Task ITestMethodLifecycle.OnTestEndAsync(TestContext context, Exception exception, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } } } diff --git a/src/Testing/src/TestFrameworkFileLoggerAttribute.cs b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs index 32d8f30584..025a5a9bd8 100644 --- a/src/Testing/src/TestFrameworkFileLoggerAttribute.cs +++ b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs @@ -1,20 +1,17 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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 Microsoft.AspNetCore.Testing; namespace Microsoft.Extensions.Logging.Testing { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] - public class TestFrameworkFileLoggerAttribute : Attribute + public class TestFrameworkFileLoggerAttribute : TestOutputDirectoryAttribute { public TestFrameworkFileLoggerAttribute(string tfm, string baseDirectory = null) + : base(tfm, baseDirectory) { - TFM = tfm; - BaseDirectory = baseDirectory; } - - public string TFM { get; } - public string BaseDirectory { get; } } -} \ No newline at end of file +} diff --git a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props index c503c32d40..3895cb5471 100644 --- a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props +++ b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props @@ -11,8 +11,8 @@ Condition="'$(GenerateLoggingTestingAssemblyAttributes)' != 'false'"> - <_Parameter1>Microsoft.Extensions.Logging.Testing.LoggedTestFramework - <_Parameter2>Microsoft.Extensions.Logging.Testing + <_Parameter1>Microsoft.AspNetCore.Testing.AspNetTestFramework + <_Parameter2>Microsoft.AspNetCore.Testing diff --git a/src/Testing/test/AssemblyTestLogTests.cs b/src/Testing/test/AssemblyTestLogTests.cs index 20f597defc..6d7ae5139a 100644 --- a/src/Testing/test/AssemblyTestLogTests.cs +++ b/src/Testing/test/AssemblyTestLogTests.cs @@ -18,12 +18,6 @@ namespace Microsoft.Extensions.Logging.Testing.Tests private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name; private static readonly string TFM = new DirectoryInfo(AppContext.BaseDirectory).Name; - [Fact] - public void FullClassNameUsedWhenShortClassNameAttributeNotSpecified() - { - Assert.Equal(GetType().FullName, ResolvedTestClassName); - } - [Fact] public void ForAssembly_ReturnsSameInstanceForSameAssembly() { @@ -57,7 +51,7 @@ namespace Microsoft.Extensions.Logging.Testing.Tests } [Fact] - private Task TestLogEscapesIllegalFileNames() => + public Task TestLogEscapesIllegalFileNames() => RunTestLogFunctionalTest((tempDir) => { var illegalTestName = "T:e/s//t"; diff --git a/src/Testing/test/LoggedTestXunitTests.cs b/src/Testing/test/LoggedTestXunitTests.cs index ab9ee746c3..d8454023a2 100644 --- a/src/Testing/test/LoggedTestXunitTests.cs +++ b/src/Testing/test/LoggedTestXunitTests.cs @@ -21,18 +21,6 @@ namespace Microsoft.Extensions.Logging.Testing.Tests _output = output; } - [Fact] - public void ShortClassNameUsedWhenShortClassNameAttributeSpecified() - { - Assert.Equal(GetType().Name, ResolvedTestClassName); - } - - [Fact] - public void LoggedTestTestOutputHelperSameInstanceAsInjectedConstructorArg() - { - Assert.Same(_output, TestOutputHelper); - } - [Fact] public void LoggedFactInitializesLoggedTestProperties() { @@ -189,9 +177,9 @@ namespace Microsoft.Extensions.Logging.Testing.Tests public bool SetupInvoked { get; private set; } = false; public bool ITestOutputHelperIsInitialized { get; private set; } = false; - public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) + public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { - base.Initialize(methodInfo, testMethodArguments, testOutputHelper); + base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper); try { From b6e5fd86713f1a96f55027e76ca54897bf28aa2a Mon Sep 17 00:00:00 2001 From: John Luo Date: Mon, 6 Jan 2020 11:40:44 -0800 Subject: [PATCH 11/57] Preserve functional test logs on CI (#2819) * Add option to preserve function test logs * Upload test logs as artifacts * Preserve binlogs * Add target to ensure all functional test logs preserved --- src/Testing/src/AssemblyTestLog.cs | 2 +- src/Testing/src/TestFrameworkFileLoggerAttribute.cs | 4 ++-- .../build/Microsoft.Extensions.Logging.Testing.props | 10 ++++++++-- src/Testing/test/AssemblyTestLogTests.cs | 12 ++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Testing/src/AssemblyTestLog.cs b/src/Testing/src/AssemblyTestLog.cs index 3c598b67d5..611b853fac 100644 --- a/src/Testing/src/AssemblyTestLog.cs +++ b/src/Testing/src/AssemblyTestLog.cs @@ -222,7 +222,7 @@ namespace Microsoft.Extensions.Logging.Testing // Try to clear previous logs, continue if it fails. var assemblyBaseDirectory = TestFileOutputContext.GetAssemblyBaseDirectory(assembly); - if (!string.IsNullOrEmpty(assemblyBaseDirectory)) + if (!string.IsNullOrEmpty(assemblyBaseDirectory) && !TestFileOutputContext.GetPreserveExistingLogsInOutput(assembly)) { try { diff --git a/src/Testing/src/TestFrameworkFileLoggerAttribute.cs b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs index 025a5a9bd8..1059fa76f2 100644 --- a/src/Testing/src/TestFrameworkFileLoggerAttribute.cs +++ b/src/Testing/src/TestFrameworkFileLoggerAttribute.cs @@ -9,8 +9,8 @@ namespace Microsoft.Extensions.Logging.Testing [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] public class TestFrameworkFileLoggerAttribute : TestOutputDirectoryAttribute { - public TestFrameworkFileLoggerAttribute(string tfm, string baseDirectory = null) - : base(tfm, baseDirectory) + public TestFrameworkFileLoggerAttribute(string preserveExistingLogsInOutput, string tfm, string baseDirectory = null) + : base(preserveExistingLogsInOutput, tfm, baseDirectory) { } } diff --git a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props index 3895cb5471..167efb3f82 100644 --- a/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props +++ b/src/Testing/src/build/Microsoft.Extensions.Logging.Testing.props @@ -9,6 +9,11 @@ + + true + false + + <_Parameter1>Microsoft.AspNetCore.Testing.AspNetTestFramework @@ -16,8 +21,9 @@ - <_Parameter1>$(TargetFramework) - <_Parameter2 Condition="'$(LoggingTestingDisableFileLogging)' != 'true'">$(LoggingTestingFileLoggingDirectory) + <_Parameter1>$(PreserveExistingLogsInOutput) + <_Parameter2>$(TargetFramework) + <_Parameter3 Condition="'$(LoggingTestingDisableFileLogging)' != 'true'">$(LoggingTestingFileLoggingDirectory) diff --git a/src/Testing/test/AssemblyTestLogTests.cs b/src/Testing/test/AssemblyTestLogTests.cs index 6d7ae5139a..dbefa4ccd2 100644 --- a/src/Testing/test/AssemblyTestLogTests.cs +++ b/src/Testing/test/AssemblyTestLogTests.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Xunit; namespace Microsoft.Extensions.Logging.Testing.Tests @@ -18,6 +19,17 @@ namespace Microsoft.Extensions.Logging.Testing.Tests private static readonly string ThisAssemblyName = ThisAssembly.GetName().Name; private static readonly string TFM = new DirectoryInfo(AppContext.BaseDirectory).Name; + [Fact] + public void FunctionalLogs_LogsPreservedFromNonFlakyRun() + { + } + + [Fact] + [Flaky("http://example.com", FlakyOn.All)] + public void FunctionalLogs_LogsPreservedFromFlakyRun() + { + } + [Fact] public void ForAssembly_ReturnsSameInstanceForSameAssembly() { From f26cf4a111ee0c81f20f659d11d601e508b90389 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Thu, 20 Feb 2020 10:23:28 -0800 Subject: [PATCH 12/57] Consume the internalized System.Net.Quic sources (#19156) * Consume the internalized System.Net.Quic sources * More changes from rebase * Remove Console.WriteLine --- ...soft.AspNetCore.Server.Kestrel.Core.csproj | 6 +- .../Kestrel/Transport.Quic/src/Libraries.cs | 12 + ...tCore.Server.Kestrel.Transport.Quic.csproj | 17 +- .../runtime/Http3/QPack/QPackDecoder.cs | 8 + src/Shared/runtime/NetEventSource.Common.cs | 738 ++++++++++++ .../Implementations/Mock/MockConnection.cs | 226 ++++ .../Mock/MockImplementationProvider.cs | 21 + .../Quic/Implementations/Mock/MockListener.cs | 120 ++ .../Quic/Implementations/Mock/MockStream.cs | 259 ++++ .../MsQuic/Internal/MsQuicAddressHelpers.cs | 85 ++ .../MsQuic/Internal/MsQuicApi.cs | 361 ++++++ .../MsQuic/Internal/MsQuicParameterHelpers.cs | 98 ++ .../MsQuic/Internal/MsQuicSecurityConfig.cs | 45 + .../MsQuic/Internal/MsQuicSession.cs | 156 +++ .../MsQuic/Internal/QuicExceptionHelpers.cs | 17 + .../Internal/ResettableCompletionSource.cs | 81 ++ .../MsQuic/MsQuicConnection.cs | 416 +++++++ .../MsQuic/MsQuicImplementationProvider.cs | 22 + .../Implementations/MsQuic/MsQuicListener.cs | 213 ++++ .../Implementations/MsQuic/MsQuicStream.cs | 1042 +++++++++++++++++ .../Implementations/QuicConnectionProvider.cs | 36 + .../QuicImplementationProvider.cs | 17 + .../Implementations/QuicListenerProvider.cs | 22 + .../Implementations/QuicStreamProvider.cs | 53 + .../runtime/Quic/Interop/Interop.MsQuic.cs | 16 + .../runtime/Quic/Interop/MsQuicEnums.cs | 167 +++ .../Quic/Interop/MsQuicNativeMethods.cs | 488 ++++++++ .../runtime/Quic/Interop/MsQuicStatusCodes.cs | 121 ++ .../Quic/Interop/MsQuicStatusHelper.cs | 26 + .../runtime/Quic/NetEventSource.Quic.cs | 13 + .../Quic/QuicClientConnectionOptions.cs | 50 + src/Shared/runtime/Quic/QuicConnection.cs | 99 ++ .../Quic/QuicConnectionAbortedException.cs | 22 + src/Shared/runtime/Quic/QuicException.cs | 14 + .../Quic/QuicImplementationProviders.cs | 13 + src/Shared/runtime/Quic/QuicListener.cs | 55 + .../runtime/Quic/QuicListenerOptions.cs | 59 + .../Quic/QuicOperationAbortedException.cs | 18 + src/Shared/runtime/Quic/QuicStream.cs | 133 +++ .../Quic/QuicStreamAbortedException.cs | 22 + src/Shared/runtime/SR.Quic.cs | 20 + src/Shared/runtime/SR.resx | 12 + .../Microsoft.AspNetCore.Shared.Tests.csproj | 8 +- 43 files changed, 5419 insertions(+), 8 deletions(-) create mode 100644 src/Servers/Kestrel/Transport.Quic/src/Libraries.cs create mode 100644 src/Shared/runtime/NetEventSource.Common.cs create mode 100644 src/Shared/runtime/Quic/Implementations/Mock/MockConnection.cs create mode 100644 src/Shared/runtime/Quic/Implementations/Mock/MockImplementationProvider.cs create mode 100644 src/Shared/runtime/Quic/Implementations/Mock/MockListener.cs create mode 100644 src/Shared/runtime/Quic/Implementations/Mock/MockStream.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/QuicExceptionHelpers.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicImplementationProvider.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs create mode 100644 src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs create mode 100644 src/Shared/runtime/Quic/Implementations/QuicConnectionProvider.cs create mode 100644 src/Shared/runtime/Quic/Implementations/QuicImplementationProvider.cs create mode 100644 src/Shared/runtime/Quic/Implementations/QuicListenerProvider.cs create mode 100644 src/Shared/runtime/Quic/Implementations/QuicStreamProvider.cs create mode 100644 src/Shared/runtime/Quic/Interop/Interop.MsQuic.cs create mode 100644 src/Shared/runtime/Quic/Interop/MsQuicEnums.cs create mode 100644 src/Shared/runtime/Quic/Interop/MsQuicNativeMethods.cs create mode 100644 src/Shared/runtime/Quic/Interop/MsQuicStatusCodes.cs create mode 100644 src/Shared/runtime/Quic/Interop/MsQuicStatusHelper.cs create mode 100644 src/Shared/runtime/Quic/NetEventSource.Quic.cs create mode 100644 src/Shared/runtime/Quic/QuicClientConnectionOptions.cs create mode 100644 src/Shared/runtime/Quic/QuicConnection.cs create mode 100644 src/Shared/runtime/Quic/QuicConnectionAbortedException.cs create mode 100644 src/Shared/runtime/Quic/QuicException.cs create mode 100644 src/Shared/runtime/Quic/QuicImplementationProviders.cs create mode 100644 src/Shared/runtime/Quic/QuicListener.cs create mode 100644 src/Shared/runtime/Quic/QuicListenerOptions.cs create mode 100644 src/Shared/runtime/Quic/QuicOperationAbortedException.cs create mode 100644 src/Shared/runtime/Quic/QuicStream.cs create mode 100644 src/Shared/runtime/Quic/QuicStreamAbortedException.cs create mode 100644 src/Shared/runtime/SR.Quic.cs diff --git a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj index 789564054a..7ea2ec562d 100644 --- a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj +++ b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj @@ -1,4 +1,4 @@ - + Core components of ASP.NET Core Kestrel cross-platform web server. @@ -16,7 +16,9 @@ - + + + diff --git a/src/Servers/Kestrel/Transport.Quic/src/Libraries.cs b/src/Servers/Kestrel/Transport.Quic/src/Libraries.cs new file mode 100644 index 0000000000..82340cf941 --- /dev/null +++ b/src/Servers/Kestrel/Transport.Quic/src/Libraries.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static class Libraries + { + // Compare to https://github.com/dotnet/runtime/blob/63c88901df460c47eaffc6b970c4b5f0aeaf0a88/src/libraries/Common/src/Interop/Linux/Interop.Libraries.cs#L10 + internal const string MsQuic = "msquic"; + } +} diff --git a/src/Servers/Kestrel/Transport.Quic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj b/src/Servers/Kestrel/Transport.Quic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj index a530b892ee..19b9c38761 100644 --- a/src/Servers/Kestrel/Transport.Quic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj +++ b/src/Servers/Kestrel/Transport.Quic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.csproj @@ -1,12 +1,12 @@ - + - Libuv transport for the ASP.NET Core Kestrel cross-platform web server. + Quic transport for the ASP.NET Core Kestrel cross-platform web server. $(DefaultNetCoreTargetFramework) true aspnetcore;kestrel true - CS1591;$(NoWarn) + CS1591;CS0436;$(NoWarn) false @@ -14,9 +14,13 @@ + + + + @@ -26,4 +30,11 @@ + + + System.Net.Quic.SR + + + + diff --git a/src/Shared/runtime/Http3/QPack/QPackDecoder.cs b/src/Shared/runtime/Http3/QPack/QPackDecoder.cs index 6f63d66ce9..958dfac303 100644 --- a/src/Shared/runtime/Http3/QPack/QPackDecoder.cs +++ b/src/Shared/runtime/Http3/QPack/QPackDecoder.cs @@ -269,6 +269,10 @@ namespace System.Net.Http.QPack if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldWithoutNameReferencePrefix, out intResult)) { + if (intResult == 0) + { + throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); + } OnStringLength(intResult, State.HeaderName); } else @@ -303,6 +307,10 @@ namespace System.Net.Http.QPack case State.HeaderNameLength: if (_integerDecoder.TryDecode(b, out intResult)) { + if (intResult == 0) + { + throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, "")); + } OnStringLength(intResult, nextState: State.HeaderName); } break; diff --git a/src/Shared/runtime/NetEventSource.Common.cs b/src/Shared/runtime/NetEventSource.Common.cs new file mode 100644 index 0000000000..46cd2ee685 --- /dev/null +++ b/src/Shared/runtime/NetEventSource.Common.cs @@ -0,0 +1,738 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if DEBUG +// Uncomment to enable runtime checks to help validate that NetEventSource isn't being misused +// in a way that will cause performance problems, e.g. unexpected boxing of value types. +//#define DEBUG_NETEVENTSOURCE_MISUSE +#endif + +#nullable enable +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET46 +using System.Security; +#endif + +#pragma warning disable CA1823 // not all IDs are used by all partial providers + +namespace System.Net +{ + // Implementation: + // This partial file is meant to be consumed into each System.Net.* assembly that needs to log. Each such assembly also provides + // its own NetEventSource partial class that adds an appropriate [EventSource] attribute, giving it a unique name for that assembly. + // Those partials can then also add additional events if needed, starting numbering from the NextAvailableEventId defined by this partial. + + // Usage: + // - Operations that may allocate (e.g. boxing a value type, using string interpolation, etc.) or that may have computations + // at call sites should guard access like: + // if (NetEventSource.IsEnabled) NetEventSource.Enter(this, refArg1, valueTypeArg2); // entering an instance method with a value type arg + // if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Found certificate: {cert}"); // info logging with a formattable string + // - Operations that have zero allocations / measurable computations at call sites can use a simpler pattern, calling methods like: + // NetEventSource.Enter(this); // entering an instance method + // NetEventSource.Info(this, "literal string"); // arbitrary message with a literal string + // NetEventSource.Enter(this, refArg1, regArg2); // entering an instance method with two reference type arguments + // NetEventSource.Enter(null); // entering a static method + // NetEventSource.Enter(null, refArg1); // entering a static method with one reference type argument + // Debug.Asserts inside the logging methods will help to flag some misuse if the DEBUG_NETEVENTSOURCE_MISUSE compilation constant is defined. + // However, because it can be difficult by observation to understand all of the costs involved, guarding can be done everywhere. + // - NetEventSource.Fail calls typically do not need to be prefixed with an IsEnabled check, even if they allocate, as FailMessage + // should only be used in cases similar to Debug.Fail, where they are not expected to happen in retail builds, and thus extra costs + // don't matter. + // - Messages can be strings, formattable strings, or any other object. Objects (including those used in formattable strings) have special + // formatting applied, controlled by the Format method. Partial specializations can also override this formatting by implementing a partial + // method that takes an object and optionally provides a string representation of it, in case a particular library wants to customize further. + + /// Provides logging facilities for System.Net libraries. +#if NET46 + [SecuritySafeCritical] +#endif + internal sealed partial class NetEventSource : EventSource + { + /// The single event source instance to use for all logging. + public static readonly NetEventSource Log = new NetEventSource(); + + #region Metadata + public class Keywords + { + public const EventKeywords Default = (EventKeywords)0x0001; + public const EventKeywords Debug = (EventKeywords)0x0002; + public const EventKeywords EnterExit = (EventKeywords)0x0004; + } + + private const string MissingMember = "(?)"; + private const string NullInstance = "(null)"; + private const string StaticMethodObject = "(static)"; + private const string NoParameters = ""; + private const int MaxDumpSize = 1024; + + private const int EnterEventId = 1; + private const int ExitEventId = 2; + private const int AssociateEventId = 3; + private const int InfoEventId = 4; + private const int ErrorEventId = 5; + private const int CriticalFailureEventId = 6; + private const int DumpArrayEventId = 7; + + // These events are implemented in NetEventSource.Security.cs. + // Define the ids here so that projects that include NetEventSource.Security.cs will not have conflicts. + private const int EnumerateSecurityPackagesId = 8; + private const int SspiPackageNotFoundId = 9; + private const int AcquireDefaultCredentialId = 10; + private const int AcquireCredentialsHandleId = 11; + private const int InitializeSecurityContextId = 12; + private const int SecurityContextInputBufferId = 13; + private const int SecurityContextInputBuffersId = 14; + private const int AcceptSecuritContextId = 15; + private const int OperationReturnedSomethingId = 16; + + private const int NextAvailableEventId = 17; // Update this value whenever new events are added. Derived types should base all events off of this to avoid conflicts. + #endregion + + #region Events + #region Enter + /// Logs entrance to a method. + /// `this`, or another object that serves to provide context for the operation. + /// A description of the entrance, including any arguments to the call. + /// The calling member. + [NonEvent] + public static void Enter(object? thisOrContextObject, FormattableString? formattableString = null, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(formattableString); + if (IsEnabled) Log.Enter(IdOf(thisOrContextObject), memberName, formattableString != null ? Format(formattableString) : NoParameters); + } + + /// Logs entrance to a method. + /// `this`, or another object that serves to provide context for the operation. + /// The object to log. + /// The calling member. + [NonEvent] + public static void Enter(object? thisOrContextObject, object arg0, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(arg0); + if (IsEnabled) Log.Enter(IdOf(thisOrContextObject), memberName, $"({Format(arg0)})"); + } + + /// Logs entrance to a method. + /// `this`, or another object that serves to provide context for the operation. + /// The first object to log. + /// The second object to log. + /// The calling member. + [NonEvent] + public static void Enter(object? thisOrContextObject, object arg0, object arg1, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(arg0); + DebugValidateArg(arg1); + if (IsEnabled) Log.Enter(IdOf(thisOrContextObject), memberName, $"({Format(arg0)}, {Format(arg1)})"); + } + + /// Logs entrance to a method. + /// `this`, or another object that serves to provide context for the operation. + /// The first object to log. + /// The second object to log. + /// The third object to log. + /// The calling member. + [NonEvent] + public static void Enter(object? thisOrContextObject, object arg0, object arg1, object arg2, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(arg0); + DebugValidateArg(arg1); + DebugValidateArg(arg2); + if (IsEnabled) Log.Enter(IdOf(thisOrContextObject), memberName, $"({Format(arg0)}, {Format(arg1)}, {Format(arg2)})"); + } + + [Event(EnterEventId, Level = EventLevel.Informational, Keywords = Keywords.EnterExit)] + private void Enter(string thisOrContextObject, string? memberName, string parameters) => + WriteEvent(EnterEventId, thisOrContextObject, memberName ?? MissingMember, parameters); + #endregion + + #region Exit + /// Logs exit from a method. + /// `this`, or another object that serves to provide context for the operation. + /// A description of the exit operation, including any return values. + /// The calling member. + [NonEvent] + public static void Exit(object? thisOrContextObject, FormattableString? formattableString = null, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(formattableString); + if (IsEnabled) Log.Exit(IdOf(thisOrContextObject), memberName, formattableString != null ? Format(formattableString) : NoParameters); + } + + /// Logs exit from a method. + /// `this`, or another object that serves to provide context for the operation. + /// A return value from the member. + /// The calling member. + [NonEvent] + public static void Exit(object? thisOrContextObject, object arg0, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(arg0); + if (IsEnabled) Log.Exit(IdOf(thisOrContextObject), memberName, Format(arg0).ToString()); + } + + /// Logs exit from a method. + /// `this`, or another object that serves to provide context for the operation. + /// A return value from the member. + /// A second return value from the member. + /// The calling member. + [NonEvent] + public static void Exit(object? thisOrContextObject, object arg0, object arg1, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(arg0); + DebugValidateArg(arg1); + if (IsEnabled) Log.Exit(IdOf(thisOrContextObject), memberName, $"{Format(arg0)}, {Format(arg1)}"); + } + + [Event(ExitEventId, Level = EventLevel.Informational, Keywords = Keywords.EnterExit)] + private void Exit(string thisOrContextObject, string? memberName, string? result) => + WriteEvent(ExitEventId, thisOrContextObject, memberName ?? MissingMember, result); + #endregion + + #region Info + /// Logs an information message. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Info(object? thisOrContextObject, FormattableString? formattableString = null, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(formattableString); + if (IsEnabled) Log.Info(IdOf(thisOrContextObject), memberName, formattableString != null ? Format(formattableString) : NoParameters); + } + + /// Logs an information message. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Info(object? thisOrContextObject, object? message, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(message); + if (IsEnabled) Log.Info(IdOf(thisOrContextObject), memberName, Format(message).ToString()); + } + + [Event(InfoEventId, Level = EventLevel.Informational, Keywords = Keywords.Default)] + private void Info(string thisOrContextObject, string? memberName, string? message) => + WriteEvent(InfoEventId, thisOrContextObject, memberName ?? MissingMember, message); + #endregion + + #region Error + /// Logs an error message. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Error(object? thisOrContextObject, FormattableString formattableString, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(formattableString); + if (IsEnabled) Log.ErrorMessage(IdOf(thisOrContextObject), memberName, Format(formattableString)); + } + + /// Logs an error message. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Error(object? thisOrContextObject, object message, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(message); + if (IsEnabled) Log.ErrorMessage(IdOf(thisOrContextObject), memberName, Format(message).ToString()); + } + + [Event(ErrorEventId, Level = EventLevel.Error, Keywords = Keywords.Default)] + private void ErrorMessage(string thisOrContextObject, string? memberName, string? message) => + WriteEvent(ErrorEventId, thisOrContextObject, memberName ?? MissingMember, message); + #endregion + + #region Fail + /// Logs a fatal error and raises an assert. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Fail(object? thisOrContextObject, FormattableString formattableString, [CallerMemberName] string? memberName = null) + { + // Don't call DebugValidateArg on args, as we expect Fail to be used in assert/failure situations + // that should never happen in production, and thus we don't care about extra costs. + + if (IsEnabled) Log.CriticalFailure(IdOf(thisOrContextObject), memberName, Format(formattableString)); + Debug.Fail(Format(formattableString), $"{IdOf(thisOrContextObject)}.{memberName}"); + } + + /// Logs a fatal error and raises an assert. + /// `this`, or another object that serves to provide context for the operation. + /// The message to be logged. + /// The calling member. + [NonEvent] + public static void Fail(object? thisOrContextObject, object message, [CallerMemberName] string? memberName = null) + { + // Don't call DebugValidateArg on args, as we expect Fail to be used in assert/failure situations + // that should never happen in production, and thus we don't care about extra costs. + + if (IsEnabled) Log.CriticalFailure(IdOf(thisOrContextObject), memberName, Format(message).ToString()); + Debug.Fail(Format(message).ToString(), $"{IdOf(thisOrContextObject)}.{memberName}"); + } + + [Event(CriticalFailureEventId, Level = EventLevel.Critical, Keywords = Keywords.Debug)] + private void CriticalFailure(string thisOrContextObject, string? memberName, string? message) => + WriteEvent(CriticalFailureEventId, thisOrContextObject, memberName ?? MissingMember, message); + #endregion + + #region DumpBuffer + /// Logs the contents of a buffer. + /// `this`, or another object that serves to provide context for the operation. + /// The buffer to be logged. + /// The calling member. + [NonEvent] + public static void DumpBuffer(object? thisOrContextObject, byte[] buffer, [CallerMemberName] string? memberName = null) + { + DumpBuffer(thisOrContextObject, buffer, 0, buffer.Length, memberName); + } + + /// Logs the contents of a buffer. + /// `this`, or another object that serves to provide context for the operation. + /// The buffer to be logged. + /// The starting offset from which to log. + /// The number of bytes to log. + /// The calling member. + [NonEvent] + public static void DumpBuffer(object? thisOrContextObject, byte[] buffer, int offset, int count, [CallerMemberName] string? memberName = null) + { + if (IsEnabled) + { + if (offset < 0 || offset > buffer.Length - count) + { + Fail(thisOrContextObject, $"Invalid {nameof(DumpBuffer)} Args. Length={buffer.Length}, Offset={offset}, Count={count}", memberName); + return; + } + + count = Math.Min(count, MaxDumpSize); + + byte[] slice = buffer; + if (offset != 0 || count != buffer.Length) + { + slice = new byte[count]; + Buffer.BlockCopy(buffer, offset, slice, 0, count); + } + + Log.DumpBuffer(IdOf(thisOrContextObject), memberName, slice); + } + } + + /// Logs the contents of a buffer. + /// `this`, or another object that serves to provide context for the operation. + /// The starting location of the buffer to be logged. + /// The number of bytes to log. + /// The calling member. + [NonEvent] + public static unsafe void DumpBuffer(object? thisOrContextObject, IntPtr bufferPtr, int count, [CallerMemberName] string? memberName = null) + { + Debug.Assert(bufferPtr != IntPtr.Zero); + Debug.Assert(count >= 0); + + if (IsEnabled) + { + var buffer = new byte[Math.Min(count, MaxDumpSize)]; + fixed (byte* targetPtr = buffer) + { + Buffer.MemoryCopy((byte*)bufferPtr, targetPtr, buffer.Length, buffer.Length); + } + Log.DumpBuffer(IdOf(thisOrContextObject), memberName, buffer); + } + } + + [Event(DumpArrayEventId, Level = EventLevel.Verbose, Keywords = Keywords.Debug)] + private unsafe void DumpBuffer(string thisOrContextObject, string? memberName, byte[] buffer) => + WriteEvent(DumpArrayEventId, thisOrContextObject, memberName ?? MissingMember, buffer); + #endregion + + #region Associate + /// Logs a relationship between two objects. + /// The first object. + /// The second object. + /// The calling member. + [NonEvent] + public static void Associate(object first, object second, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(first); + DebugValidateArg(second); + if (IsEnabled) Log.Associate(IdOf(first), memberName, IdOf(first), IdOf(second)); + } + + /// Logs a relationship between two objects. + /// `this`, or another object that serves to provide context for the operation. + /// The first object. + /// The second object. + /// The calling member. + [NonEvent] + public static void Associate(object? thisOrContextObject, object first, object second, [CallerMemberName] string? memberName = null) + { + DebugValidateArg(thisOrContextObject); + DebugValidateArg(first); + DebugValidateArg(second); + if (IsEnabled) Log.Associate(IdOf(thisOrContextObject), memberName, IdOf(first), IdOf(second)); + } + + [Event(AssociateEventId, Level = EventLevel.Informational, Keywords = Keywords.Default, Message = "[{2}]<-->[{3}]")] + private void Associate(string thisOrContextObject, string? memberName, string first, string second) => + WriteEvent(AssociateEventId, thisOrContextObject, memberName ?? MissingMember, first, second); + #endregion + #endregion + + #region Helpers + [Conditional("DEBUG_NETEVENTSOURCE_MISUSE")] + private static void DebugValidateArg(object? arg) + { + if (!IsEnabled) + { + Debug.Assert(!(arg is ValueType), $"Should not be passing value type {arg?.GetType()} to logging without IsEnabled check"); + Debug.Assert(!(arg is FormattableString), $"Should not be formatting FormattableString \"{arg}\" if tracing isn't enabled"); + } + } + + [Conditional("DEBUG_NETEVENTSOURCE_MISUSE")] + private static void DebugValidateArg(FormattableString? arg) + { + Debug.Assert(IsEnabled || arg == null, $"Should not be formatting FormattableString \"{arg}\" if tracing isn't enabled"); + } + + public static new bool IsEnabled => + Log.IsEnabled(); + + [NonEvent] + public static string IdOf(object? value) => value != null ? value.GetType().Name + "#" + GetHashCode(value) : NullInstance; + + [NonEvent] + public static int GetHashCode(object value) => value?.GetHashCode() ?? 0; + + [NonEvent] + public static object Format(object? value) + { + // If it's null, return a known string for null values + if (value == null) + { + return NullInstance; + } + + // Give another partial implementation a chance to provide its own string representation + string? result = null; + AdditionalCustomizedToString(value, ref result); + if (result != null) + { + return result; + } + + // Format arrays with their element type name and length + if (value is Array arr) + { + return $"{arr.GetType().GetElementType()}[{((Array)value).Length}]"; + } + + // Format ICollections as the name and count + if (value is ICollection c) + { + return $"{c.GetType().Name}({c.Count})"; + } + + // Format SafeHandles as their type, hash code, and pointer value + if (value is SafeHandle handle) + { + return $"{handle.GetType().Name}:{handle.GetHashCode()}(0x{handle.DangerousGetHandle():X})"; + } + + // Format IntPtrs as hex + if (value is IntPtr) + { + return $"0x{value:X}"; + } + + // If the string representation of the instance would just be its type name, + // use its id instead. + string? toString = value.ToString(); + if (toString == null || toString == value.GetType().FullName) + { + return IdOf(value); + } + + // Otherwise, return the original object so that the caller does default formatting. + return value; + } + + [NonEvent] + private static string Format(FormattableString s) + { + switch (s.ArgumentCount) + { + case 0: return s.Format; + case 1: return string.Format(s.Format, Format(s.GetArgument(0))); + case 2: return string.Format(s.Format, Format(s.GetArgument(0)), Format(s.GetArgument(1))); + case 3: return string.Format(s.Format, Format(s.GetArgument(0)), Format(s.GetArgument(1)), Format(s.GetArgument(2))); + default: + object?[] args = s.GetArguments(); + object[] formattedArgs = new object[args.Length]; + for (int i = 0; i < args.Length; i++) + { + formattedArgs[i] = Format(args[i]); + } + return string.Format(s.Format, formattedArgs); + } + } + + static partial void AdditionalCustomizedToString(T value, ref string? result); + #endregion + + #region Custom WriteEvent overloads + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3, string? arg4) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + if (arg2 == null) arg2 = ""; + if (arg3 == null) arg3 = ""; + if (arg4 == null) arg4 = ""; + + fixed (char* string1Bytes = arg1) + fixed (char* string2Bytes = arg2) + fixed (char* string3Bytes = arg3) + fixed (char* string4Bytes = arg4) + { + const int NumEventDatas = 4; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)string1Bytes, + Size = ((arg1.Length + 1) * 2) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)string2Bytes, + Size = ((arg2.Length + 1) * 2) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)string3Bytes, + Size = ((arg3.Length + 1) * 2) + }; + descrs[3] = new EventData + { + DataPointer = (IntPtr)string4Bytes, + Size = ((arg4.Length + 1) * 2) + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, byte[]? arg3) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + if (arg2 == null) arg2 = ""; + if (arg3 == null) arg3 = Array.Empty(); + + fixed (char* arg1Ptr = arg1) + fixed (char* arg2Ptr = arg2) + fixed (byte* arg3Ptr = arg3) + { + int bufferLength = arg3.Length; + const int NumEventDatas = 4; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)arg1Ptr, + Size = (arg1.Length + 1) * sizeof(char) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)arg2Ptr, + Size = (arg2.Length + 1) * sizeof(char) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)(&bufferLength), + Size = 4 + }; + descrs[3] = new EventData + { + DataPointer = (IntPtr)arg3Ptr, + Size = bufferLength + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, int arg2, int arg3, int arg4) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + + fixed (char* arg1Ptr = arg1) + { + const int NumEventDatas = 4; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)(arg1Ptr), + Size = (arg1.Length + 1) * sizeof(char) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)(&arg2), + Size = sizeof(int) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)(&arg3), + Size = sizeof(int) + }; + descrs[3] = new EventData + { + DataPointer = (IntPtr)(&arg4), + Size = sizeof(int) + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, int arg2, string? arg3) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + if (arg3 == null) arg3 = ""; + + fixed (char* arg1Ptr = arg1) + fixed (char* arg3Ptr = arg3) + { + const int NumEventDatas = 3; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)(arg1Ptr), + Size = (arg1.Length + 1) * sizeof(char) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)(&arg2), + Size = sizeof(int) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)(arg3Ptr), + Size = (arg3.Length + 1) * sizeof(char) + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, int arg3) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + if (arg2 == null) arg2 = ""; + + fixed (char* arg1Ptr = arg1) + fixed (char* arg2Ptr = arg2) + { + const int NumEventDatas = 3; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)(arg1Ptr), + Size = (arg1.Length + 1) * sizeof(char) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)(arg2Ptr), + Size = (arg2.Length + 1) * sizeof(char) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)(&arg3), + Size = sizeof(int) + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + + [NonEvent] + private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, string? arg3, int arg4) + { + if (IsEnabled()) + { + if (arg1 == null) arg1 = ""; + if (arg2 == null) arg2 = ""; + if (arg3 == null) arg3 = ""; + + fixed (char* arg1Ptr = arg1) + fixed (char* arg2Ptr = arg2) + fixed (char* arg3Ptr = arg3) + { + const int NumEventDatas = 4; + var descrs = stackalloc EventData[NumEventDatas]; + + descrs[0] = new EventData + { + DataPointer = (IntPtr)(arg1Ptr), + Size = (arg1.Length + 1) * sizeof(char) + }; + descrs[1] = new EventData + { + DataPointer = (IntPtr)(arg2Ptr), + Size = (arg2.Length + 1) * sizeof(char) + }; + descrs[2] = new EventData + { + DataPointer = (IntPtr)(arg3Ptr), + Size = (arg3.Length + 1) * sizeof(char) + }; + descrs[3] = new EventData + { + DataPointer = (IntPtr)(&arg4), + Size = sizeof(int) + }; + + WriteEventCore(eventId, NumEventDatas, descrs); + } + } + } + #endregion + } +} diff --git a/src/Shared/runtime/Quic/Implementations/Mock/MockConnection.cs b/src/Shared/runtime/Quic/Implementations/Mock/MockConnection.cs new file mode 100644 index 0000000000..d9ab07022e --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/Mock/MockConnection.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Binary; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockConnection : QuicConnectionProvider + { + private readonly bool _isClient; + private bool _disposed = false; + private IPEndPoint _remoteEndPoint; + private IPEndPoint _localEndPoint; + private object _syncObject = new object(); + private Socket _socket = null; + private IPEndPoint _peerListenEndPoint = null; + private TcpListener _inboundListener = null; + private long _nextOutboundBidirectionalStream; + private long _nextOutboundUnidirectionalStream; + + // Constructor for outbound connections + internal MockConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) + { + _remoteEndPoint = remoteEndPoint; + _localEndPoint = localEndPoint; + + _isClient = true; + _nextOutboundBidirectionalStream = 0; + _nextOutboundUnidirectionalStream = 2; + } + + // Constructor for accepted inbound connections + internal MockConnection(Socket socket, IPEndPoint peerListenEndPoint, TcpListener inboundListener) + { + _isClient = false; + _nextOutboundBidirectionalStream = 1; + _nextOutboundUnidirectionalStream = 3; + _socket = socket; + _peerListenEndPoint = peerListenEndPoint; + _inboundListener = inboundListener; + _localEndPoint = (IPEndPoint)socket.LocalEndPoint; + _remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; + } + + internal override bool Connected + { + get + { + CheckDisposed(); + + return _socket != null; + } + } + + internal override IPEndPoint LocalEndPoint => new IPEndPoint(_localEndPoint.Address, _localEndPoint.Port); + + internal override IPEndPoint RemoteEndPoint => new IPEndPoint(_remoteEndPoint.Address, _remoteEndPoint.Port); + + internal override SslApplicationProtocol NegotiatedApplicationProtocol => throw new NotImplementedException(); + + internal override async ValueTask ConnectAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (Connected) + { + // TODO: Exception text + throw new InvalidOperationException("Already connected"); + } + + Socket socket = new Socket(_remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(_remoteEndPoint).ConfigureAwait(false); + socket.NoDelay = true; + + _localEndPoint = (IPEndPoint)socket.LocalEndPoint; + + // Listen on a new local endpoint for inbound streams + TcpListener inboundListener = new TcpListener(_localEndPoint.Address, 0); + inboundListener.Start(); + int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; + + // Write inbound listen port to socket so server can read it + byte[] buffer = new byte[4]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + // Read first 4 bytes to get server listen port + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); + IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); + + _socket = socket; + _peerListenEndPoint = peerListenEndPoint; + _inboundListener = inboundListener; + } + + internal override QuicStreamProvider OpenUnidirectionalStream() + { + long streamId; + lock (_syncObject) + { + streamId = _nextOutboundUnidirectionalStream; + _nextOutboundUnidirectionalStream += 4; + } + + return new MockStream(this, streamId, bidirectional: false); + } + + internal override QuicStreamProvider OpenBidirectionalStream() + { + long streamId; + lock (_syncObject) + { + streamId = _nextOutboundBidirectionalStream; + _nextOutboundBidirectionalStream += 4; + } + + return new MockStream(this, streamId, bidirectional: true); + } + + internal override long GetRemoteAvailableUnidirectionalStreamCount() + { + throw new NotImplementedException(); + } + + internal override long GetRemoteAvailableBidirectionalStreamCount() + { + throw new NotImplementedException(); + } + + internal async Task CreateOutboundMockStreamAsync(long streamId) + { + Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(_peerListenEndPoint).ConfigureAwait(false); + socket.NoDelay = true; + + // Write stream ID to socket so server can read it + byte[] buffer = new byte[8]; + BinaryPrimitives.WriteInt64LittleEndian(buffer, streamId); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + return socket; + } + + internal override async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + Socket socket = await _inboundListener.AcceptSocketAsync().ConfigureAwait(false); + + // Read first bytes to get stream ID + byte[] buffer = new byte[8]; + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + long streamId = BinaryPrimitives.ReadInt64LittleEndian(buffer); + + bool clientInitiated = ((streamId & 0b01) == 0); + if (clientInitiated == _isClient) + { + throw new Exception($"Wrong initiator on accepted stream??? streamId={streamId}, _isClient={_isClient}"); + } + + bool bidirectional = ((streamId & 0b10) == 0); + return new MockStream(socket, streamId, bidirectional: bidirectional); + } + + internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) + { + Dispose(); + return default; + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicConnection)); + } + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _socket?.Dispose(); + _socket = null; + + _inboundListener?.Stop(); + _inboundListener = null; + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + _disposed = true; + } + } + + ~MockConnection() + { + Dispose(false); + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/Mock/MockImplementationProvider.cs b/src/Shared/runtime/Quic/Implementations/Mock/MockImplementationProvider.cs new file mode 100644 index 0000000000..b70a113284 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/Mock/MockImplementationProvider.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockImplementationProvider : QuicImplementationProvider + { + internal override QuicListenerProvider CreateListener(QuicListenerOptions options) + { + return new MockListener(options.ListenEndPoint, options.ServerAuthenticationOptions); + } + + internal override QuicConnectionProvider CreateConnection(QuicClientConnectionOptions options) + { + return new MockConnection(options.RemoteEndPoint, options.ClientAuthenticationOptions, options.LocalEndPoint); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/Mock/MockListener.cs b/src/Shared/runtime/Quic/Implementations/Mock/MockListener.cs new file mode 100644 index 0000000000..911f1896b1 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/Mock/MockListener.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Sockets; +using System.Net.Security; +using System.Threading.Tasks; +using System.Threading; +using System.Buffers.Binary; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockListener : QuicListenerProvider + { + private bool _disposed = false; + private SslServerAuthenticationOptions _sslOptions; + private IPEndPoint _listenEndPoint; + private TcpListener _tcpListener = null; + + internal MockListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + if (listenEndPoint == null) + { + throw new ArgumentNullException(nameof(listenEndPoint)); + } + + _sslOptions = sslServerAuthenticationOptions; + _listenEndPoint = listenEndPoint; + + _tcpListener = new TcpListener(listenEndPoint); + } + + // IPEndPoint is mutable, so we must create a new instance every time this is retrieved. + internal override IPEndPoint ListenEndPoint => new IPEndPoint(_listenEndPoint.Address, _listenEndPoint.Port); + + internal override async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + Socket socket = await _tcpListener.AcceptSocketAsync().ConfigureAwait(false); + socket.NoDelay = true; + + // Read first 4 bytes to get client listen port + byte[] buffer = new byte[4]; + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); + IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); + + // Listen on a new local endpoint for inbound streams + TcpListener inboundListener = new TcpListener(_listenEndPoint.Address, 0); + inboundListener.Start(); + int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; + + // Write inbound listen port to socket so client can read it + BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + return new MockConnection(socket, peerListenEndPoint, inboundListener); + } + + internal override void Start() + { + CheckDisposed(); + + _tcpListener.Start(); + + if (_listenEndPoint.Port == 0) + { + // Get auto-assigned port + _listenEndPoint = (IPEndPoint)_tcpListener.LocalEndpoint; + } + } + + internal override void Close() + { + Dispose(); + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicListener)); + } + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _tcpListener?.Stop(); + _tcpListener = null; + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + _disposed = true; + } + } + + ~MockListener() + { + Dispose(false); + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/Mock/MockStream.cs b/src/Shared/runtime/Quic/Implementations/Mock/MockStream.cs new file mode 100644 index 0000000000..187ba680e1 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/Mock/MockStream.cs @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Diagnostics; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockStream : QuicStreamProvider + { + private bool _disposed = false; + private readonly long _streamId; + private bool _canRead; + private bool _canWrite; + + private MockConnection _connection; + + private Socket _socket = null; + + // Constructor for outbound streams + internal MockStream(MockConnection connection, long streamId, bool bidirectional) + { + _connection = connection; + _streamId = streamId; + _canRead = bidirectional; + _canWrite = true; + } + + // Constructor for inbound streams + internal MockStream(Socket socket, long streamId, bool bidirectional) + { + _socket = socket; + _streamId = streamId; + _canRead = true; + _canWrite = bidirectional; + } + + private async ValueTask ConnectAsync(CancellationToken cancellationToken = default) + { + Debug.Assert(_connection != null, "Stream not connected but no connection???"); + + _socket = await _connection.CreateOutboundMockStreamAsync(_streamId).ConfigureAwait(false); + + // Don't need to hold on to the connection any longer. + _connection = null; + } + + internal override long StreamId + { + get + { + CheckDisposed(); + return _streamId; + } + } + + internal override bool CanRead => _canRead; + + internal override int Read(Span buffer) + { + CheckDisposed(); + + if (!_canRead) + { + throw new NotSupportedException(); + } + + return _socket.Receive(buffer); + } + + internal override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canRead) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + + return await _socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + } + + internal override bool CanWrite => _canWrite; + + internal override void Write(ReadOnlySpan buffer) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + _socket.Send(buffer); + } + + internal override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return WriteAsync(buffer, endStream: false, cancellationToken); + } + + internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + + if (endStream) + { + _socket.Shutdown(SocketShutdown.Send); + } + } + + internal override ValueTask WriteAsync(ReadOnlySequence buffers, CancellationToken cancellationToken = default) + { + return WriteAsync(buffers, endStream: false, cancellationToken); + } + internal override async ValueTask WriteAsync(ReadOnlySequence buffers, bool endStream, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + + foreach (ReadOnlyMemory buffer in buffers) + { + await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + } + + if (endStream) + { + _socket.Shutdown(SocketShutdown.Send); + } + } + + internal override ValueTask WriteAsync(ReadOnlyMemory> buffers, CancellationToken cancellationToken = default) + { + return WriteAsync(buffers, endStream: false, cancellationToken); + } + internal override async ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + + foreach (ReadOnlyMemory buffer in buffers.ToArray()) + { + await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + } + + if (endStream) + { + _socket.Shutdown(SocketShutdown.Send); + } + } + + internal override void Flush() + { + CheckDisposed(); + } + + internal override Task FlushAsync(CancellationToken cancellationToken) + { + CheckDisposed(); + + return Task.CompletedTask; + } + + internal override void AbortRead(long errorCode) + { + throw new NotImplementedException(); + } + + internal override void AbortWrite(long errorCode) + { + throw new NotImplementedException(); + } + + + internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + return default; + } + + internal override void Shutdown() + { + CheckDisposed(); + + _socket.Shutdown(SocketShutdown.Send); + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicStream)); + } + } + + public override void Dispose() + { + if (!_disposed) + { + _disposed = true; + + _socket?.Dispose(); + _socket = null; + } + } + + public override ValueTask DisposeAsync() + { + if (!_disposed) + { + _disposed = true; + + _socket?.Dispose(); + _socket = null; + } + + return default; + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs new file mode 100644 index 0000000000..2ecf0eb210 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicAddressHelpers.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Sockets; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicAddressHelpers + { + internal const ushort IPv4 = 2; + internal const ushort IPv6 = 23; + + internal static unsafe IPEndPoint INetToIPEndPoint(SOCKADDR_INET inetAddress) + { + if (inetAddress.si_family == IPv4) + { + return new IPEndPoint(new IPAddress(inetAddress.Ipv4.Address), (ushort)IPAddress.NetworkToHostOrder((short)inetAddress.Ipv4.sin_port)); + } + else + { + return new IPEndPoint(new IPAddress(inetAddress.Ipv6.Address), (ushort)IPAddress.NetworkToHostOrder((short)inetAddress.Ipv6._port)); + } + } + + internal static SOCKADDR_INET IPEndPointToINet(IPEndPoint endpoint) + { + SOCKADDR_INET socketAddress = default; + byte[] buffer = endpoint.Address.GetAddressBytes(); + if (endpoint.Address != IPAddress.Any && endpoint.Address != IPAddress.IPv6Any) + { + switch (endpoint.Address.AddressFamily) + { + case AddressFamily.InterNetwork: + socketAddress.Ipv4.sin_addr0 = buffer[0]; + socketAddress.Ipv4.sin_addr1 = buffer[1]; + socketAddress.Ipv4.sin_addr2 = buffer[2]; + socketAddress.Ipv4.sin_addr3 = buffer[3]; + socketAddress.Ipv4.sin_family = IPv4; + break; + case AddressFamily.InterNetworkV6: + socketAddress.Ipv6._addr0 = buffer[0]; + socketAddress.Ipv6._addr1 = buffer[1]; + socketAddress.Ipv6._addr2 = buffer[2]; + socketAddress.Ipv6._addr3 = buffer[3]; + socketAddress.Ipv6._addr4 = buffer[4]; + socketAddress.Ipv6._addr5 = buffer[5]; + socketAddress.Ipv6._addr6 = buffer[6]; + socketAddress.Ipv6._addr7 = buffer[7]; + socketAddress.Ipv6._addr8 = buffer[8]; + socketAddress.Ipv6._addr9 = buffer[9]; + socketAddress.Ipv6._addr10 = buffer[10]; + socketAddress.Ipv6._addr11 = buffer[11]; + socketAddress.Ipv6._addr12 = buffer[12]; + socketAddress.Ipv6._addr13 = buffer[13]; + socketAddress.Ipv6._addr14 = buffer[14]; + socketAddress.Ipv6._addr15 = buffer[15]; + socketAddress.Ipv6._family = IPv6; + break; + default: + throw new ArgumentException("Only IPv4 or IPv6 are supported"); + } + } + + SetPort(endpoint.Address.AddressFamily, ref socketAddress, endpoint.Port); + return socketAddress; + } + + private static void SetPort(AddressFamily addressFamily, ref SOCKADDR_INET socketAddrInet, int originalPort) + { + ushort convertedPort = (ushort)IPAddress.HostToNetworkOrder((short)originalPort); + switch (addressFamily) + { + case AddressFamily.InterNetwork: + socketAddrInet.Ipv4.sin_port = convertedPort; + break; + case AddressFamily.InterNetworkV6: + default: + socketAddrInet.Ipv6._port = convertedPort; + break; + } + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs new file mode 100644 index 0000000000..30e5cba6cb --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -0,0 +1,361 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal class MsQuicApi : IDisposable + { + private bool _disposed; + + private readonly IntPtr _registrationContext; + + private unsafe MsQuicApi() + { + MsQuicNativeMethods.NativeApi* registration; + + try + { + uint status = Interop.MsQuic.MsQuicOpen(version: 1, out registration); + if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + throw new NotSupportedException(SR.net_quic_notsupported); + } + } + catch (DllNotFoundException) + { + throw new NotSupportedException(SR.net_quic_notsupported); + } + + MsQuicNativeMethods.NativeApi nativeRegistration = *registration; + + RegistrationOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.RegistrationOpen); + RegistrationCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.RegistrationClose); + + SecConfigCreateDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SecConfigCreate); + SecConfigDeleteDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SecConfigDelete); + SessionOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SessionOpen); + SessionCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SessionClose); + SessionShutdownDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SessionShutdown); + + ListenerOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ListenerOpen); + ListenerCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ListenerClose); + ListenerStartDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ListenerStart); + ListenerStopDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ListenerStop); + + ConnectionOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ConnectionOpen); + ConnectionCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ConnectionClose); + ConnectionShutdownDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ConnectionShutdown); + ConnectionStartDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.ConnectionStart); + + StreamOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamOpen); + StreamCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamClose); + StreamStartDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamStart); + StreamShutdownDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamShutdown); + StreamSendDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamSend); + StreamReceiveCompleteDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamReceiveComplete); + StreamReceiveSetEnabledDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.StreamReceiveSetEnabled); + SetContextDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SetContext); + GetContextDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.GetContext); + SetCallbackHandlerDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SetCallbackHandler); + + SetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.SetParam); + GetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + nativeRegistration.GetParam); + + RegistrationOpenDelegate(Encoding.UTF8.GetBytes("SystemNetQuic"), out IntPtr ctx); + _registrationContext = ctx; + } + + internal static MsQuicApi Api { get; } + + internal static bool IsQuicSupported { get; } + + static MsQuicApi() + { + // MsQuicOpen will succeed even if the platform will not support it. It will then fail with unspecified + // platform-specific errors in subsequent callbacks. For now, check for the minimum build we've tested it on. + + // TODO: + // - Hopefully, MsQuicOpen will perform this check for us and give us a consistent error code. + // - Otherwise, dial this in to reflect actual minimum requirements and add some sort of platform + // error code mapping when creating exceptions. + + OperatingSystem ver = Environment.OSVersion; + + if (ver.Platform == PlatformID.Win32NT && ver.Version < new Version(10, 0, 19041, 0)) + { + IsQuicSupported = false; + return; + } + + // TODO: try to initialize TLS 1.3 in SslStream. + + try + { + Api = new MsQuicApi(); + IsQuicSupported = true; + } + catch (NotSupportedException) + { + IsQuicSupported = false; + } + } + + internal MsQuicNativeMethods.RegistrationOpenDelegate RegistrationOpenDelegate { get; } + internal MsQuicNativeMethods.RegistrationCloseDelegate RegistrationCloseDelegate { get; } + + internal MsQuicNativeMethods.SecConfigCreateDelegate SecConfigCreateDelegate { get; } + internal MsQuicNativeMethods.SecConfigDeleteDelegate SecConfigDeleteDelegate { get; } + + internal MsQuicNativeMethods.SessionOpenDelegate SessionOpenDelegate { get; } + internal MsQuicNativeMethods.SessionCloseDelegate SessionCloseDelegate { get; } + internal MsQuicNativeMethods.SessionShutdownDelegate SessionShutdownDelegate { get; } + + internal MsQuicNativeMethods.ListenerOpenDelegate ListenerOpenDelegate { get; } + internal MsQuicNativeMethods.ListenerCloseDelegate ListenerCloseDelegate { get; } + internal MsQuicNativeMethods.ListenerStartDelegate ListenerStartDelegate { get; } + internal MsQuicNativeMethods.ListenerStopDelegate ListenerStopDelegate { get; } + + internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; } + internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; } + internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } + internal MsQuicNativeMethods.ConnectionStartDelegate ConnectionStartDelegate { get; } + + internal MsQuicNativeMethods.StreamOpenDelegate StreamOpenDelegate { get; } + internal MsQuicNativeMethods.StreamCloseDelegate StreamCloseDelegate { get; } + internal MsQuicNativeMethods.StreamStartDelegate StreamStartDelegate { get; } + internal MsQuicNativeMethods.StreamShutdownDelegate StreamShutdownDelegate { get; } + internal MsQuicNativeMethods.StreamSendDelegate StreamSendDelegate { get; } + internal MsQuicNativeMethods.StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; } + internal MsQuicNativeMethods.StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; } + + internal MsQuicNativeMethods.SetContextDelegate SetContextDelegate { get; } + internal MsQuicNativeMethods.GetContextDelegate GetContextDelegate { get; } + internal MsQuicNativeMethods.SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; } + + internal MsQuicNativeMethods.SetParamDelegate SetParamDelegate { get; } + internal MsQuicNativeMethods.GetParamDelegate GetParamDelegate { get; } + + internal unsafe uint UnsafeSetParam( + IntPtr Handle, + uint Level, + uint Param, + MsQuicNativeMethods.QuicBuffer Buffer) + { + return SetParamDelegate( + Handle, + Level, + Param, + Buffer.Length, + Buffer.Buffer); + } + + internal unsafe uint UnsafeGetParam( + IntPtr Handle, + uint Level, + uint Param, + ref MsQuicNativeMethods.QuicBuffer Buffer) + { + uint bufferLength = Buffer.Length; + byte* buf = Buffer.Buffer; + return GetParamDelegate( + Handle, + Level, + Param, + &bufferLength, + buf); + } + + public async ValueTask CreateSecurityConfig(X509Certificate certificate, string certFilePath, string privateKeyFilePath) + { + MsQuicSecurityConfig secConfig = null; + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + uint secConfigCreateStatus = MsQuicStatusCodes.InternalError; + uint createConfigStatus; + IntPtr unmanagedAddr = IntPtr.Zero; + MsQuicNativeMethods.CertFileParams fileParams = default; + + try + { + if (certFilePath != null && privateKeyFilePath != null) + { + fileParams = new MsQuicNativeMethods.CertFileParams + { + CertificateFilePath = Marshal.StringToCoTaskMemUTF8(certFilePath), + PrivateKeyFilePath = Marshal.StringToCoTaskMemUTF8(privateKeyFilePath) + }; + + unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(fileParams)); + Marshal.StructureToPtr(fileParams, unmanagedAddr, fDeleteOld: false); + + createConfigStatus = SecConfigCreateDelegate( + _registrationContext, + (uint)QUIC_SEC_CONFIG_FLAG.CERT_FILE, + certificate.Handle, + null, + IntPtr.Zero, + SecCfgCreateCallbackHandler); + } + else if (certificate != null) + { + createConfigStatus = SecConfigCreateDelegate( + _registrationContext, + (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT, + certificate.Handle, + null, + IntPtr.Zero, + SecCfgCreateCallbackHandler); + } + else + { + // If no certificate is provided, provide a null one. + createConfigStatus = SecConfigCreateDelegate( + _registrationContext, + (uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL, + IntPtr.Zero, + null, + IntPtr.Zero, + SecCfgCreateCallbackHandler); + } + + QuicExceptionHelpers.ThrowIfFailed( + createConfigStatus, + "Could not create security configuration."); + + void SecCfgCreateCallbackHandler( + IntPtr context, + uint status, + IntPtr securityConfig) + { + secConfig = new MsQuicSecurityConfig(this, securityConfig); + secConfigCreateStatus = status; + tcs.SetResult(null); + } + + await tcs.Task.ConfigureAwait(false); + + QuicExceptionHelpers.ThrowIfFailed( + secConfigCreateStatus, + "Could not create security configuration."); + } + finally + { + if (fileParams.CertificateFilePath != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(fileParams.CertificateFilePath); + } + + if (fileParams.PrivateKeyFilePath != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(fileParams.PrivateKeyFilePath); + } + + if (unmanagedAddr != IntPtr.Zero) + { + Marshal.FreeHGlobal(unmanagedAddr); + } + } + + return secConfig; + } + + public IntPtr SessionOpen(byte[] alpn) + { + IntPtr sessionPtr = IntPtr.Zero; + + uint status = SessionOpenDelegate( + _registrationContext, + alpn, + IntPtr.Zero, + ref sessionPtr); + + QuicExceptionHelpers.ThrowIfFailed(status, "Could not open session."); + + return sessionPtr; + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + ~MsQuicApi() + { + Dispose(disposing: false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + RegistrationCloseDelegate?.Invoke(_registrationContext); + + _disposed = true; + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs new file mode 100644 index 0000000000..757bb0da05 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicParameterHelpers + { + internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + { + byte* ptr = stackalloc byte[sizeof(SOCKADDR_INET)]; + QuicBuffer buffer = new QuicBuffer + { + Length = (uint)sizeof(SOCKADDR_INET), + Buffer = ptr + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeGetParam(nativeObject, level, param, ref buffer), + "Could not get SOCKADDR_INET."); + + return *(SOCKADDR_INET*)ptr; + } + + internal static unsafe ushort GetUShortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + { + byte* ptr = stackalloc byte[sizeof(ushort)]; + QuicBuffer buffer = new QuicBuffer() + { + Length = sizeof(ushort), + Buffer = ptr + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeGetParam(nativeObject, level, param, ref buffer), + "Could not get ushort."); + + return *(ushort*)ptr; + } + + internal static unsafe void SetUshortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ushort value) + { + QuicBuffer buffer = new QuicBuffer() + { + Length = sizeof(ushort), + Buffer = (byte*)&value + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeSetParam(nativeObject, level, param, buffer), + "Could not set ushort."); + } + + internal static unsafe ulong GetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + { + byte* ptr = stackalloc byte[sizeof(ulong)]; + QuicBuffer buffer = new QuicBuffer() + { + Length = sizeof(ulong), + Buffer = ptr + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeGetParam(nativeObject, level, param, ref buffer), + "Could not get ulong."); + + return *(ulong*)ptr; + } + + internal static unsafe void SetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ulong value) + { + QuicBuffer buffer = new QuicBuffer() + { + Length = sizeof(ulong), + Buffer = (byte*)&value + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeGetParam(nativeObject, level, param, ref buffer), + "Could not set ulong."); + } + + internal static unsafe void SetSecurityConfig(MsQuicApi api, IntPtr nativeObject, uint level, uint param, IntPtr value) + { + QuicBuffer buffer = new QuicBuffer() + { + Length = (uint)sizeof(void*), + Buffer = (byte*)&value + }; + + QuicExceptionHelpers.ThrowIfFailed( + api.UnsafeSetParam(nativeObject, level, param, buffer), + "Could not set security configuration."); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs new file mode 100644 index 0000000000..58fc811f7c --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + // TODO this will eventually be abstracted to support both Client and Server + // certificates + internal class MsQuicSecurityConfig : IDisposable + { + private bool _disposed; + private MsQuicApi _registration; + + public MsQuicSecurityConfig(MsQuicApi registration, IntPtr nativeObjPtr) + { + _registration = registration; + NativeObjPtr = nativeObjPtr; + } + + public IntPtr NativeObjPtr { get; private set; } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + _registration.SecConfigDeleteDelegate?.Invoke(NativeObjPtr); + NativeObjPtr = IntPtr.Zero; + _disposed = true; + } + + ~MsQuicSecurityConfig() + { + Dispose(disposing: false); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs new file mode 100644 index 0000000000..89dd99f73c --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class MsQuicSession : IDisposable + { + private bool _disposed = false; + private IntPtr _nativeObjPtr; + private bool _opened; + + internal MsQuicSession() + { + if (!MsQuicApi.IsQuicSupported) + { + throw new NotSupportedException(SR.net_quic_notsupported); + } + } + + public IntPtr ConnectionOpen(QuicClientConnectionOptions options) + { + if (!_opened) + { + OpenSession(options.ClientAuthenticationOptions.ApplicationProtocols[0].Protocol.ToArray(), + (ushort)options.MaxBidirectionalStreams, + (ushort)options.MaxUnidirectionalStreams); + } + + QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ConnectionOpenDelegate( + _nativeObjPtr, + MsQuicConnection.NativeCallbackHandler, + IntPtr.Zero, + out IntPtr connectionPtr), + "Could not open the connection."); + + return connectionPtr; + } + + private void OpenSession(byte[] alpn, ushort bidirectionalStreamCount, ushort undirectionalStreamCount) + { + _opened = true; + _nativeObjPtr = MsQuicApi.Api.SessionOpen(alpn); + SetPeerBiDirectionalStreamCount(bidirectionalStreamCount); + SetPeerUnidirectionalStreamCount(undirectionalStreamCount); + } + + // TODO allow for a callback to select the certificate (SNI). + public IntPtr ListenerOpen(QuicListenerOptions options) + { + if (!_opened) + { + OpenSession(options.ServerAuthenticationOptions.ApplicationProtocols[0].Protocol.ToArray(), + (ushort)options.MaxBidirectionalStreams, + (ushort)options.MaxUnidirectionalStreams); + } + + QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerOpenDelegate( + _nativeObjPtr, + MsQuicListener.NativeCallbackHandler, + IntPtr.Zero, + out IntPtr listenerPointer), + "Could not open listener."); + + return listenerPointer; + } + + // TODO call this for graceful shutdown? + public void ShutDown( + QUIC_CONNECTION_SHUTDOWN_FLAG Flags, + ushort ErrorCode) + { + MsQuicApi.Api.SessionShutdownDelegate( + _nativeObjPtr, + (uint)Flags, + ErrorCode); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void SetPeerBiDirectionalStreamCount(ushort count) + { + SetUshortParamter(QUIC_PARAM_SESSION.PEER_BIDI_STREAM_COUNT, count); + } + + public void SetPeerUnidirectionalStreamCount(ushort count) + { + SetUshortParamter(QUIC_PARAM_SESSION.PEER_UNIDI_STREAM_COUNT, count); + } + + private unsafe void SetUshortParamter(QUIC_PARAM_SESSION param, ushort count) + { + var buffer = new MsQuicNativeMethods.QuicBuffer() + { + Length = sizeof(ushort), + Buffer = (byte*)&count + }; + + SetParam(param, buffer); + } + + public void SetDisconnectTimeout(TimeSpan timeout) + { + SetULongParamter(QUIC_PARAM_SESSION.DISCONNECT_TIMEOUT, (ulong)timeout.TotalMilliseconds); + } + + public void SetIdleTimeout(TimeSpan timeout) + { + SetULongParamter(QUIC_PARAM_SESSION.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); + + } + private unsafe void SetULongParamter(QUIC_PARAM_SESSION param, ulong count) + { + var buffer = new MsQuicNativeMethods.QuicBuffer() + { + Length = sizeof(ulong), + Buffer = (byte*)&count + }; + SetParam(param, buffer); + } + + private void SetParam( + QUIC_PARAM_SESSION param, + MsQuicNativeMethods.QuicBuffer buf) + { + QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.UnsafeSetParam( + _nativeObjPtr, + (uint)QUIC_PARAM_LEVEL.SESSION, + (uint)param, + buf), + "Could not set parameter on session."); + } + + ~MsQuicSession() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + MsQuicApi.Api.SessionCloseDelegate?.Invoke(_nativeObjPtr); + _nativeObjPtr = IntPtr.Zero; + + _disposed = true; + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/QuicExceptionHelpers.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/QuicExceptionHelpers.cs new file mode 100644 index 0000000000..1b8ab8ef26 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/QuicExceptionHelpers.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class QuicExceptionHelpers + { + internal static void ThrowIfFailed(uint status, string message = null, Exception innerException = null) + { + if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + throw new QuicException($"{message} Error Code: {MsQuicStatusCodes.GetError(status)}"); + } + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs new file mode 100644 index 0000000000..1db5dc67b4 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs @@ -0,0 +1,81 @@ +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + /// + /// A resettable completion source which can be completed multiple times. + /// Used to make methods async between completed events and their associated async method. + /// + internal class ResettableCompletionSource : IValueTaskSource, IValueTaskSource + { + protected ManualResetValueTaskSourceCore _valueTaskSource; + + public ResettableCompletionSource() + { + _valueTaskSource.RunContinuationsAsynchronously = true; + } + + public ValueTask GetValueTask() + { + return new ValueTask(this, _valueTaskSource.Version); + } + + public ValueTask GetTypelessValueTask() + { + return new ValueTask(this, _valueTaskSource.Version); + } + + public ValueTaskSourceStatus GetStatus(short token) + { + return _valueTaskSource.GetStatus(token); + } + + public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) + { + _valueTaskSource.OnCompleted(continuation, state, token, flags); + } + + public void Complete(T result) + { + _valueTaskSource.SetResult(result); + } + + public void CompleteException(Exception ex) + { + _valueTaskSource.SetException(ex); + } + + public T GetResult(short token) + { + bool isValid = token == _valueTaskSource.Version; + try + { + return _valueTaskSource.GetResult(token); + } + finally + { + if (isValid) + { + _valueTaskSource.Reset(); + } + } + } + + void IValueTaskSource.GetResult(short token) + { + bool isValid = token == _valueTaskSource.Version; + try + { + _valueTaskSource.GetResult(token); + } + finally + { + if (isValid) + { + _valueTaskSource.Reset(); + } + } + } + } + } diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs new file mode 100644 index 0000000000..1d914c2668 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -0,0 +1,416 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic +{ + internal sealed class MsQuicConnection : QuicConnectionProvider + { + private MsQuicSession _session; + + // Pointer to the underlying connection + // TODO replace all IntPtr with SafeHandles + private IntPtr _ptr; + + // Handle to this object for native callbacks. + private GCHandle _handle; + + // Delegate that wraps the static function that will be called when receiving an event. + // TODO investigate if the delegate can be static instead. + private ConnectionCallbackDelegate _connectionDelegate; + + // Endpoint to either connect to or the endpoint already accepted. + private IPEndPoint _localEndPoint; + private readonly IPEndPoint _remoteEndPoint; + + private readonly ResettableCompletionSource _connectTcs = new ResettableCompletionSource(); + private readonly ResettableCompletionSource _shutdownTcs = new ResettableCompletionSource(); + + private bool _disposed; + private bool _connected; + private MsQuicSecurityConfig _securityConfig; + private long _abortErrorCode = -1; + + // Queue for accepted streams + private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() + { + SingleReader = true, + SingleWriter = true, + }); + + // constructor for inbound connections + public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, IntPtr nativeObjPtr) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + _localEndPoint = localEndPoint; + _remoteEndPoint = remoteEndPoint; + _ptr = nativeObjPtr; + + SetCallbackHandler(); + SetIdleTimeout(TimeSpan.FromSeconds(120)); + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + // constructor for outbound connections + public MsQuicConnection(QuicClientConnectionOptions options) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + // TODO need to figure out if/how we want to expose sessions + // Creating a session per connection isn't ideal. + _session = new MsQuicSession(); + _ptr = _session.ConnectionOpen(options); + _remoteEndPoint = options.RemoteEndPoint; + + SetCallbackHandler(); + SetIdleTimeout(options.IdleTimeout); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override IPEndPoint LocalEndPoint + { + get + { + return new IPEndPoint(_localEndPoint.Address, _localEndPoint.Port); + } + } + + internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert, string certFilePath, string privateKeyFilePath) + { + _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert, certFilePath, privateKeyFilePath); + // TODO this isn't being set correctly + MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig.NativeObjPtr); + } + + internal override IPEndPoint RemoteEndPoint => new IPEndPoint(_remoteEndPoint.Address, _remoteEndPoint.Port); + + internal override SslApplicationProtocol NegotiatedApplicationProtocol => throw new NotImplementedException(); + + internal override bool Connected => _connected; + + internal uint HandleEvent(ref ConnectionEvent connectionEvent) + { + uint status = MsQuicStatusCodes.Success; + try + { + switch (connectionEvent.Type) + { + // Connection is connected, can start to create streams. + case QUIC_CONNECTION_EVENT.CONNECTED: + { + status = HandleEventConnected( + connectionEvent); + } + break; + + // Connection is being closed by the transport + case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_TRANSPORT: + { + status = HandleEventShutdownInitiatedByTransport( + connectionEvent); + } + break; + + // Connection is being closed by the peer + case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_PEER: + { + status = HandleEventShutdownInitiatedByPeer( + connectionEvent); + } + break; + + // Connection has been shutdown + case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE: + { + status = HandleEventShutdownComplete( + connectionEvent); + } + break; + + case QUIC_CONNECTION_EVENT.PEER_STREAM_STARTED: + { + status = HandleEventNewStream( + connectionEvent); + } + break; + + case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE: + { + status = HandleEventStreamsAvailable( + connectionEvent); + } + break; + + default: + break; + } + } + catch (Exception) + { + // TODO we may want to either add a debug assert here or return specific error codes + // based on the exception caught. + return MsQuicStatusCodes.InternalError; + } + + return status; + } + + private uint HandleEventConnected(ConnectionEvent connectionEvent) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); + _localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(inetAddress); + + _connected = true; + // I don't believe we need to lock here because + // handle event connected will not be called at the same time as + // handle event shutdown initiated by transport + _connectTcs.Complete(MsQuicStatusCodes.Success); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return MsQuicStatusCodes.Success; + } + + private uint HandleEventShutdownInitiatedByTransport(ConnectionEvent connectionEvent) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + if (!_connected) + { + _connectTcs.CompleteException(new IOException("Connection has been shutdown.")); + } + + _acceptQueue.Writer.Complete(); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventShutdownInitiatedByPeer(ConnectionEvent connectionEvent) + { + _abortErrorCode = connectionEvent.Data.ShutdownBeginPeer.ErrorCode; + _acceptQueue.Writer.Complete(); + return MsQuicStatusCodes.Success; + } + + private uint HandleEventShutdownComplete(ConnectionEvent connectionEvent) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + _shutdownTcs.Complete(MsQuicStatusCodes.Success); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return MsQuicStatusCodes.Success; + } + + private uint HandleEventNewStream(ConnectionEvent connectionEvent) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + MsQuicStream msQuicStream = new MsQuicStream(this, connectionEvent.StreamFlags, connectionEvent.Data.NewStream.Stream, inbound: true); + + _acceptQueue.Writer.TryWrite(msQuicStream); + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventStreamsAvailable(ConnectionEvent connectionEvent) + { + return MsQuicStatusCodes.Success; + } + + internal override async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + MsQuicStream stream; + + try + { + stream = await _acceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + } + catch (ChannelClosedException) + { + throw _abortErrorCode switch + { + -1 => new QuicOperationAbortedException(), // Shutdown initiated by us. + long err => new QuicConnectionAbortedException(err) // Shutdown initiated by peer. + }; + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return stream; + } + + internal override QuicStreamProvider OpenUnidirectionalStream() + { + ThrowIfDisposed(); + + return StreamOpen(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + } + + internal override QuicStreamProvider OpenBidirectionalStream() + { + ThrowIfDisposed(); + + return StreamOpen(QUIC_STREAM_OPEN_FLAG.NONE); + } + + internal override long GetRemoteAvailableUnidirectionalStreamCount() + { + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_UNIDI_STREAM_COUNT); + } + + internal override long GetRemoteAvailableBidirectionalStreamCount() + { + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_BIDI_STREAM_COUNT); + } + + private unsafe void SetIdleTimeout(TimeSpan timeout) + { + MsQuicParameterHelpers.SetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); + } + + internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + QuicExceptionHelpers.ThrowIfFailed( + MsQuicApi.Api.ConnectionStartDelegate( + _ptr, + (ushort)_remoteEndPoint.AddressFamily, + _remoteEndPoint.Address.ToString(), + (ushort)_remoteEndPoint.Port), + "Failed to connect to peer."); + + return _connectTcs.GetTypelessValueTask(); + } + + private MsQuicStream StreamOpen( + QUIC_STREAM_OPEN_FLAG flags) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + IntPtr streamPtr = IntPtr.Zero; + QuicExceptionHelpers.ThrowIfFailed( + MsQuicApi.Api.StreamOpenDelegate( + _ptr, + (uint)flags, + MsQuicStream.NativeCallbackHandler, + IntPtr.Zero, + out streamPtr), + "Failed to open stream to peer."); + + MsQuicStream stream = new MsQuicStream(this, flags, streamPtr, inbound: false); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return stream; + } + + private void SetCallbackHandler() + { + _handle = GCHandle.Alloc(this); + _connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); + MsQuicApi.Api.SetCallbackHandlerDelegate( + _ptr, + _connectionDelegate, + GCHandle.ToIntPtr(_handle)); + } + + private ValueTask ShutdownAsync( + QUIC_CONNECTION_SHUTDOWN_FLAG Flags, + long ErrorCode) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + uint status = MsQuicApi.Api.ConnectionShutdownDelegate( + _ptr, + (uint)Flags, + ErrorCode); + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return _shutdownTcs.GetTypelessValueTask(); + } + + internal static uint NativeCallbackHandler( + IntPtr connection, + IntPtr context, + ref ConnectionEvent connectionEventStruct) + { + GCHandle handle = GCHandle.FromIntPtr(context); + MsQuicConnection quicConnection = (MsQuicConnection)handle.Target; + return quicConnection.HandleEvent(ref connectionEventStruct); + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~MsQuicConnection() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + if (_ptr != IntPtr.Zero) + { + MsQuicApi.Api.ConnectionCloseDelegate?.Invoke(_ptr); + } + + _ptr = IntPtr.Zero; + + if (disposing) + { + _handle.Free(); + _session?.Dispose(); + _securityConfig?.Dispose(); + } + + _disposed = true; + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAG.NONE, errorCode); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MsQuicStream)); + } + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicImplementationProvider.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicImplementationProvider.cs new file mode 100644 index 0000000000..55c5e524ec --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicImplementationProvider.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Net.Security; + +namespace System.Net.Quic.Implementations.MsQuic +{ + internal sealed class MsQuicImplementationProvider : QuicImplementationProvider + { + internal override QuicListenerProvider CreateListener(QuicListenerOptions options) + { + return new MsQuicListener(options); + } + + internal override QuicConnectionProvider CreateConnection(QuicClientConnectionOptions options) + { + return new MsQuicConnection(options); + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs new file mode 100644 index 0000000000..14323d963f --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic +{ + internal sealed class MsQuicListener : QuicListenerProvider, IDisposable + { + // Security configuration for MsQuic + private MsQuicSession _session; + + // Pointer to the underlying listener + // TODO replace all IntPtr with SafeHandles + private IntPtr _ptr; + + // Handle to this object for native callbacks. + private GCHandle _handle; + + // Delegate that wraps the static function that will be called when receiving an event. + private ListenerCallbackDelegate _listenerDelegate; + + // Ssl listening options (ALPN, cert, etc) + private SslServerAuthenticationOptions _sslOptions; + + private QuicListenerOptions _options; + private volatile bool _disposed; + private IPEndPoint _listenEndPoint; + + private readonly Channel _acceptConnectionQueue; + + internal MsQuicListener(QuicListenerOptions options) + { + _session = new MsQuicSession(); + _acceptConnectionQueue = Channel.CreateBounded(new BoundedChannelOptions(options.ListenBacklog) + { + SingleReader = true, + SingleWriter = true + }); + + _options = options; + _sslOptions = options.ServerAuthenticationOptions; + _listenEndPoint = options.ListenEndPoint; + + _ptr = _session.ListenerOpen(options); + } + + internal override IPEndPoint ListenEndPoint + { + get + { + return new IPEndPoint(_listenEndPoint.Address, _listenEndPoint.Port); + } + } + + internal override async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + MsQuicConnection connection; + + try + { + connection = await _acceptConnectionQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + } + catch (ChannelClosedException) + { + throw new QuicOperationAbortedException(); + } + + await connection.SetSecurityConfigForConnection(_sslOptions.ServerCertificate, + _options.CertificateFilePath, + _options.PrivateKeyFilePath); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return connection; + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~MsQuicListener() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + StopAcceptingConnections(); + + if (_ptr != IntPtr.Zero) + { + MsQuicApi.Api.ListenerStopDelegate(_ptr); + MsQuicApi.Api.ListenerCloseDelegate(_ptr); + } + + _ptr = IntPtr.Zero; + + // TODO this call to session dispose hangs. + //_session.Dispose(); + _disposed = true; + } + + internal override void Start() + { + ThrowIfDisposed(); + + SetCallbackHandler(); + + SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); + + QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate( + _ptr, + ref address), + "Failed to start listener."); + + SetListenPort(); + } + + internal override void Close() + { + ThrowIfDisposed(); + + MsQuicApi.Api.ListenerStopDelegate(_ptr); + } + + private unsafe void SetListenPort() + { + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); + + _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(inetAddress); + } + + internal unsafe uint ListenerCallbackHandler( + ref ListenerEvent evt) + { + try + { + switch (evt.Type) + { + case QUIC_LISTENER_EVENT.NEW_CONNECTION: + { + NewConnectionInfo connectionInfo = *(NewConnectionInfo*)evt.Data.NewConnection.Info; + IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(*(SOCKADDR_INET*)connectionInfo.LocalAddress); + IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(*(SOCKADDR_INET*)connectionInfo.RemoteAddress); + MsQuicConnection msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, evt.Data.NewConnection.Connection); + _acceptConnectionQueue.Writer.TryWrite(msQuicConnection); + } + // Always pend the new connection to wait for the security config to be resolved + // TODO this doesn't need to be async always + return MsQuicStatusCodes.Pending; + default: + return MsQuicStatusCodes.InternalError; + } + } + catch (Exception) + { + return MsQuicStatusCodes.InternalError; + } + } + + private void StopAcceptingConnections() + { + _acceptConnectionQueue.Writer.TryComplete(); + } + + internal static uint NativeCallbackHandler( + IntPtr listener, + IntPtr context, + ref ListenerEvent connectionEventStruct) + { + GCHandle handle = GCHandle.FromIntPtr(context); + MsQuicListener quicListener = (MsQuicListener)handle.Target; + + return quicListener.ListenerCallbackHandler(ref connectionEventStruct); + } + + internal void SetCallbackHandler() + { + _handle = GCHandle.Alloc(this); + _listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); + MsQuicApi.Api.SetCallbackHandlerDelegate( + _ptr, + _listenerDelegate, + GCHandle.ToIntPtr(_handle)); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MsQuicStream)); + } + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs new file mode 100644 index 0000000000..536968b55c --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -0,0 +1,1042 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic +{ + internal sealed class MsQuicStream : QuicStreamProvider + { + // Pointer to the underlying stream + // TODO replace all IntPtr with SafeHandles + private readonly IntPtr _ptr; + + // Handle to this object for native callbacks. + private GCHandle _handle; + + // Delegate that wraps the static function that will be called when receiving an event. + private StreamCallbackDelegate _callback; + + // Backing for StreamId + private long _streamId = -1; + + // Resettable completions to be used for multiple calls to send, start, and shutdown. + private readonly ResettableCompletionSource _sendResettableCompletionSource; + + // Resettable completions to be used for multiple calls to receive. + private readonly ResettableCompletionSource _receiveResettableCompletionSource; + + private readonly ResettableCompletionSource _shutdownWriteResettableCompletionSource; + + // Buffers to hold during a call to send. + private MemoryHandle[] _bufferArrays = new MemoryHandle[1]; + private QuicBuffer[] _sendQuicBuffers = new QuicBuffer[1]; + + // Handle to hold when sending. + private GCHandle _sendHandle; + + // Used to check if StartAsync has been called. + private bool _started; + + private ReadState _readState; + private long _readErrorCode = -1; + + private ShutdownWriteState _shutdownState; + + private SendState _sendState; + private long _sendErrorCode = -1; + + // Used by the class to indicate that the stream is m_Readable. + private readonly bool _canRead; + + // Used by the class to indicate that the stream is writable. + private readonly bool _canWrite; + + private volatile bool _disposed = false; + + private List _receiveQuicBuffers = new List(); + + // TODO consider using Interlocked.Exchange instead of a sync if we can avoid it. + private object _sync = new object(); + + // Creates a new MsQuicStream + internal MsQuicStream(MsQuicConnection connection, QUIC_STREAM_OPEN_FLAG flags, IntPtr nativeObjPtr, bool inbound) + { + Debug.Assert(connection != null); + + _ptr = nativeObjPtr; + + _sendResettableCompletionSource = new ResettableCompletionSource(); + _receiveResettableCompletionSource = new ResettableCompletionSource(); + _shutdownWriteResettableCompletionSource = new ResettableCompletionSource(); + SetCallbackHandler(); + + if (inbound) + { + _started = true; + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canRead = true; + } + else + { + _canWrite = true; + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + StartWrites(); + } + } + + internal override bool CanRead => _canRead; + + internal override bool CanWrite => _canWrite; + + internal override long StreamId + { + get + { + ThrowIfDisposed(); + + if (_streamId == -1) + { + _streamId = GetStreamId(); + } + + return _streamId; + } + } + + internal override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return WriteAsync(buffer, endStream: false, cancellationToken); + } + + internal override ValueTask WriteAsync(ReadOnlySequence buffers, CancellationToken cancellationToken = default) + { + return WriteAsync(buffers, endStream: false, cancellationToken); + } + + internal override async ValueTask WriteAsync(ReadOnlySequence buffers, bool endStream, CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken); + + await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE); + + HandleWriteCompletedState(); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override ValueTask WriteAsync(ReadOnlyMemory> buffers, CancellationToken cancellationToken = default) + { + return WriteAsync(buffers, endStream: false, cancellationToken); + } + + internal override async ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken); + + await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE); + + HandleWriteCompletedState(); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken); + + await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE); + + HandleWriteCompletedState(); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + private async ValueTask HandleWriteStartState(CancellationToken cancellationToken) + { + if (!_canWrite) + { + throw new InvalidOperationException("Writing is not allowed on stream."); + } + + lock (_sync) + { + if (_sendState == SendState.Aborted) + { + throw new OperationCanceledException("Sending has already been aborted on the stream"); + } + } + + CancellationTokenRegistration registration = cancellationToken.Register(() => + { + bool shouldComplete = false; + lock (_sync) + { + if (_sendState == SendState.None) + { + _sendState = SendState.Aborted; + shouldComplete = true; + } + } + + if (shouldComplete) + { + _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); + } + }); + + // Make sure start has completed + if (!_started) + { + await _sendResettableCompletionSource.GetTypelessValueTask(); + _started = true; + } + + return registration; + } + + private void HandleWriteCompletedState() + { + lock (_sync) + { + if (_sendState == SendState.Finished) + { + _sendState = SendState.None; + } + } + } + + internal override async ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + if (!_canRead) + { + throw new InvalidOperationException("Reading is not allowed on stream."); + } + + lock (_sync) + { + if (_readState == ReadState.ReadsCompleted) + { + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + return 0; + } + else if (_readState == ReadState.Aborted) + { + throw _readErrorCode switch + { + -1 => new QuicOperationAbortedException(), + long err => new QuicStreamAbortedException(err) + }; + } + } + + using CancellationTokenRegistration registration = cancellationToken.Register(() => + { + bool shouldComplete = false; + lock (_sync) + { + if (_readState == ReadState.None) + { + shouldComplete = true; + } + + _readState = ReadState.Aborted; + } + + if (shouldComplete) + { + _receiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); + } + }); + + // TODO there could potentially be a perf gain by storing the buffer from the inital read + // This reduces the amount of async calls, however it makes it so MsQuic holds onto the buffers + // longer than it needs to. We will need to benchmark this. + int length = (int)await _receiveResettableCompletionSource.GetValueTask(); + + int actual = Math.Min(length, destination.Length); + + static unsafe void CopyToBuffer(Span destinationBuffer, List sourceBuffers) + { + Span slicedBuffer = destinationBuffer; + for (int i = 0; i < sourceBuffers.Count; i++) + { + QuicBuffer nativeBuffer = sourceBuffers[i]; + int length = Math.Min((int)nativeBuffer.Length, slicedBuffer.Length); + new Span(nativeBuffer.Buffer, length).CopyTo(slicedBuffer); + if (length < nativeBuffer.Length) + { + // The buffer passed in was larger that the received data, return + return; + } + slicedBuffer = slicedBuffer.Slice(length); + } + } + + CopyToBuffer(destination.Span, _receiveQuicBuffers); + + lock (_sync) + { + if (_readState == ReadState.IndividualReadComplete) + { + _receiveQuicBuffers.Clear(); + ReceiveComplete(actual); + EnableReceive(); + _readState = ReadState.None; + } + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return actual; + } + + // TODO do we want this to be a synchronization mechanism to cancel a pending read + // If so, we need to complete the read here as well. + internal override void AbortRead(long errorCode) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + lock (_sync) + { + _readState = ReadState.Aborted; + } + + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_RECV, errorCode); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override void AbortWrite(long errorCode) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + bool shouldComplete = false; + + lock (_sync) + { + if (_shutdownState == ShutdownWriteState.None) + { + _shutdownState = ShutdownWriteState.Canceled; + shouldComplete = true; + } + } + + if (shouldComplete) + { + _shutdownWriteResettableCompletionSource.CompleteException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); + } + + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + } + + internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + ThrowIfDisposed(); + + // TODO do anything to stop writes? + using CancellationTokenRegistration registration = cancellationToken.Register(() => + { + bool shouldComplete = false; + lock (_sync) + { + if (_shutdownState == ShutdownWriteState.None) + { + _shutdownState = ShutdownWriteState.Canceled; + shouldComplete = true; + } + } + + if (shouldComplete) + { + _shutdownWriteResettableCompletionSource.CompleteException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); + } + }); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return _shutdownWriteResettableCompletionSource.GetTypelessValueTask(); + } + + internal override void Shutdown() + { + ThrowIfDisposed(); + + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + } + + // TODO consider removing sync-over-async with blocking calls. + internal override int Read(Span buffer) + { + ThrowIfDisposed(); + + return ReadAsync(buffer.ToArray()).GetAwaiter().GetResult(); + } + + internal override void Write(ReadOnlySpan buffer) + { + ThrowIfDisposed(); + + // TODO: optimize this. + WriteAsync(buffer.ToArray()).GetAwaiter().GetResult(); + } + + // MsQuic doesn't support explicit flushing + internal override void Flush() + { + ThrowIfDisposed(); + } + + // MsQuic doesn't support explicit flushing + internal override Task FlushAsync(CancellationToken cancellationToken = default) + { + ThrowIfDisposed(); + + return default; + } + + public override ValueTask DisposeAsync() + { + if (_disposed) + { + return default; + } + + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + CleanupSendState(); + + if (_ptr != IntPtr.Zero) + { + // TODO resolve graceful vs abortive dispose here. Will file a separate issue. + //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); + MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); + } + + _handle.Free(); + + _disposed = true; + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return default; + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~MsQuicStream() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + CleanupSendState(); + + if (_ptr != IntPtr.Zero) + { + // TODO resolve graceful vs abortive dispose here. Will file a separate issue. + //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); + MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); + } + + _handle.Free(); + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + _disposed = true; + } + + private void EnableReceive() + { + MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_ptr, enabled: true); + } + + internal static uint NativeCallbackHandler( + IntPtr stream, + IntPtr context, + ref StreamEvent streamEvent) + { + var handle = GCHandle.FromIntPtr(context); + var quicStream = (MsQuicStream)handle.Target; + + return quicStream.HandleEvent(ref streamEvent); + } + + private uint HandleEvent(ref StreamEvent evt) + { + uint status = MsQuicStatusCodes.Success; + + try + { + switch (evt.Type) + { + // Stream has started. + // Will only be done for outbound streams (inbound streams have already started) + case QUIC_STREAM_EVENT.START_COMPLETE: + status = HandleStartComplete(); + break; + // Received data on the stream + case QUIC_STREAM_EVENT.RECEIVE: + { + status = HandleEventRecv(ref evt); + } + break; + // Send has completed. + // Contains a canceled bool to indicate if the send was canceled. + case QUIC_STREAM_EVENT.SEND_COMPLETE: + { + status = HandleEventSendComplete(ref evt); + } + break; + // Peer has told us to shutdown the reading side of the stream. + case QUIC_STREAM_EVENT.PEER_SEND_SHUTDOWN: + { + status = HandleEventPeerSendShutdown(); + } + break; + // Peer has told us to abort the reading side of the stream. + case QUIC_STREAM_EVENT.PEER_SEND_ABORTED: + { + status = HandleEventPeerSendAborted(ref evt); + } + break; + // Peer has stopped receiving data, don't send anymore. + case QUIC_STREAM_EVENT.PEER_RECEIVE_ABORTED: + { + status = HandleEventPeerRecvAborted(ref evt); + } + break; + // Occurs when shutdown is completed for the send side. + // This only happens for shutdown on sending, not receiving + // Receive shutdown can only be abortive. + case QUIC_STREAM_EVENT.SEND_SHUTDOWN_COMPLETE: + { + status = HandleEventSendShutdownComplete(ref evt); + } + break; + // Shutdown for both sending and receiving is completed. + case QUIC_STREAM_EVENT.SHUTDOWN_COMPLETE: + { + status = HandleEventShutdownComplete(); + } + break; + default: + break; + } + } + catch (Exception) + { + return MsQuicStatusCodes.InternalError; + } + + return status; + } + + private unsafe uint HandleEventRecv(ref MsQuicNativeMethods.StreamEvent evt) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + StreamEventDataRecv receieveEvent = evt.Data.Recv; + for (int i = 0; i < receieveEvent.BufferCount; i++) + { + _receiveQuicBuffers.Add(receieveEvent.Buffers[i]); + } + + bool shouldComplete = false; + lock (_sync) + { + if (_readState == ReadState.None) + { + shouldComplete = true; + } + _readState = ReadState.IndividualReadComplete; + } + + if (shouldComplete) + { + _receiveResettableCompletionSource.Complete((uint)receieveEvent.TotalBufferLength); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Pending; + } + + private uint HandleEventPeerRecvAborted(ref StreamEvent evt) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + bool shouldComplete = false; + lock (_sync) + { + if (_sendState == SendState.None) + { + shouldComplete = true; + } + _sendState = SendState.Aborted; + _sendErrorCode = evt.Data.PeerSendAbort.ErrorCode; + } + + if (shouldComplete) + { + _sendResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_sendErrorCode)); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleStartComplete() + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + bool shouldComplete = false; + lock (_sync) + { + // Check send state before completing as send cancellation is shared between start and send. + if (_sendState == SendState.None) + { + shouldComplete = true; + } + } + + if (shouldComplete) + { + _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventSendShutdownComplete(ref MsQuicNativeMethods.StreamEvent evt) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + bool shouldComplete = false; + lock (_sync) + { + if (_shutdownState == ShutdownWriteState.None) + { + _shutdownState = ShutdownWriteState.Finished; + shouldComplete = true; + } + } + + if (shouldComplete) + { + _shutdownWriteResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventShutdownComplete() + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + bool shouldReadComplete = false; + bool shouldShutdownWriteComplete = false; + + lock (_sync) + { + // This event won't occur within the middle of a receive. + if (NetEventSource.IsEnabled) NetEventSource.Info("Completing resettable event source."); + + if (_readState == ReadState.None) + { + shouldReadComplete = true; + } + + _readState = ReadState.ReadsCompleted; + + if (_shutdownState == ShutdownWriteState.None) + { + _shutdownState = ShutdownWriteState.Finished; + shouldShutdownWriteComplete = true; + } + } + + if (shouldReadComplete) + { + _receiveResettableCompletionSource.Complete(0); + } + + if (shouldShutdownWriteComplete) + { + _shutdownWriteResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventPeerSendAborted(ref StreamEvent evt) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + bool shouldComplete = false; + lock (_sync) + { + if (_readState == ReadState.None) + { + shouldComplete = true; + } + _readState = ReadState.Aborted; + _readErrorCode = evt.Data.PeerSendAbort.ErrorCode; + } + + if (shouldComplete) + { + _receiveResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_readErrorCode)); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventPeerSendShutdown() + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + bool shouldComplete = false; + + lock (_sync) + { + // This event won't occur within the middle of a receive. + if (NetEventSource.IsEnabled) NetEventSource.Info("Completing resettable event source."); + + if (_readState == ReadState.None) + { + shouldComplete = true; + } + + _readState = ReadState.ReadsCompleted; + } + + if (shouldComplete) + { + _receiveResettableCompletionSource.Complete(0); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private uint HandleEventSendComplete(ref StreamEvent evt) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(this); + + CleanupSendState(); + + // TODO throw if a write was canceled. + uint errorCode = evt.Data.SendComplete.Canceled; + + bool shouldComplete = false; + lock (_sync) + { + if (_sendState == SendState.None) + { + _sendState = SendState.Finished; + shouldComplete = true; + } + } + + if (shouldComplete) + { + _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + } + + if (NetEventSource.IsEnabled) NetEventSource.Exit(this); + + return MsQuicStatusCodes.Success; + } + + private void CleanupSendState() + { + if (_sendHandle.IsAllocated) + { + _sendHandle.Free(); + } + + // Callings dispose twice on a memory handle should be okay + foreach (MemoryHandle buffer in _bufferArrays) + { + buffer.Dispose(); + } + } + + private void SetCallbackHandler() + { + _handle = GCHandle.Alloc(this); + + _callback = new StreamCallbackDelegate(NativeCallbackHandler); + MsQuicApi.Api.SetCallbackHandlerDelegate( + _ptr, + _callback, + GCHandle.ToIntPtr(_handle)); + } + + // TODO prevent overlapping sends or consider supporting it. + private unsafe ValueTask SendReadOnlyMemoryAsync( + ReadOnlyMemory buffer, + QUIC_SEND_FLAG flags) + { + if (buffer.IsEmpty) + { + if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + { + // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + } + return default; + } + + MemoryHandle handle = buffer.Pin(); + _sendQuicBuffers[0].Length = (uint)buffer.Length; + _sendQuicBuffers[0].Buffer = (byte*)handle.Pointer; + + _bufferArrays[0] = handle; + + _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + + uint status = MsQuicApi.Api.StreamSendDelegate( + _ptr, + quicBufferPointer, + bufferCount: 1, + (uint)flags, + _ptr); + + if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + CleanupSendState(); + + // TODO this may need to be an aborted exception. + QuicExceptionHelpers.ThrowIfFailed(status, + "Could not send data to peer."); + } + + return _sendResettableCompletionSource.GetTypelessValueTask(); + } + + private unsafe ValueTask SendReadOnlySequenceAsync( + ReadOnlySequence buffers, + QUIC_SEND_FLAG flags) + { + if (buffers.IsEmpty) + { + if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + { + // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + } + return default; + } + + uint count = 0; + + foreach (ReadOnlyMemory buffer in buffers) + { + ++count; + } + + if (_sendQuicBuffers.Length < count) + { + _sendQuicBuffers = new QuicBuffer[count]; + _bufferArrays = new MemoryHandle[count]; + } + + count = 0; + + foreach (ReadOnlyMemory buffer in buffers) + { + MemoryHandle handle = buffer.Pin(); + _sendQuicBuffers[count].Length = (uint)buffer.Length; + _sendQuicBuffers[count].Buffer = (byte*)handle.Pointer; + _bufferArrays[count] = handle; + ++count; + } + + _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + + uint status = MsQuicApi.Api.StreamSendDelegate( + _ptr, + quicBufferPointer, + count, + (uint)flags, + _ptr); + + if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + CleanupSendState(); + + // TODO this may need to be an aborted exception. + QuicExceptionHelpers.ThrowIfFailed(status, + "Could not send data to peer."); + } + + return _sendResettableCompletionSource.GetTypelessValueTask(); + } + + private unsafe ValueTask SendReadOnlyMemoryListAsync( + ReadOnlyMemory> buffers, + QUIC_SEND_FLAG flags) + { + if (buffers.IsEmpty) + { + if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + { + // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. + MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + } + return default; + } + + ReadOnlyMemory[] array = buffers.ToArray(); + + uint length = (uint)array.Length; + + if (_sendQuicBuffers.Length < length) + { + _sendQuicBuffers = new QuicBuffer[length]; + _bufferArrays = new MemoryHandle[length]; + } + + for (int i = 0; i < length; i++) + { + ReadOnlyMemory buffer = array[i]; + MemoryHandle handle = buffer.Pin(); + _sendQuicBuffers[i].Length = (uint)buffer.Length; + _sendQuicBuffers[i].Buffer = (byte*)handle.Pointer; + _bufferArrays[i] = handle; + } + + _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + + uint status = MsQuicApi.Api.StreamSendDelegate( + _ptr, + quicBufferPointer, + length, + (uint)flags, + _ptr); + + if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + CleanupSendState(); + + // TODO this may need to be an aborted exception. + QuicExceptionHelpers.ThrowIfFailed(status, + "Could not send data to peer."); + } + + return _sendResettableCompletionSource.GetTypelessValueTask(); + } + + private void StartWrites() + { + Debug.Assert(!_started); + uint status = MsQuicApi.Api.StreamStartDelegate( + _ptr, + (uint)QUIC_STREAM_START_FLAG.ASYNC); + + QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); + } + + private void ReceiveComplete(int bufferLength) + { + uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_ptr, (ulong)bufferLength); + QuicExceptionHelpers.ThrowIfFailed(status, "Could not complete receive call."); + } + + // This can fail if the stream isn't started. + private unsafe long GetStreamId() + { + return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MsQuicStream)); + } + } + + private enum ReadState + { + None, + IndividualReadComplete, + ReadsCompleted, + Aborted + } + + private enum ShutdownWriteState + { + None, + Canceled, + Finished + } + + private enum SendState + { + None, + Aborted, + Finished + } + } +} diff --git a/src/Shared/runtime/Quic/Implementations/QuicConnectionProvider.cs b/src/Shared/runtime/Quic/Implementations/QuicConnectionProvider.cs new file mode 100644 index 0000000000..d77bf1df76 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/QuicConnectionProvider.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicConnectionProvider : IDisposable + { + internal abstract bool Connected { get; } + + internal abstract IPEndPoint LocalEndPoint { get; } + + internal abstract IPEndPoint RemoteEndPoint { get; } + + internal abstract ValueTask ConnectAsync(CancellationToken cancellationToken = default); + + internal abstract QuicStreamProvider OpenUnidirectionalStream(); + + internal abstract QuicStreamProvider OpenBidirectionalStream(); + + internal abstract long GetRemoteAvailableUnidirectionalStreamCount(); + + internal abstract long GetRemoteAvailableBidirectionalStreamCount(); + + internal abstract ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default); + + internal abstract System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol { get; } + + internal abstract ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default); + + public abstract void Dispose(); + } +} diff --git a/src/Shared/runtime/Quic/Implementations/QuicImplementationProvider.cs b/src/Shared/runtime/Quic/Implementations/QuicImplementationProvider.cs new file mode 100644 index 0000000000..906f456266 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/QuicImplementationProvider.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicImplementationProvider + { + internal QuicImplementationProvider() { } + + internal abstract QuicListenerProvider CreateListener(QuicListenerOptions options); + + internal abstract QuicConnectionProvider CreateConnection(QuicClientConnectionOptions options); + } +} diff --git a/src/Shared/runtime/Quic/Implementations/QuicListenerProvider.cs b/src/Shared/runtime/Quic/Implementations/QuicListenerProvider.cs new file mode 100644 index 0000000000..f533a8bb38 --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/QuicListenerProvider.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicListenerProvider : IDisposable + { + internal abstract IPEndPoint ListenEndPoint { get; } + + internal abstract ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default); + + internal abstract void Start(); + + internal abstract void Close(); + + public abstract void Dispose(); + } +} diff --git a/src/Shared/runtime/Quic/Implementations/QuicStreamProvider.cs b/src/Shared/runtime/Quic/Implementations/QuicStreamProvider.cs new file mode 100644 index 0000000000..1e96e1597a --- /dev/null +++ b/src/Shared/runtime/Quic/Implementations/QuicStreamProvider.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicStreamProvider : IDisposable, IAsyncDisposable + { + internal abstract long StreamId { get; } + + internal abstract bool CanRead { get; } + + internal abstract int Read(Span buffer); + + internal abstract ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default); + + internal abstract void AbortRead(long errorCode); + + internal abstract void AbortWrite(long errorCode); + + internal abstract bool CanWrite { get; } + + internal abstract void Write(ReadOnlySpan buffer); + + internal abstract ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default); + + internal abstract ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, CancellationToken cancellationToken = default); + + internal abstract ValueTask WriteAsync(ReadOnlySequence buffers, CancellationToken cancellationToken = default); + + internal abstract ValueTask WriteAsync(ReadOnlySequence buffers, bool endStream, CancellationToken cancellationToken = default); + + internal abstract ValueTask WriteAsync(ReadOnlyMemory> buffers, CancellationToken cancellationToken = default); + + internal abstract ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default); + + internal abstract ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default); + + internal abstract void Shutdown(); + + internal abstract void Flush(); + + internal abstract Task FlushAsync(CancellationToken cancellationToken); + + public abstract void Dispose(); + + public abstract ValueTask DisposeAsync(); + } +} diff --git a/src/Shared/runtime/Quic/Interop/Interop.MsQuic.cs b/src/Shared/runtime/Quic/Interop/Interop.MsQuic.cs new file mode 100644 index 0000000000..25a5e57755 --- /dev/null +++ b/src/Shared/runtime/Quic/Interop/Interop.MsQuic.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static class MsQuic + { + [DllImport(Libraries.MsQuic)] + internal static unsafe extern uint MsQuicOpen(int version, out MsQuicNativeMethods.NativeApi* registration); + } +} diff --git a/src/Shared/runtime/Quic/Interop/MsQuicEnums.cs b/src/Shared/runtime/Quic/Interop/MsQuicEnums.cs new file mode 100644 index 0000000000..3d294bce9c --- /dev/null +++ b/src/Shared/runtime/Quic/Interop/MsQuicEnums.cs @@ -0,0 +1,167 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + /// + /// Flags to pass when creating a security config. + /// + [Flags] + internal enum QUIC_SEC_CONFIG_FLAG : uint + { + NONE = 0, + CERT_HASH = 0x00000001, + CERT_HASH_STORE = 0x00000002, + CERT_CONTEXT = 0x00000004, + CERT_FILE = 0x00000008, + ENABL_OCSP = 0x00000010, + CERT_NULL = 0xF0000000, + } + + [Flags] + internal enum QUIC_CONNECTION_SHUTDOWN_FLAG : uint + { + NONE = 0x0, + SILENT = 0x1 + } + + [Flags] + internal enum QUIC_STREAM_OPEN_FLAG : uint + { + NONE = 0, + UNIDIRECTIONAL = 0x1, + ZERO_RTT = 0x2, + } + + [Flags] + internal enum QUIC_STREAM_START_FLAG : uint + { + NONE = 0, + FAIL_BLOCKED = 0x1, + IMMEDIATE = 0x2, + ASYNC = 0x4, + } + + [Flags] + internal enum QUIC_STREAM_SHUTDOWN_FLAG : uint + { + NONE = 0, + GRACEFUL = 0x1, + ABORT_SEND = 0x2, + ABORT_RECV = 0x4, + ABORT = ABORT_SEND | ABORT_RECV, + IMMEDIATE = 0x8 + } + + [Flags] + internal enum QUIC_RECEIVE_FLAG : uint + { + NONE = 0, + ZERO_RTT = 0x1, + FIN = 0x02 + } + + [Flags] + internal enum QUIC_SEND_FLAG : uint + { + NONE = 0, + ALLOW_0_RTT = 0x00000001, + FIN = 0x00000002, + } + + internal enum QUIC_PARAM_LEVEL : uint + { + REGISTRATION = 0, + SESSION = 1, + LISTENER = 2, + CONNECTION = 3, + TLS = 4, + STREAM = 5, + } + + internal enum QUIC_PARAM_REGISTRATION : uint + { + RETRY_MEMORY_PERCENT = 0, + CID_PREFIX = 1 + } + + internal enum QUIC_PARAM_SESSION : uint + { + TLS_TICKET_KEY = 0, + PEER_BIDI_STREAM_COUNT = 1, + PEER_UNIDI_STREAM_COUNT = 2, + IDLE_TIMEOUT = 3, + DISCONNECT_TIMEOUT = 4, + MAX_BYTES_PER_KEY = 5 + } + + internal enum QUIC_PARAM_LISTENER : uint + { + LOCAL_ADDRESS = 0, + STATS = 1 + } + + internal enum QUIC_PARAM_CONN : uint + { + QUIC_VERSION = 0, + LOCAL_ADDRESS = 1, + REMOTE_ADDRESS = 2, + IDLE_TIMEOUT = 3, + PEER_BIDI_STREAM_COUNT = 4, + PEER_UNIDI_STREAM_COUNT = 5, + LOCAL_BIDI_STREAM_COUNT = 6, + LOCAL_UNIDI_STREAM_COUNT = 7, + CLOSE_REASON_PHRASE = 8, + STATISTICS = 9, + STATISTICS_PLAT = 10, + CERT_VALIDATION_FLAGS = 11, + KEEP_ALIVE = 12, + DISCONNECT_TIMEOUT = 13, + SEC_CONFIG = 14, + SEND_BUFFERING = 15, + SEND_PACING = 16, + SHARE_UDP_BINDING = 17, + IDEAL_PROCESSOR = 18, + MAX_STREAM_IDS = 19 + } + + internal enum QUIC_PARAM_STREAM : uint + { + ID = 0, + ZERORTT_LENGTH = 1, + IDEAL_SEND_BUFFER = 2 + } + + internal enum QUIC_LISTENER_EVENT : uint + { + NEW_CONNECTION = 0 + } + + internal enum QUIC_CONNECTION_EVENT : uint + { + CONNECTED = 0, + SHUTDOWN_INITIATED_BY_TRANSPORT = 1, + SHUTDOWN_INITIATED_BY_PEER = 2, + SHUTDOWN_COMPLETE = 3, + LOCAL_ADDRESS_CHANGED = 4, + PEER_ADDRESS_CHANGED = 5, + PEER_STREAM_STARTED = 6, + STREAMS_AVAILABLE = 7, + PEER_NEEDS_STREAMS = 8, + IDEAL_PROCESSOR_CHANGED = 9, + } + + internal enum QUIC_STREAM_EVENT : uint + { + START_COMPLETE = 0, + RECEIVE = 1, + SEND_COMPLETE = 2, + PEER_SEND_SHUTDOWN = 3, + PEER_SEND_ABORTED = 4, + PEER_RECEIVE_ABORTED = 5, + SEND_SHUTDOWN_COMPLETE = 6, + SHUTDOWN_COMPLETE = 7, + IDEAL_SEND_BUFFER_SIZE = 8, + } +} diff --git a/src/Shared/runtime/Quic/Interop/MsQuicNativeMethods.cs b/src/Shared/runtime/Quic/Interop/MsQuicNativeMethods.cs new file mode 100644 index 0000000000..aca6b41a58 --- /dev/null +++ b/src/Shared/runtime/Quic/Interop/MsQuicNativeMethods.cs @@ -0,0 +1,488 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + /// + /// Contains all native delegates and structs that are used with MsQuic. + /// + internal static unsafe class MsQuicNativeMethods + { + [StructLayout(LayoutKind.Sequential)] + internal struct NativeApi + { + internal uint Version; + + internal IntPtr SetContext; + internal IntPtr GetContext; + internal IntPtr SetCallbackHandler; + + internal IntPtr SetParam; + internal IntPtr GetParam; + + internal IntPtr RegistrationOpen; + internal IntPtr RegistrationClose; + + internal IntPtr SecConfigCreate; + internal IntPtr SecConfigDelete; + + internal IntPtr SessionOpen; + internal IntPtr SessionClose; + internal IntPtr SessionShutdown; + + internal IntPtr ListenerOpen; + internal IntPtr ListenerClose; + internal IntPtr ListenerStart; + internal IntPtr ListenerStop; + + internal IntPtr ConnectionOpen; + internal IntPtr ConnectionClose; + internal IntPtr ConnectionShutdown; + internal IntPtr ConnectionStart; + + internal IntPtr StreamOpen; + internal IntPtr StreamClose; + internal IntPtr StreamStart; + internal IntPtr StreamShutdown; + internal IntPtr StreamSend; + internal IntPtr StreamReceiveComplete; + internal IntPtr StreamReceiveSetEnabled; + } + + internal delegate uint SetContextDelegate( + IntPtr handle, + IntPtr context); + + internal delegate IntPtr GetContextDelegate( + IntPtr handle); + + internal delegate void SetCallbackHandlerDelegate( + IntPtr handle, + Delegate del, + IntPtr context); + + internal delegate uint SetParamDelegate( + IntPtr handle, + uint level, + uint param, + uint bufferLength, + byte* buffer); + + internal delegate uint GetParamDelegate( + IntPtr handle, + uint level, + uint param, + uint* bufferLength, + byte* buffer); + + internal delegate uint RegistrationOpenDelegate(byte[] appName, out IntPtr registrationContext); + + internal delegate void RegistrationCloseDelegate(IntPtr registrationContext); + + internal delegate void SecConfigCreateCompleteDelegate(IntPtr context, uint status, IntPtr securityConfig); + + internal delegate uint SecConfigCreateDelegate( + IntPtr registrationContext, + uint flags, + IntPtr certificate, + [MarshalAs(UnmanagedType.LPStr)]string principal, + IntPtr context, + SecConfigCreateCompleteDelegate completionHandler); + + internal delegate void SecConfigDeleteDelegate( + IntPtr securityConfig); + + internal delegate uint SessionOpenDelegate( + IntPtr registrationContext, + byte[] utf8String, + IntPtr context, + ref IntPtr session); + + internal delegate void SessionCloseDelegate( + IntPtr session); + + internal delegate void SessionShutdownDelegate( + IntPtr session, + uint flags, + ushort errorCode); + + [StructLayout(LayoutKind.Sequential)] + internal struct ListenerEvent + { + internal QUIC_LISTENER_EVENT Type; + internal ListenerEventDataUnion Data; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct ListenerEventDataUnion + { + [FieldOffset(0)] + internal ListenerEventDataNewConnection NewConnection; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ListenerEventDataNewConnection + { + internal IntPtr Info; + internal IntPtr Connection; + internal IntPtr SecurityConfig; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NewConnectionInfo + { + internal uint QuicVersion; + internal IntPtr LocalAddress; + internal IntPtr RemoteAddress; + internal ushort CryptoBufferLength; + internal ushort AlpnListLength; + internal ushort ServerNameLength; + internal IntPtr CryptoBuffer; + internal IntPtr AlpnList; + internal IntPtr ServerName; + } + + internal delegate uint ListenerCallbackDelegate( + IntPtr listener, + IntPtr context, + ref ListenerEvent evt); + + internal delegate uint ListenerOpenDelegate( + IntPtr session, + ListenerCallbackDelegate handler, + IntPtr context, + out IntPtr listener); + + internal delegate uint ListenerCloseDelegate( + IntPtr listener); + + internal delegate uint ListenerStartDelegate( + IntPtr listener, + ref SOCKADDR_INET localAddress); + + internal delegate uint ListenerStopDelegate( + IntPtr listener); + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataConnected + { + internal bool EarlyDataAccepted; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataShutdownBegin + { + internal uint Status; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataShutdownBeginPeer + { + internal long ErrorCode; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataShutdownComplete + { + internal bool TimedOut; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataLocalAddrChanged + { + internal IntPtr Address; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataPeerAddrChanged + { + internal IntPtr Address; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataNewStream + { + internal IntPtr Stream; + internal QUIC_STREAM_OPEN_FLAG Flags; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataStreamsAvailable + { + internal ushort BiDirectionalCount; + internal ushort UniDirectionalCount; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEventDataIdealSendBuffer + { + internal ulong NumBytes; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct ConnectionEventDataUnion + { + [FieldOffset(0)] + internal ConnectionEventDataConnected Connected; + + [FieldOffset(0)] + internal ConnectionEventDataShutdownBegin ShutdownBegin; + + [FieldOffset(0)] + internal ConnectionEventDataShutdownBeginPeer ShutdownBeginPeer; + + [FieldOffset(0)] + internal ConnectionEventDataShutdownComplete ShutdownComplete; + + [FieldOffset(0)] + internal ConnectionEventDataLocalAddrChanged LocalAddrChanged; + + [FieldOffset(0)] + internal ConnectionEventDataPeerAddrChanged PeerAddrChanged; + + [FieldOffset(0)] + internal ConnectionEventDataNewStream NewStream; + + [FieldOffset(0)] + internal ConnectionEventDataStreamsAvailable StreamsAvailable; + + [FieldOffset(0)] + internal ConnectionEventDataIdealSendBuffer IdealSendBuffer; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ConnectionEvent + { + internal QUIC_CONNECTION_EVENT Type; + internal ConnectionEventDataUnion Data; + + internal bool EarlyDataAccepted => Data.Connected.EarlyDataAccepted; + internal ulong NumBytes => Data.IdealSendBuffer.NumBytes; + internal uint ShutdownBeginStatus => Data.ShutdownBegin.Status; + internal long ShutdownBeginPeerStatus => Data.ShutdownBeginPeer.ErrorCode; + internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut; + internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount; + internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount; + internal QUIC_STREAM_OPEN_FLAG StreamFlags => Data.NewStream.Flags; + } + + internal delegate uint ConnectionCallbackDelegate( + IntPtr connection, + IntPtr context, + ref ConnectionEvent connectionEvent); + + internal delegate uint ConnectionOpenDelegate( + IntPtr session, + ConnectionCallbackDelegate handler, + IntPtr context, + out IntPtr connection); + + internal delegate uint ConnectionCloseDelegate( + IntPtr connection); + + internal delegate uint ConnectionStartDelegate( + IntPtr connection, + ushort family, + [MarshalAs(UnmanagedType.LPStr)] + string serverName, + ushort serverPort); + + internal delegate uint ConnectionShutdownDelegate( + IntPtr connection, + uint flags, + long errorCode); + + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEventDataRecv + { + internal ulong AbsoluteOffset; + internal ulong TotalBufferLength; + internal QuicBuffer* Buffers; + internal uint BufferCount; + internal uint Flags; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct StreamEventDataSendComplete + { + [FieldOffset(0)] + internal byte Canceled; + [FieldOffset(1)] + internal IntPtr ClientContext; + + internal bool IsCanceled() + { + return Canceled != 0; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEventDataPeerSendAbort + { + internal long ErrorCode; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEventDataPeerRecvAbort + { + internal long ErrorCode; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEventDataSendShutdownComplete + { + internal byte Graceful; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct StreamEventDataUnion + { + [FieldOffset(0)] + internal StreamEventDataRecv Recv; + + [FieldOffset(0)] + internal StreamEventDataSendComplete SendComplete; + + [FieldOffset(0)] + internal StreamEventDataPeerSendAbort PeerSendAbort; + + [FieldOffset(0)] + internal StreamEventDataPeerRecvAbort PeerRecvAbort; + + [FieldOffset(0)] + internal StreamEventDataSendShutdownComplete SendShutdownComplete; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEvent + { + internal QUIC_STREAM_EVENT Type; + internal StreamEventDataUnion Data; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SOCKADDR_IN + { + internal ushort sin_family; + internal ushort sin_port; + internal byte sin_addr0; + internal byte sin_addr1; + internal byte sin_addr2; + internal byte sin_addr3; + + internal byte[] Address + { + get + { + return new byte[] { sin_addr0, sin_addr1, sin_addr2, sin_addr3 }; + } + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SOCKADDR_IN6 + { + internal ushort _family; + internal ushort _port; + internal uint _flowinfo; + internal byte _addr0; + internal byte _addr1; + internal byte _addr2; + internal byte _addr3; + internal byte _addr4; + internal byte _addr5; + internal byte _addr6; + internal byte _addr7; + internal byte _addr8; + internal byte _addr9; + internal byte _addr10; + internal byte _addr11; + internal byte _addr12; + internal byte _addr13; + internal byte _addr14; + internal byte _addr15; + internal uint _scope_id; + + internal byte[] Address + { + get + { + return new byte[] { + _addr0, _addr1, _addr2, _addr3, + _addr4, _addr5, _addr6, _addr7, + _addr8, _addr9, _addr10, _addr11, + _addr12, _addr13, _addr14, _addr15 }; + } + } + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] + internal struct SOCKADDR_INET + { + [FieldOffset(0)] + internal SOCKADDR_IN Ipv4; + [FieldOffset(0)] + internal SOCKADDR_IN6 Ipv6; + [FieldOffset(0)] + internal ushort si_family; + } + + internal delegate uint StreamCallbackDelegate( + IntPtr stream, + IntPtr context, + ref StreamEvent streamEvent); + + internal delegate uint StreamOpenDelegate( + IntPtr connection, + uint flags, + StreamCallbackDelegate handler, + IntPtr context, + out IntPtr stream); + + internal delegate uint StreamStartDelegate( + IntPtr stream, + uint flags); + + internal delegate uint StreamCloseDelegate( + IntPtr stream); + + internal delegate uint StreamShutdownDelegate( + IntPtr stream, + uint flags, + long errorCode); + + internal delegate uint StreamSendDelegate( + IntPtr stream, + QuicBuffer* buffers, + uint bufferCount, + uint flags, + IntPtr clientSendContext); + + internal delegate uint StreamReceiveCompleteDelegate( + IntPtr stream, + ulong bufferLength); + + internal delegate uint StreamReceiveSetEnabledDelegate( + IntPtr stream, + bool enabled); + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct QuicBuffer + { + internal uint Length; + internal byte* Buffer; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CertFileParams + { + internal IntPtr CertificateFilePath; + internal IntPtr PrivateKeyFilePath; + } + } +} diff --git a/src/Shared/runtime/Quic/Interop/MsQuicStatusCodes.cs b/src/Shared/runtime/Quic/Interop/MsQuicStatusCodes.cs new file mode 100644 index 0000000000..72c35687eb --- /dev/null +++ b/src/Shared/runtime/Quic/Interop/MsQuicStatusCodes.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicStatusCodes + { + internal static readonly uint Success = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.Success : Linux.Success; + internal static readonly uint Pending = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.Pending : Linux.Pending; + internal static readonly uint InternalError = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.InternalError : Linux.InternalError; + + // TODO return better error messages here. + public static string GetError(uint status) + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Windows.GetError(status) : Linux.GetError(status); + } + + private static class Windows + { + internal const uint Success = 0; + internal const uint Pending = 0x703E5; + internal const uint Continue = 0x704DE; + internal const uint OutOfMemory = 0x8007000E; + internal const uint InvalidParameter = 0x80070057; + internal const uint InvalidState = 0x8007139F; + internal const uint NotSupported = 0x80004002; + internal const uint NotFound = 0x80070490; + internal const uint BufferTooSmall = 0x8007007A; + internal const uint HandshakeFailure = 0x80410000; + internal const uint Aborted = 0x80004004; + internal const uint AddressInUse = 0x80072740; + internal const uint ConnectionTimeout = 0x800704CF; + internal const uint ConnectionIdle = 0x800704D4; + internal const uint InternalError = 0x80004005; + internal const uint ServerBusy = 0x800704C9; + internal const uint ProtocolError = 0x800704CD; + internal const uint HostUnreachable = 0x800704D0; + internal const uint VerNegError = 0x80410001; + + // TODO return better error messages here. + public static string GetError(uint status) + { + return status switch + { + Success => "SUCCESS", + Pending => "PENDING", + Continue => "CONTINUE", + OutOfMemory => "OUT_OF_MEMORY", + InvalidParameter => "INVALID_PARAMETER", + InvalidState => "INVALID_STATE", + NotSupported => "NOT_SUPPORTED", + NotFound => "NOT_FOUND", + BufferTooSmall => "BUFFER_TOO_SMALL", + HandshakeFailure => "HANDSHAKE_FAILURE", + Aborted => "ABORTED", + AddressInUse => "ADDRESS_IN_USE", + ConnectionTimeout => "CONNECTION_TIMEOUT", + ConnectionIdle => "CONNECTION_IDLE", + InternalError => "INTERNAL_ERROR", + ServerBusy => "SERVER_BUSY", + ProtocolError => "PROTOCOL_ERROR", + VerNegError => "VER_NEG_ERROR", + _ => status.ToString() + }; + } + } + + private static class Linux + { + internal const uint Success = 0; + internal const uint Pending = unchecked((uint)-2); + internal const uint Continue = unchecked((uint)-1); + internal const uint OutOfMemory = 12; + internal const uint InvalidParameter = 22; + internal const uint InvalidState = 200000002; + internal const uint NotSupported = 95; + internal const uint NotFound = 2; + internal const uint BufferTooSmall = 75; + internal const uint HandshakeFailure = 200000009; + internal const uint Aborted = 200000008; + internal const uint AddressInUse = 98; + internal const uint ConnectionTimeout = 110; + internal const uint ConnectionIdle = 200000011; + internal const uint InternalError = 200000012; + internal const uint ServerBusy = 200000007; + internal const uint ProtocolError = 200000013; + internal const uint VerNegError = 200000014; + + // TODO return better error messages here. + public static string GetError(uint status) + { + return status switch + { + Success => "SUCCESS", + Pending => "PENDING", + Continue => "CONTINUE", + OutOfMemory => "OUT_OF_MEMORY", + InvalidParameter => "INVALID_PARAMETER", + InvalidState => "INVALID_STATE", + NotSupported => "NOT_SUPPORTED", + NotFound => "NOT_FOUND", + BufferTooSmall => "BUFFER_TOO_SMALL", + HandshakeFailure => "HANDSHAKE_FAILURE", + Aborted => "ABORTED", + AddressInUse => "ADDRESS_IN_USE", + ConnectionTimeout => "CONNECTION_TIMEOUT", + ConnectionIdle => "CONNECTION_IDLE", + InternalError => "INTERNAL_ERROR", + ServerBusy => "SERVER_BUSY", + ProtocolError => "PROTOCOL_ERROR", + VerNegError => "VER_NEG_ERROR", + _ => status.ToString() + }; + } + } + } +} diff --git a/src/Shared/runtime/Quic/Interop/MsQuicStatusHelper.cs b/src/Shared/runtime/Quic/Interop/MsQuicStatusHelper.cs new file mode 100644 index 0000000000..f08eb861d2 --- /dev/null +++ b/src/Shared/runtime/Quic/Interop/MsQuicStatusHelper.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicStatusHelper + { + internal static bool SuccessfulStatusCode(uint status) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return status < 0x80000000; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return (int)status <= 0; + } + + return false; + } + } +} diff --git a/src/Shared/runtime/Quic/NetEventSource.Quic.cs b/src/Shared/runtime/Quic/NetEventSource.Quic.cs new file mode 100644 index 0000000000..921808829d --- /dev/null +++ b/src/Shared/runtime/Quic/NetEventSource.Quic.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Tracing; + +namespace System.Net +{ + [EventSource(Name = "Microsoft-System-Net-Quic")] + internal sealed partial class NetEventSource : EventSource + { + } +} diff --git a/src/Shared/runtime/Quic/QuicClientConnectionOptions.cs b/src/Shared/runtime/Quic/QuicClientConnectionOptions.cs new file mode 100644 index 0000000000..a9a9b0ec40 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicClientConnectionOptions.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic +{ + /// + /// Options to provide to the when connecting to a Listener. + /// + internal class QuicClientConnectionOptions + { + /// + /// Client authentication options to use when establishing a . + /// + public SslClientAuthenticationOptions ClientAuthenticationOptions { get; set; } + + /// + /// The local endpoint that will be bound to. + /// + public IPEndPoint LocalEndPoint { get; set; } + + /// + /// The endpoint to connect to. + /// + public IPEndPoint RemoteEndPoint { get; set; } + + /// + /// Limit on the number of bidirectional streams the peer connection can create + /// on an accepted connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxBidirectionalStreams { get; set; } = 100; + + /// + /// Limit on the number of unidirectional streams the peer connection can create + /// on an accepted connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxUnidirectionalStreams { get; set; } = 100; + + /// + /// Idle timeout for connections, afterwhich the connection will be closed. + /// + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(2); + } +} diff --git a/src/Shared/runtime/Quic/QuicConnection.cs b/src/Shared/runtime/Quic/QuicConnection.cs new file mode 100644 index 0000000000..877421a94f --- /dev/null +++ b/src/Shared/runtime/Quic/QuicConnection.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Quic.Implementations; +using System.Net.Quic.Implementations.MsQuic.Internal; +using System.Net.Security; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic +{ + internal sealed class QuicConnection : IDisposable + { + private readonly QuicConnectionProvider _provider; + + public static bool IsQuicSupported => MsQuicApi.IsQuicSupported; + + /// + /// Create an outbound QUIC connection. + /// + /// The remote endpoint to connect to. + /// TLS options + /// The local endpoint to connect from. + public QuicConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) + : this(QuicImplementationProviders.Default, remoteEndPoint, sslClientAuthenticationOptions, localEndPoint) + { + } + + // !!! TEMPORARY: Remove "implementationProvider" before shipping + public QuicConnection(QuicImplementationProvider implementationProvider, IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) + : this(implementationProvider, new QuicClientConnectionOptions() { RemoteEndPoint = remoteEndPoint, ClientAuthenticationOptions = sslClientAuthenticationOptions, LocalEndPoint = localEndPoint }) + { + } + + public QuicConnection(QuicImplementationProvider implementationProvider, QuicClientConnectionOptions options) + { + _provider = implementationProvider.CreateConnection(options); + } + + internal QuicConnection(QuicConnectionProvider provider) + { + _provider = provider; + } + + /// + /// Indicates whether the QuicConnection is connected. + /// + public bool Connected => _provider.Connected; + + public IPEndPoint LocalEndPoint => _provider.LocalEndPoint; + + public IPEndPoint RemoteEndPoint => _provider.RemoteEndPoint; + + public SslApplicationProtocol NegotiatedApplicationProtocol => _provider.NegotiatedApplicationProtocol; + + /// + /// Connect to the remote endpoint. + /// + /// + /// + public ValueTask ConnectAsync(CancellationToken cancellationToken = default) => _provider.ConnectAsync(cancellationToken); + + /// + /// Create an outbound unidirectional stream. + /// + /// + public QuicStream OpenUnidirectionalStream() => new QuicStream(_provider.OpenUnidirectionalStream()); + + /// + /// Create an outbound bidirectional stream. + /// + /// + public QuicStream OpenBidirectionalStream() => new QuicStream(_provider.OpenBidirectionalStream()); + + /// + /// Accept an incoming stream. + /// + /// + public async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) => new QuicStream(await _provider.AcceptStreamAsync(cancellationToken).ConfigureAwait(false)); + + /// + /// Close the connection and terminate any active streams. + /// + public ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) => _provider.CloseAsync(errorCode, cancellationToken); + + public void Dispose() => _provider.Dispose(); + + /// + /// Gets the maximum number of bidirectional streams that can be made to the peer. + /// + public long GetRemoteAvailableUnidirectionalStreamCount() => _provider.GetRemoteAvailableUnidirectionalStreamCount(); + + /// + /// Gets the maximum number of unidirectional streams that can be made to the peer. + /// + public long GetRemoteAvailableBidirectionalStreamCount() => _provider.GetRemoteAvailableBidirectionalStreamCount(); + } +} diff --git a/src/Shared/runtime/Quic/QuicConnectionAbortedException.cs b/src/Shared/runtime/Quic/QuicConnectionAbortedException.cs new file mode 100644 index 0000000000..41f4b32998 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicConnectionAbortedException.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + internal class QuicConnectionAbortedException : QuicException + { + internal QuicConnectionAbortedException(long errorCode) + : this(SR.Format(SR.net_quic_connectionaborted, errorCode), errorCode) + { + } + + public QuicConnectionAbortedException(string message, long errorCode) + : base (message) + { + ErrorCode = errorCode; + } + + public long ErrorCode { get; } + } +} diff --git a/src/Shared/runtime/Quic/QuicException.cs b/src/Shared/runtime/Quic/QuicException.cs new file mode 100644 index 0000000000..843c2f7592 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicException.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + internal class QuicException : Exception + { + public QuicException(string message) + : base (message) + { + } + } +} diff --git a/src/Shared/runtime/Quic/QuicImplementationProviders.cs b/src/Shared/runtime/Quic/QuicImplementationProviders.cs new file mode 100644 index 0000000000..66a7e0d6df --- /dev/null +++ b/src/Shared/runtime/Quic/QuicImplementationProviders.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + internal static class QuicImplementationProviders + { + public static Implementations.QuicImplementationProvider Mock { get; } = new Implementations.Mock.MockImplementationProvider(); + public static Implementations.QuicImplementationProvider MsQuic { get; } = new Implementations.MsQuic.MsQuicImplementationProvider(); + public static Implementations.QuicImplementationProvider Default => MsQuic; + } +} diff --git a/src/Shared/runtime/Quic/QuicListener.cs b/src/Shared/runtime/Quic/QuicListener.cs new file mode 100644 index 0000000000..8fb0c1e337 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicListener.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Quic.Implementations; +using System.Net.Security; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic +{ + internal sealed class QuicListener : IDisposable + { + private readonly QuicListenerProvider _provider; + + /// + /// Create a QUIC listener on the specified local endpoint and start listening. + /// + /// The local endpoint to listen on. + /// TLS options for the listener. + public QuicListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + : this(QuicImplementationProviders.Default, listenEndPoint, sslServerAuthenticationOptions) + { + } + + // !!! TEMPORARY: Remove "implementationProvider" before shipping + public QuicListener(QuicImplementationProvider implementationProvider, IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + : this(implementationProvider, new QuicListenerOptions() { ListenEndPoint = listenEndPoint, ServerAuthenticationOptions = sslServerAuthenticationOptions }) + { + } + + public QuicListener(QuicImplementationProvider implementationProvider, QuicListenerOptions options) + { + _provider = implementationProvider.CreateListener(options); + } + + public IPEndPoint ListenEndPoint => _provider.ListenEndPoint; + + /// + /// Accept a connection. + /// + /// + public async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) => + new QuicConnection(await _provider.AcceptConnectionAsync(cancellationToken).ConfigureAwait(false)); + + public void Start() => _provider.Start(); + + /// + /// Stop listening and close the listener. + /// + public void Close() => _provider.Close(); + + public void Dispose() => _provider.Dispose(); + } +} diff --git a/src/Shared/runtime/Quic/QuicListenerOptions.cs b/src/Shared/runtime/Quic/QuicListenerOptions.cs new file mode 100644 index 0000000000..f9eae30c3d --- /dev/null +++ b/src/Shared/runtime/Quic/QuicListenerOptions.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic +{ + /// + /// Options to provide to the . + /// + internal class QuicListenerOptions + { + /// + /// Server Ssl options to use for ALPN, SNI, etc. + /// + public SslServerAuthenticationOptions ServerAuthenticationOptions { get; set; } + + /// + /// Optional path to certificate file to configure the security configuration. + /// + public string CertificateFilePath { get; set; } + + /// + /// Optional path to private key file to configure the security configuration. + /// + public string PrivateKeyFilePath { get; set; } + + /// + /// The endpoint to listen on. + /// + public IPEndPoint ListenEndPoint { get; set; } + + /// + /// Number of connections to be held without accepting the connection. + /// + public int ListenBacklog { get; set; } = 512; + + /// + /// Limit on the number of bidirectional streams an accepted connection can create + /// back to the client. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxBidirectionalStreams { get; set; } = 100; + + /// + /// Limit on the number of unidirectional streams the peer connection can create. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxUnidirectionalStreams { get; set; } = 100; + + /// + /// Idle timeout for connections, afterwhich the connection will be closed. + /// + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(10); + } +} diff --git a/src/Shared/runtime/Quic/QuicOperationAbortedException.cs b/src/Shared/runtime/Quic/QuicOperationAbortedException.cs new file mode 100644 index 0000000000..25cd145ee6 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicOperationAbortedException.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + internal class QuicOperationAbortedException : QuicException + { + internal QuicOperationAbortedException() + : base(SR.net_quic_operationaborted) + { + } + + public QuicOperationAbortedException(string message) : base(message) + { + } + } +} diff --git a/src/Shared/runtime/Quic/QuicStream.cs b/src/Shared/runtime/Quic/QuicStream.cs new file mode 100644 index 0000000000..6e52bb0753 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicStream.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.IO; +using System.Net.Quic.Implementations; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic +{ + internal sealed class QuicStream : Stream + { + private readonly QuicStreamProvider _provider; + + internal QuicStream(QuicStreamProvider provider) + { + _provider = provider; + } + + // + // Boilerplate implementation stuff + // + + public override bool CanSeek => false; + public override long Length => throw new NotSupportedException(); + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => + TaskToApm.Begin(ReadAsync(buffer, offset, count, default), callback, state); + + public override int EndRead(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) => + TaskToApm.Begin(WriteAsync(buffer, offset, count, default), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) => + TaskToApm.End(asyncResult); + + private static void ValidateBufferArgs(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + if ((uint)offset > buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if ((uint)count > buffer.Length - offset) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + ValidateBufferArgs(buffer, offset, count); + return Read(buffer.AsSpan(offset, count)); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArgs(buffer, offset, count); + return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateBufferArgs(buffer, offset, count); + Write(buffer.AsSpan(offset, count)); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArgs(buffer, offset, count); + return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + /// + /// QUIC stream ID. + /// + public long StreamId => _provider.StreamId; + + public override bool CanRead => _provider.CanRead; + + public override int Read(Span buffer) => _provider.Read(buffer); + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => _provider.ReadAsync(buffer, cancellationToken); + + public override bool CanWrite => _provider.CanWrite; + + public override void Write(ReadOnlySpan buffer) => _provider.Write(buffer); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffer, cancellationToken); + + public override void Flush() => _provider.Flush(); + + public override Task FlushAsync(CancellationToken cancellationToken) => _provider.FlushAsync(cancellationToken); + + public void AbortRead(long errorCode) => _provider.AbortRead(errorCode); + + public void AbortWrite(long errorCode) => _provider.AbortWrite(errorCode); + + public ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffer, endStream, cancellationToken); + + public ValueTask WriteAsync(ReadOnlySequence buffers, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffers, cancellationToken); + + public ValueTask WriteAsync(ReadOnlySequence buffers, bool endStream, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffers, endStream, cancellationToken); + + public ValueTask WriteAsync(ReadOnlyMemory> buffers, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffers, cancellationToken); + + public ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffers, endStream, cancellationToken); + + public ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) => _provider.ShutdownWriteCompleted(cancellationToken); + + public void Shutdown() => _provider.Shutdown(); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _provider.Dispose(); + } + } + } +} diff --git a/src/Shared/runtime/Quic/QuicStreamAbortedException.cs b/src/Shared/runtime/Quic/QuicStreamAbortedException.cs new file mode 100644 index 0000000000..6e25335f99 --- /dev/null +++ b/src/Shared/runtime/Quic/QuicStreamAbortedException.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + internal class QuicStreamAbortedException : QuicException + { + internal QuicStreamAbortedException(long errorCode) + : this(SR.Format(SR.net_quic_streamaborted, errorCode), errorCode) + { + } + + public QuicStreamAbortedException(string message, long errorCode) + : base(message) + { + ErrorCode = errorCode; + } + + public long ErrorCode { get; } + } +} diff --git a/src/Shared/runtime/SR.Quic.cs b/src/Shared/runtime/SR.Quic.cs new file mode 100644 index 0000000000..6a756f9ca6 --- /dev/null +++ b/src/Shared/runtime/SR.Quic.cs @@ -0,0 +1,20 @@ +// 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. + +namespace System.Net.Quic +{ + internal static partial class SR + { + // The resource generator used in AspNetCore does not create this method. This file fills in that functional gap + // so we don't have to modify the shared source. + internal static string Format(string resourceFormat, params object[] args) + { + if (args != null) + { + return string.Format(resourceFormat, args); + } + + return resourceFormat; + } + } +} diff --git a/src/Shared/runtime/SR.resx b/src/Shared/runtime/SR.resx index bcd721a2c5..c6c8b9bd5b 100644 --- a/src/Shared/runtime/SR.resx +++ b/src/Shared/runtime/SR.resx @@ -156,4 +156,16 @@ Request headers must contain only ASCII characters. + + Connection aborted by peer ({0}). + + + QUIC is not supported on this platform. See http://aka.ms/dotnetquic + + + Operation aborted. + + + Stream aborted by peer ({0}). + \ No newline at end of file diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index f11a9c61c0..a030415f19 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -4,13 +4,15 @@ $(DefaultNetCoreTargetFramework) portable true - CS0649 + CS0649;CS0436 - + + + @@ -38,4 +40,4 @@ - + \ No newline at end of file From 717e589413079f8d2d93ce171898f733e7179517 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 21 Feb 2020 10:31:40 -0800 Subject: [PATCH 13/57] Change SiteExtension reference (#19218) (#19241) * Change SiteExtension reference * disable --- .azure/pipelines/ci.yml | 3 ++- eng/Dependencies.props | 2 ++ eng/Versions.props | 1 + ...osoft.AspNetCore.AzureAppServices.SiteExtension.csproj | 8 ++++---- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index e7e424ae8c..6f20958002 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -176,7 +176,8 @@ stages: -noBuildDeps $(_BuildArgs) $(_InternalRuntimeDownloadArgs) - condition: ne(variables['Build.Reason'], 'PullRequest') + # Disabled until 3.1.3 is released + condition: false displayName: Build SiteExtension # This runs code-signing on all packages, zips, and jar files as defined in build/CodeSign.targets. If https://github.com/dotnet/arcade/issues/1957 is resolved, diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 6a577b9fde..ae4d297ec8 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -120,6 +120,8 @@ and are generated based on the last package release. + + diff --git a/eng/Versions.props b/eng/Versions.props index ab4113b2a6..06d07c0f35 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -231,6 +231,7 @@ 2.1.1 2.2.0 + 3.1.3 0.9.9 0.10.13 diff --git a/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj b/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj index 89cf2174e0..5f51761a31 100644 --- a/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj +++ b/src/SiteExtensions/LoggingAggregate/src/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj @@ -26,11 +26,11 @@ - - + + - - + + From 294b11622d0f106a19300cca2ffd9f9dbe110004 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Fri, 21 Feb 2020 17:28:47 -0800 Subject: [PATCH 14/57] Update precedence and Identity of templates for 5.0 (#19248) --- .../BlazorWasm-CSharp/.template.config/template.json | 2 +- .../BlazorServerWeb-CSharp/.template.config/template.json | 6 +++--- .../content/EmptyWeb-CSharp/.template.config/template.json | 4 ++-- .../content/EmptyWeb-FSharp/.template.config/template.json | 4 ++-- .../GrpcService-CSharp/.template.config/template.json | 4 ++-- .../RazorClassLibrary-CSharp/.template.config/template.json | 4 ++-- .../RazorPagesWeb-CSharp/.template.config/template.json | 6 +++--- .../StarterWeb-CSharp/.template.config/template.json | 6 +++--- .../StarterWeb-FSharp/.template.config/template.json | 6 +++--- .../content/WebApi-CSharp/.template.config/template.json | 4 ++-- .../content/WebApi-FSharp/.template.config/template.json | 4 ++-- .../content/Worker-CSharp/.template.config/template.json | 4 ++-- .../content/Angular-CSharp/.template.config/template.json | 4 ++-- .../content/React-CSharp/.template.config/template.json | 4 ++-- .../ReactRedux-CSharp/.template.config/template.json | 4 ++-- 15 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/ProjectTemplates/BlazorWasm.ProjectTemplates/content/BlazorWasm-CSharp/.template.config/template.json b/src/ProjectTemplates/BlazorWasm.ProjectTemplates/content/BlazorWasm-CSharp/.template.config/template.json index 3297878253..e49f995289 100644 --- a/src/ProjectTemplates/BlazorWasm.ProjectTemplates/content/BlazorWasm-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/BlazorWasm.ProjectTemplates/content/BlazorWasm-CSharp/.template.config/template.json @@ -10,7 +10,7 @@ "defaultName": "WebApplication", "description": "A project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template can be used for web apps with rich dynamic user interfaces (UIs).", "groupIdentity": "Microsoft.Web.Blazor.Wasm", - "precedence": "6001", + "precedence": "7001", "guids": [ "4C26868E-5E7C-458D-82E3-040509D0C71F", "5990939C-7E7B-4CFA-86FF-44CA5756498A", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json index 8a669b652a..66bfdc777b 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json @@ -9,10 +9,10 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template can be used for web apps with rich dynamic user interfaces (UIs).", "groupIdentity": "Microsoft.Web.Blazor.Server", - "precedence": "6000", - "identity": "Microsoft.Web.Blazor.Server.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Blazor.Server.CSharp.5.0", "shortName": "blazorserver", - "thirdPartyNotices": "https://aka.ms/aspnetcore/3.1-third-party-notices", + "thirdPartyNotices": "https://aka.ms/aspnetcore/5.0-third-party-notices", "tags": { "language": "C#", "type": "project" diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json index 08c6bb56c8..caf1ab80e6 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json @@ -9,8 +9,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "An empty project template for creating an ASP.NET Core application. This template does not have any content in it.", "groupIdentity": "Microsoft.Web.Empty", - "precedence": "6000", - "identity": "Microsoft.Web.Empty.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Empty.CSharp.5.0", "shortName": "web", "tags": { "language": "C#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json index 7c9eb6c777..df7b62a169 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-FSharp/.template.config/template.json @@ -8,8 +8,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "An empty project template for creating an ASP.NET Core application. This template does not have any content in it.", "groupIdentity": "Microsoft.Web.Empty", - "precedence": "6000", - "identity": "Microsoft.Web.Empty.FSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Empty.FSharp.5.0", "shortName": "web", "tags": { "language": "F#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json index 0b8a573d28..0ca933164a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json @@ -9,8 +9,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating a gRPC ASP.NET Core service.", "groupIdentity": "Microsoft.Web.Grpc", - "precedence": "6000", - "identity": "Microsoft.Grpc.Service.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Grpc.Service.CSharp.5.0", "shortName": "grpc", "tags": { "language": "C#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorClassLibrary-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorClassLibrary-CSharp/.template.config/template.json index 86a7607fa2..543e0bdd5d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorClassLibrary-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorClassLibrary-CSharp/.template.config/template.json @@ -10,8 +10,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project for creating a Razor class library that targets .NET Standard", "groupIdentity": "Microsoft.Web.Razor", - "precedence": "6000", - "identity": "Microsoft.Web.Razor.Library.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Razor.Library.CSharp.5.0", "shortName": "razorclasslib", "tags": { "language": "C#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json index 2d20ad2d45..81aacfa7e7 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json @@ -10,13 +10,13 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating an ASP.NET Core application with example ASP.NET Core Razor Pages content", "groupIdentity": "Microsoft.Web.RazorPages", - "precedence": "6000", - "identity": "Microsoft.Web.RazorPages.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.RazorPages.CSharp.5.0", "shortName": [ "webapp", "razor" ], - "thirdPartyNotices": "https://aka.ms/aspnetcore/3.1-third-party-notices", + "thirdPartyNotices": "https://aka.ms/aspnetcore/5.0-third-party-notices", "tags": { "language": "C#", "type": "project" diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json index b690f80eff..d846394fa9 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json @@ -9,10 +9,10 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services.", "groupIdentity": "Microsoft.Web.Mvc", - "precedence": "6000", - "identity": "Microsoft.Web.Mvc.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Mvc.CSharp.5.0", "shortName": "mvc", - "thirdPartyNotices": "https://aka.ms/aspnetcore/3.1-third-party-notices", + "thirdPartyNotices": "https://aka.ms/aspnetcore/5.0-third-party-notices", "tags": { "language": "C#", "type": "project" diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json index 487cdfc527..779cf7d82c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/.template.config/template.json @@ -9,10 +9,10 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services.", "groupIdentity": "Microsoft.Web.Mvc", - "precedence": "6000", - "identity": "Microsoft.Web.Mvc.FSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.Mvc.FSharp.5.0", "shortName": "mvc", - "thirdPartyNotices": "https://aka.ms/aspnetcore/3.1-third-party-notices", + "thirdPartyNotices": "https://aka.ms/aspnetcore/5.0-third-party-notices", "tags": { "language": "F#", "type": "project" diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json index 2de35fc292..9c560017be 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json @@ -9,8 +9,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers.", "groupIdentity": "Microsoft.Web.WebApi", - "precedence": "6000", - "identity": "Microsoft.Web.WebApi.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.WebApi.CSharp.5.0", "shortName": "webapi", "tags": { "language": "C#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json index e1639b58b9..b3f4e81300 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/.template.config/template.json @@ -8,8 +8,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers.", "groupIdentity": "Microsoft.Web.WebApi", - "precedence": "6000", - "identity": "Microsoft.Web.WebApi.FSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Web.WebApi.FSharp.5.0", "shortName": "webapi", "tags": { "language": "F#", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json index 16156aef0d..fa3775c564 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json @@ -10,8 +10,8 @@ "generatorVersions": "[1.0.0.0-*)", "description": "An empty project template for creating a worker service.", "groupIdentity": "Microsoft.Worker.Empty", - "precedence": "6000", - "identity": "Microsoft.Worker.Empty.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.Worker.Empty.CSharp.5.0", "shortName": "worker", "tags": { "language": "C#", diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json index f0162aa616..fb376267c2 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/.template.config/template.json @@ -6,8 +6,8 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.Angular", - "precedence": "6000", - "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.Angular.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.Angular.CSharp.5.0", "name": "ASP.NET Core with Angular", "preferNameDirectory": true, "primaryOutputs": [ diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json index 6490d8e061..4b2e6ad786 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/.template.config/template.json @@ -6,8 +6,8 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.React", - "precedence": "6000", - "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.React.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.React.CSharp.5.0", "name": "ASP.NET Core with React.js", "preferNameDirectory": true, "primaryOutputs": [ diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json index 0f2945d10f..f32b4adac4 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/.template.config/template.json @@ -6,8 +6,8 @@ "SPA" ], "groupIdentity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.ReactRedux", - "precedence": "6000", - "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.ReactRedux.CSharp.3.1", + "precedence": "7000", + "identity": "Microsoft.DotNet.Web.Spa.ProjectTemplates.ReactRedux.CSharp.5.0", "name": "ASP.NET Core with React.js and Redux", "preferNameDirectory": true, "primaryOutputs": [ From 71d43cf5df1f9993a27d94585ad67e86d51a0ebd Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2020 12:56:33 -0800 Subject: [PATCH 15/57] [release/5.0-preview1] Update dependencies from dotnet/aspnetcore-tooling (#19259) Commit message --- eng/Version.Details.xml | 360 ++++++++++++++++++++-------------------- eng/Versions.props | 180 ++++++++++---------- global.json | 4 +- 3 files changed, 272 insertions(+), 272 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 60a7f06827..0fb2d9c648 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,21 +13,21 @@ https://github.com/dotnet/blazor dd7fb4d3931d556458f62642c2edfc59f6295bfb - + https://github.com/dotnet/aspnetcore-tooling - 9acda9485be8e2238067508474dfa44fcc34f81e + cc096c2460701eb1072359d939628c0b91c610a1 - + https://github.com/dotnet/aspnetcore-tooling - 9acda9485be8e2238067508474dfa44fcc34f81e + cc096c2460701eb1072359d939628c0b91c610a1 - + https://github.com/dotnet/aspnetcore-tooling - 9acda9485be8e2238067508474dfa44fcc34f81e + cc096c2460701eb1072359d939628c0b91c610a1 - + https://github.com/dotnet/aspnetcore-tooling - 9acda9485be8e2238067508474dfa44fcc34f81e + cc096c2460701eb1072359d939628c0b91c610a1 https://github.com/dotnet/efcore @@ -57,352 +57,352 @@ https://github.com/dotnet/efcore 5c5d7c06ae67eab8694a6c3207eb63ae16d05cd4 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/runtime - 0f3f8e1930c28b67f29990126bc2e8527e959a2e + 3c523a6a7a3ebc25fe524359127b1d8846e23ea3 - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 https://github.com/dotnet/arcade @@ -416,9 +416,9 @@ https://github.com/dotnet/arcade f50767f96246063f33a6565d318f3adf9058bace - + https://github.com/dotnet/extensions - 396aff55e0b4628a7a44375e4b72e5d19a6e37ab + a309c864ab86230b6f118843481673cdd89d6258 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 06d07c0f35..620988bd58 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,96 +66,96 @@ 3.5.0-beta3-20114-02 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 2.1.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 - 5.0.0-preview.1.20113.7 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 2.1.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 + 5.0.0-preview.1.20120.5 - 5.0.0-preview.1.20113.7 + 5.0.0-preview.1.20120.5 3.2.0-preview1.20067.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 - 5.0.0-preview.1.20114.1 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 + 5.0.0-preview.1.20120.4 5.0.0-preview.2.20120.8 5.0.0-preview.2.20120.8 @@ -165,10 +165,10 @@ 5.0.0-preview.2.20120.8 5.0.0-preview.2.20120.8 - 5.0.0-preview.1.20114.5 - 5.0.0-preview.1.20114.5 - 5.0.0-preview.1.20114.5 - 5.0.0-preview.1.20114.5 + 5.0.0-preview.1.20121.3 + 5.0.0-preview.1.20121.3 + 5.0.0-preview.1.20121.3 + 5.0.0-preview.1.20121.3 diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj b/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj index 7c0781eeee..7a7f90811f 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj @@ -1,5 +1,7 @@ + + $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj index 47191a34e2..073fe6ae2e 100644 --- a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj +++ b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj @@ -1,5 +1,7 @@ + + $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/common/testassets/Tests.Utils/InProcessTestServer.cs b/src/SignalR/common/testassets/Tests.Utils/InProcessTestServer.cs index cf300eaf98..a02e4bb1de 100644 --- a/src/SignalR/common/testassets/Tests.Utils/InProcessTestServer.cs +++ b/src/SignalR/common/testassets/Tests.Utils/InProcessTestServer.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj index 9e69675720..e63a400554 100644 --- a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj +++ b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj @@ -1,5 +1,7 @@  + + $(DefaultNetCoreTargetFramework) Microsoft.AspNetCore.SignalR.Tests diff --git a/src/SignalR/common/testassets/Tests.Utils/VerifiableLoggedTest.cs b/src/SignalR/common/testassets/Tests.Utils/VerifiableLoggedTest.cs index 691338e3f9..4901e85969 100644 --- a/src/SignalR/common/testassets/Tests.Utils/VerifiableLoggedTest.cs +++ b/src/SignalR/common/testassets/Tests.Utils/VerifiableLoggedTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Testing; diff --git a/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj b/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj index 15574498b1..f80c8b6f8c 100644 --- a/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj +++ b/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj @@ -1,5 +1,7 @@  + + $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj b/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj index e9395224a6..b8fc1ed952 100644 --- a/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj +++ b/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj @@ -1,5 +1,7 @@  + + $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/server/StackExchangeRedis/test/RedisServerFixture.cs b/src/SignalR/server/StackExchangeRedis/test/RedisServerFixture.cs index 9bc25368f0..ab063ee02a 100644 --- a/src/SignalR/server/StackExchangeRedis/test/RedisServerFixture.cs +++ b/src/SignalR/server/StackExchangeRedis/test/RedisServerFixture.cs @@ -4,6 +4,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Tests; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Xunit; diff --git a/src/Testing/src/AssemblyTestLog.cs b/src/Testing/src/AssemblyTestLog.cs index bd69cd20a3..f5a5314a85 100644 --- a/src/Testing/src/AssemblyTestLog.cs +++ b/src/Testing/src/AssemblyTestLog.cs @@ -10,15 +10,16 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; -using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Serilog; using Serilog.Core; using Serilog.Events; using Serilog.Extensions.Logging; using Xunit.Abstractions; +using ILogger = Microsoft.Extensions.Logging.ILogger; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public class AssemblyTestLog : IDisposable { diff --git a/src/Testing/src/CollectDumpAttribute.cs b/src/Testing/src/CollectDumpAttribute.cs index 5f4a1eee59..24567013d6 100644 --- a/src/Testing/src/CollectDumpAttribute.cs +++ b/src/Testing/src/CollectDumpAttribute.cs @@ -7,9 +7,8 @@ using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Testing; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { /// /// Capture the memory dump upon test failure. diff --git a/src/Testing/src/DumpCollector/DumpCollector.Windows.cs b/src/Testing/src/DumpCollector/DumpCollector.Windows.cs index 20395208d7..7a71ac34cb 100644 --- a/src/Testing/src/DumpCollector/DumpCollector.Windows.cs +++ b/src/Testing/src/DumpCollector/DumpCollector.Windows.cs @@ -8,7 +8,7 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public static partial class DumpCollector { diff --git a/src/Testing/src/DumpCollector/DumpCollector.cs b/src/Testing/src/DumpCollector/DumpCollector.cs index d67e109b38..64ddc80ec6 100644 --- a/src/Testing/src/DumpCollector/DumpCollector.cs +++ b/src/Testing/src/DumpCollector/DumpCollector.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public static partial class DumpCollector { diff --git a/src/Testing/src/LoggedTest/ILoggedTest.cs b/src/Testing/src/LoggedTest/ILoggedTest.cs index 750f45cd91..8a90acd5cd 100644 --- a/src/Testing/src/LoggedTest/ILoggedTest.cs +++ b/src/Testing/src/LoggedTest/ILoggedTest.cs @@ -5,9 +5,10 @@ using System; using System.Reflection; using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public interface ILoggedTest : IDisposable { diff --git a/src/Testing/src/LoggedTest/LoggedTest.cs b/src/Testing/src/LoggedTest/LoggedTest.cs index 169a94f59d..2b6a18dfc7 100644 --- a/src/Testing/src/LoggedTest/LoggedTest.cs +++ b/src/Testing/src/LoggedTest/LoggedTest.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Reflection; -using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; using Xunit.Abstractions; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public class LoggedTest : LoggedTestBase { diff --git a/src/Testing/src/LoggedTest/LoggedTestBase.cs b/src/Testing/src/LoggedTest/LoggedTestBase.cs index 16dde9676c..aa48eb8d51 100644 --- a/src/Testing/src/LoggedTest/LoggedTestBase.cs +++ b/src/Testing/src/LoggedTest/LoggedTestBase.cs @@ -11,10 +11,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; using Serilog; using Xunit.Abstractions; -namespace Microsoft.Extensions.Logging.Testing +namespace Microsoft.AspNetCore.Testing { public class LoggedTestBase : ILoggedTest, ITestMethodLifecycle { @@ -37,7 +39,7 @@ namespace Microsoft.Extensions.Logging.Testing public string ResolvedTestMethodName { get; set; } - public ILogger Logger { get; set; } + public Microsoft.Extensions.Logging.ILogger Logger { get; set; } public ILoggerFactory LoggerFactory { get; set; } diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 96336a47a8..5ddad7b645 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -2,7 +2,7 @@ Various helpers for writing tests that use ASP.NET Core. - netstandard2.0;net46 + netstandard2.0;net461 $(NoWarn);CS1591 true aspnetcore @@ -19,7 +19,10 @@ + + + - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 - 5.0.0-preview.2.20154.8 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 5.0.0-preview.2.20154.3 5.0.0-preview.2.20154.3 From d01f3ebe2811f59841b8bec0fbc1c27856fa9c40 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 5 Mar 2020 21:52:29 -0800 Subject: [PATCH 21/57] Don't run arm queues on quarantined runs (#19625) --- eng/targets/Helix.Common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/targets/Helix.Common.props b/eng/targets/Helix.Common.props index a49cbc6a0a..e77bcd8828 100644 --- a/eng/targets/Helix.Common.props +++ b/eng/targets/Helix.Common.props @@ -37,7 +37,7 @@ - + From 8cba1cbb231b37d56c352fad196619b11c5c6613 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 5 Mar 2020 23:19:21 -0800 Subject: [PATCH 22/57] Skip arm64 queues on internal builds (#19620) --- .azure/pipelines/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index eb38671015..924012b0c9 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -683,7 +683,7 @@ stages: # Helix ARM64 - template: jobs/default-build.yml parameters: - condition: ne(variables['Build.Reason'], 'PullRequest') + condition: and(eq(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) jobName: Helix_arm64_daily jobDisplayName: "Tests: Helix ARM64 Daily" agentOs: Linux From e886169745ad626b52499aaf3338143bf58dd0cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2020 10:08:04 -0800 Subject: [PATCH 23/57] Sync shared code from runtime (#19636) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/Shared/runtime/ReadMe.SharedCode.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/runtime/ReadMe.SharedCode.md b/src/Shared/runtime/ReadMe.SharedCode.md index dcb4cc4f0e..f24980b8a2 100644 --- a/src/Shared/runtime/ReadMe.SharedCode.md +++ b/src/Shared/runtime/ReadMe.SharedCode.md @@ -28,8 +28,8 @@ dotnet/AspNetCore code paths: - Run restore in the root once: `PS D:\github\AspNetCore> .\restore.cmd` - Activate to use the repo local runtime: `PS D:\github\AspNetCore> . .\activate.ps1` - Build the individual projects: -- `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet msbuild` -- `(AspNetCore) PS D:\github\AspNetCore\src\servers\Kestrel\core\src> dotnet msbuild` +- `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet build` +- `(AspNetCore) PS D:\github\AspNetCore\src\servers\Kestrel\core\src> dotnet build` ### Running dotnet/AspNetCore tests: - `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet test` From 7cb9029a0f8ae2d98e690232f784de1719f59f7b Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 7 Mar 2020 07:31:35 +1300 Subject: [PATCH 24/57] Fix StreamPool_StreamIsInvalidState_DontReturnedToPool (#19622) --- .../test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index 26b146843b..b0595d4545 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -225,7 +225,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await ExpectAsync(Http2FrameType.HEADERS, - withLength: 37, + withLength: 33, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withStreamId: 1); await ExpectAsync(Http2FrameType.DATA, From db27388306fa572de1047b9c9a858c4cc976bdb3 Mon Sep 17 00:00:00 2001 From: Doug Bunting <6431421+dougbu@users.noreply.github.com> Date: Fri, 6 Mar 2020 10:31:45 -0800 Subject: [PATCH 25/57] Update branding to preview3 (#19632) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index a817bcc520..16929e203d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -9,7 +9,7 @@ 5 0 0 - 2 + 3 From 82c87d678b556e732360c8741fc21ff8857919bb Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 6 Mar 2020 11:22:54 -0800 Subject: [PATCH 26/57] Use QuarantinedTest --- src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs index ab1692d431..c9ef2f32e8 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs @@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests } [Fact] - [Flaky("", FlakyOn.All)] + [QuarantinedTest] public async Task ClientHandshakeFailureLoggedAsDebug() { var loggerProvider = new HandshakeErrorLoggerProvider(); From 08faf4281c48b98acd16126b6dfc6945be7e5c00 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2020 20:38:22 +0000 Subject: [PATCH 27/57] [master] Update dependencies from dotnet/efcore (#19634) * Update dependencies from https://github.com/dotnet/efcore build 20200305.4 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.2.20155.4 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.2.20155.4 - dotnet-ef - 5.0.0-preview.2.20155.4 - Microsoft.EntityFrameworkCore - 5.0.0-preview.2.20155.4 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.2.20155.4 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.2.20155.4 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.2.20155.4 * Update dependencies from https://github.com/dotnet/efcore build 20200305.5 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.2.20155.5 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.2.20155.5 - dotnet-ef - 5.0.0-preview.2.20155.5 - Microsoft.EntityFrameworkCore - 5.0.0-preview.2.20155.5 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.2.20155.5 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.2.20155.5 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.2.20155.5 * Update dependencies from https://github.com/dotnet/efcore build 20200306.2 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20156.2 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20156.2 - dotnet-ef - 5.0.0-preview.3.20156.2 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20156.2 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20156.2 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20156.2 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20156.2 * Update dependencies from https://github.com/dotnet/efcore build 20200306.1 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.2.20156.1 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.2.20156.1 - dotnet-ef - 5.0.0-preview.2.20156.1 - Microsoft.EntityFrameworkCore - 5.0.0-preview.2.20156.1 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.2.20156.1 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.2.20156.1 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.2.20156.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 57921ca9b6..e5a3463634 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -29,33 +29,33 @@ https://github.com/dotnet/aspnetcore-tooling ac98f02739df23e5a850c667a4ebaee2b558b87e - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 - + https://github.com/dotnet/efcore - d3a240188d27736132a6ca263c2ab974cce452ba + ec65dec54a3ce3aaca9baa9949dfb7333fd60528 https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index 16929e203d..d778151041 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,13 +140,13 @@ 5.0.0-preview.2.20154.2 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 + 5.0.0-preview.2.20156.1 5.0.0-preview.2.20154.3 5.0.0-preview.2.20154.3 From d2b67cad829e25db64610dd3adf0b7527718468b Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 6 Mar 2020 12:42:07 -0800 Subject: [PATCH 28/57] Feedback --- eng/targets/CSharp.Common.props | 2 ++ .../Microsoft.AspNetCore.FunctionalTests.csproj | 3 --- .../Microsoft.AspNetCore.Hosting.FunctionalTests.csproj | 2 -- .../Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj | 2 -- .../Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj | 2 -- .../UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj | 2 -- .../test/MusicStore.E2ETests/MusicStore.E2ETests.csproj | 2 -- .../Mvc.Core/test/Microsoft.AspNetCore.Mvc.Core.Test.csproj | 2 -- .../test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj | 2 -- .../Microsoft.AspNetCore.Mvc.FunctionalTests.csproj | 1 - .../IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj | 2 -- .../IIS.NewHandler.FunctionalTests.csproj | 2 -- .../IIS.NewShim.FunctionalTests.csproj | 2 -- src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj | 1 - .../IISExpress.FunctionalTests.csproj | 1 - .../InMemory.FunctionalTests/InMemory.FunctionalTests.csproj | 2 -- .../Interop.FunctionalTests/Interop.FunctionalTests.csproj | 2 -- .../Kestrel/test/Libuv.BindTests/Libuv.BindTests.csproj | 2 -- .../test/Libuv.FunctionalTests/Libuv.FunctionalTests.csproj | 2 -- .../Kestrel/test/Sockets.BindTests/Sockets.BindTests.csproj | 2 -- .../Sockets.FunctionalTests/Sockets.FunctionalTests.csproj | 2 -- .../FunctionalTests/ServerComparison.FunctionalTests.csproj | 2 -- .../Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj | 2 -- .../UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj | 2 -- .../test/Microsoft.AspNetCore.Http.Connections.Tests.csproj | 2 -- .../Microsoft.AspNetCore.SignalR.Tests.Utils.csproj | 2 -- .../SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj | 2 -- ...icrosoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj | 2 -- src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj | 2 -- 29 files changed, 2 insertions(+), 54 deletions(-) diff --git a/eng/targets/CSharp.Common.props b/eng/targets/CSharp.Common.props index 4600fad635..958788435b 100644 --- a/eng/targets/CSharp.Common.props +++ b/eng/targets/CSharp.Common.props @@ -27,6 +27,8 @@ + + PreserveNewest diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj b/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj index 06598eae10..463edd8d44 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj @@ -1,7 +1,4 @@ - - - $(DefaultNetCoreTargetFramework) diff --git a/src/Hosting/test/FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.csproj b/src/Hosting/test/FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.csproj index 9cdd0d42e3..c964da91d9 100644 --- a/src/Hosting/test/FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.csproj +++ b/src/Hosting/test/FunctionalTests/Microsoft.AspNetCore.Hosting.FunctionalTests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) diff --git a/src/Middleware/StaticFiles/test/FunctionalTests/Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj b/src/Middleware/StaticFiles/test/FunctionalTests/Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj index cee4325308..f3537f09b5 100644 --- a/src/Middleware/StaticFiles/test/FunctionalTests/Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj +++ b/src/Middleware/StaticFiles/test/FunctionalTests/Microsoft.AspNetCore.StaticFiles.FunctionalTests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) diff --git a/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj b/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj index 2a3fd7eaa6..8a3b9ec58c 100644 --- a/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj +++ b/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) $(DefaultItemExcludes);AutobahnTestApp\**\* diff --git a/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj b/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj index 3a61b5507f..a79f651ecb 100644 --- a/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj +++ b/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) diff --git a/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj b/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj index 650b85a586..2861af06f8 100644 --- a/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj +++ b/src/MusicStore/test/MusicStore.E2ETests/MusicStore.E2ETests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) MusicStore.E2ETests diff --git a/src/Mvc/Mvc.Core/test/Microsoft.AspNetCore.Mvc.Core.Test.csproj b/src/Mvc/Mvc.Core/test/Microsoft.AspNetCore.Mvc.Core.Test.csproj index 1b1354e79b..8ebae5504f 100644 --- a/src/Mvc/Mvc.Core/test/Microsoft.AspNetCore.Mvc.Core.Test.csproj +++ b/src/Mvc/Mvc.Core/test/Microsoft.AspNetCore.Mvc.Core.Test.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) Microsoft.AspNetCore.Mvc diff --git a/src/Mvc/Mvc.NewtonsoftJson/test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj b/src/Mvc/Mvc.NewtonsoftJson/test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj index 2b15e5c94b..1d395a2733 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj +++ b/src/Mvc/Mvc.NewtonsoftJson/test/Microsoft.AspNetCore.Mvc.NewtonsoftJson.Test.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) diff --git a/src/Mvc/test/Mvc.FunctionalTests/Microsoft.AspNetCore.Mvc.FunctionalTests.csproj b/src/Mvc/test/Mvc.FunctionalTests/Microsoft.AspNetCore.Mvc.FunctionalTests.csproj index 8356d6bd2b..1cdb311bab 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/Microsoft.AspNetCore.Mvc.FunctionalTests.csproj +++ b/src/Mvc/test/Mvc.FunctionalTests/Microsoft.AspNetCore.Mvc.FunctionalTests.csproj @@ -1,6 +1,5 @@  - $(DefaultNetCoreTargetFramework) diff --git a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj index 89711ae241..5d689a6bee 100644 --- a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) IIS.FunctionalTests diff --git a/src/Servers/IIS/IIS/test/IIS.NewHandler.FunctionalTests/IIS.NewHandler.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IIS.NewHandler.FunctionalTests/IIS.NewHandler.FunctionalTests.csproj index 1baa00befb..530f6f8bbe 100644 --- a/src/Servers/IIS/IIS/test/IIS.NewHandler.FunctionalTests/IIS.NewHandler.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.NewHandler.FunctionalTests/IIS.NewHandler.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) IISNewHandler.FunctionalTests diff --git a/src/Servers/IIS/IIS/test/IIS.NewShim.FunctionalTests/IIS.NewShim.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IIS.NewShim.FunctionalTests/IIS.NewShim.FunctionalTests.csproj index fca400fab5..07b99fa8d5 100644 --- a/src/Servers/IIS/IIS/test/IIS.NewShim.FunctionalTests/IIS.NewShim.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.NewShim.FunctionalTests/IIS.NewShim.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) IISNewShim.FunctionalTests diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj b/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj index af154f1e00..1b54904dee 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj @@ -1,7 +1,6 @@ - $(DefaultNetCoreTargetFramework) diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj index de9b67cab6..d874d0093e 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj @@ -1,7 +1,6 @@  - $(DefaultNetCoreTargetFramework) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj index c2556fe36e..cad32b08bd 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) true diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj index 5467930445..e5c19de1eb 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) true diff --git a/src/Servers/Kestrel/test/Libuv.BindTests/Libuv.BindTests.csproj b/src/Servers/Kestrel/test/Libuv.BindTests/Libuv.BindTests.csproj index e47f329c80..39871045f6 100644 --- a/src/Servers/Kestrel/test/Libuv.BindTests/Libuv.BindTests.csproj +++ b/src/Servers/Kestrel/test/Libuv.BindTests/Libuv.BindTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) true diff --git a/src/Servers/Kestrel/test/Libuv.FunctionalTests/Libuv.FunctionalTests.csproj b/src/Servers/Kestrel/test/Libuv.FunctionalTests/Libuv.FunctionalTests.csproj index b32e551b5d..d9ee70e3fc 100644 --- a/src/Servers/Kestrel/test/Libuv.FunctionalTests/Libuv.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/Libuv.FunctionalTests/Libuv.FunctionalTests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) $(DefineConstants);MACOS diff --git a/src/Servers/Kestrel/test/Sockets.BindTests/Sockets.BindTests.csproj b/src/Servers/Kestrel/test/Sockets.BindTests/Sockets.BindTests.csproj index c7943f2aeb..3ad3eb297a 100644 --- a/src/Servers/Kestrel/test/Sockets.BindTests/Sockets.BindTests.csproj +++ b/src/Servers/Kestrel/test/Sockets.BindTests/Sockets.BindTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) true diff --git a/src/Servers/Kestrel/test/Sockets.FunctionalTests/Sockets.FunctionalTests.csproj b/src/Servers/Kestrel/test/Sockets.FunctionalTests/Sockets.FunctionalTests.csproj index f8e67ffef5..4bc4f8fbdf 100644 --- a/src/Servers/Kestrel/test/Sockets.FunctionalTests/Sockets.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/Sockets.FunctionalTests/Sockets.FunctionalTests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) $(DefineConstants);MACOS diff --git a/src/Servers/test/FunctionalTests/ServerComparison.FunctionalTests.csproj b/src/Servers/test/FunctionalTests/ServerComparison.FunctionalTests.csproj index 79b2bb3b33..b3b461eafe 100644 --- a/src/Servers/test/FunctionalTests/ServerComparison.FunctionalTests.csproj +++ b/src/Servers/test/FunctionalTests/ServerComparison.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj index 9c3f956520..64df7e4cf6 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Microsoft.AspNetCore.SignalR.Client.FunctionalTests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj b/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj index 7a7f90811f..7c0781eeee 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/Microsoft.AspNetCore.SignalR.Client.Tests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj index 073fe6ae2e..47191a34e2 100644 --- a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj +++ b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj @@ -1,7 +1,5 @@ - - $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj index e63a400554..9e69675720 100644 --- a/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj +++ b/src/SignalR/common/testassets/Tests.Utils/Microsoft.AspNetCore.SignalR.Tests.Utils.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) Microsoft.AspNetCore.SignalR.Tests diff --git a/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj b/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj index f80c8b6f8c..15574498b1 100644 --- a/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj +++ b/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) diff --git a/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj b/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj index b8fc1ed952..e9395224a6 100644 --- a/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj +++ b/src/SignalR/server/StackExchangeRedis/test/Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj @@ -1,7 +1,5 @@  - - $(DefaultNetCoreTargetFramework) diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index a64b4b72d8..a203e1289d 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -1,6 +1,4 @@  - - $(DefaultNetCoreTargetFramework);net472 From 9f5686232043b8a740128fbf6ad3bf1aaf9653ca Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 6 Mar 2020 14:47:29 -0800 Subject: [PATCH 29/57] Build aspnetcore for win-arm64 (#19317) * Build for win-arm64 * Missed file * Disable Selenium tests on arm64 * Include installers in the uploaded artifacts --- .azure/pipelines/ci.yml | 32 ++++++++++++++++++++++++++ Directory.Build.props | 1 + build.ps1 | 4 ++-- eng/Dependencies.props | 1 + eng/Signing.props | 2 ++ src/Shared/E2ETesting/E2ETesting.props | 2 +- 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index 924012b0c9..6070086773 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -254,6 +254,38 @@ stages: - name: Windows_arm_Packages path: artifacts/packages/ + # Build Windows ARM64 + - template: jobs/default-build.yml + parameters: + codeSign: true + jobName: Windows_64_build + jobDisplayName: "Build: Windows ARM64" + agentOs: Windows + buildArgs: + -arch arm64 + -sign + -pack + -noBuildNodeJS + -noBuildJava + /bl:artifacts/log/build.win-arm64.binlog + /p:DotNetSignType=$(_SignType) + /p:OnlyPackPlatformSpecificPackages=true + /p:AssetManifestFileName=aspnetcore-win-arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + installNodeJs: false + installJdk: false + artifacts: + - name: Windows_arm64_Logs + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Windows_arm64_Packages + path: artifacts/packages/ + - name: Windows_arm64_Installers + path: artifacts/installers/ + # Build MacOS - template: jobs/default-build.yml parameters: diff --git a/Directory.Build.props b/Directory.Build.props index a2765ff560..c40d203441 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -123,6 +123,7 @@ win-x64; win-x86; win-arm; + win-arm64; osx-x64; linux-musl-x64; linux-musl-arm64; diff --git a/build.ps1 b/build.ps1 index 7ce1d87f58..f371d26673 100644 --- a/build.ps1 +++ b/build.ps1 @@ -118,7 +118,7 @@ param( [ValidateSet('Debug', 'Release')] $Configuration, - [ValidateSet('x64', 'x86', 'arm')] + [ValidateSet('x64', 'x86', 'arm', 'arm64')] $Architecture = 'x64', # A list of projects which should be built. @@ -157,7 +157,7 @@ param( # Other lifecycle targets [switch]$Help, # Show help - + # Optional arguments that enable downloading an internal # runtime or runtime from a non-default location [string]$DotNetRuntimeSourceFeed, diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 0603add6c3..6e8e7b9a8b 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -94,6 +94,7 @@ and are generated based on the last package release. + diff --git a/eng/Signing.props b/eng/Signing.props index 3b61e9205f..024a5c8a0a 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -93,9 +93,11 @@ <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x64\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x86\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> + <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm64\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x64\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x86\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm\host\**\*.dll" CertificateName="None" /> + <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm64\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(RedistNetCorePath)dotnet.exe" CertificateName="None" /> diff --git a/src/Shared/E2ETesting/E2ETesting.props b/src/Shared/E2ETesting/E2ETesting.props index 64e74c095e..0e6552e7ec 100644 --- a/src/Shared/E2ETesting/E2ETesting.props +++ b/src/Shared/E2ETesting/E2ETesting.props @@ -4,7 +4,7 @@ $(DefaultItemExcludes);node_modules\** $([MSBuild]::NormalizeDirectory('$(ArtifactsTestResultsDir)','$(MSBuildProjectName)')) $([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))artifacts\tmp\selenium\ - true + true $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory)selenium-config.json)) From 7fabb6c9f44c1369f75a48359d3021b0174d8bdf Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 6 Mar 2020 15:06:58 -0800 Subject: [PATCH 30/57] Don't run Java tests on quarantine run (#19652) --- .azure/pipelines/quarantined-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.azure/pipelines/quarantined-tests.yml b/.azure/pipelines/quarantined-tests.yml index bb9224b38b..415853ed8e 100644 --- a/.azure/pipelines/quarantined-tests.yml +++ b/.azure/pipelines/quarantined-tests.yml @@ -32,10 +32,10 @@ jobs: steps: # Build the shared framework - script: ./build.cmd -ci -all -pack -arch x64 -buildNative /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log /bl:artifacts/log/helix.build.x64.binlog - displayName: Build shared fx + displayName: Build shared fx - script: .\restore.cmd -ci displayName: Restore - - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl + - script: .\build.cmd -ci -NoRestore -test -noBuildJava -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl displayName: Run build.cmd helix target env: HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues From f053620895fbd9577af5bb26192b0b5272ca5820 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 6 Mar 2020 15:25:51 -0800 Subject: [PATCH 31/57] [SignalR] Avoid deadlock with closing and user callbacks (#19612) --- .../csharp/Client.Core/src/HubConnection.cs | 4 +- .../test/UnitTests/HubConnectionTests.cs | 55 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index acd21ef2c4..4d975d5d7e 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -1214,11 +1214,13 @@ namespace Microsoft.AspNetCore.SignalR.Client finally { invocationMessageChannel.Writer.TryComplete(); - await invocationMessageReceiveTask; timer.Stop(); await timerTask; uploadStreamSource.Cancel(); await HandleConnectionClose(connectionState); + + // await after the connection has been closed, otherwise could deadlock on a user's .On callback(s) + await invocationMessageReceiveTask; } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs index b303e71ddd..e2b18c9857 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs @@ -345,7 +345,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var complete = await connection.ReadSentJsonAsync().OrTimeout(); Assert.Equal(HubProtocolConstants.CompletionMessageType, complete["type"]); Assert.EndsWith("canceled by client.", ((string)complete["error"])); - } + } } [Fact] @@ -414,6 +414,59 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests } } + [Fact] + public async Task CanAwaitInvokeFromOnHandlerWithServerClosingConnection() + { + using (StartVerifiableLog()) + { + var connection = new TestConnection(); + var hubConnection = CreateHubConnection(connection, loggerFactory: LoggerFactory); + await hubConnection.StartAsync().OrTimeout(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + hubConnection.On("Echo", async msg => + { + try + { + // This should be canceled when the connection is closed + await hubConnection.InvokeAsync("Echo", msg).OrTimeout(); + } + catch (Exception ex) + { + tcs.SetException(ex); + return; + } + + tcs.SetResult(null); + }); + + var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + hubConnection.Closed += _ => + { + closedTcs.SetResult(null); + + return Task.CompletedTask; + }; + + await connection.ReceiveJsonMessage(new { type = HubProtocolConstants.InvocationMessageType, target = "Echo", arguments = new object[] { "42" } }); + + // Read sent message first to make sure invoke has been processed and is waiting for a response + await connection.ReadSentJsonAsync().OrTimeout(); + await connection.ReceiveJsonMessage(new { type = HubProtocolConstants.CloseMessageType }); + + await closedTcs.Task.OrTimeout(); + + try + { + await tcs.Task.OrTimeout(); + Assert.True(false); + } + catch (TaskCanceledException) + { + } + } + } + [Fact] public async Task CanAwaitUsingHubConnection() { From 7aaad9dde22a699a3c8d991547c6551fbdbf43c3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2020 00:26:29 +0000 Subject: [PATCH 32/57] Update dependencies from https://github.com/dotnet/efcore build 20200306.3 (#19662) - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20156.3 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20156.3 - dotnet-ef - 5.0.0-preview.3.20156.3 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20156.3 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20156.3 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20156.3 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20156.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e5a3463634..49c5231059 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -29,33 +29,33 @@ https://github.com/dotnet/aspnetcore-tooling ac98f02739df23e5a850c667a4ebaee2b558b87e - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - ec65dec54a3ce3aaca9baa9949dfb7333fd60528 + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index d778151041..e4da99361c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,13 +140,13 @@ 5.0.0-preview.2.20154.2 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 - 5.0.0-preview.2.20156.1 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 5.0.0-preview.2.20154.3 5.0.0-preview.2.20154.3 From ce934b1b5e163604ec3f6a076cd84d4342a9147f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2020 01:50:00 +0000 Subject: [PATCH 33/57] [master] Update dependencies from dotnet/aspnetcore-tooling (#19610) * Update dependencies from https://github.com/dotnet/aspnetcore-tooling build 20200305.6 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 5.0.0-preview.2.20155.6 - Microsoft.AspNetCore.Razor.Language - 5.0.0-preview.2.20155.6 - Microsoft.CodeAnalysis.Razor - 5.0.0-preview.2.20155.6 - Microsoft.NET.Sdk.Razor - 5.0.0-preview.2.20155.6 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Memory - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.SqlServer - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.StackExchangeRedis - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.AzureKeyVault - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Binder - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.CommandLine - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.EnvironmentVariables - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.FileExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Ini - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.UserSecrets - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DiagnosticAdapter - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Composite - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Physical - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileSystemGlobbing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Http - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.AzureAppServices - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Console - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Debug - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.TraceSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.ConfigurationExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.DataAnnotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Primitives - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Internal.Extensions.Refs - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.Registry - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.SystemEvents - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Drawing.Common - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.Http.WinHttpHandler - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.WebSockets.WebSocketProtocol - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Pkcs - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Permissions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Principal.Windows - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Threading.Channels - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Windows.Extensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.Extensions.DependencyModel - 5.0.0-preview.2-runtime.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Ref - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Internal - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - NETStandard.Library.Ref - 2.1.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.Platforms - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Internal.AspNetCore.Analyzers - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) * Update dependencies from https://github.com/dotnet/aspnetcore-tooling build 20200305.7 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 5.0.0-preview.2.20155.7 - Microsoft.AspNetCore.Razor.Language - 5.0.0-preview.2.20155.7 - Microsoft.CodeAnalysis.Razor - 5.0.0-preview.2.20155.7 - Microsoft.NET.Sdk.Razor - 5.0.0-preview.2.20155.7 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Memory - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.SqlServer - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.StackExchangeRedis - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.AzureKeyVault - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Binder - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.CommandLine - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.EnvironmentVariables - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.FileExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Ini - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.UserSecrets - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DiagnosticAdapter - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Composite - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Physical - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileSystemGlobbing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Http - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.AzureAppServices - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Console - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Debug - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.TraceSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.ConfigurationExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.DataAnnotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Primitives - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Internal.Extensions.Refs - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.Registry - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.SystemEvents - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Drawing.Common - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.Http.WinHttpHandler - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.WebSockets.WebSocketProtocol - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Pkcs - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Permissions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Principal.Windows - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Threading.Channels - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Windows.Extensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.Extensions.DependencyModel - 5.0.0-preview.2-runtime.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Ref - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Internal - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - NETStandard.Library.Ref - 2.1.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.Platforms - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Internal.AspNetCore.Analyzers - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) * Update dependencies from https://github.com/dotnet/aspnetcore-tooling build 20200305.9 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 5.0.0-preview.2.20155.9 - Microsoft.AspNetCore.Razor.Language - 5.0.0-preview.2.20155.9 - Microsoft.CodeAnalysis.Razor - 5.0.0-preview.2.20155.9 - Microsoft.NET.Sdk.Razor - 5.0.0-preview.2.20155.9 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Memory - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.SqlServer - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.StackExchangeRedis - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.AzureKeyVault - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Binder - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.CommandLine - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.EnvironmentVariables - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.FileExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Ini - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.UserSecrets - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DiagnosticAdapter - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Composite - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Physical - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileSystemGlobbing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Http - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.AzureAppServices - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Console - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Debug - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.TraceSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.ConfigurationExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.DataAnnotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Primitives - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Internal.Extensions.Refs - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.Registry - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.SystemEvents - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Drawing.Common - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.Http.WinHttpHandler - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.WebSockets.WebSocketProtocol - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Pkcs - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Permissions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Principal.Windows - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Threading.Channels - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Windows.Extensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.Extensions.DependencyModel - 5.0.0-preview.2-runtime.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Ref - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Internal - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - NETStandard.Library.Ref - 2.1.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.Platforms - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Internal.AspNetCore.Analyzers - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) * Update dependencies from https://github.com/dotnet/aspnetcore-tooling build 20200306.1 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 5.0.0-preview.3.20156.1 - Microsoft.AspNetCore.Razor.Language - 5.0.0-preview.3.20156.1 - Microsoft.CodeAnalysis.Razor - 5.0.0-preview.3.20156.1 - Microsoft.NET.Sdk.Razor - 5.0.0-preview.3.20156.1 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.Memory - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.SqlServer - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Caching.StackExchangeRedis - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.AzureKeyVault - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Binder - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.CommandLine - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.EnvironmentVariables - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.FileExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Ini - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.UserSecrets - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DependencyInjection - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.DiagnosticAdapter - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Composite - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileProviders.Physical - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.FileSystemGlobbing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Hosting - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Http - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Abstractions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.AzureAppServices - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Configuration - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Console - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Debug - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.TraceSource - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Logging.Testing - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.ConfigurationExtensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options.DataAnnotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Options - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Extensions.Primitives - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Internal.Extensions.Refs - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.Registry - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.NETCore.App.Runtime.win-x64 - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) - Microsoft.Win32.SystemEvents - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ComponentModel.Annotations - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Diagnostics.EventLog - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Drawing.Common - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.IO.Pipelines - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.Http.WinHttpHandler - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Net.WebSockets.WebSocketProtocol - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Reflection.Metadata - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Runtime.CompilerServices.Unsafe - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Cng - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Pkcs - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Cryptography.Xml - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Permissions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Security.Principal.Windows - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.ServiceProcess.ServiceController - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Encodings.Web - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Text.Json - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Threading.Channels - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - System.Windows.Extensions - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Microsoft.Extensions.DependencyModel - 5.0.0-preview.2-runtime.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Ref - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.App.Internal - 5.0.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - NETStandard.Library.Ref - 2.1.0-preview.2.20155.1 (parent: Microsoft.Extensions.Logging) - Microsoft.NETCore.Platforms - 5.0.0-preview.2.20155.1 (parent: Microsoft.NETCore.App.Runtime.win-x64) - Internal.AspNetCore.Analyzers - 5.0.0-preview.2.20155.1 (parent: Microsoft.AspNetCore.Razor.Language) Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 288 ++++++++++++++++++++-------------------- eng/Versions.props | 144 ++++++++++---------- 2 files changed, 216 insertions(+), 216 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 49c5231059..c72907564b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,21 +13,21 @@ https://github.com/dotnet/blazor dd7fb4d3931d556458f62642c2edfc59f6295bfb - + https://github.com/dotnet/aspnetcore-tooling - ac98f02739df23e5a850c667a4ebaee2b558b87e + de540ed7af17cb2a9bc3af76245243060e6c0442 - + https://github.com/dotnet/aspnetcore-tooling - ac98f02739df23e5a850c667a4ebaee2b558b87e + de540ed7af17cb2a9bc3af76245243060e6c0442 - + https://github.com/dotnet/aspnetcore-tooling - ac98f02739df23e5a850c667a4ebaee2b558b87e + de540ed7af17cb2a9bc3af76245243060e6c0442 - + https://github.com/dotnet/aspnetcore-tooling - ac98f02739df23e5a850c667a4ebaee2b558b87e + de540ed7af17cb2a9bc3af76245243060e6c0442 https://github.com/dotnet/efcore @@ -57,284 +57,284 @@ https://github.com/dotnet/efcore f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + e2881ae96bb4399864723aa46469c82379b03b32 - + https://github.com/dotnet/extensions - 26ff835952efd87e6794d72c6d684d61f7ff7f6c + 6ae4b22a059858eba7b3d9068f812ada92df8e24 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index e4da99361c..d4b91fe6a3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,79 +66,79 @@ 3.5.0-beta4-20153-05 - 5.0.0-preview.2-runtime.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 2.1.0-preview.2.20154.1 + 5.0.0-preview.2-runtime.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 2.1.0-preview.2.20155.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.2.20155.1 3.2.0-preview1.20067.1 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 - 5.0.0-preview.2.20154.2 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.1 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 @@ -148,10 +148,10 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.2.20154.3 - 5.0.0-preview.2.20154.3 - 5.0.0-preview.2.20154.3 - 5.0.0-preview.2.20154.3 + 5.0.0-preview.3.20156.1 + 5.0.0-preview.3.20156.1 + 5.0.0-preview.3.20156.1 + 5.0.0-preview.3.20156.1 - + https://github.com/dotnet/runtime - e2881ae96bb4399864723aa46469c82379b03b32 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - e2881ae96bb4399864723aa46469c82379b03b32 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - e2881ae96bb4399864723aa46469c82379b03b32 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - e2881ae96bb4399864723aa46469c82379b03b32 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/extensions - 6ae4b22a059858eba7b3d9068f812ada92df8e24 + 9a8d035b5f1c7264d8537d73a08cb812238699f1 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index d4b91fe6a3..d7b213afa5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,79 +66,79 @@ 3.5.0-beta4-20153-05 - 5.0.0-preview.2-runtime.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 2.1.0-preview.2.20155.1 + 5.0.0-preview.2-runtime.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 2.1.0-preview.2.20155.3 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20155.1 + 5.0.0-preview.2.20155.3 3.2.0-preview1.20067.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 - 5.0.0-preview.2.20155.1 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.2 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 @@ -148,10 +148,10 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.1 - 5.0.0-preview.3.20156.1 - 5.0.0-preview.3.20156.1 - 5.0.0-preview.3.20156.1 + 5.0.0-preview.3.20156.6 + 5.0.0-preview.3.20156.6 + 5.0.0-preview.3.20156.6 + 5.0.0-preview.3.20156.6 - 5.0.0-preview.3.20156.6 - 5.0.0-preview.3.20156.6 - 5.0.0-preview.3.20156.6 - 5.0.0-preview.3.20156.6 + 5.0.0-preview.3.20156.7 + 5.0.0-preview.3.20156.7 + 5.0.0-preview.3.20156.7 + 5.0.0-preview.3.20156.7 3.2.0-preview1.20067.1 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 - 5.0.0-preview.3.20156.2 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 @@ -148,10 +148,10 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.7 - 5.0.0-preview.3.20156.7 - 5.0.0-preview.3.20156.7 - 5.0.0-preview.3.20156.7 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.1 5.0.0-preview.3.20157.1 5.0.0-preview.3.20157.1 From a0175a2357ddd604acb050ee5a1f69a2ea522559 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 20:19:13 +0000 Subject: [PATCH 44/57] Update dependencies from https://github.com/dotnet/arcade build 20200308.2 (#19703) - Microsoft.DotNet.Arcade.Sdk - 5.0.0-beta.20158.2 - Microsoft.DotNet.GenAPI - 5.0.0-beta.20158.2 - Microsoft.DotNet.Helix.Sdk - 5.0.0-beta.20158.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 12 +-- eng/Versions.props | 2 +- eng/common/SigningValidation.proj | 83 ------------------- eng/common/build.ps1 | 5 ++ eng/common/cross/android/arm/toolchain.cmake | 41 --------- .../cross/android/arm64/toolchain.cmake | 42 ---------- eng/common/cross/build-android-rootfs.sh | 69 ++++++--------- eng/common/cross/toolchain.cmake | 30 +++++-- eng/common/darc-init.ps1 | 6 +- eng/common/pipeline-logging-functions.ps1 | 4 +- .../channels/generic-public-channel.yml | 5 ++ .../templates/post-build/post-build.yml | 6 ++ eng/common/tools.ps1 | 2 +- global.json | 4 +- 14 files changed, 83 insertions(+), 228 deletions(-) delete mode 100644 eng/common/SigningValidation.proj delete mode 100644 eng/common/cross/android/arm/toolchain.cmake delete mode 100644 eng/common/cross/android/arm64/toolchain.cmake diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3b4dfc0fe1..fa4d240448 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -336,17 +336,17 @@ https://github.com/dotnet/extensions 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/arcade - 8ccad075bbb0db445e03eed0a6073d27bdd4f31a + 527179abfbb5cd738035c4ac2d2566c571ce85db - + https://github.com/dotnet/arcade - 8ccad075bbb0db445e03eed0a6073d27bdd4f31a + 527179abfbb5cd738035c4ac2d2566c571ce85db - + https://github.com/dotnet/arcade - 8ccad075bbb0db445e03eed0a6073d27bdd4f31a + 527179abfbb5cd738035c4ac2d2566c571ce85db https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index b4e8a3bf4c..3789afccc8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -62,7 +62,7 @@ --> - 5.0.0-beta.20151.1 + 5.0.0-beta.20158.2 3.5.0-beta4-20153-05 diff --git a/eng/common/SigningValidation.proj b/eng/common/SigningValidation.proj deleted file mode 100644 index 3d0ac80af3..0000000000 --- a/eng/common/SigningValidation.proj +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - netcoreapp2.1 - - - - - - - - $(NuGetPackageRoot)Microsoft.DotNet.SignCheck\$(SignCheckVersion)\tools\Microsoft.DotNet.SignCheck.exe - - $(PackageBasePath) - signcheck.log - signcheck.errors.log - signcheck.exclusions.txt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 88814514d8..813d440d2a 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -26,6 +26,11 @@ Param( [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties ) +# Unset 'Platform' environment variable to avoid unwanted collision in InstallDotNetCore.targets file +# some computer has this env var defined (e.g. Some HP) +if($env:Platform) { + $env:Platform="" +} function Print-Usage() { Write-Host "Common settings:" Write-Host " -configuration Build configuration: 'Debug' or 'Release' (short: -c)" diff --git a/eng/common/cross/android/arm/toolchain.cmake b/eng/common/cross/android/arm/toolchain.cmake deleted file mode 100644 index a7e1c73501..0000000000 --- a/eng/common/cross/android/arm/toolchain.cmake +++ /dev/null @@ -1,41 +0,0 @@ -set(CROSS_NDK_TOOLCHAIN $ENV{ROOTFS_DIR}/../) -set(CROSS_ROOTFS ${CROSS_NDK_TOOLCHAIN}/sysroot) -set(CLR_CMAKE_PLATFORM_ANDROID "Android") - -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR arm) - -## Specify the toolchain -set(TOOLCHAIN "arm-linux-androideabi") -set(CMAKE_PREFIX_PATH ${CROSS_NDK_TOOLCHAIN}) -set(TOOLCHAIN_PREFIX ${TOOLCHAIN}-) - -find_program(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}clang) -find_program(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}clang++) -find_program(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}clang) -find_program(CMAKE_AR ${TOOLCHAIN_PREFIX}ar) -find_program(CMAKE_LD ${TOOLCHAIN_PREFIX}ar) -find_program(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy) -find_program(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) - -add_compile_options(--sysroot=${CROSS_ROOTFS}) -add_compile_options(-fPIE) -add_compile_options(-mfloat-abi=soft) -include_directories(SYSTEM ${CROSS_NDK_TOOLCHAIN}/include/c++/4.9.x/) -include_directories(SYSTEM ${CROSS_NDK_TOOLCHAIN}/include/c++/4.9.x/arm-linux-androideabi/) - -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/${TOOLCHAIN}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/${TOOLCHAIN}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -fPIE -pie") - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) - -set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}") -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/eng/common/cross/android/arm64/toolchain.cmake b/eng/common/cross/android/arm64/toolchain.cmake deleted file mode 100644 index 29415899c1..0000000000 --- a/eng/common/cross/android/arm64/toolchain.cmake +++ /dev/null @@ -1,42 +0,0 @@ -set(CROSS_NDK_TOOLCHAIN $ENV{ROOTFS_DIR}/../) -set(CROSS_ROOTFS ${CROSS_NDK_TOOLCHAIN}/sysroot) -set(CLR_CMAKE_PLATFORM_ANDROID "Android") - -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR aarch64) - -## Specify the toolchain -set(TOOLCHAIN "aarch64-linux-android") -set(CMAKE_PREFIX_PATH ${CROSS_NDK_TOOLCHAIN}) -set(TOOLCHAIN_PREFIX ${TOOLCHAIN}-) - -find_program(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}clang) -find_program(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}clang++) -find_program(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}clang) -find_program(CMAKE_AR ${TOOLCHAIN_PREFIX}ar) -find_program(CMAKE_LD ${TOOLCHAIN_PREFIX}ar) -find_program(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy) -find_program(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump) - -add_compile_options(--sysroot=${CROSS_ROOTFS}) -add_compile_options(-fPIE) - -## Needed for Android or bionic specific conditionals -add_compile_options(-D__ANDROID__) -add_compile_options(-D__BIONIC__) - -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -B ${CROSS_ROOTFS}/usr/lib/gcc/${TOOLCHAIN}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -L${CROSS_ROOTFS}/lib/${TOOLCHAIN}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} --sysroot=${CROSS_ROOTFS}") -set(CROSS_LINK_FLAGS "${CROSS_LINK_FLAGS} -fPIE -pie") - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) -set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CROSS_LINK_FLAGS}" CACHE STRING "" FORCE) - -set(CMAKE_FIND_ROOT_PATH "${CROSS_ROOTFS}") -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/eng/common/cross/build-android-rootfs.sh b/eng/common/cross/build-android-rootfs.sh index adceda877a..5e74881eb2 100755 --- a/eng/common/cross/build-android-rootfs.sh +++ b/eng/common/cross/build-android-rootfs.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -e -__NDK_Version=r14 +__NDK_Version=r21 usage() { @@ -16,11 +16,11 @@ usage() echo. echo "By default, the NDK will be downloaded into the cross/android-rootfs/android-ndk-$__NDK_Version directory. If you already have an NDK installation," echo "you can set the NDK_DIR environment variable to have this script use that installation of the NDK." - echo "By default, this script will generate a file, android_platform, in the root of the ROOTFS_DIR directory that contains the RID for the supported and tested Android build: android.21-arm64. This file is to replace '/etc/os-release', which is not available for Android." + echo "By default, this script will generate a file, android_platform, in the root of the ROOTFS_DIR directory that contains the RID for the supported and tested Android build: android.28-arm64. This file is to replace '/etc/os-release', which is not available for Android." exit 1 } -__ApiLevel=21 # The minimum platform for arm64 is API level 21 +__ApiLevel=28 # The minimum platform for arm64 is API level 21 but the minimum version that support glob(3) is 28. See $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/glob.h __BuildArch=arm64 __AndroidArch=aarch64 __AndroidToolchain=aarch64-linux-android @@ -54,12 +54,11 @@ done # Obtain the location of the bash script to figure out where the root of the repo is. __CrossDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +__Android_Cross_Dir="$(cd "$__CrossDir/../../../.tools/android-rootfs" && pwd)" -__Android_Cross_Dir="$__CrossDir/android-rootfs" __NDK_Dir="$__Android_Cross_Dir/android-ndk-$__NDK_Version" -__libunwind_Dir="$__Android_Cross_Dir/libunwind" __lldb_Dir="$__Android_Cross_Dir/lldb" -__ToolchainDir="$__Android_Cross_Dir/toolchain/$__BuildArch" +__ToolchainDir="$__Android_Cross_Dir/android-ndk-$__NDK_Version" if [[ -n "$TOOLCHAIN_DIR" ]]; then __ToolchainDir=$TOOLCHAIN_DIR @@ -89,49 +88,33 @@ if [ ! -d $__lldb_Dir ]; then unzip -q $__Android_Cross_Dir/lldb-2.3.3614996-linux-x86_64.zip -d $__lldb_Dir fi -# Create the RootFS for both arm64 as well as aarch -rm -rf $__Android_Cross_Dir/toolchain - -echo Generating the $__BuildArch toolchain -$__NDK_Dir/build/tools/make_standalone_toolchain.py --arch $__BuildArch --api $__ApiLevel --install-dir $__ToolchainDir - -# Install the required packages into the toolchain -# TODO: Add logic to get latest pkg version instead of specific version number -rm -rf $__Android_Cross_Dir/deb/ -rm -rf $__Android_Cross_Dir/tmp - -mkdir -p $__Android_Cross_Dir/deb/ +echo "Download dependencies..." mkdir -p $__Android_Cross_Dir/tmp/$arch/ -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libicu_60.2_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libicu_60.2_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libicu-dev_60.2_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libicu-dev_60.2_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libandroid-glob-dev_0.4_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libandroid-glob-dev_0.4_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libandroid-glob_0.4_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libandroid-glob_0.4_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libandroid-support-dev_22_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libandroid-support-dev_22_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libandroid-support_22_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libandroid-support_22_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/liblzma-dev_5.2.3_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/liblzma-dev_5.2.3_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/liblzma_5.2.3_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/liblzma_5.2.3_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libunwind-dev_1.2.20170304_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libunwind-dev_1.2.20170304_$__AndroidArch.deb -wget -nv -nc http://termux.net/dists/stable/main/binary-$__AndroidArch/libunwind_1.2.20170304_$__AndroidArch.deb -O $__Android_Cross_Dir/deb/libunwind_1.2.20170304_$__AndroidArch.deb +# combined dependencies for coreclr, installer and libraries +__AndroidPackages="libicu" +__AndroidPackages+=" libandroid-glob" +__AndroidPackages+=" liblzma" +__AndroidPackages+=" krb5" +__AndroidPackages+=" openssl" -echo Unpacking Termux packages -dpkg -x $__Android_Cross_Dir/deb/libicu_60.2_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libicu-dev_60.2_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libandroid-glob-dev_0.4_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libandroid-glob_0.4_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libandroid-support-dev_22_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libandroid-support_22_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/liblzma-dev_5.2.3_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/liblzma_5.2.3_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libunwind-dev_1.2.20170304_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ -dpkg -x $__Android_Cross_Dir/deb/libunwind_1.2.20170304_$__AndroidArch.deb $__Android_Cross_Dir/tmp/$__AndroidArch/ +for path in $(wget -qO- http://termux.net/dists/stable/main/binary-$__AndroidArch/Packages |\ + grep -A15 "Package: \(${__AndroidPackages// /\\|}\)" | grep -v "static\|tool" | grep Filename); do + + if [[ "$path" != "Filename:" ]]; then + echo "Working on: $path" + wget -qO- http://termux.net/$path | dpkg -x - $__Android_Cross_Dir/tmp/$__AndroidArch/ + fi +done cp -R $__Android_Cross_Dir/tmp/$__AndroidArch/data/data/com.termux/files/usr/* $__ToolchainDir/sysroot/usr/ # Generate platform file for build.sh script to assign to __DistroRid echo "Generating platform file..." +echo "RID=android.${__ApiLevel}-${__BuildArch}" > $__ToolchainDir/sysroot/android_platform -echo "RID=android.21-arm64" > $__ToolchainDir/sysroot/android_platform -echo Now run: -echo CONFIG_DIR=\`realpath cross/android/$__BuildArch\` ROOTFS_DIR=\`realpath $__ToolchainDir/sysroot\` ./build.sh cross $__BuildArch skipgenerateversion skipnuget cmakeargs -DENABLE_LLDBPLUGIN=0 - +echo "Now to build coreclr, libraries and installers; run:" +echo ROOTFS_DIR=\$\(realpath $__ToolchainDir/sysroot\) ./build.sh --cross --arch $__BuildArch \ + --subsetCategory coreclr \ + --subsetCategory libraries \ + --subsetCategory installer diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index 556da2e135..1823804da4 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -43,10 +43,30 @@ if(TARGET_ARCH_NAME STREQUAL "armel") endif() endif() -set(CMAKE_SYSROOT "${CROSS_ROOTFS}") -set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") -set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") -set(CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") +if("$ENV{__DistroRid}" MATCHES "android.*") + if(TARGET_ARCH_NAME STREQUAL "arm") + set(ANDROID_ABI armeabi-v7a) + elseif(TARGET_ARCH_NAME STREQUAL "arm64") + set(ANDROID_ABI arm64-v8a) + endif() + + # extract platform number required by the NDK's toolchain + string(REGEX REPLACE ".*\\.([0-9]+)-.*" "\\1" ANDROID_PLATFORM "$ENV{__DistroRid}") + + set(ANDROID_TOOLCHAIN clang) + set(FEATURE_EVENT_TRACE 0) # disable event trace as there is no lttng-ust package in termux repository + set(CMAKE_SYSTEM_LIBRARY_PATH "${CROSS_ROOTFS}/usr/lib") + set(CMAKE_SYSTEM_INCLUDE_PATH "${CROSS_ROOTFS}/usr/include") + + # include official NDK toolchain script + include(${CROSS_ROOTFS}/../build/cmake/android.toolchain.cmake) +else() + set(CMAKE_SYSROOT "${CROSS_ROOTFS}") + + set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") + set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") + set(CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/usr") +endif() # Specify link flags @@ -63,7 +83,7 @@ endif() # Specify compile options -if(TARGET_ARCH_NAME MATCHES "^(arm|armel|arm64)$") +if(TARGET_ARCH_NAME MATCHES "^(arm|armel|arm64)$" AND NOT "$ENV{__DistroRid}" MATCHES "android.*") set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN}) diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 index 2a30ccfd74..435e764134 100644 --- a/eng/common/darc-init.ps1 +++ b/eng/common/darc-init.ps1 @@ -7,7 +7,7 @@ param ( . $PSScriptRoot\tools.ps1 -function InstallDarcCli ($darcVersion) { +function InstallDarcCli ($darcVersion, $toolpath) { $darcCliPackageName = 'microsoft.dotnet.darc' $dotnetRoot = InitializeDotNetCli -install:$true @@ -32,13 +32,13 @@ function InstallDarcCli ($darcVersion) { Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity -g" & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g }else { - Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity -g --tool-path '$toolpath'" + Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'" & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath" } } try { - InstallDarcCli $darcVersion + InstallDarcCli $darcVersion $toolpath } catch { Write-Host $_.ScriptStackTrace diff --git a/eng/common/pipeline-logging-functions.ps1 b/eng/common/pipeline-logging-functions.ps1 index a3e1317ad4..5042baebf1 100644 --- a/eng/common/pipeline-logging-functions.ps1 +++ b/eng/common/pipeline-logging-functions.ps1 @@ -31,7 +31,9 @@ function Write-PipelineTelemetryError { $PSBoundParameters.Remove('Category') | Out-Null - $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message" + if($Force -Or ((Test-Path variable:ci) -And $ci)) { + $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message" + } $PSBoundParameters.Remove('Message') | Out-Null $PSBoundParameters.Add('Message', $Message) Write-PipelineTaskError @PSBoundParameters diff --git a/eng/common/templates/post-build/channels/generic-public-channel.yml b/eng/common/templates/post-build/channels/generic-public-channel.yml index 29bc1a941a..08853ec45e 100644 --- a/eng/common/templates/post-build/channels/generic-public-channel.yml +++ b/eng/common/templates/post-build/channels/generic-public-channel.yml @@ -10,6 +10,8 @@ parameters: transportFeed: '' shippingFeed: '' symbolsFeed: '' + # If the channel name is empty, no links will be generated + akaMSChannelName: '' stages: - stage: ${{ parameters.stageName }} @@ -161,6 +163,9 @@ stages: /p:AzureDevOpsStaticTransportFeedKey='$(dn-bot-dnceng-artifact-feeds-rw)' /p:AzureDevOpsStaticSymbolsFeed='${{ parameters.symbolsFeed }}' /p:AzureDevOpsStaticSymbolsFeedKey='$(dn-bot-dnceng-artifact-feeds-rw)' + /p:LatestLinkShortUrlPrefix=dotnet/'${{ parameters.akaMSChannelName }}' + /p:AkaMSClientId=$(akams-client-id) + /p:AkaMSClientSecret=$(akams-client-secret) ${{ parameters.artifactsPublishingAdditionalParameters }} - template: ../../steps/publish-logs.yml diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index 05ff3561fc..7be5b0bfad 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -199,6 +199,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'NetCore_Dev5_Publish' channelName: '.NET Core 5 Dev' + akaMSChannelName: 'net5/dev' channelId: ${{ parameters.NetCoreDev5ChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json' @@ -212,6 +213,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'Net5_Preview1_Publish' channelName: '.NET 5 Preview 1' + akaMSChannelName: 'net5/preview1' channelId: ${{ parameters.Net5Preview1ChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json' @@ -225,6 +227,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'Net5_Preview2_Publish' channelName: '.NET 5 Preview 2' + akaMSChannelName: 'net5/preview2' channelId: ${{ parameters.Net5Preview2ChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json' @@ -238,6 +241,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'Net_Eng_Latest_Publish' channelName: '.NET Eng - Latest' + akaMSChannelName: 'eng/daily' channelId: ${{ parameters.NetEngLatestChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' @@ -251,6 +255,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'Net_Eng_Validation_Publish' channelName: '.NET Eng - Validation' + akaMSChannelName: 'eng/validation' channelId: ${{ parameters.NetEngValidationChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' @@ -264,6 +269,7 @@ stages: symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} stageName: 'General_Testing_Publish' channelName: 'General Testing' + akaMSChannelName: 'generaltesting' channelId: ${{ parameters.GeneralTestingChannelId }} transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/general-testing/nuget/v3/index.json' shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/general-testing/nuget/v3/index.json' diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index a45302b377..60c1cd8975 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -635,7 +635,7 @@ function MSBuild-Core() { $exitCode = Exec-Process $buildTool.Path $cmdArgs if ($exitCode -ne 0) { - Write-PipelineTelemetryError Category 'Build' -Message 'Build failed.' + Write-PipelineTelemetryError -Category 'Build' -Message 'Build failed.' $buildLog = GetMSBuildBinaryLogCommandLineArgument $args if ($buildLog -ne $null) { diff --git a/global.json b/global.json index 15a1a6543e..e8b4fdb412 100644 --- a/global.json +++ b/global.json @@ -25,7 +25,7 @@ }, "msbuild-sdks": { "Yarn.MSBuild": "1.15.2", - "Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20151.1", - "Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20151.1" + "Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.20158.2", + "Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20158.2" } } From fb0abb968f85fa9fa0cbaa156a480bb9972756fe Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 9 Mar 2020 13:28:12 -0700 Subject: [PATCH 45/57] Quarantine template tests thaat require artifacts --- src/ProjectTemplates/test/GrpcTemplateTest.cs | 1 + src/ProjectTemplates/test/IdentityUIPackageTest.cs | 1 + src/ProjectTemplates/test/MvcTemplateTest.cs | 3 ++- src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs | 2 ++ src/ProjectTemplates/test/RazorPagesTemplateTest.cs | 4 +++- src/ProjectTemplates/test/WorkerTemplateTest.cs | 1 + 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ProjectTemplates/test/GrpcTemplateTest.cs b/src/ProjectTemplates/test/GrpcTemplateTest.cs index eda3fb44ef..7f64f4b29d 100644 --- a/src/ProjectTemplates/test/GrpcTemplateTest.cs +++ b/src/ProjectTemplates/test/GrpcTemplateTest.cs @@ -26,6 +26,7 @@ namespace Templates.Test [ConditionalFact] [SkipOnHelix("Not supported queues", Queues = "Windows.7.Amd64;Windows.7.Amd64.Open;OSX.1014.Amd64;OSX.1014.Amd64.Open")] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task GrpcTemplate() { Project = await ProjectFactory.GetOrCreateProject("grpc", Output); diff --git a/src/ProjectTemplates/test/IdentityUIPackageTest.cs b/src/ProjectTemplates/test/IdentityUIPackageTest.cs index f6974eceb2..b89c19b61a 100644 --- a/src/ProjectTemplates/test/IdentityUIPackageTest.cs +++ b/src/ProjectTemplates/test/IdentityUIPackageTest.cs @@ -121,6 +121,7 @@ namespace Templates.Test [ConditionalTheory] [MemberData(nameof(MSBuildIdentityUIPackageOptions))] [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task IdentityUIPackage_WorksWithDifferentOptions(IDictionary packageOptions, string versionValidator, string[] expectedFiles) { Project = await ProjectFactory.GetOrCreateProject("identityuipackage" + string.Concat(packageOptions.Values), Output); diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index 3bcb51416a..25866634fc 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -107,7 +107,7 @@ namespace Templates.Test [ConditionalTheory] [InlineData(true)] [InlineData(false)] - [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task MvcTemplate_IndividualAuth(bool useLocalDB) { Project = await ProjectFactory.GetOrCreateProject("mvcindividual" + (useLocalDB ? "uld" : ""), Output); @@ -223,6 +223,7 @@ namespace Templates.Test } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task MvcTemplate_RazorRuntimeCompilation_BuildsAndPublishes() { Project = await ProjectFactory.GetOrCreateProject("mvc_rc", Output); diff --git a/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs b/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs index 0d02a56f8f..b16f8fa345 100644 --- a/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs +++ b/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Templates.Test.Helpers; using Xunit; using Xunit.Abstractions; @@ -41,6 +42,7 @@ namespace Templates.Test } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task RazorClassLibraryTemplateAsync() { Project = await ProjectFactory.GetOrCreateProject("razorclasslib", Output); diff --git a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs index 4e7b924492..10370a3573 100644 --- a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs +++ b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs @@ -97,7 +97,8 @@ namespace Templates.Test [ConditionalTheory] [InlineData(false)] [InlineData(true)] - [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB) { Project = await ProjectFactory.GetOrCreateProject("razorpagesindividual" + (useLocalDB ? "uld" : ""), Output); @@ -213,6 +214,7 @@ namespace Templates.Test } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task RazorPagesTemplate_RazorRuntimeCompilation_BuildsAndPublishes() { Project = await ProjectFactory.GetOrCreateProject("razorpages_rc", Output); diff --git a/src/ProjectTemplates/test/WorkerTemplateTest.cs b/src/ProjectTemplates/test/WorkerTemplateTest.cs index 0a3bca6568..1bc2d8d5b3 100644 --- a/src/ProjectTemplates/test/WorkerTemplateTest.cs +++ b/src/ProjectTemplates/test/WorkerTemplateTest.cs @@ -22,6 +22,7 @@ namespace Templates.Test public ITestOutputHelper Output { get; } [Fact] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task WorkerTemplateAsync() { Project = await ProjectFactory.GetOrCreateProject("worker", Output); From 19d49670a6237df8308acb590e5a58b267e7b07c Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 9 Mar 2020 13:39:59 -0700 Subject: [PATCH 46/57] Update MvcTemplateTest.cs --- src/ProjectTemplates/test/MvcTemplateTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index 25866634fc..ff60a16bd8 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -107,6 +107,7 @@ namespace Templates.Test [ConditionalTheory] [InlineData(true)] [InlineData(false)] + [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")] public async Task MvcTemplate_IndividualAuth(bool useLocalDB) { From 72bb72f11c2268af13eb89b07284e5034b6372bc Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 9 Mar 2020 14:28:51 -0700 Subject: [PATCH 47/57] Quarantine Blazor-Ignitor based tests (#19667) --- .../ServerExecutionTests/ComponentHubInvalidEventTest.cs | 3 ++- .../ServerExecutionTests/ComponentHubReliabilityTest.cs | 1 + .../E2ETest/ServerExecutionTests/InteropReliabilityTests.cs | 1 + .../ServerExecutionTests/RemoteRendererBufferLimitTest.cs | 4 +++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubInvalidEventTest.cs b/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubInvalidEventTest.cs index 26bce75205..7d46a7defb 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubInvalidEventTest.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubInvalidEventTest.cs @@ -4,11 +4,11 @@ using System; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.E2ETest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using TestServer; using Xunit; @@ -16,6 +16,7 @@ using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests { + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19666")] public class ComponentHubInvalidEventTest : IgnitorTest { public ComponentHubInvalidEventTest(BasicTestAppServerSiteFixture serverFixture, ITestOutputHelper output) diff --git a/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubReliabilityTest.cs b/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubReliabilityTest.cs index 774197872d..616cdab1e0 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubReliabilityTest.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/ComponentHubReliabilityTest.cs @@ -14,6 +14,7 @@ using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests { + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19666")] public class ComponentHubReliabilityTest : IgnitorTest { public ComponentHubReliabilityTest(BasicTestAppServerSiteFixture serverFixture, ITestOutputHelper output) diff --git a/src/Components/test/E2ETest/ServerExecutionTests/InteropReliabilityTests.cs b/src/Components/test/E2ETest/ServerExecutionTests/InteropReliabilityTests.cs index 288b3e7c8c..ef66a592d3 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/InteropReliabilityTests.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/InteropReliabilityTests.cs @@ -19,6 +19,7 @@ using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests { + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19666")] public class InteropReliabilityTests : IgnitorTest { public InteropReliabilityTests(BasicTestAppServerSiteFixture serverFixture, ITestOutputHelper output) diff --git a/src/Components/test/E2ETest/ServerExecutionTests/RemoteRendererBufferLimitTest.cs b/src/Components/test/E2ETest/ServerExecutionTests/RemoteRendererBufferLimitTest.cs index 5e9e2ac4e9..3f4958bdc3 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/RemoteRendererBufferLimitTest.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/RemoteRendererBufferLimitTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Ignitor; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using TestServer; using Xunit; @@ -13,6 +14,7 @@ using Xunit.Abstractions; namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests { + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19666")] public class RemoteRendererBufferLimitTest : IgnitorTest { public RemoteRendererBufferLimitTest(BasicTestAppServerSiteFixture serverFixture, ITestOutputHelper output) @@ -31,7 +33,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests await Client.SelectAsync("test-selector-select", "BasicTestApp.LimitCounterComponent"); Client.ConfirmRenderBatch = false; - for (int i = 0; i < 10; i++) + for (var i = 0; i < 10; i++) { await Client.ClickAsync("increment"); } From dd7fa110c3c3810cb099f79407a31dcc692c822a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 21:46:26 +0000 Subject: [PATCH 48/57] Update dependencies from https://github.com/dotnet/efcore build 20200309.2 (#19718) - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20159.2 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20159.2 - dotnet-ef - 5.0.0-preview.3.20159.2 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20159.2 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20159.2 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20159.2 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20159.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index fa4d240448..8406b923ae 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -29,33 +29,33 @@ https://github.com/dotnet/aspnetcore-tooling cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 - + https://github.com/dotnet/efcore - 71e1b3e182dda86e4051870b1b2f60fff1012946 + 8425e2df5c8b933fb717526731375095162de368 https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index 3789afccc8..3142e4c0ba 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,13 +140,13 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 - 5.0.0-preview.3.20159.1 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.2 5.0.0-preview.3.20157.1 5.0.0-preview.3.20157.1 From 58c13c312eabfb00fdb56c7e0f69813359d2fa9a Mon Sep 17 00:00:00 2001 From: Guillaume Nury Date: Tue, 10 Mar 2020 00:00:35 +0100 Subject: [PATCH 49/57] Support nullable enum in InputSelect (#19506) As BindConverter supports Nullable enums (here), call it if the underlying type has type of enum. Addresses #13624 --- src/Components/Web/src/Forms/InputSelect.cs | 4 +- .../Web/test/Forms/InputSelectTest.cs | 153 ++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/Components/Web/test/Forms/InputSelectTest.cs diff --git a/src/Components/Web/src/Forms/InputSelect.cs b/src/Components/Web/src/Forms/InputSelect.cs index b8cdbeab1f..e1a3ab0eae 100644 --- a/src/Components/Web/src/Forms/InputSelect.cs +++ b/src/Components/Web/src/Forms/InputSelect.cs @@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Components.Forms /// [Parameter] public RenderFragment ChildContent { get; set; } + private readonly Type _nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(TValue)); + /// protected override void BuildRenderTree(RenderTreeBuilder builder) { @@ -38,7 +40,7 @@ namespace Microsoft.AspNetCore.Components.Forms validationErrorMessage = null; return true; } - else if (typeof(TValue).IsEnum) + else if (typeof(TValue).IsEnum || (_nullableUnderlyingType != null && _nullableUnderlyingType.IsEnum)) { var success = BindConverter.TryConvertTo(value, CultureInfo.CurrentCulture, out var parsedValue); if (success) diff --git a/src/Components/Web/test/Forms/InputSelectTest.cs b/src/Components/Web/test/Forms/InputSelectTest.cs new file mode 100644 index 0000000000..7c49fe03be --- /dev/null +++ b/src/Components/Web/test/Forms/InputSelectTest.cs @@ -0,0 +1,153 @@ +// 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.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.AspNetCore.Components.RenderTree; +using Microsoft.AspNetCore.Components.Test.Helpers; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Forms +{ + public class InputSelectTest + { + [Fact] + public async Task ParsesCurrentValueWhenUsingNotNullableEnumWithNotEmptyValue() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestInputSelectHostComponent + { + EditContext = new EditContext(model), + ValueExpression = () => model.NotNullableEnum + }; + var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent); + + // Act + inputSelectComponent.CurrentValueAsString = "Two"; + + // Assert + Assert.Equal(TestEnum.Two, inputSelectComponent.CurrentValue); + } + + [Fact] + public async Task ParsesCurrentValueWhenUsingNotNullableEnumWithEmptyValue() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestInputSelectHostComponent + { + EditContext = new EditContext(model), + ValueExpression = () => model.NotNullableEnum + }; + var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent); + + // Act + inputSelectComponent.CurrentValueAsString = ""; + + // Assert + Assert.Equal(default, inputSelectComponent.CurrentValue); + } + + [Fact] + public async Task ParsesCurrentValueWhenUsingNullableEnumWithNotEmptyValue() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestInputSelectHostComponent + { + EditContext = new EditContext(model), + ValueExpression = () => model.NullableEnum + }; + var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent); + + // Act + inputSelectComponent.CurrentValueAsString = "Two"; + + // Assert + Assert.Equal(TestEnum.Two, inputSelectComponent.Value); + } + + [Fact] + public async Task ParsesCurrentValueWhenUsingNullableEnumWithEmptyValue() + { + // Arrange + var model = new TestModel(); + var rootComponent = new TestInputSelectHostComponent + { + EditContext = new EditContext(model), + ValueExpression = () => model.NullableEnum + }; + var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent); + + // Act + inputSelectComponent.CurrentValueAsString = ""; + + // Assert + Assert.Null(inputSelectComponent.CurrentValue); + } + + private static TestInputSelect FindInputSelectComponent(CapturedBatch batch) + => batch.ReferenceFrames + .Where(f => f.FrameType == RenderTreeFrameType.Component) + .Select(f => f.Component) + .OfType>() + .Single(); + + private static async Task> RenderAndGetTestInputComponentAsync(TestInputSelectHostComponent hostComponent) + { + var testRenderer = new TestRenderer(); + var componentId = testRenderer.AssignRootComponentId(hostComponent); + await testRenderer.RenderRootComponentAsync(componentId); + return FindInputSelectComponent(testRenderer.Batches.Single()); + } + + enum TestEnum + { + One, + Two, + Tree + } + + class TestModel + { + public TestEnum NotNullableEnum { get; set; } + + public TestEnum? NullableEnum { get; set; } + } + + class TestInputSelect : InputSelect + { + public new TValue CurrentValue => base.CurrentValue; + + public new string CurrentValueAsString + { + get => base.CurrentValueAsString; + set => base.CurrentValueAsString = value; + } + } + + class TestInputSelectHostComponent : AutoRenderComponent + { + public EditContext EditContext { get; set; } + + public Expression> ValueExpression { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenComponent>(0); + builder.AddAttribute(1, "Value", EditContext); + builder.AddAttribute(2, "ChildContent", new RenderFragment(childBuilder => + { + childBuilder.OpenComponent>(0); + childBuilder.AddAttribute(0, "ValueExpression", ValueExpression); + childBuilder.CloseComponent(); + })); + builder.CloseComponent(); + } + } + } +} From 8ec31594eae83295fb2c86d99d23c32ad33da5b0 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Mon, 9 Mar 2020 17:30:54 -0700 Subject: [PATCH 50/57] Fix #2442 by adding webmanifest extension to content-type map (#19661) --- .../StaticFiles/src/FileExtensionContentTypeProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Middleware/StaticFiles/src/FileExtensionContentTypeProvider.cs b/src/Middleware/StaticFiles/src/FileExtensionContentTypeProvider.cs index 69b92c4c47..a40c1f2e76 100644 --- a/src/Middleware/StaticFiles/src/FileExtensionContentTypeProvider.cs +++ b/src/Middleware/StaticFiles/src/FileExtensionContentTypeProvider.cs @@ -343,6 +343,7 @@ namespace Microsoft.AspNetCore.StaticFiles { ".wcm", "application/vnd.ms-works" }, { ".wdb", "application/vnd.ms-works" }, { ".webm", "video/webm" }, + { ".webmanifest", "application/manifest+json" }, // https://w3c.github.io/manifest/#media-type-registration { ".webp", "image/webp" }, { ".wks", "application/vnd.ms-works" }, { ".wm", "video/x-ms-wm" }, From cd6e6ae0bc461173601132d0f2b6eb8bf9d05912 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 10 Mar 2020 15:45:24 +1300 Subject: [PATCH 51/57] Reuse Http2OutputProducer.ProcessDataWrites task (#19695) --- .../src/Internal/Http2/Http2Connection.cs | 34 ++-- .../src/Internal/Http2/Http2OutputProducer.cs | 163 ++++++++++++------ .../Core/src/Internal/Http2/Http2Stream.cs | 7 +- .../Http2/Http2ConnectionTests.cs | 24 ++- .../ManualResetValueTaskSource.cs | 25 +++ 5 files changed, 184 insertions(+), 69 deletions(-) create mode 100644 src/Shared/ServerInfrastructure/ManualResetValueTaskSource.cs diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs index 41d09b162a..7db15bb95b 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs @@ -55,7 +55,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private int _highestOpenedStreamId; private bool _gracefulCloseStarted; - private readonly Dictionary _streams = new Dictionary(); private int _clientActiveStreamCount = 0; private int _serverActiveStreamCount = 0; @@ -66,6 +65,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private int _isClosed; // Internal for testing + internal readonly Dictionary _streams = new Dictionary(); internal Http2StreamStack StreamPool; internal const int InitialStreamPoolSize = 5; @@ -304,6 +304,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 UpdateCompletedStreams(); } + while (StreamPool.TryPop(out var pooledStream)) + { + pooledStream.Dispose(); + } + // This cancels keep-alive and request header timeouts, but not the response drain timeout. TimeoutControl.CancelTimeout(); TimeoutControl.StartDrainTimeout(Limits.MinResponseDataRate, Limits.MaxResponseBufferSize); @@ -909,6 +914,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } catch (Http2StreamErrorException) { + _currentHeadersStream.Dispose(); ResetRequestHeaderParsingState(); throw; } @@ -1069,11 +1075,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED); } - _streams.Remove(stream.StreamId); - if (stream.CanReuse) - { - ReturnStream(stream); - } + RemoveStream(stream); } else { @@ -1087,6 +1089,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } } + private void RemoveStream(Http2Stream stream) + { + _streams.Remove(stream.StreamId); + if (stream.CanReuse) + { + ReturnStream(stream); + } + else + { + stream.Dispose(); + } + } + // Compare to UpdateCompletedStreams, but only removes streams if over the max stream drain limit. private void MakeSpaceInDrainQueue() { @@ -1099,11 +1114,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _serverActiveStreamCount--; } - _streams.Remove(stream.StreamId); - if (stream.CanReuse) - { - ReturnStream(stream); - } + RemoveStream(stream); } } @@ -1460,7 +1471,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 Unknown = 0x40000000 } - private static class GracefulCloseInitiator { public const int None = 0; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs index d129540342..c1c8a320f5 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; +using System.Threading.Tasks.Sources; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; @@ -17,7 +18,7 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - internal class Http2OutputProducer : IHttpOutputProducer, IHttpOutputAborter + internal class Http2OutputProducer : IHttpOutputProducer, IHttpOutputAborter, IValueTaskSource, IDisposable { private int StreamId => _stream.StreamId; private readonly Http2FrameWriter _frameWriter; @@ -33,14 +34,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private readonly Pipe _pipe; private readonly ConcurrentPipeWriter _pipeWriter; private readonly PipeReader _pipeReader; - private ValueTask _dataWriteProcessingTask; + private readonly ManualResetValueTaskSource _resetAwaitable = new ManualResetValueTaskSource(); + private IMemoryOwner _fakeMemoryOwner; private bool _startedWritingDataFrames; private bool _streamCompleted; private bool _suffixSent; private bool _streamEnded; private bool _writerComplete; + private bool _disposed; - private IMemoryOwner _fakeMemoryOwner; + // Internal for testing + internal ValueTask _dataWriteProcessingTask; + + /// The core logic for the IValueTaskSource implementation. + private ManualResetValueTaskSourceCore _responseCompleteTaskSource = new ManualResetValueTaskSourceCore { RunContinuationsAsynchronously = true }; // mutable struct, do not make this readonly + + // This object is itself usable as a backing source for ValueTask. Since there's only ever one awaiter + // for this object's state transitions at a time, we allow the object to be awaited directly. All functionality + // associated with the implementation is just delegated to the ManualResetValueTaskSourceCore. + private ValueTask GetWaiterTask() => new ValueTask(this, _responseCompleteTaskSource.Version); + ValueTaskSourceStatus IValueTaskSource.GetStatus(short token) => _responseCompleteTaskSource.GetStatus(token); + void IValueTaskSource.OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _responseCompleteTaskSource.OnCompleted(continuation, state, token, flags); + FlushResult IValueTaskSource.GetResult(short token) => _responseCompleteTaskSource.GetResult(token); public Http2OutputProducer(Http2Stream stream, Http2StreamContext context, StreamOutputFlowControl flowControl) { @@ -64,7 +79,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 public void StreamReset() { - Debug.Assert(_dataWriteProcessingTask.IsCompletedSuccessfully); + // Data background task must still be running. + Debug.Assert(!_dataWriteProcessingTask.IsCompleted); + // Response should have been completed. + Debug.Assert(_responseCompleteTaskSource.GetStatus(_responseCompleteTaskSource.Version) == ValueTaskSourceStatus.Succeeded); _streamEnded = false; _suffixSent = false; @@ -75,8 +93,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _pipe.Reset(); _pipeWriter.Reset(); + _responseCompleteTaskSource.Reset(); - _dataWriteProcessingTask = ProcessDataWrites(); + // Trigger the data process task to resume + _resetAwaitable.SetResult(null); } public void Complete() @@ -222,14 +242,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { if (_streamCompleted) { - return _dataWriteProcessingTask; + return GetWaiterTask(); } _streamCompleted = true; _suffixSent = true; _pipeWriter.Complete(); - return _dataWriteProcessingTask; + return GetWaiterTask(); } } @@ -370,68 +390,90 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { } - private async ValueTask ProcessDataWrites() + private async ValueTask ProcessDataWrites() { - FlushResult flushResult = default; - try + // ProcessDataWrites runs for the lifetime of the Http2OutputProducer, and is designed to be reused by multiple streams. + // When Http2OutputProducer is no longer used (e.g. a stream is aborted and will no longer be used, or the connection is closed) + // it should be disposed so ProcessDataWrites exits. Not disposing won't cause a memory leak in release builds, but in debug + // builds active tasks are rooted on Task.s_currentActiveTasks. Dispose could be removed in the future when active tasks are + // tracked by a weak reference. See https://github.com/dotnet/runtime/issues/26565 + do { - ReadResult readResult; - - do + FlushResult flushResult = default; + ReadResult readResult = default; + try { - readResult = await _pipeReader.ReadAsync(); - if (readResult.IsCanceled) + do { - // Response body is aborted, break and complete reader. - break; - } - else if (readResult.IsCompleted && _stream.ResponseTrailers?.Count > 0) - { - // Output is ending and there are trailers to write - // Write any remaining content then write trailers - if (readResult.Buffer.Length > 0) + readResult = await _pipeReader.ReadAsync(); + + if (readResult.IsCanceled) { - // Only flush if required (i.e. content length exceeds flow control availability) - // Writing remaining content without flushing allows content and trailers to be sent in the same packet - await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream: false, forceFlush: false); + // Response body is aborted, break and complete reader. + break; } - - _stream.ResponseTrailers.SetReadOnly(); - _stream.DecrementActiveClientStreamCount(); - flushResult = await _frameWriter.WriteResponseTrailers(StreamId, _stream.ResponseTrailers); - } - else if (readResult.IsCompleted && _streamEnded) - { - if (readResult.Buffer.Length != 0) + else if (readResult.IsCompleted && _stream.ResponseTrailers?.Count > 0) { - ThrowUnexpectedState(); - } + // Output is ending and there are trailers to write + // Write any remaining content then write trailers + if (readResult.Buffer.Length > 0) + { + // Only flush if required (i.e. content length exceeds flow control availability) + // Writing remaining content without flushing allows content and trailers to be sent in the same packet + await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream: false, forceFlush: false); + } - // Headers have already been written and there is no other content to write - flushResult = await _frameWriter.FlushAsync(outputAborter: null, cancellationToken: default); - } - else - { - var endStream = readResult.IsCompleted; - if (endStream) - { + _stream.ResponseTrailers.SetReadOnly(); _stream.DecrementActiveClientStreamCount(); + flushResult = await _frameWriter.WriteResponseTrailers(StreamId, _stream.ResponseTrailers); } - flushResult = await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream, forceFlush: true); - } + else if (readResult.IsCompleted && _streamEnded) + { + if (readResult.Buffer.Length != 0) + { + ThrowUnexpectedState(); + } - _pipeReader.AdvanceTo(readResult.Buffer.End); - } while (!readResult.IsCompleted); - } - catch (Exception ex) - { - _log.LogCritical(ex, nameof(Http2OutputProducer) + "." + nameof(ProcessDataWrites) + " observed an unexpected exception."); - } + // Headers have already been written and there is no other content to write + flushResult = await _frameWriter.FlushAsync(outputAborter: null, cancellationToken: default); + } + else + { + var endStream = readResult.IsCompleted; + if (endStream) + { + _stream.DecrementActiveClientStreamCount(); + } + flushResult = await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream, forceFlush: true); + } - _pipeReader.Complete(); + _pipeReader.AdvanceTo(readResult.Buffer.End); + } while (!readResult.IsCompleted); + } + catch (Exception ex) + { + _log.LogCritical(ex, nameof(Http2OutputProducer) + "." + nameof(ProcessDataWrites) + " observed an unexpected exception."); + } - return flushResult; + _pipeReader.Complete(); + + // Signal via WriteStreamSuffixAsync to the stream that output has finished. + // Stream state will move to RequestProcessingStatus.ResponseCompleted + _responseCompleteTaskSource.SetResult(flushResult); + + if (readResult.IsCompleted) + { + // Successfully read all data. Wait here for the stream to be reset. + await new ValueTask(_resetAwaitable, _resetAwaitable.Version); + _resetAwaitable.Reset(); + } + else + { + // Stream was aborted. + break; + } + } while (!_disposed); static void ThrowUnexpectedState() { @@ -486,5 +528,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 useSynchronizationContext: false, minimumSegmentSize: pool.GetMinimumSegmentSize() )); + + public void Dispose() + { + if (_disposed) + { + return; + } + _disposed = true; + + _resetAwaitable.SetResult(null); + } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index ee97bec12f..b46dc0f0f6 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -17,7 +17,7 @@ using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - internal abstract partial class Http2Stream : HttpProtocol, IThreadPoolWorkItem + internal abstract partial class Http2Stream : HttpProtocol, IThreadPoolWorkItem, IDisposable { private Http2StreamContext _context; private Http2OutputProducer _http2Output; @@ -588,6 +588,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 /// public abstract void Execute(); + public void Dispose() + { + _http2Output.Dispose(); + } + [Flags] private enum StreamCompletionFlags { diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index b0595d4545..f29cc35df5 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -76,12 +76,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public async Task StreamPool_SingleStream_ReturnedToPool() { - await InitializeConnectionAsync(_echoApplication); + var serverTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + await InitializeConnectionAsync(async context => + { + await serverTcs.Task; + await _echoApplication(context); + }); Assert.Equal(0, _connection.StreamPool.Count); await StartStreamAsync(1, _browserRequestHeaders, endStream: true); + var stream = _connection._streams[1]; + serverTcs.SetResult(null); + await ExpectAsync(Http2FrameType.HEADERS, withLength: 37, withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), @@ -98,6 +107,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests Assert.Equal(1, _connection.StreamPool.Count); await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); + + var output = (Http2OutputProducer)stream.Output; + await output._dataWriteProcessingTask; } [Fact] @@ -216,14 +228,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [QuarantinedTest] public async Task StreamPool_StreamIsInvalidState_DontReturnedToPool() { + var serverTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await InitializeConnectionAsync(async context => { + await serverTcs.Task; + await context.Response.WriteAsync("Content"); throw new InvalidOperationException("Put the stream into an invalid state by throwing after writing to response."); }); await StartStreamAsync(1, _browserRequestHeaders, endStream: true); + var stream = _connection._streams[1]; + serverTcs.SetResult(null); + await ExpectAsync(Http2FrameType.HEADERS, withLength: 33, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, @@ -244,6 +263,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests // Stream is not returned to the pool Assert.Equal(0, _connection.StreamPool.Count); + var output = (Http2OutputProducer)stream.Output; + await output._dataWriteProcessingTask; + await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); } diff --git a/src/Shared/ServerInfrastructure/ManualResetValueTaskSource.cs b/src/Shared/ServerInfrastructure/ManualResetValueTaskSource.cs new file mode 100644 index 0000000000..f11d2d50bd --- /dev/null +++ b/src/Shared/ServerInfrastructure/ManualResetValueTaskSource.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks.Sources; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal +{ + internal sealed class ManualResetValueTaskSource : IValueTaskSource, IValueTaskSource + { + private ManualResetValueTaskSourceCore _core; // mutable struct; do not make this readonly + + public bool RunContinuationsAsynchronously { get => _core.RunContinuationsAsynchronously; set => _core.RunContinuationsAsynchronously = value; } + public short Version => _core.Version; + public void Reset() => _core.Reset(); + public void SetResult(T result) => _core.SetResult(result); + public void SetException(Exception error) => _core.SetException(error); + + public T GetResult(short token) => _core.GetResult(token); + void IValueTaskSource.GetResult(short token) => _core.GetResult(token); + public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token); + public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _core.OnCompleted(continuation, state, token, flags); + } +} From 92e98b7ede2bc913d842152847d7d25a0cbf9fc7 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 10 Mar 2020 15:50:16 +1300 Subject: [PATCH 52/57] Read server response in Http2ConnectionBenchmark (#19694) --- .../Http2ConnectionBenchmark.cs | 111 +++++++++++++++--- .../Mocks/MockHttpContextFactory.cs | 42 +++++++ .../test/PipeWriterHttp2FrameExtensions.cs | 23 ++-- .../Http2/Http2TestBase.cs | 8 +- src/Shared/BenchmarkRunner/Program.cs | 2 +- 5 files changed, 158 insertions(+), 28 deletions(-) create mode 100644 src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockHttpContextFactory.cs diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs index 3018f4a346..b3e8f15403 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs @@ -3,9 +3,11 @@ using System; using System.Buffers; -using System.Collections.Generic; +using System.Buffers.Binary; +using System.Diagnostics; +using System.IO; using System.IO.Pipelines; -using System.Net.Http.HPack; +using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Http; @@ -25,20 +27,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance public class Http2ConnectionBenchmark { private MemoryPool _memoryPool; - private Pipe _pipe; private HttpRequestHeaders _httpRequestHeaders; private Http2Connection _connection; private Http2HeadersEnumerator _requestHeadersEnumerator; private int _currentStreamId; private byte[] _headersBuffer; + private DuplexPipe.DuplexPipePair _connectionPair; + private Http2Frame _httpFrame; + private string _responseData; + private int _dataWritten; + + [Params(0, 10, 1024 * 1024)] + public int ResponseDataLength { get; set; } [GlobalSetup] public void GlobalSetup() { _memoryPool = SlabMemoryPoolFactory.Create(); + _httpFrame = new Http2Frame(); + _responseData = new string('!', ResponseDataLength); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); - _pipe = new Pipe(options); + + _connectionPair = DuplexPipe.CreateConnectionPair(options, options); _httpRequestHeaders = new HttpRequestHeaders(); _httpRequestHeaders.Append(HeaderNames.Method, new StringValues("GET")); @@ -55,15 +66,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance Log = new KestrelTrace(NullLogger.Instance), SystemClock = new MockSystemClock() }; - serviceContext.ServerOptions.Limits.Http2.MaxStreamsPerConnection = int.MaxValue; serviceContext.DateHeaderValueManager.OnHeartbeat(default); _connection = new Http2Connection(new HttpConnectionContext { MemoryPool = _memoryPool, ConnectionId = "TestConnectionId", - Protocols = Core.HttpProtocols.Http2, - Transport = new MockDuplexPipe(_pipe.Reader, new NullPipeWriter()), + Protocols = HttpProtocols.Http2, + Transport = _connectionPair.Transport, ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), TimeoutControl = new MockTimeoutControl(), @@ -73,11 +83,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance _currentStreamId = 1; - _ = _connection.ProcessRequestsAsync(new DummyApplication()); + _ = _connection.ProcessRequestsAsync(new DummyApplication(c => ResponseDataLength == 0 ? Task.CompletedTask : c.Response.WriteAsync(_responseData), new MockHttpContextFactory())); - _pipe.Writer.Write(Http2Connection.ClientPreface); - _pipe.Writer.WriteSettings(new Http2PeerSettings()); - _pipe.Writer.FlushAsync().GetAwaiter().GetResult(); + _connectionPair.Application.Output.Write(Http2Connection.ClientPreface); + _connectionPair.Application.Output.WriteSettings(new Http2PeerSettings + { + InitialWindowSize = 2147483647 + }); + _connectionPair.Application.Output.FlushAsync().GetAwaiter().GetResult(); + + // Read past connection setup frames + ReceiveFrameAsync(_connectionPair.Application.Input, _httpFrame).GetAwaiter().GetResult(); + Debug.Assert(_httpFrame.Type == Http2FrameType.SETTINGS); + ReceiveFrameAsync(_connectionPair.Application.Input, _httpFrame).GetAwaiter().GetResult(); + Debug.Assert(_httpFrame.Type == Http2FrameType.WINDOW_UPDATE); + ReceiveFrameAsync(_connectionPair.Application.Input, _httpFrame).GetAwaiter().GetResult(); + Debug.Assert(_httpFrame.Type == Http2FrameType.SETTINGS); } [Benchmark] @@ -85,15 +106,77 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance { _requestHeadersEnumerator.Initialize(_httpRequestHeaders); _requestHeadersEnumerator.MoveNext(); - _pipe.Writer.WriteStartStream(streamId: _currentStreamId, _requestHeadersEnumerator, _headersBuffer, endStream: true); + _connectionPair.Application.Output.WriteStartStream(streamId: _currentStreamId, _requestHeadersEnumerator, _headersBuffer, endStream: true, frame: _httpFrame); + await _connectionPair.Application.Output.FlushAsync(); + + while (true) + { + await ReceiveFrameAsync(_connectionPair.Application.Input, _httpFrame); + + if (_httpFrame.StreamId != _currentStreamId && _httpFrame.StreamId != 0) + { + throw new Exception($"Unexpected stream ID: {_httpFrame.StreamId}"); + } + + if (_httpFrame.Type == Http2FrameType.DATA) + { + _dataWritten += _httpFrame.DataPayloadLength; + } + + if (_dataWritten > 1024 * 32) + { + _connectionPair.Application.Output.WriteWindowUpdateAsync(streamId: 0, _dataWritten, _httpFrame); + await _connectionPair.Application.Output.FlushAsync(); + + _dataWritten = 0; + } + + if ((_httpFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM) + { + break; + } + } + _currentStreamId += 2; - await _pipe.Writer.FlushAsync(); + } + + internal async ValueTask ReceiveFrameAsync(PipeReader pipeReader, Http2Frame frame, uint maxFrameSize = Http2PeerSettings.DefaultMaxFrameSize) + { + while (true) + { + var result = await pipeReader.ReadAsync(); + var buffer = result.Buffer; + var consumed = buffer.Start; + var examined = buffer.Start; + + try + { + if (Http2FrameReader.TryReadFrame(ref buffer, frame, maxFrameSize, out var framePayload)) + { + consumed = examined = framePayload.End; + return; + } + else + { + examined = buffer.End; + } + + if (result.IsCompleted) + { + throw new IOException("The reader completed without returning a frame."); + } + } + finally + { + pipeReader.AdvanceTo(consumed, examined); + } + } } [GlobalCleanup] public void Dispose() { - _pipe.Writer.Complete(); + _connectionPair.Application.Output.Complete(); _memoryPool?.Dispose(); } } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockHttpContextFactory.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockHttpContextFactory.cs new file mode 100644 index 0000000000..e89076aba2 --- /dev/null +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockHttpContextFactory.cs @@ -0,0 +1,42 @@ +// 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.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class MockHttpContextFactory : IHttpContextFactory + { + private readonly object _lock = new object(); + private readonly Queue _cache = new Queue(); + + public HttpContext Create(IFeatureCollection featureCollection) + { + DefaultHttpContext httpContext; + + lock (_lock) + { + if (!_cache.TryDequeue(out httpContext)) + { + httpContext = new DefaultHttpContext(); + } + } + + httpContext.Initialize(featureCollection); + return httpContext; + } + + public void Dispose(HttpContext httpContext) + { + lock (_lock) + { + var defaultHttpContext = (DefaultHttpContext)httpContext; + + defaultHttpContext.Uninitialize(); + _cache.Enqueue(defaultHttpContext); + } + } + } +} diff --git a/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs b/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs index daf333e703..481278ae42 100644 --- a/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs +++ b/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO.Pipelines; using System.Net.Http.HPack; @@ -24,9 +25,9 @@ namespace Microsoft.AspNetCore.Testing writer.Write(payload); } - public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream) + public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream, Http2Frame frame = null) { - var frame = new Http2Frame(); + frame ??= new Http2Frame(); frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); var buffer = headerEncodingBuffer.AsSpan(); @@ -63,9 +64,9 @@ namespace Microsoft.AspNetCore.Testing } } - public static void WriteStartStream(this PipeWriter writer, int streamId, Span headerData, bool endStream) + public static void WriteStartStream(this PipeWriter writer, int streamId, Span headerData, bool endStream, Http2Frame frame = null) { - var frame = new Http2Frame(); + frame ??= new Http2Frame(); frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); frame.PayloadLength = headerData.Length; frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS; @@ -79,10 +80,9 @@ namespace Microsoft.AspNetCore.Testing writer.Write(headerData); } - public static void WriteData(this PipeWriter writer, int streamId, Memory data, bool endStream) + public static void WriteData(this PipeWriter writer, int streamId, Memory data, bool endStream, Http2Frame frame = null) { - var frame = new Http2Frame(); - + frame ??= new Http2Frame(); frame.PrepareData(streamId); frame.PayloadLength = data.Length; frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE; @@ -90,5 +90,14 @@ namespace Microsoft.AspNetCore.Testing Http2FrameWriter.WriteHeader(frame, writer); writer.Write(data.Span); } + + public static void WriteWindowUpdateAsync(this PipeWriter writer, int streamId, int sizeIncrement, Http2Frame frame = null) + { + frame ??= new Http2Frame(); + frame.PrepareWindowUpdate(streamId, sizeIncrement); + Http2FrameWriter.WriteHeader(frame, writer); + BinaryPrimitives.WriteUInt32BigEndian(writer.GetSpan(4), (uint)sizeIncrement); + writer.Advance(4); + } } } diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs index 9277372a79..f80e5ad386 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.IO.Pipelines; using System.Linq; @@ -1051,12 +1052,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests protected Task SendWindowUpdateAsync(int streamId, int sizeIncrement) { var outputWriter = _pair.Application.Output; - var frame = new Http2Frame(); - frame.PrepareWindowUpdate(streamId, sizeIncrement); - Http2FrameWriter.WriteHeader(frame, outputWriter); - var buffer = outputWriter.GetSpan(4); - BinaryPrimitives.WriteUInt32BigEndian(buffer, (uint)sizeIncrement); - outputWriter.Advance(4); + outputWriter.WriteWindowUpdateAsync(streamId, sizeIncrement); return FlushAsync(outputWriter); } diff --git a/src/Shared/BenchmarkRunner/Program.cs b/src/Shared/BenchmarkRunner/Program.cs index a1db1a50e8..87a01cf6c2 100644 --- a/src/Shared/BenchmarkRunner/Program.cs +++ b/src/Shared/BenchmarkRunner/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; From bc6bc2d22c42369627b5941e7c1dd98c4c8ad20a Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 9 Mar 2020 22:14:20 -0700 Subject: [PATCH 53/57] Reenable Components analyzers on helix (#19721) --- .../Microsoft.AspNetCore.Components.Analyzers.Tests.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Components/Analyzers/test/Microsoft.AspNetCore.Components.Analyzers.Tests.csproj b/src/Components/Analyzers/test/Microsoft.AspNetCore.Components.Analyzers.Tests.csproj index 8becc8014b..80b92842f4 100644 --- a/src/Components/Analyzers/test/Microsoft.AspNetCore.Components.Analyzers.Tests.csproj +++ b/src/Components/Analyzers/test/Microsoft.AspNetCore.Components.Analyzers.Tests.csproj @@ -3,9 +3,6 @@ $(DefaultNetCoreTargetFramework) - - - false From 7cf264f25ae77d9a6b235a85c66dc13835e5b398 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2020 09:51:26 +0000 Subject: [PATCH 54/57] Update dependencies from https://github.com/dotnet/efcore build 20200309.5 (#19735) - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20159.5 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20159.5 - dotnet-ef - 5.0.0-preview.3.20159.5 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20159.5 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20159.5 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20159.5 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20159.5 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8406b923ae..f43987e367 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -29,33 +29,33 @@ https://github.com/dotnet/aspnetcore-tooling cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 - + https://github.com/dotnet/efcore - 8425e2df5c8b933fb717526731375095162de368 + 1d40504263179e00abee534e8c764c60eee51aa4 https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index ab943e029e..b514705aad 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,13 +140,13 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 - 5.0.0-preview.3.20159.2 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20159.5 5.0.0-preview.3.20157.1 5.0.0-preview.3.20157.1 From e00a7c4d52107e8090d06d1345920ef3e39e3233 Mon Sep 17 00:00:00 2001 From: Brennan Date: Tue, 10 Mar 2020 07:51:27 -0700 Subject: [PATCH 55/57] Increase logging in TS functional tests (#19582) --- src/SignalR/clients/ts/FunctionalTests/Program.cs | 9 +++++++-- src/SignalR/clients/ts/FunctionalTests/Startup.cs | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/SignalR/clients/ts/FunctionalTests/Program.cs b/src/SignalR/clients/ts/FunctionalTests/Program.cs index a762f99e55..2f67e3d5cd 100644 --- a/src/SignalR/clients/ts/FunctionalTests/Program.cs +++ b/src/SignalR/clients/ts/FunctionalTests/Program.cs @@ -30,9 +30,14 @@ namespace FunctionalTests var hostBuilder = new WebHostBuilder() .ConfigureLogging(factory => { - factory.AddConsole(options => options.IncludeScopes = true); + factory.AddConsole(options => + { + options.IncludeScopes = true; + options.TimestampFormat = "[HH:mm:ss] "; + options.UseUtcTimestamp = true; + }); factory.AddDebug(); - factory.SetMinimumLevel(LogLevel.Information); + factory.SetMinimumLevel(LogLevel.Debug); }) .UseKestrel((builderContext, options) => { diff --git a/src/SignalR/clients/ts/FunctionalTests/Startup.cs b/src/SignalR/clients/ts/FunctionalTests/Startup.cs index 747d99305d..8a3ccfa767 100644 --- a/src/SignalR/clients/ts/FunctionalTests/Startup.cs +++ b/src/SignalR/clients/ts/FunctionalTests/Startup.cs @@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using Microsoft.Net.Http.Headers; @@ -104,7 +105,7 @@ namespace FunctionalTests }); } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) { if (env.IsDevelopment()) { @@ -120,6 +121,7 @@ namespace FunctionalTests var originHeader = context.Request.Headers[HeaderNames.Origin]; if (!StringValues.IsNullOrEmpty(originHeader)) { + logger.LogInformation("Setting CORS headers."); context.Response.Headers[HeaderNames.AccessControlAllowOrigin] = originHeader; context.Response.Headers[HeaderNames.AccessControlAllowCredentials] = "true"; @@ -138,6 +140,7 @@ namespace FunctionalTests if (HttpMethods.IsOptions(context.Request.Method)) { + logger.LogInformation("Setting '204' CORS response."); context.Response.StatusCode = StatusCodes.Status204NoContent; return Task.CompletedTask; } From 1e735afa929e958dd62a7e68db990bbacd675a25 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2020 00:07:16 +0000 Subject: [PATCH 56/57] [master] Update dependencies from dotnet/efcore (#19748) * Update dependencies from https://github.com/dotnet/efcore build 20200310.4 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20160.4 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20160.4 - dotnet-ef - 5.0.0-preview.3.20160.4 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20160.4 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20160.4 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20160.4 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20160.4 * Update dependencies from https://github.com/dotnet/efcore build 20200310.3 - Microsoft.EntityFrameworkCore.Tools - 5.0.0-preview.3.20160.3 - Microsoft.EntityFrameworkCore.SqlServer - 5.0.0-preview.3.20160.3 - dotnet-ef - 5.0.0-preview.3.20160.3 - Microsoft.EntityFrameworkCore - 5.0.0-preview.3.20160.3 - Microsoft.EntityFrameworkCore.InMemory - 5.0.0-preview.3.20160.3 - Microsoft.EntityFrameworkCore.Relational - 5.0.0-preview.3.20160.3 - Microsoft.EntityFrameworkCore.Sqlite - 5.0.0-preview.3.20160.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f43987e367..26f02eb118 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -29,33 +29,33 @@ https://github.com/dotnet/aspnetcore-tooling cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc - + https://github.com/dotnet/efcore - 1d40504263179e00abee534e8c764c60eee51aa4 + 9bad189861bded96caf604294628134de6b250cc https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index b514705aad..aad2efaa68 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,13 +140,13 @@ 5.0.0-preview.3.20156.3 5.0.0-preview.3.20156.3 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 - 5.0.0-preview.3.20159.5 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 + 5.0.0-preview.3.20160.3 5.0.0-preview.3.20157.1 5.0.0-preview.3.20157.1 From 8ad0f1fbe6f089783c7f7051e71c7987edaab2cb Mon Sep 17 00:00:00 2001 From: Julio Avellaneda Date: Wed, 11 Mar 2020 11:31:59 -0500 Subject: [PATCH 57/57] Update gitter url (#19755) Update gitter url. --- src/Components/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/README.md b/src/Components/README.md index f234cefc74..6f7ed330ff 100644 --- a/src/Components/README.md +++ b/src/Components/README.md @@ -8,7 +8,7 @@ Blazor is a component based web UI framework. Blazor apps can run client-side in Blazor uses only the latest web standards. No plugins or transpilation needed. It runs in the browser on a real .NET runtime implemented in [WebAssembly](http://webassembly.org) that executes normal .NET assemblies. -[![Gitter](https://badges.gitter.im/dotnet/blazor.svg)](https://gitter.im/dotnet/blazor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Gitter](https://badges.gitter.im/aspnet/blazor.svg)](https://gitter.im/aspnet/blazor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) You can learn more about Blazor at https://blazor.net.