From a6dce7b33c0217392ec374a3b9cd7b759a434654 Mon Sep 17 00:00:00 2001 From: Artak <34246760+mkArtakMSFT@users.noreply.github.com> Date: Thu, 23 May 2019 10:26:07 -0700 Subject: [PATCH 01/95] Updating minimum required VS version to 16.2 (#10458) * Updating minimum required VS version to 16.2 --- .../Blazor/BlazorExtension/src/source.extension.vsixmanifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Blazor/BlazorExtension/src/source.extension.vsixmanifest b/src/Components/Blazor/BlazorExtension/src/source.extension.vsixmanifest index 85c5c76f40..64d36b9b99 100644 --- a/src/Components/Blazor/BlazorExtension/src/source.extension.vsixmanifest +++ b/src/Components/Blazor/BlazorExtension/src/source.extension.vsixmanifest @@ -12,7 +12,7 @@ Content\WebConfiguration.png - + From 2948c81aea2e5a947dc80fe4a62fa7150a9c6f94 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 23 May 2019 10:39:53 -0700 Subject: [PATCH 02/95] Fix RendererSyncContext.Post() (#10451) * Fix RendererSyncContext.Post() Fixes: #9683 - SignalR connection breaks on large DOM The root cause here is a misbehaving sync context. It's not legal for a Post() implementation to run a callback synchronously. We want that behavior for most of the functionality Blazor calls directly, but Post() should always go async or else various threading primitives are broken. * Fix incorrect tests These tests have the assumption that setting the result of a TCS will execution continuations synchronously. This was a bug in our SyncContext, and these tests needed updating to be more resiliant. * Remove a delegate allocation --- .../RendererSynchronizationContext.cs | 94 ++++++++++++------- .../Components/test/Auth/AuthorizeViewTest.cs | 28 +++++- .../Components/test/ComponentBaseTest.cs | 19 +++- .../Components/test/RendererTest.cs | 41 +++++++- ... => RendererSynchronizationContextTest.cs} | 36 +++++-- .../test/Circuits/RemoteRendererTest.cs | 21 ++++- src/Components/Shared/test/TestRenderer.cs | 7 ++ 7 files changed, 196 insertions(+), 50 deletions(-) rename src/Components/Components/test/Rendering/{RendererSynchronizationContextTests.cs => RendererSynchronizationContextTest.cs} (95%) diff --git a/src/Components/Components/src/Rendering/RendererSynchronizationContext.cs b/src/Components/Components/src/Rendering/RendererSynchronizationContext.cs index 9a235b791f..4a97329fe1 100644 --- a/src/Components/Components/src/Rendering/RendererSynchronizationContext.cs +++ b/src/Components/Components/src/Rendering/RendererSynchronizationContext.cs @@ -39,12 +39,13 @@ namespace Microsoft.AspNetCore.Components.Rendering public Task Invoke(Action action) { - var completion = new TaskCompletionSource(); - Post(_ => + var completion = new RendererSynchronizationTaskCompletionSource(action); + ExecuteSynchronouslyIfPossible((state) => { + var completion = (RendererSynchronizationTaskCompletionSource)state; try { - action(); + completion.Callback(); completion.SetResult(null); } catch (OperationCanceledException) @@ -55,19 +56,20 @@ namespace Microsoft.AspNetCore.Components.Rendering { completion.SetException(exception); } - }, null); + }, completion); return completion.Task; } public Task InvokeAsync(Func asyncAction) { - var completion = new TaskCompletionSource(); - Post(async (_) => + var completion = new RendererSynchronizationTaskCompletionSource, object>(asyncAction); + ExecuteSynchronouslyIfPossible(async (state) => { + var completion = (RendererSynchronizationTaskCompletionSource, object>)state; try { - await asyncAction(); + await completion.Callback(); completion.SetResult(null); } catch (OperationCanceledException) @@ -78,19 +80,20 @@ namespace Microsoft.AspNetCore.Components.Rendering { completion.SetException(exception); } - }, null); + }, completion); return completion.Task; } public Task Invoke(Func function) { - var completion = new TaskCompletionSource(); - Post(_ => + var completion = new RendererSynchronizationTaskCompletionSource, TResult>(function); + ExecuteSynchronouslyIfPossible((state) => { + var completion = (RendererSynchronizationTaskCompletionSource, TResult>)state; try { - var result = function(); + var result = completion.Callback(); completion.SetResult(result); } catch (OperationCanceledException) @@ -101,19 +104,20 @@ namespace Microsoft.AspNetCore.Components.Rendering { completion.SetException(exception); } - }, null); + }, completion); return completion.Task; } public Task InvokeAsync(Func> asyncFunction) { - var completion = new TaskCompletionSource(); - Post(async (_) => + var completion = new RendererSynchronizationTaskCompletionSource>, TResult>(asyncFunction); + ExecuteSynchronouslyIfPossible(async (state) => { + var completion = (RendererSynchronizationTaskCompletionSource>, TResult>)state; try { - var result = await asyncFunction(); + var result = await completion.Callback(); completion.SetResult(result); } catch (OperationCanceledException) @@ -124,30 +128,20 @@ namespace Microsoft.AspNetCore.Components.Rendering { completion.SetException(exception); } - }, null); + }, completion); return completion.Task; } // asynchronously runs the callback + // + // NOTE: this must always run async. It's not legal here to execute the work item synchronously. public override void Post(SendOrPostCallback d, object state) { - TaskCompletionSource completion; lock (_state.Lock) { - if (!_state.Task.IsCompleted) - { - _state.Task = Enqueue(_state.Task, d, state); - return; - } - - // We can execute this synchronously because nothing is currently running - // or queued. - completion = new TaskCompletionSource(); - _state.Task = completion.Task; + _state.Task = Enqueue(_state.Task, d, state, forceAsync: true); } - - ExecuteSynchronously(completion, d, state); } // synchronously runs the callback @@ -177,10 +171,33 @@ namespace Microsoft.AspNetCore.Components.Rendering return new RendererSynchronizationContext(_state); } - private Task Enqueue(Task antecedant, SendOrPostCallback d, object state) + // Similar to Post, but it can runs the work item synchronously if the context is not busy. + // + // This is the main code path used by components, we want to be able to run async work but only dispatch + // if necessary. + private void ExecuteSynchronouslyIfPossible(SendOrPostCallback d, object state) { - // If we get here is means that a callback is being queued while something is currently executing - // in this context. Let's instead add it to the queue and yield. + TaskCompletionSource completion; + lock (_state.Lock) + { + if (!_state.Task.IsCompleted) + { + _state.Task = Enqueue(_state.Task, d, state); + return; + } + + // We can execute this synchronously because nothing is currently running + // or queued. + completion = new TaskCompletionSource(); + _state.Task = completion.Task; + } + + ExecuteSynchronously(completion, d, state); + } + + private Task Enqueue(Task antecedant, SendOrPostCallback d, object state, bool forceAsync = false) + { + // If we get here is means that a callback is being explicitly queued. Let's instead add it to the queue and yield. // // We use our own queue here to maintain the execution order of the callbacks scheduled here. Also // we need a queue rather than just scheduling an item in the thread pool - those items would immediately @@ -194,13 +211,14 @@ namespace Microsoft.AspNetCore.Components.Rendering executionContext = ExecutionContext.Capture(); } + var flags = forceAsync ? TaskContinuationOptions.RunContinuationsAsynchronously : TaskContinuationOptions.None; return antecedant.ContinueWith(BackgroundWorkThunk, new WorkItem() { SynchronizationContext = this, ExecutionContext = executionContext, Callback = d, State = state, - }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current); + }, CancellationToken.None, flags, TaskScheduler.Current); } private void ExecuteSynchronously( @@ -280,5 +298,15 @@ namespace Microsoft.AspNetCore.Components.Rendering public SendOrPostCallback Callback; public object State; } + + private class RendererSynchronizationTaskCompletionSource : TaskCompletionSource + { + public RendererSynchronizationTaskCompletionSource(TCallback callback) + { + Callback = callback; + } + + public TCallback Callback { get; } + } } } diff --git a/src/Components/Components/test/Auth/AuthorizeViewTest.cs b/src/Components/Components/test/Auth/AuthorizeViewTest.cs index 3039cca28e..5617c3b32d 100644 --- a/src/Components/Components/test/Auth/AuthorizeViewTest.cs +++ b/src/Components/Components/test/Auth/AuthorizeViewTest.cs @@ -2,9 +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.Linq; using System.Security.Claims; using System.Security.Principal; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Test.Helpers; @@ -14,6 +16,10 @@ namespace Microsoft.AspNetCore.Components { public class AuthorizeViewTest { + // Nothing should exceed the timeout in a successful run of the the tests, this is just here to catch + // failures. + private static readonly TimeSpan Timeout = Debugger.IsAttached ? System.Threading.Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10); + [Fact] public void RendersNothingIfNotAuthorized() { @@ -180,7 +186,11 @@ namespace Microsoft.AspNetCore.Components public void RendersNothingUntilAuthorizationCompleted() { // Arrange - var renderer = new TestRenderer(); + var @event = new ManualResetEventSlim(); + var renderer = new TestRenderer() + { + OnUpdateDisplayComplete = () => { @event.Set(); }, + }; var rootComponent = WrapInAuthorizeView( notAuthorizedContent: builder => builder.AddContent(0, "You are not authorized")); var authTcs = new TaskCompletionSource(); @@ -195,7 +205,12 @@ namespace Microsoft.AspNetCore.Components Assert.Empty(diff1.Edits); // Act/Assert 2: Auth process completes asynchronously + @event.Reset(); authTcs.SetResult(new AuthenticationState(new ClaimsPrincipal())); + + // We need to wait here because the continuations of SetResult will be scheduled to run asynchronously. + @event.Wait(Timeout); + Assert.Equal(2, renderer.Batches.Count); var batch2 = renderer.Batches[1]; var diff2 = batch2.DiffsByComponentId[authorizeViewComponentId].Single(); @@ -212,7 +227,11 @@ namespace Microsoft.AspNetCore.Components public void RendersAuthorizingContentUntilAuthorizationCompleted() { // Arrange - var renderer = new TestRenderer(); + var @event = new ManualResetEventSlim(); + var renderer = new TestRenderer() + { + OnUpdateDisplayComplete = () => { @event.Set(); }, + }; var rootComponent = WrapInAuthorizeView( authorizingContent: builder => builder.AddContent(0, "Auth pending..."), authorizedContent: context => builder => builder.AddContent(0, $"Hello, {context.User.Identity.Name}!")); @@ -234,7 +253,12 @@ namespace Microsoft.AspNetCore.Components }); // Act/Assert 2: Auth process completes asynchronously + @event.Reset(); authTcs.SetResult(CreateAuthenticationState("Monsieur").Result); + + // We need to wait here because the continuations of SetResult will be scheduled to run asynchronously. + @event.Wait(Timeout); + Assert.Equal(2, renderer.Batches.Count); var batch2 = renderer.Batches[1]; var diff2 = batch2.DiffsByComponentId[authorizeViewComponentId].Single(); diff --git a/src/Components/Components/test/ComponentBaseTest.cs b/src/Components/Components/test/ComponentBaseTest.cs index 15c2ae98c2..ba9c17f46b 100644 --- a/src/Components/Components/test/ComponentBaseTest.cs +++ b/src/Components/Components/test/ComponentBaseTest.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 System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.RenderTree; @@ -12,6 +13,10 @@ namespace Microsoft.AspNetCore.Components.Test { public class ComponentBaseTest { + // Nothing should exceed the timeout in a successful run of the the tests, this is just here to catch + // failures. + private static readonly TimeSpan Timeout = Debugger.IsAttached ? System.Threading.Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10); + [Fact] public void RunsOnInitWhenRendered() { @@ -178,7 +183,12 @@ namespace Microsoft.AspNetCore.Components.Test public async Task RendersAfterParametersSetAndInitAsyncTasksAreCompleted() { // Arrange - var renderer = new TestRenderer(); + var @event = new ManualResetEventSlim(); + + var renderer = new TestRenderer() + { + OnUpdateDisplayComplete = () => { @event.Set(); }, + }; var component = new TestComponent(); component.Counter = 1; @@ -197,10 +207,16 @@ namespace Microsoft.AspNetCore.Components.Test // A rendering should have happened after the synchronous execution of Init Assert.Single(renderer.Batches); + @event.Reset(); + // Completes task started by OnInitAsync component.Counter = 2; initTask.SetResult(true); + // We need to wait here, because the continuation from SetResult needs to be scheduled. + @event.Wait(Timeout); + @event.Reset(); + // Component should be rendered once, after set parameters Assert.Equal(2, renderer.Batches.Count); @@ -209,6 +225,7 @@ namespace Microsoft.AspNetCore.Components.Test parametersSetTask.SetResult(false); await renderTask; + Assert.True(@event.IsSet); // Component should be rendered again // after the async part of onparameterssetasync completes diff --git a/src/Components/Components/test/RendererTest.cs b/src/Components/Components/test/RendererTest.cs index be4994a83b..8c8d9b1d49 100644 --- a/src/Components/Components/test/RendererTest.cs +++ b/src/Components/Components/test/RendererTest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.ExceptionServices; using System.Threading; @@ -19,6 +20,10 @@ namespace Microsoft.AspNetCore.Components.Test { public class RendererTest { + // Nothing should exceed the timeout in a successful run of the the tests, this is just here to catch + // failures. + private static readonly TimeSpan Timeout = Debugger.IsAttached ? System.Threading.Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10); + private const string EventActionsName = nameof(NestedAsyncComponent.EventActions); private const string WhatToRenderName = nameof(NestedAsyncComponent.WhatToRender); private const string LogName = nameof(NestedAsyncComponent.Log); @@ -2458,13 +2463,17 @@ namespace Microsoft.AspNetCore.Components.Test public void CallsAfterRenderAfterTheUIHasFinishedUpdatingAsynchronously() { // Arrange + var @event = new ManualResetEventSlim(); var tcs = new TaskCompletionSource(); var afterRenderTcs = new TaskCompletionSource(); var onAfterRenderCallCountLog = new List(); - var component = new AsyncAfterRenderComponent(afterRenderTcs.Task); + var component = new AsyncAfterRenderComponent(afterRenderTcs.Task) + { + OnAfterRenderComplete = () => @event.Set(), + }; var renderer = new AsyncUpdateTestRenderer() { - OnUpdateDisplayAsync = _ => tcs.Task + OnUpdateDisplayAsync = _ => tcs.Task, }; renderer.AssignRootComponentId(component); @@ -2473,6 +2482,9 @@ namespace Microsoft.AspNetCore.Components.Test tcs.SetResult(null); afterRenderTcs.SetResult(null); + // We need to wait here because the completions from SetResult will be scheduled. + @event.Wait(Timeout); + // Assert Assert.True(component.Called); } @@ -2481,9 +2493,13 @@ namespace Microsoft.AspNetCore.Components.Test public void CallsAfterRenderAfterTheUIHasFinishedUpdatingSynchronously() { // Arrange + var @event = new ManualResetEventSlim(); var afterRenderTcs = new TaskCompletionSource(); var onAfterRenderCallCountLog = new List(); - var component = new AsyncAfterRenderComponent(afterRenderTcs.Task); + var component = new AsyncAfterRenderComponent(afterRenderTcs.Task) + { + OnAfterRenderComplete = () => @event.Set(), + }; var renderer = new AsyncUpdateTestRenderer() { OnUpdateDisplayAsync = _ => Task.CompletedTask @@ -2494,6 +2510,9 @@ namespace Microsoft.AspNetCore.Components.Test component.TriggerRender(); afterRenderTcs.SetResult(null); + // We need to wait here because the completions from SetResult will be scheduled. + @event.Wait(Timeout); + // Assert Assert.True(component.Called); } @@ -2798,7 +2817,12 @@ namespace Microsoft.AspNetCore.Components.Test // code paths are special cased for the first render because of prerendering. // Arrange - var renderer = new TestRenderer { ShouldHandleExceptions = true }; + var @event = new ManualResetEventSlim(); + var renderer = new TestRenderer() + { + ShouldHandleExceptions = true, + OnExceptionHandled = () => { @event.Set(); }, + }; var taskToAwait = Task.CompletedTask; var component = new TestComponent(builder => { @@ -2815,8 +2839,13 @@ namespace Microsoft.AspNetCore.Components.Test // Act var exception = new InvalidOperationException(); + + @event.Reset(); asyncExceptionTcs.SetException(exception); + // We need to wait here because the continuations of SetException will be scheduled to run asynchronously. + @event.Wait(Timeout); + // Assert Assert.Same(exception, Assert.Single(renderer.HandledExceptions).GetBaseException()); } @@ -3829,10 +3858,14 @@ namespace Microsoft.AspNetCore.Components.Test public bool Called { get; private set; } + public Action OnAfterRenderComplete { get; set; } + public async Task OnAfterRenderAsync() { await _task; Called = true; + + OnAfterRenderComplete?.Invoke(); } protected override void BuildRenderTree(RenderTreeBuilder builder) diff --git a/src/Components/Components/test/Rendering/RendererSynchronizationContextTests.cs b/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs similarity index 95% rename from src/Components/Components/test/Rendering/RendererSynchronizationContextTests.cs rename to src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs index 153f362b0c..5be5050983 100644 --- a/src/Components/Components/test/Rendering/RendererSynchronizationContextTests.cs +++ b/src/Components/Components/test/Rendering/RendererSynchronizationContextTest.cs @@ -1,8 +1,9 @@ +// 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.Globalization; -using System.Text; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -16,34 +17,51 @@ namespace Microsoft.AspNetCore.Components.Rendering public TimeSpan Timeout = Debugger.IsAttached ? System.Threading.Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10); [Fact] - public void Post_CanRunSynchronously_WhenNotBusy() + public void Post_RunsAsynchronously_WhenNotBusy() { // Arrange var context = new RendererSynchronizationContext(); var thread = Thread.CurrentThread; Thread capturedThread = null; + var e = new ManualResetEventSlim(); + // Act context.Post((_) => { capturedThread = Thread.CurrentThread; + + e.Set(); }, null); // Assert - Assert.Same(thread, capturedThread); + Assert.True(e.Wait(Timeout), "timeout"); + Assert.NotSame(thread, capturedThread); } [Fact] - public void Post_CanRunSynchronously_WhenNotBusy_Exception() + public void Post_RunsAynchronously_WhenNotBusy_Exception() { // Arrange var context = new RendererSynchronizationContext(); - // Act & Assert - Assert.Throws(() => context.Post((_) => + Exception exception = null; + context.UnhandledException += (sender, e) => + { + exception = (InvalidTimeZoneException)e.ExceptionObject; + }; + + // Act + context.Post((_) => { throw new InvalidTimeZoneException(); - }, null)); + }, null); + + // Assert + // + // Use another item to 'push through' the throwing one + context.Send((_) => { }, null); + Assert.NotNull(exception); } [Fact] @@ -573,7 +591,7 @@ namespace Microsoft.AspNetCore.Components.Rendering Thread capturedThread = null; // Act - var task = context.Invoke(() => + var task = context.InvokeAsync(() => { capturedThread = Thread.CurrentThread; return Task.CompletedTask; diff --git a/src/Components/Server/test/Circuits/RemoteRendererTest.cs b/src/Components/Server/test/Circuits/RemoteRendererTest.cs index 428e173a26..630fd75c7e 100644 --- a/src/Components/Server/test/Circuits/RemoteRendererTest.cs +++ b/src/Components/Server/test/Circuits/RemoteRendererTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text.Encodings.Web; using System.Threading; using System.Threading.Tasks; @@ -19,6 +20,10 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering { public class RemoteRendererTest : HtmlRendererTestBase { + // Nothing should exceed the timeout in a successful run of the the tests, this is just here to catch + // failures. + private static readonly TimeSpan Timeout = Debugger.IsAttached ? System.Threading.Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(10); + protected override HtmlRenderer GetHtmlRenderer(IServiceProvider serviceProvider) { return GetRemoteRenderer(serviceProvider, new CircuitClientProxy()); @@ -50,6 +55,7 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering public async Task ProcessBufferedRenderBatches_WritesRenders() { // Arrange + var @event = new ManualResetEventSlim(); var serviceProvider = new ServiceCollection().BuildServiceProvider(); var renderIds = new List(); @@ -78,8 +84,13 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering var componentId = renderer.AssignRootComponentId(component); component.TriggerRender(); renderer.OnRenderCompleted(2, null); + + @event.Reset(); firstBatchTCS.SetResult(null); + // Waiting is required here because the continuations of SetResult will not execute synchronously. + @event.Wait(Timeout); + circuitClient.SetDisconnected(); component.TriggerRender(); component.TriggerRender(); @@ -261,7 +272,7 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering NullLogger.Instance); } - private class TestComponent : IComponent + private class TestComponent : IComponent, IHandleAfterRender { private RenderHandle _renderHandle; private RenderFragment _renderFragment = (builder) => @@ -280,11 +291,19 @@ namespace Microsoft.AspNetCore.Components.Browser.Rendering _renderFragment = renderFragment; } + public Action OnAfterRenderComplete { get; set; } + public void Configure(RenderHandle renderHandle) { _renderHandle = renderHandle; } + public Task OnAfterRenderAsync() + { + OnAfterRenderComplete?.Invoke(); + return Task.CompletedTask; + } + public Task SetParametersAsync(ParameterCollection parameters) { TriggerRender(); diff --git a/src/Components/Shared/test/TestRenderer.cs b/src/Components/Shared/test/TestRenderer.cs index 8b0cecd4f9..f2a99ccdd4 100644 --- a/src/Components/Shared/test/TestRenderer.cs +++ b/src/Components/Shared/test/TestRenderer.cs @@ -25,8 +25,12 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers { } + public Action OnExceptionHandled { get; set; } + public Action OnUpdateDisplay { get; set; } + public Action OnUpdateDisplayComplete { get; set; } + public List Batches { get; } = new List(); @@ -81,6 +85,7 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers } HandledExceptions.Add(exception); + OnExceptionHandled?.Invoke(); } protected override Task UpdateDisplayAsync(in RenderBatch renderBatch) @@ -102,6 +107,8 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers // This renderer updates the UI synchronously, like the WebAssembly one. // To test async UI updates, subclass TestRenderer and override UpdateDisplayAsync. + + OnUpdateDisplayComplete?.Invoke(); return Task.CompletedTask; } } From c05244cda928c94c25dc17bc9f8c6211106ded9a Mon Sep 17 00:00:00 2001 From: BrennanConroy Date: Thu, 23 May 2019 14:46:32 -0700 Subject: [PATCH 03/95] More docker resiliency for tests (#10425) --- src/SignalR/server/StackExchangeRedis/test/Docker.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SignalR/server/StackExchangeRedis/test/Docker.cs b/src/SignalR/server/StackExchangeRedis/test/Docker.cs index c5f613461e..cdcbe9bfcc 100644 --- a/src/SignalR/server/StackExchangeRedis/test/Docker.cs +++ b/src/SignalR/server/StackExchangeRedis/test/Docker.cs @@ -81,6 +81,9 @@ namespace Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests { logger.LogError(ex, "Error starting redis docker container, retrying."); Thread.Sleep(1000); + + // Call stop just in case the container somehow started after the timeout so our retry logic doesn't fail + RunProcessAndWait(_path, $"stop {_dockerContainerName}", "docker stop", logger, TimeSpan.FromSeconds(15), out var _); Run(); } @@ -90,7 +93,7 @@ namespace Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests // use static name 'redisTestContainer' so if the container doesn't get removed we don't keep adding more // use redis base docker image // 30 second timeout to allow redis image to be downloaded, should be a rare occurrence, only happening when a new version is released - RunProcessAndThrowIfFailed(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d redis", "redis", logger, TimeSpan.FromSeconds(30)); + RunProcessAndThrowIfFailed(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d redis", "redis", logger, TimeSpan.FromMinutes(1)); } } From e01d713909e4905643b33c4f7fb261bb90e80b3d Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 23 May 2019 15:35:49 -0700 Subject: [PATCH 04/95] Use Microsoft.Net.Compilers.Toolset (#10415) --- Directory.Build.props | 10 +- build/sources.props | 1 + eng/Version.Details.xml | 12 +- eng/Versions.props | 6 +- eng/common/PublishToPackageFeed.proj | 1 + eng/common/build.ps1 | 4 + eng/common/build.sh | 5 + eng/common/cross/arm64/sources.list.buster | 11 ++ eng/common/cross/arm64/sources.list.stretch | 12 ++ eng/common/cross/build-rootfs.sh | 17 ++- eng/common/darc-init.ps1 | 11 +- eng/common/darc-init.sh | 15 ++- eng/common/dotnet-install.ps1 | 11 +- eng/common/templates/steps/send-to-helix.yml | 3 + eng/common/tools.ps1 | 6 +- eng/common/tools.sh | 115 +++++++++++++++++-- global.json | 4 +- 17 files changed, 212 insertions(+), 32 deletions(-) create mode 100644 eng/common/cross/arm64/sources.list.buster create mode 100644 eng/common/cross/arm64/sources.list.stretch diff --git a/Directory.Build.props b/Directory.Build.props index 8b002c635e..8a8ef6d27d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,15 @@  + + + + + Microsoft ASP.NET Core @@ -121,7 +130,6 @@ $(MSBuildProjectName)-ref - diff --git a/build/sources.props b/build/sources.props index 26d8be6677..b561688d1f 100644 --- a/build/sources.props +++ b/build/sources.props @@ -14,6 +14,7 @@ https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json; https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json; https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; + https://dotnet.myget.org/F/roslyn/api/v3/index.json; https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev; https://api.nuget.org/v3/index.json; diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index fc32cbe01a..11b8454994 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -384,13 +384,17 @@ https://github.com/aspnet/Extensions 8dfb4ece7ca9a6dea14264dafc38a0c953874559 - + https://github.com/dotnet/arcade - e913fb3b02d4089a91ff91c041c5f6e7c29038b0 + 86e674361bdcefecbd8199ab62d0b1a6cb25703d - + https://github.com/dotnet/arcade - e913fb3b02d4089a91ff91c041c5f6e7c29038b0 + 86e674361bdcefecbd8199ab62d0b1a6cb25703d + + + https://github.com/dotnet/arcade + 86e674361bdcefecbd8199ab62d0b1a6cb25703d https://github.com/aspnet/Extensions diff --git a/eng/Versions.props b/eng/Versions.props index 10a317d441..108f3ea38a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -5,6 +5,10 @@ --> + + + true + - 1.0.0-beta.19270.1 + 1.0.0-beta.19272.13 3.0.0-preview6-27714-15 3.0.0-preview6-27714-15 diff --git a/eng/common/PublishToPackageFeed.proj b/eng/common/PublishToPackageFeed.proj index e17f72644e..9120b2d212 100644 --- a/eng/common/PublishToPackageFeed.proj +++ b/eng/common/PublishToPackageFeed.proj @@ -53,6 +53,7 @@ https://dotnetfeed.blob.core.windows.net/dotnet-toolset/index.json https://dotnetfeed.blob.core.windows.net/dotnet-windowsdesktop/index.json https://dotnetfeed.blob.core.windows.net/nuget-nugetclient/index.json + https://dotnetfeed.blob.core.windows.net/aspnet-entityframework6/index.json Build configuration: 'Debug' or 'Release' (short: -c)" + Write-Host " -platform Platform configuration: 'x86', 'x64' or any valid Platform value to pass to msbuild" Write-Host " -verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic] (short: -v)" Write-Host " -binaryLog Output binary log (short: -bl)" Write-Host " -help Print help and exit" @@ -77,6 +79,7 @@ function Build { InitializeCustomToolset $bl = if ($binaryLog) { "/bl:" + (Join-Path $LogDir "Build.binlog") } else { "" } + $platformArg = if ($platform) { "/p:Platform=$platform" } else { "" } if ($projects) { # Re-assign properties to a new variable because PowerShell doesn't let us append properties directly for unclear reasons. @@ -88,6 +91,7 @@ function Build { MSBuild $toolsetBuildProj ` $bl ` + $platformArg ` /p:Configuration=$configuration ` /p:RepoRoot=$RepoRoot ` /p:Restore=$restore ` diff --git a/eng/common/build.sh b/eng/common/build.sh index ce846d888d..6236fc4d38 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -66,6 +66,7 @@ ci=false warn_as_error=true node_reuse=true binary_log=false +pipelines_log=false projects='' configuration='Debug' @@ -92,6 +93,9 @@ while [[ $# > 0 ]]; do -binarylog|-bl) binary_log=true ;; + -pipelineslog|-pl) + pipelines_log=true + ;; -restore|-r) restore=true ;; @@ -146,6 +150,7 @@ while [[ $# > 0 ]]; do done if [[ "$ci" == true ]]; then + pipelines_log=true binary_log=true node_reuse=false fi diff --git a/eng/common/cross/arm64/sources.list.buster b/eng/common/cross/arm64/sources.list.buster new file mode 100644 index 0000000000..7194ac64a9 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.buster @@ -0,0 +1,11 @@ +deb http://deb.debian.org/debian buster main +deb-src http://deb.debian.org/debian buster main + +deb http://deb.debian.org/debian-security/ buster/updates main +deb-src http://deb.debian.org/debian-security/ buster/updates main + +deb http://deb.debian.org/debian buster-updates main +deb-src http://deb.debian.org/debian buster-updates main + +deb http://deb.debian.org/debian buster-backports main contrib non-free +deb-src http://deb.debian.org/debian buster-backports main contrib non-free diff --git a/eng/common/cross/arm64/sources.list.stretch b/eng/common/cross/arm64/sources.list.stretch new file mode 100644 index 0000000000..0e12157743 --- /dev/null +++ b/eng/common/cross/arm64/sources.list.stretch @@ -0,0 +1,12 @@ +deb http://deb.debian.org/debian stretch main +deb-src http://deb.debian.org/debian stretch main + +deb http://deb.debian.org/debian-security/ stretch/updates main +deb-src http://deb.debian.org/debian-security/ stretch/updates main + +deb http://deb.debian.org/debian stretch-updates main +deb-src http://deb.debian.org/debian stretch-updates main + +deb http://deb.debian.org/debian stretch-backports main contrib non-free +deb-src http://deb.debian.org/debian stretch-backports main contrib non-free + diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 83ec39195c..34d3d2ba1f 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -2,7 +2,7 @@ usage() { - echo "Usage: $0 [BuildArch] [LinuxCodeName] [lldbx.y] [--skipunmount] --rootfs ]" + echo "Usage: $0 [BuildArch] [LinuxCodeName] [lldbx.y] [--skipunmount] --rootfsdir ]" echo "BuildArch can be: arm(default), armel, arm64, x86" echo "LinuxCodeName - optional, Code name for Linux, can be: trusty, xenial(default), zesty, bionic, alpine. If BuildArch is armel, LinuxCodeName is jessie(default) or tizen." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine" @@ -113,12 +113,12 @@ while :; do __LinuxCodeName=trusty fi ;; - xenial) # Ubunry 16.04 + xenial) # Ubuntu 16.04 if [ "$__LinuxCodeName" != "jessie" ]; then __LinuxCodeName=xenial fi ;; - zesty) # Ununtu 17.04 + zesty) # Ubuntu 17.04 if [ "$__LinuxCodeName" != "jessie" ]; then __LinuxCodeName=zesty fi @@ -132,7 +132,16 @@ while :; do __LinuxCodeName=jessie __UbuntuRepo="http://ftp.debian.org/debian/" ;; - # TBD Stretch -> Debian 9, Buster -> Debian 10 + stretch) # Debian 9 + __LinuxCodeName=stretch + __UbuntuRepo="http://ftp.debian.org/debian/" + __LLDB_Package="liblldb-6.0-dev" + ;; + buster) # Debian 10 + __LinuxCodeName=buster + __UbuntuRepo="http://ftp.debian.org/debian/" + __LLDB_Package="liblldb-6.0-dev" + ;; tizen) if [ "$__BuildArch" != "armel" ]; then echo "Tizen is available only for armel." diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 index 81ffd16779..dea7cdd903 100644 --- a/eng/common/darc-init.ps1 +++ b/eng/common/darc-init.ps1 @@ -1,5 +1,6 @@ param ( - $darcVersion = $null + $darcVersion = $null, + $versionEndpoint = "https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16" ) $verbosity = "m" @@ -16,13 +17,13 @@ function InstallDarcCli ($darcVersion) { Invoke-Expression "& `"$dotnet`" tool uninstall $darcCliPackageName -g" } - # Until we can anonymously query the BAR API for the latest arcade-services - # build applied to the PROD channel, this is hardcoded. + # If the user didn't explicitly specify the darc version, + # query the Maestro API for the correct version of darc to install. if (-not $darcVersion) { - $darcVersion = '1.1.0-beta.19205.4' + $darcVersion = $(Invoke-WebRequest -Uri $versionEndpoint -UseBasicParsing).Content } - $arcadeServicesSource = 'https://dotnetfeed.blob.core.windows.net/dotnet-arcade/index.json' + $arcadeServicesSource = 'https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json' Write-Host "Installing Darc CLI version $darcVersion..." Write-Host "You may need to restart your command window if this is the first dotnet tool you have installed." diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index bd7eb46398..abdd0bc05a 100755 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash source="${BASH_SOURCE[0]}" -darcVersion="1.1.0-beta.19205.4" +darcVersion='' +versionEndpoint="https://maestro-prod.westus2.cloudapp.azure.com/api/assets/darc-version?api-version=2019-01-16" while [[ $# > 0 ]]; do opt="$(echo "$1" | awk '{print tolower($0)}')" @@ -10,6 +11,10 @@ while [[ $# > 0 ]]; do darcVersion=$2 shift ;; + --versionendpoint) + versionEndpoint=$2 + shift + ;; *) echo "Invalid argument: $1" usage @@ -33,6 +38,10 @@ verbosity=m . "$scriptroot/tools.sh" +if [ -z "$darcVersion" ]; then + darcVersion=$(curl -X GET "$versionEndpoint" -H "accept: text/plain") +fi + function InstallDarcCli { local darc_cli_package_name="microsoft.dotnet.darc" @@ -45,9 +54,9 @@ function InstallDarcCli { echo $($dotnet_root/dotnet tool uninstall $darc_cli_package_name -g) fi - local arcadeServicesSource="https://dotnetfeed.blob.core.windows.net/dotnet-arcade/index.json" + local arcadeServicesSource="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" - echo "Installing Darc CLI version $toolset_version..." + echo "Installing Darc CLI version $darcVersion..." echo "You may need to restart your command shell if this is the first dotnet tool you have installed." echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g) } diff --git a/eng/common/dotnet-install.ps1 b/eng/common/dotnet-install.ps1 index 5987943fd6..0b629b8301 100644 --- a/eng/common/dotnet-install.ps1 +++ b/eng/common/dotnet-install.ps1 @@ -8,9 +8,14 @@ Param( . $PSScriptRoot\tools.ps1 +$dotnetRoot = Join-Path $RepoRoot ".dotnet" + +$installdir = $dotnetRoot try { - $dotnetRoot = Join-Path $RepoRoot ".dotnet" - InstallDotNet $dotnetRoot $version $architecture $runtime $true + if ($architecture -and $architecture.Trim() -eq "x86") { + $installdir = Join-Path $installdir "x86" + } + InstallDotNet $installdir $version $architecture $runtime $true } catch { Write-Host $_ @@ -19,4 +24,4 @@ catch { ExitWithExitCode 1 } -ExitWithExitCode 0 \ No newline at end of file +ExitWithExitCode 0 diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index d1ce577db5..05df886f55 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -5,6 +5,7 @@ parameters: HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixConfiguration: '' # optional -- additional property attached to a job HelixPreCommands: '' # optional -- commands to run before Helix work item execution HelixPostCommands: '' # optional -- commands to run after Helix work item execution WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects @@ -35,6 +36,7 @@ steps: HelixSource: ${{ parameters.HelixSource }} HelixType: ${{ parameters.HelixType }} HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} HelixTargetQueues: ${{ parameters.HelixTargetQueues }} HelixAccessToken: ${{ parameters.HelixAccessToken }} HelixPreCommands: ${{ parameters.HelixPreCommands }} @@ -64,6 +66,7 @@ steps: HelixSource: ${{ parameters.HelixSource }} HelixType: ${{ parameters.HelixType }} HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} HelixTargetQueues: ${{ parameters.HelixTargetQueues }} HelixAccessToken: ${{ parameters.HelixAccessToken }} HelixPreCommands: ${{ parameters.HelixPreCommands }} diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 9ca177b82a..9cea610a27 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -213,7 +213,11 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = if ($env:VSINSTALLDIR -ne $null) { $msbuildCmd = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue if ($msbuildCmd -ne $null) { - if ($msbuildCmd.Version -ge $vsMinVersion) { + # Workaround for https://github.com/dotnet/roslyn/issues/35793 + # Due to this issue $msbuildCmd.Version returns 0.0.0.0 for msbuild.exe 16.2+ + $msbuildVersion = [Version]::new((Get-Item $msbuildCmd.Path).VersionInfo.ProductVersion.Split(@('-', '+'))[0]) + + if ($msbuildVersion -ge $vsMinVersion) { return $global:_MSBuildExe = $msbuildCmd.Path } diff --git a/eng/common/tools.sh b/eng/common/tools.sh index df3eb8bce0..34a23e9476 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -1,8 +1,20 @@ +#!/usr/bin/env bash + # Initialize variables if they aren't already defined. # CI mode - set to true on CI server for PR validation build or official build. ci=${ci:-false} +# Set to true to use the pipelines logger which will enable Azure logging output. +# https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/authoring/commands.md +# This flag is meant as a temporary opt-opt for the feature while validate it across +# our consumers. It will be deleted in the future. +if [[ "$ci" == true ]]; then + pipelines_log=${pipelines_log:-true} +else + pipelines_log=${pipelines_log:-false} +fi + # Build configuration. Common values include 'Debug' and 'Release', but the repository may use other names. configuration=${configuration:-'Debug'} @@ -40,6 +52,78 @@ else use_global_nuget_cache=${use_global_nuget_cache:-true} fi +function EmitError { + if [[ "$ci" != true ]]; then + echo "$@" >&2 + return + fi + + message_type="error" + sourcepath='' + linenumber='' + columnnumber='' + error_code='' + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + case "$opt" in + -type|-t) + message_type=$2 + shift + ;; + -sourcepath|-s) + sourcepath=$2 + shift + ;; + -linenumber|-l) + linenumber=$2 + shift + ;; + -columnnumber|-col) + columnnumber=$2 + shift + ;; + -code|-c) + error_code=$2 + shift + ;; + *) + break + ;; + esac + + shift + done + + message='##vso[task.logissue' + + message="$message type=$message_type" + + if [ -n "$sourcepath" ]; then + message="$message;sourcepath=$sourcepath" + else + message="$message;sourcepath=${BASH_SOURCE[1]}" + fi + + if [ -n "$linenumber" ]; then + message="$message;linenumber=$linenumber" + else + message="$message;linenumber=${BASH_LINENO[0]}" + fi + + if [ -n "$columnnumber" ]; then + message="$message;columnnumber=$columnnumber" + fi + + if [ -n "$error_code" ]; then + message="$message;code=$error_code" + fi + + message="$message]$*" + + echo "$message" +} + # Resolve any symlinks in the given path. function ResolvePath { local path=$1 @@ -65,7 +149,7 @@ function ReadGlobalVersion { local pattern="\"$key\" *: *\"(.*)\"" if [[ ! $line =~ $pattern ]]; then - echo "Error: Cannot find \"$key\" in $global_json_file" >&2 + EmitError "Error: Cannot find \"$key\" in $global_json_file" ExitWithExitCode 1 fi @@ -126,7 +210,7 @@ function InitializeDotNetCli { if [[ "$install" == true ]]; then InstallDotNetSdk "$dotnet_root" "$dotnet_sdk_version" else - echo "Unable to find dotnet with SDK version '$dotnet_sdk_version'" >&2 + EmitError "Unable to find dotnet with SDK version '$dotnet_sdk_version'" ExitWithExitCode 1 fi fi @@ -179,7 +263,7 @@ function InstallDotNet { fi bash "$install_script" --version $version --install-dir "$root" $archArg $runtimeArg $skipNonVersionedFilesArg || { local exit_code=$? - echo "Failed to install dotnet SDK (exit code '$exit_code')." >&2 + EmitError "Failed to install dotnet SDK (exit code '$exit_code')." ExitWithExitCode $exit_code } } @@ -216,6 +300,7 @@ function InitializeBuildTool { # return values _InitializeBuildTool="$_InitializeDotNetCli/dotnet" _InitializeBuildToolCommand="msbuild" + _InitializeBuildToolFramework="netcoreapp2.1" } function GetNuGetPackageCachePath { @@ -264,7 +349,7 @@ function InitializeToolset { fi if [[ "$restore" != true ]]; then - echo "Toolset version $toolsetVersion has not been restored." >&2 + EmitError "Toolset version $toolsetVersion has not been restored." ExitWithExitCode 2 fi @@ -276,12 +361,12 @@ function InitializeToolset { fi echo '' > "$proj" - MSBuild "$proj" $bl /t:__WriteToolsetLocation /clp:ErrorsOnly\;NoSummary /p:__ToolsetLocationOutputFile="$toolset_location_file" + MSBuild-Core "$proj" $bl /t:__WriteToolsetLocation /clp:ErrorsOnly\;NoSummary /p:__ToolsetLocationOutputFile="$toolset_location_file" local toolset_build_proj=`cat "$toolset_location_file"` if [[ ! -a "$toolset_build_proj" ]]; then - echo "Invalid toolset path: $toolset_build_proj" >&2 + EmitError "Invalid toolset path: $toolset_build_proj" ExitWithExitCode 3 fi @@ -304,14 +389,26 @@ function StopProcesses { } function MSBuild { + args=$@ + if [[ "$pipelines_log" == true ]]; then + InitializeBuildTool + InitializeToolset + _toolset_dir="${_InitializeToolset%/*}" + _loggerPath="$_toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" + args=( "${args[@]}" "-logger:$_loggerPath" ) + fi + MSBuild-Core ${args[@]} +} + +function MSBuild-Core { if [[ "$ci" == true ]]; then if [[ "$binary_log" != true ]]; then - echo "Binary log must be enabled in CI build." >&2 + EmitError "Binary log must be enabled in CI build." ExitWithExitCode 1 fi if [[ "$node_reuse" == true ]]; then - echo "Node reuse must be disabled in CI build." >&2 + EmitError "Node reuse must be disabled in CI build." ExitWithExitCode 1 fi fi @@ -325,7 +422,7 @@ function MSBuild { "$_InitializeBuildTool" "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" || { local exit_code=$? - echo "Build failed (exit code '$exit_code')." >&2 + EmitError "Build failed (exit code '$exit_code')." ExitWithExitCode $exit_code } } diff --git a/global.json b/global.json index 30db777b99..e4c42b3c60 100644 --- a/global.json +++ b/global.json @@ -3,10 +3,12 @@ "version": "3.0.100-preview5-011568" }, "tools": { + "dotnet": "3.0.100-preview5-011568", "jdk": "11.0.3" }, "msbuild-sdks": { "Yarn.MSBuild": "1.15.2", - "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19270.1" + "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19272.13", + "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19272.13" } } From 72433039c02abe13963945238abdfa5d1ae4d5dd Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 23 May 2019 15:40:54 -0700 Subject: [PATCH 05/95] Update dependencies and pin System.Data.SqlClient (#10434) --- eng/Version.Details.xml | 358 +++++++++--------- eng/Versions.props | 172 ++++----- .../Build/test/BindRazorIntegrationTest.cs | 18 +- .../test/RenderingRazorIntegrationTest.cs | 2 +- .../BasicTestApp/InputEventComponent.razor | 2 +- 5 files changed, 276 insertions(+), 276 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 11b8454994..0bfad6512e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,21 +9,21 @@ --> - + https://github.com/aspnet/AspNetCore-Tooling - 62e33dac1d5ec88ab15d7af694c1adf29ffc4c59 + 5bcd00877984c63293665f75bad20f522132638a - + https://github.com/aspnet/AspNetCore-Tooling - 62e33dac1d5ec88ab15d7af694c1adf29ffc4c59 + 5bcd00877984c63293665f75bad20f522132638a - + https://github.com/aspnet/AspNetCore-Tooling - 62e33dac1d5ec88ab15d7af694c1adf29ffc4c59 + 5bcd00877984c63293665f75bad20f522132638a - + https://github.com/aspnet/AspNetCore-Tooling - 62e33dac1d5ec88ab15d7af694c1adf29ffc4c59 + 5bcd00877984c63293665f75bad20f522132638a https://github.com/aspnet/EntityFrameworkCore @@ -53,336 +53,336 @@ https://github.com/aspnet/EntityFrameworkCore 08edd86216be4857b45b47bf0a9b29e98e525c05 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 - + + https://github.com/dotnet/corefx + ef1a5aa730098b6c3350977a991232c1ff11cfe3 + + + https://github.com/dotnet/corefx + ef1a5aa730098b6c3350977a991232c1ff11cfe3 + + + https://github.com/dotnet/corefx + ef1a5aa730098b6c3350977a991232c1ff11cfe3 + + https://github.com/dotnet/corefx a28176b5ec68b6da1472934fe9493790d1665cae - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - - https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae - - - https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae - - - https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae - - + https://github.com/dotnet/core-setup - 73226d6f119c757813f47b0dc021e8700d525f71 + c3145b06ba5151d5eafcf177a2e380f7acb61189 - + https://github.com/dotnet/core-setup - 73226d6f119c757813f47b0dc021e8700d525f71 + c3145b06ba5151d5eafcf177a2e380f7acb61189 - + https://github.com/dotnet/corefx - a28176b5ec68b6da1472934fe9493790d1665cae + ef1a5aa730098b6c3350977a991232c1ff11cfe3 - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 https://github.com/dotnet/arcade @@ -396,9 +396,9 @@ https://github.com/dotnet/arcade 86e674361bdcefecbd8199ab62d0b1a6cb25703d - + https://github.com/aspnet/Extensions - 8dfb4ece7ca9a6dea14264dafc38a0c953874559 + da5dee978f4eece3de85b776dfcf9dcc4cf5b109 diff --git a/eng/Versions.props b/eng/Versions.props index 108f3ea38a..78e4d044b8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,92 +23,92 @@ 1.0.0-beta.19272.13 - 3.0.0-preview6-27714-15 - 3.0.0-preview6-27714-15 + 3.0.0-preview6-27720-09 + 3.0.0-preview6-27720-09 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 4.7.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 1.7.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 - 4.6.0-preview6.19264.9 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 1.7.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.12 - 3.0.0-preview6.19264.9 + 3.0.0-preview6.19270.12 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 - 3.0.0-preview6.19265.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 + 3.0.0-preview6.19271.2 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 @@ -118,10 +118,10 @@ 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 - 3.0.0-preview6.19270.2 - 3.0.0-preview6.19270.2 - 3.0.0-preview6.19270.2 - 3.0.0-preview6.19270.2 + 3.0.0-preview6.19272.2 + 3.0.0-preview6.19272.2 + 3.0.0-preview6.19272.2 + 3.0.0-preview6.19272.2 true @@ -20,8 +19,7 @@ - - + diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreOnlyUsersTestBase.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreOnlyUsersTestBase.cs index 12aacce182..c0b0c49225 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreOnlyUsersTestBase.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreOnlyUsersTestBase.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.Test; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -27,11 +28,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test _fixture = fixture; } - protected override bool ShouldSkipDbTests() - { - return TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows; - } - public class TestUserDbContext : IdentityUserContext { public TestUserDbContext(DbContextOptions options) : base(options) { } @@ -54,9 +50,9 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test protected override Expression> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName); - public TestUserDbContext CreateContext() + private TestUserDbContext CreateContext() { - var db = DbUtil.Create(_fixture.ConnectionString); + var db = DbUtil.Create(_fixture.Connection); db.Database.EnsureCreated(); return db; } @@ -89,7 +85,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { var sqlConn = dbContext.Database.GetDbConnection(); - using (var db = new SqlConnection(sqlConn.ConnectionString)) + using (var db = new SqliteConnection(sqlConn.ConnectionString)) { db.Open(); Assert.True(DbUtil.VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp", @@ -104,7 +100,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test DbUtil.VerifyIndex(db, "AspNetUsers", "UserNameIndex", isUnique: true); DbUtil.VerifyIndex(db, "AspNetUsers", "EmailIndex"); - DbUtil.VerifyMaxLength(db, "AspNetUsers", 256, "UserName", "Email", "NormalizeUserName", "NormalizeEmail"); + DbUtil.VerifyMaxLength(dbContext, "AspNetUsers", 256, "UserName", "Email", "NormalizeUserName", "NormalizeEmail"); db.Close(); } @@ -249,4 +245,4 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test Assert.Equal(2, (await manager.GetRolesAsync(userByEmail)).Count); } } -} \ No newline at end of file +} diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreTestBase.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreTestBase.cs index 3c7ae781a4..d889876dcc 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreTestBase.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/SqlStoreTestBase.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.Test; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -58,11 +59,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test SetupAddIdentity(services); } - protected override bool ShouldSkipDbTests() - { - return TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows; - } - public class TestDbContext : IdentityDbContext { public TestDbContext(DbContextOptions options) : base(options) { } } @@ -94,11 +90,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test protected override Expression> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName); - public virtual TestDbContext CreateContext() + protected virtual TestDbContext CreateContext() { var services = new ServiceCollection(); SetupAddIdentity(services); - var db = DbUtil.Create(_fixture.ConnectionString, services); + var db = DbUtil.Create(_fixture.Connection, services); db.Database.EnsureCreated(); return db; } @@ -136,7 +132,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { var sqlConn = dbContext.Database.GetDbConnection(); - using (var db = new SqlConnection(sqlConn.ConnectionString)) + using (var db = new SqliteConnection(sqlConn.ConnectionString)) { db.Open(); Assert.True(DbUtil.VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp", @@ -148,8 +144,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test Assert.True(DbUtil.VerifyColumns(db, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName")); Assert.True(DbUtil.VerifyColumns(db, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value")); - Assert.True(DbUtil.VerifyMaxLength(db, "AspNetUsers", 256, "UserName", "Email", "NormalizedUserName", "NormalizedEmail")); - Assert.True(DbUtil.VerifyMaxLength(db, "AspNetRoles", 256, "Name", "NormalizedName")); + Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetUsers", 256, "UserName", "Email", "NormalizedUserName", "NormalizedEmail")); + Assert.True(DbUtil.VerifyMaxLength(dbContext, "AspNetRoles", 256, "Name", "NormalizedName")); DbUtil.VerifyIndex(db, "AspNetRoles", "RoleNameIndex", isUnique: true); DbUtil.VerifyIndex(db, "AspNetUsers", "UserNameIndex", isUnique: true); diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyCustomContextTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyCustomContextTest.cs index 47d1e30d5c..14eb1a77ee 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyCustomContextTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyCustomContextTest.cs @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test services .AddSingleton(new ConfigurationBuilder().Build()) .AddDbContext(o => - o.UseSqlServer(fixture.ConnectionString) + o.UseSqlite(fixture.Connection) .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))) .AddIdentityCore(o => { }) .AddEntityFrameworkStores(); diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyTest.cs index 8f1ea38881..4fa6c005cb 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserOnlyTest.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test services .AddSingleton(new ConfigurationBuilder().Build()) .AddDbContext( - o => o.UseSqlServer(fixture.ConnectionString) + o => o.UseSqlite(fixture.Connection) .ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))) .AddIdentityCore(o => { }) .AddEntityFrameworkStores(); diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreEncryptPersonalDataTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreEncryptPersonalDataTest.cs index 8d8c4c5a26..bf186561ed 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreEncryptPersonalDataTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreEncryptPersonalDataTest.cs @@ -75,10 +75,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [Fact] public async Task CanRotateKeysAndStillFind() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var name = Guid.NewGuid().ToString(); var user = CreateTestUser(name); @@ -176,11 +172,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [InlineData(false)] public async Task CustomPersonalDataPropertiesAreProtected(bool protect) { - if (ShouldSkipDbTests()) - { - return; - } - using (var scratch = new ScratchDatabaseFixture()) { var services = new ServiceCollection().AddLogging(); @@ -191,7 +182,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test .AddEntityFrameworkStores>() .AddPersonalDataProtection(); - var dbOptions = new DbContextOptionsBuilder().UseSqlServer(scratch.ConnectionString) + var dbOptions = new DbContextOptionsBuilder().UseSqlite(scratch.Connection) .UseApplicationServiceProvider(services.BuildServiceProvider()) .Options; var dbContext = new IdentityDbContext(dbOptions); @@ -260,11 +251,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [Fact] public void ProtectedPersonalDataThrowsOnNonString() { - if (ShouldSkipDbTests()) - { - return; - } - using (var scratch = new ScratchDatabaseFixture()) { var services = new ServiceCollection().AddLogging(); @@ -274,7 +260,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test }) .AddEntityFrameworkStores>() .AddPersonalDataProtection(); - var dbOptions = new DbContextOptionsBuilder().UseSqlServer(scratch.ConnectionString) + var dbOptions = new DbContextOptionsBuilder().UseSqlite(scratch.Connection) .UseApplicationServiceProvider(services.BuildServiceProvider()) .Options; var dbContext = new IdentityDbContext(dbOptions); diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs index d143047a58..3618b97309 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs @@ -23,9 +23,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test _fixture = fixture; } - protected override bool ShouldSkipDbTests() - => TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows; - public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) @@ -48,9 +45,9 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test } } - public IdentityDbContext CreateContext(bool delete = false) + private IdentityDbContext CreateContext(bool delete = false) { - var db = DbUtil.Create(_fixture.ConnectionString); + var db = DbUtil.Create(_fixture.Connection); if (delete) { db.Database.EnsureDeleted(); @@ -64,13 +61,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test return CreateContext(); } - public ApplicationDbContext CreateAppContext() - { - var db = DbUtil.Create(_fixture.ConnectionString); - db.Database.EnsureCreated(); - return db; - } - protected override void AddUserStore(IServiceCollection services, object context = null) { services.AddSingleton>(new UserStore((IdentityDbContext)context)); diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreWithGenericsTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreWithGenericsTest.cs index 951e91cabf..8e07a8dc8e 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreWithGenericsTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreWithGenericsTest.cs @@ -8,7 +8,6 @@ using System.Linq.Expressions; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.Test; -using Microsoft.AspNetCore.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -24,9 +23,9 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test _fixture = fixture; } - public ContextWithGenerics CreateContext() + private ContextWithGenerics CreateContext() { - var db = DbUtil.Create(_fixture.ConnectionString); + var db = DbUtil.Create(_fixture.Connection); db.Database.EnsureCreated(); return db; } @@ -36,11 +35,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test return CreateContext(); } - protected override bool ShouldSkipDbTests() - { - return TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows; - } - protected override void AddUserStore(IServiceCollection services, object context = null) { services.AddSingleton>(new UserStoreWithGenerics((ContextWithGenerics)context, "TestContext")); @@ -104,10 +98,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [Fact] public async Task CanAddRemoveUserClaimWithIssuer() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -136,10 +126,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [Fact] public async Task RemoveClaimWithIssuerOnlyAffectsUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); var user2 = CreateTestUser(); @@ -169,10 +155,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [Fact] public async Task CanReplaceUserClaimWithIssuer() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -348,4 +330,4 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test } #endregion -} \ No newline at end of file +} diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/ScratchDatabaseFixture.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/ScratchDatabaseFixture.cs index 6634a81591..39aed8910b 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/ScratchDatabaseFixture.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/ScratchDatabaseFixture.cs @@ -2,28 +2,40 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.Utilities; -using Microsoft.EntityFrameworkCore.Internal; +using System.Data.Common; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { public class ScratchDatabaseFixture : IDisposable { - private readonly Lazy _testStore; + private readonly SqliteConnection _connection; public ScratchDatabaseFixture() { - _testStore = new Lazy(() => SqlServerTestStore.CreateScratch()); + _connection = new SqliteConnection($"DataSource=D{Guid.NewGuid()}.db"); + + using (var context = CreateEmptyContext()) + { + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } } - public string ConnectionString => _testStore.Value.Connection.ConnectionString; + private DbContext CreateEmptyContext() + => new DbContext(new DbContextOptionsBuilder().UseSqlite(_connection).Options); + + public DbConnection Connection => _connection; public void Dispose() { - if (_testStore.IsValueCreated) + using (var context = CreateEmptyContext()) { - _testStore.Value.Dispose(); + context.Database.EnsureDeleted(); } + + _connection.Dispose(); } } } diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/SqlServerTestStore.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/SqlServerTestStore.cs deleted file mode 100644 index dab5854282..0000000000 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/Utilities/SqlServerTestStore.cs +++ /dev/null @@ -1,165 +0,0 @@ -// 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.Data.Common; -using System.Data.SqlClient; -using System.IO; -using System.Threading; - -namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test.Utilities -{ - public class SqlServerTestStore : IDisposable - { - public const int CommandTimeout = 90; - - public static string CreateConnectionString(string name) - { - var connStrBuilder = new SqlConnectionStringBuilder(TestEnvironment.Config["Test:SqlServer:DefaultConnectionString"]) - { - InitialCatalog = name - }; - - return connStrBuilder.ConnectionString; - } - - public static SqlServerTestStore CreateScratch(bool createDatabase = true) - => new SqlServerTestStore(GetScratchDbName()).CreateTransient(createDatabase); - - private SqlConnection _connection; - private readonly string _name; - private bool _deleteDatabase; - - private SqlServerTestStore(string name) - { - _name = name; - } - - private static string GetScratchDbName() - { - string name; - do - { - name = "Scratch_" + Guid.NewGuid(); - } while (DatabaseExists(name) - || DatabaseFilesExist(name)); - - return name; - } - - private static void WaitForExists(SqlConnection connection) - { - var retryCount = 0; - while (true) - { - try - { - connection.Open(); - - connection.Close(); - - return; - } - catch (SqlException e) - { - if (++retryCount >= 30 - || (e.Number != 233 && e.Number != -2 && e.Number != 4060)) - { - throw; - } - - SqlConnection.ClearPool(connection); - - Thread.Sleep(100); - } - } - } - - private SqlServerTestStore CreateTransient(bool createDatabase) - { - _connection = new SqlConnection(CreateConnectionString(_name)); - - if (createDatabase) - { - using (var master = new SqlConnection(CreateConnectionString("master"))) - { - master.Open(); - using (var command = master.CreateCommand()) - { - command.CommandTimeout = CommandTimeout; - command.CommandText = $"{Environment.NewLine}CREATE DATABASE [{_name}]"; - - command.ExecuteNonQuery(); - - WaitForExists(_connection); - } - } - _connection.Open(); - } - - _deleteDatabase = true; - return this; - } - - private static bool DatabaseExists(string name) - { - using (var master = new SqlConnection(CreateConnectionString("master"))) - { - master.Open(); - - using (var command = master.CreateCommand()) - { - command.CommandTimeout = CommandTimeout; - command.CommandText = $@"SELECT COUNT(*) FROM sys.databases WHERE name = N'{name}'"; - - return (int) command.ExecuteScalar() > 0; - } - } - } - - private static bool DatabaseFilesExist(string name) - { - var userFolder = Environment.GetEnvironmentVariable("USERPROFILE") ?? - Environment.GetEnvironmentVariable("HOME"); - return userFolder != null - && (File.Exists(Path.Combine(userFolder, name + ".mdf")) - || File.Exists(Path.Combine(userFolder, name + "_log.ldf"))); - } - - private void DeleteDatabase(string name) - { - using (var master = new SqlConnection(CreateConnectionString("master"))) - { - master.Open(); - - using (var command = master.CreateCommand()) - { - command.CommandTimeout = CommandTimeout; - // Query will take a few seconds if (and only if) there are active connections - - // SET SINGLE_USER will close any open connections that would prevent the drop - command.CommandText - = string.Format(@"IF EXISTS (SELECT * FROM sys.databases WHERE name = N'{0}') - BEGIN - ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; - DROP DATABASE [{0}]; - END", name); - - command.ExecuteNonQuery(); - } - } - } - - public DbConnection Connection => _connection; - - public void Dispose() - { - _connection.Dispose(); - - if (_deleteDatabase) - { - DeleteDatabase(_name); - } - } - } -} diff --git a/src/Identity/Specification.Tests/src/IdentitySpecificationTestBase.cs b/src/Identity/Specification.Tests/src/IdentitySpecificationTestBase.cs index 9d43420682..4e67e26253 100644 --- a/src/Identity/Specification.Tests/src/IdentitySpecificationTestBase.cs +++ b/src/Identity/Specification.Tests/src/IdentitySpecificationTestBase.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Linq.Expressions; using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; @@ -129,10 +128,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanCreateRoleTest() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var roleName = "create" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -169,10 +164,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task BadValidatorBlocksCreateRole() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); manager.RoleValidators.Clear(); manager.RoleValidators.Add(new AlwaysBadValidator()); @@ -189,10 +180,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChainRoleValidators() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); manager.RoleValidators.Clear(); manager.RoleValidators.Add(new AlwaysBadValidator()); @@ -211,10 +198,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task BadValidatorBlocksRoleUpdate() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var role = CreateTestRole("poorguy"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); @@ -232,10 +215,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanDeleteRole() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var roleName = "delete" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -253,10 +232,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanAddRemoveRoleClaim() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var role = CreateTestRole("ClaimsAddRemove"); var roleSafe = CreateTestRole("ClaimsAdd"); @@ -296,10 +271,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanRoleFindById() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var role = CreateTestRole("FindByIdAsync"); Assert.Null(await manager.FindByIdAsync(await manager.GetRoleIdAsync(role))); @@ -314,10 +285,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanRoleFindByName() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var roleName = "FindByNameAsync" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -334,10 +301,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanUpdateRoleName() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var roleName = "update" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -357,10 +320,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanQueryableRoles() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); if (manager.SupportsQueryableRoles) { @@ -384,10 +343,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CreateRoleFailsIfExists() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateRoleManager(); var roleName = "dupeRole" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -405,10 +360,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanAddUsersToRole() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var manager = CreateManager(context); var roleManager = CreateRoleManager(context); @@ -434,11 +385,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGetRolesForUser() { - if (ShouldSkipDbTests()) - { - return; - } - var context = CreateTestContext(); var userManager = CreateManager(context); var roleManager = CreateRoleManager(context); @@ -477,10 +423,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task RemoveUserFromRoleWithMultipleRoles() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var userManager = CreateManager(context); var roleManager = CreateRoleManager(context); @@ -504,10 +446,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanRemoveUsersFromRole() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var userManager = CreateManager(context); var roleManager = CreateRoleManager(context); @@ -538,10 +476,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task RemoveUserNotInRoleFails() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var userMgr = CreateManager(context); var roleMgr = CreateRoleManager(context); @@ -562,10 +496,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddUserToRoleFailsIfAlreadyInRole() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var userMgr = CreateManager(context); var roleMgr = CreateRoleManager(context); @@ -587,10 +517,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddUserToRolesIgnoresDuplicates() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var userMgr = CreateManager(context); var roleMgr = CreateRoleManager(context); @@ -611,10 +537,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanFindRoleByNameWithManager() { - if (ShouldSkipDbTests()) - { - return; - } var roleMgr = CreateRoleManager(); var roleName = "findRoleByNameTest" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -629,10 +551,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanFindRoleWithManager() { - if (ShouldSkipDbTests()) - { - return; - } var roleMgr = CreateRoleManager(); var roleName = "findRoleTest" + Guid.NewGuid().ToString(); var role = CreateTestRole(roleName, useRoleNamePrefixAsRoleName: true); @@ -647,10 +565,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGetUsersInRole() { - if (ShouldSkipDbTests()) - { - return; - } var context = CreateTestContext(); var manager = CreateManager(context); var roleManager = CreateRoleManager(context); diff --git a/src/Identity/Specification.Tests/src/UserManagerSpecificationTests.cs b/src/Identity/Specification.Tests/src/UserManagerSpecificationTests.cs index b5fe50375a..9b743e4cf9 100644 --- a/src/Identity/Specification.Tests/src/UserManagerSpecificationTests.cs +++ b/src/Identity/Specification.Tests/src/UserManagerSpecificationTests.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Linq.Expressions; using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; @@ -72,12 +71,6 @@ namespace Microsoft.AspNetCore.Identity.Test return builder; } - /// - /// If true, tests that require a database will be skipped. - /// - /// - protected virtual bool ShouldSkipDbTests() => false; - /// /// Creates the user manager used for tests. /// @@ -184,10 +177,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorWithNoErrorsCanBlockAddPassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -203,10 +192,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CreateUserWillSetCreateDateOnlyIfSupported() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -220,10 +205,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanDeleteUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -239,10 +220,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanUpdateUserName() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var name = Guid.NewGuid().ToString(); var user = CreateTestUser(name); @@ -262,10 +239,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CheckSetUserNameValidatesUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var username = "UpdateAsync" + Guid.NewGuid().ToString(); var newUsername = "New" + Guid.NewGuid().ToString(); @@ -294,10 +267,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task SetUserNameUpdatesSecurityStamp() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var username = "UpdateAsync" + Guid.NewGuid().ToString(); var newUsername = "New" + Guid.NewGuid().ToString(); @@ -316,10 +285,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ResetAuthenticatorKeyUpdatesSecurityStamp() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var username = "Create" + Guid.NewGuid().ToString(); var user = CreateTestUser(username, useNamePrefixAsUserName: true); @@ -336,10 +301,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CheckSetEmailValidatesUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.Options.User.RequireUniqueEmail = true; manager.UserValidators.Add(new UserValidator()); @@ -363,10 +324,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanUpdatePasswordUsingHasher() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("UpdatePassword"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); @@ -387,10 +344,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanFindById() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -404,10 +357,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task UserValidatorCanBlockCreate() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); manager.UserValidators.Clear(); @@ -423,10 +372,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task UserValidatorCanBlockUpdate() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -443,10 +388,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChainUserValidators() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.UserValidators.Clear(); var user = CreateTestUser(); @@ -467,10 +408,6 @@ namespace Microsoft.AspNetCore.Identity.Test [InlineData(null)] public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); manager.Options.User.RequireUniqueEmail = true; @@ -486,10 +423,6 @@ namespace Microsoft.AspNetCore.Identity.Test [InlineData("bogus")] public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("UpdateBlocked", email); manager.Options.User.RequireUniqueEmail = true; @@ -503,10 +436,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorCanBlockAddPassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -524,10 +453,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChainPasswordValidators() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.PasswordValidators.Clear(); manager.PasswordValidators.Add(new EmptyBadValidator()); @@ -546,10 +471,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorWithNoErrorsCanBlockChangePassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); @@ -565,10 +486,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorWithNoErrorsCanBlockCreateUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); manager.PasswordValidators.Clear(); @@ -583,10 +500,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorWithNoErrorsCanBlockResetPasswordWithStaticTokenProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.PasswordResetTokenProvider = "Static"; @@ -611,10 +524,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorCanBlockChangePassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); @@ -632,10 +541,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorCanBlockCreateUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); manager.PasswordValidators.Clear(); @@ -651,10 +556,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanCreateUserNoPassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var username = "CreateUserTest" + Guid.NewGuid(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(CreateTestUser(username, useNamePrefixAsUserName: true))); @@ -674,10 +575,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanCreateUserAddLogin() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); const string provider = "ZzAuth"; const string display = "display"; @@ -700,10 +597,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanCreateUserLoginAndAddPassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -727,10 +620,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddPasswordFailsIfAlreadyHave() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); @@ -747,10 +636,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanCreateUserAddRemoveLogin() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); var result = await manager.CreateAsync(user); @@ -782,10 +667,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanRemovePassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("CanRemovePassword"); const string password = "password"; @@ -806,10 +687,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChangePassword() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); const string password = "password"; @@ -830,10 +707,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanAddRemoveUserClaim() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -863,10 +736,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task RemoveClaimOnlyAffectsUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); var user2 = CreateTestUser(); @@ -900,10 +769,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanReplaceUserClaim() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -927,10 +792,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ReplaceUserClaimOnlyAffectsUser() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); var user2 = CreateTestUser(); @@ -964,10 +825,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangePasswordFallsIfPasswordWrong() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); @@ -983,10 +840,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddDupeUserNameFails() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var username = "AddDupeUserNameFails" + Guid.NewGuid(); var user = CreateTestUser(username, useNamePrefixAsUserName: true); @@ -1002,10 +855,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddDupeEmailAllowedByDefault() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(email: "yup@yup.com"); var user2 = CreateTestUser(email: "yup@yup.com"); @@ -1021,10 +870,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddDupeEmailFailsWhenUniqueEmailRequired() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.Options.User.RequireUniqueEmail = true; var user = CreateTestUser(email: "FooUser@yup.com"); @@ -1040,10 +885,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task UpdateSecurityStampActuallyChanges() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1059,10 +900,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task AddDupeLoginFails() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); var login = new UserLoginInfo("Provider", "key", "display"); @@ -1082,10 +919,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanFindByEmail() { - if (ShouldSkipDbTests()) - { - return; - } var email = "foouser@test.com"; var manager = CreateManager(); var user = CreateTestUser(email: email); @@ -1101,11 +934,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async virtual Task CanFindUsersViaUserQuerable() { - if (ShouldSkipDbTests()) - { - return; - } - var mgr = CreateManager(); if (mgr.SupportsQueryableUsers) { @@ -1126,10 +954,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ConfirmEmailFalseByDefaultTest() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1166,10 +990,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanResetPasswordWithStaticTokenProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.PasswordResetTokenProvider = "Static"; @@ -1195,10 +1015,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.PasswordResetTokenProvider = "Static"; @@ -1225,10 +1041,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.PasswordResetTokenProvider = "Static"; @@ -1251,10 +1063,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); var user = CreateTestUser(); @@ -1283,10 +1091,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanConfirmEmailWithStaticToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.EmailConfirmationTokenProvider = "Static"; @@ -1309,10 +1113,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Static", new StaticTokenProvider()); manager.Options.Tokens.EmailConfirmationTokenProvider = "Static"; @@ -1331,10 +1131,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ConfirmTokenFailsAfterPasswordChange() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(namePrefix: "Test"); Assert.False(await manager.IsEmailConfirmedAsync(user)); @@ -1356,10 +1152,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task SingleFailureLockout() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.MaxFailedAccessAttempts = 0; @@ -1382,10 +1174,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task TwoFailureLockout() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.MaxFailedAccessAttempts = 2; @@ -1411,10 +1199,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ResetAccessCountPreventsLockout() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.MaxFailedAccessAttempts = 2; @@ -1443,10 +1227,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanEnableLockoutManuallyAndLockout() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); mgr.Options.Lockout.AllowedForNewUsers = false; @@ -1475,10 +1255,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); @@ -1495,10 +1271,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task LockoutFailsIfNotEnabled() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); mgr.Options.Lockout.AllowedForNewUsers = false; var user = CreateTestUser(); @@ -1517,10 +1289,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); var user = CreateTestUser(lockoutEnd: DateTimeOffset.UtcNow.AddSeconds(-1)); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); @@ -1535,10 +1303,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); @@ -1554,10 +1318,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); var lockoutEnd = DateTimeOffset.UtcNow.AddMinutes(5); var user = CreateTestUser(lockoutEnd: lockoutEnd); @@ -1573,10 +1333,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() { - if (ShouldSkipDbTests()) - { - return; - } var mgr = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); @@ -1595,10 +1351,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task SetPhoneNumberTest() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "123-456-7890"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1616,10 +1368,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChangePhoneNumber() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "123-456-7890"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1639,10 +1387,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangePhoneNumberTokenIsInt() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "123-456-7890"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1657,10 +1401,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangePhoneNumberFailsWithWrongToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "123-456-7890"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1693,10 +1433,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangePhoneNumberWithCustomProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); manager.RegisterTokenProvider("Yes", new YesPhoneNumberProvider()); manager.Options.Tokens.ChangePhoneNumberTokenProvider = "Yes"; @@ -1717,10 +1453,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangePhoneNumberFailsWithWrongPhoneNumber() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "123-456-7890"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1741,10 +1473,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanVerifyPhoneNumber() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1770,10 +1498,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChangeEmail() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("foouser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1796,10 +1520,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChangeEmailOnlyIfEmailSame() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("foouser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1824,10 +1544,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanChangeEmailWithDifferentTokenProvider() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(context: null, services: null, configureServices: s => s.Configure( o => o.Tokens.ProviderMap["NewProvider2"] = new TokenProviderDescriptor(typeof(EmailTokenProvider)))); @@ -1853,10 +1569,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangeEmailTokensFailsAfterEmailChanged() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("foouser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1880,10 +1592,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangeEmailFailsWithWrongToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("foouser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1907,10 +1615,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task ChangeEmailFailsWithEmail() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser("foouser"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1936,10 +1640,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/1766", FlakyOn.All)] public async Task EmailFactorFailsAfterSecurityStampChangeTest() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); string factorId = "Email"; //default var user = CreateTestUser("foouser"); @@ -1970,10 +1670,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task EnableTwoFactorChangesSecurityStamp() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -1991,10 +1687,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2020,10 +1712,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task GetValidTwoFactorTestEmptyWithNoProviders() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2039,10 +1727,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGetSetUpdateAndRemoveUserToken() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2067,10 +1751,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanRedeemRecoveryCodeOnlyOnce() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2099,10 +1779,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task RecoveryCodesInvalidAfterReplace() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2131,10 +1807,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGetValidTwoFactor() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2175,10 +1847,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task PhoneFactorFailsAfterSecurityStampChangeTest() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var factorId = "Phone"; // default var user = CreateTestUser(phoneNumber: "4251234567"); @@ -2199,10 +1867,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task VerifyTokenFromWrongTokenProviderFails() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "4251234567"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2219,10 +1883,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task VerifyWithWrongSmsTokenFails() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); var user = CreateTestUser(phoneNumber: "4251234567"); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); @@ -2237,10 +1897,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task NullableDateTimeOperationTest() { - if (ShouldSkipDbTests()) - { - return; - } var userMgr = CreateManager(); var user = CreateTestUser(lockoutEnabled: true); IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); @@ -2263,10 +1919,6 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public async Task CanGetUsersWithClaims() { - if (ShouldSkipDbTests()) - { - return; - } var manager = CreateManager(); for (int i = 0; i < 6; i++) diff --git a/src/Identity/test/Identity.FunctionalTests/AuthorizationTests.cs b/src/Identity/test/Identity.FunctionalTests/AuthorizationTests.cs index c4a1b43083..3ccc399bfd 100644 --- a/src/Identity/test/Identity.FunctionalTests/AuthorizationTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/AuthorizationTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests where TStartup : class where TContext : DbContext { - public AuthorizationTests(ServerFactory serverFactory) + protected AuthorizationTests(ServerFactory serverFactory) { ServerFactory = serverFactory; } diff --git a/src/Identity/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs b/src/Identity/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs index 50f67cad09..9070d6ba14 100644 --- a/src/Identity/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs +++ b/src/Identity/test/Identity.FunctionalTests/Infrastructure/FunctionalTestsServiceCollectionExtensions.cs @@ -2,12 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Data.Common; using System.Security.Claims; using System.Threading.Tasks; using Identity.DefaultUI.WebSite; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.EntityFrameworkCore; @@ -18,10 +18,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests { public static class FunctionalTestsServiceCollectionExtensions { - public static IServiceCollection SetupTestDatabase(this IServiceCollection services, string databaseName) where TContext : DbContext => + public static IServiceCollection SetupTestDatabase(this IServiceCollection services, DbConnection connection) where TContext : DbContext => services.AddDbContext(options => options.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning)) - .UseInMemoryDatabase(databaseName, memoryOptions => { })); + .UseSqlite(connection)); public static IServiceCollection SetupTestThirdPartyLogin(this IServiceCollection services) => services.AddAuthentication() diff --git a/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs b/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs index af4a6d9b81..e84840aa68 100644 --- a/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs +++ b/src/Identity/test/Identity.FunctionalTests/Infrastructure/ServerFactory.cs @@ -3,19 +3,24 @@ using System; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Identity.FunctionalTests { - public class ServerFactory: WebApplicationFactory + public class ServerFactory: WebApplicationFactory where TStartup : class where TContext : DbContext { + private readonly SqliteConnection _connection + = new SqliteConnection($"DataSource=:memory:"); + public ServerFactory() { + _connection.Open(); + ClientOptions.AllowAutoRedirect = false; ClientOptions.BaseAddress = new Uri("https://localhost"); } @@ -24,11 +29,30 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests { base.ConfigureWebHost(builder); builder.UseStartup(); - builder.ConfigureServices(sc => sc.SetupTestDatabase(Guid.NewGuid().ToString()) + + builder.ConfigureServices(sc => + { + sc.SetupTestDatabase(_connection) .AddMvc() // Mark the cookie as essential for right now, as Identity uses it on // several places to pass important data in post-redirect-get flows. - .AddCookieTempDataProvider(o => o.Cookie.IsEssential = true)); + .AddCookieTempDataProvider(o => o.Cookie.IsEssential = true); + }); + } + + public override void EnsureDatabaseCreated() + { + using (var scope = Services.CreateScope()) + { + scope.ServiceProvider.GetService().Database.EnsureCreated(); + } + } + + protected override void Dispose(bool disposing) + { + _connection.Dispose(); + + base.Dispose(disposing); } } } diff --git a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs index dc1124a664..d49255a001 100644 --- a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests where TStartup : class where TContext : DbContext { - public LoginTests(ServerFactory serverFactory) + protected LoginTests(ServerFactory serverFactory) { ServerFactory = serverFactory; } @@ -289,6 +289,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests var server = ServerFactory.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)); + ServerFactory.EnsureDatabaseCreated(); + var client = server.CreateClient(); var newClient = server.CreateClient(); @@ -311,6 +313,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests var server = ServerFactory.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)); + ServerFactory.EnsureDatabaseCreated(); + var client = server.CreateClient(); var resetPasswordClient = server.CreateClient(); var newClient = server.CreateClient(); diff --git a/src/Identity/test/Identity.FunctionalTests/ManagementTests.cs b/src/Identity/test/Identity.FunctionalTests/ManagementTests.cs index 6bb5c8e64d..f370d0dec3 100644 --- a/src/Identity/test/Identity.FunctionalTests/ManagementTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/ManagementTests.cs @@ -289,6 +289,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureTestServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var userName = $"{Guid.NewGuid()}@example.com"; var guid = Guid.NewGuid(); var email = userName; diff --git a/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj b/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj index 3230a236b9..b3ab76bd7d 100644 --- a/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj +++ b/src/Identity/test/Identity.FunctionalTests/Microsoft.AspNetCore.Identity.FunctionalTests.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs b/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs index dc07c17992..e1222ed381 100644 --- a/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests where TStartup : class where TContext : DbContext { - public RegistrationTests(ServerFactory serverFactory) + protected RegistrationTests(ServerFactory serverFactory) { ServerFactory = serverFactory; } @@ -31,6 +31,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var userName = $"{Guid.NewGuid()}@example.com"; var password = $"!Test.Password1$"; @@ -49,6 +51,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests var client = server.CreateClient(); var client2 = server.CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var userName = $"{Guid.NewGuid()}@example.com"; var password = $"!Test.Password1$"; @@ -81,6 +85,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests var client = server.CreateClient(); var client2 = server.CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var userName = $"{Guid.NewGuid()}@example.com"; var password = $"!Test.Password1$"; @@ -102,6 +108,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var userName = $"{Guid.NewGuid()}@example.com"; var password = $"!Test.Password1$"; @@ -121,6 +129,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var guid = Guid.NewGuid(); var userName = $"{guid}"; var email = $"{guid}@example.com"; @@ -141,6 +151,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var guid = Guid.NewGuid(); var userName = $"{guid}"; var email = $"{guid}@example.com"; @@ -162,6 +174,8 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) .CreateClient(); + ServerFactory.EnsureDatabaseCreated(); + var guid = Guid.NewGuid(); var userName = $"{guid}"; var email = $"{guid}@example.com"; diff --git a/src/Identity/test/InMemory.Test/InMemoryUserStoreTest.cs b/src/Identity/test/InMemory.Test/InMemoryUserStoreTest.cs index aa9c992523..ac7a08caa6 100644 --- a/src/Identity/test/InMemory.Test/InMemoryUserStoreTest.cs +++ b/src/Identity/test/InMemory.Test/InMemoryUserStoreTest.cs @@ -4,11 +4,13 @@ using System; using System.Linq.Expressions; using Microsoft.AspNetCore.Identity.Test; +using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; +using Xunit; namespace Microsoft.AspNetCore.Identity.InMemory.Test { - public class InMemoryUserStoreTest : UserManagerSpecificationTestBase + public class InMemoryUserStoreTest : UserManagerSpecificationTestBase, IClassFixture { protected override object CreateTestContext() { @@ -41,5 +43,22 @@ namespace Microsoft.AspNetCore.Identity.InMemory.Test protected override Expression> UserNameEqualsPredicate(string userName) => u => u.UserName == userName; protected override Expression> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName); + + public class Fixture : IDisposable + { + private readonly SqliteConnection _connection + = new SqliteConnection($"DataSource=:memory:"); + + public Fixture() + { + _connection.Open(); + } + + public void Dispose() + { + _connection.Close(); + _connection.Dispose(); + } + } } -} \ No newline at end of file +} diff --git a/src/Identity/test/InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj b/src/Identity/test/InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj index 8f4c117c4e..4026796ab7 100644 --- a/src/Identity/test/InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj +++ b/src/Identity/test/InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -14,6 +14,7 @@ + diff --git a/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.netcoreapp3.0.cs b/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.netcoreapp3.0.cs index 069eae8180..01c33165e6 100644 --- a/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.netcoreapp3.0.cs +++ b/src/Mvc/Mvc.Testing/ref/Microsoft.AspNetCore.Mvc.Testing.netcoreapp3.0.cs @@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing protected virtual Microsoft.AspNetCore.Hosting.IWebHostBuilder CreateWebHostBuilder() { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public virtual void EnsureDatabaseCreated() { } ~WebApplicationFactory() { } protected virtual System.Collections.Generic.IEnumerable GetTestAssemblies() { throw null; } public Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory WithWebHostBuilder(System.Action configuration) { throw null; } diff --git a/src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs b/src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs index d9d9072cb9..43e8b90f9a 100644 --- a/src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs +++ b/src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs @@ -162,6 +162,12 @@ namespace Microsoft.AspNetCore.Mvc.Testing SetContentRoot(builder); _configuration(builder); _server = CreateServer(builder); + + EnsureDatabaseCreated(); + } + + public virtual void EnsureDatabaseCreated() + { } private void SetContentRoot(IWebHostBuilder builder) From 166851df325364538c0157d33609f373d8fd6da2 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 23 May 2019 17:53:11 -0700 Subject: [PATCH 07/95] Make it so Helix runs can be rerun and preserve history (#10457) * Make it so Helix runs can be rerun and preserve history Addresses https://github.com/aspnet/AspNetCore/issues/8235 * Update eng/helix/helix.proj Co-Authored-By: Nate McMaster --- eng/helix/helix.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/helix.proj b/eng/helix/helix.proj index 9274e5a6df..df0567251a 100644 --- a/eng/helix/helix.proj +++ b/eng/helix/helix.proj @@ -19,7 +19,7 @@ ci aspnetcore - $(BUILD_BUILDNUMBER) + $(BUILD_BUILDNUMBER).$([System.DateTime]::Now.ToString('HH.mm')) true true true From 3f9f40630102ceff0fbe27a84f662cbf6c0bea43 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 23 May 2019 18:04:26 -0700 Subject: [PATCH 08/95] Revert "Make it so Helix runs can be rerun and preserve history (#10457)" This reverts commit 166851df325364538c0157d33609f373d8fd6da2. --- eng/helix/helix.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/helix/helix.proj b/eng/helix/helix.proj index df0567251a..9274e5a6df 100644 --- a/eng/helix/helix.proj +++ b/eng/helix/helix.proj @@ -19,7 +19,7 @@ ci aspnetcore - $(BUILD_BUILDNUMBER).$([System.DateTime]::Now.ToString('HH.mm')) + $(BUILD_BUILDNUMBER) true true true From 2c70498c136a13b12005e9afbad727091730709a Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 23 May 2019 20:07:39 -0700 Subject: [PATCH 09/95] Fix TLS protocols used in tests (#10483) --- .../Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs index 30847af463..00602983e4 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs @@ -408,7 +408,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests using (var sslStream = new SslStream(connection.Stream, true, (sender, certificate, chain, errors) => true)) { await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null, - enabledSslProtocols: SslProtocols.None, + enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, checkCertificateRevocation: false); } } @@ -447,7 +447,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests using (var sslStream = new SslStream(connection.Stream, true, (sender, certificate, chain, errors) => true)) { await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null, - enabledSslProtocols: SslProtocols.None, + enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, checkCertificateRevocation: false); } } From 3bb9cda189dabfbd1b992b20a738d9479fd5a58b Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Thu, 23 May 2019 22:14:40 -0700 Subject: [PATCH 10/95] Perform symbol publication using Microsoft.SymbolUploader.Build.Task (#10358) - aspnet/AspNetCore-Internal#2126 - allow release pipeline to publish symbols on all builds: #10113 - add new project that publishes symbols to MSDL and SymWeb - release pipeline prepares then restores and builds the new project - expire symbols after 10 years (or so) nits: - add `$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE` to ci.yml --- .azure/pipelines/ci.yml | 4 +++ eng/PublishSymbols.proj | 62 +++++++++++++++++++++++++++++++++++++++++ eng/Versions.props | 1 + 3 files changed, 67 insertions(+) create mode 100644 eng/PublishSymbols.proj diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index 755ffe182e..900b033f83 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -17,6 +17,10 @@ pr: include: - '*' +variables: +- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE + value: true + jobs: - template: jobs/default-build.yml parameters: diff --git a/eng/PublishSymbols.proj b/eng/PublishSymbols.proj new file mode 100644 index 0000000000..dd2c968566 --- /dev/null +++ b/eng/PublishSymbols.proj @@ -0,0 +1,62 @@ + + + + netcoreapp3.0 + true + artifacts\manifests\ + true + + + 3650 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Versions.props b/eng/Versions.props index 78e4d044b8..7fdd2d0d75 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -140,6 +140,7 @@ 15.9.0 1.0.0-beta2-18618-05 1.0.0-beta2-18618-05 + 1.0.0-beta-64023-03 4.5.0 4.4.0 From 405d8bbdc91bd7cda376cf6f17cea664d976b8c1 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Thu, 23 May 2019 13:33:07 -0700 Subject: [PATCH 11/95] Turn SETLOCAL on so that we don't accumulate in the PATH --- startvs.cmd | 1 + 1 file changed, 1 insertion(+) diff --git a/startvs.cmd b/startvs.cmd index d15843640b..dd9c798c5b 100644 --- a/startvs.cmd +++ b/startvs.cmd @@ -1,4 +1,5 @@ @ECHO OFF +SETLOCAL :: This command launches a Visual Studio solution with environment variables required to use a local version of the .NET Core SDK. From d18a033b1ee6d923a72d440718c5d496b57c2ffc Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 24 May 2019 15:28:37 +0100 Subject: [PATCH 12/95] Integrate AuthorizeView with actual authorization (#10487) --- .../src/Hosting/WebAssemblyHostBuilder.cs | 7 + .../src/Services/WebAssemblyConsoleLogger.cs | 34 +++ .../src/Services/WebAssemblyLoggerFactory.cs | 23 ++ .../Microsoft.AspNetCore.Components.csproj | 1 + ...etCore.Components.netstandard2.0.Manual.cs | 8 +- .../src/Auth/AuthorizeDataAdapter.cs | 39 +++ .../Components/src/Auth/AuthorizeView.razor | 49 +++- .../Microsoft.AspNetCore.Components.csproj | 1 + .../Components/test/Auth/AuthorizeViewTest.cs | 275 ++++++++++++++++-- src/Components/Shared/test/TestRenderer.cs | 2 +- src/Components/test/E2ETest/Tests/AuthTest.cs | 52 +++- .../AuthTest/AuthorizeViewCases.razor | 34 ++- .../ClientSideAuthenticationStateData.cs | 2 +- .../ServerAuthenticationStateProvider.cs | 2 +- .../test/testassets/BasicTestApp/Startup.cs | 6 + .../TestServer/Controllers/UserController.cs | 20 +- .../TestServer/Pages/Authentication.cshtml | 19 +- .../test/testassets/TestServer/Startup.cs | 6 + 18 files changed, 529 insertions(+), 51 deletions(-) create mode 100644 src/Components/Blazor/Blazor/src/Services/WebAssemblyConsoleLogger.cs create mode 100644 src/Components/Blazor/Blazor/src/Services/WebAssemblyLoggerFactory.cs create mode 100644 src/Components/Components/src/Auth/AuthorizeDataAdapter.cs diff --git a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHostBuilder.cs b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHostBuilder.cs index 8cf720e1ca..0b741cbd22 100644 --- a/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/Components/Blazor/Blazor/src/Hosting/WebAssemblyHostBuilder.cs @@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Blazor.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Routing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using Microsoft.JSInterop; namespace Microsoft.AspNetCore.Blazor.Hosting @@ -92,6 +94,7 @@ namespace Microsoft.AspNetCore.Blazor.Hosting services.AddSingleton(); services.AddSingleton(WebAssemblyUriHelper.Instance); services.AddSingleton(WebAssemblyNavigationInterception.Instance); + services.AddSingleton(); services.AddSingleton(s => { // Creating the URI helper needs to wait until the JS Runtime is initialized, so defer it. @@ -102,6 +105,10 @@ namespace Microsoft.AspNetCore.Blazor.Hosting }; }); + // Needed for authorization + services.AddOptions(); + services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(WebAssemblyConsoleLogger<>))); + foreach (var configureServicesAction in _configureServicesActions) { configureServicesAction(_BrowserHostBuilderContext, services); diff --git a/src/Components/Blazor/Blazor/src/Services/WebAssemblyConsoleLogger.cs b/src/Components/Blazor/Blazor/src/Services/WebAssemblyConsoleLogger.cs new file mode 100644 index 0000000000..c86c1cf30b --- /dev/null +++ b/src/Components/Blazor/Blazor/src/Services/WebAssemblyConsoleLogger.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Blazor.Services +{ + internal class WebAssemblyConsoleLogger : ILogger, ILogger + { + public IDisposable BeginScope(TState state) + { + return NoOpDisposable.Instance; + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel >= LogLevel.Warning; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + var formattedMessage = formatter(state, exception); + Console.WriteLine($"[{logLevel}] {formattedMessage}"); + } + + private class NoOpDisposable : IDisposable + { + public static NoOpDisposable Instance = new NoOpDisposable(); + + public void Dispose() { } + } + } +} diff --git a/src/Components/Blazor/Blazor/src/Services/WebAssemblyLoggerFactory.cs b/src/Components/Blazor/Blazor/src/Services/WebAssemblyLoggerFactory.cs new file mode 100644 index 0000000000..73458387e7 --- /dev/null +++ b/src/Components/Blazor/Blazor/src/Services/WebAssemblyLoggerFactory.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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Blazor.Services +{ + internal class WebAssemblyLoggerFactory : ILoggerFactory + { + public void AddProvider(ILoggerProvider provider) + { + // No-op + } + + public ILogger CreateLogger(string categoryName) + => new WebAssemblyConsoleLogger(); + + public void Dispose() + { + // No-op + } + } +} diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj index bb08a3ecfe..4e878d13e3 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs index 72db875d48..73ee830f5c 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs @@ -59,7 +59,13 @@ namespace Microsoft.AspNetCore.Components [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } [Microsoft.AspNetCore.Components.ParameterAttribute] - public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public string Policy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public string Roles { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public object Resource { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { } [System.Diagnostics.DebuggerStepThroughAttribute] protected override System.Threading.Tasks.Task OnParametersSetAsync() { throw null; } diff --git a/src/Components/Components/src/Auth/AuthorizeDataAdapter.cs b/src/Components/Components/src/Auth/AuthorizeDataAdapter.cs new file mode 100644 index 0000000000..3da3e76261 --- /dev/null +++ b/src/Components/Components/src/Auth/AuthorizeDataAdapter.cs @@ -0,0 +1,39 @@ +// 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.Authorization; + +namespace Microsoft.AspNetCore.Components +{ + // This is so the AuthorizeView can avoid implementing IAuthorizeData (even privately) + internal class AuthorizeDataAdapter : IAuthorizeData + { + private readonly AuthorizeView _component; + + public AuthorizeDataAdapter(AuthorizeView component) + { + _component = component ?? throw new ArgumentNullException(nameof(component)); + } + + public string Policy + { + get => _component.Policy; + set => throw new NotSupportedException(); + } + + public string Roles + { + get => _component.Roles; + set => throw new NotSupportedException(); + } + + // AuthorizeView doesn't expose any such parameter, as it wouldn't be used anyway, + // since we already have the ClaimsPrincipal by the time AuthorizeView gets involved. + public string AuthenticationSchemes + { + get => null; + set => throw new NotSupportedException(); + } + } +} diff --git a/src/Components/Components/src/Auth/AuthorizeView.razor b/src/Components/Components/src/Auth/AuthorizeView.razor index ae7b1682d5..0514ba3f9c 100644 --- a/src/Components/Components/src/Auth/AuthorizeView.razor +++ b/src/Components/Components/src/Auth/AuthorizeView.razor @@ -1,20 +1,26 @@ @namespace Microsoft.AspNetCore.Components +@using System.Security.Claims +@using Microsoft.AspNetCore.Authorization +@inject IAuthorizationService AuthorizationService +@inject IAuthorizationPolicyProvider AuthorizationPolicyProvider @if (currentAuthenticationState == null) { @Authorizing } -else if (IsAuthorized()) +else if (isAuthorized) { @((Authorized ?? ChildContent)?.Invoke(currentAuthenticationState)) } else { - @NotAuthorized + @(NotAuthorized?.Invoke(currentAuthenticationState)) } @functions { + private IAuthorizeData[] selfAsAuthorizeData; private AuthenticationState currentAuthenticationState; + private bool isAuthorized; [CascadingParameter] private Task AuthenticationState { get; set; } @@ -26,7 +32,7 @@ else /// /// The content that will be displayed if the user is not authorized. /// - [Parameter] public RenderFragment NotAuthorized { get; private set; } + [Parameter] public RenderFragment NotAuthorized { get; private set; } /// /// The content that will be displayed if the user is authorized. @@ -39,6 +45,29 @@ else /// [Parameter] public RenderFragment Authorizing { get; private set; } + /// + /// The policy name that determines whether the content can be displayed. + /// + [Parameter] public string Policy { get; private set; } + + /// + /// A comma delimited list of roles that are allowed to display the content. + /// + [Parameter] public string Roles { get; private set; } + + /// + /// The resource to which access is being controlled. + /// + [Parameter] public object Resource { get; private set; } + + protected override void OnInit() + { + selfAsAuthorizeData = new[] + { + new AuthorizeDataAdapter((AuthorizeView)(object)this) + }; + } + protected override async Task OnParametersSetAsync() { // We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry @@ -54,15 +83,17 @@ else currentAuthenticationState = null; // Then render in completed state + // Importantly, we *don't* call StateHasChanged between the following async steps, + // otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync currentAuthenticationState = await AuthenticationState; + isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User); } - private bool IsAuthorized() + private async Task IsAuthorizedAsync(ClaimsPrincipal user) { - // TODO: Support various authorization condition parameters, equivalent to those offered - // by the [Authorize] attribute, e.g., "Roles" and "Policy". This is on hold until we're - // able to reference the policy evaluator APIs from this package. - - return currentAuthenticationState.User?.Identity?.IsAuthenticated == true; + var policy = await AuthorizationPolicy.CombineAsync( + AuthorizationPolicyProvider, selfAsAuthorizeData); + var result = await AuthorizationService.AuthorizeAsync(user, Resource, policy); + return result.Succeeded; } } diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 46121def31..abdf16892e 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Components/Components/test/Auth/AuthorizeViewTest.cs b/src/Components/Components/test/Auth/AuthorizeViewTest.cs index 5617c3b32d..60c3f445d6 100644 --- a/src/Components/Components/test/Auth/AuthorizeViewTest.cs +++ b/src/Components/Components/test/Auth/AuthorizeViewTest.cs @@ -2,14 +2,18 @@ // 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.Linq; using System.Security.Claims; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Infrastructure; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Test.Helpers; +using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.AspNetCore.Components @@ -24,7 +28,8 @@ namespace Microsoft.AspNetCore.Components public void RendersNothingIfNotAuthorized() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( childContent: context => builder => builder.AddContent(0, "This should not be rendered")); @@ -36,18 +41,27 @@ namespace Microsoft.AspNetCore.Components // Assert var diff = renderer.Batches.Single().GetComponentDiffs().Single(); Assert.Empty(diff.Edits); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Null(call.user.Identity); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); } [Fact] public void RendersNotAuthorizedContentIfNotAuthorized() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( - childContent: - context => builder => builder.AddContent(0, "This should not be rendered"), notAuthorizedContent: - builder => builder.AddContent(0, "You are not authorized")); + context => builder => builder.AddContent(0, $"You are not authorized, even though we know you are {context.User.Identity.Name}")); + rootComponent.AuthenticationState = CreateAuthenticationState("Nellie"); // Act renderer.AssignRootComponentId(rootComponent); @@ -60,7 +74,16 @@ namespace Microsoft.AspNetCore.Components Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); AssertFrame.Text( renderer.Batches.Single().ReferenceFrames[edit.ReferenceFrameIndex], - "You are not authorized"); + "You are not authorized, even though we know you are Nellie"); + }); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); }); } @@ -68,7 +91,9 @@ namespace Microsoft.AspNetCore.Components public void RendersNothingIfAuthorizedButNoChildContentOrAuthorizedContentProvided() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + authorizationService.NextResult = AuthorizationResult.Success(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView(); rootComponent.AuthenticationState = CreateAuthenticationState("Nellie"); @@ -79,13 +104,24 @@ namespace Microsoft.AspNetCore.Components // Assert var diff = renderer.Batches.Single().GetComponentDiffs().Single(); Assert.Empty(diff.Edits); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); } [Fact] public void RendersChildContentIfAuthorized() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + authorizationService.NextResult = AuthorizationResult.Success(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( childContent: context => builder => builder.AddContent(0, $"You are authenticated as {context.User.Identity.Name}")); @@ -104,13 +140,24 @@ namespace Microsoft.AspNetCore.Components renderer.Batches.Single().ReferenceFrames[edit.ReferenceFrameIndex], "You are authenticated as Nellie"); }); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); } [Fact] public void RendersAuthorizedContentIfAuthorized() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + authorizationService.NextResult = AuthorizationResult.Success(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( authorizedContent: context => builder => builder.AddContent(0, $"You are authenticated as {context.User.Identity.Name}")); @@ -129,13 +176,24 @@ namespace Microsoft.AspNetCore.Components renderer.Batches.Single().ReferenceFrames[edit.ReferenceFrameIndex], "You are authenticated as Nellie"); }); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); } [Fact] public void RespondsToChangeInAuthorizationState() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + authorizationService.NextResult = AuthorizationResult.Success(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( childContent: context => builder => builder.AddContent(0, $"You are authenticated as {context.User.Identity.Name}")); @@ -147,6 +205,7 @@ namespace Microsoft.AspNetCore.Components rootComponent.TriggerRender(); var authorizeViewComponentId = renderer.Batches.Single() .GetComponentFrames().Single().ComponentId; + authorizationService.AuthorizeCalls.Clear(); // Act rootComponent.AuthenticationState = CreateAuthenticationState("Ronaldo"); @@ -164,13 +223,23 @@ namespace Microsoft.AspNetCore.Components batch.ReferenceFrames[edit.ReferenceFrameIndex], "You are authenticated as Ronaldo"); }); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Ronaldo", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); } [Fact] public void ThrowsIfBothChildContentAndAuthorizedContentProvided() { // Arrange - var renderer = new TestRenderer(); + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); var rootComponent = WrapInAuthorizeView( authorizedContent: context => builder => { }, childContent: context => builder => { }); @@ -187,12 +256,12 @@ namespace Microsoft.AspNetCore.Components { // Arrange var @event = new ManualResetEventSlim(); - var renderer = new TestRenderer() - { - OnUpdateDisplayComplete = () => { @event.Set(); }, - }; + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); + renderer.OnUpdateDisplayComplete = () => { @event.Set(); }; var rootComponent = WrapInAuthorizeView( - notAuthorizedContent: builder => builder.AddContent(0, "You are not authorized")); + notAuthorizedContent: + context => builder => builder.AddContent(0, "You are not authorized")); var authTcs = new TaskCompletionSource(); rootComponent.AuthenticationState = authTcs.Task; @@ -228,10 +297,10 @@ namespace Microsoft.AspNetCore.Components { // Arrange var @event = new ManualResetEventSlim(); - var renderer = new TestRenderer() - { - OnUpdateDisplayComplete = () => { @event.Set(); }, - }; + var authorizationService = new TestAuthorizationService(); + authorizationService.NextResult = AuthorizationResult.Success(); + var renderer = CreateTestRenderer(authorizationService); + renderer.OnUpdateDisplayComplete = () => { @event.Set(); }; var rootComponent = WrapInAuthorizeView( authorizingContent: builder => builder.AddContent(0, "Auth pending..."), authorizedContent: context => builder => builder.AddContent(0, $"Hello, {context.User.Identity.Name}!")); @@ -276,13 +345,96 @@ namespace Microsoft.AspNetCore.Components batch2.ReferenceFrames[edit.ReferenceFrameIndex], "Hello, Monsieur!"); }); + + // Assert: The IAuthorizationService was given expected criteria + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Monsieur", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.IsType(req)); + }); + } + + [Fact] + public void IncludesPolicyInAuthorizeCall() + { + // Arrange + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); + var rootComponent = WrapInAuthorizeView(policy: "MyTestPolicy"); + rootComponent.AuthenticationState = CreateAuthenticationState("Nellie"); + + // Act + renderer.AssignRootComponentId(rootComponent); + rootComponent.TriggerRender(); + + // Assert + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, + req => Assert.Equal("MyTestPolicy", ((TestPolicyRequirement)req).PolicyName)); + }); + } + + [Fact] + public void IncludesRolesInAuthorizeCall() + { + // Arrange + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); + var rootComponent = WrapInAuthorizeView(roles: "SuperTestRole1, SuperTestRole2"); + rootComponent.AuthenticationState = CreateAuthenticationState("Nellie"); + + // Act + renderer.AssignRootComponentId(rootComponent); + rootComponent.TriggerRender(); + + // Assert + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Null(call.resource); + Assert.Collection(call.requirements, req => Assert.Equal( + new[] { "SuperTestRole1", "SuperTestRole2" }, + ((RolesAuthorizationRequirement)req).AllowedRoles)); + }); + } + + [Fact] + public void IncludesResourceInAuthorizeCall() + { + // Arrange + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); + var resource = new object(); + var rootComponent = WrapInAuthorizeView(resource: resource); + rootComponent.AuthenticationState = CreateAuthenticationState("Nellie"); + + // Act + renderer.AssignRootComponentId(rootComponent); + rootComponent.TriggerRender(); + + // Assert + Assert.Collection(authorizationService.AuthorizeCalls, call => + { + Assert.Equal("Nellie", call.user.Identity.Name); + Assert.Same(resource, call.resource); + Assert.Collection(call.requirements, req => + Assert.IsType(req)); + }); } private static TestAuthStateProviderComponent WrapInAuthorizeView( RenderFragment childContent = null, RenderFragment authorizedContent = null, - RenderFragment notAuthorizedContent = null, - RenderFragment authorizingContent = null) + RenderFragment notAuthorizedContent = null, + RenderFragment authorizingContent = null, + string policy = null, + string roles = null, + object resource = null) { return new TestAuthStateProviderComponent(builder => { @@ -291,6 +443,9 @@ namespace Microsoft.AspNetCore.Components builder.AddAttribute(2, nameof(AuthorizeView.Authorized), authorizedContent); builder.AddAttribute(3, nameof(AuthorizeView.NotAuthorized), notAuthorizedContent); builder.AddAttribute(4, nameof(AuthorizeView.Authorizing), authorizingContent); + builder.AddAttribute(5, nameof(AuthorizeView.Policy), policy); + builder.AddAttribute(6, nameof(AuthorizeView.Roles), roles); + builder.AddAttribute(7, nameof(AuthorizeView.Resource), resource); builder.CloseComponent(); }); } @@ -311,11 +466,31 @@ namespace Microsoft.AspNetCore.Components { builder.OpenComponent>>(0); builder.AddAttribute(1, nameof(CascadingValue>.Value), AuthenticationState); - builder.AddAttribute(2, RenderTreeBuilder.ChildContent, _childContent); + builder.AddAttribute(2, RenderTreeBuilder.ChildContent, (RenderFragment)(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, RenderTreeBuilder.ChildContent, _childContent); + builder.CloseComponent(); + })); builder.CloseComponent(); } } + // This is useful to show that the reason why a CascadingValue refreshes is because the + // value itself changed, not just that we're re-rendering the entire tree and have to + // recurse into all descendants because we're passing ChildContent + class NeverReRenderComponent : ComponentBase + { + [Parameter] RenderFragment ChildContent { get; set; } + + protected override bool ShouldRender() => false; + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.AddContent(0, ChildContent); + } + } + public static Task CreateAuthenticationState(string username) => Task.FromResult(new AuthenticationState( new ClaimsPrincipal(new TestIdentity { Name = username }))); @@ -328,5 +503,59 @@ namespace Microsoft.AspNetCore.Components public string Name { get; set; } } + + public TestRenderer CreateTestRenderer(IAuthorizationService authorizationService) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(authorizationService); + serviceCollection.AddSingleton(new TestAuthorizationPolicyProvider()); + return new TestRenderer(serviceCollection.BuildServiceProvider()); + } + + private class TestAuthorizationService : IAuthorizationService + { + public AuthorizationResult NextResult { get; set; } + = AuthorizationResult.Failed(); + + public List<(ClaimsPrincipal user, object resource, IEnumerable requirements)> AuthorizeCalls { get; } + = new List<(ClaimsPrincipal user, object resource, IEnumerable requirements)>(); + + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) + { + AuthorizeCalls.Add((user, resource, requirements)); + + // The TestAuthorizationService doesn't actually apply any authorization requirements + // It just returns the specified NextResult, since we're not trying to test the logic + // in DefaultAuthorizationService or similar here. So it's up to tests to set a desired + // NextResult and assert that the expected criteria were passed by inspecting AuthorizeCalls. + return Task.FromResult(NextResult); + } + + public Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) + => throw new NotImplementedException(); + } + + private class TestAuthorizationPolicyProvider : IAuthorizationPolicyProvider + { + private readonly AuthorizationOptions options = new AuthorizationOptions(); + + public Task GetDefaultPolicyAsync() + => Task.FromResult(options.DefaultPolicy); + + public Task GetFallbackPolicyAsync() + => Task.FromResult(options.FallbackPolicy); + + public Task GetPolicyAsync(string policyName) => Task.FromResult( + new AuthorizationPolicy(new[] + { + new TestPolicyRequirement { PolicyName = policyName } + }, + new[] { $"TestScheme:{policyName}" })); + } + + public class TestPolicyRequirement : IAuthorizationRequirement + { + public string PolicyName { get; set; } + } } } diff --git a/src/Components/Shared/test/TestRenderer.cs b/src/Components/Shared/test/TestRenderer.cs index f2a99ccdd4..d838ea007b 100644 --- a/src/Components/Shared/test/TestRenderer.cs +++ b/src/Components/Shared/test/TestRenderer.cs @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers { if (!ShouldHandleExceptions) { - throw exception; + ExceptionDispatchInfo.Capture(exception).Throw(); } HandledExceptions.Add(exception); diff --git a/src/Components/test/E2ETest/Tests/AuthTest.cs b/src/Components/test/E2ETest/Tests/AuthTest.cs index cc3139b6ab..3e9e6ef478 100644 --- a/src/Components/test/E2ETest/Tests/AuthTest.cs +++ b/src/Components/test/E2ETest/Tests/AuthTest.cs @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests [Fact] public void CascadingAuthenticationState_Unauthenticated() { - SignInAs(null); + SignInAs(null, null); var appElement = MountAndNavigateToAuthTest(CascadingAuthenticationStateLink); @@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests [Fact] public void CascadingAuthenticationState_Authenticated() { - SignInAs("someone cool"); + SignInAs("someone cool", null); var appElement = MountAndNavigateToAuthTest(CascadingAuthenticationStateLink); @@ -56,20 +56,58 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests [Fact] public void AuthorizeViewCases_NoAuthorizationRule_Unauthenticated() { - SignInAs(null); - MountAndNavigateToAuthTest(AuthorizeViewCases); + SignInAs(null, null); + var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); WaitUntilExists(By.CssSelector("#no-authorization-rule .not-authorized")); + Browser.Equal("You're not authorized, anonymous", () => + appElement.FindElement(By.CssSelector("#no-authorization-rule .not-authorized")).Text); } [Fact] public void AuthorizeViewCases_NoAuthorizationRule_Authenticated() { - SignInAs("Some User"); + SignInAs("Some User", null); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); Browser.Equal("Welcome, Some User!", () => appElement.FindElement(By.CssSelector("#no-authorization-rule .authorized")).Text); } + [Fact] + public void AuthorizeViewCases_RequireRole_Authenticated() + { + SignInAs("Some User", "IrrelevantRole,TestRole"); + var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); + Browser.Equal("Welcome, Some User!", () => + appElement.FindElement(By.CssSelector("#authorize-role .authorized")).Text); + } + + [Fact] + public void AuthorizeViewCases_RequireRole_Unauthenticated() + { + SignInAs("Some User", "IrrelevantRole"); + var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); + Browser.Equal("You're not authorized, Some User", () => + appElement.FindElement(By.CssSelector("#authorize-role .not-authorized")).Text); + } + + [Fact] + public void AuthorizeViewCases_RequirePolicy_Authenticated() + { + SignInAs("Bert", null); + var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); + Browser.Equal("Welcome, Bert!", () => + appElement.FindElement(By.CssSelector("#authorize-policy .authorized")).Text); + } + + [Fact] + public void AuthorizeViewCases_RequirePolicy_Unauthenticated() + { + SignInAs("Mallory", null); + var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); + Browser.Equal("You're not authorized, Mallory", () => + appElement.FindElement(By.CssSelector("#authorize-policy .not-authorized")).Text); + } + IWebElement MountAndNavigateToAuthTest(string authLinkText) { Navigate(ServerPathBase); @@ -79,12 +117,12 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests return appElement; } - void SignInAs(string usernameOrNull) + void SignInAs(string usernameOrNull, string rolesOrNull) { const string authenticationPageUrl = "/Authentication"; var baseRelativeUri = usernameOrNull == null ? $"{authenticationPageUrl}?signout=true" - : $"{authenticationPageUrl}?username={usernameOrNull}"; + : $"{authenticationPageUrl}?username={usernameOrNull}&roles={rolesOrNull}"; Navigate(baseRelativeUri); WaitUntilExists(By.CssSelector("h1#authentication")); } diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthorizeViewCases.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthorizeViewCases.razor index d78c70f72a..00b39652f2 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthorizeViewCases.razor +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthorizeViewCases.razor @@ -11,7 +11,39 @@

Welcome, @context.User.Identity.Name!

-

You're not logged in.

+

You're not authorized, @(context.User.Identity.Name ?? "anonymous")

+
+
+ + +
+

Scenario: Require role

+ + + +

Authorizing...

+
+ +

Welcome, @context.User.Identity.Name!

+
+ +

You're not authorized, @(context.User.Identity.Name ?? "anonymous")

+
+
+
+ +
+

Scenario: Require policy

+ + + +

Authorizing...

+
+ +

Welcome, @context.User.Identity.Name!

+
+ +

You're not authorized, @(context.User.Identity.Name ?? "anonymous")

diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/ClientSideAuthenticationStateData.cs b/src/Components/test/testassets/BasicTestApp/AuthTest/ClientSideAuthenticationStateData.cs index 15178b5d82..f4845803ac 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/ClientSideAuthenticationStateData.cs +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/ClientSideAuthenticationStateData.cs @@ -12,6 +12,6 @@ namespace BasicTestApp.AuthTest public string UserName { get; set; } - public Dictionary ExposedClaims { get; set; } + public List<(string Type, string Value)> ExposedClaims { get; set; } } } diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/ServerAuthenticationStateProvider.cs b/src/Components/test/testassets/BasicTestApp/AuthTest/ServerAuthenticationStateProvider.cs index 08639f9254..40750c9c9d 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/ServerAuthenticationStateProvider.cs +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/ServerAuthenticationStateProvider.cs @@ -29,7 +29,7 @@ namespace BasicTestApp.AuthTest if (data.IsAuthenticated) { var claims = new[] { new Claim(ClaimTypes.Name, data.UserName) } - .Concat(data.ExposedClaims.Select(c => new Claim(c.Key, c.Value))); + .Concat(data.ExposedClaims.Select(c => new Claim(c.Type, c.Value))); identity = new ClaimsIdentity(claims, "Server authentication"); } else diff --git a/src/Components/test/testassets/BasicTestApp/Startup.cs b/src/Components/test/testassets/BasicTestApp/Startup.cs index 73084e0260..a97b966f69 100644 --- a/src/Components/test/testassets/BasicTestApp/Startup.cs +++ b/src/Components/test/testassets/BasicTestApp/Startup.cs @@ -15,6 +15,12 @@ namespace BasicTestApp public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); + + services.AddAuthorizationCore(options => + { + options.AddPolicy("NameMustStartWithB", policy => + policy.RequireAssertion(ctx => ctx.User.Identity.Name?.StartsWith("B") ?? false)); + }); } public void Configure(IComponentsApplicationBuilder app) diff --git a/src/Components/test/testassets/TestServer/Controllers/UserController.cs b/src/Components/test/testassets/TestServer/Controllers/UserController.cs index 16764e4080..da6a424419 100644 --- a/src/Components/test/testassets/TestServer/Controllers/UserController.cs +++ b/src/Components/test/testassets/TestServer/Controllers/UserController.cs @@ -1,4 +1,6 @@ +using System; using System.Linq; +using System.Security.Claims; using BasicTestApp.AuthTest; using Microsoft.AspNetCore.Mvc; @@ -7,22 +9,28 @@ namespace Components.TestServer.Controllers [Route("api/[controller]")] public class UserController : Controller { + // Servers are not expected to expose everything from the server-side ClaimsPrincipal + // to the client. It's up to the developer to choose what kind of authentication state + // data is needed on the client so it can display suitable options in the UI. + // In this class, we inform the client only about certain roles and certain other claims. + static string[] ExposedRoles = new[] { "IrrelevantRole", "TestRole" }; + // GET api/user [HttpGet] public ClientSideAuthenticationStateData Get() { - // Servers are not expected to expose everything from the server-side ClaimsPrincipal - // to the client. It's up to the developer to choose what kind of authentication state - // data is needed on the client so it can display suitable options in the UI. - return new ClientSideAuthenticationStateData { IsAuthenticated = User.Identity.IsAuthenticated, UserName = User.Identity.Name, ExposedClaims = User.Claims - .Where(c => c.Type == "test-claim") - .ToDictionary(c => c.Type, c => c.Value) + .Where(c => c.Type == "test-claim" || IsExposedRole(c)) + .Select(c => (c.Type, c.Value)).ToList() }; } + + private bool IsExposedRole(Claim claim) + => claim.Type == ClaimTypes.Role + && ExposedRoles.Contains(claim.Value); } } diff --git a/src/Components/test/testassets/TestServer/Pages/Authentication.cshtml b/src/Components/test/testassets/TestServer/Pages/Authentication.cshtml index 3f6cbdefd5..165618a019 100644 --- a/src/Components/test/testassets/TestServer/Pages/Authentication.cshtml +++ b/src/Components/test/testassets/TestServer/Pages/Authentication.cshtml @@ -26,6 +26,10 @@ User name:

+

+ Roles: + +

@@ -37,7 +41,11 @@

Authenticated: @User.Identity.IsAuthenticated Username: @User.Identity.Name -

+ Roles: + + @string.Join(", ", User.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray()) + +

foreach Sign out @@ -61,6 +69,15 @@ new Claim("test-claim", "Test claim value"), }; + var roles = Request.Query["roles"]; + if (!string.IsNullOrEmpty(roles)) + { + foreach (var role in roles.ToString().Split(',')) + { + claims.Add(new Claim(ClaimTypes.Role, role)); + } + } + await HttpContext.SignInAsync( new ClaimsPrincipal(new ClaimsIdentity(claims, "FakeAuthenticationType"))); diff --git a/src/Components/test/testassets/TestServer/Startup.cs b/src/Components/test/testassets/TestServer/Startup.cs index 26da1af8e0..0075a8dfac 100644 --- a/src/Components/test/testassets/TestServer/Startup.cs +++ b/src/Components/test/testassets/TestServer/Startup.cs @@ -30,6 +30,12 @@ namespace TestServer }); services.AddServerSideBlazor(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); + + services.AddAuthorization(options => + { + options.AddPolicy("NameMustStartWithB", policy => + policy.RequireAssertion(ctx => ctx.User.Identity.Name?.StartsWith("B") ?? false)); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From 3e47c72de3ab3420a80c9689a2980e1a88cb8c98 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 24 May 2019 07:47:31 -0700 Subject: [PATCH 13/95] Update Microsoft.AspNetCore.Blazor.Mono to 0.10.0-preview6.19273.9 and automatically flow updates (#10512) --- build/sources.props | 6 +----- eng/Version.Details.xml | 4 ++++ eng/Versions.props | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/sources.props b/build/sources.props index b561688d1f..decee683d3 100644 --- a/build/sources.props +++ b/build/sources.props @@ -10,6 +10,7 @@ $(RestoreSources); + https://dotnetfeed.blob.core.windows.net/aspnet-blazor/index.json; https://dotnetfeed.blob.core.windows.net/aspnet-extensions/index.json; https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json; https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json; @@ -28,11 +29,6 @@ $(RestoreSources); https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; - - - $(RestoreSources); - https://dotnet.myget.org/F/blazor-dev/api/v3/index.json; - https://dotnetcli.blob.core.windows.net/dotnet/ diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0bfad6512e..28ee92afa5 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,6 +9,10 @@ --> + + https://github.com/aspnet/Blazor + c879c3a911b4c2d6cccd4d6ff2de86a6949cda88 + https://github.com/aspnet/AspNetCore-Tooling 5bcd00877984c63293665f75bad20f522132638a diff --git a/eng/Versions.props b/eng/Versions.props index 7fdd2d0d75..1776c1e4ab 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -47,6 +47,8 @@ 4.6.0-preview6.19270.12 3.0.0-preview6.19270.12 + + 0.10.0-preview6.19273.9 3.0.0-preview6.19271.2 3.0.0-preview6.19271.2 @@ -169,8 +171,6 @@ 11.1.0 1.4.0 5.3.0 - - 0.10.0-preview-20190523.1 2.1.1 2.2.0 From 3be11f6544485948ba17646ffa7b4242c2c5339a Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Fri, 24 May 2019 08:16:54 -0700 Subject: [PATCH 14/95] Increase stack size for IIS Inprocess (#10511) --- .../CommonLib/ConfigurationSection.h | 1 + .../InProcessOptions.cpp | 1 + .../InProcessOptions.h | 7 ++++++ .../inprocessapplication.cpp | 1 + .../Inprocess/StartupTests.cs | 22 ++++++++++++++++++ .../testassets/InProcessWebSite/Startup.cs | 23 +++++++++++++++++++ 6 files changed, 55 insertions(+) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index 3eab02bc7c..446f8c7d4b 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -29,6 +29,7 @@ #define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage" #define CS_ENABLED L"enabled" #define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook" +#define CS_ASPNETCORE_HANDLER_STACK_SIZE L"stackSize" class ConfigurationSection: NonCopyable { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index d0e264e0f8..01ec10f6f6 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -59,6 +59,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS); m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true"); m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true"); + m_strStackSize = find_element(handlerSettings, CS_ASPNETCORE_HANDLER_STACK_SIZE).value_or(L"1048576"); m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000; m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h index 6cb8fcceef..61a79664b2 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h @@ -112,6 +112,12 @@ public: return m_bindingInformation; } + std::wstring + QueryStackSize() const + { + return m_strStackSize; + } + InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite); static @@ -125,6 +131,7 @@ private: std::wstring m_strArguments; std::wstring m_strProcessPath; std::wstring m_struStdoutLogFile; + std::wstring m_strStackSize; bool m_fStdoutLogEnabled; bool m_fDisableStartUpErrorPage; bool m_fSetCurrentDirectory; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index afb60a8892..aa90367734 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -253,6 +253,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication() // Used to make .NET Runtime always log to event log when there is an unhandled exception. LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1")); + LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_DefaultStackSize", m_pConfig->QueryStackSize().c_str())); bool clrThreadExited; { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 009ecd8319..4374ba519a 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -673,6 +673,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests VerifyDotnetRuntimeEventLog(deploymentResult); } + [ConditionalFact] + [RequiresNewHandler] + public async Task StackOverflowIsAvoidedBySettingLargerStack() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StackSize"); + Assert.True(result.IsSuccessStatusCode); + } + + [ConditionalFact] + [RequiresNewHandler] + public async Task StackOverflowCanBeSetBySettingLargerStackViaHandlerSetting() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.HandlerSettings["stackSize"] = "10000000"; + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StackSizeLarge"); + Assert.True(result.IsSuccessStatusCode); + } + private static void VerifyDotnetRuntimeEventLog(IISDeploymentResult deploymentResult) { var entries = GetEventLogsFromDotnetRuntime(deploymentResult); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index 110ac5e625..656fe0614d 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -696,6 +696,29 @@ namespace TestSite await server.StopAsync(cts.Token); } + private async Task StackSize(HttpContext ctx) + { + // This would normally stackoverflow if we didn't increase the stack size per thread. + RecursiveFunction(10000); + await ctx.Response.WriteAsync("Hello World"); + } + + private async Task StackSizeLarge(HttpContext ctx) + { + // This would normally stackoverflow if we didn't increase the stack size per thread. + RecursiveFunction(30000); + await ctx.Response.WriteAsync("Hello World"); + } + + private void RecursiveFunction(int i) + { + if (i == 0) + { + return; + } + RecursiveFunction(i - 1); + } + private async Task GetServerVariableStress(HttpContext ctx) { // This test simulates the scenario where native Flush call is being From 54fff2b1681cec89bad5089d61ae5fe54791fb1d Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 24 May 2019 18:10:52 +0100 Subject: [PATCH 15/95] Server-side Blazor template with authentication (#10444) --- ...etCore.Components.netstandard2.0.Manual.cs | 2 +- .../RazorComponentsWeb-CSharp.csproj.in | 23 ++ .../.template.config/dotnetcli.host.json | 48 +++- .../.template.config/template.json | 207 ++++++++++++++- .../.template.config/vs-2017.3.host.json | 33 +++ .../RazorComponentsWeb-CSharp/App.razor | 13 +- .../Identity/Pages/Account/LogOut.cshtml | 14 ++ .../Pages/Shared/_LoginPartial.cshtml | 25 ++ .../Data/ApplicationDbContext.cs | 16 ++ ...000000000_CreateIdentitySchema.Designer.cs | 229 +++++++++++++++++ .../00000000000000_CreateIdentitySchema.cs | 217 ++++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 227 +++++++++++++++++ ...000000000_CreateIdentitySchema.Designer.cs | 236 ++++++++++++++++++ .../00000000000000_CreateIdentitySchema.cs | 220 ++++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 234 +++++++++++++++++ .../Shared/LoginDisplay.razor | 57 +++++ .../Shared/MainLayout.razor | 5 +- .../RazorComponentsWeb-CSharp/Startup.cs | 121 ++++++++- .../content/RazorComponentsWeb-CSharp/app.db | Bin 0 -> 106496 bytes .../appsettings.json | 32 +++ .../wwwroot/css/site.css | 5 + .../test/RazorComponentsTemplateTest.cs | 54 +++- .../test/template-baselines.json | 221 ++++++++++++++-- 23 files changed, 2198 insertions(+), 41 deletions(-) create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Account/LogOut.cshtml create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/ApplicationDbContext.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor create mode 100644 src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/app.db diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs index 73ee830f5c..41b4d9a028 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs @@ -250,7 +250,7 @@ namespace Microsoft.AspNetCore.Components.Routing [Microsoft.AspNetCore.Components.ParameterAttribute] public System.Reflection.Assembly AppAssembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] - public System.Type FallbackComponent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} + public Microsoft.AspNetCore.Components.RenderFragment NotFoundContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} public void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } public void Dispose() { } protected virtual void Render(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder, System.Type handler, System.Collections.Generic.IDictionary parameters) { } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in index a5d1fdc23a..9b6510bf6f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in @@ -3,8 +3,31 @@ netcoreapp3.0 7.3 + aspnet-RazorComponentsWeb_CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502 + 0 + 1 True RazorComponentsWeb_CSharp + + + + + + + + + + + + + + + + + + + + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/dotnetcli.host.json index 79c2dc39e3..2473827568 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/dotnetcli.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/dotnetcli.host.json @@ -1,6 +1,49 @@ { "$schema": "http://json.schemastore.org/dotnetcli.host", "symbolInfo": { + "UseLocalDB": { + "longName": "use-local-db" + }, + "AADInstance": { + "longName": "aad-instance", + "shortName": "" + }, + "AAdB2CInstance": { + "longName": "aad-b2c-instance", + "shortName": "" + }, + "SignUpSignInPolicyId": { + "longName": "susi-policy-id", + "shortName": "ssp" + }, + "ResetPasswordPolicyId": { + "longName": "reset-password-policy-id", + "shortName": "rp" + }, + "EditProfilePolicyId": { + "longName": "edit-profile-policy-id", + "shortName": "ep" + }, + "OrgReadAccess": { + "longName": "org-read-access", + "shortName": "r" + }, + "ClientId": { + "longName": "client-id", + "shortName": "" + }, + "CallbackPath": { + "longName": "callback-path", + "shortName": "" + }, + "Domain": { + "longName": "domain", + "shortName": "" + }, + "TenantId": { + "longName": "tenant-id", + "shortName": "" + }, "Framework": { "longName": "framework" }, @@ -18,12 +61,15 @@ "longName": "exclude-launch-settings", "shortName": "" }, + "UserSecretsId": { + "isHidden": true + }, "NoHttps": { "longName": "no-https", "shortName": "" } }, "usageExamples": [ - "" + "--auth Individual" ] } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/template.json index 5e1f178915..106e9fcc39 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/template.json @@ -12,25 +12,174 @@ "precedence": "5000", "identity": "Microsoft.Web.RazorComponents.CSharp.3.0", "shortName": "blazorserverside", + "thirdPartyNotices": "https://aka.ms/aspnetcore-template-3pn-210", "tags": { "language": "C#", "type": "project" }, "sourceName": "RazorComponentsWeb-CSharp", "preferNameDirectory": true, + "guids": [ + "09732173-2cef-46b7-83db-1334bcb079d3", // Tenant ID + "53bc9b9d-9d6a-45d4-8429-2a2761773502" // Client ID + ], "sources": [ { "modifiers": [ + { + "condition": "(!IndividualLocalAuth || UseLocalDB)", + "exclude": [ + "app.db" + ] + }, + { + "condition": "(!IndividualLocalAuth)", + "exclude": [ + "Data/SqlLite/**", + "Data/SqlServer/**", + "Data/ApplicationDbContext.cs", + "Areas/**" + ] + }, { "condition": "(ExcludeLaunchSettings)", "exclude": [ "Properties/launchSettings.json" ] + }, + { + "condition": "(IndividualLocalAuth && UseLocalDB)", + "rename": { + "Data/SqlServer/": "Data/Migrations/" + }, + "exclude": [ + "Data/SqlLite/**" + ] + }, + { + "condition": "(IndividualLocalAuth && !UseLocalDB)", + "rename": { + "Data/SqlLite/": "Data/Migrations/" + }, + "exclude": [ + "Data/SqlServer/**" + ] + }, + { + "condition": "(NoAuth)", + "exclude": [ + "Shared/LoginDisplay.razor" + ] } ] } ], "symbols": { + "auth": { + "type": "parameter", + "datatype": "choice", + "choices": [ + { + "choice": "None", + "description": "No authentication" + }, + { + "choice": "Individual", + "description": "Individual authentication" + }, + { + "choice": "IndividualB2C", + "description": "Individual authentication with Azure AD B2C" + }, + { + "choice": "SingleOrg", + "description": "Organizational authentication for a single tenant" + }, + { + "choice": "MultiOrg", + "description": "Organizational authentication for multiple tenants" + }, + { + "choice": "Windows", + "description": "Windows authentication" + } + ], + "defaultValue": "None", + "description": "The type of authentication to use" + }, + "AAdB2CInstance": { + "type": "parameter", + "datatype": "string", + "defaultValue": "https://login.microsoftonline.com/tfp/", + "replaces": "https:////login.microsoftonline.com/tfp/", + "description": "The Azure Active Directory B2C instance to connect to (use with IndividualB2C auth)." + }, + "SignUpSignInPolicyId": { + "type": "parameter", + "datatype": "string", + "defaultValue": "", + "replaces": "MySignUpSignInPolicyId", + "description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)." + }, + "ResetPasswordPolicyId": { + "type": "parameter", + "datatype": "string", + "defaultValue": "", + "replaces": "MyResetPasswordPolicyId", + "description": "The reset password policy ID for this project (use with IndividualB2C auth)." + }, + "EditProfilePolicyId": { + "type": "parameter", + "datatype": "string", + "defaultValue": "", + "replaces": "MyEditProfilePolicyId", + "description": "The edit profile policy ID for this project (use with IndividualB2C auth)." + }, + "AADInstance": { + "type": "parameter", + "datatype": "string", + "defaultValue": "https://login.microsoftonline.com/", + "replaces": "https:////login.microsoftonline.com/", + "description": "The Azure Active Directory instance to connect to (use with SingleOrg or MultiOrg auth)." + }, + "ClientId": { + "type": "parameter", + "datatype": "string", + "replaces": "11111111-1111-1111-11111111111111111", + "description": "The Client ID for this project (use with IndividualB2C, SingleOrg or MultiOrg auth)." + }, + "Domain": { + "type": "parameter", + "datatype": "string", + "replaces": "qualified.domain.name", + "description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth)." + }, + "TenantId": { + "type": "parameter", + "datatype": "string", + "replaces": "22222222-2222-2222-2222-222222222222", + "description": "The TenantId ID of the directory to connect to (use with SingleOrg auth)." + }, + "CallbackPath": { + "type": "parameter", + "datatype": "string", + "replaces": "/signin-oidc", + "defaultValue": "/signin-oidc", + "description": "The request path within the application's base path of the redirect URI (use with SingleOrg or IndividualB2C auth)." + }, + "OrgReadAccess": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether or not to allow this application read access to the directory (only applies to SingleOrg or MultiOrg auth)." + }, + "UserSecretsId": { + "type": "parameter", + "datatype": "string", + "replaces": "aspnet-RazorComponentsWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "defaultValue": "aspnet-RazorComponentsWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "description": "The ID to use for secrets (use with OrgReadAccess or Individual auth)." + }, "ExcludeLaunchSettings": { "type": "parameter", "datatype": "bool", @@ -77,6 +226,54 @@ }, "replaces": "44300" }, + "OrganizationalAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\" || auth == \"MultiOrg\")" + }, + "WindowsAuth": { + "type": "computed", + "value": "(auth == \"Windows\")" + }, + "MultiOrgAuth": { + "type": "computed", + "value": "(auth == \"MultiOrg\")" + }, + "SingleOrgAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\")" + }, + "IndividualLocalAuth": { + "type": "computed", + "value": "(auth == \"Individual\")" + }, + "IndividualAuth": { + "type": "computed", + "value": "(auth == \"Individual\" || auth == \"IndividualB2C\")" + }, + "IndividualB2CAuth": { + "type": "computed", + "value": "(auth == \"IndividualB2C\")" + }, + "NoAuth": { + "type": "computed", + "value": "(!(IndividualAuth || OrganizationalAuth || WindowsAuth))" + }, + "RequiresHttps": { + "type": "computed", + "value": "(OrganizationalAuth || IndividualAuth || !NoHttps)" + }, + "NoHttps": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth." + }, + "UseLocalDB": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified." + }, "Framework": { "type": "parameter", "description": "The target framework for the project.", @@ -103,16 +300,6 @@ "datatype": "bool", "description": "If specified, skips the automatic restore of the project on create.", "defaultValue": "false" - }, - "RequiresHttps": { - "type": "computed", - "value": "(!NoHttps)" - }, - "NoHttps": { - "type": "parameter", - "datatype": "bool", - "defaultValue": "false", - "description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth." } }, "primaryOutputs": [ diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/vs-2017.3.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/vs-2017.3.host.json index d7c0347fe3..a41e7e76d7 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/vs-2017.3.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/.template.config/vs-2017.3.host.json @@ -16,6 +16,38 @@ "uiFilters": [ "oneaspnet" ], + "usesOidc": true, + "supportedAuthentications": [ + { + "auth": "None", + "authenticationType": "NoAuth", + "allowUnsecured": true + }, + { + "auth": "Individual", + "authenticationType": "IndividualAuth", + "b2cAuthenticationOptions": "CloudExisting" + }, + { + "auth": "Individual", + "authenticationType": "IndividualAuth", + "b2cAuthenticationOptions": "Local" + }, + { + "auth": "SingleOrg", + "authenticationType": "OrgAuth", + "orgAuthenticationOptions": "SSO" + }, + { + "auth": "MultiOrg", + "authenticationType": "OrgAuth", + "orgAuthenticationOptions": "MultiOrg" + }, + { + "auth": "Windows", + "authenticationType": "WindowsAuth" + } + ], "ports": [ { "name": "HttpPort", @@ -27,6 +59,7 @@ } ], "excludeLaunchSettings": false, + "azureReplyUrlPortName": "HttpsPort", "minFullFrameworkVersion": "4.6.1", "disableHttpsSymbol": "NoHttps" } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/App.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/App.razor index 03eee81cef..844a2b2455 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/App.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/App.razor @@ -1,5 +1,8 @@ -@* - The Router component displays whichever component has a @page - directive matching the current URI. -*@ - + + + +

Page not found

+

Sorry, there's nothing at this address.

+
+
+
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Account/LogOut.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Account/LogOut.cshtml new file mode 100644 index 0000000000..4ea81fc052 --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Account/LogOut.cshtml @@ -0,0 +1,14 @@ +@page +@using Microsoft.AspNetCore.Identity +@inject SignInManager SignInManager +@functions { + public async Task OnGet() + { + if (SignInManager.IsSignedIn(User)) + { + await SignInManager.SignOutAsync(); + } + + return Redirect("~/"); + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml new file mode 100644 index 0000000000..e55bafe6a7 --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml @@ -0,0 +1,25 @@ +@using Microsoft.AspNetCore.Identity +@inject SignInManager SignInManager +@inject UserManager UserManager +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/ApplicationDbContext.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/ApplicationDbContext.cs new file mode 100644 index 0000000000..6a343d68c3 --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/ApplicationDbContext.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace RazorComponentsWeb_CSharp.Data +{ + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..7366f582d3 --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,229 @@ +// +using System; +using RazorComponentsWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..3fb498bc9a --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,217 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 256, nullable: true), + NormalizedName = table.Column(maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(nullable: false), + UserName = table.Column(maxLength: 256, nullable: true), + NormalizedUserName = table.Column(maxLength: 256, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false), + PasswordHash = table.Column(nullable: true), + SecurityStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + TwoFactorEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(maxLength: 128, nullable: false), + ProviderKey = table.Column(maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 128, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..b3309a77ea --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,227 @@ +// +using System; +using RazorComponentsWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..784684d257 --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,236 @@ +// +using System; +using RazorComponentsWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..d7cf2c926d --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,220 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 256, nullable: true), + NormalizedName = table.Column(maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(nullable: false), + UserName = table.Column(maxLength: 256, nullable: true), + NormalizedUserName = table.Column(maxLength: 256, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false), + PasswordHash = table.Column(nullable: true), + SecurityStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + TwoFactorEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + UserId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(maxLength: 128, nullable: false), + ProviderKey = table.Column(maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 128, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..2f6c07f6ae --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,234 @@ +// +using System; +using RazorComponentsWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace RazorComponentsWeb_CSharp.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-preview1") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor new file mode 100644 index 0000000000..6ccbbd20ae --- /dev/null +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor @@ -0,0 +1,57 @@ +@*#if (IndividualLocalAuth) + + + Hello, @context.User.Identity.Name! + Log out + + + Register + Log in + + +#elseif (IndividualB2CAuth) +@using Microsoft.AspNetCore.Authentication.AzureADB2C.UI +@using Microsoft.Extensions.Options +@inject IOptionsMonitor AzureADB2COptions + + + + @if (canEditProfile) + { + Hello, @context.User.Identity.Name! + } + else + { + Hello, @context.User.Identity.Name! + } + Log out + + + Log in + + + +@functions { + bool canEditProfile; + + protected override void OnInit() + { + var options = AzureADB2COptions.Get(AzureADB2CDefaults.AuthenticationScheme); + canEditProfile = !string.IsNullOrEmpty(options.EditProfilePolicyId); + } +} +#elseif (OrganizationalAuth) + + + Hello, @context.User.Identity.Name! + Log out + + + Log in + + +#elseif (WindowsAuth) + + Hello, @context.User.Identity.Name! + +#endif*@ diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/MainLayout.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/MainLayout.razor index 97e764d353..e2c2d6500e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/MainLayout.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/MainLayout.razor @@ -6,7 +6,10 @@
- About +@*#if (!NoAuth) + +#endif*@ + About
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs index ae6a05a3bb..828c349c43 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs @@ -2,24 +2,125 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +#if (OrganizationalAuth || IndividualB2CAuth) +using Microsoft.AspNetCore.Authentication; +#endif +#if (OrganizationalAuth) +using Microsoft.AspNetCore.Authentication.AzureAD.UI; +#if (MultiOrgAuth) +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +#endif +using Microsoft.AspNetCore.Authorization; +#endif +#if (IndividualB2CAuth) +using Microsoft.AspNetCore.Authentication.AzureADB2C.UI; +#endif using Microsoft.AspNetCore.Builder; +#if (IndividualLocalAuth) +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI; +#endif using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; #if (RequiresHttps) using Microsoft.AspNetCore.HttpsPolicy; #endif +#if (OrganizationalAuth) +using Microsoft.AspNetCore.Mvc.Authorization; +#endif +#if (IndividualLocalAuth) +using Microsoft.EntityFrameworkCore; +#endif +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +#if(MultiOrgAuth) +using Microsoft.IdentityModel.Tokens; +#endif using RazorComponentsWeb_CSharp.Data; namespace RazorComponentsWeb_CSharp { public class Startup { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { +#if (IndividualLocalAuth) + services.AddDbContext(options => +#if (UseLocalDB) + options.UseSqlServer( + Configuration.GetConnectionString("DefaultConnection"))); +#else + options.UseSqlite( + Configuration.GetConnectionString("DefaultConnection"))); +#endif + services.AddDefaultIdentity() + .AddEntityFrameworkStores(); +#elif (OrganizationalAuth) + services.AddAuthentication(AzureADDefaults.AuthenticationScheme) + .AddAzureAD(options => Configuration.Bind("AzureAd", options)); +#if (MultiOrgAuth) + + services.Configure(AzureADDefaults.OpenIdScheme, options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + // Instead of using the default validation (validating against a single issuer value, as we do in + // line of business apps), we inject our own multitenant validation logic + ValidateIssuer = false, + + // If the app is meant to be accessed by entire organizations, add your issuer validation logic here. + //IssuerValidator = (issuer, securityToken, validationParameters) => { + // if (myIssuerValidationLogic(issuer)) return issuer; + //} + }; + + options.Events = new OpenIdConnectEvents + { + OnTicketReceived = context => + { + // If your authentication logic is based on users then add your logic here + return Task.CompletedTask; + }, + OnAuthenticationFailed = context => + { + context.Response.Redirect("/Error"); + context.HandleResponse(); // Suppress the exception + return Task.CompletedTask; + }, + // If your application needs to authenticate single users, add your user validation below. + //OnTokenValidated = context => + //{ + // return myUserValidationLogic(context.Ticket.Principal); + //} + }; + }); +#endif + +#elif (IndividualB2CAuth) + services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme) + .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options)); + +#endif +#if (OrganizationalAuth) + services.AddControllersWithViews(options => + { + var policy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + options.Filters.Add(new AuthorizeFilter(policy)); + }) + .AddNewtonsoftJson(); + +#endif services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton(); @@ -31,25 +132,37 @@ namespace RazorComponentsWeb_CSharp if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); +#if (IndividualLocalAuth) + app.UseDatabaseErrorPage(); +#endif } -#if (RequiresHttps) else { + app.UseExceptionHandler("/Home/Error"); +#if (RequiresHttps) // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } -#endif -#if (RequiresHttps) app.UseHttpsRedirection(); +#else + } #endif app.UseStaticFiles(); app.UseRouting(); +#if (OrganizationalAuth || IndividualAuth) + app.UseAuthentication(); + app.UseAuthorization(); + +#endif app.UseEndpoints(endpoints => { +#if (OrganizationalAuth || IndividualAuth) + endpoints.MapControllers(); +#endif endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/app.db b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/app.db new file mode 100644 index 0000000000000000000000000000000000000000..ec163057f1f5433ac77fa2fea05aa4a544e6ded0 GIT binary patch literal 106496 zcmeI)UvJyi83$m|b|g!-Eay+dt-w&02G$6+i)2IL6- zL`S5Yx5y532 zGl}(oKf6FT&iz2Q`C0CVrLPtbvtOpaUwnW5^ZB*p^SN)6y_v7)p3JDicS0fY&A6^& z)(?csDY39F4Bj@oE#nh)zfrIcHN)Anjb7QhZ+7hleXF*jtFIKRN};Yus4(^0Ba74@d(_3Xu%m#RuhsVZ7gsrjetMRH9h=a8q>yrq;CQu@V0tytJn zUM2!s@_seT5+8v-9AYAHccZzCk06`Le~?Rxw^xOPTRQfmHkpwcdf&0!<)~`oMkLG2 zr>l!8aeZBQnvT>2n*)N)*>ZMVRj}ruEZpVYo^@olj9z>J$bR#-aXev*Ez>@1>&I>x zyT#-em`C?=1?I=fR~R2LDYS5S`ANBEeTKYWNwPPyNwKy*IoVz`p=`4?OD9JQZn~em z6-hUn>VkB$<;8L79?qlQK&OM8jAZ4eQXNhLQg~=`Bi!FRoPfe!y`pO5csq(#_tWcc zSU)~ASbcD-@96D*Wc_)oftx2-Ft0juyO|`NdJkN7Z^{=OJN#92AUW*d zLFjK-YiRw>UH|IiA1~B!j!HyNRUcbUjw?ad9E|w0 zrcIf@EXIyUIY`WK^7pw%XX)_h<*o{TSfN7aKKSvGkPEbX*gWyvOrGzu$c_+3WouFc zZPp#qC(V#7ol!hg_w8Jqr`r-xj!aqcL)zObbf&)7+2b^T_O}m4NYF+rY;JOexk30C z?!tw^zK{|(HiVOk>(FQ@r5*ErPj^hKYu_?$QtijFD`$9{*lhx(VV@JZ#&i?|M?S5! z`c3B!`It?Dja)d0h&q!LKi(KO=330u|DWOn@&oRyr0uX=z1Rwwb2tWV=5P$##Ab{`xr~wE-00Izz00bZa0SG_<0uX?} z^b6qo|Mce=X9xiZKmY;|fB*y_009U<00I!e_kYv?1Rwwb2tWV=5P$##AOHafKw$a> z@cn=KbBr^D00bZa0SG_<0uX=z1Rwwb2xN0F5(~-aiKV~i{+j*U(pQUz*)P-IFTOwj z`TSb)`P{e3-pto?Pi9o%JE4&HChT&V?2G&Xxy_=-uxa<2 z)$8bO^HZZmyOZ;|7jv`X>Zx#BYF^J?jCrZ5l$5HX6_uKQy6$dNlAJ@H4yY}qtdPN1EYymHE#+k* zpe65Dvn=ru_`@M40(ZCZ5o9y@4{}NI_NtI@OUHiHHXWm(_Z`b!j;c0pM6$e`dm;Qw zTwfQSrXw}MMr*J+Th5NFis9h)GVd<$_N*hbW%S|;K=zxrjpGShY?=09TR(Qo*exc% zz&yH_D=oes2N|L>qO^UVk$;tMj31yqDSvomdNYQjZc`K4` zHq`~`X3LA?(mk9!z-PDZkFQ>hLo0VzB*xe@N~9Zo=DuU=6#a=aZytNZD7H>@8Y z8mvCJ)pzuEKeGP3)xgaYESOiFx!p{XPQ3>%yEja2<5Az}HjT*Q0*4-Hyw)XC`5M_X ze{z%_O(mVN*a@*z2Fn?)SMQTS&`jo-mriAP@1HuAwD0p=IfkhWf_bHLKACa9*i5Cw zl@;Nc5*b5wFnsL!IBtJ74@q;E&G93KT+c(o{x~Q!N%7bv-E14JqSfy@ql3`jv}kpk z{a%lh+i}g&JBR#$QaYrMm{nx2duFd=#Efpgs%e&?=#_3@7vYB(FQ@r5*ErPj^hKYu_?$QtijFD`$9{*lhx(VV@JZ#&i?|M?S5!`c3B!`It?D zWs||M8i|NHlN3MR7&qo(kJGpjyLy&OzIRBf(Qg!L#;%^uJv+(DIOPN1qGU-327w7*%8t{Su1Rwwb2tWV=p1`5V z9`{;tpV9H}4u&50qO0sTy;~}KntFS0_`n~xb@-_-cOP<#CG^ynzpb4A!_}1dHhI7- z?H3?oO*qDLjiAeG-z6YjRKcb$u4|M?ao-y1M8-;en?yM;`Y0 zKYSV!_y6&c4gwH>00bZa0SG_<0uX=z1RyZ|0{H$v{W-=NLI45~fB*y_009U<00Izz H00jOE{cSD9 literal 0 HcmV?d00001 diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/appsettings.json index d9d9a9bff6..a1b1d1c212 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/appsettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/appsettings.json @@ -1,4 +1,36 @@ { +////#if (IndividualB2CAuth) +// "AzureAdB2C": { +// "Instance": "https:////login.microsoftonline.com/tfp/", +// "ClientId": "11111111-1111-1111-11111111111111111", +// "CallbackPath": "/signin-oidc", +// "Domain": "qualified.domain.name", +// "SignUpSignInPolicyId": "MySignUpSignInPolicyId", +// "ResetPasswordPolicyId": "MyResetPasswordPolicyId", +// "EditProfilePolicyId": "MyEditProfilePolicyId" +// }, +////#elseif (OrganizationalAuth) +// "AzureAd": { +//#if (MultiOrgAuth) +// "Instance": "https:////login.microsoftonline.com/common", +//#elseif (SingleOrgAuth) +// "Instance": "https:////login.microsoftonline.com/", +// "Domain": "qualified.domain.name", +// "TenantId": "22222222-2222-2222-2222-222222222222", +//#endif +// "ClientId": "11111111-1111-1111-11111111111111111", +// "CallbackPath": "/signin-oidc" +// }, +//#endif +////#if (IndividualLocalAuth) +// "ConnectionStrings": { +////#if (UseLocalDB) +// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-RazorComponentsWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true" +////#else +// "DefaultConnection": "DataSource=app.db" +//#endif +// }, +//#endif "Logging": { "LogLevel": { "Default": "Information", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/wwwroot/css/site.css b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/wwwroot/css/site.css index 222f33c61d..546810a185 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/wwwroot/css/site.css +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/wwwroot/css/site.css @@ -27,8 +27,13 @@ app { .main .top-row { background-color: #e6e6e6; border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; } + .main .top-row > a { + margin-left: 1.5rem; + } + .sidebar { background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); } diff --git a/src/ProjectTemplates/test/RazorComponentsTemplateTest.cs b/src/ProjectTemplates/test/RazorComponentsTemplateTest.cs index 9477d08134..b8c8e34ace 100644 --- a/src/ProjectTemplates/test/RazorComponentsTemplateTest.cs +++ b/src/ProjectTemplates/test/RazorComponentsTemplateTest.cs @@ -27,9 +27,9 @@ namespace Templates.Test [Fact] [Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2407", FlakyOn.AzP.Windows)] - public async Task RazorComponentsTemplateWorks() + public async Task RazorComponentsTemplateWorks_NoAuth() { - Project = await ProjectFactory.GetOrCreateProject("blazorserverside", Output); + Project = await ProjectFactory.GetOrCreateProject("blazorserversidenoauth", Output); var createResult = await Project.RunDotNetNewAsync("blazorserverside"); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); @@ -73,6 +73,56 @@ namespace Templates.Test } } + [Theory] + [InlineData(true)] + [InlineData(false)] + [Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2407", FlakyOn.AzP.Windows)] + public async Task RazorComponentsTemplateWorks_IndividualAuth(bool useLocalDB) + { + Project = await ProjectFactory.GetOrCreateProject("blazorserversideindividual" + (useLocalDB ? "uld" : ""), Output); + + var createResult = await Project.RunDotNetNewAsync("blazorserverside", auth: "Individual", useLocalDB: useLocalDB); + Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); + + var publishResult = await Project.RunDotNetPublishAsync(); + Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult)); + + // Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release + // The output from publish will go into bin/Release/netcoreapp3.0/publish and won't be affected by calling build + // later, while the opposite is not true. + + var buildResult = await Project.RunDotNetBuildAsync(); + Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult)); + + using (var aspNetProcess = Project.StartBuiltProjectAsync()) + { + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + + await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); + if (BrowserFixture.IsHostAutomationSupported()) + { + aspNetProcess.VisitInBrowser(Browser); + TestBasicNavigation(); + } + } + + using (var aspNetProcess = Project.StartPublishedProjectAsync()) + { + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process)); + + await aspNetProcess.AssertStatusCode("/", HttpStatusCode.OK, "text/html"); + if (BrowserFixture.IsHostAutomationSupported()) + { + aspNetProcess.VisitInBrowser(Browser); + TestBasicNavigation(); + } + } + } + private void TestBasicNavigation() { // Give components.server enough time to load so that it can replace diff --git a/src/ProjectTemplates/test/template-baselines.json b/src/ProjectTemplates/test/template-baselines.json index e9a93ffe8a..279d5a6852 100644 --- a/src/ProjectTemplates/test/template-baselines.json +++ b/src/ProjectTemplates/test/template-baselines.json @@ -889,39 +889,226 @@ } }, "razorcomponents": { - "None": { + "Individual": { "Template": "blazorserverside", - "Arguments": "new blazorserverside", + "Arguments": "new blazorserverside -au Individual", "Files": [ - "Pages/_Imports.razor", - "Pages/Counter.razor", - "Pages/FetchData.razor", - "Pages/_Host.cshtml", - "Pages/Index.razor", - "Shared/MainLayout.razor", - "Shared/NavMenu.razor", - "_Imports.razor", + "app.db", "App.razor", - "Properties/launchSettings.json", + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "Startup.cs", + "_Imports.razor", + "Areas/Identity/Pages/Account/LogOut.cshtml", + "Areas/Identity/Pages/Shared/_LoginPartial.cshtml", + "Data/ApplicationDbContext.cs", "Data/WeatherForecast.cs", "Data/WeatherForecastService.cs", + "Data/Migrations/00000000000000_CreateIdentitySchema.cs", + "Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs", + "Data/Migrations/ApplicationDbContextModelSnapshot.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/LoginDisplay.razor", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", "wwwroot/favicon.ico", + "wwwroot/css/site.css", "wwwroot/css/bootstrap/bootstrap.min.css", "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", - "wwwroot/css/open-iconic/font/fonts/open-iconic.woff", - "wwwroot/css/open-iconic/FONT-LICENSE", - "wwwroot/css/open-iconic/ICON-LICENSE", - "wwwroot/css/open-iconic/README.md", - "wwwroot/css/site.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" + ] + }, + "IndividualB2C": { + "Template": "blazorserverside", + "Arguments": "new blazorserverside -au IndividualB2C", + "Files": [ + "App.razor", "appsettings.Development.json", "appsettings.json", "Program.cs", - "Startup.cs" + "Startup.cs", + "_Imports.razor", + "Data/WeatherForecast.cs", + "Data/WeatherForecastService.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/LoginDisplay.razor", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", + "wwwroot/favicon.ico", + "wwwroot/css/site.css", + "wwwroot/css/bootstrap/bootstrap.min.css", + "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", + "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", + "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", + "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" + ] + }, + "MultiOrg": { + "Template": "blazorserverside", + "Arguments": "new blazorserverside -au MultiOrg", + "Files": [ + "App.razor", + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "Startup.cs", + "_Imports.razor", + "Data/WeatherForecast.cs", + "Data/WeatherForecastService.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/LoginDisplay.razor", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", + "wwwroot/favicon.ico", + "wwwroot/css/site.css", + "wwwroot/css/bootstrap/bootstrap.min.css", + "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", + "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", + "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", + "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" + ] + }, + "None": { + "Template": "blazorserverside", + "Arguments": "new blazorserverside", + "Files": [ + "App.razor", + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "Startup.cs", + "_Imports.razor", + "Data/WeatherForecast.cs", + "Data/WeatherForecastService.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", + "wwwroot/favicon.ico", + "wwwroot/css/site.css", + "wwwroot/css/bootstrap/bootstrap.min.css", + "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", + "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", + "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", + "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" + ] + }, + "SingleOrg": { + "Template": "blazorserverside", + "Arguments": "new blazorserverside -au SingleOrg", + "Files": [ + "App.razor", + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "Startup.cs", + "_Imports.razor", + "Data/WeatherForecast.cs", + "Data/WeatherForecastService.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/LoginDisplay.razor", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", + "wwwroot/favicon.ico", + "wwwroot/css/site.css", + "wwwroot/css/bootstrap/bootstrap.min.css", + "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", + "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", + "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", + "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" + ] + }, + "Windows": { + "Template": "blazorserverside", + "Arguments": "new blazorserverside -au Windows", + "Files": [ + "App.razor", + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "Startup.cs", + "_Imports.razor", + "Data/WeatherForecast.cs", + "Data/WeatherForecastService.cs", + "Pages/Counter.razor", + "Pages/FetchData.razor", + "Pages/Index.razor", + "Pages/_Host.cshtml", + "Pages/_Imports.razor", + "Properties/launchSettings.json", + "Shared/LoginDisplay.razor", + "Shared/MainLayout.razor", + "Shared/NavMenu.razor", + "wwwroot/favicon.ico", + "wwwroot/css/site.css", + "wwwroot/css/bootstrap/bootstrap.min.css", + "wwwroot/css/bootstrap/bootstrap.min.css.map", + "wwwroot/css/open-iconic/FONT-LICENSE", + "wwwroot/css/open-iconic/ICON-LICENSE", + "wwwroot/css/open-iconic/README.md", + "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css", + "wwwroot/css/open-iconic/font/fonts/open-iconic.eot", + "wwwroot/css/open-iconic/font/fonts/open-iconic.otf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.svg", + "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf", + "wwwroot/css/open-iconic/font/fonts/open-iconic.woff" ] } }, From 49c01eefecf1dbd75e5536aa803d689390c6770a Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 24 May 2019 10:32:41 -0700 Subject: [PATCH 16/95] Log clearer handshake failures in SignalR .NET client (#10433) --- .../Client.Core/src/HubConnection.Log.cs | 26 +- .../csharp/Client.Core/src/HubConnection.cs | 66 +- ...HttpConnectionTests.ConnectionLifecycle.cs | 41 +- .../HubConnectionTests.ConnectionLifecycle.cs | 290 +-- .../UnitTests/HubConnectionTests.Helpers.cs | 10 +- .../UnitTests/HubConnectionTests.Protocol.cs | 80 +- .../UnitTests/HubConnectionTests.Reconnect.cs | 1682 +++++++++-------- 7 files changed, 1179 insertions(+), 1016 deletions(-) diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.Log.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.Log.cs index 7061e276b1..b37239869a 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.Log.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.Log.cs @@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.SignalR.Client LoggerMessage.Define(LogLevel.Error, new EventId(34, "ErrorInvokingClientSideMethod"), "Invoking client side method '{MethodName}' failed."); private static readonly Action _errorProcessingHandshakeResponse = - LoggerMessage.Define(LogLevel.Error, new EventId(35, "ErrorReceivingHandshakeResponse"), "Error processing the handshake response."); + LoggerMessage.Define(LogLevel.Error, new EventId(35, "ErrorReceivingHandshakeResponse"), "The underlying connection closed while processing the handshake response. See exception for details."); private static readonly Action _handshakeServerError = LoggerMessage.Define(LogLevel.Error, new EventId(36, "HandshakeServerError"), "Server returned handshake error: {Error}"); @@ -240,6 +240,15 @@ namespace Microsoft.AspNetCore.SignalR.Client private static readonly Action _attemptingStateTransition = LoggerMessage.Define(LogLevel.Trace, new EventId(80, "AttemptingStateTransition"), "The HubConnection is attempting to transition from the {ExpectedState} state to the {NewState} state."); + private static readonly Action _errorInvalidHandshakeResponse = + LoggerMessage.Define(LogLevel.Error, new EventId(81, "ErrorInvalidHandshakeResponse"), "Received an invalid handshake response."); + + private static readonly Action _errorHandshakeTimedOut = + LoggerMessage.Define(LogLevel.Error, new EventId(82, "ErrorHandshakeTimedOut"), "The handshake timed out after {HandshakeTimeoutSeconds} seconds."); + + private static readonly Action _errorHandshakeCanceled = + LoggerMessage.Define(LogLevel.Error, new EventId(83, "ErrorHandshakeCanceled"), "The handshake was canceled by the client."); + public static void PreparingNonBlockingInvocation(ILogger logger, string target, int count) { _preparingNonBlockingInvocation(logger, target, count, null); @@ -640,6 +649,21 @@ namespace Microsoft.AspNetCore.SignalR.Client { _attemptingStateTransition(logger, expectedState, newState, null); } + + public static void ErrorInvalidHandshakeResponse(ILogger logger, Exception exception) + { + _errorInvalidHandshakeResponse(logger, exception); + } + + public static void ErrorHandshakeTimedOut(ILogger logger, TimeSpan handshakeTimeout, Exception exception) + { + _errorHandshakeTimedOut(logger, handshakeTimeout.TotalSeconds, exception); + } + + public static void ErrorHandshakeCanceled(ILogger logger, Exception exception) + { + _errorHandshakeCanceled(logger, exception); + } } } } diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index 13ddb56792..0cde5b0dcc 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -253,9 +253,9 @@ namespace Microsoft.AspNetCore.SignalR.Client throw new InvalidOperationException($"The {nameof(HubConnection)} cannot be started while {nameof(StopAsync)} is running."); } - using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _state.StopCts.Token)) + using (CreateLinkedToken(cancellationToken, _state.StopCts.Token, out var linkedToken)) { - await StartAsyncCore(cancellationToken); + await StartAsyncCore(linkedToken); } _state.ChangeState(HubConnectionState.Connecting, HubConnectionState.Connected); @@ -1018,20 +1018,23 @@ namespace Microsoft.AspNetCore.SignalR.Client if (sendHandshakeResult.IsCompleted) { // The other side disconnected - throw new InvalidOperationException("The server disconnected before the handshake was completed"); + var ex = new IOException("The server disconnected before the handshake could be started."); + Log.ErrorReceivingHandshakeResponse(_logger, ex); + throw ex; } var input = startingConnectionState.Connection.Transport.Input; + using var handshakeCts = new CancellationTokenSource(HandshakeTimeout); + try { - using (var handshakeCts = new CancellationTokenSource(HandshakeTimeout)) // cancellationToken already contains _state.StopCts.Token, so we don't have to link it again - using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, handshakeCts.Token)) + using (CreateLinkedToken(cancellationToken, handshakeCts.Token, out var linkedToken)) { while (true) { - var result = await input.ReadAsync(cts.Token); + var result = await input.ReadAsync(linkedToken); var buffer = result.Buffer; var consumed = buffer.Start; @@ -1057,6 +1060,7 @@ namespace Microsoft.AspNetCore.SignalR.Client $"Unable to complete handshake with the server due to an error: {message.Error}"); } + Log.HandshakeComplete(_logger); break; } } @@ -1075,17 +1079,34 @@ namespace Microsoft.AspNetCore.SignalR.Client } } } + catch (HubException) + { + // This was already logged as a HandshakeServerError + throw; + } + catch (InvalidDataException ex) + { + Log.ErrorInvalidHandshakeResponse(_logger, ex); + throw; + } + catch (OperationCanceledException ex) + { + if (handshakeCts.IsCancellationRequested) + { + Log.ErrorHandshakeTimedOut(_logger, HandshakeTimeout, ex); + } + else + { + Log.ErrorHandshakeCanceled(_logger, ex); + } - // shutdown if we're unable to read handshake - // Ignore HubException because we throw it when we receive a handshake response with an error - // And because we already have the error, we don't need to log that the handshake failed - catch (Exception ex) when (!(ex is HubException)) + throw; + } + catch (Exception ex) { Log.ErrorReceivingHandshakeResponse(_logger, ex); throw; } - - Log.HandshakeComplete(_logger); } private async Task ReceiveLoop(ConnectionState connectionState) @@ -1485,6 +1506,26 @@ namespace Microsoft.AspNetCore.SignalR.Client } } + private IDisposable CreateLinkedToken(CancellationToken token1, CancellationToken token2, out CancellationToken linkedToken) + { + if (!token1.CanBeCanceled) + { + linkedToken = token2; + return null; + } + else if (!token2.CanBeCanceled) + { + linkedToken = token1; + return null; + } + else + { + var cts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2); + linkedToken = cts.Token; + return cts; + } + } + // Debug.Assert plays havoc with Unit Tests. But I want something that I can "assert" only in Debug builds. [Conditional("DEBUG")] private static void SafeAssert(bool condition, string message, [CallerMemberName] string memberName = null, [CallerFilePath] string fileName = null, [CallerLineNumber] int lineNumber = 0) @@ -1495,7 +1536,6 @@ namespace Microsoft.AspNetCore.SignalR.Client } } - private class Subscription : IDisposable { private readonly InvocationHandler _handler; diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs index c141ffbe8a..2088ef3927 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs @@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(loggerFactory: LoggerFactory, transport: new TestTransport(onTransportStart: SyncPoint.Create(out var syncPoint))), async (connection) => { - var firstStart = connection.StartAsync(TransferFormat.Text).OrTimeout(); - await syncPoint.WaitForSyncPoint(); - var secondStart = connection.StartAsync(TransferFormat.Text).OrTimeout(); + var firstStart = connection.StartAsync(TransferFormat.Text); + await syncPoint.WaitForSyncPoint().OrTimeout(); + var secondStart = connection.StartAsync(TransferFormat.Text); syncPoint.Continue(); - await firstStart; - await secondStart; + await firstStart.OrTimeout(); + await secondStart.OrTimeout(); }); } } @@ -64,10 +64,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { await connection.StartAsync(TransferFormat.Text).OrTimeout(); - await connection.DisposeAsync(); + await connection.DisposeAsync().OrTimeout(); var exception = await Assert.ThrowsAsync( - async () => await connection.StartAsync(TransferFormat.Text).OrTimeout()); + async () => await connection.StartAsync(TransferFormat.Text)).OrTimeout(); Assert.Equal(nameof(HttpConnection), exception.ObjectName); }); @@ -121,7 +121,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { Assert.Equal(0, startCounter); - await connection.StartAsync(TransferFormat.Text); + await connection.StartAsync(TransferFormat.Text).OrTimeout(); Assert.Equal(passThreshold, startCounter); }); } @@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests transport: new TestTransport(onTransportStart: OnTransportStart)), async (connection) => { - var ex = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text)); + var ex = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text)).OrTimeout(); Assert.Equal("Unable to connect to the server with any of the available transports. " + "(WebSockets failed: Transport failed to start) (ServerSentEvents failed: Transport failed to start) (LongPolling failed: Transport failed to start)", ex.Message); @@ -179,8 +179,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(loggerFactory: LoggerFactory), async (connection) => { - await connection.DisposeAsync(); - + await connection.DisposeAsync().OrTimeout(); }); } } @@ -203,7 +202,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await transportStart.WaitForSyncPoint().OrTimeout(); // While the transport is starting, dispose the connection - var disposeTask = connection.DisposeAsync().OrTimeout(); + var disposeTask = connection.DisposeAsync(); transportStart.Continue(); // We need to release StartAsync, because Dispose waits for it. // Wait for start to finish, as that has to finish before the transport will be stopped. @@ -214,7 +213,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests transportStop.Continue(); // Dispose should finish - await disposeTask; + await disposeTask.OrTimeout(); }); } } @@ -234,14 +233,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await connection.StartAsync(TransferFormat.Text).OrTimeout(); // Dispose the connection - var stopTask = connection.DisposeAsync().OrTimeout(); + var stopTask = connection.DisposeAsync(); // Once the transport starts shutting down - await transportStop.WaitForSyncPoint(); + await transportStop.WaitForSyncPoint().OrTimeout(); Assert.False(stopTask.IsCompleted); // Start disposing again, and then let the first dispose continue - var disposeTask = connection.DisposeAsync().OrTimeout(); + var disposeTask = connection.DisposeAsync(); transportStop.Continue(); // Wait for the tasks to complete @@ -249,7 +248,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await disposeTask.OrTimeout(); // We should be disposed and thus unable to restart. - await AssertDisposedAsync(connection); + await AssertDisposedAsync(connection).OrTimeout(); }); } } @@ -316,7 +315,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await connection.Transport.Output.WriteAsync(new byte[] { 0x42 }).OrTimeout(); // We should get the exception in the transport input completion. - await Assert.ThrowsAsync(() => connection.Transport.Input.WaitForWriterToComplete()); + await Assert.ThrowsAsync(() => connection.Transport.Input.WaitForWriterToComplete()).OrTimeout(); }); } } @@ -371,11 +370,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(httpHandler, loggerFactory: LoggerFactory, transport: sse), async (connection) => { - var startTask = connection.StartAsync(TransferFormat.Text).OrTimeout(); + var startTask = connection.StartAsync(TransferFormat.Text); Assert.False(connectResponseTcs.Task.IsCompleted); Assert.False(startTask.IsCompleted); connectResponseTcs.TrySetResult(null); - await startTask; + await startTask.OrTimeout(); }); } } @@ -383,7 +382,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests private static async Task AssertDisposedAsync(HttpConnection connection) { var exception = - await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text).OrTimeout()); + await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text)); Assert.Equal(nameof(HttpConnection), exception.ObjectName); } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs index f60b124d59..f48742474b 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Testing; using Newtonsoft.Json.Linq; using Xunit; @@ -18,7 +19,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { public partial class HubConnectionTests { - public class ConnectionLifecycle + public class ConnectionLifecycle : VerifiableLoggedTest { // This tactic (using names and a dictionary) allows non-serializable data (like a Func) to be used in a theory AND get it to show in the new hierarchical view in Test Explorer as separate tests you can run individually. private static readonly IDictionary> MethodsThatRequireActiveConnection = new Dictionary>() @@ -30,27 +31,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests public static IEnumerable MethodsNamesThatRequireActiveConnection => MethodsThatRequireActiveConnection.Keys.Select(k => new object[] { k }); - private HubConnection CreateHubConnection(TestConnection testConnection) - { - var builder = new HubConnectionBuilder(); - - var delegateConnectionFactory = new DelegateConnectionFactory( - testConnection.StartAsync, - connection => ((TestConnection)connection).DisposeAsync()); - builder.Services.AddSingleton(delegateConnectionFactory); - - return builder.Build(); - } - - private HubConnection CreateHubConnection(Func> connectDelegate, Func disposeDelegate) - { - var builder = new HubConnectionBuilder(); - - var delegateConnectionFactory = new DelegateConnectionFactory(connectDelegate, disposeDelegate); - builder.Services.AddSingleton(delegateConnectionFactory); - - return builder.Build(); - } [Fact] public async Task StartAsyncStartsTheUnderlyingConnection() @@ -60,7 +40,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { Assert.Equal(HubConnectionState.Disconnected, connection.State); - await connection.StartAsync(); + await connection.StartAsync().OrTimeout(); Assert.True(testConnection.Started.IsCompleted); Assert.Equal(HubConnectionState.Connected, connection.State); }); @@ -73,22 +53,22 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var testConnection = new TestConnection(onStart: SyncPoint.Create(out var syncPoint)); await AsyncUsing(CreateHubConnection(testConnection), async connection => { - var firstStart = connection.StartAsync().OrTimeout(); + var firstStart = connection.StartAsync(); Assert.False(firstStart.IsCompleted); // Wait for us to be in IConnectionFactory.ConnectAsync - await syncPoint.WaitForSyncPoint(); + await syncPoint.WaitForSyncPoint().OrTimeout(); // Try starting again - var secondStart = connection.StartAsync().OrTimeout(); + var secondStart = connection.StartAsync(); Assert.False(secondStart.IsCompleted); // Release the sync point syncPoint.Continue(); // The first start should finish fine, but the second throws an InvalidOperationException. - await firstStart; - await Assert.ThrowsAsync(() => secondStart); + await firstStart.OrTimeout(); + await Assert.ThrowsAsync(() => secondStart).OrTimeout(); }); } @@ -108,7 +88,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests return ((TestConnection)connection).DisposeAsync(); } - await AsyncUsing(CreateHubConnection(ConnectionFactory, DisposeAsync), async connection => + var builder = new HubConnectionBuilder(); + var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory, DisposeAsync); + builder.Services.AddSingleton(delegateConnectionFactory); + + await AsyncUsing(builder.Build(), async connection => { Assert.Equal(HubConnectionState.Disconnected, connection.State); @@ -139,47 +123,74 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Task DisposeAsync(ConnectionContext connection) => ((TestConnection)connection).DisposeAsync(); - await AsyncUsing(CreateHubConnection(ConnectionFactory, DisposeAsync), async connection => + var builder = new HubConnectionBuilder(); + var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory, DisposeAsync); + builder.Services.AddSingleton(delegateConnectionFactory); + + await AsyncUsing(builder.Build(), async connection => { await connection.StartAsync().OrTimeout(); Assert.Equal(1, createCount); - var stopTask = connection.StopAsync().OrTimeout(); + var stopTask = connection.StopAsync(); // Wait to hit DisposeAsync on TestConnection (which should be after StopAsync has cleared the connection state) await syncPoint.WaitForSyncPoint().OrTimeout(); // We should not yet be able to start now because StopAsync hasn't completed Assert.False(stopTask.IsCompleted); - var startTask = connection.StartAsync().OrTimeout(); + var startTask = connection.StartAsync(); Assert.False(stopTask.IsCompleted); // When we release the sync point, the StopAsync task will finish syncPoint.Continue(); - await stopTask; + await stopTask.OrTimeout(); // Which will then allow StartAsync to finish. - await startTask; + await startTask.OrTimeout(); }); } [Fact] public async Task StartAsyncWithFailedHandshakeCanBeStopped() { - var testConnection = new TestConnection(autoHandshake: false); - await AsyncUsing(CreateHubConnection(testConnection), async connection => - { - testConnection.Transport.Input.Complete(); - try - { - await connection.StartAsync(); - } - catch - { } + var handshakeConnectionErrorLogged = false; - await connection.StopAsync(); - Assert.True(testConnection.Started.IsCompleted); - }); + bool ExpectedErrors(WriteContext writeContext) + { + if (writeContext.LoggerName == typeof(HubConnection).FullName) + { + if (writeContext.EventId.Name == "ErrorReceivingHandshakeResponse") + { + handshakeConnectionErrorLogged = true; + return true; + } + + return writeContext.EventId.Name == "ErrorStartingConnection"; + } + + return false; + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var testConnection = new TestConnection(autoHandshake: false); + await AsyncUsing(CreateHubConnection(testConnection, loggerFactory: LoggerFactory), async connection => + { + testConnection.Transport.Input.Complete(); + try + { + await connection.StartAsync().OrTimeout(); + } + catch + { } + + await connection.StopAsync().OrTimeout(); + Assert.True(testConnection.Started.IsCompleted); + }); + } + + Assert.True(handshakeConnectionErrorLogged, "The connnection error during the handshake wasn't logged."); } [Theory] @@ -191,7 +202,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var testConnection = new TestConnection(); await AsyncUsing(CreateHubConnection(testConnection), async connection => { - var ex = await Assert.ThrowsAsync(() => method(connection)); + var ex = await Assert.ThrowsAsync(() => method(connection)).OrTimeout(); Assert.Equal($"The '{name}' method cannot be called if the connection is not active", ex.Message); }); } @@ -207,27 +218,27 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await AsyncUsing(CreateHubConnection(testConnection), async connection => { // Start, and wait for the sync point to be hit - var startTask = connection.StartAsync().OrTimeout(); + var startTask = connection.StartAsync(); Assert.False(startTask.IsCompleted); - await syncPoint.WaitForSyncPoint(); + await syncPoint.WaitForSyncPoint().OrTimeout(); // Run the method, but it will be waiting for the lock - var targetTask = method(connection).OrTimeout(); + var targetTask = method(connection); // Release the SyncPoint syncPoint.Continue(); // Wait for start to finish - await startTask; + await startTask.OrTimeout(); // We need some special logic to ensure InvokeAsync completes. if (string.Equals(name, nameof(HubConnection.InvokeCoreAsync))) { - await ForceLastInvocationToComplete(testConnection); + await ForceLastInvocationToComplete(testConnection).OrTimeout(); } // Wait for the method to complete. - await targetTask; + await targetTask.OrTimeout(); }); } @@ -239,9 +250,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await AsyncUsing(CreateHubConnection(testConnection), async connection => { // Start, and wait for the sync point to be hit - var startTask = connection.StartAsync().OrTimeout(); + var startTask = connection.StartAsync(); Assert.False(startTask.IsCompleted); - await syncPoint.WaitForSyncPoint(); + await syncPoint.WaitForSyncPoint().OrTimeout(); Assert.Equal(HubConnectionState.Connecting, connection.State); @@ -249,7 +260,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests syncPoint.Continue(); // Wait for start to finish - await startTask; + await startTask.OrTimeout(); Assert.Equal(HubConnectionState.Connected, connection.State); }); @@ -349,7 +360,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await closed.Task.OrTimeout(); // We should be stopped now - var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo").OrTimeout()); + var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo")).OrTimeout(); Assert.Equal($"The '{nameof(HubConnection.SendCoreAsync)}' method cannot be called if the connection is not active", ex.Message); }); } @@ -374,20 +385,20 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Assert.True(testConnection.Started.IsCompleted); // Start shutting down and complete the transport side - var stopTask = connection.StopAsync().OrTimeout(); + var stopTask = connection.StopAsync(); testConnection.CompleteFromTransport(); // Wait for the connection to close. await testConnectionClosed.Task.OrTimeout(); // The stop should be completed. - await stopTask; + await stopTask.OrTimeout(); // The HubConnection should now be closed. await connectionClosed.Task.OrTimeout(); // We should be stopped now - var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo").OrTimeout()); + var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo")).OrTimeout(); Assert.Equal($"The '{nameof(HubConnection.SendCoreAsync)}' method cannot be called if the connection is not active", ex.Message); await testConnection.Disposed.OrTimeout(); @@ -416,16 +427,16 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests testConnection.CompleteFromTransport(); // Start stopping manually (these can't be synchronized by a Sync Point because the transport is disposed outside the lock) - var stopTask = connection.StopAsync().OrTimeout(); + var stopTask = connection.StopAsync(); await testConnection.Disposed.OrTimeout(); // Wait for the stop task to complete and the closed event to fire - await stopTask; + await stopTask.OrTimeout(); await connectionClosed.Task.OrTimeout(); // We should be stopped now - var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo").OrTimeout()); + var ex = await Assert.ThrowsAsync(() => connection.SendAsync("Foo")).OrTimeout(); Assert.Equal($"The '{nameof(HubConnection.SendCoreAsync)}' method cannot be called if the connection is not active", ex.Message); }); } @@ -444,89 +455,141 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests // Stop and invoke the method. These two aren't synchronizable via a Sync Point any more because the transport is disposed // outside the lock :( - var disposeTask = connection.StopAsync().OrTimeout(); + var disposeTask = connection.StopAsync(); // Wait to hit DisposeAsync on TestConnection (which should be after StopAsync has cleared the connection state) await syncPoint.WaitForSyncPoint().OrTimeout(); - var targetTask = method(connection).OrTimeout(); + var targetTask = method(connection); // Release the sync point syncPoint.Continue(); // Wait for the method to complete, with an expected error. - var ex = await Assert.ThrowsAsync(() => targetTask); + var ex = await Assert.ThrowsAsync(() => targetTask).OrTimeout(); Assert.Equal($"The '{methodName}' method cannot be called if the connection is not active", ex.Message); - await disposeTask; + await disposeTask.OrTimeout(); }); } [Fact] public async Task ClientTimesoutWhenHandshakeResponseTakesTooLong() { - var connection = new TestConnection(autoHandshake: false); - var hubConnection = CreateHubConnection(connection); - try - { - hubConnection.HandshakeTimeout = TimeSpan.FromMilliseconds(1); + var handshakeTimeoutLogged = false; - await Assert.ThrowsAsync(() => hubConnection.StartAsync().OrTimeout()); - Assert.Equal(HubConnectionState.Disconnected, hubConnection.State); - } - finally + bool ExpectedErrors(WriteContext writeContext) { - await hubConnection.DisposeAsync().OrTimeout(); - await connection.DisposeAsync().OrTimeout(); + if (writeContext.LoggerName == typeof(HubConnection).FullName) + { + if (writeContext.EventId.Name == "ErrorHandshakeTimedOut") + { + handshakeTimeoutLogged = true; + return true; + } + + return writeContext.EventId.Name == "ErrorStartingConnection"; + } + + return false; } + + using (StartVerifiableLog(ExpectedErrors)) + { + var connection = new TestConnection(autoHandshake: false); + var hubConnection = CreateHubConnection(connection, loggerFactory: LoggerFactory); + try + { + hubConnection.HandshakeTimeout = TimeSpan.FromMilliseconds(1); + + await Assert.ThrowsAsync(() => hubConnection.StartAsync()).OrTimeout(); + Assert.Equal(HubConnectionState.Disconnected, hubConnection.State); + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + await connection.DisposeAsync().OrTimeout(); + } + } + + Assert.True(handshakeTimeoutLogged, "The handshake timeout wasn't logged."); } [Fact] public async Task StartAsyncWithTriggeredCancellationTokenIsCanceled() { - var onStartCalled = false; - var connection = new TestConnection(onStart: () => + using (StartVerifiableLog()) { - onStartCalled = true; - return Task.CompletedTask; - }); - var hubConnection = CreateHubConnection(connection); - try - { - await Assert.ThrowsAsync(() => hubConnection.StartAsync(new CancellationToken(canceled: true)).OrTimeout()); - Assert.False(onStartCalled); - } - finally - { - await hubConnection.DisposeAsync().OrTimeout(); - await connection.DisposeAsync().OrTimeout(); + var onStartCalled = false; + var connection = new TestConnection(onStart: () => + { + onStartCalled = true; + return Task.CompletedTask; + }); + var hubConnection = CreateHubConnection(connection, loggerFactory: LoggerFactory); + try + { + await Assert.ThrowsAsync(() => hubConnection.StartAsync(new CancellationToken(canceled: true))).OrTimeout(); + Assert.False(onStartCalled); + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + await connection.DisposeAsync().OrTimeout(); + } } } [Fact] public async Task StartAsyncCanTriggerCancellationTokenToCancelHandshake() { - var cts = new CancellationTokenSource(); - var connection = new TestConnection(onStart: () => - { - cts.Cancel(); - return Task.CompletedTask; - }, autoHandshake: false); - var hubConnection = CreateHubConnection(connection); - // We want to make sure the cancellation is because of the token passed to StartAsync - hubConnection.HandshakeTimeout = Timeout.InfiniteTimeSpan; - try - { - var startTask = hubConnection.StartAsync(cts.Token); - await Assert.ThrowsAnyAsync(() => startTask.OrTimeout()); + var handshakeCancellationLogged = false; - // We aren't worried about the exact message and it's localized so asserting it is non-trivial. - } - finally + bool ExpectedErrors(WriteContext writeContext) { - await hubConnection.DisposeAsync().OrTimeout(); - await connection.DisposeAsync().OrTimeout(); + if (writeContext.LoggerName == typeof(HubConnection).FullName) + { + if (writeContext.EventId.Name == "ErrorHandshakeCanceled") + { + handshakeCancellationLogged = true; + return true; + } + + return writeContext.EventId.Name == "ErrorStartingConnection"; + } + + return false; } + + using (StartVerifiableLog(ExpectedErrors)) + { + var cts = new CancellationTokenSource(); + TestConnection connection = null; + + connection = new TestConnection(autoHandshake: false); + + var hubConnection = CreateHubConnection(connection, loggerFactory: LoggerFactory); + // We want to make sure the cancellation is because of the token passed to StartAsync + hubConnection.HandshakeTimeout = Timeout.InfiniteTimeSpan; + + try + { + var startTask = hubConnection.StartAsync(cts.Token); + + await connection.ReadSentTextMessageAsync().OrTimeout(); + cts.Cancel(); + + // We aren't worried about the exact message and it's localized so asserting it is non-trivial. + await Assert.ThrowsAnyAsync(() => startTask).OrTimeout(); + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + await connection.DisposeAsync().OrTimeout(); + } + } + + Assert.True(handshakeCancellationLogged, "The handshake cancellation wasn't logged."); } [Fact] @@ -574,13 +637,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { try { + // Using OrTimeout here will hide any timeout issues in the test :(. await action(connection); } finally { // Dispose isn't under test here, so fire and forget so that errors/timeouts here don't cause // test errors that mask the real errors. - _ = connection.DisposeAsync(); + _ = connection.DisposeAsync(); } } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs index 6aa6106f40..0ec53e6479 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.DependencyInjection; @@ -7,7 +10,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { public partial class HubConnectionTests { - private static HubConnection CreateHubConnection(TestConnection connection, IHubProtocol protocol = null, ILoggerFactory loggerFactory = null, IRetryPolicy reconnectPolicy = null) + private static HubConnection CreateHubConnection(TestConnection connection, IHubProtocol protocol = null, ILoggerFactory loggerFactory = null) { var builder = new HubConnectionBuilder(); @@ -27,11 +30,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests builder.Services.AddSingleton(protocol); } - if (reconnectPolicy != null) - { - builder.WithAutomaticReconnect(reconnectPolicy); - } - return builder.Build(); } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Protocol.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Protocol.cs index 5ddcc51161..93cc3dd863 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Protocol.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Protocol.cs @@ -2,9 +2,10 @@ // 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.IO; using System.Threading.Channels; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Tests; using Xunit; namespace Microsoft.AspNetCore.SignalR.Client.Tests @@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests // don't cause problems. public partial class HubConnectionTests { - public class Protocol + public class Protocol : VerifiableLoggedTest { [Fact] public async Task SendAsyncSendsANonBlockingInvocationMessage() @@ -31,6 +32,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests // ReadSentTextMessageAsync strips off the record separator (because it has use it as a separator now that we use Pipelines) Assert.Equal("{\"type\":1,\"target\":\"Foo\",\"arguments\":[]}", invokeMessage); + + await invokeTask.OrTimeout(); } finally { @@ -47,14 +50,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests try { // We can't await StartAsync because it depends on the negotiate process! - var startTask = hubConnection.StartAsync().OrTimeout(); + var startTask = hubConnection.StartAsync(); var handshakeMessage = await connection.ReadHandshakeAndSendResponseAsync().OrTimeout(); // ReadSentTextMessageAsync strips off the record separator (because it has use it as a separator now that we use Pipelines) Assert.Equal("{\"protocol\":\"json\",\"version\":1}", handshakeMessage); - await startTask; + await startTask.OrTimeout(); } finally { @@ -63,6 +66,35 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests } } + [Fact] + public async Task InvalidHandshakeResponseCausesStartToFail() + { + using (StartVerifiableLog()) + { + var connection = new TestConnection(autoHandshake: false); + var hubConnection = CreateHubConnection(connection); + try + { + // We can't await StartAsync because it depends on the negotiate process! + var startTask = hubConnection.StartAsync(); + + await connection.ReadSentTextMessageAsync().OrTimeout(); + + // The client expects the first message to be a handshake response, but a handshake response doesn't have a "type". + await connection.ReceiveJsonMessage(new { type = "foo" }).OrTimeout(); + + var ex = await Assert.ThrowsAsync(() => startTask).OrTimeout(); + + Assert.Equal("Expected a handshake response from the server.", ex.Message); + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + await connection.DisposeAsync().OrTimeout(); + } + } + } + [Fact] public async Task ClientIsOkayReceivingMinorVersionInHandshake() { @@ -74,9 +106,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests try { var startTask = hubConnection.StartAsync(); - var message = await connection.ReadHandshakeAndSendResponseAsync(56); + var message = await connection.ReadHandshakeAndSendResponseAsync(56).OrTimeout(); - await startTask; + await startTask.OrTimeout(); } finally { @@ -94,12 +126,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await hubConnection.StartAsync().OrTimeout(); - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); var invokeMessage = await connection.ReadSentTextMessageAsync().OrTimeout(); // ReadSentTextMessageAsync strips off the record separator (because it has use it as a separator now that we use Pipelines) Assert.Equal("{\"type\":1,\"invocationId\":\"1\",\"target\":\"Foo\",\"arguments\":[]}", invokeMessage); + + Assert.Equal(TaskStatus.WaitingForActivation, invokeTask.Status); } finally { @@ -184,7 +218,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests // Complete the channel await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3 }).OrTimeout(); - await channel.Completion; + await channel.Completion.OrTimeout(); } finally { @@ -202,7 +236,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await hubConnection.StartAsync().OrTimeout(); - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3 }).OrTimeout(); @@ -246,7 +280,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await hubConnection.StartAsync().OrTimeout(); - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3, result = 42 }).OrTimeout(); @@ -268,7 +302,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await hubConnection.StartAsync().OrTimeout(); - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3, error = "An error occurred" }).OrTimeout(); @@ -295,7 +329,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3, result = "Oops" }).OrTimeout(); - var ex = await Assert.ThrowsAsync(async () => await channel.ReadAndCollectAllAsync().OrTimeout()); + var ex = await Assert.ThrowsAsync(() => channel.ReadAndCollectAllAsync()).OrTimeout(); Assert.Equal("Server provided a result in a completion response to a streamed invocation.", ex.Message); } finally @@ -318,7 +352,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await connection.ReceiveJsonMessage(new { invocationId = "1", type = 3, error = "An error occurred" }).OrTimeout(); - var ex = await Assert.ThrowsAsync(async () => await channel.ReadAndCollectAllAsync().OrTimeout()); + var ex = await Assert.ThrowsAsync(async () => await channel.ReadAndCollectAllAsync()).OrTimeout(); Assert.Equal("An error occurred", ex.Message); } finally @@ -337,7 +371,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await hubConnection.StartAsync().OrTimeout(); - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); await connection.ReceiveJsonMessage(new { invocationId = "1", type = 2, item = 42 }).OrTimeout(); @@ -479,7 +513,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await hubConnection.StartAsync().OrTimeout(); // Send an invocation - var invokeTask = hubConnection.InvokeAsync("Foo").OrTimeout(); + var invokeTask = hubConnection.InvokeAsync("Foo"); // Receive the ping mid-invocation so we can see that the rest of the flow works fine await connection.ReceiveJsonMessage(new { type = 6 }).OrTimeout(); @@ -506,15 +540,15 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { var task = hubConnection.StartAsync(); - await connection.ReceiveTextAsync("{"); + await connection.ReceiveTextAsync("{").OrTimeout(); Assert.False(task.IsCompleted); - await connection.ReceiveTextAsync("}"); + await connection.ReceiveTextAsync("}").OrTimeout(); Assert.False(task.IsCompleted); - await connection.ReceiveTextAsync("\u001e"); + await connection.ReceiveTextAsync("\u001e").OrTimeout(); await task.OrTimeout(); } @@ -539,9 +573,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests tcs.TrySetResult(data); }); - await connection.ReceiveTextAsync(payload); + await connection.ReceiveTextAsync(payload).OrTimeout(); - await hubConnection.StartAsync(); + await hubConnection.StartAsync().OrTimeout(); var response = await tcs.Task.OrTimeout(); Assert.Equal("hello", response); @@ -568,15 +602,15 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await hubConnection.StartAsync().OrTimeout(); - await connection.ReceiveTextAsync("{\"type\":1, "); + await connection.ReceiveTextAsync("{\"type\":1, ").OrTimeout(); Assert.False(tcs.Task.IsCompleted); - await connection.ReceiveTextAsync("\"target\": \"Echo\", \"arguments\""); + await connection.ReceiveTextAsync("\"target\": \"Echo\", \"arguments\"").OrTimeout(); Assert.False(tcs.Task.IsCompleted); - await connection.ReceiveTextAsync(":[\"hello\"]}\u001e"); + await connection.ReceiveTextAsync(":[\"hello\"]}\u001e").OrTimeout(); var response = await tcs.Task.OrTimeout(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs index 8e6c684b7b..a039bbd0ef 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; +using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Testing; using Moq; @@ -17,897 +18,900 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { public partial class HubConnectionTests { - [Fact] - public async Task ReconnectIsNotEnabledByDefault() + public class Reconnect : VerifiableLoggedTest { - bool ExpectedErrors(WriteContext writeContext) + [Fact] + public async Task IsNotEnabledByDefault() { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ShutdownWithError" || - writeContext.EventId.Name == "ServerDisconnectedWithError"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var exception = new Exception(); - - var testConnection = new TestConnection(); - await using var hubConnection = CreateHubConnection(testConnection, loggerFactory: LoggerFactory); - - var reconnectingCalled = false; - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => + bool ExpectedErrors(WriteContext writeContext) { - reconnectingCalled = true; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - await hubConnection.StartAsync().OrTimeout(); - - testConnection.CompleteFromTransport(exception); - - Assert.Same(exception, await closedErrorTcs.Task.OrTimeout()); - Assert.False(reconnectingCalled); - } - } - - [Fact] - public async Task ReconnectCanBeOptedInto() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError"); - } - - var failReconnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = default(ReconnectingConnectionFactory); - var startCallCount = 0; - var originalConnectionId = "originalConnectionId"; - var reconnectedConnectionId = "reconnectedConnectionId"; - - async Task OnTestConnectionStart() - { - startCallCount++; - - // Only fail the first reconnect attempt. - if (startCallCount == 2) - { - await failReconnectTcs.Task; - } - - var testConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - - // Change the connection id before reconnecting. - if (startCallCount == 3) - { - testConnection.ConnectionId = reconnectedConnectionId; - } - else - { - testConnection.ConnectionId = originalConnectionId; - } + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ShutdownWithError" || + writeContext.EventId.Name == "ServerDisconnectedWithError"); } - testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + using (StartVerifiableLog(ExpectedErrors)) { - retryContexts.Add(context); - return TimeSpan.Zero; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + var exception = new Exception(); - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var testConnection = new TestConnection(); + await using var hubConnection = CreateHubConnection(testConnection, loggerFactory: LoggerFactory); - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; + var reconnectingCalled = false; + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - reconnectedConnectionIdTcs.SetResult(connectionId); - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - await hubConnection.StartAsync().OrTimeout(); - - Assert.Same(originalConnectionId, hubConnection.ConnectionId); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - var reconnectException = new Exception(); - failReconnectTcs.SetException(reconnectException); - - Assert.Same(reconnectedConnectionId, await reconnectedConnectionIdTcs.Task.OrTimeout()); - - Assert.Equal(2, retryContexts.Count); - Assert.Same(reconnectException, retryContexts[1].RetryReason); - Assert.Equal(1, retryContexts[1].PreviousRetryCount); - Assert.True(TimeSpan.Zero <= retryContexts[1].ElapsedTime); - - await hubConnection.StopAsync().OrTimeout(); - - var closeError = await closedErrorTcs.Task.OrTimeout(); - Assert.Null(closeError); - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectStopsIfTheReconnectPolicyReturnsNull() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError"); - } - - var failReconnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var startCallCount = 0; - - Task OnTestConnectionStart() - { - startCallCount++; - - // Fail the first reconnect attempts. - if (startCallCount > 1) + hubConnection.Reconnecting += error => { - return failReconnectTcs.Task; - } + reconnectingCalled = true; + return Task.CompletedTask; + }; - return Task.CompletedTask; + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + await hubConnection.StartAsync().OrTimeout(); + + testConnection.CompleteFromTransport(exception); + + Assert.Same(exception, await closedErrorTcs.Task.OrTimeout()); + Assert.False(reconnectingCalled); + } + } + + [Fact] + public async Task CanBeOptedInto() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError"); } - var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); - builder.Services.AddSingleton(testConnectionFactory); + var failReconnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var retryContexts = new List(); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + using (StartVerifiableLog(ExpectedErrors)) { - retryContexts.Add(context); - return context.PreviousRetryCount == 0 ? TimeSpan.Zero : (TimeSpan?)null; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = default(ReconnectingConnectionFactory); + var startCallCount = 0; + var originalConnectionId = "originalConnectionId"; + var reconnectedConnectionId = "reconnectedConnectionId"; - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - await hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - var reconnectException = new Exception(); - failReconnectTcs.SetException(reconnectException); - - var closeError = await closedErrorTcs.Task.OrTimeout(); - Assert.IsType(closeError); - - Assert.Equal(2, retryContexts.Count); - Assert.Same(reconnectException, retryContexts[1].RetryReason); - Assert.Equal(1, retryContexts[1].PreviousRetryCount); - Assert.True(TimeSpan.Zero <= retryContexts[1].ElapsedTime); - - Assert.Equal(1, reconnectingCount); - Assert.Equal(0, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectCanHappenMultipleTimes() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => - { - retryContexts.Add(context); - return TimeSpan.Zero; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); - - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - reconnectedConnectionIdTcs.SetResult(connectionId); - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - await hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - await reconnectedConnectionIdTcs.Task.OrTimeout(); - - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); - - reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - var secondException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(secondException); - - Assert.Same(secondException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Equal(2, retryContexts.Count); - Assert.Same(secondException, retryContexts[1].RetryReason); - Assert.Equal(0, retryContexts[1].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[1].ElapsedTime); - - await reconnectedConnectionIdTcs.Task.OrTimeout(); - - Assert.Equal(2, reconnectingCount); - Assert.Equal(2, reconnectedCount); - Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); - - await hubConnection.StopAsync().OrTimeout(); - - var closeError = await closedErrorTcs.Task.OrTimeout(); - Assert.Null(closeError); - Assert.Equal(2, reconnectingCount); - Assert.Equal(2, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectEventsNotFiredIfFirstRetryDelayIsNull() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - writeContext.EventId.Name == "ServerDisconnectedWithError"; - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(); - builder.Services.AddSingleton(testConnectionFactory); - - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(null); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); - - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - await hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - await closedErrorTcs.Task.OrTimeout(); - - Assert.Equal(0, reconnectingCount); - Assert.Equal(0, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectDoesNotStartIfConnectionIsLostDuringInitialHandshake() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || - writeContext.EventId.Name == "ErrorStartingConnection"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); - builder.Services.AddSingleton(testConnectionFactory); - - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(null); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); - - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var closedCount = 0; - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedCount++; - return Task.CompletedTask; - }; - - var startTask = hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await Assert.ThrowsAsync(() => startTask).OrTimeout()); - Assert.Equal(HubConnectionState.Disconnected, hubConnection.State); - Assert.Equal(0, reconnectingCount); - Assert.Equal(0, reconnectedCount); - Assert.Equal(0, closedCount); - } - } - - [Fact] - public async Task ReconnectContinuesIfConnectionLostDuringReconnectHandshake() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError" || - writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || - writeContext.EventId.Name == "ErrorStartingConnection"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var secondRetryDelayTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => - { - retryContexts.Add(context); - - if (retryContexts.Count == 2) + async Task OnTestConnectionStart() { - secondRetryDelayTcs.SetResult(null); + startCallCount++; + + // Only fail the first reconnect attempt. + if (startCallCount == 2) + { + await failReconnectTcs.Task; + } + + var testConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + + // Change the connection id before reconnecting. + if (startCallCount == 3) + { + testConnection.ConnectionId = reconnectedConnectionId; + } + else + { + testConnection.ConnectionId = originalConnectionId; + } } - return TimeSpan.Zero; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); + builder.Services.AddSingleton(testConnectionFactory); - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - reconnectedConnectionIdTcs.SetResult(connectionId); - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - var startTask = hubConnection.StartAsync(); - - // Complete handshake - var currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); - - await startTask.OrTimeout(); - - var firstException = new Exception(); - currentTestConnection.CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - var secondException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(secondException); - - await secondRetryDelayTcs.Task.OrTimeout(); - - Assert.Equal(2, retryContexts.Count); - Assert.Same(secondException, retryContexts[1].RetryReason); - Assert.Equal(1, retryContexts[1].PreviousRetryCount); - Assert.True(TimeSpan.Zero <= retryContexts[0].ElapsedTime); - - // Complete handshake - currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); - await reconnectedConnectionIdTcs.Task.OrTimeout(); - - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); - - await hubConnection.StopAsync().OrTimeout(); - - var closeError = await closedErrorTcs.Task.OrTimeout(); - Assert.Null(closeError); - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectContinuesIfInvalidHandshakeResponse() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError" || - writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || - writeContext.EventId.Name == "HandshakeServerError" || - writeContext.EventId.Name == "ErrorStartingConnection"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var secondRetryDelayTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => - { - retryContexts.Add(context); - - if (retryContexts.Count == 2) + var retryContexts = new List(); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => { - secondRetryDelayTcs.SetResult(null); + retryContexts.Add(context); + return TimeSpan.Zero; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + reconnectedConnectionIdTcs.SetResult(connectionId); + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + await hubConnection.StartAsync().OrTimeout(); + + Assert.Same(originalConnectionId, hubConnection.ConnectionId); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + var reconnectException = new Exception(); + failReconnectTcs.SetException(reconnectException); + + Assert.Same(reconnectedConnectionId, await reconnectedConnectionIdTcs.Task.OrTimeout()); + + Assert.Equal(2, retryContexts.Count); + Assert.Same(reconnectException, retryContexts[1].RetryReason); + Assert.Equal(1, retryContexts[1].PreviousRetryCount); + Assert.True(TimeSpan.Zero <= retryContexts[1].ElapsedTime); + + await hubConnection.StopAsync().OrTimeout(); + + var closeError = await closedErrorTcs.Task.OrTimeout(); + Assert.Null(closeError); + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + } + } + + [Fact] + public async Task StopsIfTheReconnectPolicyReturnsNull() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError"); + } + + var failReconnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var startCallCount = 0; + + Task OnTestConnectionStart() + { + startCallCount++; + + // Fail the first reconnect attempts. + if (startCallCount > 1) + { + return failReconnectTcs.Task; + } + + return Task.CompletedTask; } - return TimeSpan.Zero; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); + builder.Services.AddSingleton(testConnectionFactory); - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var retryContexts = new List(); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + return context.PreviousRetryCount == 0 ? TimeSpan.Zero : (TimeSpan?)null; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - reconnectedConnectionIdTcs.SetResult(connectionId); - return Task.CompletedTask; - }; + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + return Task.CompletedTask; + }; - var startTask = hubConnection.StartAsync(); + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; - // Complete handshake - var currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); + await hubConnection.StartAsync().OrTimeout(); - await startTask.OrTimeout(); + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - var firstException = new Exception(); - currentTestConnection.CompleteFromTransport(firstException); + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + var reconnectException = new Exception(); + failReconnectTcs.SetException(reconnectException); - // Respond to handshake with error. - currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - await currentTestConnection.ReadSentTextMessageAsync().OrTimeout(); + var closeError = await closedErrorTcs.Task.OrTimeout(); + Assert.IsType(closeError); - var output = MemoryBufferWriter.Get(); - try - { - HandshakeProtocol.WriteResponseMessage(new HandshakeResponseMessage("Error!"), output); - await currentTestConnection.Application.Output.WriteAsync(output.ToArray()).OrTimeout(); + Assert.Equal(2, retryContexts.Count); + Assert.Same(reconnectException, retryContexts[1].RetryReason); + Assert.Equal(1, retryContexts[1].PreviousRetryCount); + Assert.True(TimeSpan.Zero <= retryContexts[1].ElapsedTime); + + Assert.Equal(1, reconnectingCount); + Assert.Equal(0, reconnectedCount); } - finally - { - MemoryBufferWriter.Return(output); - } - - await secondRetryDelayTcs.Task.OrTimeout(); - - Assert.Equal(2, retryContexts.Count); - Assert.IsType(retryContexts[1].RetryReason); - Assert.Equal(1, retryContexts[1].PreviousRetryCount); - Assert.True(TimeSpan.Zero <= retryContexts[0].ElapsedTime); - - // Complete handshake - - currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); - await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); - await reconnectedConnectionIdTcs.Task.OrTimeout(); - - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); - - await hubConnection.StopAsync().OrTimeout(); - - var closeError = await closedErrorTcs.Task.OrTimeout(); - Assert.Null(closeError); - Assert.Equal(1, reconnectingCount); - Assert.Equal(1, reconnectedCount); - } - } - - [Fact] - public async Task ReconnectCanBeStoppedWhileRestartingUnderlyingConnection() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError" || - writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || - writeContext.EventId.Name == "ErrorStartingConnection"); } - using (StartVerifiableLog(ExpectedErrors)) + [Fact] + public async Task CanHappenMultipleTimes() { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var connectionStartTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - async Task OnTestConnectionStart() + bool ExpectedErrors(WriteContext writeContext) { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(); + builder.Services.AddSingleton(testConnectionFactory); + + var retryContexts = new List(); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + return TimeSpan.Zero; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + reconnectedConnectionIdTcs.SetResult(connectionId); + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + await hubConnection.StartAsync().OrTimeout(); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + await reconnectedConnectionIdTcs.Task.OrTimeout(); + + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); + + reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var secondException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(secondException); + + Assert.Same(secondException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Equal(2, retryContexts.Count); + Assert.Same(secondException, retryContexts[1].RetryReason); + Assert.Equal(0, retryContexts[1].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[1].ElapsedTime); + + await reconnectedConnectionIdTcs.Task.OrTimeout(); + + Assert.Equal(2, reconnectingCount); + Assert.Equal(2, reconnectedCount); + Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); + + await hubConnection.StopAsync().OrTimeout(); + + var closeError = await closedErrorTcs.Task.OrTimeout(); + Assert.Null(closeError); + Assert.Equal(2, reconnectingCount); + Assert.Equal(2, reconnectedCount); + } + } + + [Fact] + public async Task EventsNotFiredIfFirstRetryDelayIsNull() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + writeContext.EventId.Name == "ServerDisconnectedWithError"; + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(); + builder.Services.AddSingleton(testConnectionFactory); + + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(null); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + await hubConnection.StartAsync().OrTimeout(); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + await closedErrorTcs.Task.OrTimeout(); + + Assert.Equal(0, reconnectingCount); + Assert.Equal(0, reconnectedCount); + } + } + + [Fact] + public async Task DoesNotStartIfConnectionIsLostDuringInitialHandshake() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || + writeContext.EventId.Name == "ErrorStartingConnection"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); + builder.Services.AddSingleton(testConnectionFactory); + + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(null); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var closedCount = 0; + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedCount++; + return Task.CompletedTask; + }; + + var startTask = hubConnection.StartAsync().OrTimeout(); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + Assert.Same(firstException, await Assert.ThrowsAsync(() => startTask).OrTimeout()); + Assert.Equal(HubConnectionState.Disconnected, hubConnection.State); + Assert.Equal(0, reconnectingCount); + Assert.Equal(0, reconnectedCount); + Assert.Equal(0, closedCount); + } + } + + [Fact] + public async Task ContinuesIfConnectionLostDuringReconnectHandshake() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError" || + writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || + writeContext.EventId.Name == "ErrorStartingConnection"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); + builder.Services.AddSingleton(testConnectionFactory); + + var retryContexts = new List(); + var secondRetryDelayTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + + if (retryContexts.Count == 2) + { + secondRetryDelayTcs.SetResult(null); + } + + return TimeSpan.Zero; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + reconnectedConnectionIdTcs.SetResult(connectionId); + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + var startTask = hubConnection.StartAsync(); + + // Complete handshake + var currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); + + await startTask.OrTimeout(); + + var firstException = new Exception(); + currentTestConnection.CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + var secondException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(secondException); + + await secondRetryDelayTcs.Task.OrTimeout(); + + Assert.Equal(2, retryContexts.Count); + Assert.Same(secondException, retryContexts[1].RetryReason); + Assert.Equal(1, retryContexts[1].PreviousRetryCount); + Assert.True(TimeSpan.Zero <= retryContexts[0].ElapsedTime); + + // Complete handshake + currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); + await reconnectedConnectionIdTcs.Task.OrTimeout(); + + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); + + await hubConnection.StopAsync().OrTimeout(); + + var closeError = await closedErrorTcs.Task.OrTimeout(); + Assert.Null(closeError); + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + } + } + + [Fact] + public async Task ContinuesIfInvalidHandshakeResponse() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError" || + writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || + writeContext.EventId.Name == "HandshakeServerError" || + writeContext.EventId.Name == "ErrorStartingConnection"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); + builder.Services.AddSingleton(testConnectionFactory); + + var retryContexts = new List(); + var secondRetryDelayTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + + if (retryContexts.Count == 2) + { + secondRetryDelayTcs.SetResult(null); + } + + return TimeSpan.Zero; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reconnectedConnectionIdTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + reconnectedConnectionIdTcs.SetResult(connectionId); + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + var startTask = hubConnection.StartAsync(); + + // Complete handshake + var currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); + + await startTask.OrTimeout(); + + var firstException = new Exception(); + currentTestConnection.CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + // Respond to handshake with error. + currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + await currentTestConnection.ReadSentTextMessageAsync().OrTimeout(); + + var output = MemoryBufferWriter.Get(); try { - await connectionStartTcs.Task; + HandshakeProtocol.WriteResponseMessage(new HandshakeResponseMessage("Error!"), output); + await currentTestConnection.Application.Output.WriteAsync(output.ToArray()).OrTimeout(); } finally { - connectionStartTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + MemoryBufferWriter.Return(output); + } + + await secondRetryDelayTcs.Task.OrTimeout(); + + Assert.Equal(2, retryContexts.Count); + Assert.IsType(retryContexts[1].RetryReason); + Assert.Equal(1, retryContexts[1].PreviousRetryCount); + Assert.True(TimeSpan.Zero <= retryContexts[0].ElapsedTime); + + // Complete handshake + + currentTestConnection = await testConnectionFactory.GetNextOrCurrentTestConnection(); + await currentTestConnection.ReadHandshakeAndSendResponseAsync().OrTimeout(); + await reconnectedConnectionIdTcs.Task.OrTimeout(); + + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + Assert.Equal(TaskStatus.WaitingForActivation, closedErrorTcs.Task.Status); + + await hubConnection.StopAsync().OrTimeout(); + + var closeError = await closedErrorTcs.Task.OrTimeout(); + Assert.Null(closeError); + Assert.Equal(1, reconnectingCount); + Assert.Equal(1, reconnectedCount); + } + } + + [Fact] + public async Task CanBeStoppedWhileRestartingUnderlyingConnection() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError" || + writeContext.EventId.Name == "ErrorHandshakeCanceled" || + writeContext.EventId.Name == "ErrorStartingConnection"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var connectionStartTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + async Task OnTestConnectionStart() + { + try + { + await connectionStartTcs.Task; + } + finally + { + connectionStartTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + } + + var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); + builder.Services.AddSingleton(testConnectionFactory); + + var retryContexts = new List(); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + return TimeSpan.Zero; + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + // Allow the first connection to start successfully. + connectionStartTcs.SetResult(null); + await hubConnection.StartAsync().OrTimeout(); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + var secondException = new Exception(); + var stopTask = hubConnection.StopAsync(); + connectionStartTcs.SetResult(null); + + Assert.IsType(await closedErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Equal(1, reconnectingCount); + Assert.Equal(0, reconnectedCount); + await stopTask.OrTimeout(); + } + } + + [Fact] + public async Task CanBeStoppedDuringRetryDelay() + { + bool ExpectedErrors(WriteContext writeContext) + { + return writeContext.LoggerName == typeof(HubConnection).FullName && + (writeContext.EventId.Name == "ServerDisconnectedWithError" || + writeContext.EventId.Name == "ReconnectingWithError" || + writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || + writeContext.EventId.Name == "ErrorStartingConnection"); + } + + using (StartVerifiableLog(ExpectedErrors)) + { + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var testConnectionFactory = new ReconnectingConnectionFactory(); + builder.Services.AddSingleton(testConnectionFactory); + + var retryContexts = new List(); + var mockReconnectPolicy = new Mock(); + mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + { + retryContexts.Add(context); + // Hopefully this test never takes over a minute. + return TimeSpan.FromMinutes(1); + }); + builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + + await using var hubConnection = builder.Build(); + var reconnectingCount = 0; + var reconnectedCount = 0; + var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + hubConnection.Reconnecting += error => + { + reconnectingCount++; + reconnectingErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + hubConnection.Reconnected += connectionId => + { + reconnectedCount++; + return Task.CompletedTask; + }; + + hubConnection.Closed += error => + { + closedErrorTcs.SetResult(error); + return Task.CompletedTask; + }; + + // Allow the first connection to start successfully. + await hubConnection.StartAsync().OrTimeout(); + + var firstException = new Exception(); + (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); + + Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Same(firstException, retryContexts[0].RetryReason); + Assert.Equal(0, retryContexts[0].PreviousRetryCount); + Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); + + await hubConnection.StopAsync().OrTimeout(); + + Assert.IsType(await closedErrorTcs.Task.OrTimeout()); + Assert.Single(retryContexts); + Assert.Equal(1, reconnectingCount); + Assert.Equal(0, reconnectedCount); + } + } + + private class ReconnectingConnectionFactory : IConnectionFactory + { + public readonly Func _testConnectionFactory; + public TaskCompletionSource _testConnectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public ReconnectingConnectionFactory() + : this(() => new TestConnection()) + { + } + + public ReconnectingConnectionFactory(Func testConnectionFactory) + { + _testConnectionFactory = testConnectionFactory; + } + + public Task GetNextOrCurrentTestConnection() + { + return _testConnectionTcs.Task; + } + + public async Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) + { + var testConnection = _testConnectionFactory(); + + _testConnectionTcs.SetResult(testConnection); + + try + { + return await testConnection.StartAsync(transferFormat); + } + catch + { + _testConnectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + throw; } } - var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(OnTestConnectionStart)); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => + public async Task DisposeAsync(ConnectionContext connection) { - retryContexts.Add(context); - return TimeSpan.Zero; - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); + var disposingTestConnection = await _testConnectionTcs.Task; - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - // Allow the first connection to start successfully. - connectionStartTcs.SetResult(null); - await hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - var secondException = new Exception(); - var stopTask = hubConnection.StopAsync(); - connectionStartTcs.SetResult(null); - - Assert.IsType(await closedErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Equal(1, reconnectingCount); - Assert.Equal(0, reconnectedCount); - await stopTask.OrTimeout(); - } - } - - [Fact] - public async Task ReconnectCanBeStoppedDuringRetryDelay() - { - bool ExpectedErrors(WriteContext writeContext) - { - return writeContext.LoggerName == typeof(HubConnection).FullName && - (writeContext.EventId.Name == "ServerDisconnectedWithError" || - writeContext.EventId.Name == "ReconnectingWithError" || - writeContext.EventId.Name == "ErrorReceivingHandshakeResponse" || - writeContext.EventId.Name == "ErrorStartingConnection"); - } - - using (StartVerifiableLog(ExpectedErrors)) - { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); - var testConnectionFactory = new ReconnectingConnectionFactory(); - builder.Services.AddSingleton(testConnectionFactory); - - var retryContexts = new List(); - var mockReconnectPolicy = new Mock(); - mockReconnectPolicy.Setup(p => p.NextRetryDelay(It.IsAny())).Returns(context => - { - retryContexts.Add(context); - // Hopefully this test never takes over a minute. - return TimeSpan.FromMinutes(1); - }); - builder.WithAutomaticReconnect(mockReconnectPolicy.Object); - - await using var hubConnection = builder.Build(); - var reconnectingCount = 0; - var reconnectedCount = 0; - var reconnectingErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var closedErrorTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - hubConnection.Reconnecting += error => - { - reconnectingCount++; - reconnectingErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - hubConnection.Reconnected += connectionId => - { - reconnectedCount++; - return Task.CompletedTask; - }; - - hubConnection.Closed += error => - { - closedErrorTcs.SetResult(error); - return Task.CompletedTask; - }; - - // Allow the first connection to start successfully. - await hubConnection.StartAsync().OrTimeout(); - - var firstException = new Exception(); - (await testConnectionFactory.GetNextOrCurrentTestConnection()).CompleteFromTransport(firstException); - - Assert.Same(firstException, await reconnectingErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Same(firstException, retryContexts[0].RetryReason); - Assert.Equal(0, retryContexts[0].PreviousRetryCount); - Assert.Equal(TimeSpan.Zero, retryContexts[0].ElapsedTime); - - await hubConnection.StopAsync().OrTimeout(); - - Assert.IsType(await closedErrorTcs.Task.OrTimeout()); - Assert.Single(retryContexts); - Assert.Equal(1, reconnectingCount); - Assert.Equal(0, reconnectedCount); - } - } - - private class ReconnectingConnectionFactory : IConnectionFactory - { - public readonly Func _testConnectionFactory; - public TaskCompletionSource _testConnectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - public ReconnectingConnectionFactory() - : this (() => new TestConnection()) - { - } - - public ReconnectingConnectionFactory(Func testConnectionFactory) - { - _testConnectionFactory = testConnectionFactory; - } - - public Task GetNextOrCurrentTestConnection() - { - return _testConnectionTcs.Task; - } - - public async Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) - { - var testConnection = _testConnectionFactory(); - - _testConnectionTcs.SetResult(testConnection); - - try - { - return await testConnection.StartAsync(transferFormat); - } - catch - { _testConnectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - throw; + + await disposingTestConnection.DisposeAsync(); } } - - public async Task DisposeAsync(ConnectionContext connection) - { - var disposingTestConnection = await _testConnectionTcs.Task; - - _testConnectionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - await disposingTestConnection.DisposeAsync(); - } } } } From b344b2b1593b2c663a8b13e520c4bb445532e658 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Sat, 25 May 2019 01:45:43 +0000 Subject: [PATCH 17/95] [master] Update dependencies from dotnet/arcadeaspnet/AspNetCore-Tooling (#10532) * Update dependencies from https://github.com/dotnet/arcade build 20190524.3 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19274.3 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19274.3 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19274.3 * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190524.3 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19274.3 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19274.3 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19274.3 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19274.3 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19270.15 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27722-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19273.3 (parent: Microsoft.CodeAnalysis.Razor) --- eng/Version.Details.xml | 356 +++++++++++++-------------- eng/Versions.props | 174 ++++++------- eng/common/PublishToPackageFeed.proj | 1 + eng/common/tools.sh | 9 +- global.json | 4 +- 5 files changed, 273 insertions(+), 271 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 28ee92afa5..fd155f6a78 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,21 +13,21 @@ https://github.com/aspnet/Blazor c879c3a911b4c2d6cccd4d6ff2de86a6949cda88 - + https://github.com/aspnet/AspNetCore-Tooling - 5bcd00877984c63293665f75bad20f522132638a + d0e71c8895dba831a9c3a39459be028dcef03f95 - + https://github.com/aspnet/AspNetCore-Tooling - 5bcd00877984c63293665f75bad20f522132638a + d0e71c8895dba831a9c3a39459be028dcef03f95 - + https://github.com/aspnet/AspNetCore-Tooling - 5bcd00877984c63293665f75bad20f522132638a + d0e71c8895dba831a9c3a39459be028dcef03f95 - + https://github.com/aspnet/AspNetCore-Tooling - 5bcd00877984c63293665f75bad20f522132638a + d0e71c8895dba831a9c3a39459be028dcef03f95 https://github.com/aspnet/EntityFrameworkCore @@ -57,352 +57,352 @@ https://github.com/aspnet/EntityFrameworkCore 08edd86216be4857b45b47bf0a9b29e98e525c05 - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 https://github.com/dotnet/corefx a28176b5ec68b6da1472934fe9493790d1665cae - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/dotnet/core-setup - c3145b06ba5151d5eafcf177a2e380f7acb61189 + 4573cea18beb40dc9a41014798060e48ff044c63 - + https://github.com/dotnet/core-setup - c3145b06ba5151d5eafcf177a2e380f7acb61189 + 4573cea18beb40dc9a41014798060e48ff044c63 - + https://github.com/dotnet/corefx - ef1a5aa730098b6c3350977a991232c1ff11cfe3 + 11eba7947e9736109c588a1cfcb77987cb80f570 - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d - + https://github.com/dotnet/arcade - 86e674361bdcefecbd8199ab62d0b1a6cb25703d + cb9317c52c891fba6c6c43901ec8621fe2b9bb3b - + https://github.com/dotnet/arcade - 86e674361bdcefecbd8199ab62d0b1a6cb25703d + cb9317c52c891fba6c6c43901ec8621fe2b9bb3b - + https://github.com/dotnet/arcade - 86e674361bdcefecbd8199ab62d0b1a6cb25703d + cb9317c52c891fba6c6c43901ec8621fe2b9bb3b - + https://github.com/aspnet/Extensions - da5dee978f4eece3de85b776dfcf9dcc4cf5b109 + dde6dba5fe9f240c69bc0049dd40fc967d613e0d diff --git a/eng/Versions.props b/eng/Versions.props index 1776c1e4ab..d445b635a8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,96 +21,96 @@ --> - 1.0.0-beta.19272.13 + 1.0.0-beta.19274.3 - 3.0.0-preview6-27720-09 - 3.0.0-preview6-27720-09 + 3.0.0-preview6-27722-02 + 3.0.0-preview6-27722-02 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 4.7.0-preview6.19264.9 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 1.7.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 - 4.6.0-preview6.19270.12 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 1.7.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 + 4.6.0-preview6.19270.15 - 3.0.0-preview6.19270.12 + 3.0.0-preview6.19270.15 0.10.0-preview6.19273.9 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 - 3.0.0-preview6.19271.2 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 + 3.0.0-preview6.19273.3 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 @@ -120,10 +120,10 @@ 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 - 3.0.0-preview6.19272.2 - 3.0.0-preview6.19272.2 - 3.0.0-preview6.19272.2 - 3.0.0-preview6.19272.2 + 3.0.0-preview6.19274.3 + 3.0.0-preview6.19274.3 + 3.0.0-preview6.19274.3 + 3.0.0-preview6.19274.3 - + https://github.com/dotnet/corefx - 11eba7947e9736109c588a1cfcb77987cb80f570 + 41489a93acf3f36abcaaaea2003a8fdbb577cf35 - + https://github.com/aspnet/Extensions - dde6dba5fe9f240c69bc0049dd40fc967d613e0d + 8550f61acc7d78990b7c67ea1647eaff29f80dc3 https://github.com/dotnet/arcade @@ -400,9 +400,9 @@ https://github.com/dotnet/arcade cb9317c52c891fba6c6c43901ec8621fe2b9bb3b - + https://github.com/aspnet/Extensions - dde6dba5fe9f240c69bc0049dd40fc967d613e0d + 8550f61acc7d78990b7c67ea1647eaff29f80dc3 diff --git a/eng/Versions.props b/eng/Versions.props index d445b635a8..9937756229 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,94 +23,94 @@ 1.0.0-beta.19274.3 - 3.0.0-preview6-27722-02 - 3.0.0-preview6-27722-02 + 3.0.0-preview6-27723-08 + 3.0.0-preview6-27723-08 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 4.7.0-preview6.19264.9 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 1.7.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 - 4.6.0-preview6.19270.15 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 1.7.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 + 4.6.0-preview6.19273.5 - 3.0.0-preview6.19270.15 + 3.0.0-preview6.19273.5 0.10.0-preview6.19273.9 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 - 3.0.0-preview6.19273.3 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 + 3.0.0-preview6.19274.1 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 @@ -120,10 +120,10 @@ 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 - 3.0.0-preview6.19274.3 - 3.0.0-preview6.19274.3 - 3.0.0-preview6.19274.3 - 3.0.0-preview6.19274.3 + 3.0.0-preview6.19274.4 + 3.0.0-preview6.19274.4 + 3.0.0-preview6.19274.4 + 3.0.0-preview6.19274.4 $(MSBuildThisFileDirectory)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.targets - true + true $(ArtifactsObjDir)TargetingPack.Layout\$(Configuration)\ diff --git a/build.ps1 b/build.ps1 index b382ae3867..726946a89b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -258,13 +258,12 @@ elseif ($Projects) { } # When adding new sub-group build flags, add them to this check. elseif((-not $BuildNative) -and (-not $BuildManaged) -and (-not $BuildNodeJS) -and (-not $BuildInstallers) -and (-not $BuildJava)) { - Write-Warning "No default group of projects was specified, so building the 'managed' and 'native' subsets of projects. Run ``build.cmd -help`` for more details." + Write-Warning "No default group of projects was specified, so building the 'managed' subsets of projects. Run ``build.cmd -help`` for more details." # This goal of this is to pick a sensible default for `build.cmd` with zero arguments. # Now that we support subfolder invokations of build.cmd, we will be pushing to have build.cmd build everything (-all) by default $BuildManaged = $true - $BuildNative = $true } if ($BuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=true" } diff --git a/build/repo.props b/build/repo.props index 4282500c14..16748ee70a 100644 --- a/build/repo.props +++ b/build/repo.props @@ -34,6 +34,7 @@ true true true + true diff --git a/src/Shared/E2ETesting/E2ETesting.targets b/src/Shared/E2ETesting/E2ETesting.targets index 628880eb53..3b27dc75f7 100644 --- a/src/Shared/E2ETesting/E2ETesting.targets +++ b/src/Shared/E2ETesting/E2ETesting.targets @@ -37,6 +37,7 @@ + From cc40c37765caec9f78984179fd1fb202e7d7c702 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 27 May 2019 04:08:25 -0500 Subject: [PATCH 23/95] Work around mono url differences (#10551) --- src/Components/Browser.JS/dist/Debug/blazor.webassembly.js | 1 + src/Components/Browser.JS/dist/Release/blazor.webassembly.js | 2 +- src/Components/Browser.JS/src/Platform/Mono/MonoPlatform.ts | 1 + src/Components/Browser.JS/src/Platform/Mono/MonoTypes.d.ts | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Components/Browser.JS/dist/Debug/blazor.webassembly.js b/src/Components/Browser.JS/dist/Debug/blazor.webassembly.js index 328a626aea..71ddb8489c 100644 --- a/src/Components/Browser.JS/dist/Debug/blazor.webassembly.js +++ b/src/Components/Browser.JS/dist/Debug/blazor.webassembly.js @@ -880,6 +880,7 @@ function createEmscriptenModuleInstance(loadAssemblyUrls, onReady, onError) { }); }); module.postRun.push(function () { + MONO.mono_wasm_setenv("MONO_URI_DOTNETRELATIVEORABSOLUTE", "true"); var load_runtime = Module.cwrap('mono_wasm_load_runtime', null, ['string', 'number']); load_runtime(appBinDirName, MonoDebugger_1.hasDebuggingEnabled() ? 1 : 0); MONO.mono_wasm_runtime_is_ready = true; diff --git a/src/Components/Browser.JS/dist/Release/blazor.webassembly.js b/src/Components/Browser.JS/dist/Release/blazor.webassembly.js index 0634a57f18..514e0ee94c 100644 --- a/src/Components/Browser.JS/dist/Release/blazor.webassembly.js +++ b/src/Components/Browser.JS/dist/Release/blazor.webassembly.js @@ -1 +1 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=42)}([,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(23),o=n(9),a={};function i(e,t,n){var o=a[e];o||(o=a[e]=new r.BrowserRenderer(e)),o.attachRootComponentToLogicalElement(n,t)}t.attachRootComponentToLogicalElement=i,t.attachRootComponentToElement=function(e,t,n){var r=document.querySelector(t);if(!r)throw new Error("Could not find any element matching selector '"+t+"'.");i(e,o.toLogicalElement(r,!0),n)},t.renderBatch=function(e,t){var n=a[e];if(!n)throw new Error("There is no browser renderer with ID "+e+".");for(var r=t.arrayRangeReader,o=t.updatedComponents(),i=r.values(o),u=r.count(o),l=t.referenceFrames(),s=r.values(l),c=t.diffReader,f=0;f0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(s(a)&&s(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(l(a))throw new Error("Not implemented: moving existing logical children");var i=s(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=l,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return s(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=s,t.permuteLogicalChildren=function(e,t){var n=s(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=l(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]-1?a.substring(0,u):"",s=u>-1?a.substring(u+1):a,c=t.monoPlatform.findMethod(e,l,s,i);t.monoPlatform.callMethod(c,null,r)},callMethod:function(e,n,r){if(r.length>4)throw new Error("Currently, MonoPlatform supports passing a maximum of 4 arguments from JS to .NET. You tried to pass "+r.length+".");var o=Module.stackSave();try{for(var a=Module.stackAlloc(r.length),u=Module.stackAlloc(4),l=0;l0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(s(a)&&s(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(l(a))throw new Error("Not implemented: moving existing logical children");var i=s(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=l,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return s(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=s,t.permuteLogicalChildren=function(e,t){var n=s(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=l(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]-1?a.substring(0,u):"",s=u>-1?a.substring(u+1):a,c=t.monoPlatform.findMethod(e,l,s,i);t.monoPlatform.callMethod(c,null,r)},callMethod:function(e,n,r){if(r.length>4)throw new Error("Currently, MonoPlatform supports passing a maximum of 4 arguments from JS to .NET. You tried to pass "+r.length+".");var o=Module.stackSave();try{for(var a=Module.stackAlloc(r.length),u=Module.stackAlloc(4),l=0;l { + MONO.mono_wasm_setenv("MONO_URI_DOTNETRELATIVEORABSOLUTE","true"); const load_runtime = Module.cwrap('mono_wasm_load_runtime', null, ['string', 'number']); load_runtime(appBinDirName, hasDebuggingEnabled() ? 1 : 0); MONO.mono_wasm_runtime_is_ready = true; diff --git a/src/Components/Browser.JS/src/Platform/Mono/MonoTypes.d.ts b/src/Components/Browser.JS/src/Platform/Mono/MonoTypes.d.ts index 1001286e55..783af016f7 100644 --- a/src/Components/Browser.JS/src/Platform/Mono/MonoTypes.d.ts +++ b/src/Components/Browser.JS/src/Platform/Mono/MonoTypes.d.ts @@ -24,4 +24,5 @@ declare namespace Mono { declare namespace MONO { var loaded_files: string[]; var mono_wasm_runtime_is_ready: boolean; + function mono_wasm_setenv (name: string, value: string): void; } From 01d20c134caa6f2142ac44260089c29d0ef9833e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 27 May 2019 15:37:02 +0000 Subject: [PATCH 24/95] Update dependencies from https://github.com/dotnet/arcade build 20190524.6 (#10553) - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19274.6 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19274.6 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19274.6 --- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 2 +- global.json | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 85700c5268..45a9967926 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -388,17 +388,17 @@ https://github.com/aspnet/Extensions 8550f61acc7d78990b7c67ea1647eaff29f80dc3 - + https://github.com/dotnet/arcade - cb9317c52c891fba6c6c43901ec8621fe2b9bb3b + b5016f5688dc8ca9f3e4811ee7e2e86ad8907a40 - + https://github.com/dotnet/arcade - cb9317c52c891fba6c6c43901ec8621fe2b9bb3b + b5016f5688dc8ca9f3e4811ee7e2e86ad8907a40 - + https://github.com/dotnet/arcade - cb9317c52c891fba6c6c43901ec8621fe2b9bb3b + b5016f5688dc8ca9f3e4811ee7e2e86ad8907a40 https://github.com/aspnet/Extensions diff --git a/eng/Versions.props b/eng/Versions.props index 9937756229..557ca946e5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,7 +21,7 @@ --> - 1.0.0-beta.19274.3 + 1.0.0-beta.19274.6 3.0.0-preview6-27723-08 3.0.0-preview6-27723-08 diff --git a/global.json b/global.json index 536a0b4973..11a7eb5a26 100644 --- a/global.json +++ b/global.json @@ -8,7 +8,7 @@ }, "msbuild-sdks": { "Yarn.MSBuild": "1.15.2", - "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19274.3", - "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19274.3" + "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19274.6", + "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19274.6" } } From 9969e99ef4170085dae20aab3785d8d15e510dfd Mon Sep 17 00:00:00 2001 From: Dylan Dmitri Gray Date: Mon, 27 May 2019 14:46:14 -0700 Subject: [PATCH 25/95] Dylan/request throttle (#10413) * request throttling -- initial implementation * prevented semaphore leak; added xml docs * small doc fixes * reference document * Added internals folder, added structured logging, * removed typo'd dependency * no default MaxConcurrentRequests; other polishing * renamed SemaphoreWrapper->RequestQueue; cleanup * moved SyncPoint; prevented possible semaphore leak * adjusting feedback * regen refs * Final changes! --- ...rosoft.AspNetCore.RequestThrottling.csproj | 4 +- ...NetCore.RequestThrottling.netcoreapp3.0.cs | 21 ++++ .../sample/RequestThrottlingSample.csproj | 4 +- .../RequestThrottling/sample/Startup.cs | 19 ++- .../src/Internal/RequestQueue.cs | 64 ++++++++++ ...rosoft.AspNetCore.RequestThrottling.csproj | 8 +- .../src/RequestThrottlingExtensions.cs | 29 +++++ .../src/RequestThrottlingMiddleware.cs | 116 ++++++++++++++++++ .../src/RequestThrottlingOptions.cs | 19 +++ .../RequestThrottling/src/SemaphoreWrapper.cs | 38 ------ ....AspNetCore.RequestThrottling.Tests.csproj | 8 +- .../RequestThrottling/test/MiddlewareTests.cs | 86 +++++++++++++ ...reWrapperTests.cs => RequestQueueTests.cs} | 42 ++++--- .../RequestThrottling/test/TestUtils.cs | 28 +++++ .../samples/ResponseCachingSample/Startup.cs | 2 +- ...icrosoft.AspNetCore.ResponseCaching.csproj | 2 +- .../Microsoft.AspNetCore.ANCMSymbols.csproj | 10 ++ ...ft.AspNetCore.ANCMSymbols.netcoreapp3.0.cs | 3 + .../SyncPoint}/SyncPoint.cs | 2 +- ...HttpConnectionTests.ConnectionLifecycle.cs | 5 +- .../HubConnectionTests.ConnectionLifecycle.cs | 1 + ...oft.AspNetCore.SignalR.Client.Tests.csproj | 3 +- .../ServerSentEventsTransportTests.cs | 1 + .../test/HttpConnectionDispatcherTests.cs | 1 + ...t.AspNetCore.Http.Connections.Tests.csproj | 3 +- .../Microsoft.AspNetCore.SignalR.Tests.csproj | 7 +- .../SignalR/test/SerializedHubMessageTests.cs | 1 + 27 files changed, 455 insertions(+), 72 deletions(-) create mode 100644 src/Middleware/RequestThrottling/src/Internal/RequestQueue.cs create mode 100644 src/Middleware/RequestThrottling/src/RequestThrottlingExtensions.cs create mode 100644 src/Middleware/RequestThrottling/src/RequestThrottlingMiddleware.cs create mode 100644 src/Middleware/RequestThrottling/src/RequestThrottlingOptions.cs delete mode 100644 src/Middleware/RequestThrottling/src/SemaphoreWrapper.cs create mode 100644 src/Middleware/RequestThrottling/test/MiddlewareTests.cs rename src/Middleware/RequestThrottling/test/{SemaphoreWrapperTests.cs => RequestQueueTests.cs} (60%) create mode 100644 src/Middleware/RequestThrottling/test/TestUtils.cs create mode 100644 src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.csproj create mode 100644 src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.netcoreapp3.0.cs rename src/{SignalR/common/testassets/Tests.Utils => Shared/SyncPoint}/SyncPoint.cs (98%) diff --git a/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.csproj b/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.csproj index 0a1bcdd0b9..8e8c1dfce6 100644 --- a/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.csproj +++ b/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.csproj @@ -5,6 +5,8 @@ - + + + diff --git a/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.netcoreapp3.0.cs b/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.netcoreapp3.0.cs index 618082bc4a..b5a3acf406 100644 --- a/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.netcoreapp3.0.cs +++ b/src/Middleware/RequestThrottling/ref/Microsoft.AspNetCore.RequestThrottling.netcoreapp3.0.cs @@ -1,3 +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. +namespace Microsoft.AspNetCore.Builder +{ + public static partial class RequestThrottlingExtensions + { + public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseRequestThrottling(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { throw null; } + } +} +namespace Microsoft.AspNetCore.RequestThrottling +{ + public partial class RequestThrottlingMiddleware + { + public RequestThrottlingMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.Extensions.Options.IOptions options) { } + [System.Diagnostics.DebuggerStepThroughAttribute] + public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; } + } + public partial class RequestThrottlingOptions + { + public RequestThrottlingOptions() { } + public int? MaxConcurrentRequests { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } +} diff --git a/src/Middleware/RequestThrottling/sample/RequestThrottlingSample.csproj b/src/Middleware/RequestThrottling/sample/RequestThrottlingSample.csproj index 0f80e6516a..9f49f115c0 100644 --- a/src/Middleware/RequestThrottling/sample/RequestThrottlingSample.csproj +++ b/src/Middleware/RequestThrottling/sample/RequestThrottlingSample.csproj @@ -1,13 +1,13 @@ - + netcoreapp3.0 - + diff --git a/src/Middleware/RequestThrottling/sample/Startup.cs b/src/Middleware/RequestThrottling/sample/Startup.cs index 95a94be56d..113f48d860 100644 --- a/src/Middleware/RequestThrottling/sample/Startup.cs +++ b/src/Middleware/RequestThrottling/sample/Startup.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; +// 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.IO; -using System.Linq; -using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.RequestThrottling; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -19,13 +19,20 @@ namespace RequestThrottlingSample // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { + services.Configure(options => + { + options.MaxConcurrentRequests = 2; + }); } - public void Configure(IApplicationBuilder app, IWebHostEnvironment environment) + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { + app.UseRequestThrottling(); + app.Run(async context => { - await context.Response.WriteAsync("Hello world!"); + await context.Response.WriteAsync("Hello Request Throttling!

"); + await Task.Delay(1000); }); } diff --git a/src/Middleware/RequestThrottling/src/Internal/RequestQueue.cs b/src/Middleware/RequestThrottling/src/Internal/RequestQueue.cs new file mode 100644 index 0000000000..a09ddeb79d --- /dev/null +++ b/src/Middleware/RequestThrottling/src/Internal/RequestQueue.cs @@ -0,0 +1,64 @@ +// 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.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.RequestThrottling.Internal +{ + internal class RequestQueue : IDisposable + { + private SemaphoreSlim _semaphore; + private object _waitingRequestsLock = new object(); + public readonly int MaxConcurrentRequests; + public int WaitingRequests { get; private set; } + + public RequestQueue(int maxConcurrentRequests) + { + MaxConcurrentRequests = maxConcurrentRequests; + _semaphore = new SemaphoreSlim(maxConcurrentRequests); + } + + public async Task EnterQueue() + { + var waitInQueueTask = _semaphore.WaitAsync(); + + var needsToWaitOnQueue = !waitInQueueTask.IsCompletedSuccessfully; + if (needsToWaitOnQueue) + { + lock (_waitingRequestsLock) + { + WaitingRequests++; + } + + await waitInQueueTask; + + lock (_waitingRequestsLock) + { + WaitingRequests--; + } + } + } + + public void Release() + { + _semaphore.Release(); + } + + public int Count + { + get => _semaphore.CurrentCount; + } + + public int ConcurrentRequests + { + get => MaxConcurrentRequests - _semaphore.CurrentCount; + } + + public void Dispose() + { + _semaphore.Dispose(); + } + } +} diff --git a/src/Middleware/RequestThrottling/src/Microsoft.AspNetCore.RequestThrottling.csproj b/src/Middleware/RequestThrottling/src/Microsoft.AspNetCore.RequestThrottling.csproj index 5014e9cec5..0090f373c0 100644 --- a/src/Middleware/RequestThrottling/src/Microsoft.AspNetCore.RequestThrottling.csproj +++ b/src/Middleware/RequestThrottling/src/Microsoft.AspNetCore.RequestThrottling.csproj @@ -1,4 +1,4 @@ - + ASP.NET Core middleware for queuing incoming HTTP requests, to avoid threadpool starvation. @@ -7,4 +7,10 @@ aspnetcore;queue;queuing + + + + + + diff --git a/src/Middleware/RequestThrottling/src/RequestThrottlingExtensions.cs b/src/Middleware/RequestThrottling/src/RequestThrottlingExtensions.cs new file mode 100644 index 0000000000..17968b44d5 --- /dev/null +++ b/src/Middleware/RequestThrottling/src/RequestThrottlingExtensions.cs @@ -0,0 +1,29 @@ +// 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.RequestThrottling; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods for adding the to an application. + /// + public static class RequestThrottlingExtensions + { + /// + /// Adds the to limit the number of concurrently-executing requests. + /// + /// The . + /// The . + public static IApplicationBuilder UseRequestThrottling(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + return app.UseMiddleware(); + } + } +} diff --git a/src/Middleware/RequestThrottling/src/RequestThrottlingMiddleware.cs b/src/Middleware/RequestThrottling/src/RequestThrottlingMiddleware.cs new file mode 100644 index 0000000000..9eaec8cfbc --- /dev/null +++ b/src/Middleware/RequestThrottling/src/RequestThrottlingMiddleware.cs @@ -0,0 +1,116 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.RequestThrottling.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.RequestThrottling +{ + /// + /// Limits the number of concurrent requests allowed in the application. + /// + public class RequestThrottlingMiddleware + { + private readonly RequestQueue _requestQueue; + private readonly RequestThrottlingOptions _options; + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + /// + /// Creates a new . + /// + /// The representing the next middleware in the pipeline. + /// The used for logging. + /// The containing the initialization parameters. + public RequestThrottlingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions options) + { + if (options.Value.MaxConcurrentRequests == null) + { + throw new ArgumentException("The value of 'options.MaxConcurrentRequests' must be specified.", nameof(options)); + } + + _next = next; + _logger = loggerFactory.CreateLogger(); + _options = options.Value; + _requestQueue = new RequestQueue(_options.MaxConcurrentRequests.Value); + } + + /// + /// Invokes the logic of the middleware. + /// + /// The . + /// A that completes when the request leaves. + public async Task Invoke(HttpContext context) + { + var waitInQueueTask = _requestQueue.EnterQueue(); + + if (waitInQueueTask.IsCompletedSuccessfully) + { + RequestThrottlingLog.RequestRunImmediately(_logger); + } + else + { + RequestThrottlingLog.RequestEnqueued(_logger, WaitingRequests); + await waitInQueueTask; + RequestThrottlingLog.RequestDequeued(_logger, WaitingRequests); + } + + try + { + await _next(context); + } + finally + { + _requestQueue.Release(); + } + } + + /// + /// The number of live requests that are downstream from this middleware. + /// Cannot exceeed . + /// + internal int ConcurrentRequests + { + get => _requestQueue.ConcurrentRequests; + } + + /// + /// Number of requests currently enqueued and waiting to be processed. + /// + internal int WaitingRequests + { + get => _requestQueue.WaitingRequests; + } + + private static class RequestThrottlingLog + { + private static readonly Action _requestEnqueued = + LoggerMessage.Define(LogLevel.Debug, new EventId(1, "RequestEnqueued"), "Concurrent request limit reached, queuing request. Current queue length: {QueuedRequests}."); + + private static readonly Action _requestDequeued = + LoggerMessage.Define(LogLevel.Debug, new EventId(2, "RequestDequeued"), "Request dequeued. Current queue length: {QueuedRequests}."); + + private static readonly Action _requestRunImmediately = + LoggerMessage.Define(LogLevel.Debug, new EventId(3, "RequestRunImmediately"), "Concurrent request limit has not been reached, running request immediately."); + + internal static void RequestEnqueued(ILogger logger, int queuedRequests) + { + _requestEnqueued(logger, queuedRequests, null); + } + + internal static void RequestDequeued(ILogger logger, int queuedRequests) + { + _requestDequeued(logger, queuedRequests, null); + } + + internal static void RequestRunImmediately(ILogger logger) + { + _requestRunImmediately(logger, null); + } + } + } +} diff --git a/src/Middleware/RequestThrottling/src/RequestThrottlingOptions.cs b/src/Middleware/RequestThrottling/src/RequestThrottlingOptions.cs new file mode 100644 index 0000000000..64a832640f --- /dev/null +++ b/src/Middleware/RequestThrottling/src/RequestThrottlingOptions.cs @@ -0,0 +1,19 @@ +// 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.RequestThrottling; + +namespace Microsoft.AspNetCore.RequestThrottling +{ + /// + /// Specifies options for the . + /// + public class RequestThrottlingOptions + { + /// + /// Maximum number of concurrent requests. Any extras will be queued on the server. + /// This is null by default because the correct value is application specific. This option must be configured by the application. + /// + public int? MaxConcurrentRequests { get; set; } + } +} diff --git a/src/Middleware/RequestThrottling/src/SemaphoreWrapper.cs b/src/Middleware/RequestThrottling/src/SemaphoreWrapper.cs deleted file mode 100644 index 4c79b94777..0000000000 --- a/src/Middleware/RequestThrottling/src/SemaphoreWrapper.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.AspNetCore.RequestThrottling -{ - internal class SemaphoreWrapper : IDisposable - { - private SemaphoreSlim _semaphore; - - public SemaphoreWrapper(int queueLength) - { - _semaphore = new SemaphoreSlim(queueLength); - } - - public Task EnterQueue() - { - return _semaphore.WaitAsync(); - } - - public void LeaveQueue() - { - _semaphore.Release(); - } - - public int Count - { - get => _semaphore.CurrentCount; - } - - public void Dispose() - { - _semaphore.Dispose(); - } - } -} diff --git a/src/Middleware/RequestThrottling/test/Microsoft.AspNetCore.RequestThrottling.Tests.csproj b/src/Middleware/RequestThrottling/test/Microsoft.AspNetCore.RequestThrottling.Tests.csproj index 8c0dd8e989..78b1c88692 100644 --- a/src/Middleware/RequestThrottling/test/Microsoft.AspNetCore.RequestThrottling.Tests.csproj +++ b/src/Middleware/RequestThrottling/test/Microsoft.AspNetCore.RequestThrottling.Tests.csproj @@ -1,10 +1,16 @@ - + netcoreapp3.0 + + + + + + diff --git a/src/Middleware/RequestThrottling/test/MiddlewareTests.cs b/src/Middleware/RequestThrottling/test/MiddlewareTests.cs new file mode 100644 index 0000000000..8124cddb49 --- /dev/null +++ b/src/Middleware/RequestThrottling/test/MiddlewareTests.cs @@ -0,0 +1,86 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Internal; +using Xunit; + +namespace Microsoft.AspNetCore.RequestThrottling.Tests +{ + public class MiddlewareTests + { + [Fact] + public async Task RequestsCanEnterIfSpaceAvailible() + { + var middleware = TestUtils.CreateTestMiddleWare(maxConcurrentRequests: 1); + var context = new DefaultHttpContext(); + + // a request should go through with no problems + await middleware.Invoke(context).OrTimeout(); + } + + [Fact] + public async Task SemaphoreStatePreservedIfRequestsError() + { + var middleware = TestUtils.CreateTestMiddleWare( + maxConcurrentRequests: 1, + next: httpContext => + { + throw new DivideByZeroException(); + }); + + Assert.Equal(0, middleware.ConcurrentRequests); + + await Assert.ThrowsAsync(() => middleware.Invoke(new DefaultHttpContext())); + + Assert.Equal(0, middleware.ConcurrentRequests); + } + + [Fact] + public async Task QueuedRequestsContinueWhenSpaceBecomesAvailible() + { + var blocker = new SyncPoint(); + var firstRequest = true; + + var middleware = TestUtils.CreateTestMiddleWare( + maxConcurrentRequests: 1, + next: httpContext => + { + if (firstRequest) + { + firstRequest = false; + return blocker.WaitToContinue(); + } + return Task.CompletedTask; + }); + + // t1 (as the first request) is blocked by the tcs blocker + var t1 = middleware.Invoke(new DefaultHttpContext()); + Assert.Equal(1, middleware.ConcurrentRequests); + Assert.Equal(0, middleware.WaitingRequests); + + // t2 is blocked from entering the server since t1 already exists there + // note: increasing MaxConcurrentRequests would allow t2 through while t1 is blocked + var t2 = middleware.Invoke(new DefaultHttpContext()); + Assert.Equal(1, middleware.ConcurrentRequests); + Assert.Equal(1, middleware.WaitingRequests); + + // unblock the first task, and the second should follow + blocker.Continue(); + await t1.OrTimeout(); + await t2.OrTimeout(); + } + + [Fact] + public void InvalidArgumentIfMaxConcurrentRequestsIsNull() + { + var ex = Assert.Throws(() => + { + TestUtils.CreateTestMiddleWare(maxConcurrentRequests: null); + }); + Assert.Equal("options", ex.ParamName); + } + } +} diff --git a/src/Middleware/RequestThrottling/test/SemaphoreWrapperTests.cs b/src/Middleware/RequestThrottling/test/RequestQueueTests.cs similarity index 60% rename from src/Middleware/RequestThrottling/test/SemaphoreWrapperTests.cs rename to src/Middleware/RequestThrottling/test/RequestQueueTests.cs index b5cdfce18f..67eeda5d67 100644 --- a/src/Middleware/RequestThrottling/test/SemaphoreWrapperTests.cs +++ b/src/Middleware/RequestThrottling/test/RequestQueueTests.cs @@ -1,34 +1,48 @@ // 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 Xunit; -using System.Threading; using System.Threading.Tasks; -using System; -using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Testing; +using Microsoft.AspNetCore.RequestThrottling.Internal; +using Xunit; namespace Microsoft.AspNetCore.RequestThrottling.Tests { - public class SemaphoreWrapperTests + public class RequestQueueTests { [Fact] - public async Task TracksQueueLength() + public async Task LimitsIncomingRequests() { - using var s = new SemaphoreWrapper(1); + using var s = new RequestQueue(1); Assert.Equal(1, s.Count); await s.EnterQueue().OrTimeout(); Assert.Equal(0, s.Count); - s.LeaveQueue(); + s.Release(); Assert.Equal(1, s.Count); } + [Fact] + public async Task TracksQueueLength() + { + using var s = new RequestQueue(1); + Assert.Equal(0, s.WaitingRequests); + + await s.EnterQueue(); + Assert.Equal(0, s.WaitingRequests); + + var enterQueueTask = s.EnterQueue(); + Assert.Equal(1, s.WaitingRequests); + + s.Release(); + await enterQueueTask; + Assert.Equal(0, s.WaitingRequests); + } + [Fact] public void DoesNotWaitIfSpaceAvailible() { - using var s = new SemaphoreWrapper(2); + using var s = new RequestQueue(2); var t1 = s.EnterQueue(); Assert.True(t1.IsCompleted); @@ -43,21 +57,21 @@ namespace Microsoft.AspNetCore.RequestThrottling.Tests [Fact] public async Task WaitsIfNoSpaceAvailible() { - using var s = new SemaphoreWrapper(1); + using var s = new RequestQueue(1); await s.EnterQueue().OrTimeout(); var waitingTask = s.EnterQueue(); Assert.False(waitingTask.IsCompleted); - s.LeaveQueue(); + s.Release(); await waitingTask.OrTimeout(); } [Fact] public async Task IsEncapsulated() { - using var s1 = new SemaphoreWrapper(1); - using var s2 = new SemaphoreWrapper(1); + using var s1 = new RequestQueue(1); + using var s2 = new RequestQueue(1); await s1.EnterQueue().OrTimeout(); await s2.EnterQueue().OrTimeout(); diff --git a/src/Middleware/RequestThrottling/test/TestUtils.cs b/src/Middleware/RequestThrottling/test/TestUtils.cs new file mode 100644 index 0000000000..438d08ab8d --- /dev/null +++ b/src/Middleware/RequestThrottling/test/TestUtils.cs @@ -0,0 +1,28 @@ +// 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.Threading.Tasks; +using Microsoft.AspNetCore.RequestThrottling; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.RequestThrottling.Tests +{ + public static class TestUtils + { + public static RequestThrottlingMiddleware CreateTestMiddleWare(int? maxConcurrentRequests, RequestDelegate next = null) + { + var options = new RequestThrottlingOptions + { + MaxConcurrentRequests = maxConcurrentRequests + }; + + return new RequestThrottlingMiddleware( + next: next ?? (context => Task.CompletedTask), + loggerFactory: NullLoggerFactory.Instance, + options: Options.Create(options) + ); + } + } +} diff --git a/src/Middleware/ResponseCaching/samples/ResponseCachingSample/Startup.cs b/src/Middleware/ResponseCaching/samples/ResponseCachingSample/Startup.cs index ca2e7fbcf3..6184a36946 100644 --- a/src/Middleware/ResponseCaching/samples/ResponseCachingSample/Startup.cs +++ b/src/Middleware/ResponseCaching/samples/ResponseCachingSample/Startup.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; diff --git a/src/Middleware/ResponseCaching/src/Microsoft.AspNetCore.ResponseCaching.csproj b/src/Middleware/ResponseCaching/src/Microsoft.AspNetCore.ResponseCaching.csproj index 9611dfbdaf..ef8199808c 100644 --- a/src/Middleware/ResponseCaching/src/Microsoft.AspNetCore.ResponseCaching.csproj +++ b/src/Middleware/ResponseCaching/src/Microsoft.AspNetCore.ResponseCaching.csproj @@ -1,4 +1,4 @@ - + ASP.NET Core middleware for caching HTTP responses on the server. diff --git a/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.csproj b/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.csproj new file mode 100644 index 0000000000..36c3a47a9a --- /dev/null +++ b/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.csproj @@ -0,0 +1,10 @@ + + + + netcoreapp3.0 + + + + + + diff --git a/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.netcoreapp3.0.cs b/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.netcoreapp3.0.cs new file mode 100644 index 0000000000..618082bc4a --- /dev/null +++ b/src/Servers/IIS/AspNetCoreModuleV2/ref/Microsoft.AspNetCore.ANCMSymbols.netcoreapp3.0.cs @@ -0,0 +1,3 @@ +// 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. + diff --git a/src/SignalR/common/testassets/Tests.Utils/SyncPoint.cs b/src/Shared/SyncPoint/SyncPoint.cs similarity index 98% rename from src/SignalR/common/testassets/Tests.Utils/SyncPoint.cs rename to src/Shared/SyncPoint/SyncPoint.cs index 55f4a034d5..ccf36d59df 100644 --- a/src/SignalR/common/testassets/Tests.Utils/SyncPoint.cs +++ b/src/Shared/SyncPoint/SyncPoint.cs @@ -4,7 +4,7 @@ using System; using System.Threading.Tasks; -namespace Microsoft.AspNetCore.SignalR.Tests +namespace Microsoft.AspNetCore.Internal { public class SyncPoint { diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs index 2088ef3927..61821226b2 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.Http.Connections.Client.Internal; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; @@ -90,8 +91,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var startCounter = 0; var expected = new Exception("Transport failed to start"); - // We have 4 cases here. Falling back once, falling back twice and each of these - // with WebSockets available and not. If Websockets aren't available and + // We have 4 cases here. Falling back once, falling back twice and each of these + // with WebSockets available and not. If Websockets aren't available and // we can't to test the fallback once scenario we don't decrement the passthreshold // because we still try to start twice (SSE and LP). if (!TestHelpers.IsWebSocketsSupported() && passThreshold > 2) diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs index f48742474b..647aa433ca 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.DependencyInjection; 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 a2b40fbe25..cc45556edd 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,4 +1,4 @@ - + netcoreapp3.0 @@ -8,6 +8,7 @@ + diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/ServerSentEventsTransportTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/ServerSentEventsTransportTests.cs index 421e1cef61..aa3c41bbed 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/ServerSentEventsTransportTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/ServerSentEventsTransportTests.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Connections.Client.Internal; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.Logging.Testing; using Moq; diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs index 5b4c164ce3..2fcf129790 100644 --- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs +++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs @@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Http.Connections.Internal; using Microsoft.AspNetCore.Http.Connections.Internal.Transports; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Internal; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; 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 6c03dfe4eb..1f6b36a8ba 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,4 +1,4 @@ - + netcoreapp3.0 @@ -6,6 +6,7 @@ + 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 c4fd5fd37b..24958a00b6 100644 --- a/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj +++ b/src/SignalR/server/SignalR/test/Microsoft.AspNetCore.SignalR.Tests.csproj @@ -1,9 +1,12 @@ - + netcoreapp3.0 + + + @@ -23,4 +26,4 @@ - + \ No newline at end of file diff --git a/src/SignalR/server/SignalR/test/SerializedHubMessageTests.cs b/src/SignalR/server/SignalR/test/SerializedHubMessageTests.cs index b69952b523..da55a5e675 100644 --- a/src/SignalR/server/SignalR/test/SerializedHubMessageTests.cs +++ b/src/SignalR/server/SignalR/test/SerializedHubMessageTests.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Xunit; From bc011b5c973018f333ee5eaa367f531883a1e36e Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 28 May 2019 02:12:01 +0100 Subject: [PATCH 26/95] Integrate authorization into Blazor router (#10491) * Split AuthorizeView in two, so "Core" part can be reused from routing * Rename LayoutDisplay to PageDisplay * Integrate authorization with Router/PageDisplay * CR: Replace AuthorizeViewCore.razor with AuthorizeViewCore.cs * Update tests * Update ref assemblies * Add E2E tests * Update ref assembly exclusions * More manual ref assembly updating * Oh these ref assemblies --- eng/GenAPI.exclusions.txt | 3 +- ...etCore.Components.netstandard2.0.Manual.cs | 29 ++-- .../src/Auth/AttributeAuthorizeDataCache.cs | 42 ++++++ .../Components/src/Auth/AuthorizeView.cs | 39 +++++ .../Components/src/Auth/AuthorizeView.razor | 99 ------------- .../Components/src/Auth/AuthorizeViewCore.cs | 111 ++++++++++++++ .../Components/src/Layouts/LayoutDisplay.cs | 90 ----------- .../Components/src/Layouts/PageDisplay.cs | 140 ++++++++++++++++++ .../Components/src/Routing/Router.cs | 18 ++- .../Components/test/Auth/AuthorizeViewTest.cs | 2 +- .../{LayoutTest.cs => PageDisplayTest.cs} | 36 ++--- src/Components/test/E2ETest/Tests/AuthTest.cs | 88 ++++++++++- .../BasicTestApp/AuthTest/AuthRouter.razor | 9 +- .../BasicTestApp/AuthTest/Links.razor | 11 +- .../AuthTest/PageAllowingAnonymous.razor | 5 + .../AuthTest/PageRequiringAuthorization.razor | 4 + .../AuthTest/PageRequiringPolicy.razor | 4 + .../AuthTest/PageRequiringRole.razor | 4 + 18 files changed, 502 insertions(+), 232 deletions(-) create mode 100644 src/Components/Components/src/Auth/AttributeAuthorizeDataCache.cs create mode 100644 src/Components/Components/src/Auth/AuthorizeView.cs delete mode 100644 src/Components/Components/src/Auth/AuthorizeView.razor create mode 100644 src/Components/Components/src/Auth/AuthorizeViewCore.cs delete mode 100644 src/Components/Components/src/Layouts/LayoutDisplay.cs create mode 100644 src/Components/Components/src/Layouts/PageDisplay.cs rename src/Components/Components/test/{LayoutTest.cs => PageDisplayTest.cs} (86%) create mode 100644 src/Components/test/testassets/BasicTestApp/AuthTest/PageAllowingAnonymous.razor create mode 100644 src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringAuthorization.razor create mode 100644 src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringPolicy.razor create mode 100644 src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringRole.razor diff --git a/eng/GenAPI.exclusions.txt b/eng/GenAPI.exclusions.txt index 4595fc772a..3053eb79e1 100644 --- a/eng/GenAPI.exclusions.txt +++ b/eng/GenAPI.exclusions.txt @@ -5,6 +5,7 @@ T:Microsoft.AspNetCore.Mvc.ApplicationModels.PageParameterModel T:Microsoft.AspNetCore.Mvc.ApplicationModels.PagePropertyModel # Manually implemented - https://github.com/aspnet/AspNetCore/issues/8825 T:Microsoft.AspNetCore.Components.AuthorizeView +T:Microsoft.AspNetCore.Components.AuthorizeViewCore T:Microsoft.AspNetCore.Components.CascadingAuthenticationState T:Microsoft.AspNetCore.Components.CascadingValue`1 T:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator @@ -18,6 +19,6 @@ T:Microsoft.AspNetCore.Components.Forms.InputText T:Microsoft.AspNetCore.Components.Forms.InputTextArea T:Microsoft.AspNetCore.Components.Forms.ValidationMessage`1 T:Microsoft.AspNetCore.Components.Forms.ValidationSummary -T:Microsoft.AspNetCore.Components.Layouts.LayoutDisplay +T:Microsoft.AspNetCore.Components.PageDisplay T:Microsoft.AspNetCore.Components.Routing.NavLink T:Microsoft.AspNetCore.Components.Routing.Router \ No newline at end of file diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs index 41b4d9a028..357b78e82a 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs @@ -49,10 +49,22 @@ namespace Microsoft.AspNetCore.Components.RenderTree // Built-in components: https://github.com/aspnet/AspNetCore/issues/8825 namespace Microsoft.AspNetCore.Components { - public partial class AuthorizeView : Microsoft.AspNetCore.Components.ComponentBase + public partial class AuthorizeView : Microsoft.AspNetCore.Components.AuthorizeViewCore { public AuthorizeView() { } [Microsoft.AspNetCore.Components.ParameterAttribute] + public string Policy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public object Resource { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public string Roles { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } + protected override Microsoft.AspNetCore.Authorization.IAuthorizeData[] GetAuthorizeData() { throw null; } + } + + public abstract partial class AuthorizeViewCore : Microsoft.AspNetCore.Components.ComponentBase + { + public AuthorizeViewCore() { } + [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.RenderFragment Authorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } @@ -60,13 +72,8 @@ namespace Microsoft.AspNetCore.Components public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } - [Microsoft.AspNetCore.Components.ParameterAttribute] - public string Policy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } - [Microsoft.AspNetCore.Components.ParameterAttribute] - public string Roles { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } - [Microsoft.AspNetCore.Components.ParameterAttribute] - public object Resource { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } } protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { } + protected abstract Microsoft.AspNetCore.Authorization.IAuthorizeData[] GetAuthorizeData(); [System.Diagnostics.DebuggerStepThroughAttribute] protected override System.Threading.Tasks.Task OnParametersSetAsync() { throw null; } } @@ -218,9 +225,13 @@ namespace Microsoft.AspNetCore.Components.Forms namespace Microsoft.AspNetCore.Components.Layouts { - public partial class LayoutDisplay : Microsoft.AspNetCore.Components.IComponent + public partial class PageDisplay : Microsoft.AspNetCore.Components.IComponent { - public LayoutDisplay() { } + public PageDisplay() { } + [Microsoft.AspNetCore.Components.ParameterAttribute] + public Microsoft.AspNetCore.Components.RenderFragment AuthorizingContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} + [Microsoft.AspNetCore.Components.ParameterAttribute] + public Microsoft.AspNetCore.Components.RenderFragment NotAuthorizedContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] public System.Type Page { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] diff --git a/src/Components/Components/src/Auth/AttributeAuthorizeDataCache.cs b/src/Components/Components/src/Auth/AttributeAuthorizeDataCache.cs new file mode 100644 index 0000000000..92cdf1fb39 --- /dev/null +++ b/src/Components/Components/src/Auth/AttributeAuthorizeDataCache.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; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Authorization; + +namespace Microsoft.AspNetCore.Components.Auth +{ + internal static class AttributeAuthorizeDataCache + { + private static ConcurrentDictionary _cache + = new ConcurrentDictionary(); + + public static IAuthorizeData[] GetAuthorizeDataForType(Type type) + { + IAuthorizeData[] result; + if (!_cache.TryGetValue(type, out result)) + { + result = ComputeAuthorizeDataForType(type); + _cache[type] = result; // Safe race - doesn't matter if it overwrites + } + + return result; + } + + private static IAuthorizeData[] ComputeAuthorizeDataForType(Type type) + { + // Allow Anonymous skips all authorization + var allAttributes = type.GetCustomAttributes(inherit: true); + if (allAttributes.OfType().Any()) + { + return null; + } + + var authorizeDataAttributes = allAttributes.OfType().ToArray(); + return authorizeDataAttributes.Length > 0 ? authorizeDataAttributes : null; + } + } +} diff --git a/src/Components/Components/src/Auth/AuthorizeView.cs b/src/Components/Components/src/Auth/AuthorizeView.cs new file mode 100644 index 0000000000..f1a65b0804 --- /dev/null +++ b/src/Components/Components/src/Auth/AuthorizeView.cs @@ -0,0 +1,39 @@ +// 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.Authorization; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Displays differing content depending on the user's authorization status. + /// + public class AuthorizeView : AuthorizeViewCore + { + private readonly IAuthorizeData[] selfAsAuthorizeData; + + /// + /// Constructs an instance of . + /// + public AuthorizeView() + { + selfAsAuthorizeData = new[] { new AuthorizeDataAdapter(this) }; + } + + /// + /// The policy name that determines whether the content can be displayed. + /// + [Parameter] public string Policy { get; private set; } + + /// + /// A comma delimited list of roles that are allowed to display the content. + /// + [Parameter] public string Roles { get; private set; } + + /// + /// Gets the data used for authorization. + /// + protected override IAuthorizeData[] GetAuthorizeData() + => selfAsAuthorizeData; + } +} diff --git a/src/Components/Components/src/Auth/AuthorizeView.razor b/src/Components/Components/src/Auth/AuthorizeView.razor deleted file mode 100644 index 0514ba3f9c..0000000000 --- a/src/Components/Components/src/Auth/AuthorizeView.razor +++ /dev/null @@ -1,99 +0,0 @@ -@namespace Microsoft.AspNetCore.Components -@using System.Security.Claims -@using Microsoft.AspNetCore.Authorization -@inject IAuthorizationService AuthorizationService -@inject IAuthorizationPolicyProvider AuthorizationPolicyProvider - -@if (currentAuthenticationState == null) -{ - @Authorizing -} -else if (isAuthorized) -{ - @((Authorized ?? ChildContent)?.Invoke(currentAuthenticationState)) -} -else -{ - @(NotAuthorized?.Invoke(currentAuthenticationState)) -} - -@functions { - private IAuthorizeData[] selfAsAuthorizeData; - private AuthenticationState currentAuthenticationState; - private bool isAuthorized; - - [CascadingParameter] private Task AuthenticationState { get; set; } - - /// - /// The content that will be displayed if the user is authorized. - /// - [Parameter] public RenderFragment ChildContent { get; private set; } - - /// - /// The content that will be displayed if the user is not authorized. - /// - [Parameter] public RenderFragment NotAuthorized { get; private set; } - - /// - /// The content that will be displayed if the user is authorized. - /// If you specify a value for this parameter, do not also specify a value for . - /// - [Parameter] public RenderFragment Authorized { get; private set; } - - /// - /// The content that will be displayed while asynchronous authorization is in progress. - /// - [Parameter] public RenderFragment Authorizing { get; private set; } - - /// - /// The policy name that determines whether the content can be displayed. - /// - [Parameter] public string Policy { get; private set; } - - /// - /// A comma delimited list of roles that are allowed to display the content. - /// - [Parameter] public string Roles { get; private set; } - - /// - /// The resource to which access is being controlled. - /// - [Parameter] public object Resource { get; private set; } - - protected override void OnInit() - { - selfAsAuthorizeData = new[] - { - new AuthorizeDataAdapter((AuthorizeView)(object)this) - }; - } - - protected override async Task OnParametersSetAsync() - { - // We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry - // with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid - // confusion, explicitly prevent the case where both are supplied. - if (ChildContent != null && Authorized != null) - { - throw new InvalidOperationException($"When using {nameof(AuthorizeView)}, do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'."); - } - - // First render in pending state - // If the task has already completed, this render will be skipped - currentAuthenticationState = null; - - // Then render in completed state - // Importantly, we *don't* call StateHasChanged between the following async steps, - // otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync - currentAuthenticationState = await AuthenticationState; - isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User); - } - - private async Task IsAuthorizedAsync(ClaimsPrincipal user) - { - var policy = await AuthorizationPolicy.CombineAsync( - AuthorizationPolicyProvider, selfAsAuthorizeData); - var result = await AuthorizationService.AuthorizeAsync(user, Resource, policy); - return result.Succeeded; - } -} diff --git a/src/Components/Components/src/Auth/AuthorizeViewCore.cs b/src/Components/Components/src/Auth/AuthorizeViewCore.cs new file mode 100644 index 0000000000..07e2cd1c48 --- /dev/null +++ b/src/Components/Components/src/Auth/AuthorizeViewCore.cs @@ -0,0 +1,111 @@ +// 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.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Components.RenderTree; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// A base class for components that display differing content depending on the user's authorization status. + /// + public abstract class AuthorizeViewCore : ComponentBase + { + private AuthenticationState currentAuthenticationState; + private bool isAuthorized; + + /// + /// The content that will be displayed if the user is authorized. + /// + [Parameter] public RenderFragment ChildContent { get; private set; } + + /// + /// The content that will be displayed if the user is not authorized. + /// + [Parameter] public RenderFragment NotAuthorized { get; private set; } + + /// + /// The content that will be displayed if the user is authorized. + /// If you specify a value for this parameter, do not also specify a value for . + /// + [Parameter] public RenderFragment Authorized { get; private set; } + + /// + /// The content that will be displayed while asynchronous authorization is in progress. + /// + [Parameter] public RenderFragment Authorizing { get; private set; } + + /// + /// The resource to which access is being controlled. + /// + [Parameter] public object Resource { get; private set; } + + [CascadingParameter] private Task AuthenticationState { get; set; } + + [Inject] private IAuthorizationPolicyProvider AuthorizationPolicyProvider { get; set; } + + [Inject] private IAuthorizationService AuthorizationService { get; set; } + + /// + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (currentAuthenticationState == null) + { + builder.AddContent(0, Authorizing); + } + else if (isAuthorized) + { + var authorizedContent = Authorized ?? ChildContent; + builder.AddContent(1, authorizedContent?.Invoke(currentAuthenticationState)); + } + else + { + builder.AddContent(2, NotAuthorized?.Invoke(currentAuthenticationState)); + } + } + + /// + protected override async Task OnParametersSetAsync() + { + // We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry + // with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid + // confusion, explicitly prevent the case where both are supplied. + if (ChildContent != null && Authorized != null) + { + throw new InvalidOperationException($"Do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'."); + } + + if (AuthenticationState == null) + { + throw new InvalidOperationException($"Authorization requires a cascading parameter of type Task<{nameof(AuthenticationState)}>. Consider using {typeof(CascadingAuthenticationState).Name} to supply this."); + } + + // First render in pending state + // If the task has already completed, this render will be skipped + currentAuthenticationState = null; + + // Then render in completed state + // Importantly, we *don't* call StateHasChanged between the following async steps, + // otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync + currentAuthenticationState = await AuthenticationState; + isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User); + } + + /// + /// Gets the data required to apply authorization rules. + /// + protected abstract IAuthorizeData[] GetAuthorizeData(); + + private async Task IsAuthorizedAsync(ClaimsPrincipal user) + { + var authorizeData = GetAuthorizeData(); + var policy = await AuthorizationPolicy.CombineAsync( + AuthorizationPolicyProvider, authorizeData); + var result = await AuthorizationService.AuthorizeAsync(user, Resource, policy); + return result.Succeeded; + } + } +} diff --git a/src/Components/Components/src/Layouts/LayoutDisplay.cs b/src/Components/Components/src/Layouts/LayoutDisplay.cs deleted file mode 100644 index c4ae14ab46..0000000000 --- a/src/Components/Components/src/Layouts/LayoutDisplay.cs +++ /dev/null @@ -1,90 +0,0 @@ -// 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.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.RenderTree; - -namespace Microsoft.AspNetCore.Components.Layouts -{ - /// - /// Displays the specified page component, rendering it inside its layout - /// and any further nested layouts. - /// - public class LayoutDisplay : IComponent - { - internal const string NameOfPage = nameof(Page); - internal const string NameOfPageParameters = nameof(PageParameters); - - private RenderHandle _renderHandle; - - /// - /// Gets or sets the type of the page component to display. - /// The type must implement . - /// - [Parameter] - public Type Page { get; private set; } - - /// - /// Gets or sets the parameters to pass to the page. - /// - [Parameter] - public IDictionary PageParameters { get; private set; } - - /// - public void Configure(RenderHandle renderHandle) - { - _renderHandle = renderHandle; - } - - /// - public Task SetParametersAsync(ParameterCollection parameters) - { - parameters.SetParameterProperties(this); - Render(); - return Task.CompletedTask; - } - - private void Render() - { - // In the middle, we render the requested page - var fragment = RenderComponentWithBody(Page, bodyParam: null); - - // Repeatedly wrap it in each layer of nested layout until we get - // to a layout that has no parent - Type layoutType = Page; - while ((layoutType = GetLayoutType(layoutType)) != null) - { - fragment = RenderComponentWithBody(layoutType, fragment); - } - - _renderHandle.Render(fragment); - } - - private RenderFragment RenderComponentWithBody(Type componentType, RenderFragment bodyParam) => builder => - { - builder.OpenComponent(0, componentType); - if (bodyParam != null) - { - builder.AddAttribute(1, LayoutComponentBase.BodyPropertyName, bodyParam); - } - else - { - if (PageParameters != null) - { - foreach (var kvp in PageParameters) - { - builder.AddAttribute(1, kvp.Key, kvp.Value); - } - } - } - builder.CloseComponent(); - }; - - private Type GetLayoutType(Type type) - => type.GetCustomAttribute()?.LayoutType; - } -} diff --git a/src/Components/Components/src/Layouts/PageDisplay.cs b/src/Components/Components/src/Layouts/PageDisplay.cs new file mode 100644 index 0000000000..a73a7ee6e2 --- /dev/null +++ b/src/Components/Components/src/Layouts/PageDisplay.cs @@ -0,0 +1,140 @@ +// 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.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Components.Auth; +using Microsoft.AspNetCore.Components.Layouts; +using Microsoft.AspNetCore.Components.RenderTree; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Displays the specified page component, rendering it inside its layout + /// and any further nested layouts, plus applying any authorization rules. + /// + public class PageDisplay : IComponent + { + private RenderHandle _renderHandle; + + /// + /// Gets or sets the type of the page component to display. + /// The type must implement . + /// + [Parameter] + public Type Page { get; private set; } + + /// + /// Gets or sets the parameters to pass to the page. + /// + [Parameter] + public IDictionary PageParameters { get; private set; } + + /// + /// The content that will be displayed if the user is not authorized. + /// + [Parameter] + public RenderFragment NotAuthorizedContent { get; private set; } + + /// + /// The content that will be displayed while asynchronous authorization is in progress. + /// + [Parameter] + public RenderFragment AuthorizingContent { get; private set; } + + /// + public void Configure(RenderHandle renderHandle) + { + _renderHandle = renderHandle; + } + + /// + public Task SetParametersAsync(ParameterCollection parameters) + { + parameters.SetParameterProperties(this); + Render(); + return Task.CompletedTask; + } + + private void Render() + { + // In the middle goes the requested page + var fragment = (RenderFragment)RenderPageWithParameters; + + // Around that goes an AuthorizeViewCore + fragment = WrapInAuthorizeViewCore(fragment); + + // Then repeatedly wrap that in each layer of nested layout until we get + // to a layout that has no parent + Type layoutType = Page; + while ((layoutType = GetLayoutType(layoutType)) != null) + { + fragment = WrapInLayout(layoutType, fragment); + } + + _renderHandle.Render(fragment); + } + + private RenderFragment WrapInLayout(Type layoutType, RenderFragment bodyParam) => builder => + { + builder.OpenComponent(0, layoutType); + builder.AddAttribute(1, LayoutComponentBase.BodyPropertyName, bodyParam); + builder.CloseComponent(); + }; + + private void RenderPageWithParameters(RenderTreeBuilder builder) + { + builder.OpenComponent(0, Page); + + if (PageParameters != null) + { + foreach (var kvp in PageParameters) + { + builder.AddAttribute(1, kvp.Key, kvp.Value); + } + } + + builder.CloseComponent(); + } + + private RenderFragment WrapInAuthorizeViewCore(RenderFragment pageFragment) + { + var authorizeData = AttributeAuthorizeDataCache.GetAuthorizeDataForType(Page); + if (authorizeData == null) + { + // No authorization, so no need to wrap the fragment + return pageFragment; + } + + // Some authorization data exists, so we do need to wrap the fragment + RenderFragment authorizedContent = context => pageFragment; + return builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(AuthorizeViewWithSuppliedData.AuthorizeDataParam), authorizeData); + builder.AddAttribute(2, nameof(AuthorizeViewWithSuppliedData.Authorized), authorizedContent); + builder.AddAttribute(3, nameof(AuthorizeViewWithSuppliedData.NotAuthorized), NotAuthorizedContent ?? DefaultNotAuthorizedContent); + builder.AddAttribute(4, nameof(AuthorizeViewWithSuppliedData.Authorizing), AuthorizingContent); + builder.CloseComponent(); + }; + } + + private static Type GetLayoutType(Type type) + => type.GetCustomAttribute()?.LayoutType; + + private class AuthorizeViewWithSuppliedData : AuthorizeViewCore + { + [Parameter] public IAuthorizeData[] AuthorizeDataParam { get; private set; } + + protected override IAuthorizeData[] GetAuthorizeData() => AuthorizeDataParam; + } + + // There has to be some default content. If we render blank by default, developers + // will find it hard to guess why their UI isn't appearing. + private static RenderFragment DefaultNotAuthorizedContent(AuthenticationState authenticationState) + => builder => builder.AddContent(0, "Not authorized"); + } +} diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 88c781bbc7..e8060708f4 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -40,6 +40,16 @@ namespace Microsoft.AspNetCore.Components.Routing /// [Parameter] public RenderFragment NotFoundContent { get; private set; } + /// + /// The content that will be displayed if the user is not authorized. + /// + [Parameter] public RenderFragment NotAuthorizedContent { get; private set; } + + /// + /// The content that will be displayed while asynchronous authorization is in progress. + /// + [Parameter] public RenderFragment AuthorizingContent { get; private set; } + private RouteTable Routes { get; set; } /// @@ -78,9 +88,11 @@ namespace Microsoft.AspNetCore.Components.Routing /// protected virtual void Render(RenderTreeBuilder builder, Type handler, IDictionary parameters) { - builder.OpenComponent(0, typeof(LayoutDisplay)); - builder.AddAttribute(1, LayoutDisplay.NameOfPage, handler); - builder.AddAttribute(2, LayoutDisplay.NameOfPageParameters, parameters); + builder.OpenComponent(0, typeof(PageDisplay)); + builder.AddAttribute(1, nameof(PageDisplay.Page), handler); + builder.AddAttribute(2, nameof(PageDisplay.PageParameters), parameters); + builder.AddAttribute(3, nameof(PageDisplay.NotAuthorizedContent), NotAuthorizedContent); + builder.AddAttribute(4, nameof(PageDisplay.AuthorizingContent), AuthorizingContent); builder.CloseComponent(); } diff --git a/src/Components/Components/test/Auth/AuthorizeViewTest.cs b/src/Components/Components/test/Auth/AuthorizeViewTest.cs index 60c3f445d6..36a83cd935 100644 --- a/src/Components/Components/test/Auth/AuthorizeViewTest.cs +++ b/src/Components/Components/test/Auth/AuthorizeViewTest.cs @@ -248,7 +248,7 @@ namespace Microsoft.AspNetCore.Components renderer.AssignRootComponentId(rootComponent); var ex = Assert.Throws(() => rootComponent.TriggerRender()); - Assert.Equal("When using AuthorizeView, do not specify both 'Authorized' and 'ChildContent'.", ex.Message); + Assert.Equal("Do not specify both 'Authorized' and 'ChildContent'.", ex.Message); } [Fact] diff --git a/src/Components/Components/test/LayoutTest.cs b/src/Components/Components/test/PageDisplayTest.cs similarity index 86% rename from src/Components/Components/test/LayoutTest.cs rename to src/Components/Components/test/PageDisplayTest.cs index 9439068bd8..f2b64a7001 100644 --- a/src/Components/Components/test/LayoutTest.cs +++ b/src/Components/Components/test/PageDisplayTest.cs @@ -11,26 +11,26 @@ using Xunit; namespace Microsoft.AspNetCore.Components.Test { - public class LayoutTest + public class PageDisplayTest { private TestRenderer _renderer = new TestRenderer(); - private LayoutDisplay _layoutDisplayComponent = new LayoutDisplay(); - private int _layoutDisplayComponentId; + private PageDisplay _pageDisplayComponent = new PageDisplay(); + private int _pageDisplayComponentId; - public LayoutTest() + public PageDisplayTest() { _renderer = new TestRenderer(); - _layoutDisplayComponent = new LayoutDisplay(); - _layoutDisplayComponentId = _renderer.AssignRootComponentId(_layoutDisplayComponent); + _pageDisplayComponent = new PageDisplay(); + _pageDisplayComponentId = _renderer.AssignRootComponentId(_pageDisplayComponent); } [Fact] public void DisplaysComponentInsideLayout() { // Arrange/Act - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(ComponentWithLayout) } + { nameof(PageDisplay.Page), typeof(ComponentWithLayout) } }))); // Assert @@ -85,9 +85,9 @@ namespace Microsoft.AspNetCore.Components.Test public void DisplaysComponentInsideNestedLayout() { // Arrange/Act - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(ComponentWithNestedLayout) } + { nameof(PageDisplay.Page), typeof(ComponentWithNestedLayout) } }))); // Assert @@ -112,15 +112,15 @@ namespace Microsoft.AspNetCore.Components.Test public void CanChangeDisplayedPageWithSameLayout() { // Arrange - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(ComponentWithLayout) } + { nameof(PageDisplay.Page), typeof(ComponentWithLayout) } }))); // Act - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(DifferentComponentWithLayout) } + { nameof(PageDisplay.Page), typeof(DifferentComponentWithLayout) } }))); // Assert @@ -163,15 +163,15 @@ namespace Microsoft.AspNetCore.Components.Test public void CanChangeDisplayedPageWithDifferentLayout() { // Arrange - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(ComponentWithLayout) } + { nameof(PageDisplay.Page), typeof(ComponentWithLayout) } }))); // Act - _renderer.Invoke(() => _layoutDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary + _renderer.Invoke(() => _pageDisplayComponent.SetParametersAsync(ParameterCollection.FromDictionary(new Dictionary { - { LayoutDisplay.NameOfPage, typeof(ComponentWithNestedLayout) } + { nameof(PageDisplay.Page), typeof(ComponentWithNestedLayout) } }))); // Assert diff --git a/src/Components/test/E2ETest/Tests/AuthTest.cs b/src/Components/test/E2ETest/Tests/AuthTest.cs index 3e9e6ef478..9c2c26b0c6 100644 --- a/src/Components/test/E2ETest/Tests/AuthTest.cs +++ b/src/Components/test/E2ETest/Tests/AuthTest.cs @@ -16,6 +16,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests // These strings correspond to the links in BasicTestApp\AuthTest\Links.razor const string CascadingAuthenticationStateLink = "Cascading authentication state"; const string AuthorizeViewCases = "AuthorizeView cases"; + const string PageAllowingAnonymous = "Page allowing anonymous"; + const string PageRequiringAuthorization = "Page requiring any authentication"; + const string PageRequiringPolicy = "Page requiring policy"; + const string PageRequiringRole = "Page requiring role"; public AuthTest( BrowserFixture browserFixture, @@ -54,7 +58,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_NoAuthorizationRule_Unauthenticated() + public void AuthorizeViewCases_NoAuthorizationRule_NotAuthorized() { SignInAs(null, null); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -64,7 +68,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_NoAuthorizationRule_Authenticated() + public void AuthorizeViewCases_NoAuthorizationRule_Authorized() { SignInAs("Some User", null); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -73,7 +77,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_RequireRole_Authenticated() + public void AuthorizeViewCases_RequireRole_Authorized() { SignInAs("Some User", "IrrelevantRole,TestRole"); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -82,7 +86,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_RequireRole_Unauthenticated() + public void AuthorizeViewCases_RequireRole_NotAuthorized() { SignInAs("Some User", "IrrelevantRole"); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -91,7 +95,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_RequirePolicy_Authenticated() + public void AuthorizeViewCases_RequirePolicy_Authorized() { SignInAs("Bert", null); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -100,7 +104,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests } [Fact] - public void AuthorizeViewCases_RequirePolicy_Unauthenticated() + public void AuthorizeViewCases_RequirePolicy_NotAuthorized() { SignInAs("Mallory", null); var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases); @@ -108,6 +112,78 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests appElement.FindElement(By.CssSelector("#authorize-policy .not-authorized")).Text); } + [Fact] + public void Router_AllowAnonymous_Anonymous() + { + SignInAs(null, null); + var appElement = MountAndNavigateToAuthTest(PageAllowingAnonymous); + Browser.Equal("Welcome to PageAllowingAnonymous!", () => + appElement.FindElement(By.CssSelector("#auth-success")).Text); + } + + [Fact] + public void Router_AllowAnonymous_Authenticated() + { + SignInAs("Bert", null); + var appElement = MountAndNavigateToAuthTest(PageAllowingAnonymous); + Browser.Equal("Welcome to PageAllowingAnonymous!", () => + appElement.FindElement(By.CssSelector("#auth-success")).Text); + } + + [Fact] + public void Router_RequireAuthorization_Authorized() + { + SignInAs("Bert", null); + var appElement = MountAndNavigateToAuthTest(PageRequiringAuthorization); + Browser.Equal("Welcome to PageRequiringAuthorization!", () => + appElement.FindElement(By.CssSelector("#auth-success")).Text); + } + + [Fact] + public void Router_RequireAuthorization_NotAuthorized() + { + SignInAs(null, null); + var appElement = MountAndNavigateToAuthTest(PageRequiringAuthorization); + Browser.Equal("Sorry, anonymous, you're not authorized.", () => + appElement.FindElement(By.CssSelector("#auth-failure")).Text); + } + + [Fact] + public void Router_RequirePolicy_Authorized() + { + SignInAs("Bert", null); + var appElement = MountAndNavigateToAuthTest(PageRequiringPolicy); + Browser.Equal("Welcome to PageRequiringPolicy!", () => + appElement.FindElement(By.CssSelector("#auth-success")).Text); + } + + [Fact] + public void Router_RequirePolicy_NotAuthorized() + { + SignInAs("Mallory", null); + var appElement = MountAndNavigateToAuthTest(PageRequiringPolicy); + Browser.Equal("Sorry, Mallory, you're not authorized.", () => + appElement.FindElement(By.CssSelector("#auth-failure")).Text); + } + + [Fact] + public void Router_RequireRole_Authorized() + { + SignInAs("Bert", "IrrelevantRole,TestRole"); + var appElement = MountAndNavigateToAuthTest(PageRequiringRole); + Browser.Equal("Welcome to PageRequiringRole!", () => + appElement.FindElement(By.CssSelector("#auth-success")).Text); + } + + [Fact] + public void Router_RequireRole_NotAuthorized() + { + SignInAs("Bert", "IrrelevantRole"); + var appElement = MountAndNavigateToAuthTest(PageRequiringRole); + Browser.Equal("Sorry, Bert, you're not authorized.", () => + appElement.FindElement(By.CssSelector("#auth-failure")).Text); + } + IWebElement MountAndNavigateToAuthTest(string authLinkText) { Navigate(ServerPathBase); diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor index 299a5beb87..22951925af 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor @@ -9,7 +9,14 @@ *@ - + + Authorizing... + +
+ Sorry, @(context.User.Identity.Name ?? "anonymous"), you're not authorized. +
+
+

diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/Links.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/Links.razor index 70f524a763..35ff333ed7 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/Links.razor +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/Links.razor @@ -1,8 +1,11 @@ -@using Microsoft.AspNetCore.Components.Routing

To change the underlying authentication state, go here.

diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/PageAllowingAnonymous.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/PageAllowingAnonymous.razor new file mode 100644 index 0000000000..7a5d7597e3 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/PageAllowingAnonymous.razor @@ -0,0 +1,5 @@ +@page "/PageAllowingAnonymous" +@using Microsoft.AspNetCore.Authorization +@attribute [Authorize(Roles = "NobodyIsInThisRole")] +@attribute [AllowAnonymous] +
Welcome to PageAllowingAnonymous!
diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringAuthorization.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringAuthorization.razor new file mode 100644 index 0000000000..a509e07df8 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringAuthorization.razor @@ -0,0 +1,4 @@ +@page "/PageRequiringAuthorization" +@using Microsoft.AspNetCore.Authorization +@attribute [Authorize] +
Welcome to PageRequiringAuthorization!
diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringPolicy.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringPolicy.razor new file mode 100644 index 0000000000..61d831b9f6 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringPolicy.razor @@ -0,0 +1,4 @@ +@page "/PageRequiringPolicy" +@using Microsoft.AspNetCore.Authorization +@attribute [Authorize(Policy = "NameMustStartWithB")] +
Welcome to PageRequiringPolicy!
diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringRole.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringRole.razor new file mode 100644 index 0000000000..e12462c7e2 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/PageRequiringRole.razor @@ -0,0 +1,4 @@ +@page "/PageRequiringRole" +@using Microsoft.AspNetCore.Authorization +@attribute [Authorize(Roles = "TestRole")] +
Welcome to PageRequiringRole!
From c5e9904f5705283589be7009b81c02e482cf0c25 Mon Sep 17 00:00:00 2001 From: Barry Dorrans Date: Tue, 28 May 2019 11:36:31 -0700 Subject: [PATCH 27/95] Starting point for security policy (#10566) --- SECURITY.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..92d052767f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +The .NET Core and ASP.NET Core support policy, including supported versions can be found at the [.NET Core Support Policy Page](https://dotnet.microsoft.com/platform/support/policy/dotnet-core). + +## Reporting a Vulnerability + +Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your +original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). + +Reports via MSRC may qualify for the .NET Core Bug Bounty. Details of the .NET Core Bug Bounty including terms and conditions are at [https://aka.ms/corebounty](https://aka.ms/corebounty). + +Please do not open issues for anything you think might have a security implication. From 3eca32965d699e08692d074b449d1148bb626703 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 10 May 2019 14:02:44 +0100 Subject: [PATCH 28/95] Use sealed contexts for ActionInvoker --- .../Infrastructure/ControllerActionInvoker.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvoker.cs b/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvoker.cs index 28510c2d5b..ee0b0a2e1f 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvoker.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/ControllerActionInvoker.cs @@ -21,8 +21,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure private Dictionary _arguments; - private ActionExecutingContext _actionExecutingContext; - private ActionExecutedContext _actionExecutedContext; + private ActionExecutingContextSealed _actionExecutingContext; + private ActionExecutedContextSealed _actionExecutedContext; internal ControllerActionInvoker( ILogger logger, @@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure { if (_actionExecutingContext == null) { - _actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance); + _actionExecutingContext = new ActionExecutingContextSealed(_controllerContext, _filters, _arguments, _instance); } state = current.FilterAsync; @@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure { if (_actionExecutingContext == null) { - _actionExecutingContext = new ActionExecutingContext(_controllerContext, _filters, _arguments, _instance); + _actionExecutingContext = new ActionExecutingContextSealed(_controllerContext, _filters, _arguments, _instance); } state = current.Filter; @@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure // If we get here then the filter didn't call 'next' indicating a short circuit. _logger.ActionFilterShortCircuited(filter); - _actionExecutedContext = new ActionExecutedContext( + _actionExecutedContext = new ActionExecutedContextSealed( _controllerContext, _filters, _instance) @@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure // Short-circuited by setting a result. _logger.ActionFilterShortCircuited(filter); - _actionExecutedContext = new ActionExecutedContext( + _actionExecutedContext = new ActionExecutedContextSealed( _actionExecutingContext, _filters, _instance) @@ -255,7 +255,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure { if (_actionExecutedContext == null) { - _actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _instance) + _actionExecutedContext = new ActionExecutedContextSealed(_controllerContext, _filters, _instance) { Result = _result, }; @@ -301,7 +301,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure } catch (Exception exception) { - _actionExecutedContext = new ActionExecutedContext(_controllerContext, _filters, _instance) + _actionExecutedContext = new ActionExecutedContextSealed(_controllerContext, _filters, _instance) { ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception), }; @@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure } catch (Exception exception) { - invoker._actionExecutedContext = new ActionExecutedContext(invoker._controllerContext, invoker._filters, invoker._instance) + invoker._actionExecutedContext = new ActionExecutedContextSealed(invoker._controllerContext, invoker._filters, invoker._instance) { ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception), }; @@ -349,7 +349,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure } Debug.Assert(_actionExecutedContext != null); - return Task.FromResult(_actionExecutedContext); + return Task.FromResult(_actionExecutedContext); static async Task Awaited(ControllerActionInvoker invoker, Task task) { @@ -485,7 +485,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure } } - private static void Rethrow(ActionExecutedContext context) + private static void Rethrow(ActionExecutedContextSealed context) { if (context == null) { @@ -567,5 +567,15 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure ActionInside, ActionEnd, } + + private sealed class ActionExecutingContextSealed : ActionExecutingContext + { + public ActionExecutingContextSealed(ActionContext actionContext, IList filters, IDictionary actionArguments, object controller) : base(actionContext, filters, actionArguments, controller) { } + } + + private sealed class ActionExecutedContextSealed : ActionExecutedContext + { + public ActionExecutedContextSealed(ActionContext actionContext, IList filters, object controller) : base(actionContext, filters, controller) { } + } } } From 6e253996d9cacf27a94bfeeeeb95192de50c8970 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Tue, 28 May 2019 14:28:33 -0700 Subject: [PATCH 29/95] Usability cleanup in IIS tests (#10460) --- .../IIS.Performance/IIS.Performance.csproj | 6 - .../IIS.Performance/PlaintextBenchmark.cs | 2 +- .../Common.FunctionalTests/AppOfflineTests.cs | 2 +- .../Common.FunctionalTests/BasicAuthTests.cs | 2 +- .../ClientCertificateFixture.cs | 2 +- .../ClientCertificateTests.cs | 2 +- .../ClientDisconnectStress.cs | 2 +- .../CommonStartupTests.cs | 2 +- .../CompressionTests.cs | 2 +- .../ConfigurationChangeTests.cs | 10 +- .../{Inprocess => }/FixtureLoggedTest.cs | 2 +- .../test/Common.FunctionalTests/HttpsTests.cs | 2 +- .../Inprocess/ClientDisconnectTests.cs | 2 +- .../Inprocess/CompressionTests.cs | 2 +- .../Inprocess/EnvironmentVariableTests.cs | 2 +- .../Inprocess/ErrorPagesTests.cs | 2 +- .../Inprocess/EventLogTests.cs | 2 +- .../Inprocess/FeatureCollectionTests.cs | 2 +- .../Inprocess/FrebTests.cs | 2 +- .../Inprocess/HelloWorldTests.cs | 2 +- .../Inprocess/HostingEnvironmentTests.cs | 2 +- .../InvalidReadWriteOperationTests.cs | 2 +- .../Inprocess/LargeResponseBodyTests.cs | 2 +- .../Inprocess/LogPipeTests.cs | 2 +- .../Inprocess/MaxRequestBodySizeTests.cs | 2 +- .../Inprocess/ResponseHeaderTests.cs | 2 +- .../Inprocess/ResponseInvalidOrderingTests.cs | 2 +- .../Inprocess/ServerVariablesTest.cs | 2 +- .../Inprocess/ShutdownTests.cs | 2 +- .../Inprocess/StartupExceptionTests.cs | 2 +- .../Inprocess/StartupTests.cs | 2 +- .../Inprocess/SynchronousReadAndWriteTests.cs | 2 +- .../Common.FunctionalTests/LogFileTests.cs | 4 +- .../MultiApplicationTests.cs | 2 +- .../OutOfProcess/AspNetCorePortTests.cs | 2 +- .../OutOfProcess/GlobalVersionTests.cs | 4 +- .../OutOfProcess/HelloWorldTest.cs | 5 +- .../PublishedApplicationPublisher.cs | 2 +- .../PublishedSitesFixture.cs | 4 +- .../RequiresNewHandler.cs | 2 +- .../Common.FunctionalTests/RequiresNewShim.cs | 2 +- .../ServerAbortTests.cs | 2 +- .../SkipIfNotAdminAttribute.cs | 2 +- .../SkipVSTSAttribute.cs | 2 +- .../Utilities/EventLogHelpers.cs | 2 +- .../Utilities/FunctionalTestsBase.cs | 2 +- .../Utilities/Helpers.cs | 4 +- .../Utilities/IISCapability.cs | 2 +- .../Utilities/IISCompressionSiteCollection.cs | 2 +- .../Utilities/IISCompressionSiteFixture.cs | 2 +- .../Utilities/IISFunctionalTestBase.cs | 2 +- .../Utilities/IISTestSiteCollection.cs | 2 +- .../Utilities/IISTestSiteFixture.cs | 2 +- .../Utilities/LogFileTestBase.cs | 2 +- .../RequiresEnvironmentVariableAttribute.cs | 2 +- .../Utilities/SkipIfDebugAttribute.cs | 2 +- .../WindowsAuthTests.cs | 2 +- .../BackwardsCompatibilityTests.cs | 2 +- .../DeployerSelector.cs | 2 +- ...kwardsCompatibility.FunctionalTests.csproj | 3 - .../DeployerSelector.cs | 2 +- .../ForwardsCompatibilityTests.cs | 2 +- ...rwardsCompatibility.FunctionalTests.csproj | 3 - .../IIS.FunctionalTests/DeployerSelector.cs | 2 +- .../IIS.FunctionalTests.csproj | 3 - .../ApplicationInitializationTests.cs | 4 +- .../Inprocess/StdOutRedirectionTests.cs | 5 +- .../MofFileTests.cs | 31 --- .../Properties/AssemblyInfo.cs | 2 +- .../RequiresIISAttribute.cs | 2 +- .../test/IIS.Tests/ClientDisconnectTests.cs | 2 +- .../IIS.Tests/ConnectionIdFeatureTests.cs | 2 +- .../IIS.Tests/HttpBodyControlFeatureTests.cs | 2 +- .../test/IIS.Tests/MaxRequestBodySizeTests.cs | 2 +- .../IIS/test/IIS.Tests/ResponseAbortTests.cs | 2 +- .../test/IIS.Tests/StrictTestServerTests.cs | 2 +- .../IIS/IIS/test/IIS.Tests/TestServerTest.cs | 2 +- ...pIfHostableWebCoreNotAvailibleAttribute.cs | 2 +- .../test/IIS.Tests/Utilities/TestServer.cs | 2 +- .../DeployerSelector.cs | 2 +- .../IISExpress.FunctionalTests.csproj | 3 - .../InProcess/AppOfflineIISExpressTests.cs | 4 +- .../InProcess/AuthenticationTests.cs | 2 +- .../InProcess/IISExpressShutdownTests.cs | 2 +- .../InProcess/WebSocketTests.cs | 2 +- .../OutOfProcess/MultipleAppTests.cs | 2 +- .../OutOfProcess/NtlmAuthentationTest.cs | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../RequiresIISAttribute.cs | 2 +- .../UpgradeFeatureDetectionTests.cs | 2 +- .../InProcessForwardsCompatWebSite.csproj | 10 +- .../InProcessWebSite/InProcessWebSite.csproj | 3 +- .../testassets/InProcessWebSite/Program.cs | 9 + .../testassets/InProcessWebSite/Startup.cs | 214 +++++++++++++++- .../testassets/InProcessWebSite/web.config | 2 +- .../OutOfProcessWebSite.csproj | 35 --- .../testassets/OutOfProcessWebSite/Program.cs | 55 ----- .../testassets/OutOfProcessWebSite/Startup.cs | 101 -------- .../OutOfProcessWebSite/wwwroot/static.txt | 0 .../testassets/StressTestWebSite/Program.cs | 26 -- .../testassets/StressTestWebSite/Startup.cs | 233 ------------------ .../StressTestWebSite.csproj | 24 -- .../testassets/TestTasks/TestTasks.csproj | 2 +- .../shared/SharedStartup/Startup.shared.cs | 124 ---------- src/Servers/IIS/IISIntegration.sln | 108 ++++---- 105 files changed, 354 insertions(+), 835 deletions(-) rename src/Servers/IIS/IIS/test/Common.FunctionalTests/{Inprocess => }/FixtureLoggedTest.cs (92%) delete mode 100644 src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/MofFileTests.cs delete mode 100644 src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/OutOfProcessWebSite.csproj delete mode 100644 src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Program.cs delete mode 100644 src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Startup.cs delete mode 100644 src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/wwwroot/static.txt delete mode 100644 src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Program.cs delete mode 100644 src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Startup.cs delete mode 100644 src/Servers/IIS/IIS/test/testassets/StressTestWebSite/StressTestWebSite.csproj diff --git a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj index bf679ddbd2..44c1a66221 100644 --- a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj +++ b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/IIS.Performance.csproj @@ -28,12 +28,6 @@ False - - False - - - False -
diff --git a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/PlaintextBenchmark.cs b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/PlaintextBenchmark.cs index be6c8d9da3..32ed007c73 100644 --- a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/PlaintextBenchmark.cs +++ b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/PlaintextBenchmark.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.IIS.Performance diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/AppOfflineTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/AppOfflineTests.cs index dc6d470fc6..b63c2ff2ec 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/AppOfflineTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/AppOfflineTests.cs @@ -8,7 +8,7 @@ using System.Net.Http; using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/BasicAuthTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/BasicAuthTests.cs index c921a4e3dc..fbe47f4df7 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/BasicAuthTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/BasicAuthTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class BasicAuthTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateFixture.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateFixture.cs index 40b6f2265e..7e41664f86 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateFixture.cs @@ -6,7 +6,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class ClientCertificateFixture : IDisposable { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateTests.cs index 8cdd0f102c..3ce31f90f6 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientCertificateTests.cs @@ -6,7 +6,7 @@ using System.Net.Http; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.Common; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientDisconnectStress.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientDisconnectStress.cs index 9deeae3f92..6d1f30e386 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientDisconnectStress.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ClientDisconnectStress.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class ClientDisconnectStressTests: FunctionalTestsBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/CommonStartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/CommonStartupTests.cs index a7c98be57e..6f2ba0d609 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/CommonStartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/CommonStartupTests.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class CommonStartupTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/CompressionTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/CompressionTests.cs index 262d3ff59c..4727058341 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/CompressionTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/CompressionTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class CompressionTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ConfigurationChangeTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ConfigurationChangeTests.cs index 2332bfa9f3..e26445110b 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ConfigurationChangeTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ConfigurationChangeTests.cs @@ -15,7 +15,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class ConfigurationChangeTests : IISFunctionalTestBase @@ -72,11 +72,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests .SetAttributeValue("hostingModel", "inprocess")); // Have to retry here to allow ANCM to receive notification and react to it - // Verify that inprocess application was created and tried to start - await deploymentResult.HttpClient.RetryRequestAsync("/HelloWorld", r => r.StatusCode == HttpStatusCode.InternalServerError); - - StopServer(); - EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.CouldNotFindHandler(), Logger); + // Verify that inprocess application was created and started, checking the server + // header to see that it is running inprocess + await deploymentResult.HttpClient.RetryRequestAsync("/HelloWorld", r => r.Headers.Server.ToString().StartsWith("Microsoft")); } [ConditionalTheory] diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FixtureLoggedTest.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/FixtureLoggedTest.cs similarity index 92% rename from src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FixtureLoggedTest.cs rename to src/Servers/IIS/IIS/test/Common.FunctionalTests/FixtureLoggedTest.cs index 705af2b213..a8aaea4f2c 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FixtureLoggedTest.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/FixtureLoggedTest.cs @@ -5,7 +5,7 @@ using System.Reflection; using Microsoft.Extensions.Logging.Testing; using Xunit.Abstractions; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class FixtureLoggedTest: LoggedTest { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/HttpsTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/HttpsTests.cs index 7a5158f9bf..c56ea32047 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/HttpsTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/HttpsTests.cs @@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class HttpsTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ClientDisconnectTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ClientDisconnectTests.cs index 6e2d1bc752..409e403872 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ClientDisconnectTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ClientDisconnectTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class ClientDisconnectTests: FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/CompressionTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/CompressionTests.cs index 09b1ecc077..e5ace9fb3c 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/CompressionTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/CompressionTests.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISCompressionSiteCollection.Name)] public class CompressionModuleTests : FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs index b738155ea6..2fb0c53a9a 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs index 12086a3ab9..474d662481 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class ErrorPagesTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EventLogTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EventLogTests.cs index a138cd659a..461cc9a344 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EventLogTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/EventLogTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class EventLogTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FeatureCollectionTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FeatureCollectionTests.cs index 696a47e420..badbe2d5f0 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FeatureCollectionTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FeatureCollectionTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class FeatureCollectionTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FrebTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FrebTests.cs index b94baa4baa..eb4dc7cbac 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FrebTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/FrebTests.cs @@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class FrebTests : LogFileTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HelloWorldTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HelloWorldTests.cs index 04dc8bd393..42c4f2d076 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HelloWorldTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HelloWorldTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class HelloWorldInProcessTests: FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs index 3c2df6cb84..3a5f1e81f5 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class HostingEnvironmentTests: FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs index 95c05308bd..270dff4d50 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/InvalidReadWriteOperationTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class InvalidReadWriteOperationTests diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LargeResponseBodyTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LargeResponseBodyTests.cs index 40db2cfdb8..ce0491fe6a 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LargeResponseBodyTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LargeResponseBodyTests.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class LargeResponseBodyTests diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LogPipeTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LogPipeTests.cs index 926e81ad45..cd322b4cfa 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LogPipeTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/LogPipeTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class LogPipeTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs index f14574e172..79a60814e1 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class MaxRequestBodySizeTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs index a779f73585..ccce64873e 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseHeaderTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Net.Http.Headers; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class ResponseHeaders diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseInvalidOrderingTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseInvalidOrderingTests.cs index c39e155e77..7a29f027bc 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseInvalidOrderingTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ResponseInvalidOrderingTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class ResponseInvalidOrderingTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ServerVariablesTest.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ServerVariablesTest.cs index d18c4c7f09..a7091b3094 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ServerVariablesTest.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ServerVariablesTest.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class ServerVariablesTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ShutdownTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ShutdownTests.cs index d4e9c9f7cb..398d0bfb25 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ShutdownTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ShutdownTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class ShutdownTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupExceptionTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupExceptionTests.cs index bd468820a2..f7d6390e6d 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupExceptionTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupExceptionTests.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class StartupExceptionTests : LogFileTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 7acbc1d2e3..d6ffd044c9 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -20,7 +20,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Win32; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class StartupTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs index ae8bf44217..d1cb8eac4f 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(IISTestSiteCollection.Name)] public class SynchronousReadAndWriteTests: FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs index 2e640536b4..26f3ec6812 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class LoggingTests : LogFileTestBase @@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task CaptureLogsForOutOfProcessWhenProcessFailsToStart() { var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess); - deploymentParameters.TransformArguments((a, _) => $"{a} ConsoleWrite"); + deploymentParameters.TransformArguments((a, _) => $"{a} ConsoleWriteSingle"); var deploymentResult = await DeployAsync(deploymentParameters); var response = await deploymentResult.HttpClient.GetAsync("Test"); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/MultiApplicationTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/MultiApplicationTests.cs index d22856610f..50b19b5061 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/MultiApplicationTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/MultiApplicationTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class MultiApplicationTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs index 7a0ef3d7b4..060e63370f 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.OutOfProcess { [Collection(PublishedSitesCollection.Name)] public class AspNetCorePortTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/GlobalVersionTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/GlobalVersionTests.cs index e4cdcc1c07..a7f0c1b753 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/GlobalVersionTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/GlobalVersionTests.cs @@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.OutOfProcess { [Collection(PublishedSitesCollection.Name)] public class GlobalVersionTests : IISFunctionalTestBase @@ -241,7 +241,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests foreach (FileInfo fileInfo in source.GetFiles()) { var destFileName = Path.Combine(target.FullName, fileInfo.Name); - fileInfo.CopyTo(destFileName); + fileInfo.CopyTo(destFileName, overwrite: true); } } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs index b648c82059..09cc1099e7 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.OutOfProcess { [Collection(PublishedSitesCollection.Name)] public class HelloWorldTests : IISFunctionalTestBase @@ -66,7 +66,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests Assert.Equal("null", responseText); - Assert.Equal(deploymentResult.ContentRoot, await deploymentResult.HttpClient.GetStringAsync("/ContentRootPath")); + // Trailing slash + Assert.StartsWith(deploymentResult.ContentRoot, await deploymentResult.HttpClient.GetStringAsync("/ContentRootPath")); Assert.Equal(deploymentResult.ContentRoot + "\\wwwroot", await deploymentResult.HttpClient.GetStringAsync("/WebRootPath")); var expectedDll = "aspnetcorev2.dll"; Assert.Contains(deploymentResult.HostProcess.Modules.OfType(), m => m.FileName.Contains(expectedDll)); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedApplicationPublisher.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedApplicationPublisher.cs index d9aac2dac5..8bf370e640 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedApplicationPublisher.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedApplicationPublisher.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class PublishedApplicationPublisher: ApplicationPublisher { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedSitesFixture.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedSitesFixture.cs index 097d7e8ccb..2e03292ca4 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedSitesFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/PublishedSitesFixture.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { /// /// This type just maps collection names to available fixtures @@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public class PublishedSitesFixture : IDisposable { public PublishedApplicationPublisher InProcessTestSite { get; } = new PublishedApplicationPublisher(Helpers.GetInProcessTestSitesName()); - public PublishedApplicationPublisher OutOfProcessTestSite { get; } = new PublishedApplicationPublisher(Helpers.GetOutOfProcessTestSitesName()); + public PublishedApplicationPublisher OutOfProcessTestSite { get; } = new PublishedApplicationPublisher(Helpers.GetInProcessTestSitesName()); public void Dispose() { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs index e768df3bb6..051c995e5a 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs @@ -4,7 +4,7 @@ using System; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresNewHandlerAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs index 3f9b5cae8b..fa1c2c993f 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs @@ -4,7 +4,7 @@ using System; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresNewShimAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ServerAbortTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ServerAbortTests.cs index 1e3603aef4..d4c5a3c615 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/ServerAbortTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/ServerAbortTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class ServerAbortOutOfProcessTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipIfNotAdminAttribute.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipIfNotAdminAttribute.cs index 649a3984f9..56f99a1784 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipIfNotAdminAttribute.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipIfNotAdminAttribute.cs @@ -5,7 +5,7 @@ using System; using System.Security.Principal; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class SkipIfNotAdminAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipVSTSAttribute.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipVSTSAttribute.cs index 58a9347148..b29b253056 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipVSTSAttribute.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/SkipVSTSAttribute.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class SkipInVSTSAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs index a487ba20dd..0e714414d0 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class EventLogHelpers { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs index f57aa0ebec..e15863c0fb 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs @@ -4,7 +4,7 @@ using System; using System.IO; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging.Testing; using Xunit.Abstractions; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/Helpers.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/Helpers.cs index 0c299af7b4..a790d38b26 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/Helpers.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/Helpers.cs @@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public static class Helpers { @@ -28,8 +28,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests return DeployerSelector.IsForwardsCompatibilityTest ? "InProcessForwardsCompatWebSite" : "InProcessWebSite"; } - public static string GetOutOfProcessTestSitesName() => "OutOfProcessWebSite"; - public static async Task AssertStarts(this IISDeploymentResult deploymentResult, string path = "/HelloWorld") { var response = await deploymentResult.HttpClient.GetAsync(path); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCapability.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCapability.cs index 612b61d442..735af7ceef 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCapability.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCapability.cs @@ -3,7 +3,7 @@ using System; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Flags] public enum IISCapability diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteCollection.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteCollection.cs index 2c424943f3..3ba41fdc58 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteCollection.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteCollection.cs @@ -3,7 +3,7 @@ using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [CollectionDefinition(Name)] public class IISCompressionSiteCollection : ICollectionFixture diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteFixture.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteFixture.cs index 3aff68d11b..c963538913 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISCompressionSiteFixture.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class IISCompressionSiteFixture : IISTestSiteFixture { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISFunctionalTestBase.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISFunctionalTestBase.cs index 79766f68ca..d143d2ad82 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISFunctionalTestBase.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISFunctionalTestBase.cs @@ -5,7 +5,7 @@ using System; using System.IO; using System.Net; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteCollection.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteCollection.cs index 8d53affc98..88f0710e48 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteCollection.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteCollection.cs @@ -3,7 +3,7 @@ using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { /// /// This type just maps collection names to available fixtures diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs index 01d5b2b525..b4fe5ee552 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/IISTestSiteFixture.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class IISTestSiteFixture : IDisposable { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/LogFileTestBase.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/LogFileTestBase.cs index 774b74eb15..1b376ee366 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/LogFileTestBase.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/LogFileTestBase.cs @@ -3,7 +3,7 @@ using System; using System.IO; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Xunit.Abstractions; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/RequiresEnvironmentVariableAttribute.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/RequiresEnvironmentVariableAttribute.cs index d2749db547..a082a61c92 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/RequiresEnvironmentVariableAttribute.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/RequiresEnvironmentVariableAttribute.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresEnvironmentVariableAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs index 6cdd725392..e88406e88e 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class SkipIfDebugAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/WindowsAuthTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/WindowsAuthTests.cs index 5037436499..ec89251779 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/WindowsAuthTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/WindowsAuthTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class WindowsAuthTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/BackwardsCompatibilityTests.cs b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/BackwardsCompatibilityTests.cs index 807e01389b..1aa826c128 100644 --- a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/BackwardsCompatibilityTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/BackwardsCompatibilityTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Xunit; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(IISTestSiteCollection.Name)] public class BackwardsCompatibilityTests : FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs index 18ee47d427..bdbcad2780 100644 --- a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public static class DeployerSelector { diff --git a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/IIS.BackwardsCompatibility.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/IIS.BackwardsCompatibility.FunctionalTests.csproj index 77024081bb..6b92406b14 100644 --- a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/IIS.BackwardsCompatibility.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/IIS.BackwardsCompatibility.FunctionalTests.csproj @@ -22,9 +22,6 @@ False - - False - diff --git a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs index 2addfc912c..c53da1c67f 100644 --- a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public static class DeployerSelector { diff --git a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/ForwardsCompatibilityTests.cs b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/ForwardsCompatibilityTests.cs index 02b247ac4b..fdb3aef561 100644 --- a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/ForwardsCompatibilityTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/ForwardsCompatibilityTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Xunit; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(IISTestSiteCollection.Name)] public class ForwardsCompatibilityTests : FixtureLoggedTest diff --git a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/IIS.ForwardsCompatibility.FunctionalTests.csproj b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/IIS.ForwardsCompatibility.FunctionalTests.csproj index edd20b3aa9..e8e19b55af 100644 --- a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/IIS.ForwardsCompatibility.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/IIS.ForwardsCompatibility.FunctionalTests.csproj @@ -16,9 +16,6 @@ False - - False - diff --git a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs index 1451a0c34c..6f15410e55 100644 --- a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public static class DeployerSelector { 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 4cc4457514..f95e32bb3e 100644 --- a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/IIS.FunctionalTests.csproj @@ -18,9 +18,6 @@ False - - False - diff --git a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/ApplicationInitializationTests.cs b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/ApplicationInitializationTests.cs index 12b853799d..33abf9e22e 100644 --- a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/ApplicationInitializationTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/ApplicationInitializationTests.cs @@ -7,14 +7,14 @@ using System.ServiceProcess; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace IIS.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class ApplicationInitializationTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Inprocess/StdOutRedirectionTests.cs b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Inprocess/StdOutRedirectionTests.cs index a8a47e7c68..1d59259189 100644 --- a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Inprocess/StdOutRedirectionTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Inprocess/StdOutRedirectionTests.cs @@ -1,18 +1,15 @@ // 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.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; -using Newtonsoft.Json; using Xunit; -namespace IIS.FunctionalTests.Inprocess +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class StdOutRedirectionTests : LogFileTestBase diff --git a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/MofFileTests.cs b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/MofFileTests.cs deleted file mode 100644 index df0e867854..0000000000 --- a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/MofFileTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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.IO; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; -using Microsoft.AspNetCore.Testing; -using Microsoft.AspNetCore.Testing.xunit; -using Xunit; - -namespace IIS.FunctionalTests -{ - public class MofFileTests - { - [ConditionalFact] - [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] - [RequiresIIS(IISCapability.TracingModule)] - [SkipOnHelix("https://github.com/aspnet/AspNetCore-Internal/issues/2222")] - public void CheckMofFile() - { -// This test code needs to be updated to support distributed testing. -// See https://github.com/aspnet/AspNetCore-Internal/issues/2222 -#pragma warning disable 0618 - var path = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"), "aspnetcoremodulev2", "aspnetcore", "ancm.mof"); -#pragma warning restore 0618 - var process = Process.Start("mofcomp.exe", path); - process.WaitForExit(); - Assert.Equal(0, process.ExitCode); - } - } -} diff --git a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Properties/AssemblyInfo.cs b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Properties/AssemblyInfo.cs index b26f48a815..863fe8564c 100644 --- a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Properties/AssemblyInfo.cs +++ b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/Properties/AssemblyInfo.cs @@ -1,7 +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 Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.Extensions.Logging.Testing; using Xunit; diff --git a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/RequiresIISAttribute.cs b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/RequiresIISAttribute.cs index 4f00a17531..395ddc3a37 100644 --- a/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/RequiresIISAttribute.cs +++ b/src/Servers/IIS/IIS/test/IIS.Shared.FunctionalTests/RequiresIISAttribute.cs @@ -10,7 +10,7 @@ using System.Xml.Linq; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Win32; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresIISAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/ClientDisconnectTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/ClientDisconnectTests.cs index 16586947a1..a1dbc0f28e 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/ClientDisconnectTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/ClientDisconnectTests.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [SkipIfHostableWebCoreNotAvailable] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")] diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/ConnectionIdFeatureTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/ConnectionIdFeatureTests.cs index 27688b5e58..74773e1067 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/ConnectionIdFeatureTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/ConnectionIdFeatureTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [SkipIfHostableWebCoreNotAvailable] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")] diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/HttpBodyControlFeatureTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/HttpBodyControlFeatureTests.cs index 8ac359a21b..68f357be0f 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/HttpBodyControlFeatureTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/HttpBodyControlFeatureTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [SkipIfHostableWebCoreNotAvailable] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")] diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/MaxRequestBodySizeTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/MaxRequestBodySizeTests.cs index f70d47e198..1b34f9a371 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/MaxRequestBodySizeTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/MaxRequestBodySizeTests.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.IIS; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging.Testing; using Xunit; diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs index 3cbdc2c219..e467dd1c6c 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [SkipIfHostableWebCoreNotAvailable] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")] diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/StrictTestServerTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/StrictTestServerTests.cs index c8f62beedb..e1564b8cfe 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/StrictTestServerTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/StrictTestServerTests.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class StrictTestServerTests: LoggedTest { diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/TestServerTest.cs b/src/Servers/IIS/IIS/test/IIS.Tests/TestServerTest.cs index 40b538ea52..37254c7248 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/TestServerTest.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/TestServerTest.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [SkipIfHostableWebCoreNotAvailable] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")] diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/SkipIfHostableWebCoreNotAvailibleAttribute.cs b/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/SkipIfHostableWebCoreNotAvailibleAttribute.cs index b63743f106..2f2e694167 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/SkipIfHostableWebCoreNotAvailibleAttribute.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/SkipIfHostableWebCoreNotAvailibleAttribute.cs @@ -5,7 +5,7 @@ using System; using System.IO; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class SkipIfHostableWebCoreNotAvailableAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/TestServer.cs b/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/TestServer.cs index ebd99721d1..d688fd7413 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/TestServer.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/Utilities/TestServer.cs @@ -21,7 +21,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public class TestServer: IDisposable, IStartup { diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs index f91b27009f..4150eea6bc 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { public static class DeployerSelector { 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 97fb951acb..1ddfecfad5 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/IISExpress.FunctionalTests.csproj @@ -19,9 +19,6 @@ False - - False - diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AppOfflineIISExpressTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AppOfflineIISExpressTests.cs index 5b7af505cd..9c8d065ee0 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AppOfflineIISExpressTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AppOfflineIISExpressTests.cs @@ -3,12 +3,12 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; -using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace IISExpress.FunctionalTests.Inprocess +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class AppOfflineIISExpressTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs index 95fed4d462..ac6e702fca 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/AuthenticationTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess { [Collection(PublishedSitesCollection.Name)] public class AuthenticationTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs index 912aecf293..0aa5b2fedf 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class IISExpressShutdownTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/WebSocketTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/WebSocketTests.cs index 826001c96d..f69b205b3d 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/WebSocketTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/WebSocketTests.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(IISTestSiteCollection.Name)] [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, SkipReason = "No supported on this platform")] diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/MultipleAppTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/MultipleAppTests.cs index 9b9a7e8dba..471096e166 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/MultipleAppTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/MultipleAppTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class MultipleAppTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs index 65a10d1521..e3d04768ef 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/OutOfProcess/NtlmAuthentationTest.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Testing.xunit; using Xunit; using Xunit.Abstractions; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class NtlmAuthenticationTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/Properties/AssemblyInfo.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/Properties/AssemblyInfo.cs index b26f48a815..863fe8564c 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/Properties/AssemblyInfo.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/Properties/AssemblyInfo.cs @@ -1,7 +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 Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests; using Microsoft.Extensions.Logging.Testing; using Xunit; diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs index 2e0a68b9f7..958e86dd11 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/RequiresIISAttribute.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresIISAttribute : Attribute, ITestCondition diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/UpgradeFeatureDetectionTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/UpgradeFeatureDetectionTests.cs index 7c036c1815..9a1d0208bc 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/UpgradeFeatureDetectionTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/UpgradeFeatureDetectionTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; using Xunit; -namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests { [Collection(PublishedSitesCollection.Name)] public class UpgradeFeatureDetectionTests : IISFunctionalTestBase diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessForwardsCompatWebSite/InProcessForwardsCompatWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/InProcessForwardsCompatWebSite/InProcessForwardsCompatWebSite.csproj index 2ac833a487..341df1f26b 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessForwardsCompatWebSite/InProcessForwardsCompatWebSite.csproj +++ b/src/Servers/IIS/IIS/test/testassets/InProcessForwardsCompatWebSite/InProcessForwardsCompatWebSite.csproj @@ -12,20 +12,16 @@ - + - - - - @@ -34,8 +30,10 @@ - + + + diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj index 3faf04f7e4..8c80a84b62 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj @@ -10,7 +10,6 @@ - @@ -22,6 +21,8 @@ + + diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs index db543f266f..4b9889a141 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Program.cs @@ -85,6 +85,13 @@ namespace TestSite Console.BackgroundColor = ConsoleColor.Blue; Console.WriteLine("彡⾔"); break; + case "ConsoleWriteSingle": + Console.WriteLine("Wow!"); + return 0; + case "ConsoleWrite30Kb": + // Write over 30kb to make sure logs are truncated. + Console.WriteLine(new string('a', 40000)); + return 0; case "OverriddenServer": { var host = new WebHostBuilder() @@ -172,7 +179,9 @@ namespace TestSite factory.AddConsole(); factory.AddFilter("Console", level => level >= LogLevel.Information); }) + .UseKestrel() .UseIIS() + .UseIISIntegration() .UseStartup() .Build(); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index 656fe0614d..1825ac7a1d 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -2,9 +2,12 @@ // 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.Linq; using System.Net; +using System.Runtime.InteropServices; +using System.Security.Principal; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -12,10 +15,13 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.IISIntegration.FunctionalTests; using Microsoft.AspNetCore.Server.IIS; +using Microsoft.AspNetCore.Server.IISIntegration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Primitives; @@ -29,6 +35,132 @@ namespace TestSite { TestStartup.Register(app, this); } + + public void ConfigureServices(IServiceCollection serviceCollection) + { + serviceCollection.AddResponseCompression(); + } + + private async Task ContentRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().ContentRootPath); + + private async Task WebRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().WebRootPath); + + private async Task CurrentDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.CurrentDirectory); + + private async Task BaseDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(AppContext.BaseDirectory); + + private async Task ASPNETCORE_IIS_PHYSICAL_PATH(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH")); + + private async Task ServerAddresses(HttpContext ctx) + { + var serverAddresses = ctx.RequestServices.GetService().Features.Get(); + await ctx.Response.WriteAsync(string.Join(",", serverAddresses.Addresses)); + } + + private async Task ConsoleWrite(HttpContext ctx) + { + Console.WriteLine("TEST MESSAGE"); + + await ctx.Response.WriteAsync("Hello World"); + } + + private async Task ConsoleErrorWrite(HttpContext ctx) + { + Console.Error.WriteLine("TEST MESSAGE"); + + await ctx.Response.WriteAsync("Hello World"); + } + + public async Task Auth(HttpContext ctx) + { + var authProvider = ctx.RequestServices.GetService(); + var authScheme = (await authProvider.GetAllSchemesAsync()).SingleOrDefault(); + + await ctx.Response.WriteAsync(authScheme?.Name ?? "null"); + if (ctx.User.Identity.Name != null) + { + await ctx.Response.WriteAsync(":" + ctx.User.Identity.Name); + } + } + + public async Task GetClientCert(HttpContext context) + { + var clientCert = context.Connection.ClientCertificate; + await context.Response.WriteAsync(clientCert != null ? $"Enabled;{clientCert.GetCertHashString()}" : "Disabled"); + } + + private static int _waitingRequestCount; + + public Task WaitForAbort(HttpContext context) + { + Interlocked.Increment(ref _waitingRequestCount); + try + { + context.RequestAborted.WaitHandle.WaitOne(); + return Task.CompletedTask; + } + finally + { + Interlocked.Decrement(ref _waitingRequestCount); + } + } + + public Task Abort(HttpContext context) + { + context.Abort(); + return Task.CompletedTask; + } + + public async Task WaitingRequestCount(HttpContext context) + { + await context.Response.WriteAsync(_waitingRequestCount.ToString()); + } + + public Task CreateFile(HttpContext context) + { + var hostingEnv = context.RequestServices.GetService(); + + if (context.Connection.LocalIpAddress == null || context.Connection.RemoteIpAddress == null) + { + throw new Exception("Failed to set local and remote ip addresses"); + } + + File.WriteAllText(System.IO.Path.Combine(hostingEnv.ContentRootPath, "Started.txt"), ""); + return Task.CompletedTask; + } + + public Task OverrideServer(HttpContext context) + { + context.Response.Headers["Server"] = "MyServer/7.8"; + return Task.CompletedTask; + } + + public void CompressedData(IApplicationBuilder builder) + { + builder.UseResponseCompression(); + // write random bytes to check that compressed data is passed through + builder.Run( + async context => + { + context.Response.ContentType = "text/html"; + await context.Response.Body.WriteAsync(new byte[100], 0, 100); + }); + } + + [DllImport("kernel32.dll")] + static extern uint GetDllDirectory(uint nBufferLength, [Out] StringBuilder lpBuffer); + + private async Task DllDirectory(HttpContext context) + { + var builder = new StringBuilder(1024); + GetDllDirectory(1024, builder); + await context.Response.WriteAsync(builder.ToString()); + } + + private async Task GetEnvironmentVariable(HttpContext ctx) + { + await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable(ctx.Request.Query["name"].ToString())); + } private async Task ServerVariable(HttpContext ctx) { @@ -447,18 +579,6 @@ namespace TestSite await ctx.Request.Body.CopyToAsync(ctx.Response.Body); } - private async Task UpgradeFeatureDetection(HttpContext ctx) - { - if (ctx.Features.Get() != null) - { - await ctx.Response.WriteAsync("Enabled"); - } - else - { - await ctx.Response.WriteAsync("Disabled"); - } - } - private async Task TestReadOffsetWorks(HttpContext ctx) { var buffer = new byte[11]; @@ -645,7 +765,7 @@ namespace TestSite private async Task LargeResponseFile(HttpContext ctx) { - var tempFile = Path.GetTempFileName(); + var tempFile = System.IO.Path.GetTempFileName(); var fileContent = new string('a', 200000); var fileStream = File.OpenWrite(tempFile); @@ -742,5 +862,73 @@ namespace TestSite public Task HttpsHelloWorld(HttpContext ctx) => ctx.Response.WriteAsync("Scheme:" + ctx.Request.Scheme + "; Original:" + ctx.Request.Headers["x-original-proto"]); + + public Task Path(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.Path.Value); + + public Task Query(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.QueryString.Value); + + public Task BodyLimit(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Features.Get()?.MaxRequestBodySize?.ToString() ?? "null"); + + public Task Anonymous(HttpContext context) => context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated); + + public Task Restricted(HttpContext context) + { + if (context.User.Identity.IsAuthenticated) + { + Assert.IsType(context.User); + return context.Response.WriteAsync(context.User.Identity.AuthenticationType); + } + else + { + return context.ChallengeAsync(IISDefaults.AuthenticationScheme); + } + } + + public Task Forbidden(HttpContext context) => context.ForbidAsync(IISDefaults.AuthenticationScheme); + + public Task RestrictedNTLM(HttpContext context) + { + if (string.Equals("NTLM", context.User.Identity.AuthenticationType, StringComparison.Ordinal)) + { + return context.Response.WriteAsync("NTLM"); + } + else + { + return context.ChallengeAsync(IISDefaults.AuthenticationScheme); + } + } + + public Task UpgradeFeatureDetection(HttpContext context) => + context.Response.WriteAsync(context.Features.Get() != null? "Enabled": "Disabled"); + + public Task CheckRequestHandlerVersion(HttpContext context) + { + // We need to check if the aspnetcorev2_outofprocess dll is loaded by iisexpress.exe + // As they aren't in the same process, we will try to delete the file and expect a file + // in use error + try + { + File.Delete(context.Request.Headers["ANCMRHPath"]); + } + catch(UnauthorizedAccessException) + { + // TODO calling delete on the file will succeed when running with IIS + return context.Response.WriteAsync("Hello World"); + } + + return context.Response.WriteAsync(context.Request.Headers["ANCMRHPath"]); + } + + private async Task ProcessId(HttpContext context) + { + await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString()); + } + + public async Task HTTPS_PORT(HttpContext context) + { + var httpsPort = context.RequestServices.GetService().GetValue("HTTPS_PORT"); + + await context.Response.WriteAsync(httpsPort.HasValue ? httpsPort.Value.ToString() : "NOVALUE"); + } } } diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/web.config b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/web.config index 352ab4021b..00012eeb29 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/web.config +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/web.config @@ -4,7 +4,7 @@ - + diff --git a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/OutOfProcessWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/OutOfProcessWebSite.csproj deleted file mode 100644 index da8ee2740e..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/OutOfProcessWebSite.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - netcoreapp3.0 - OutOfProcess - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Program.cs b/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Program.cs deleted file mode 100644 index b626839f62..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -// 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.Linq; -using System.Threading; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging; - -namespace TestSite -{ - public static class Program - { - public static int Main(string[] args) - { - var mode = args.FirstOrDefault(); - - switch (mode) - { - case "CreateFile": - File.WriteAllText(args[1], ""); - return StartServer(); - case "ConsoleWrite": - Console.WriteLine("Wow!"); - return 0; - case "ConsoleWrite30Kb": - // Write over 30kb to make sure logs are truncated. - Console.WriteLine(new string('a', 40000)); - return 0; - } - - return StartServer(); - } - - private static int StartServer() - { - var host = new WebHostBuilder() - .ConfigureLogging( - (_, factory) => { - factory.AddConsole(); - factory.AddFilter("Console", level => level >= LogLevel.Information); - }) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup() - .UseKestrel() - .Build(); - - host.Run(); - return 0; - } - } -} diff --git a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Startup.cs deleted file mode 100644 index 394f248eb4..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/Startup.cs +++ /dev/null @@ -1,101 +0,0 @@ -// 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.Security.Principal; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.IISIntegration.FunctionalTests; -using Microsoft.AspNetCore.Server.IISIntegration; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace TestSite -{ - public partial class Startup - { - public void Configure(IApplicationBuilder app) - { - TestStartup.Register(app, this); - } - - public Task Path(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.Path.Value); - - public Task Query(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.QueryString.Value); - - public Task BodyLimit(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Features.Get()?.MaxRequestBodySize?.ToString() ?? "null"); - - public Task HelloWorld(HttpContext ctx) => ctx.Response.WriteAsync("Hello World"); - - public Task HttpsHelloWorld(HttpContext ctx) => - ctx.Response.WriteAsync("Scheme:" + ctx.Request.Scheme + "; Original:" + ctx.Request.Headers["x-original-proto"]); - - public Task Anonymous(HttpContext context) => context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated); - - public Task Restricted(HttpContext context) - { - if (context.User.Identity.IsAuthenticated) - { - Assert.IsType(context.User); - return context.Response.WriteAsync(context.User.Identity.AuthenticationType); - } - else - { - return context.ChallengeAsync(IISDefaults.AuthenticationScheme); - } - } - - public Task Forbidden(HttpContext context) => context.ForbidAsync(IISDefaults.AuthenticationScheme); - - public Task RestrictedNTLM(HttpContext context) - { - if (string.Equals("NTLM", context.User.Identity.AuthenticationType, StringComparison.Ordinal)) - { - return context.Response.WriteAsync("NTLM"); - } - else - { - return context.ChallengeAsync(IISDefaults.AuthenticationScheme); - } - } - - public Task UpgradeFeatureDetection(HttpContext context) => - context.Response.WriteAsync(context.Features.Get() != null? "Enabled": "Disabled"); - - public Task CheckRequestHandlerVersion(HttpContext context) - { - // We need to check if the aspnetcorev2_outofprocess dll is loaded by iisexpress.exe - // As they aren't in the same process, we will try to delete the file and expect a file - // in use error - try - { - File.Delete(context.Request.Headers["ANCMRHPath"]); - } - catch(UnauthorizedAccessException) - { - // TODO calling delete on the file will succeed when running with IIS - return context.Response.WriteAsync("Hello World"); - } - - return context.Response.WriteAsync(context.Request.Headers["ANCMRHPath"]); - } - - private async Task ProcessId(HttpContext context) - { - await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString()); - } - - public async Task HTTPS_PORT(HttpContext context) - { - var httpsPort = context.RequestServices.GetService().GetValue("HTTPS_PORT"); - - await context.Response.WriteAsync(httpsPort.HasValue ? httpsPort.Value.ToString() : "NOVALUE"); - } - } -} diff --git a/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/wwwroot/static.txt b/src/Servers/IIS/IIS/test/testassets/OutOfProcessWebSite/wwwroot/static.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Program.cs b/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Program.cs deleted file mode 100644 index e8e5392c2c..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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.Hosting; -using Microsoft.Extensions.Logging; - -namespace ANCMStressTestApp -{ - public class Program - { - public static void Main(string[] args) - { - var host = new WebHostBuilder() - .ConfigureLogging((_, factory) => - { - factory.AddConsole(); - }) - .UseKestrel() - .UseIISIntegration() - .UseStartup() - .Build(); - - host.Run(); - } - } -} diff --git a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Startup.cs deleted file mode 100644 index 3045906c5f..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/Startup.cs +++ /dev/null @@ -1,233 +0,0 @@ -// 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.Threading.Tasks; -using System.Threading; -using System.Text; -using System.Net.WebSockets; -using Microsoft.AspNetCore.Server.IIS.FunctionalTests; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Net.Http.Headers; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Primitives; -using Microsoft.Extensions.Hosting; - -namespace ANCMStressTestApp -{ - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - } - - public void Configure(IApplicationBuilder app) - { - app.Map("/HelloWorld", HelloWorld); - app.Map("/ConnectionClose", ConnectionClose); - app.Map("/EchoPostData", EchoPostData); - app.Map("/LargeResponseBody", LargeResponseBody); - app.Map("/ResponseHeaders", ResponseHeaders); - app.Map("/EnvironmentVariables", EnvironmentVariables); - app.Map("/RequestInformation", RequestInformation); - app.Map("/WebSocket", WebSockets); - - app.Run(async context => - { - await context.Response.WriteAsync("Default Page"); - }); - } - - private void HelloWorld(IApplicationBuilder app) - { - app.Run(async context => - { - await context.Response.WriteAsync("Hello World"); - }); - } - - private void ConnectionClose(IApplicationBuilder app) - { - app.Run(async context => - { - context.Response.Headers[HeaderNames.Connection] = "close"; - await context.Response.WriteAsync("Connnection Close"); - await context.Response.Body.FlushAsync(); - }); - } - - private void EchoPostData(IApplicationBuilder app) - { - app.Run(async context => - { - string responseBody = string.Empty; - - if (string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) - { - using (StreamReader reader = new StreamReader(context.Request.Body, Encoding.UTF8)) - { - responseBody = await reader.ReadToEndAsync(); - } - } - else - { - responseBody = "NoAction"; - } - - await context.Response.WriteAsync(responseBody); - }); - } - - private void LargeResponseBody(IApplicationBuilder app) - { - app.Run(async context => - { - if (int.TryParse(context.Request.Query["length"], out var length)) - { - await context.Response.WriteAsync(new string('a', length)); - } - }); - } - - private void ResponseHeaders(IApplicationBuilder app) - { - app.Run(async context => - { - context.Response.Headers["UnknownHeader"] = "test123=foo"; - context.Response.ContentType = "text/plain"; - context.Response.Headers["MultiHeader"] = new StringValues(new string[] { "1", "2" }); - await context.Response.WriteAsync("Request Complete"); - }); - } - - private void EnvironmentVariables(IApplicationBuilder app) - { - app.Run(async context => - { - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Environment Variables:" + Environment.NewLine); - var vars = Environment.GetEnvironmentVariables(); - foreach (var key in vars.Keys.Cast().OrderBy(key => key, StringComparer.OrdinalIgnoreCase)) - { - var value = vars[key]; - await context.Response.WriteAsync(key + ": " + value + Environment.NewLine); - } - await context.Response.WriteAsync(Environment.NewLine); - }); - } - - private void RequestInformation(IApplicationBuilder app) - { - app.Run(async context => - { - context.Response.ContentType = "text/plain"; - - await context.Response.WriteAsync("Address:" + Environment.NewLine); - await context.Response.WriteAsync("Scheme: " + context.Request.Scheme + Environment.NewLine); - await context.Response.WriteAsync("Host: " + context.Request.Headers["Host"] + Environment.NewLine); - await context.Response.WriteAsync("PathBase: " + context.Request.PathBase.Value + Environment.NewLine); - await context.Response.WriteAsync("Path: " + context.Request.Path.Value + Environment.NewLine); - await context.Response.WriteAsync("Query: " + context.Request.QueryString.Value + Environment.NewLine); - await context.Response.WriteAsync(Environment.NewLine); - - await context.Response.WriteAsync("Connection:" + Environment.NewLine); - await context.Response.WriteAsync("RemoteIp: " + context.Connection.RemoteIpAddress + Environment.NewLine); - await context.Response.WriteAsync("RemotePort: " + context.Connection.RemotePort + Environment.NewLine); - await context.Response.WriteAsync("LocalIp: " + context.Connection.LocalIpAddress + Environment.NewLine); - await context.Response.WriteAsync("LocalPort: " + context.Connection.LocalPort + Environment.NewLine); - await context.Response.WriteAsync(Environment.NewLine); - - await context.Response.WriteAsync("Headers:" + Environment.NewLine); - foreach (var header in context.Request.Headers) - { - await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine); - } - await context.Response.WriteAsync(Environment.NewLine); - }); - } - - private void WebSockets(IApplicationBuilder app) - { - app.Run(async context => - { - var upgradeFeature = context.Features.Get(); - - // Generate WebSocket response headers - string key = context.Request.Headers[Constants.Headers.SecWebSocketKey].ToString(); - var responseHeaders = HandshakeHelpers.GenerateResponseHeaders(key); - foreach (var headerPair in responseHeaders) - { - context.Response.Headers[headerPair.Key] = headerPair.Value; - } - - // Upgrade the connection - Stream opaqueTransport = await upgradeFeature.UpgradeAsync(); - - // Get the WebSocket object - var ws = WebSocket.CreateFromStream(opaqueTransport, isServer: true, subProtocol: null, keepAliveInterval: TimeSpan.FromMinutes(2)); - - var appLifetime = app.ApplicationServices.GetRequiredService(); - - await Echo(ws, appLifetime.ApplicationStopping); - }); - } - - private async Task Echo(WebSocket webSocket, CancellationToken token) - { - try - { - var buffer = new byte[1024 * 4]; - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), token); - bool closeFromServer = false; - string closeFromServerCmd = "CloseFromServer"; - int closeFromServerLength = closeFromServerCmd.Length; - - while (!result.CloseStatus.HasValue && !token.IsCancellationRequested && !closeFromServer) - { - if (result.Count == closeFromServerLength && - Encoding.ASCII.GetString(buffer).Substring(0, result.Count) == closeFromServerCmd) - { - // The client sent "CloseFromServer" text message to request the server to close (a test scenario). - closeFromServer = true; - } - else - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, token); - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), token); - } - } - - if (result.CloseStatus.HasValue) - { - // Client-initiated close handshake - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - else - { - // Server-initiated close handshake due to either of the two conditions: - // (1) The applicaton host is performing a graceful shutdown. - // (2) The client sent "CloseFromServer" text message to request the server to close (a test scenario). - await webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, closeFromServerCmd, CancellationToken.None); - - // The server has sent the Close frame. - // Stop sending but keep receiving until we get the Close frame from the client. - while (!result.CloseStatus.HasValue) - { - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - } - } - catch (Exception e) - { - Console.WriteLine("{0} Exception caught!", e); - } - } - } -} diff --git a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/StressTestWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/StressTestWebSite.csproj deleted file mode 100644 index bc3b3de218..0000000000 --- a/src/Servers/IIS/IIS/test/testassets/StressTestWebSite/StressTestWebSite.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - netcoreapp3.0 - true - - - - - - - - - - - - - - - - - diff --git a/src/Servers/IIS/IIS/test/testassets/TestTasks/TestTasks.csproj b/src/Servers/IIS/IIS/test/testassets/TestTasks/TestTasks.csproj index 77237de91f..6d17b83b83 100644 --- a/src/Servers/IIS/IIS/test/testassets/TestTasks/TestTasks.csproj +++ b/src/Servers/IIS/IIS/test/testassets/TestTasks/TestTasks.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.0 - win-x64;win-x86;linux-x64;osx-x64 + win-x64;linux-x64;osx-x64 diff --git a/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs b/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs index 63afe25ac9..fa38d21a09 100644 --- a/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs +++ b/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs @@ -22,130 +22,6 @@ namespace TestSite { public partial class Startup { - public void ConfigureServices(IServiceCollection serviceCollection) - { - serviceCollection.AddResponseCompression(); - } - private async Task ContentRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().ContentRootPath); - - private async Task WebRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().WebRootPath); - - private async Task CurrentDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.CurrentDirectory); - - private async Task BaseDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(AppContext.BaseDirectory); - - private async Task ASPNETCORE_IIS_PHYSICAL_PATH(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH")); - - private async Task ServerAddresses(HttpContext ctx) - { - var serverAddresses = ctx.RequestServices.GetService().Features.Get(); - await ctx.Response.WriteAsync(string.Join(",", serverAddresses.Addresses)); - } - - private async Task ConsoleWrite(HttpContext ctx) - { - Console.WriteLine("TEST MESSAGE"); - - await ctx.Response.WriteAsync("Hello World"); - } - - private async Task ConsoleErrorWrite(HttpContext ctx) - { - Console.Error.WriteLine("TEST MESSAGE"); - - await ctx.Response.WriteAsync("Hello World"); - } - - public async Task Auth(HttpContext ctx) - { - var authProvider = ctx.RequestServices.GetService(); - var authScheme = (await authProvider.GetAllSchemesAsync()).SingleOrDefault(); - - await ctx.Response.WriteAsync(authScheme?.Name ?? "null"); - if (ctx.User.Identity.Name != null) - { - await ctx.Response.WriteAsync(":" + ctx.User.Identity.Name); - } - } - - public async Task GetClientCert(HttpContext context) - { - var clientCert = context.Connection.ClientCertificate; - await context.Response.WriteAsync(clientCert != null ? $"Enabled;{clientCert.GetCertHashString()}" : "Disabled"); - } - - private static int _waitingRequestCount; - - public Task WaitForAbort(HttpContext context) - { - Interlocked.Increment(ref _waitingRequestCount); - try - { - context.RequestAborted.WaitHandle.WaitOne(); - return Task.CompletedTask; - } - finally - { - Interlocked.Decrement(ref _waitingRequestCount); - } - } - - public Task Abort(HttpContext context) - { - context.Abort(); - return Task.CompletedTask; - } - - public async Task WaitingRequestCount(HttpContext context) - { - await context.Response.WriteAsync(_waitingRequestCount.ToString()); - } - - public Task CreateFile(HttpContext context) - { - var hostingEnv = context.RequestServices.GetService(); - - if (context.Connection.LocalIpAddress == null || context.Connection.RemoteIpAddress == null) - { - throw new Exception("Failed to set local and remote ip addresses"); - } - - File.WriteAllText(System.IO.Path.Combine(hostingEnv.ContentRootPath, "Started.txt"), ""); - return Task.CompletedTask; - } - - public Task OverrideServer(HttpContext context) - { - context.Response.Headers["Server"] = "MyServer/7.8"; - return Task.CompletedTask; - } - - public void CompressedData(IApplicationBuilder builder) - { - builder.UseResponseCompression(); - // write random bytes to check that compressed data is passed through - builder.Run( - async context => - { - context.Response.ContentType = "text/html"; - await context.Response.Body.WriteAsync(new byte[100], 0, 100); - }); - } - - [DllImport("kernel32.dll")] - static extern uint GetDllDirectory(uint nBufferLength, [Out] StringBuilder lpBuffer); - - private async Task DllDirectory(HttpContext context) - { - var builder = new StringBuilder(1024); - GetDllDirectory(1024, builder); - await context.Response.WriteAsync(builder.ToString()); - } - - private async Task GetEnvironmentVariable(HttpContext ctx) - { - await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable(ctx.Request.Query["name"].ToString())); - } } } diff --git a/src/Servers/IIS/IISIntegration.sln b/src/Servers/IIS/IISIntegration.sln index b108c88420..1e41537e31 100644 --- a/src/Servers/IIS/IISIntegration.sln +++ b/src/Servers/IIS/IISIntegration.sln @@ -71,10 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestTasks", "IIS\test\testa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "IIS\test\testassets\InProcessWebSite\InProcessWebSite.csproj", "{E685D546-FDCD-4A4C-9183-728C308A9A9E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutOfProcessWebSite", "IIS\test\testassets\OutOfProcessWebSite\OutOfProcessWebSite.csproj", "{CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StressTestWebSite", "IIS\test\testassets\StressTestWebSite\StressTestWebSite.csproj", "{7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IIS", "IIS", "{8446E0D1-F421-4FD2-8B49-9183CCABD9D3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTesting.IIS", "IntegrationTesting.IIS", "{E4A4ED67-4802-41B9-8CC4-319BA94217C5}" @@ -125,8 +121,8 @@ Global {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Debug|x64.Build.0 = Debug|Any CPU {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Debug|x86.ActiveCfg = Debug|Any CPU {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Debug|x86.Build.0 = Debug|Any CPU - {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|Any CPU.Build.0 = Debug|Any CPU + {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|Any CPU.Build.0 = Release|Any CPU {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|x64.ActiveCfg = Release|Any CPU {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|x64.Build.0 = Release|Any CPU {8B3446E8-E6A8-4591-AA63-A95837C6E97C}.Release|x86.ActiveCfg = Release|Any CPU @@ -137,8 +133,8 @@ Global {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|x64.Build.0 = Debug|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|x86.ActiveCfg = Debug|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|x86.Build.0 = Debug|Any CPU - {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|Any CPU.Build.0 = Debug|Any CPU + {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|Any CPU.Build.0 = Release|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|x64.ActiveCfg = Release|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|x64.Build.0 = Release|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Release|x86.ActiveCfg = Release|Any CPU @@ -149,8 +145,8 @@ Global {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Debug|x64.Build.0 = Debug|x64 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Debug|x86.ActiveCfg = Debug|x86 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Debug|x86.Build.0 = Debug|x86 - {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|Any CPU.ActiveCfg = Debug|x64 - {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|Any CPU.Build.0 = Debug|x64 + {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|Any CPU.ActiveCfg = Release|x64 + {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|Any CPU.Build.0 = Release|x64 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x64.ActiveCfg = Release|x64 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x64.Build.0 = Release|x64 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x86.ActiveCfg = Release|x86 @@ -161,8 +157,8 @@ Global {46A8612B-418B-4D70-B3A7-A21DD0627473}.Debug|x64.Build.0 = Debug|Any CPU {46A8612B-418B-4D70-B3A7-A21DD0627473}.Debug|x86.ActiveCfg = Debug|Any CPU {46A8612B-418B-4D70-B3A7-A21DD0627473}.Debug|x86.Build.0 = Debug|Any CPU - {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|Any CPU.Build.0 = Debug|Any CPU + {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|Any CPU.Build.0 = Release|Any CPU {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|x64.ActiveCfg = Release|Any CPU {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|x64.Build.0 = Release|Any CPU {46A8612B-418B-4D70-B3A7-A21DD0627473}.Release|x86.ActiveCfg = Release|Any CPU @@ -172,7 +168,7 @@ Global {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x64.Build.0 = Debug|x64 {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x86.ActiveCfg = Debug|Win32 {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x86.Build.0 = Debug|Win32 - {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|Any CPU.ActiveCfg = Debug|Win32 + {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|Any CPU.ActiveCfg = Release|Win32 {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x64.ActiveCfg = Release|x64 {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x64.Build.0 = Release|x64 {EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x86.ActiveCfg = Release|Win32 @@ -182,7 +178,7 @@ Global {55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x64.Build.0 = Debug|x64 {55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x86.ActiveCfg = Debug|Win32 {55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x86.Build.0 = Debug|Win32 - {55494E58-E061-4C4C-A0A8-837008E72F85}.Release|Any CPU.ActiveCfg = Debug|Win32 + {55494E58-E061-4C4C-A0A8-837008E72F85}.Release|Any CPU.ActiveCfg = Release|Win32 {55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x64.ActiveCfg = Release|x64 {55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x64.Build.0 = Release|x64 {55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x86.ActiveCfg = Release|Win32 @@ -192,7 +188,7 @@ Global {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x64.Build.0 = Debug|x64 {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x86.ActiveCfg = Debug|Win32 {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x86.Build.0 = Debug|Win32 - {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|Any CPU.ActiveCfg = Debug|Win32 + {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|Any CPU.ActiveCfg = Release|Win32 {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x64.ActiveCfg = Release|x64 {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x64.Build.0 = Release|x64 {09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x86.ActiveCfg = Release|Win32 @@ -203,8 +199,8 @@ Global {48F46909-E76A-4788-BCE1-E543C0E140FE}.Debug|x64.Build.0 = Debug|Any CPU {48F46909-E76A-4788-BCE1-E543C0E140FE}.Debug|x86.ActiveCfg = Debug|Any CPU {48F46909-E76A-4788-BCE1-E543C0E140FE}.Debug|x86.Build.0 = Debug|Any CPU - {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|Any CPU.Build.0 = Debug|Any CPU + {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|Any CPU.Build.0 = Release|Any CPU {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|x64.ActiveCfg = Release|Any CPU {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|x64.Build.0 = Release|Any CPU {48F46909-E76A-4788-BCE1-E543C0E140FE}.Release|x86.ActiveCfg = Release|Any CPU @@ -214,7 +210,7 @@ Global {D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x64.Build.0 = Debug|x64 {D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x86.ActiveCfg = Debug|Win32 {D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x86.Build.0 = Debug|Win32 - {D57EA297-6DC2-4BC0-8C91-334863327863}.Release|Any CPU.ActiveCfg = Debug|Win32 + {D57EA297-6DC2-4BC0-8C91-334863327863}.Release|Any CPU.ActiveCfg = Release|Win32 {D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x64.ActiveCfg = Release|x64 {D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x64.Build.0 = Release|x64 {D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x86.ActiveCfg = Release|Win32 @@ -224,7 +220,7 @@ Global {7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x64.Build.0 = Debug|x64 {7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x86.ActiveCfg = Debug|Win32 {7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x86.Build.0 = Debug|Win32 - {7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|Any CPU.ActiveCfg = Debug|Win32 + {7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|Any CPU.ActiveCfg = Release|Win32 {7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x64.ActiveCfg = Release|x64 {7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x64.Build.0 = Release|x64 {7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.ActiveCfg = Release|Win32 @@ -234,7 +230,7 @@ Global {1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x64.Build.0 = Debug|x64 {1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x86.ActiveCfg = Debug|Win32 {1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x86.Build.0 = Debug|Win32 - {1533E271-F61B-441B-8B74-59FB61DF0552}.Release|Any CPU.ActiveCfg = Debug|Win32 + {1533E271-F61B-441B-8B74-59FB61DF0552}.Release|Any CPU.ActiveCfg = Release|Win32 {1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x64.ActiveCfg = Release|x64 {1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x64.Build.0 = Release|x64 {1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x86.ActiveCfg = Release|Win32 @@ -245,20 +241,20 @@ Global {D182103F-8405-4647-B158-C36F598657EF}.Debug|x64.Build.0 = Debug|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Debug|x86.ActiveCfg = Debug|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Debug|x86.Build.0 = Debug|Any CPU - {D182103F-8405-4647-B158-C36F598657EF}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {D182103F-8405-4647-B158-C36F598657EF}.Release|Any CPU.Build.0 = Debug|Any CPU + {D182103F-8405-4647-B158-C36F598657EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D182103F-8405-4647-B158-C36F598657EF}.Release|Any CPU.Build.0 = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x64.ActiveCfg = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x64.Build.0 = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x86.ActiveCfg = Release|Any CPU {D182103F-8405-4647-B158-C36F598657EF}.Release|x86.Build.0 = Release|Any CPU - {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.ActiveCfg = Debug|x86 - {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.Build.0 = Debug|x86 + {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.ActiveCfg = Debug|x64 + {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.Build.0 = Debug|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x64.ActiveCfg = Debug|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x64.Build.0 = Debug|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x86.ActiveCfg = Debug|x86 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x86.Build.0 = Debug|x86 - {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.ActiveCfg = Debug|x86 - {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.Build.0 = Debug|x86 + {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.ActiveCfg = Release|x64 + {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.Build.0 = Release|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x64.ActiveCfg = Release|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x64.Build.0 = Release|x64 {C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x86.ActiveCfg = Release|x86 @@ -269,8 +265,8 @@ Global {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x64.Build.0 = Debug|Any CPU {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x86.ActiveCfg = Debug|Any CPU {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x86.Build.0 = Debug|Any CPU - {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.Build.0 = Debug|Any CPU + {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.Build.0 = Release|Any CPU {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x64.ActiveCfg = Release|Any CPU {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x64.Build.0 = Release|Any CPU {D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x86.ActiveCfg = Release|Any CPU @@ -281,8 +277,8 @@ Global {582B07BC-73F4-4689-8557-B039298BD82C}.Debug|x64.Build.0 = Debug|Any CPU {582B07BC-73F4-4689-8557-B039298BD82C}.Debug|x86.ActiveCfg = Debug|Any CPU {582B07BC-73F4-4689-8557-B039298BD82C}.Debug|x86.Build.0 = Debug|Any CPU - {582B07BC-73F4-4689-8557-B039298BD82C}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {582B07BC-73F4-4689-8557-B039298BD82C}.Release|Any CPU.Build.0 = Debug|Any CPU + {582B07BC-73F4-4689-8557-B039298BD82C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {582B07BC-73F4-4689-8557-B039298BD82C}.Release|Any CPU.Build.0 = Release|Any CPU {582B07BC-73F4-4689-8557-B039298BD82C}.Release|x64.ActiveCfg = Release|Any CPU {582B07BC-73F4-4689-8557-B039298BD82C}.Release|x64.Build.0 = Release|Any CPU {582B07BC-73F4-4689-8557-B039298BD82C}.Release|x86.ActiveCfg = Release|Any CPU @@ -293,8 +289,8 @@ Global {D1EA5D99-28FD-4197-81DE-17098846B38B}.Debug|x64.Build.0 = Debug|Any CPU {D1EA5D99-28FD-4197-81DE-17098846B38B}.Debug|x86.ActiveCfg = Debug|Any CPU {D1EA5D99-28FD-4197-81DE-17098846B38B}.Debug|x86.Build.0 = Debug|Any CPU - {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|Any CPU.Build.0 = Debug|Any CPU + {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|Any CPU.Build.0 = Release|Any CPU {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|x64.ActiveCfg = Release|Any CPU {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|x64.Build.0 = Release|Any CPU {D1EA5D99-28FD-4197-81DE-17098846B38B}.Release|x86.ActiveCfg = Release|Any CPU @@ -305,50 +301,32 @@ Global {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Debug|x64.Build.0 = Debug|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Debug|x86.ActiveCfg = Debug|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Debug|x86.Build.0 = Debug|Any CPU - {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|Any CPU.Build.0 = Debug|Any CPU + {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|Any CPU.Build.0 = Release|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|x64.ActiveCfg = Release|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|x64.Build.0 = Release|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|x86.ActiveCfg = Release|Any CPU {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0}.Release|x86.Build.0 = Release|Any CPU - {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|Any CPU.ActiveCfg = Debug|x86 + {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|Any CPU.ActiveCfg = Debug|x64 + {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|Any CPU.Build.0 = Debug|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|x64.ActiveCfg = Debug|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|x64.Build.0 = Debug|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|x86.ActiveCfg = Debug|x86 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Debug|x86.Build.0 = Debug|x86 - {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|Any CPU.ActiveCfg = Debug|x86 + {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|Any CPU.ActiveCfg = Release|x64 + {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|Any CPU.Build.0 = Release|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|x64.ActiveCfg = Release|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|x64.Build.0 = Release|x64 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|x86.ActiveCfg = Release|x86 {E685D546-FDCD-4A4C-9183-728C308A9A9E}.Release|x86.Build.0 = Release|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Debug|Any CPU.ActiveCfg = Debug|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Debug|x64.ActiveCfg = Debug|x64 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Debug|x64.Build.0 = Debug|x64 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Debug|x86.ActiveCfg = Debug|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Debug|x86.Build.0 = Debug|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Release|Any CPU.ActiveCfg = Debug|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Release|x64.ActiveCfg = Release|x64 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Release|x64.Build.0 = Release|x64 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Release|x86.ActiveCfg = Release|x86 - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70}.Release|x86.Build.0 = Release|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Debug|Any CPU.ActiveCfg = Debug|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Debug|x64.ActiveCfg = Debug|x64 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Debug|x64.Build.0 = Debug|x64 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Debug|x86.ActiveCfg = Debug|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Debug|x86.Build.0 = Debug|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Release|Any CPU.ActiveCfg = Debug|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Release|x64.ActiveCfg = Release|x64 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Release|x64.Build.0 = Release|x64 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Release|x86.ActiveCfg = Release|x86 - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE}.Release|x86.Build.0 = Release|x86 {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|x64.ActiveCfg = Debug|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|x64.Build.0 = Debug|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|x86.ActiveCfg = Debug|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Debug|x86.Build.0 = Debug|Any CPU - {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|Any CPU.Build.0 = Debug|Any CPU + {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|Any CPU.Build.0 = Release|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|x64.ActiveCfg = Release|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|x64.Build.0 = Release|Any CPU {BACB6E5C-A4DB-4513-B9DD-8FEC752585F0}.Release|x86.ActiveCfg = Release|Any CPU @@ -359,8 +337,8 @@ Global {01452FA1-65C9-4A38-A544-E55E63B93357}.Debug|x64.Build.0 = Debug|Any CPU {01452FA1-65C9-4A38-A544-E55E63B93357}.Debug|x86.ActiveCfg = Debug|Any CPU {01452FA1-65C9-4A38-A544-E55E63B93357}.Debug|x86.Build.0 = Debug|Any CPU - {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|Any CPU.Build.0 = Debug|Any CPU + {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|Any CPU.Build.0 = Release|Any CPU {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|x64.ActiveCfg = Release|Any CPU {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|x64.Build.0 = Release|Any CPU {01452FA1-65C9-4A38-A544-E55E63B93357}.Release|x86.ActiveCfg = Release|Any CPU @@ -370,7 +348,7 @@ Global {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x64.Build.0 = Debug|x64 {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x86.ActiveCfg = Debug|Win32 {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x86.Build.0 = Debug|Win32 - {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|Any CPU.ActiveCfg = Debug|Win32 + {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|Any CPU.ActiveCfg = Release|Win32 {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x64.ActiveCfg = Release|x64 {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x64.Build.0 = Release|x64 {1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x86.ActiveCfg = Release|Win32 @@ -381,8 +359,8 @@ Global {2C720685-FBE2-4450-9A01-CAA327D3485A}.Debug|x64.Build.0 = Debug|Any CPU {2C720685-FBE2-4450-9A01-CAA327D3485A}.Debug|x86.ActiveCfg = Debug|Any CPU {2C720685-FBE2-4450-9A01-CAA327D3485A}.Debug|x86.Build.0 = Debug|Any CPU - {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|Any CPU.Build.0 = Debug|Any CPU + {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|Any CPU.Build.0 = Release|Any CPU {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|x64.ActiveCfg = Release|Any CPU {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|x64.Build.0 = Release|Any CPU {2C720685-FBE2-4450-9A01-CAA327D3485A}.Release|x86.ActiveCfg = Release|Any CPU @@ -392,7 +370,7 @@ Global {CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x64.Build.0 = Debug|x64 {CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x86.ActiveCfg = Debug|Win32 {CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x86.Build.0 = Debug|Win32 - {CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Any CPU.ActiveCfg = Debug|Win32 + {CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Any CPU.ActiveCfg = Release|Win32 {CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.ActiveCfg = Release|x64 {CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.Build.0 = Release|x64 {CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x86.ActiveCfg = Release|Win32 @@ -420,8 +398,6 @@ Global {D1EA5D99-28FD-4197-81DE-17098846B38B} = {11AEFF49-1076-4D7B-9F9A-98AC68B5193A} {2DD1269D-131C-4531-BB0D-7BE0EF8E56D0} = {E93D0704-4E09-4266-82BA-29E14CADE422} {E685D546-FDCD-4A4C-9183-728C308A9A9E} = {E93D0704-4E09-4266-82BA-29E14CADE422} - {CFC23E23-7AD5-4D7D-9F3C-DBD241231E70} = {E93D0704-4E09-4266-82BA-29E14CADE422} - {7C0B25FC-DADA-489D-A3FB-F3D732FC8ECE} = {E93D0704-4E09-4266-82BA-29E14CADE422} {8FD4E4F7-C4A2-4934-88DA-67209AB12017} = {8446E0D1-F421-4FD2-8B49-9183CCABD9D3} {8E03138E-100D-46C7-ADCE-DAC054135825} = {8446E0D1-F421-4FD2-8B49-9183CCABD9D3} {11AEFF49-1076-4D7B-9F9A-98AC68B5193A} = {8446E0D1-F421-4FD2-8B49-9183CCABD9D3} From 9f9c79bbe8cc2628e6f68a9f47f167c1c078deaf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 29 May 2019 00:59:11 +0100 Subject: [PATCH 30/95] Use same code for Json Platform as TE (#7292) --- .../perf/PlatformBenchmarks/BenchmarkApplication.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.cs b/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.cs index b1e5c8b475..25382fe7b0 100644 --- a/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.cs +++ b/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.cs @@ -117,7 +117,7 @@ namespace PlatformBenchmarks // Content-Length header writer.Write(_headerContentLength); - var jsonPayload = JsonSerializer.ToBytes(new { message = "Hello, World!" }, SerializerOptions); + var jsonPayload = JsonSerializer.ToUtf8Bytes(new JsonMessage { message = "Hello, World!" }, SerializerOptions); writer.WriteNumeric((uint)jsonPayload.Length); // End of headers @@ -155,5 +155,10 @@ namespace PlatformBenchmarks PlainText, Json } + + public struct JsonMessage + { + public string message { get; set; } + } } } From 6ca30bbfc9075bc1285df9d8a1e8d81fa20d9f9e Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Tue, 28 May 2019 17:17:50 -0700 Subject: [PATCH 31/95] Add runtime support for Blazor attribute splatting (#10357) * Add basic tests for duplicate attributes * Add AddMultipleAttributes improve RTB - Adds AddMultipleAttributes - Fix RTB to de-dupe attributes - Fix RTB behaviour with boxed EventCallback (#8336) - Add lots of tests for new RTB behaviour and EventCallback * Harden EventCallback test This was being flaky while I was running E2E tests locally, and it wasn't using a resiliant equality comparison. * Add new setting on ParameterAttribute Adds the option to mark a parameter as an *extra* parameter. Why is this on ParameterAttribute and not a new type? It makes sense to make it a modifier on Parameter so you can use it both ways (explicitly set it, or allow it to collect *extras*). Added unit tests and validations for interacting with the new setting. * Add renderer tests for 'extra' parameters * Refactor Diagnostics for more analyzers * Simplify analyzer and fix CascadingParameter This is the *easy way* to write an analyzer that looks at declarations. The information that's avaialable from symbols is much more high level than syntax. Much of what's in this code today is needed to reverse engineer what the compiler does already. If you use symbols you get to benefit from all of that. Also added validation for cascading parameters to the analyzer that I think was just missing due to oversight. The overall design pattern here is what I've been converging on for the ASP.NET Core analyzers as a whole, and it seems to scale really well. * Add analyzer for types * Add analyzer for uniqueness This involved a refactor to run the analyzer per-type instead of per-property. * Fix project file * Adjust name * PR feedback on PCE and more renames * Remove unused parameter * Fix #10398 * Add E2E test * Pranavs cool feedback * Optimize silent frame removal * code check * pr feedback --- .../Analyzers/src/ComponentFacts.cs | 89 +++ .../src/ComponentParameterAnalyzer.cs | 111 +++ ...nentParametersShouldNotBePublicAnalyzer.cs | 76 -- ...ametersShouldNotBePublicCodeFixProvider.cs | 12 +- .../Analyzers/src/ComponentSymbols.cs | 65 ++ src/Components/Analyzers/src/ComponentsApi.cs | 10 +- .../Analyzers/src/DiagnosticDescriptors.cs | 41 + .../Analyzers/src/Properties/AssemblyInfo.cs | 3 + .../Analyzers/src/Resources.Designer.cs | 59 +- src/Components/Analyzers/src/Resources.resx | 74 +- ...rCaptureUnmatchedValuesHasWrongTypeTest.cs | 80 ++ ...rCaptureUnmatchedValuesMustBeUniqueTest.cs | 68 ++ ...omponentParametersShouldNotBePublicTest.cs | 42 +- .../test/ComponentsTestDeclarations.cs | 22 + ...ft.AspNetCore.Components.netstandard2.0.cs | 3 + .../Components/src/EventCallback.cs | 24 +- .../Components/src/ParameterAttribute.cs | 21 + .../src/ParameterCollectionExtensions.cs | 136 +++- .../Components/src/RenderTree/ArrayBuilder.cs | 2 +- .../src/RenderTree/RenderTreeBuilder.cs | 219 +++++- .../src/RenderTree/RenderTreeFrameType.cs | 5 + ...meterCollectionAssignmentExtensionsTest.cs | 157 +++- .../Components/test/RenderTreeBuilderTest.cs | 700 +++++++++++++++++- .../Components/test/RendererTest.cs | 85 +++ .../test/Rendering/HtmlRendererTestBase.cs | 34 + .../E2ETest/Tests/ComponentRenderingTest.cs | 20 + .../test/E2ETest/Tests/EventCallbackTest.cs | 5 +- .../DuplicateAttributesComponent.razor | 28 + ...licateAttributesOnElementChildComponent.cs | 37 + .../test/testassets/BasicTestApp/Index.razor | 1 + 30 files changed, 2060 insertions(+), 169 deletions(-) create mode 100644 src/Components/Analyzers/src/ComponentFacts.cs create mode 100644 src/Components/Analyzers/src/ComponentParameterAnalyzer.cs delete mode 100644 src/Components/Analyzers/src/ComponentParametersShouldNotBePublicAnalyzer.cs create mode 100644 src/Components/Analyzers/src/ComponentSymbols.cs create mode 100644 src/Components/Analyzers/src/DiagnosticDescriptors.cs create mode 100644 src/Components/Analyzers/src/Properties/AssemblyInfo.cs create mode 100644 src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesHasWrongTypeTest.cs create mode 100644 src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs create mode 100644 src/Components/Analyzers/test/ComponentsTestDeclarations.cs create mode 100644 src/Components/test/testassets/BasicTestApp/DuplicateAttributesComponent.razor create mode 100644 src/Components/test/testassets/BasicTestApp/DuplicateAttributesOnElementChildComponent.cs diff --git a/src/Components/Analyzers/src/ComponentFacts.cs b/src/Components/Analyzers/src/ComponentFacts.cs new file mode 100644 index 0000000000..031df5586f --- /dev/null +++ b/src/Components/Analyzers/src/ComponentFacts.cs @@ -0,0 +1,89 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + internal static class ComponentFacts + { + public static bool IsAnyParameter(ComponentSymbols symbols, IPropertySymbol property) + { + if (symbols == null) + { + throw new ArgumentNullException(nameof(symbols)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + return property.GetAttributes().Any(a => + { + return a.AttributeClass == symbols.ParameterAttribute || a.AttributeClass == symbols.CascadingParameterAttribute; + }); + } + + public static bool IsParameter(ComponentSymbols symbols, IPropertySymbol property) + { + if (symbols == null) + { + throw new ArgumentNullException(nameof(symbols)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + return property.GetAttributes().Any(a => a.AttributeClass == symbols.ParameterAttribute); + } + + public static bool IsParameterWithCaptureUnmatchedValues(ComponentSymbols symbols, IPropertySymbol property) + { + if (symbols == null) + { + throw new ArgumentNullException(nameof(symbols)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + var attribute = property.GetAttributes().FirstOrDefault(a => a.AttributeClass == symbols.ParameterAttribute); + if (attribute == null) + { + return false; + } + + foreach (var kvp in attribute.NamedArguments) + { + if (string.Equals(kvp.Key, ComponentsApi.ParameterAttribute.CaptureUnmatchedValues, StringComparison.Ordinal)) + { + return kvp.Value.Value as bool? ?? false; + } + } + + return false; + } + + public static bool IsCascadingParameter(ComponentSymbols symbols, IPropertySymbol property) + { + if (symbols == null) + { + throw new ArgumentNullException(nameof(symbols)); + } + + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + return property.GetAttributes().Any(a => a.AttributeClass == symbols.CascadingParameterAttribute); + } + } +} diff --git a/src/Components/Analyzers/src/ComponentParameterAnalyzer.cs b/src/Components/Analyzers/src/ComponentParameterAnalyzer.cs new file mode 100644 index 0000000000..20ad4ab6bd --- /dev/null +++ b/src/Components/Analyzers/src/ComponentParameterAnalyzer.cs @@ -0,0 +1,111 @@ +// 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.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ComponentParameterAnalyzer : DiagnosticAnalyzer + { + public ComponentParameterAnalyzer() + { + SupportedDiagnostics = ImmutableArray.Create(new[] + { + DiagnosticDescriptors.ComponentParametersShouldNotBePublic, + DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesMustBeUnique, + DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesHasWrongType, + }); + } + + public override ImmutableArray SupportedDiagnostics { get; } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(context => + { + if (!ComponentSymbols.TryCreate(context.Compilation, out var symbols)) + { + // Types we need are not defined. + return; + } + + // This operates per-type because one of the validations we need has to look for duplicates + // defined on the same type. + context.RegisterSymbolStartAction(context => + { + var properties = new List(); + + var type = (INamedTypeSymbol)context.Symbol; + foreach (var member in type.GetMembers()) + { + if (member is IPropertySymbol property && ComponentFacts.IsAnyParameter(symbols, property)) + { + // Annotated with [Parameter] or [CascadingParameter] + properties.Add(property); + } + } + + if (properties.Count == 0) + { + return; + } + + context.RegisterSymbolEndAction(context => + { + var captureUnmatchedValuesParameters = new List(); + + // Per-property validations + foreach (var property in properties) + { + if (property.SetMethod?.DeclaredAccessibility == Accessibility.Public) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.ComponentParametersShouldNotBePublic, + property.Locations[0], + property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat))); + } + + if (ComponentFacts.IsParameterWithCaptureUnmatchedValues(symbols, property)) + { + captureUnmatchedValuesParameters.Add(property); + + // Check the type, we need to be able to assign a Dictionary + var conversion = context.Compilation.ClassifyConversion(symbols.ParameterCaptureUnmatchedValuesRuntimeType, property.Type); + if (!conversion.Exists || conversion.IsExplicit) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesHasWrongType, + property.Locations[0], + property.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), + property.Type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), + symbols.ParameterCaptureUnmatchedValuesRuntimeType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat))); + } + } + } + + // Check if the type defines multiple CaptureUnmatchedValues parameters. Doing this outside the loop means we place the + // errors on the type. + if (captureUnmatchedValuesParameters.Count > 1) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesMustBeUnique, + context.Symbol.Locations[0], + type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), + Environment.NewLine, + string.Join( + Environment.NewLine, + captureUnmatchedValuesParameters.Select(p => p.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)).OrderBy(n => n)))); + } + }); + }, SymbolKind.NamedType); + }); + } + } +} diff --git a/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicAnalyzer.cs b/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicAnalyzer.cs deleted file mode 100644 index a9ef40f12c..0000000000 --- a/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicAnalyzer.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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.Components.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Immutable; -using System.Linq; - -namespace Microsoft.AspNetCore.Components.Analyzers -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class ComponentParametersShouldNotBePublicAnalyzer : DiagnosticAnalyzer - { - public const string DiagnosticId = "BL9993"; - private const string Category = "Encapsulation"; - - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Title), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Format), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Description), Resources.ResourceManager, typeof(Resources)); - private static DiagnosticDescriptor Rule = new DiagnosticDescriptor( - DiagnosticId, - Title, - MessageFormat, - Category, - DiagnosticSeverity.Warning, - isEnabledByDefault: true, - description: Description); - - public override ImmutableArray SupportedDiagnostics - => ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.PropertyDeclaration); - } - - private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) - { - var semanticModel = context.SemanticModel; - var declaration = (PropertyDeclarationSyntax)context.Node; - - var parameterAttribute = declaration.AttributeLists - .SelectMany(list => list.Attributes) - .Where(attr => semanticModel.GetTypeInfo(attr).Type?.ToDisplayString() == ComponentsApi.ParameterAttribute.FullTypeName) - .FirstOrDefault(); - - if (parameterAttribute != null && IsPubliclySettable(declaration)) - { - var identifierText = declaration.Identifier.Text; - if (!string.IsNullOrEmpty(identifierText)) - { - context.ReportDiagnostic(Diagnostic.Create( - Rule, - declaration.GetLocation(), - identifierText)); - } - } - } - - private static bool IsPubliclySettable(PropertyDeclarationSyntax declaration) - { - // If the property has a setter explicitly marked private/protected/internal, then it's not public - var setter = declaration.AccessorList?.Accessors.SingleOrDefault(x => x.Keyword.IsKind(SyntaxKind.SetKeyword)); - if (setter != null && setter.Modifiers.Any(x => x.IsKind(SyntaxKind.PrivateKeyword) || x.IsKind(SyntaxKind.ProtectedKeyword) || x.IsKind(SyntaxKind.InternalKeyword))) - { - return false; - } - - // Otherwise fallback to the property declaration modifiers - return declaration.Modifiers.Any(x => x.IsKind(SyntaxKind.PublicKeyword)); - } - } -} diff --git a/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicCodeFixProvider.cs b/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicCodeFixProvider.cs index 001e761571..84555969b9 100644 --- a/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicCodeFixProvider.cs +++ b/src/Components/Analyzers/src/ComponentParametersShouldNotBePublicCodeFixProvider.cs @@ -1,15 +1,15 @@ -// 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.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading.Tasks; namespace Microsoft.AspNetCore.Components.Analyzers { @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_FixTitle), Resources.ResourceManager, typeof(Resources)); public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(ComponentParametersShouldNotBePublicAnalyzer.DiagnosticId); + => ImmutableArray.Create(DiagnosticDescriptors.ComponentParametersShouldNotBePublic.Id); public sealed override FixAllProvider GetFixAllProvider() { diff --git a/src/Components/Analyzers/src/ComponentSymbols.cs b/src/Components/Analyzers/src/ComponentSymbols.cs new file mode 100644 index 0000000000..ba1bfc4869 --- /dev/null +++ b/src/Components/Analyzers/src/ComponentSymbols.cs @@ -0,0 +1,65 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + internal class ComponentSymbols + { + public static bool TryCreate(Compilation compilation, out ComponentSymbols symbols) + { + if (compilation == null) + { + throw new ArgumentNullException(nameof(compilation)); + } + + var parameterAttribute = compilation.GetTypeByMetadataName(ComponentsApi.ParameterAttribute.MetadataName); + if (parameterAttribute == null) + { + symbols = null; + return false; + } + + var cascadingParameterAttribute = compilation.GetTypeByMetadataName(ComponentsApi.CascadingParameterAttribute.MetadataName); + if (cascadingParameterAttribute == null) + { + symbols = null; + return false; + } + + var dictionary = compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2"); + var @string = compilation.GetSpecialType(SpecialType.System_String); + var @object = compilation.GetSpecialType(SpecialType.System_Object); + if (dictionary == null || @string == null || @object == null) + { + symbols = null; + return false; + } + + var parameterCaptureUnmatchedValuesRuntimeType = dictionary.Construct(@string, @object); + + symbols = new ComponentSymbols(parameterAttribute, cascadingParameterAttribute, parameterCaptureUnmatchedValuesRuntimeType); + return true; + } + + private ComponentSymbols( + INamedTypeSymbol parameterAttribute, + INamedTypeSymbol cascadingParameterAttribute, + INamedTypeSymbol parameterCaptureUnmatchedValuesRuntimeType) + { + ParameterAttribute = parameterAttribute; + CascadingParameterAttribute = cascadingParameterAttribute; + ParameterCaptureUnmatchedValuesRuntimeType = parameterCaptureUnmatchedValuesRuntimeType; + } + + public INamedTypeSymbol ParameterAttribute { get; } + + // Dictionary + public INamedTypeSymbol ParameterCaptureUnmatchedValuesRuntimeType { get; } + + public INamedTypeSymbol CascadingParameterAttribute { get; } + } +} diff --git a/src/Components/Analyzers/src/ComponentsApi.cs b/src/Components/Analyzers/src/ComponentsApi.cs index fdb83c6f05..83eb39cc2c 100644 --- a/src/Components/Analyzers/src/ComponentsApi.cs +++ b/src/Components/Analyzers/src/ComponentsApi.cs @@ -1,7 +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. -namespace Microsoft.AspNetCore.Components.Shared +namespace Microsoft.AspNetCore.Components.Analyzers { // Constants for type and method names used in code-generation // Keep these in sync with the actual definitions @@ -13,6 +13,14 @@ namespace Microsoft.AspNetCore.Components.Shared { public static readonly string FullTypeName = "Microsoft.AspNetCore.Components.ParameterAttribute"; public static readonly string MetadataName = FullTypeName; + + public static readonly string CaptureUnmatchedValues = "CaptureUnmatchedValues"; + } + + public static class CascadingParameterAttribute + { + public static readonly string FullTypeName = "Microsoft.AspNetCore.Components.CascadingParameterAttribute"; + public static readonly string MetadataName = FullTypeName; } } } diff --git a/src/Components/Analyzers/src/DiagnosticDescriptors.cs b/src/Components/Analyzers/src/DiagnosticDescriptors.cs new file mode 100644 index 0000000000..cb380e5646 --- /dev/null +++ b/src/Components/Analyzers/src/DiagnosticDescriptors.cs @@ -0,0 +1,41 @@ +// 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.CodeAnalysis; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + internal static class DiagnosticDescriptors + { + // Note: The Razor Compiler (including Components features) use the RZ prefix for diagnostics, so there's currently + // no change of clashing between that and the BL prefix used here. + // + // Tracking https://github.com/aspnet/AspNetCore/issues/10382 to rationalize this + public static readonly DiagnosticDescriptor ComponentParametersShouldNotBePublic = new DiagnosticDescriptor( + "BL0001", + new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Title), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Format), Resources.ResourceManager, typeof(Resources)), + "Encapsulation", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(Resources.ComponentParametersShouldNotBePublic_Description), Resources.ResourceManager, typeof(Resources))); + + public static readonly DiagnosticDescriptor ComponentParameterCaptureUnmatchedValuesMustBeUnique = new DiagnosticDescriptor( + "BL0002", + new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesMustBeUnique_Title), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesMustBeUnique_Format), Resources.ResourceManager, typeof(Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesMustBeUnique_Description), Resources.ResourceManager, typeof(Resources))); + + public static readonly DiagnosticDescriptor ComponentParameterCaptureUnmatchedValuesHasWrongType = new DiagnosticDescriptor( + "BL0003", + new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesHasWrongType_Title), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesHasWrongType_Format), Resources.ResourceManager, typeof(Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(Resources.ComponentParameterCaptureUnmatchedValuesHasWrongType_Description), Resources.ResourceManager, typeof(Resources))); + } +} diff --git a/src/Components/Analyzers/src/Properties/AssemblyInfo.cs b/src/Components/Analyzers/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6e6689b592 --- /dev/null +++ b/src/Components/Analyzers/src/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.Analyzers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Components/Analyzers/src/Resources.Designer.cs b/src/Components/Analyzers/src/Resources.Designer.cs index df4ae809b0..e7f866fe08 100644 --- a/src/Components/Analyzers/src/Resources.Designer.cs +++ b/src/Components/Analyzers/src/Resources.Designer.cs @@ -10,7 +10,6 @@ namespace Microsoft.AspNetCore.Components.Analyzers { using System; - using System.Reflection; /// @@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -40,7 +39,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Components.Analyzers.Resources", typeof(Resources).GetTypeInfo().Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Components.Analyzers.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -61,6 +60,60 @@ namespace Microsoft.AspNetCore.Components.Analyzers { } } + /// + /// Looks up a localized string similar to Component parameters with CaptureUnmatchedValuess must be a correct type.. + /// + internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Description { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component parameter '{0}' defines CaptureUnmatchedValuess but has an unsupported type '{1}'. Use a type assignable from '{2}'.. + /// + internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Format { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component parameter with CaptureUnmatchedValues has the wrong type. + /// + internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Title { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Components may only define a single parameter with CaptureUnmatchedValues.. + /// + internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Description { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component type '{0}' defines properties multiple parameters with CaptureUnmatchedValues. Properties: {1}{2}. + /// + internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Format { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component has multiple CaptureUnmatchedValues parameters. + /// + internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Title { + get { + return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Component parameters should not have public setters.. /// diff --git a/src/Components/Analyzers/src/Resources.resx b/src/Components/Analyzers/src/Resources.resx index ec781527eb..2996ec3082 100644 --- a/src/Components/Analyzers/src/Resources.resx +++ b/src/Components/Analyzers/src/Resources.resx @@ -1,17 +1,17 @@ - @@ -129,4 +129,22 @@ Component parameter has public setter - \ No newline at end of file + + Components may only define a single parameter with CaptureUnmatchedValues. + + + Component type '{0}' defines properties multiple parameters with CaptureUnmatchedValues. Properties: {1}{2} + + + Component has multiple CaptureUnmatchedValues parameters + + + Component parameters with CaptureUnmatchedValues must be a correct type. + + + Component parameter '{0}' defines CaptureUnmatchedValues but has an unsupported type '{1}'. Use a type assignable from '{2}'. + + + Component parameter with CaptureUnmatchedValues has the wrong type + + diff --git a/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesHasWrongTypeTest.cs b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesHasWrongTypeTest.cs new file mode 100644 index 0000000000..ba547ff85b --- /dev/null +++ b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesHasWrongTypeTest.cs @@ -0,0 +1,80 @@ +// 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.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Analyzers.Test +{ + public class ComponentParameterCaptureUnmatchedValuesHasWrongTypeTest : DiagnosticVerifier + { + [Theory] + [InlineData("System.Collections.Generic.IEnumerable>")] + [InlineData("System.Collections.Generic.Dictionary")] + [InlineData("System.Collections.Generic.IDictionary")] + [InlineData("System.Collections.Generic.IReadOnlyDictionary")] + public void IgnoresPropertiesWithSupportedType(string propertyType) + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + [Parameter(CaptureUnmatchedValues = true)] {propertyType} MyProperty {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void IgnoresPropertiesWithCaptureUnmatchedValuesFalse() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + [Parameter(CaptureUnmatchedValues = false)] string MyProperty {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void AddsDiagnosticForInvalidType() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + [Parameter(CaptureUnmatchedValues = true)] string MyProperty {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + + VerifyCSharpDiagnostic(test, + new DiagnosticResult + { + Id = DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesHasWrongType.Id, + Message = "Component parameter 'ConsoleApplication1.TypeName.MyProperty' defines CaptureUnmatchedValues but has an unsupported type 'string'. Use a type assignable from 'System.Collections.Generic.Dictionary'.", + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation("Test0.cs", 7, 63) + } + }); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new ComponentParameterAnalyzer(); + } + } +} diff --git a/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs new file mode 100644 index 0000000000..0a9668182c --- /dev/null +++ b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs @@ -0,0 +1,68 @@ +// 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.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Analyzers.Test +{ + public class ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest : DiagnosticVerifier + { + [Fact] + public void IgnoresPropertiesWithCaptureUnmatchedValuesFalse() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using System.Collections.Generic; + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + [Parameter(CaptureUnmatchedValues = false)] string MyProperty {{ get; set; }} + [Parameter(CaptureUnmatchedValues = true)] Dictionary MyOtherProperty {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void AddsDiagnosticForMultipleCaptureUnmatchedValuesProperties() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using System.Collections.Generic; + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + [Parameter(CaptureUnmatchedValues = true)] Dictionary MyProperty {{ get; set; }} + [Parameter(CaptureUnmatchedValues = true)] Dictionary MyOtherProperty {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + + var message = @"Component type 'ConsoleApplication1.TypeName' defines properties multiple parameters with CaptureUnmatchedValues. Properties: +ConsoleApplication1.TypeName.MyOtherProperty +ConsoleApplication1.TypeName.MyProperty"; + + VerifyCSharpDiagnostic(test, + new DiagnosticResult + { + Id = DiagnosticDescriptors.ComponentParameterCaptureUnmatchedValuesMustBeUnique.Id, + Message = message, + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation("Test0.cs", 6, 15) + } + }); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() + { + return new ComponentParameterAnalyzer(); + } + } +} diff --git a/src/Components/Analyzers/test/ComponentParametersShouldNotBePublicTest.cs b/src/Components/Analyzers/test/ComponentParametersShouldNotBePublicTest.cs index 1a4f93a5a6..8d5df9053f 100644 --- a/src/Components/Analyzers/test/ComponentParametersShouldNotBePublicTest.cs +++ b/src/Components/Analyzers/test/ComponentParametersShouldNotBePublicTest.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.Components; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; @@ -12,15 +11,6 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test { public class ComponentParametersShouldNotBePublic : CodeFixVerifier { - static string ParameterSource = $@" - namespace {typeof(ParameterAttribute).Namespace} - {{ - public class {typeof(ParameterAttribute).Name} : System.Attribute - {{ - }} - }} -"; - [Fact] public void IgnoresPublicPropertiesWithoutParameterAttribute() { @@ -31,7 +21,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test { public string MyProperty { get; set; } } - }" + ParameterSource; + }" + ComponentsTestDeclarations.Source; VerifyCSharpDiagnostic(test); } @@ -48,10 +38,10 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test { [Parameter] string MyPropertyNoModifer { get; set; } [Parameter] private string MyPropertyPrivate { get; set; } - [Parameter] protected string MyPropertyProtected { get; set; } - [Parameter] internal string MyPropertyInternal { get; set; } + [CascadingParameter] protected string MyPropertyProtected { get; set; } + [CascadingParameter] internal string MyPropertyInternal { get; set; } } - }" + ParameterSource; + }" + ComponentsTestDeclarations.Source; VerifyCSharpDiagnostic(test); } @@ -67,29 +57,29 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test class TypeName { [Parameter] public string BadProperty1 { get; set; } - [Parameter] public object BadProperty2 { get; set; } + [CascadingParameter] public object BadProperty2 { get; set; } } - }" + ParameterSource; + }" + ComponentsTestDeclarations.Source; VerifyCSharpDiagnostic(test, new DiagnosticResult { - Id = "BL9993", - Message = "Component parameter 'BadProperty1' has a public setter, but component parameters should not be publicly settable.", + Id = DiagnosticDescriptors.ComponentParametersShouldNotBePublic.Id, + Message = "Component parameter 'ConsoleApplication1.TypeName.BadProperty1' has a public setter, but component parameters should not be publicly settable.", Severity = DiagnosticSeverity.Warning, Locations = new[] { - new DiagnosticResultLocation("Test0.cs", 8, 13) + new DiagnosticResultLocation("Test0.cs", 8, 39) } }, new DiagnosticResult { - Id = "BL9993", - Message = "Component parameter 'BadProperty2' has a public setter, but component parameters should not be publicly settable.", + Id = DiagnosticDescriptors.ComponentParametersShouldNotBePublic.Id, + Message = "Component parameter 'ConsoleApplication1.TypeName.BadProperty2' has a public setter, but component parameters should not be publicly settable.", Severity = DiagnosticSeverity.Warning, Locations = new[] { - new DiagnosticResultLocation("Test0.cs", 9, 13) + new DiagnosticResultLocation("Test0.cs", 9, 48) } }); @@ -101,9 +91,9 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test class TypeName { [Parameter] string BadProperty1 { get; set; } - [Parameter] object BadProperty2 { get; set; } + [CascadingParameter] object BadProperty2 { get; set; } } - }" + ParameterSource); + }" + ComponentsTestDeclarations.Source); } [Fact] @@ -120,7 +110,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test [Parameter] public object MyProperty2 { get; protected set; } [Parameter] public object MyProperty2 { get; internal set; } } - }" + ParameterSource; + }" + ComponentsTestDeclarations.Source; VerifyCSharpDiagnostic(test); } @@ -132,7 +122,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() { - return new ComponentParametersShouldNotBePublicAnalyzer(); + return new ComponentParameterAnalyzer(); } } } diff --git a/src/Components/Analyzers/test/ComponentsTestDeclarations.cs b/src/Components/Analyzers/test/ComponentsTestDeclarations.cs new file mode 100644 index 0000000000..e09f156ff4 --- /dev/null +++ b/src/Components/Analyzers/test/ComponentsTestDeclarations.cs @@ -0,0 +1,22 @@ +// 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 Microsoft.AspNetCore.Components.Analyzers +{ + public static class ComponentsTestDeclarations + { + public static readonly string Source = $@" + namespace {typeof(ParameterAttribute).Namespace} + {{ + public class {typeof(ParameterAttribute).Name} : System.Attribute + {{ + public bool CaptureUnmatchedValues {{ get; set; }} + }} + + public class {typeof(CascadingParameterAttribute).Name} : System.Attribute + {{ + }} + }} +"; + } +} diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs index a1b3d1e2f8..eecff40f30 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs @@ -373,6 +373,7 @@ namespace Microsoft.AspNetCore.Components public sealed partial class ParameterAttribute : System.Attribute { public ParameterAttribute() { } + public bool CaptureUnmatchedValues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct ParameterCollection @@ -769,6 +770,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree public void AddContent(int sequence, Microsoft.AspNetCore.Components.RenderFragment fragment, T value) { } public void AddElementReferenceCapture(int sequence, System.Action elementReferenceCaptureAction) { } public void AddMarkupContent(int sequence, string markupContent) { } + public void AddMultipleAttributes(int sequence, System.Collections.Generic.IEnumerable> attributes) { } public void Clear() { } public void CloseComponent() { } public void CloseElement() { } @@ -813,6 +815,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree } public enum RenderTreeFrameType { + None = 0, Element = 1, Text = 2, Attribute = 3, diff --git a/src/Components/Components/src/EventCallback.cs b/src/Components/Components/src/EventCallback.cs index ca3e3d6adc..5a396e306b 100644 --- a/src/Components/Components/src/EventCallback.cs +++ b/src/Components/Components/src/EventCallback.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Components /// /// A bound event handler delegate. /// - public readonly struct EventCallback + public readonly struct EventCallback : IEventCallback { /// /// Gets a reference to the . @@ -60,12 +60,17 @@ namespace Microsoft.AspNetCore.Components return Receiver.HandleEventAsync(new EventCallbackWorkItem(Delegate), arg); } + + object IEventCallback.UnpackForRenderTree() + { + return RequiresExplicitReceiver ? (object)this : Delegate; + } } /// /// A bound event handler delegate. /// - public readonly struct EventCallback + public readonly struct EventCallback : IEventCallback { internal readonly MulticastDelegate Delegate; internal readonly IHandleEvent Receiver; @@ -86,7 +91,7 @@ namespace Microsoft.AspNetCore.Components /// public bool HasDelegate => Delegate != null; - // This is a hint to the runtime that Reciever is a different object than what + // This is a hint to the runtime that Receiver is a different object than what // Delegate.Target points to. This allows us to avoid boxing the command object // when building the render tree. See logic where this is used. internal bool RequiresExplicitReceiver => Receiver != null && !object.ReferenceEquals(Receiver, Delegate?.Target); @@ -111,5 +116,18 @@ namespace Microsoft.AspNetCore.Components { return new EventCallback(Receiver ?? Delegate?.Target as IHandleEvent, Delegate); } + + object IEventCallback.UnpackForRenderTree() + { + return RequiresExplicitReceiver ? (object)AsUntyped() : Delegate; + } + } + + // Used to understand boxed generic EventCallbacks + internal interface IEventCallback + { + bool HasDelegate { get; } + + object UnpackForRenderTree(); } } diff --git a/src/Components/Components/src/ParameterAttribute.cs b/src/Components/Components/src/ParameterAttribute.cs index ffe39191bf..5fb5ec2088 100644 --- a/src/Components/Components/src/ParameterAttribute.cs +++ b/src/Components/Components/src/ParameterAttribute.cs @@ -2,6 +2,8 @@ // 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 Microsoft.AspNetCore.Components.RenderTree; namespace Microsoft.AspNetCore.Components { @@ -11,5 +13,24 @@ namespace Microsoft.AspNetCore.Components [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public sealed class ParameterAttribute : Attribute { + /// + /// Gets or sets a value that determines whether the parameter will capture values that + /// don't match any other parameter. + /// + /// + /// + /// allows a component to accept arbitrary additional + /// attributes, and pass them to another component, or some element of the underlying markup. + /// + /// + /// can be used on at most one parameter per component. + /// + /// + /// should only be applied to parameters of a type that + /// can be used with + /// such as . + /// + /// + public bool CaptureUnmatchedValues { get; set; } } } diff --git a/src/Components/Components/src/ParameterCollectionExtensions.cs b/src/Components/Components/src/ParameterCollectionExtensions.cs index 323662f403..fb23b5de49 100644 --- a/src/Components/Components/src/ParameterCollectionExtensions.cs +++ b/src/Components/Components/src/ParameterCollectionExtensions.cs @@ -1,11 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.Components.Reflection; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Components.Reflection; namespace Microsoft.AspNetCore.Components { @@ -41,17 +43,72 @@ namespace Microsoft.AspNetCore.Components _cachedWritersByType[targetType] = writers; } - foreach (var parameter in parameterCollection) + // The logic is split up for simplicity now that we have CaptureUnmatchedValues parameters. + if (writers.CaptureUnmatchedValuesWriter == null) { - var parameterName = parameter.Name; - if (!writers.WritersByName.TryGetValue(parameterName, out var writerWithIndex)) + // Logic for components without a CaptureUnmatchedValues parameter + foreach (var parameter in parameterCollection) { - ThrowForUnknownIncomingParameterName(targetType, parameterName); + var parameterName = parameter.Name; + if (!writers.WritersByName.TryGetValue(parameterName, out var writer)) + { + // Case 1: There is nowhere to put this value. + ThrowForUnknownIncomingParameterName(targetType, parameterName); + throw null; // Unreachable + } + + SetProperty(target, writer, parameterName, parameter.Value); + } + } + else + { + // Logic with components with a CaptureUnmatchedValues parameter + var isCaptureUnmatchedValuesParameterSetExplicitly = false; + Dictionary unmatched = null; + foreach (var parameter in parameterCollection) + { + var parameterName = parameter.Name; + if (string.Equals(parameterName, writers.CaptureUnmatchedValuesPropertyName, StringComparison.OrdinalIgnoreCase)) + { + isCaptureUnmatchedValuesParameterSetExplicitly = true; + } + + var isUnmatchedValue = !writers.WritersByName.TryGetValue(parameterName, out var writer); + if (isUnmatchedValue) + { + unmatched ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + unmatched[parameterName] = parameter.Value; + } + else + { + Debug.Assert(writer != null); + SetProperty(target, writer, parameterName, parameter.Value); + } } + if (unmatched != null && isCaptureUnmatchedValuesParameterSetExplicitly) + { + // This has to be an error because we want to allow users to set the CaptureUnmatchedValues + // parameter explicitly and .... + // 1. We don't ever want to mutate a value the user gives us. + // 2. We also don't want to implicitly copy a value the user gives us. + // + // Either one of those implementation choices would do something unexpected. + ThrowForCaptureUnmatchedValuesConflict(targetType, writers.CaptureUnmatchedValuesPropertyName, unmatched); + throw null; // Unreachable + } + else if (unmatched != null) + { + // We had some unmatched values, set the CaptureUnmatchedValues property + SetProperty(target, writers.CaptureUnmatchedValuesWriter, writers.CaptureUnmatchedValuesPropertyName, unmatched); + } + } + + static void SetProperty(object target, IPropertySetter writer, string parameterName, object value) + { try { - writerWithIndex.SetValue(target, parameter.Value); + writer.SetValue(target, value); } catch (Exception ex) { @@ -93,18 +150,49 @@ namespace Microsoft.AspNetCore.Components } } - class WritersForType + private static void ThrowForCaptureUnmatchedValuesConflict(Type targetType, string parameterName, Dictionary unmatched) { - public Dictionary WritersByName { get; } + throw new InvalidOperationException( + $"The property '{parameterName}' on component type '{targetType.FullName}' cannot be set explicitly " + + $"when also used to capture unmatched values. Unmatched values:" + Environment.NewLine + + string.Join(Environment.NewLine, unmatched.Keys.OrderBy(k => k))); + } + private static void ThrowForMultipleCaptureUnmatchedValuesParameters(Type targetType) + { + // We don't care about perf here, we want to report an accurate and useful error. + var propertyNames = targetType + .GetProperties(_bindablePropertyFlags) + .Where(p => p.GetCustomAttribute()?.CaptureUnmatchedValues == true) + .Select(p => p.Name) + .OrderBy(p => p) + .ToArray(); + + throw new InvalidOperationException( + $"Multiple properties were found on component type '{targetType.FullName}' with " + + $"'{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Only a single property " + + $"per type can use '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. Properties:" + Environment.NewLine + + string.Join(Environment.NewLine, propertyNames)); + } + + private static void ThrowForInvalidCaptureUnmatchedValuesParameterType(Type targetType, PropertyInfo propertyInfo) + { + throw new InvalidOperationException( + $"The property '{propertyInfo.Name}' on component type '{targetType.FullName}' cannot be used " + + $"with '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}' because it has the wrong type. " + + $"The property must be assignable from 'Dictionary'."); + } + + private class WritersForType + { public WritersForType(Type targetType) { WritersByName = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var propertyInfo in GetCandidateBindableProperties(targetType)) { - var shouldCreateWriter = propertyInfo.IsDefined(typeof(ParameterAttribute)) - || propertyInfo.IsDefined(typeof(CascadingParameterAttribute)); - if (!shouldCreateWriter) + var parameterAttribute = propertyInfo.GetCustomAttribute(); + var isParameter = parameterAttribute != null || propertyInfo.IsDefined(typeof(CascadingParameterAttribute)); + if (!isParameter) { continue; } @@ -120,8 +208,34 @@ namespace Microsoft.AspNetCore.Components } WritersByName.Add(propertyName, propertySetter); + + if (parameterAttribute != null && parameterAttribute.CaptureUnmatchedValues) + { + // This is an "Extra" parameter. + // + // There should only be one of these. + if (CaptureUnmatchedValuesWriter != null) + { + ThrowForMultipleCaptureUnmatchedValuesParameters(targetType); + } + + // It must be able to hold a Dictionary since that's what we create. + if (!propertyInfo.PropertyType.IsAssignableFrom(typeof(Dictionary))) + { + ThrowForInvalidCaptureUnmatchedValuesParameterType(targetType, propertyInfo); + } + + CaptureUnmatchedValuesWriter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo); + CaptureUnmatchedValuesPropertyName = propertyInfo.Name; + } } } + + public Dictionary WritersByName { get; } + + public IPropertySetter CaptureUnmatchedValuesWriter { get; } + + public string CaptureUnmatchedValuesPropertyName { get; } } } } diff --git a/src/Components/Components/src/RenderTree/ArrayBuilder.cs b/src/Components/Components/src/RenderTree/ArrayBuilder.cs index 918766aee9..6d38c95b32 100644 --- a/src/Components/Components/src/RenderTree/ArrayBuilder.cs +++ b/src/Components/Components/src/RenderTree/ArrayBuilder.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; diff --git a/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs index 78a174885b..79290f8e07 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; @@ -27,6 +28,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree private readonly ArrayBuilder _entries = new ArrayBuilder(10); private readonly Stack _openElementIndices = new Stack(); private RenderTreeFrameType? _lastNonAttributeFrameType; + private bool _hasSeenAddMultipleAttributes; + private Dictionary _seenAttributeNames; /// /// The reserved parameter name used for supplying child content. @@ -52,6 +55,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// A value representing the type of the element. public void OpenElement(int sequence, string elementName) { + // We are entering a new scope, since we track the "duplicate attributes" per + // element/component we might need to clean them up now. + if (_hasSeenAddMultipleAttributes) + { + var indexOfLastElementOrComponent = _openElementIndices.Peek(); + ProcessDuplicateAttributes(first: indexOfLastElementOrComponent + 1); + } + _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.Element(sequence, elementName)); } @@ -63,6 +74,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree public void CloseElement() { var indexOfEntryBeingClosed = _openElementIndices.Pop(); + + // We might be closing an element with only attributes, run the duplicate cleanup pass + // if necessary. + if (_hasSeenAddMultipleAttributes) + { + ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1); + } + ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed); } @@ -157,6 +176,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree // or absence of an attribute, and false => "False" which isn't falsy in js. Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); } + else + { + TrackAttributeName(name); + } } /// @@ -178,6 +201,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree { Append(RenderTreeFrame.Attribute(sequence, name, value)); } + else + { + TrackAttributeName(name); + } } /// @@ -275,6 +302,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree { Append(RenderTreeFrame.Attribute(sequence, name, value)); } + else + { + TrackAttributeName(name); + } } /// @@ -308,12 +339,17 @@ namespace Microsoft.AspNetCore.Components.RenderTree // so we can get it out on the other side. Append(RenderTreeFrame.Attribute(sequence, name, (object)value)); } - else + else if (value.HasDelegate) { // In the common case the receiver is also the delegate's target, so we // just need to retain the delegate. This allows us to avoid an allocation. Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate)); } + else + { + // Track the attribute name if needed since we elided the frame. + TrackAttributeName(name); + } } /// @@ -347,12 +383,17 @@ namespace Microsoft.AspNetCore.Components.RenderTree // need to preserve the type of an EventCallback when it's invoked from the DOM. Append(RenderTreeFrame.Attribute(sequence, name, (object)value.AsUntyped())); } - else + else if (value.HasDelegate) { // In the common case the receiver is also the delegate's target, so we // just need to retain the delegate. This allows us to avoid an allocation. Append(RenderTreeFrame.Attribute(sequence, name, value.Delegate)); } + else + { + // Track the attribute name if needed since we elided the frame. + TrackAttributeName(name); + } } /// @@ -372,7 +413,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree { if (value == null) { - // Do nothing, treat 'null' attribute values for elements as a conditional attribute. + // Treat 'null' attribute values for elements as a conditional attribute. + TrackAttributeName(name); } else if (value is bool boolValue) { @@ -380,8 +422,22 @@ namespace Microsoft.AspNetCore.Components.RenderTree { Append(RenderTreeFrame.Attribute(sequence, name, BoxedTrue)); } - - // Don't add anything for false bool value. + else + { + // Don't add anything for false bool value. + TrackAttributeName(name); + } + } + else if (value is IEventCallback callbackValue) + { + if (callbackValue.HasDelegate) + { + Append(RenderTreeFrame.Attribute(sequence, name, callbackValue.UnpackForRenderTree())); + } + else + { + TrackAttributeName(name); + } } else if (value is MulticastDelegate) { @@ -395,6 +451,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree } else if (_lastNonAttributeFrameType == RenderTreeFrameType.Component) { + // If this is a component, we always want to preserve the original type. Append(RenderTreeFrame.Attribute(sequence, name, value)); } else @@ -425,6 +482,40 @@ namespace Microsoft.AspNetCore.Components.RenderTree Append(frame.WithAttributeSequence(sequence)); } + /// + /// Adds frames representing multiple attributes with the same sequence number. + /// + /// The attribute value type. + /// An integer that represents the position of the instruction in the source code. + /// A collection of key-value pairs representing attributes. + public void AddMultipleAttributes(int sequence, IEnumerable> attributes) + { + // NOTE: The IEnumerable> is the simplest way to support a variety of + // different types like IReadOnlyDictionary<>, Dictionary<>, and IDictionary<>. + // + // None of those types are contravariant, and since we want to support attributes having a value + // of type object, the simplest thing to do is drop down to IEnumerable> which + // is contravariant. This also gives us things like List> and KeyValuePair<>[] + // for free even though we don't expect those types to be common. + + // Calling this up-front just to make sure we validate before mutating anything. + AssertCanAddAttribute(); + + if (attributes != null) + { + _hasSeenAddMultipleAttributes = true; + + foreach (var attribute in attributes) + { + // This will call the AddAttribute(int, string, object) overload. + // + // This is fine because we try to make the object overload behave identically + // to the others. + AddAttribute(sequence, attribute.Key, attribute.Value); + } + } + } + /// /// Appends a frame representing a child component. /// @@ -482,6 +573,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree private void OpenComponentUnchecked(int sequence, Type componentType) { + // We are entering a new scope, since we track the "duplicate attributes" per + // element/component we might need to clean them up now. + if (_hasSeenAddMultipleAttributes) + { + var indexOfLastElementOrComponent = _openElementIndices.Peek(); + ProcessDuplicateAttributes(first: indexOfLastElementOrComponent + 1); + } + _openElementIndices.Push(_entries.Count); Append(RenderTreeFrame.ChildComponent(sequence, componentType)); } @@ -493,6 +592,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree public void CloseComponent() { var indexOfEntryBeingClosed = _openElementIndices.Pop(); + + // We might be closing a component with only attributes. Run the attribute cleanup pass + // if necessary. + if (_hasSeenAddMultipleAttributes) + { + ProcessDuplicateAttributes(first: indexOfEntryBeingClosed + 1); + } + ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed]; entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed); } @@ -579,6 +686,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree _entries.Clear(); _openElementIndices.Clear(); _lastNonAttributeFrameType = null; + _hasSeenAddMultipleAttributes = false; + _seenAttributeNames?.Clear(); } /// @@ -590,13 +699,111 @@ namespace Microsoft.AspNetCore.Components.RenderTree private void Append(in RenderTreeFrame frame) { + var frameType = frame.FrameType; _entries.Append(frame); - var frameType = frame.FrameType; if (frameType != RenderTreeFrameType.Attribute) { _lastNonAttributeFrameType = frame.FrameType; } } + + // Internal for testing + internal void ProcessDuplicateAttributes(int first) + { + Debug.Assert(_hasSeenAddMultipleAttributes); + + // When AddMultipleAttributes method has been called, we need to postprocess attributes while closing + // the element/component. However, we also don't know the end index we should look at because it + // will contain nested content. + var buffer = _entries.Buffer; + var last = _entries.Count - 1; + + for (var i = first; i <= last; i++) + { + if (buffer[i].FrameType != RenderTreeFrameType.Attribute) + { + last = i - 1; + break; + } + } + + // Now that we've found the last attribute, we can iterate backwards and process duplicates. + var seenAttributeNames = (_seenAttributeNames ??= new Dictionary(StringComparer.OrdinalIgnoreCase)); + for (var i = last; i >= first; i--) + { + ref var frame = ref buffer[i]; + Debug.Assert(frame.FrameType == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameType} at {i}"); + + if (!seenAttributeNames.TryGetValue(frame.AttributeName, out var index)) + { + // This is the first time seeing this attribute name. Add to the dictionary and move on. + seenAttributeNames.Add(frame.AttributeName, i); + } + else if (index < i) + { + // This attribute is overriding a "silent frame" where we didn't create a frame for an AddAttribute call. + // This is the case for a null event handler, or bool false value. + // + // We need to update our tracking, in case the attribute appeared 3 or more times. + seenAttributeNames[frame.AttributeName] = i; + } + else if (index > i) + { + // This attribute has been overridden. For now, blank out its name to *mark* it. We'll do a pass + // later to wipe it out. + frame = default; + } + else + { + // OK so index == i. How is that possible? Well it's possible for a "silent frame" immediately + // followed by setting the same attribute. Think of it this way, when we create a "silent frame" + // we have to track that attribute name with *some* index. + // + // The only index value we can safely use is _entries.Count (next available). This is fine because + // we never use these indexes to look stuff up, only for comparison. + // + // That gets you here, and there's no action to take. + } + } + + // This is the pass where we cleanup attributes that have been wiped out. + // + // We copy the entries we're keeping into the earlier parts of the list (preserving order). + // + // Note that we iterate to the end of the list here, there might be additional frames after the attributes + // (ref) or content) that need to move to the left. + var offset = first; + for (var i = first; i < _entries.Count; i++) + { + ref var frame = ref buffer[i]; + if (frame.FrameType != RenderTreeFrameType.None) + { + buffer[offset++] = frame; + } + } + + // Clean up now unused space at the end of the list. + var residue = _entries.Count - offset; + for (var i = 0; i < residue; i++) + { + _entries.RemoveLast(); + } + + seenAttributeNames.Clear(); + _hasSeenAddMultipleAttributes = false; + } + + // Internal for testing + internal void TrackAttributeName(string name) + { + if (!_hasSeenAddMultipleAttributes) + { + return; + } + + var seenAttributeNames = (_seenAttributeNames ??= new Dictionary(StringComparer.OrdinalIgnoreCase)); + seenAttributeNames[name] = _entries.Count; // See comment in ProcessAttributes for why this is OK. + } } } diff --git a/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs b/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs index 799185e202..09630e3fcf 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs @@ -8,6 +8,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// public enum RenderTreeFrameType: int { + /// + /// Used only for unintialized frames. + /// + None = 0, + /// /// Represents a container for other frames. /// diff --git a/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs b/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs index 44c58dd4d6..7193081888 100644 --- a/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs +++ b/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs @@ -4,8 +4,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Test.Helpers; using Xunit; @@ -138,6 +138,142 @@ namespace Microsoft.AspNetCore.Components.Test ex.Message); } + [Fact] + public void SettingCaptureUnmatchedValuesParameterExplicitlyWorks() + { + // Arrange + var target = new HasCaptureUnmatchedValuesProperty(); + var value = new Dictionary(); + var parameterCollection = new ParameterCollectionBuilder + { + { nameof(HasCaptureUnmatchedValuesProperty.CaptureUnmatchedValues), value }, + }.Build(); + + // Act + parameterCollection.SetParameterProperties(target); + + // Assert + Assert.Same(value, target.CaptureUnmatchedValues); + } + + [Fact] + public void SettingCaptureUnmatchedValuesParameterWithUnmatchedValuesWorks() + { + // Arrange + var target = new HasCaptureUnmatchedValuesProperty(); + var parameterCollection = new ParameterCollectionBuilder + { + { nameof(HasCaptureUnmatchedValuesProperty.StringProp), "hi" }, + { "test1", 123 }, + { "test2", 456 }, + }.Build(); + + // Act + parameterCollection.SetParameterProperties(target); + + // Assert + Assert.Equal("hi", target.StringProp); + Assert.Collection( + target.CaptureUnmatchedValues.OrderBy(kvp => kvp.Key), + kvp => + { + Assert.Equal("test1", kvp.Key); + Assert.Equal(123, kvp.Value); + }, + kvp => + { + Assert.Equal("test2", kvp.Key); + Assert.Equal(456, kvp.Value); + }); + } + + [Fact] + public void SettingCaptureUnmatchedValuesParameterExplicitlyAndImplicitly_Throws() + { + // Arrange + var target = new HasCaptureUnmatchedValuesProperty(); + var parameterCollection = new ParameterCollectionBuilder + { + { nameof(HasCaptureUnmatchedValuesProperty.CaptureUnmatchedValues), new Dictionary() }, + { "test1", 123 }, + { "test2", 456 }, + }.Build(); + + // Act + var ex = Assert.Throws(() => parameterCollection.SetParameterProperties(target)); + + // Assert + Assert.Equal( + $"The property '{nameof(HasCaptureUnmatchedValuesProperty.CaptureUnmatchedValues)}' on component type '{typeof(HasCaptureUnmatchedValuesProperty).FullName}' cannot be set explicitly when " + + $"also used to capture unmatched values. Unmatched values:" + Environment.NewLine + + $"test1" + Environment.NewLine + + $"test2", + ex.Message); + } + + [Fact] + public void SettingCaptureUnmatchedValuesParameterExplicitlyAndImplicitly_ReverseOrder_Throws() + { + // Arrange + var target = new HasCaptureUnmatchedValuesProperty(); + var parameterCollection = new ParameterCollectionBuilder + { + { "test2", 456 }, + { "test1", 123 }, + { nameof(HasCaptureUnmatchedValuesProperty.CaptureUnmatchedValues), new Dictionary() }, + }.Build(); + + // Act + var ex = Assert.Throws(() => parameterCollection.SetParameterProperties(target)); + + // Assert + Assert.Equal( + $"The property '{nameof(HasCaptureUnmatchedValuesProperty.CaptureUnmatchedValues)}' on component type '{typeof(HasCaptureUnmatchedValuesProperty).FullName}' cannot be set explicitly when " + + $"also used to capture unmatched values. Unmatched values:" + Environment.NewLine + + $"test1" + Environment.NewLine + + $"test2", + ex.Message); + } + + [Fact] + public void HasDuplicateCaptureUnmatchedValuesParameters_Throws() + { + // Arrange + var target = new HasDupliateCaptureUnmatchedValuesProperty(); + var parameterCollection = new ParameterCollectionBuilder().Build(); + + // Act + var ex = Assert.Throws(() => parameterCollection.SetParameterProperties(target)); + + // Assert + Assert.Equal( + $"Multiple properties were found on component type '{typeof(HasDupliateCaptureUnmatchedValuesProperty).FullName}' " + + $"with '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. " + + $"Only a single property per type can use '{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}'. " + + $"Properties:" + Environment.NewLine + + $"{nameof(HasDupliateCaptureUnmatchedValuesProperty.CaptureUnmatchedValuesProp1)}" + Environment.NewLine + + $"{nameof(HasDupliateCaptureUnmatchedValuesProperty.CaptureUnmatchedValuesProp2)}", + ex.Message); + } + + [Fact] + public void HasCaptureUnmatchedValuesParameteterWithWrongType_Throws() + { + // Arrange + var target = new HasWrongTypeCaptureUnmatchedValuesProperty(); + var parameterCollection = new ParameterCollectionBuilder().Build(); + + // Act + var ex = Assert.Throws(() => parameterCollection.SetParameterProperties(target)); + + // Assert + Assert.Equal( + $"The property '{nameof(HasWrongTypeCaptureUnmatchedValuesProperty.CaptureUnmatchedValuesProp)}' on component type '{typeof(HasWrongTypeCaptureUnmatchedValuesProperty).FullName}' cannot be used with " + + $"'{nameof(ParameterAttribute)}.{nameof(ParameterAttribute.CaptureUnmatchedValues)}' because it has the wrong type. " + + $"The property must be assignable from 'Dictionary'.", + ex.Message); + } + [Fact] public void IncomingParameterValueMismatchesDeclaredParameterType_Throws() { @@ -273,6 +409,25 @@ namespace Microsoft.AspNetCore.Components.Test [Parameter] new int IntProp { get; set; } } + class HasCaptureUnmatchedValuesProperty + { + [Parameter] internal int IntProp { get; set; } + [Parameter] internal string StringProp { get; set; } + [Parameter] internal object ObjectProp { get; set; } + [Parameter(CaptureUnmatchedValues = true)] internal IReadOnlyDictionary CaptureUnmatchedValues { get; set; } + } + + class HasDupliateCaptureUnmatchedValuesProperty + { + [Parameter(CaptureUnmatchedValues = true)] internal Dictionary CaptureUnmatchedValuesProp1 { get; set; } + [Parameter(CaptureUnmatchedValues = true)] internal IDictionary CaptureUnmatchedValuesProp2 { get; set; } + } + + class HasWrongTypeCaptureUnmatchedValuesProperty + { + [Parameter(CaptureUnmatchedValues = true)] internal KeyValuePair[] CaptureUnmatchedValuesProp { get; set; } + } + class ParameterCollectionBuilder : IEnumerable { private readonly List<(string Name, object Value)> _keyValuePairs diff --git a/src/Components/Components/test/RenderTreeBuilderTest.cs b/src/Components/Components/test/RenderTreeBuilderTest.cs index ac05e9b195..c92ee63658 100644 --- a/src/Components/Components/test/RenderTreeBuilderTest.cs +++ b/src/Components/Components/test/RenderTreeBuilderTest.cs @@ -1,13 +1,14 @@ // 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.Components; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.AspNetCore.Components.Test.Helpers; -using System; -using System.Linq; -using System.Threading.Tasks; +using Moq; using Xunit; namespace Microsoft.AspNetCore.Components.Test @@ -245,6 +246,200 @@ namespace Microsoft.AspNetCore.Components.Test frame => AssertFrame.Text(frame, "some text")); } + [Fact] + public void CanAddMultipleAttributes_AllowsNull() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + + // Act + builder.OpenElement(0, "myelement"); + builder.AddMultipleAttributes(0, null); + builder.CloseElement(); + + // Assert + var frames = builder.GetFrames().AsEnumerable().ToArray(); + Assert.Collection( + frames, + frame => AssertFrame.Element(frame, "myelement", 1)); + } + + [Fact] + public void CanAddMultipleAttributes_InterspersedWithOtherAttributes() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + Action eventHandler = eventInfo => { }; + + // Act + builder.OpenElement(0, "myelement"); + builder.AddAttribute(0, "attribute1", "value 1"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "attribute1", "test1" }, + { "attribute2", true }, + { "attribute3", eventHandler }, + }); + builder.AddAttribute(0, "ATTRIBUTE2", true); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "attribute4", "test4" }, + { "attribute5", false }, + { "attribute6", eventHandler }, + }); + + // Null or false values don't create frames of their own, but they can + // "knock out" earlier values. + builder.AddAttribute(0, "attribute6", false); + builder.AddAttribute(0, "attribute4", (string)null); + + builder.AddAttribute(0, "attribute7", "the end"); + builder.CloseElement(); + + // Assert + var frames = builder.GetFrames().AsEnumerable().ToArray(); + Assert.Collection( + frames, + frame => AssertFrame.Element(frame, "myelement", 5), + frame => AssertFrame.Attribute(frame, "attribute1", "test1"), + frame => AssertFrame.Attribute(frame, "attribute3", eventHandler), + frame => AssertFrame.Attribute(frame, "ATTRIBUTE2", true), + frame => AssertFrame.Attribute(frame, "attribute7", "the end")); + } + + [Fact] + public void CanAddMultipleAttributes_DictionaryString() + { + var attributes = new Dictionary + { + { "attribute1", "test1" }, + { "attribute2", "123" }, + { "attribute3", "456" }, + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + [Fact] + public void CanAddMultipleAttributes_DictionaryObject() + { + var attributes = new Dictionary + { + { "attribute1", "test1" }, + { "attribute2", "123" }, + { "attribute3", true }, + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + [Fact] + public void CanAddMultipleAttributes_IReadOnlyDictionaryString() + { + var attributes = new Dictionary + { + { "attribute1", "test1" }, + { "attribute2", "123" }, + { "attribute3", "456" }, + }; + + // Act & Assert + CanAddMultipleAttributesTest((IReadOnlyDictionary)attributes); + } + + [Fact] + public void CanAddMultipleAttributes_IReadOnlyDictionaryObject() + { + var attributes = new Dictionary + { + { "attribute1", "test1" }, + { "attribute2", "123" }, + { "attribute3", true }, + }; + + // Act & Assert + CanAddMultipleAttributesTest((IReadOnlyDictionary)attributes); + } + + [Fact] + public void CanAddMultipleAttributes_ListKvpString() + { + var attributes = new List>() + { + new KeyValuePair("attribute1", "test1"), + new KeyValuePair("attribute2", "123"), + new KeyValuePair("attribute3", "456"), + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + [Fact] + public void CanAddMultipleAttributes_ListKvpObject() + { + var attributes = new List>() + { + new KeyValuePair("attribute1", "test1"), + new KeyValuePair("attribute2", "123"), + new KeyValuePair("attribute3", true), + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + [Fact] + public void CanAddMultipleAttributes_ArrayKvpString() + { + var attributes = new KeyValuePair[] + { + new KeyValuePair("attribute1", "test1"), + new KeyValuePair("attribute2", "123"), + new KeyValuePair("attribute3", "456"), + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + [Fact] + public void CanAddMultipleAttributes_ArrayKvpObject() + { + var attributes = new KeyValuePair[] + { + new KeyValuePair("attribute1", "test1"), + new KeyValuePair("attribute2", "123"), + new KeyValuePair("attribute3", true), + }; + + // Act & Assert + CanAddMultipleAttributesTest(attributes); + } + + private void CanAddMultipleAttributesTest(IEnumerable> attributes) + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + + // Act + builder.OpenElement(0, "myelement"); + builder.AddMultipleAttributes(0, attributes); + builder.CloseElement(); + + // Assert + var frames = builder.GetFrames().AsEnumerable().ToArray(); + + var i = 1; + foreach (var attribute in attributes) + { + var frame = frames[i++]; + AssertFrame.Attribute(frame, attribute.Key, attribute.Value); + } + } + [Fact] public void CannotAddAttributeAtRoot() { @@ -859,6 +1054,160 @@ namespace Microsoft.AspNetCore.Components.Test frame => AssertFrame.Attribute(frame, "attr", value, 1)); } + [Fact] + public void AddAttribute_Element_EventCallback_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = new EventCallback(null, new Action(() => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback.Delegate, 1)); + } + + [Fact] + public void AddAttribute_Element_EventCallback_Default_DoesNotAddFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = default(EventCallback); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 1, 0)); + } + + [Fact] + public void AddAttribute_Element_EventCallbackWithReceiver_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action(() => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback, 1)); + } + + [Fact] + public void AddAttribute_Component_EventCallback_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action(() => { })); + + // Act + builder.OpenComponent(0); + builder.AddAttribute(1, "attr", callback); + builder.CloseComponent(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Component(frame, 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback, 1)); + } + + [Fact] + public void AddAttribute_Element_EventCallbackOfT_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = new EventCallback(null, new Action((s) => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback.Delegate, 1)); + } + + [Fact] + public void AddAttribute_Element_EventCallbackOfT_Default_DoesNotAddFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = default(EventCallback); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 1, 0)); + } + + [Fact] + public void AddAttribute_Element_EventCallbackWithReceiverOfT_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action((s) => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", new EventCallback(callback.Receiver, callback.Delegate), 1)); + } + + [Fact] + public void AddAttribute_Component_EventCallbackOfT_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action((s) => { })); + + // Act + builder.OpenComponent(0); + builder.AddAttribute(1, "attr", callback); + builder.CloseComponent(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Component(frame, 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback, 1)); + } + [Fact] public void AddAttribute_Element_ObjectBoolTrue_AddsFrame() { @@ -1030,6 +1379,140 @@ namespace Microsoft.AspNetCore.Components.Test frame => AssertFrame.Attribute(frame, "attr", value, 1)); } + [Fact] + public void AddAttribute_Element_ObjectEventCallback_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = new EventCallback(null, new Action(() => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback.Delegate, 1)); + } + + [Fact] + public void AddAttribute_Element_ObjectEventCallback_Default_DoesNotAddFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = default(EventCallback); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 1, 0)); + } + + [Fact] + public void AddAttribute_Element_ObjectEventCallbackWithReceiver_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action(() => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback, 1)); + } + + [Fact] + public void AddAttribute_Component_ObjectEventCallback_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action(() => { })); + + // Act + builder.OpenComponent(0); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseComponent(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Component(frame, 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback, 1)); + } + + [Fact] + public void AddAttribute_Element_ObjectEventCallbackOfT_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = new EventCallback(null, new Action((s) => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", callback.Delegate, 1)); + } + + [Fact] + public void AddAttribute_Element_ObjectEventCallbackOfT_Default_DoesNotAddFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var callback = default(EventCallback); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 1, 0)); + } + + [Fact] + public void AddAttribute_Element_ObjectEventCallbackWithReceiverOfT_AddsFrame() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + var receiver = Mock.Of(); + var callback = new EventCallback(receiver, new Action((s) => { })); + + // Act + builder.OpenElement(0, "elem"); + builder.AddAttribute(1, "attr", (object)callback); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => AssertFrame.Element(frame, "elem", 2, 0), + frame => AssertFrame.Attribute(frame, "attr", new EventCallback(callback.Receiver, callback.Delegate), 1)); + } + [Fact] public void AddAttribute_Element_ObjectNull_IgnoresFrame() { @@ -1148,6 +1631,215 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Equal("value", ex.ParamName); } + [Fact] + public void ProcessDuplicateAttributes_DoesNotRemoveDuplicatesWithoutAddMultipleAttributes() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "id", "hi"); + builder.AddAttribute(0, "id", "bye"); + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 3, 0), + f => AssertFrame.Attribute(f, "id", "hi"), + f => AssertFrame.Attribute(f, "id", "bye")); + } + + + [Fact] + public void ProcessDuplicateAttributes_StopsAtFirstNonAttributeFrame_Capture() + { + // Arrange + var capture = (Action)((_) => { }); + + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "id", "hi"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.AddElementReferenceCapture(0, capture); + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 3, 0), + f => AssertFrame.Attribute(f, "id", "bye"), + f => AssertFrame.ElementReferenceCapture(f, capture)); + } + + [Fact] + public void ProcessDuplicateAttributes_StopsAtFirstNonAttributeFrame_Content() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "id", "hi"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.AddContent(0, "hey"); + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 3, 0), + f => AssertFrame.Attribute(f, "id", "bye"), + f => AssertFrame.Text(f, "hey")); + } + + [Fact] + public void ProcessDuplicateAttributes_CanRemoveDuplicateInsideElement() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "id", "hi"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 2, 0), + f => AssertFrame.Attribute(f, "id", "bye")); + } + + [Fact] + public void ProcessDuplicateAttributes_CanRemoveDuplicateInsideComponent() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenComponent(0); + builder.AddAttribute(0, "id", "hi"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.CloseComponent(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Component(f, 2, 0), + f => AssertFrame.Attribute(f, "id", "bye")); + } + + // This covers a special case we have to handle explicitly in the RTB logic. + [Fact] + public void ProcessDuplicateAttributes_SilentFrameFollowedBySameAttribute() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenComponent(0); + builder.AddAttribute(0, "id", (string)null); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.CloseComponent(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Component(f, 2, 0), + f => AssertFrame.Attribute(f, "id", "bye")); + } + + [Fact] + public void ProcessDuplicateAttributes_DoesNotRemoveDuplicatesInsideChildElement() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "id", "hi"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "id", "bye" }, + }); + builder.OpenElement(0, "strong"); + builder.AddAttribute(0, "id", "hi"); + builder.AddAttribute(0, "id", "bye"); + builder.CloseElement(); + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 5, 0), + f => AssertFrame.Attribute(f, "id", "bye"), + f => AssertFrame.Element(f, "strong", 3), + f => AssertFrame.Attribute(f, "id", "hi"), + f => AssertFrame.Attribute(f, "id", "bye")); + } + + [Fact] + public void ProcessDuplicateAttributes_CanRemoveOverwrittenAttributes() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + builder.OpenElement(0, "div"); + builder.AddAttribute(0, "A", "hi"); + builder.AddAttribute(0, "2", new EventCallback(null, (Action)(() => { }))); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "a", null }, // Replace with null value (case-insensitive) + { "2", false }, // Replace with 'false' + { "3", "hey there" }, // Add a new value + }); + builder.AddAttribute(0, "3", "see ya"); // Overwrite value added by splat + builder.AddAttribute(0, "4", false); // Add a false value + builder.AddAttribute(0, "5", "another one"); + builder.AddMultipleAttributes(0, new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "5", null }, // overwrite value with null + { "6", new EventCallback(null, (Action)(() =>{ })) }, + }); + builder.AddAttribute(0, "6", default(EventCallback)); // Replace with a 'silent' EventCallback + builder.CloseElement(); + + // Act + var frames = builder.GetFrames().AsEnumerable(); + + // Assert + Assert.Collection( + frames, + f => AssertFrame.Element(f, "div", 2, 0), + f => AssertFrame.Attribute(f, "3", "see ya")); + } + private class TestComponent : IComponent { public void Configure(RenderHandle renderHandle) { } diff --git a/src/Components/Components/test/RendererTest.cs b/src/Components/Components/test/RendererTest.cs index 8c8d9b1d49..2c3e110e41 100644 --- a/src/Components/Components/test/RendererTest.cs +++ b/src/Components/Components/test/RendererTest.cs @@ -1900,6 +1900,76 @@ namespace Microsoft.AspNetCore.Components.Test AssertFrame.Text(renderer.Batches[1].ReferenceFrames[0], "second"); } + [Fact] + public void ReRendersChildComponentWhenUnmatchedValuesChange() + { + // Arrange: First render + var renderer = new TestRenderer(); + var firstRender = true; + var component = new TestComponent(builder => + { + builder.OpenComponent(1); + builder.AddAttribute(1, "class", firstRender ? "first" : "second"); + builder.AddAttribute(2, "id", "some_text"); + builder.AddAttribute(3, nameof(MyStrongComponent.Text), "hi there."); + builder.CloseComponent(); + }); + + var rootComponentId = renderer.AssignRootComponentId(component); + component.TriggerRender(); + + var childComponentId = renderer.Batches.Single() + .ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component) + .ComponentId; + + // Act: Second render + firstRender = false; + component.TriggerRender(); + var diff = renderer.Batches[1].DiffsByComponentId[childComponentId].Single(); + + // Assert + Assert.Collection(diff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.SetAttribute, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + AssertFrame.Attribute(renderer.Batches[1].ReferenceFrames[0], "class", "second"); + } + + // This is a sanity check that diffs of "unmatched" values *just work* without any specialized + // code in the renderer to handle it. All of the data that's used in the diff is contained in + // the render tree, and the diff process does not need to inspect the state of the component. + [Fact] + public void ReRendersDoesNotReRenderChildComponentWhenUnmatchedValuesDoNotChange() + { + // Arrange: First render + var renderer = new TestRenderer(); + var component = new TestComponent(builder => + { + builder.OpenComponent(1); + builder.AddAttribute(1, "class", "cool-beans"); + builder.AddAttribute(2, "id", "some_text"); + builder.AddAttribute(3, nameof(MyStrongComponent.Text), "hi there."); + builder.CloseComponent(); + }); + + var rootComponentId = renderer.AssignRootComponentId(component); + component.TriggerRender(); + + var childComponentId = renderer.Batches.Single() + .ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component) + .ComponentId; + + // Act: Second render + component.TriggerRender(); + + // Assert + Assert.False(renderer.Batches[1].DiffsByComponentId.ContainsKey(childComponentId)); + } + [Fact] public void RenderBatchIncludesListOfDisposedComponents() { @@ -3270,6 +3340,21 @@ namespace Microsoft.AspNetCore.Components.Test } } + private class MyStrongComponent : AutoRenderComponent + { + [Parameter(CaptureUnmatchedValues = true)] internal IDictionary Attributes { get; set; } + + [Parameter] internal string Text { get; set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "strong"); + builder.AddMultipleAttributes(1, Attributes); + builder.AddContent(2, Text); + builder.CloseElement(); + } + } + private class FakeComponent : IComponent { [Parameter] diff --git a/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs b/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs index 8d4ba09a46..fbf5a8f9bf 100644 --- a/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs +++ b/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs @@ -125,6 +125,40 @@ namespace Microsoft.AspNetCore.Components.Rendering Assert.Equal(expectedHtml, result); } + [Fact] + public void RenderComponentAsync_SkipsDuplicatedAttribute() + { + // Arrange + var expectedHtml = new[] + { + "<", "p", " ", + "another", "=", "\"", "another-value", "\"", " ", + "Class", "=", "\"", "test2", "\"", ">", + "Hello world!", + "" + }; + var serviceProvider = new ServiceCollection().AddSingleton(new RenderFragment(rtb => + { + rtb.OpenElement(0, "p"); + rtb.AddAttribute(1, "class", "test1"); + rtb.AddAttribute(2, "another", "another-value"); + rtb.AddMultipleAttributes(3, new Dictionary() + { + { "Class", "test2" }, // Matching is case-insensitive. + }); + rtb.AddContent(4, "Hello world!"); + rtb.CloseElement(); + })).BuildServiceProvider(); + + var htmlRenderer = GetHtmlRenderer(serviceProvider); + + // Act + var result = GetResult(Dispatcher.InvokeAsync(() => htmlRenderer.RenderComponentAsync(ParameterCollection.Empty))); + + // Assert + Assert.Equal(expectedHtml, result); + } + [Fact] public void RenderComponentAsync_HtmlEncodesAttributeValues() { diff --git a/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs b/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs index c88d2997c6..59d6c73211 100644 --- a/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs +++ b/src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs @@ -597,6 +597,26 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests Browser.Equal("Hello from interop call", () => appElement.FindElement(By.Id("val-set-by-interop")).GetAttribute("value")); } + [Fact] + public void CanUseAddMultipleAttributes() + { + var appElement = MountTestComponent(); + + var selector = By.CssSelector("#duplicate-on-element > div"); + WaitUntilExists(selector); + + var element = appElement.FindElement(selector); + Assert.Equal(string.Empty, element.GetAttribute("bool")); // attribute is present + Assert.Equal("middle-value", element.GetAttribute("string")); + Assert.Equal("unmatched-value", element.GetAttribute("unmatched")); + + selector = By.CssSelector("#duplicate-on-element-override > div"); + element = appElement.FindElement(selector); + Assert.Null(element.GetAttribute("bool")); // attribute is not present + Assert.Equal("other-text", element.GetAttribute("string")); + Assert.Equal("unmatched-value", element.GetAttribute("unmatched")); + } + static IAlert SwitchToAlert(IWebDriver driver) { try diff --git a/src/Components/test/E2ETest/Tests/EventCallbackTest.cs b/src/Components/test/E2ETest/Tests/EventCallbackTest.cs index 01d53d6b20..2ab5bebe05 100644 --- a/src/Components/test/E2ETest/Tests/EventCallbackTest.cs +++ b/src/Components/test/E2ETest/Tests/EventCallbackTest.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading.Tasks; using BasicTestApp; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; @@ -40,9 +39,9 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests { var target = Browser.FindElement(By.CssSelector($"#{@case} button")); var count = Browser.FindElement(By.Id("render_count")); - Assert.Equal("Render Count: 1", count.Text); + Browser.Equal("Render Count: 1", () => count.Text); target.Click(); - Assert.Equal("Render Count: 2", count.Text); + Browser.Equal("Render Count: 2", () => count.Text); } } } diff --git a/src/Components/test/testassets/BasicTestApp/DuplicateAttributesComponent.razor b/src/Components/test/testassets/BasicTestApp/DuplicateAttributesComponent.razor new file mode 100644 index 0000000000..6993a5e400 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/DuplicateAttributesComponent.razor @@ -0,0 +1,28 @@ +
+ +
+ +
+ +
+ +@functions { + void SomeMethod() + { + } + + Dictionary elementValues = new Dictionary() + { + { "bool", true }, + { "string", "middle-value" }, + { "unmatched", "unmatched-value" }, + }; +} diff --git a/src/Components/test/testassets/BasicTestApp/DuplicateAttributesOnElementChildComponent.cs b/src/Components/test/testassets/BasicTestApp/DuplicateAttributesOnElementChildComponent.cs new file mode 100644 index 0000000000..055ecf7149 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/DuplicateAttributesOnElementChildComponent.cs @@ -0,0 +1,37 @@ +// 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.Components; +using Microsoft.AspNetCore.Components.RenderTree; + +namespace BasicTestApp +{ + // Written in C# for flexibility and because we don't currently have the ability to write this in .razor. + public class DuplicateAttributesOnElementChildComponent : ComponentBase + { + [Parameter] public string StringAttributeBefore { get; private set; } + [Parameter] public bool BoolAttributeBefore { get; private set; } + [Parameter] public string StringAttributeAfter { get; private set; } + [Parameter] public bool? BoolAttributeAfter { get; private set; } + + [Parameter(CaptureUnmatchedValues = true)] public Dictionary UnmatchedValues { get; private set; } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "div"); + builder.AddAttribute(1, "string", StringAttributeBefore); + builder.AddAttribute(2, "bool", BoolAttributeBefore); + builder.AddMultipleAttributes(3, UnmatchedValues); + if (StringAttributeAfter != null) + { + builder.AddAttribute(4, "string", StringAttributeAfter); + } + if (BoolAttributeAfter != null) + { + builder.AddAttribute(5, "bool", BoolAttributeAfter); + } + builder.CloseElement(); + } + } +} diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index 7570d92e99..c3f4468cde 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -56,6 +56,7 @@ + @if (SelectedComponentType != null) From f1ff37efdb2c08cc779b5465c7046453f6b8a17c Mon Sep 17 00:00:00 2001 From: Kevin Pilch Date: Tue, 28 May 2019 17:24:11 -0700 Subject: [PATCH 32/95] Clean up some stale comments (#10561) Addresses AzDO Bug #890301 (https://dev.azure.com/devdiv/DevDiv/_workitems/edit/890301/) --- .../Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs | 1 - .../Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs | 4 ++-- .../AspNetCoreModule-Setup/IIS-Setup/iisca/lib/msiutil.cpp | 4 ++-- src/MusicStore/samples/MusicStore/Models/Order.cs | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs index 6ff776bffe..d72c540f11 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs @@ -247,7 +247,6 @@ namespace WsProxy { } //step one, figure out where did we hit - //lol no, fuck it, let's use fake data var res_value = res.Value? ["result"]? ["value"]; if (res_value == null || res_value is JValue) { //Give up and send the original call stack diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs index e575e0a244..17c72e9ce7 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs @@ -71,7 +71,7 @@ namespace WsProxy { pending.Add (bytes); if (pending.Count == 1) { if (current_send != null) - throw new Exception ("WTF, current_send MUST BE NULL IF THERE'S no pending send"); + throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send"); //Console.WriteLine ("sending {0} bytes", bytes.Length); current_send = Ws.SendAsync (new ArraySegment (bytes), WebSocketMessageType.Text, true, token); return current_send; @@ -86,7 +86,7 @@ namespace WsProxy { if (pending.Count > 0) { if (current_send != null) - throw new Exception ("WTF, current_send MUST BE NULL IF THERE'S no pending send"); + throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send"); //Console.WriteLine ("sending more {0} bytes", pending[0].Length); current_send = Ws.SendAsync (new ArraySegment (pending [0]), WebSocketMessageType.Text, true, token); return current_send; diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/msiutil.cpp b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/msiutil.cpp index 2a9b65ab37..a25dd4425b 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/msiutil.cpp +++ b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/msiutil.cpp @@ -326,7 +326,7 @@ exit: WCHAR CA_DATA_DELIM[] = { '^', 0 }; // -// BUGBUG - Prefix will barf on this +// BUGBUG - Prefix will not handle this // Can I really trust this data hasn't been tampered with? // WCHAR * @@ -578,4 +578,4 @@ exit: IISLogWrite(SETUP_LOG_SEVERITY_INFORMATION, L"Error in function %s, hr=0x%x", UNITEXT(__FUNCTION__), hr); } return hr; -} \ No newline at end of file +} diff --git a/src/MusicStore/samples/MusicStore/Models/Order.cs b/src/MusicStore/samples/MusicStore/Models/Order.cs index 3406b963a8..f1886ff0ae 100644 --- a/src/MusicStore/samples/MusicStore/Models/Order.cs +++ b/src/MusicStore/samples/MusicStore/Models/Order.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; namespace MusicStore.Models { - //[Bind(Include = "FirstName,LastName,Address,City,State,PostalCode,Country,Phone,Email")] public class Order { [BindNever] @@ -71,4 +70,4 @@ namespace MusicStore.Models [BindNever] public List OrderDetails { get; set; } } -} \ No newline at end of file +} From 2e8ae438633d5b0008bf531e9fe3291257990e95 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 28 May 2019 23:35:42 -0700 Subject: [PATCH 33/95] React to breaking change in dotnet/arcade#2753 (#10600) --- eng/targets/ReferenceAssembly.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/targets/ReferenceAssembly.targets b/eng/targets/ReferenceAssembly.targets index cbe21a8976..605c715eb7 100644 --- a/eng/targets/ReferenceAssembly.targets +++ b/eng/targets/ReferenceAssembly.targets @@ -55,7 +55,7 @@ - <_GenAPICommand Condition="'$(MSBuildRuntimeType)' == 'core'">$(ToolHostCmd) --roll-forward-on-no-candidate-fx 2 "$(_GenAPIPath)" + <_GenAPICommand Condition="'$(MSBuildRuntimeType)' == 'core'">"$(DotNetTool)" --roll-forward-on-no-candidate-fx 2 "$(_GenAPIPath)" <_GenAPICmd>$(_GenAPICommand) <_GenAPICmd>$(_GenAPICmd) "$(TargetPath)" <_GenAPICmd>$(_GenAPICmd) --lib-path "@(_ReferencePathDirectories)" From 760df198c8494001f8fa860d0b57f6c6a49c06eb Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Wed, 29 May 2019 09:58:20 -0700 Subject: [PATCH 34/95] Add inproc handler to shared framework (#10586) --- .azure/pipelines/ci.yml | 4 ++-- build/repo.props | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index 99b2bd9d6c..00d95c3a1d 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -50,14 +50,14 @@ jobs: # if they have already been signed. This results in slower builds due to re-submitting the same .nupkg many times for signing. # The sign settings have been configured to - - script: ./eng/scripts/cibuild.cmd -arch x64 /p:DisableCodeSigning=true /bl:artifacts/log/build.x64.binlog + - script: ./eng/scripts/cibuild.cmd -BuildNative -arch x64 /p:DisableCodeSigning=true /bl:artifacts/log/build.x64.binlog displayName: Build x64 # TODO: make it possible to build for one Windows architecture at a time # This is going to actually build x86 native assets. See https://github.com/aspnet/AspNetCore/issues/7196 # Build the x86 shared framework # Set DisableSignCheck because we'll run sign check in an explicit step after installers build - - script: ./eng/scripts/cibuild.cmd -arch x86 -NoRestore /t:BuildSharedFx /p:DisableCodeSigning=true /bl:artifacts/log/build.x86.binlog + - script: ./eng/scripts/cibuild.cmd -arch x86 -NoRestore -BuildNative /t:BuildSharedFx /p:DisableCodeSigning=true /bl:artifacts/log/build.x86.binlog displayName: Build x86 # This is in a separate build step with -forceCoreMsbuild to workaround MAX_PATH limitations - https://github.com/Microsoft/msbuild/issues/53 diff --git a/build/repo.props b/build/repo.props index 16748ee70a..681ba52b0a 100644 --- a/build/repo.props +++ b/build/repo.props @@ -3,6 +3,13 @@ $(TargetOsName)-$(TargetArchitecture) + + true + true + true + true + + true @@ -27,13 +34,6 @@ true $(RepoRoot)src\Shared\ - - - - true - true - true - true true From e16aa29e676a27877b0fbcf6224ef977bba356dc Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Wed, 29 May 2019 12:47:17 -0700 Subject: [PATCH 35/95] Make new HttpContext properties virtual (#10613) --- .../Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs | 6 +++--- src/Http/Http.Abstractions/src/HttpRequest.cs | 3 ++- src/Http/Http.Abstractions/src/HttpResponse.cs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs index cae69dd66c..9dd9e01997 100644 --- a/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs +++ b/src/Http/Http.Abstractions/ref/Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs @@ -250,7 +250,7 @@ namespace Microsoft.AspNetCore.Http { protected HttpRequest() { } public abstract System.IO.Stream Body { get; set; } - public abstract System.IO.Pipelines.PipeReader BodyReader { get; set; } + public virtual System.IO.Pipelines.PipeReader BodyReader { get { throw null; } set { } } public abstract long? ContentLength { get; set; } public abstract string ContentType { get; set; } public abstract Microsoft.AspNetCore.Http.IRequestCookieCollection Cookies { get; set; } @@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Http { protected HttpResponse() { } public abstract System.IO.Stream Body { get; set; } - public abstract System.IO.Pipelines.PipeWriter BodyWriter { get; set; } + public virtual System.IO.Pipelines.PipeWriter BodyWriter { get { throw null; } set { } } public abstract long? ContentLength { get; set; } public abstract string ContentType { get; set; } public abstract Microsoft.AspNetCore.Http.IResponseCookies Cookies { get; } @@ -290,7 +290,7 @@ namespace Microsoft.AspNetCore.Http public abstract void Redirect(string location, bool permanent); public virtual void RegisterForDispose(System.IDisposable disposable) { } public virtual void RegisterForDisposeAsync(System.IAsyncDisposable disposable) { } - public abstract System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public virtual System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public static partial class HttpResponseWritingExtensions { diff --git a/src/Http/Http.Abstractions/src/HttpRequest.cs b/src/Http/Http.Abstractions/src/HttpRequest.cs index 1163cbf52a..a63438450b 100644 --- a/src/Http/Http.Abstractions/src/HttpRequest.cs +++ b/src/Http/Http.Abstractions/src/HttpRequest.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; using System.IO; using System.IO.Pipelines; using System.Threading; @@ -106,7 +107,7 @@ namespace Microsoft.AspNetCore.Http /// /// Gets or sets the request body pipe . /// - public abstract PipeReader BodyReader { get; set; } + public virtual PipeReader BodyReader { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } /// /// Checks the Content-Type header for form types. diff --git a/src/Http/Http.Abstractions/src/HttpResponse.cs b/src/Http/Http.Abstractions/src/HttpResponse.cs index f38e953771..d5a144dd7d 100644 --- a/src/Http/Http.Abstractions/src/HttpResponse.cs +++ b/src/Http/Http.Abstractions/src/HttpResponse.cs @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Http /// /// Gets or sets the response body pipe /// - public abstract PipeWriter BodyWriter { get; set; } + public virtual PipeWriter BodyWriter { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } /// /// Gets or sets the value for the Content-Length response header. @@ -129,6 +129,6 @@ namespace Microsoft.AspNetCore.Http /// /// If the isn't set, StartAsync will default to calling HttpResponse.Body.FlushAsync(). /// - public abstract Task StartAsync(CancellationToken cancellationToken = default); + public virtual Task StartAsync(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } } } From cef57cb3eda9e97884f591deb083e74789e4837b Mon Sep 17 00:00:00 2001 From: Brennan Date: Wed, 29 May 2019 12:50:54 -0700 Subject: [PATCH 36/95] Enable netstandard2.1 libraries to build (#10614) --- Directory.Build.targets | 13 ++++++++++--- eng/SharedFramework.External.props | 2 +- eng/Version.Details.xml | 4 ++++ eng/Versions.props | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 212c9878c2..2486a20a85 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -81,9 +81,12 @@ - $(MicrosoftNETCoreAppPackageVersion) - $(MicrosoftNETCoreAppPackageVersion) - $(MicrosoftNETCoreAppPackageVersion) + + $(MicrosoftNETCoreAppPackageVersion) + + $(MicrosoftNETCoreAppPackageVersion) + + $(MicrosoftNETCoreAppPackageVersion) @@ -95,6 +98,10 @@ $(SharedFxVersion) + + $(NETStandardLibraryRefPackageVersion) + + diff --git a/eng/SharedFramework.External.props b/eng/SharedFramework.External.props index d735b0764f..e894b2f988 100644 --- a/eng/SharedFramework.External.props +++ b/eng/SharedFramework.External.props @@ -83,7 +83,7 @@ These compilation references are necessary to compile netstandard2.0 assemblies which are in the shared framework. This references are part of Microsoft.NETCore.App, so are listed here as references to be used during compilation only. --> - + <_CompilationOnlyReference Include="System.Buffers" /> <_CompilationOnlyReference Include="System.ComponentModel.Annotations" /> <_CompilationOnlyReference Include="System.Runtime.CompilerServices.Unsafe" /> diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 45a9967926..5971f3367b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -377,6 +377,10 @@ https://github.com/dotnet/core-setup 20426e8c486d8715337cb6438ec70bc3619a514d + + https://github.com/dotnet/core-setup + 20426e8c486d8715337cb6438ec70bc3619a514d + diff --git a/eng/Versions.props b/eng/Versions.props index 557ca946e5..553e0d4c7f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -25,6 +25,7 @@ 3.0.0-preview6-27723-08 3.0.0-preview6-27723-08 + 2.1.0-preview6-27723-08 4.6.0-preview6.19273.5 4.6.0-preview6.19273.5 From 443d60d8bb6ed53f0bb7eb4aea899935c1a509a2 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 29 May 2019 13:11:51 -0700 Subject: [PATCH 37/95] Remove arch from filename for RPM for targeting pack (#10593) --- src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj b/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj index 2ef50e8c63..6694256075 100644 --- a/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj +++ b/src/Installers/Rpm/TargetingPack/Rpm.TargetingPack.rpmproj @@ -3,7 +3,7 @@ /usr/share/dotnet/ - $(TargetingPackInstallerBaseName)-$(TargetingPackVersion)-x64.rpm + $(TargetingPackInstallerBaseName)-$(TargetingPackVersion).rpm $(TargetingPackLayoutRoot) From 95d2f8ee7a489319158edf35fd0abbf2560bdcd7 Mon Sep 17 00:00:00 2001 From: Artak <34246760+mkArtakMSFT@users.noreply.github.com> Date: Wed, 29 May 2019 13:36:24 -0700 Subject: [PATCH 38/95] Changing `@functions` block to `@code` block (#10591) * Changing `@functions` block to `@code` block --- .../Build/test/BindRazorIntegrationTest.cs | 38 +++++++++---------- .../ComponentRenderingRazorIntegrationTest.cs | 6 +-- .../GenericComponentRazorIntegrationTest.cs | 6 +-- .../test/RenderingRazorIntegrationTest.cs | 24 ++++++------ .../Pages/Counter.razor | 2 +- .../Pages/FetchData.razor | 2 +- .../Shared/NavMenu.razor | 2 +- .../Shared/SurveyPrompt.razor | 2 +- .../Pages/Counter.razor | 2 +- .../Pages/FetchData.razor | 2 +- .../Shared/NavMenu.razor | 2 +- .../Shared/SurveyPrompt.razor | 2 +- .../Pages/Index.razor | 2 +- .../Pages/Json.razor | 2 +- .../Pages/RenderList.razor | 2 +- .../StandaloneApp/Pages/Counter.razor | 2 +- .../StandaloneApp/Pages/FetchData.razor | 2 +- .../StandaloneApp/Shared/NavMenu.razor | 2 +- .../Auth/CascadingAuthenticationState.razor | 2 +- .../AddRemoveChildComponents.razor | 2 +- .../AfterRenderInteropComponent.razor | 2 +- .../AsyncEventHandlerComponent.razor | 2 +- .../BasicTestApp/AuthTest/AuthRouter.razor | 2 +- ...CascadingAuthenticationStateConsumer.razor | 2 +- .../BasicTestApp/BindCasesComponent.razor | 2 +- .../CascadingValueReceiveByName.razor | 2 +- .../CascadingValueReceiveByType.razor | 2 +- ...ascadingValueReceiveFixedByInterface.razor | 2 +- .../CascadingValueSupplier.razor | 2 +- .../BasicTestApp/ComponentRefComponent.razor | 2 +- .../BasicTestApp/ConcurrentRenderChild.razor | 2 +- .../BasicTestApp/CounterComponent.razor | 2 +- .../CounterComponentUsingChild.razor | 2 +- .../BasicTestApp/DataDashComponent.razor | 2 +- .../BasicTestApp/DispatchingComponent.razor | 2 +- .../BasicTestApp/ElementRefComponent.razor | 2 +- .../BasicTestApp/EventBubblingComponent.razor | 2 +- .../EventCallbackTest/ButtonComponent.razor | 2 +- .../EventCallbackCases.razor | 2 +- .../EventCallbackTest/InnerButton.razor | 2 +- .../EventCallbackTest/MiddleButton.razor | 2 +- .../StronglyTypedButton.razor | 2 +- .../EventCallbackTest/TemplatedControl.razor | 2 +- .../EventPreventDefaultComponent.razor | 2 +- .../BasicTestApp/ExternalContentPackage.razor | 2 +- .../BasicTestApp/FocusEventComponent.razor | 4 +- ...fyPropertyChangedValidationComponent.razor | 4 +- .../FormsTest/SimpleValidationComponent.razor | 2 +- .../TypicalValidationComponent.razor | 2 +- .../BinaryHttpRequestsComponent.razor | 2 +- .../CookieCounterComponent.razor | 2 +- .../HttpRequestsComponent.razor | 2 +- .../test/testassets/BasicTestApp/Index.razor | 2 +- .../BasicTestApp/InputEventComponent.razor | 2 +- .../BasicTestApp/InteropComponent.razor | 2 +- .../InteropOnInitializationComponent.razor | 2 +- .../BasicTestApp/KeyCasesComponent.razor | 2 +- .../BasicTestApp/KeyCasesTreeNode.razor | 2 +- .../BasicTestApp/KeyPressEventComponent.razor | 2 +- .../BasicTestApp/MarkupBlockComponent.razor | 2 +- .../BasicTestApp/MessageComponent.razor | 2 +- .../BasicTestApp/MouseEventComponent.razor | 4 +- .../BasicTestApp/MultipleChildContent.razor | 4 +- .../testassets/BasicTestApp/OrderedList.razor | 4 +- .../PassThroughContentComponent.razor | 2 +- .../PrerenderedToInteractiveTransition.razor | 2 +- .../PropertiesChangedHandlerChild.razor | 2 +- .../PropertiesChangedHandlerParent.razor | 2 +- .../BasicTestApp/RenderFragmentToggler.razor | 2 +- .../ReorderingFocusComponent.razor | 2 +- .../RouterTest/UriHelperComponent.razor | 2 +- .../RouterTest/WithNumberParameters.razor | 2 +- .../RouterTest/WithParameters.razor | 2 +- .../BasicTestApp/SvgComponent.razor | 2 +- .../BasicTestApp/TemplatedTable.razor | 4 +- .../BasicTestApp/TouchEventComponent.razor | 4 +- .../testassets/ComponentsApp.App/App.razor | 2 +- .../ComponentsApp.App/Pages/Counter.razor | 2 +- .../ComponentsApp.App/Pages/Error.razor | 4 +- .../ComponentsApp.App/Pages/FetchData.razor | 2 +- .../ComponentsApp.App/Pages/Greeter.razor | 2 +- .../ComponentsApp.App/Pages/Ticker.razor | 2 +- .../ComponentsApp.App/Shared/NavMenu.razor | 2 +- .../ComponentFromPackage.razor | 2 +- .../RazorComponents/FetchData.razor | 2 +- .../BasicWebSite/RazorComponents/Throws.razor | 4 +- .../content/RazorComponent/Component1.razor | 2 +- .../Pages/Counter.razor | 2 +- .../Pages/FetchData.razor | 2 +- .../Shared/LoginDisplay.razor | 2 +- .../Shared/NavMenu.razor | 2 +- 91 files changed, 133 insertions(+), 133 deletions(-) diff --git a/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs b/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs index 88799baeb6..1e07df80cf 100644 --- a/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs +++ b/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs @@ -40,7 +40,7 @@ namespace Test var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -77,7 +77,7 @@ namespace Test var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -114,7 +114,7 @@ namespace Test var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -151,7 +151,7 @@ namespace Test var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -184,7 +184,7 @@ namespace Test var component = CompileToComponent(@"
-@functions { +@code { public string ParentValue { get; set; } = ""hi""; }"); @@ -217,7 +217,7 @@ namespace Test var component = CompileToComponent(@"
-@functions { +@code { public string ParentValue { get; set; } = ""hi""; }"); @@ -252,7 +252,7 @@ namespace Test // Act var result = CompileToCSharp(@"
-@functions { +@code { public string ParentValue { get; set; } = ""hi""; }"); @@ -272,7 +272,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -293,7 +293,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1); }"); @@ -315,7 +315,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1); public string Format { get; set; } = ""MM/dd/yyyy""; @@ -339,7 +339,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -361,7 +361,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public bool Enabled { get; set; } }"); @@ -382,7 +382,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -404,7 +404,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1); }"); @@ -426,7 +426,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -449,7 +449,7 @@ namespace Test // Arrange var component = CompileToComponent(@" -@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -477,7 +477,7 @@ namespace Test
@(42.ToString())
-@functions { +@code { public int ParentValue { get; set; } = 42; }"); @@ -502,7 +502,7 @@ namespace Test // Arrange & Act var generated = CompileToCSharp(@" -@functions { +@code { public string Text { get; set; } = ""text""; }"); @@ -517,7 +517,7 @@ namespace Test // Arrange & Act var generated = CompileToCSharp(@" -@functions { +@code { public string Text { get; set; } = ""text""; }"); diff --git a/src/Components/Blazor/Build/test/ComponentRenderingRazorIntegrationTest.cs b/src/Components/Blazor/Build/test/ComponentRenderingRazorIntegrationTest.cs index ae8bb7b26d..fde8fa9d7a 100644 --- a/src/Components/Blazor/Build/test/ComponentRenderingRazorIntegrationTest.cs +++ b/src/Components/Blazor/Build/test/ComponentRenderingRazorIntegrationTest.cs @@ -216,7 +216,7 @@ namespace Test var component = CompileToComponent($@" -@functions {{ +@code {{ private int counter; private void Increment(UIMouseEventArgs e) {{ counter++; @@ -261,7 +261,7 @@ namespace Test var component = CompileToComponent(@" -@functions { +@code { private int counter; private void Increment(UIEventArgs e) { counter++; @@ -446,7 +446,7 @@ namespace Test // Act var component = CompileToComponent(@"

-@functions { +@code { public string ParentBgColor { get; set; } = ""#FFFFFF""; public void OnComponentHover(UIMouseEventArgs e) diff --git a/src/Components/Blazor/Build/test/GenericComponentRazorIntegrationTest.cs b/src/Components/Blazor/Build/test/GenericComponentRazorIntegrationTest.cs index 66073c06d7..62f2b134cb 100644 --- a/src/Components/Blazor/Build/test/GenericComponentRazorIntegrationTest.cs +++ b/src/Components/Blazor/Build/test/GenericComponentRazorIntegrationTest.cs @@ -123,7 +123,7 @@ namespace Test var component = CompileToComponent(@" () { 1, 2, })"" ref=""_my"" /> -@functions { +@code { GenericContext _my; void Foo() { GC.KeepAlive(_my); } }"); @@ -190,7 +190,7 @@ namespace Test var component = CompileToComponent(@" () { 1, 2, })"" ref=""_my"" /> -@functions { +@code { GenericContext _my; void Foo() { GC.KeepAlive(_my); } }"); @@ -223,7 +223,7 @@ namespace Test @typeparam TItem -@functions { +@code { [Parameter] List MyItems { get; set; } GenericContext _my; void Foo() { GC.KeepAlive(_my); } diff --git a/src/Components/Blazor/Build/test/RenderingRazorIntegrationTest.cs b/src/Components/Blazor/Build/test/RenderingRazorIntegrationTest.cs index a4d8eefc59..0e5e308434 100644 --- a/src/Components/Blazor/Build/test/RenderingRazorIntegrationTest.cs +++ b/src/Components/Blazor/Build/test/RenderingRazorIntegrationTest.cs @@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test @foreach(var item in items) { @item } - @functions { + @code { string[] items = new[] { ""First"", ""Second"", ""Third"" }; } "); @@ -296,7 +296,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test // Arrange/Act var component = CompileToComponent( @" - @functions { + @code { public bool HandlerWasCalled { get; set; } = false; void MyHandleEvent(Microsoft.AspNetCore.Components.UIEventArgs eventArgs) @@ -341,7 +341,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test // Arrange/Act var component = CompileToComponent( @" - @functions { + @code { public string MyValue { get; set; } = ""Initial value""; }"); var myValueProperty = component.GetType().GetProperty("MyValue"); @@ -376,7 +376,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test // Arrange/Act var component = CompileToComponent( @" - @functions { + @code { public string MyValue { get; set; } = ""Initial value""; }"); var myValueProperty = component.GetType().GetProperty("MyValue"); @@ -411,7 +411,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test // Arrange/Act var component = CompileToComponent( @" - @functions { + @code { public DateTime MyDate { get; set; } = new DateTime(2018, 3, 4, 1, 2, 3); }"); var myDateProperty = component.GetType().GetProperty("MyDate"); @@ -449,7 +449,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test var testDateFormat = "ddd yyyy-MM-dd"; var component = CompileToComponent( $@" - @functions {{ + @code {{ public DateTime MyDate {{ get; set; }} = new DateTime(2018, 3, 4); }}"); var myDateProperty = component.GetType().GetProperty("MyDate"); @@ -500,7 +500,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test // Arrange var component = CompileToComponent(@" -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Pages/FetchData.razor b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Pages/FetchData.razor index 2f8e580529..553979536a 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Pages/FetchData.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Pages/FetchData.razor @@ -35,7 +35,7 @@ else } -@functions { +@code { WeatherForecast[] forecasts; protected override async Task OnInitAsync() diff --git a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/NavMenu.razor b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/NavMenu.razor index 6fd8763b01..7a344f7508 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/NavMenu.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/NavMenu.razor @@ -25,7 +25,7 @@

-@functions { +@code { bool collapseNavMenu = true; string NavMenuCssClass => collapseNavMenu ? "collapse" : null; diff --git a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/SurveyPrompt.razor b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/SurveyPrompt.razor index 1db3c13c90..27306aa0d0 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/SurveyPrompt.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorHosted-CSharp/BlazorHosted-CSharp.Client/Shared/SurveyPrompt.razor @@ -9,7 +9,7 @@ and tell us what you think.
-@functions { +@code { // Demonstrates how a parent component can supply parameters [Parameter] string Title { get; set; } } diff --git a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/Counter.razor b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/Counter.razor index 973af92354..572f9c88dc 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/Counter.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/Counter.razor @@ -6,7 +6,7 @@ -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/FetchData.razor b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/FetchData.razor index 3f5b9a9e49..a7dc14ae5c 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/FetchData.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Pages/FetchData.razor @@ -34,7 +34,7 @@ else } -@functions { +@code { WeatherForecast[] forecasts; protected override async Task OnInitAsync() diff --git a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/NavMenu.razor b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/NavMenu.razor index c5904d3bd6..4e9b63b2a2 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/NavMenu.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/NavMenu.razor @@ -25,7 +25,7 @@
-@functions { +@code { bool collapseNavMenu = true; string NavMenuCssClass => collapseNavMenu ? "collapse" : null; diff --git a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/SurveyPrompt.razor b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/SurveyPrompt.razor index 1db3c13c90..27306aa0d0 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/SurveyPrompt.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorStandalone-CSharp/Shared/SurveyPrompt.razor @@ -9,7 +9,7 @@ and tell us what you think. -@functions { +@code { // Demonstrates how a parent component can supply parameters [Parameter] string Title { get; set; } } diff --git a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Index.razor b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Index.razor index 870c79b1a4..7509137e37 100644 --- a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Index.razor +++ b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Index.razor @@ -3,7 +3,7 @@ Hello, world! -@functions { +@code { protected override void OnAfterRender() { BenchmarkEvent.Send(JSRuntime, "Rendered index.cshtml"); diff --git a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Json.razor b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Json.razor index 9deb601126..db8b38e5a3 100644 --- a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Json.razor +++ b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/Json.razor @@ -22,7 +22,7 @@

Deserialized @numPeopleDeserialized people

} -@functions { +@code { static string[] Clearances = new[] { "Alpha", "Beta", "Gamma", "Delta", "Epsilon" }; Person smallOrgChart = GenerateOrgChart(1, 4); Person largeOrgChart = GenerateOrgChart(5, 4); diff --git a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/RenderList.razor b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/RenderList.razor index 0a9ad97997..0d9feb6fec 100644 --- a/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/RenderList.razor +++ b/src/Components/Blazor/testassets/Microsoft.AspNetCore.Blazor.E2EPerformance/Pages/RenderList.razor @@ -32,7 +32,7 @@ Number of items: } -@functions { +@code { int numItems = 10; bool show = false; diff --git a/src/Components/Blazor/testassets/StandaloneApp/Pages/Counter.razor b/src/Components/Blazor/testassets/StandaloneApp/Pages/Counter.razor index 973af92354..572f9c88dc 100644 --- a/src/Components/Blazor/testassets/StandaloneApp/Pages/Counter.razor +++ b/src/Components/Blazor/testassets/StandaloneApp/Pages/Counter.razor @@ -6,7 +6,7 @@ -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor index 0ecea85876..9024a800a7 100644 --- a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor +++ b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor @@ -43,7 +43,7 @@ else

} -@functions { +@code { [Parameter] DateTime StartDate { get; set; } WeatherForecast[] forecasts; diff --git a/src/Components/Blazor/testassets/StandaloneApp/Shared/NavMenu.razor b/src/Components/Blazor/testassets/StandaloneApp/Shared/NavMenu.razor index c2824be334..3218b3a351 100644 --- a/src/Components/Blazor/testassets/StandaloneApp/Shared/NavMenu.razor +++ b/src/Components/Blazor/testassets/StandaloneApp/Shared/NavMenu.razor @@ -25,7 +25,7 @@ -@functions { +@code { bool collapseNavMenu = true; void ToggleNavMenu() diff --git a/src/Components/Components/src/Auth/CascadingAuthenticationState.razor b/src/Components/Components/src/Auth/CascadingAuthenticationState.razor index f3bf0bd327..2910fc5fba 100644 --- a/src/Components/Components/src/Auth/CascadingAuthenticationState.razor +++ b/src/Components/Components/src/Auth/CascadingAuthenticationState.razor @@ -4,7 +4,7 @@ -@functions { +@code { private Task _currentAuthenticationStateTask; /// diff --git a/src/Components/test/testassets/BasicTestApp/AddRemoveChildComponents.razor b/src/Components/test/testassets/BasicTestApp/AddRemoveChildComponents.razor index 3603cbb45f..2780b58a4d 100644 --- a/src/Components/test/testassets/BasicTestApp/AddRemoveChildComponents.razor +++ b/src/Components/test/testassets/BasicTestApp/AddRemoveChildComponents.razor @@ -8,7 +8,7 @@ Child components follow.

} -@functions { +@code { int numAdded = 0; List currentChildrenMessages = new List(); diff --git a/src/Components/test/testassets/BasicTestApp/AfterRenderInteropComponent.razor b/src/Components/test/testassets/BasicTestApp/AfterRenderInteropComponent.razor index c0a8cb4bca..eac65dec0d 100644 --- a/src/Components/test/testassets/BasicTestApp/AfterRenderInteropComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/AfterRenderInteropComponent.razor @@ -3,7 +3,7 @@ -@functions { +@code { ElementRef myInput; protected override void OnAfterRender() diff --git a/src/Components/test/testassets/BasicTestApp/AsyncEventHandlerComponent.razor b/src/Components/test/testassets/BasicTestApp/AsyncEventHandlerComponent.razor index b35b775cd7..a12701dadb 100644 --- a/src/Components/test/testassets/BasicTestApp/AsyncEventHandlerComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/AsyncEventHandlerComponent.razor @@ -6,7 +6,7 @@ -@functions +@code { TaskCompletionSource _tcs; string state = "Stopped"; diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor index 22951925af..34be56726f 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/AuthRouter.razor @@ -22,7 +22,7 @@
-@functions { +@code { protected override void OnInit() { // Start at AuthHome, not at any other component in the same app that happens to diff --git a/src/Components/test/testassets/BasicTestApp/AuthTest/CascadingAuthenticationStateConsumer.razor b/src/Components/test/testassets/BasicTestApp/AuthTest/CascadingAuthenticationStateConsumer.razor index 8113e1080f..9c61665fc7 100644 --- a/src/Components/test/testassets/BasicTestApp/AuthTest/CascadingAuthenticationStateConsumer.razor +++ b/src/Components/test/testassets/BasicTestApp/AuthTest/CascadingAuthenticationStateConsumer.razor @@ -32,7 +32,7 @@ else

} -@functions +@code { static Predicate TestClaimPredicate = c => c.Type == "test-claim"; diff --git a/src/Components/test/testassets/BasicTestApp/BindCasesComponent.razor b/src/Components/test/testassets/BasicTestApp/BindCasesComponent.razor index 707ed9124a..45ef3a6e38 100644 --- a/src/Components/test/testassets/BasicTestApp/BindCasesComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/BindCasesComponent.razor @@ -137,7 +137,7 @@

-@functions { +@code { string textboxInitiallyBlankValue = null; string textboxInitiallyPopulatedValue = "Hello"; diff --git a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByName.razor b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByName.razor index a0d14e571a..ce912c78f8 100644 --- a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByName.razor +++ b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByName.razor @@ -10,7 +10,7 @@ Flag 2: @MyFlag2

-@functions { +@code { [CascadingParameter(Name = "TestFlag1")] bool MyFlag1 { get; set; } [CascadingParameter(Name = "TestFlag2")] bool MyFlag2 { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByType.razor b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByType.razor index a1731d14d5..e51cfd66cd 100644 --- a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByType.razor +++ b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveByType.razor @@ -3,6 +3,6 @@ @CurrentCounter.NumClicks

-@functions { +@code { [CascadingParameter] CounterDTO CurrentCounter { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveFixedByInterface.razor b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveFixedByInterface.razor index cf6b9abc90..89b92db3ef 100644 --- a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveFixedByInterface.razor +++ b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueReceiveFixedByInterface.razor @@ -16,7 +16,7 @@

-@functions { +@code { int numRenders = 0; [CascadingParameter] ICanDecrement Ancestor { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueSupplier.razor b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueSupplier.razor index f1f70cfba0..a2a30d4f7f 100644 --- a/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueSupplier.razor +++ b/src/Components/test/testassets/BasicTestApp/CascadingValueTest/CascadingValueSupplier.razor @@ -20,7 +20,7 @@

-@functions { +@code { CounterDTO counterState = new CounterDTO { NumClicks = 100 }; bool currentFlagValue1; bool currentFlagValue2; diff --git a/src/Components/test/testassets/BasicTestApp/ComponentRefComponent.razor b/src/Components/test/testassets/BasicTestApp/ComponentRefComponent.razor index ded245d99b..394dae74a5 100644 --- a/src/Components/test/testassets/BasicTestApp/ComponentRefComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/ComponentRefComponent.razor @@ -24,7 +24,7 @@ -@functions { +@code { bool _toggleCapturedComponentPresence = true; CounterComponent _myChildCounter; diff --git a/src/Components/test/testassets/BasicTestApp/ConcurrentRenderChild.razor b/src/Components/test/testassets/BasicTestApp/ConcurrentRenderChild.razor index 817f31bbdd..c843e11a91 100644 --- a/src/Components/test/testassets/BasicTestApp/ConcurrentRenderChild.razor +++ b/src/Components/test/testassets/BasicTestApp/ConcurrentRenderChild.razor @@ -1,5 +1,5 @@ @(isAfterDelay ? "😊" :"WAITING") -@functions +@code { protected bool isAfterDelay; diff --git a/src/Components/test/testassets/BasicTestApp/CounterComponent.razor b/src/Components/test/testassets/BasicTestApp/CounterComponent.razor index ed58054b8f..9a635e7ad0 100644 --- a/src/Components/test/testassets/BasicTestApp/CounterComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/CounterComponent.razor @@ -12,7 +12,7 @@

Listening

} -@functions { +@code { int currentCount = 0; bool handleClicks = true; diff --git a/src/Components/test/testassets/BasicTestApp/CounterComponentUsingChild.razor b/src/Components/test/testassets/BasicTestApp/CounterComponentUsingChild.razor index 08e55d6027..efd3ec575c 100644 --- a/src/Components/test/testassets/BasicTestApp/CounterComponentUsingChild.razor +++ b/src/Components/test/testassets/BasicTestApp/CounterComponentUsingChild.razor @@ -5,7 +5,7 @@ -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/Components/test/testassets/BasicTestApp/DataDashComponent.razor b/src/Components/test/testassets/BasicTestApp/DataDashComponent.razor index ae00ab1bc9..435ef0fc04 100644 --- a/src/Components/test/testassets/BasicTestApp/DataDashComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/DataDashComponent.razor @@ -1,5 +1,5 @@
@TabId
-@functions { +@code { string TabId = "17"; } diff --git a/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor b/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor index f59d58bb33..e55cc33965 100644 --- a/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor @@ -15,7 +15,7 @@ -@functions { +@code { string result; async Task RunWithoutDispatch() diff --git a/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor b/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor index a8a0a85aa5..17010b9d6e 100644 --- a/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor @@ -25,7 +25,7 @@ Toggle input -@functions { +@code { int _count = 0; bool _toggleCapturedElementPresence = true; ElementRef _myInput; diff --git a/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor b/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor index f5fcfac319..5afc89a4fa 100644 --- a/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor @@ -24,7 +24,7 @@ -@functions { +@code { string logValue = string.Empty; void LogEvent(string message) diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor index 13d814bbe3..08ffee0422 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor @@ -1,7 +1,7 @@ -@functions { +@code { [Parameter] int Count { get; set; } [Parameter] EventCallback CountChanged { get; set; } [Parameter] string Text { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor index 945e89a290..b3859bfb0e 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor @@ -35,7 +35,7 @@ -@functions { +@code { int renderCount; int buttonComponentCount = 1; // Avoid CS0649 diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor index 32a59dbeed..70eb5f662a 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor @@ -1,7 +1,7 @@ -@functions { +@code { [Parameter] EventCallback OnClick { get; set; } [Parameter] string Text { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/MiddleButton.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/MiddleButton.razor index 642b201430..33294d11b7 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/MiddleButton.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/MiddleButton.razor @@ -1,7 +1,7 @@ -@functions { +@code { [Parameter] EventCallback OnClick { get; set; } [Parameter] string Text { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor index 727aaa5e33..b9520414e2 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor @@ -1,7 +1,7 @@ -@functions { +@code { [Parameter] EventCallback OnClick { get; set; } [Parameter] string Text { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/TemplatedControl.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/TemplatedControl.razor index f5a1bfbf1b..c200183f3f 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/TemplatedControl.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/TemplatedControl.razor @@ -3,6 +3,6 @@ @ChildContent -@functions { +@code { [Parameter] RenderFragment ChildContent { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor b/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor index 3681b170f8..5a1c7c0b07 100644 --- a/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor @@ -28,7 +28,7 @@

Event was handled

} -@functions { +@code { bool didHandleEvent; async Task HandleClick() diff --git a/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor b/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor index ef02e9f256..0f4ef9e5f3 100644 --- a/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor +++ b/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor @@ -29,7 +29,7 @@ -@functions +@code { string result; diff --git a/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor b/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor index b6ca0f7141..7eb958bbf5 100644 --- a/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor @@ -16,7 +16,7 @@ Another input (to distract you)

-@functions { +@code { string message; @@ -48,4 +48,4 @@ { message = string.Empty; } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor index e921bc58aa..e094ce6ecd 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor @@ -12,7 +12,7 @@

This example also shows that you don't strictly have to use EditForm. You can manually - cascade an EditContext to the components that integrate with it. + cascade an EditContext to the components that integrate with it.

@@ -34,7 +34,7 @@
@submissionStatus
-@functions { +@code { MyModel person = new MyModel(); EditContext editContext; string submissionStatus; diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor index bc9a267403..f2cee1b6c4 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor @@ -28,7 +28,7 @@ @lastCallback } -@functions { +@code { string lastCallback; [Required(ErrorMessage = "Please choose a username")] diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor index d4285e92e2..ad2aa89550 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor @@ -50,7 +50,7 @@
    @foreach (var entry in submissionLog) {
  • @entry
  • }
-@functions { +@code { Person person = new Person(); // Usually this would be in a different file diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor index 3ffbd88850..58107214d8 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor @@ -20,7 +20,7 @@ @testOutcome -@functions { +@code { string uri = ""; HttpStatusCode? responseStatusCode; string responseStatusText; diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor index 7570dc9915..b2e9ec6101 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor @@ -11,7 +11,7 @@

@responseText

} -@functions +@code { bool requestInProgress = false; string testServerBaseUrl; diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor index 81594508de..6352a7e7b1 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor @@ -60,7 +60,7 @@ } -@functions { +@code { string uri = "http://api.icndb.com/jokes/random"; string method = "GET"; string requestBody = ""; diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index c3f4468cde..6a1d2886c9 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -70,7 +70,7 @@ @((RenderFragment)RenderSelectedComponent) -@functions { +@code { string SelectedComponentTypeName { get; set; } = "none"; Type SelectedComponentType diff --git a/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor b/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor index d5e58566be..eb469715db 100644 --- a/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor @@ -4,6 +4,6 @@

@inputText

-@functions { +@code { string inputText { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor index 1a1b26535d..2ae6ccd0ae 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor @@ -51,7 +51,7 @@

Done with interop.

} -@functions { +@code { public IDictionary ReturnValues { get; set; } = new Dictionary(); public IDictionary Invocations { get; set; } = new Dictionary(); diff --git a/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor index 8ac0815070..e3cafe0d65 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor @@ -22,7 +22,7 @@

-@functions { +@code { string infoFromJs; ElementRef myElem; diff --git a/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor b/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor index 9ef1a5bffa..ab5271118d 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor @@ -25,7 +25,7 @@ .key-cases .node .node .children { margin-left: 1.1rem; } -@functions { +@code { string modelJson = @"{ ""label"": ""root"", ""children"": [ diff --git a/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor b/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor index 16ed8de0eb..7634bb95f0 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor @@ -32,7 +32,7 @@ } -@functions { +@code { public class Node { public object Key { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor b/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor index 4281b7d297..bc8f8b95ea 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor @@ -8,7 +8,7 @@ Type here: } -@functions { +@code { List keysPressed = new List(); void OnKeyPressed(UIKeyboardEventArgs eventArgs) diff --git a/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor b/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor index 175091fc0b..186eeec72e 100644 --- a/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor @@ -24,7 +24,7 @@ @((MarkupString)myMarkup) -@functions { +@code { bool changeOutput; string myMarkup = "

This is a markup string.

"; diff --git a/src/Components/test/testassets/BasicTestApp/MessageComponent.razor b/src/Components/test/testassets/BasicTestApp/MessageComponent.razor index 75b3ecc823..889c938517 100644 --- a/src/Components/test/testassets/BasicTestApp/MessageComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/MessageComponent.razor @@ -1,4 +1,4 @@ @Message -@functions { +@code { [Parameter] string Message { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor b/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor index 9576fdc848..2bba6f2b62 100644 --- a/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor @@ -24,7 +24,7 @@

-@functions { +@code { string message; @@ -72,4 +72,4 @@ { message = string.Empty; } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor b/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor index cc0338c7d3..c6a5e9f624 100644 --- a/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor +++ b/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor @@ -13,7 +13,7 @@ Toggle: -@functions { +@code { List Items { get; } = new List() { new Item(){ Col1 = "a0", Col2 = "b0", Col3 = "c0", }, @@ -29,4 +29,4 @@ Toggle: public string Col2 { get; set; } public string Col3 { get; set; } } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/OrderedList.razor b/src/Components/test/testassets/BasicTestApp/OrderedList.razor index 3e9dafb1b3..c212f194d6 100644 --- a/src/Components/test/testassets/BasicTestApp/OrderedList.razor +++ b/src/Components/test/testassets/BasicTestApp/OrderedList.razor @@ -10,7 +10,7 @@ } -@functions{ +@code{ [Parameter] IEnumerable Items { get; set; } [Parameter] RenderFragment Template { get; set; } @@ -21,4 +21,4 @@ public TItem Item { get; set; } } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/PassThroughContentComponent.razor b/src/Components/test/testassets/BasicTestApp/PassThroughContentComponent.razor index 6e9c3d941b..8ab805ed1b 100644 --- a/src/Components/test/testassets/BasicTestApp/PassThroughContentComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/PassThroughContentComponent.razor @@ -1,5 +1,5 @@ @ChildContent -@functions { +@code { // Note: The lack of any whitespace or other output besides @ChildContent is important for // what scenarios this component is used for in E2E tests. diff --git a/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor b/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor index 05be9c65e1..27bb0041cf 100644 --- a/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor +++ b/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor @@ -15,7 +15,7 @@

-@functions { +@code { int count; bool firstRender = false; protected override Task OnAfterRenderAsync() diff --git a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerChild.razor b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerChild.razor index 28ec61b45c..f359d6c326 100644 --- a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerChild.razor +++ b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerChild.razor @@ -1,7 +1,7 @@ 
You supplied: @SuppliedValue
I computed: @computedValue
-@functions { +@code { [Parameter] int SuppliedValue { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor index 4fbdada071..b201311876 100644 --- a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor +++ b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor @@ -1,6 +1,6 @@  -@functions { +@code { private int valueToSupply = 100; } diff --git a/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor b/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor index 6ce52d14b1..fb6b98aba6 100644 --- a/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor +++ b/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor @@ -10,7 +10,7 @@

The end

-@functions { +@code { bool showFragment; static RenderFragment ExampleFragment = builder => diff --git a/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor b/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor index acede2b465..594555a689 100644 --- a/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor @@ -30,7 +30,7 @@ } -@functions { +@code { Random rng = new Random(); TodoItem[] todoItems = new[] { diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor index 27268d73fe..c27f827a6d 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor @@ -5,7 +5,7 @@ @UrlLocation -@functions{ +@code{ string UrlLocation; protected override void OnInit() diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/WithNumberParameters.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/WithNumberParameters.razor index 634e2d40c5..67ffca8770 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/WithNumberParameters.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/WithNumberParameters.razor @@ -1,7 +1,7 @@ @page "/WithNumberParameters/{TestInt:int}/{TestLong:long}/{TestDouble:double}/{TestFloat:float}/{TestDec:decimal}"
Test parameters: @TestInt @TestLong @TestDouble @TestFloat @TestDec
-@functions +@code { [Parameter] int TestInt { get; set; } [Parameter] long TestLong { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor index 2caf8c67bb..e13b253ff1 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/WithParameters.razor @@ -3,7 +3,7 @@
Your full name is @FirstName @LastName.
-@functions +@code { [Parameter] string FirstName { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/SvgComponent.razor b/src/Components/test/testassets/BasicTestApp/SvgComponent.razor index 342e6fcb8c..4c538e6c2d 100644 --- a/src/Components/test/testassets/BasicTestApp/SvgComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/SvgComponent.razor @@ -4,6 +4,6 @@ -@functions { +@code { int radius = 10; } diff --git a/src/Components/test/testassets/BasicTestApp/TemplatedTable.razor b/src/Components/test/testassets/BasicTestApp/TemplatedTable.razor index 57fb0a3fa1..18ea949f31 100644 --- a/src/Components/test/testassets/BasicTestApp/TemplatedTable.razor +++ b/src/Components/test/testassets/BasicTestApp/TemplatedTable.razor @@ -20,7 +20,7 @@ } -@functions { +@code { [Parameter] RenderFragment Header { get; set; } @@ -32,4 +32,4 @@ [Parameter] IReadOnlyList Items { get; set; } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor b/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor index 425abfcd87..0a7fe9e462 100644 --- a/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor @@ -21,7 +21,7 @@

-@functions { +@code { string message; @@ -36,4 +36,4 @@ { message = string.Empty; } -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/ComponentsApp.App/App.razor b/src/Components/test/testassets/ComponentsApp.App/App.razor index 03ebda9b0f..a6a7873965 100644 --- a/src/Components/test/testassets/ComponentsApp.App/App.razor +++ b/src/Components/test/testassets/ComponentsApp.App/App.razor @@ -7,6 +7,6 @@ -@functions{ +@code{ [Parameter] public string Name { get; set; } } diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor index 973af92354..572f9c88dc 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor @@ -6,7 +6,7 @@ -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor index 35f83bf20d..cb1be064f1 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor @@ -3,13 +3,13 @@

Error

@if(ShouldCauseError) { - throw new InvalidOperationException("Exception while rendering"); + throw new InvalidOperationException("Exception while rendering"); } else { } -@functions { +@code { public bool ShouldCauseError { get; set; } void CauseError() diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/FetchData.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/FetchData.razor index af9cc8b037..cdc696a7ce 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/FetchData.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/FetchData.razor @@ -43,7 +43,7 @@ else

} -@functions { +@code { [Parameter] DateTime StartDate { get; set; } WeatherForecast[] forecasts; diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Greeter.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Greeter.razor index cb7f5bc192..fd39012d86 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Greeter.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Greeter.razor @@ -3,6 +3,6 @@

Greeter

Hello @Name

-@functions { +@code { [CascadingParameter(Name=nameof(Name))] public string Name { get; set; } } diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Ticker.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Ticker.razor index 249dfc51e3..2ff15a18ba 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Ticker.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Ticker.razor @@ -5,7 +5,7 @@

Ticker

@tick

-@functions { +@code { int tick; private bool _isDisposed; diff --git a/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor b/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor index 87ee39f518..38e23a6b6d 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor @@ -45,7 +45,7 @@ -@functions { +@code { bool collapseNavMenu = true; void ToggleNavMenu() diff --git a/src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor b/src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor index 5e4ca29480..93a353b95a 100644 --- a/src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor +++ b/src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor @@ -4,7 +4,7 @@ -@functions { +@code { string buttonLabel = "Click me"; void ChangeLabel() diff --git a/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor b/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor index 79f6d6937c..3ed6d5d979 100644 --- a/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor +++ b/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/FetchData.razor @@ -35,7 +35,7 @@ else } -@functions { +@code { [Parameter] DateTime StartDate { get; set; } WeatherForecast[] forecasts; diff --git a/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/Throws.razor b/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/Throws.razor index 3291fba732..2bfe1d7c4d 100644 --- a/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/Throws.razor +++ b/src/Mvc/test/WebSites/BasicWebSite/RazorComponents/Throws.razor @@ -1,7 +1,7 @@ @page "/components/throws" - + @* This is expected to throw and result in a 500 *@ -@functions { +@code { protected override async Task OnInitAsync() { await base.OnInitAsync(); diff --git a/src/ProjectTemplates/Web.ItemTemplates/content/RazorComponent/Component1.razor b/src/ProjectTemplates/Web.ItemTemplates/content/RazorComponent/Component1.razor index 68d4749c0d..bc0a14eec4 100644 --- a/src/ProjectTemplates/Web.ItemTemplates/content/RazorComponent/Component1.razor +++ b/src/ProjectTemplates/Web.ItemTemplates/content/RazorComponent/Component1.razor @@ -1,5 +1,5 @@

Component1

-@functions { +@code { } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/Counter.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/Counter.razor index e7244066a2..def057f5c4 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/Counter.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/Counter.razor @@ -6,7 +6,7 @@ -@functions { +@code { int currentCount = 0; void IncrementCount() diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/FetchData.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/FetchData.razor index 3b24942890..25e30159f4 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/FetchData.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Pages/FetchData.razor @@ -35,7 +35,7 @@ else } -@functions { +@code { WeatherForecast[] forecasts; protected override async Task OnInitAsync() diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor index 6ccbbd20ae..daa24cbb86 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/LoginDisplay.razor @@ -31,7 +31,7 @@ -@functions { +@code { bool canEditProfile; protected override void OnInit() diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/NavMenu.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/NavMenu.razor index 34fc7d3f7c..511e17b455 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/NavMenu.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Shared/NavMenu.razor @@ -25,7 +25,7 @@ -@functions { +@code { bool collapseNavMenu = true; string NavMenuCssClass => collapseNavMenu ? "collapse" : null; From 456dbf1309f9fcae1d7b376784088dcd7818c01e Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 29 May 2019 13:48:22 -0700 Subject: [PATCH 39/95] Update CONTRIBUTING.md to reference 'help wanted' labels and link to contributor docs (#10625) --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index efbfe08ee4..222522b092 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,12 @@ Our team members also monitor several other discussion forums: ## Contributing code and content +We accept fixes and features! Here are some resources to help you get started on how to contribute code or new content. + +* Look at the [Contributor documentation](/docs/) to get started on building the source code on your own. +* ["Help wanted" issues](https://github.com/aspnet/AspNetCore/labels/help%20wanted) - these issues are up for grabs. Comment on an issue if you want to create a fix. +* ["Good first issue" issues](https://github.com/aspnet/AspNetCore/labels/good%20first%20issue) - we think these are a good for newcomers. + ### Identifying the scale If you would like to contribute to one of our repositories, first identify the scale of what you would like to contribute. If it is small (grammar/spelling or a bug fix) feel free to start working on a fix. If you are submitting a feature or substantial code contribution, please discuss it with the team and ensure it follows the product roadmap. You might also read these two blogs posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. All code submissions will be rigorously reviewed and tested by the ASP.NET and Entity Framework teams, and only those that meet an extremely high bar for both quality and design/roadmap appropriateness will be merged into the source. From 231bd526ad7cecfc41459846e3a1514453172e00 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 29 May 2019 22:52:13 +0100 Subject: [PATCH 40/95] Fix linux CI break (#10610) --- ...onentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs index 0a9668182c..7e2c919287 100644 --- a/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.cs +++ b/src/Components/Analyzers/test/ComponentParameterCaptureUnmatchedValuesMustBeUniqueTest.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; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using TestHelper; @@ -43,9 +44,9 @@ namespace Microsoft.AspNetCore.Components.Analyzers.Test }} }}" + ComponentsTestDeclarations.Source; - var message = @"Component type 'ConsoleApplication1.TypeName' defines properties multiple parameters with CaptureUnmatchedValues. Properties: -ConsoleApplication1.TypeName.MyOtherProperty -ConsoleApplication1.TypeName.MyProperty"; + var message = @"Component type 'ConsoleApplication1.TypeName' defines properties multiple parameters with CaptureUnmatchedValues. Properties: " + Environment.NewLine + +"ConsoleApplication1.TypeName.MyOtherProperty" + Environment.NewLine + +"ConsoleApplication1.TypeName.MyProperty"; VerifyCSharpDiagnostic(test, new DiagnosticResult From 2a9655b8d6650841ba38b1810ad708727cbeb38e Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 29 May 2019 17:49:07 -0700 Subject: [PATCH 41/95] Clear ShutdownTestRun flaky state (#10618) --- src/Hosting/test/FunctionalTests/ShutdownTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Hosting/test/FunctionalTests/ShutdownTests.cs b/src/Hosting/test/FunctionalTests/ShutdownTests.cs index 7e6124e146..82e707487d 100644 --- a/src/Hosting/test/FunctionalTests/ShutdownTests.cs +++ b/src/Hosting/test/FunctionalTests/ShutdownTests.cs @@ -28,7 +28,6 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests [ConditionalFact] [OSSkipCondition(OperatingSystems.Windows)] [OSSkipCondition(OperatingSystems.MacOSX)] - [Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/1687", FlakyOn.AzP.Linux)] public async Task ShutdownTestRun() { await ExecuteShutdownTest(nameof(ShutdownTestRun), "Run"); From e39b4b5c3e5a42fe7a28319e3339a2a561bb6160 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 29 May 2019 17:58:57 -0700 Subject: [PATCH 42/95] Rename CacheEntryHelpers .cs to CacheEntryHelpers.cs (#10633) --- .../CacheEntry/{CacheEntryHelpers .cs => CacheEntryHelpers.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Middleware/ResponseCaching/src/Internal/CacheEntry/{CacheEntryHelpers .cs => CacheEntryHelpers.cs} (100%) diff --git a/src/Middleware/ResponseCaching/src/Internal/CacheEntry/CacheEntryHelpers .cs b/src/Middleware/ResponseCaching/src/Internal/CacheEntry/CacheEntryHelpers.cs similarity index 100% rename from src/Middleware/ResponseCaching/src/Internal/CacheEntry/CacheEntryHelpers .cs rename to src/Middleware/ResponseCaching/src/Internal/CacheEntry/CacheEntryHelpers.cs From abd70031cb4cde06299fe980c08dc54282293c7b Mon Sep 17 00:00:00 2001 From: Alessio Franceschelli Date: Wed, 29 May 2019 00:10:08 +0100 Subject: [PATCH 43/95] HeaderPropagation middleware: small cleanup - Removed MessageHandler DI registration as not needed. - Fixed two parameters XML descriptions. - Cleaned up usings in the example. --- .../samples/HeaderPropagationSample/Program.cs | 1 - .../samples/HeaderPropagationSample/Startup.cs | 3 --- .../HeaderPropagationServiceCollectionExtensions.cs | 1 - .../HeaderPropagation/src/HeaderPropagationEntryCollection.cs | 4 ++-- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Program.cs b/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Program.cs index 648d98e459..63aca35aa6 100644 --- a/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Program.cs +++ b/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Program.cs @@ -1,4 +1,3 @@ -using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; diff --git a/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Startup.cs b/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Startup.cs index 6787910f19..00138f6efe 100644 --- a/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Startup.cs +++ b/src/Middleware/HeaderPropagation/samples/HeaderPropagationSample/Startup.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.HeaderPropagation; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; diff --git a/src/Middleware/HeaderPropagation/src/DependencyInjection/HeaderPropagationServiceCollectionExtensions.cs b/src/Middleware/HeaderPropagation/src/DependencyInjection/HeaderPropagationServiceCollectionExtensions.cs index b175cd36e9..ab3d60cc0d 100644 --- a/src/Middleware/HeaderPropagation/src/DependencyInjection/HeaderPropagationServiceCollectionExtensions.cs +++ b/src/Middleware/HeaderPropagation/src/DependencyInjection/HeaderPropagationServiceCollectionExtensions.cs @@ -23,7 +23,6 @@ namespace Microsoft.Extensions.DependencyInjection } services.TryAddSingleton(); - services.TryAddTransient(); return services; } diff --git a/src/Middleware/HeaderPropagation/src/HeaderPropagationEntryCollection.cs b/src/Middleware/HeaderPropagation/src/HeaderPropagationEntryCollection.cs index c0d2617674..eac5086275 100644 --- a/src/Middleware/HeaderPropagation/src/HeaderPropagationEntryCollection.cs +++ b/src/Middleware/HeaderPropagation/src/HeaderPropagationEntryCollection.cs @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.HeaderPropagation /// The header name to be propagated. /// /// A filter delegate that can be used to transform the header value. - /// . + /// See . /// public void Add(string headerName, Func valueFilter) { @@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.HeaderPropagation /// /// /// A filter delegate that can be used to transform the header value. - /// . + /// See . /// public void Add( string inboundHeaderName, From 0b54783877cca229e2fc3f772ce7257cf0b3814f Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 29 May 2019 21:15:29 -0700 Subject: [PATCH 44/95] [Helix] Try running node services tests on helix again (#9046) --- eng/helix/content/InstallNode.ps1 | 71 +++++++++++++++++++ eng/helix/content/installnode.sh | 29 ++++++++ eng/helix/content/runtests.cmd | 2 +- eng/helix/content/runtests.sh | 2 +- eng/targets/Helix.props | 9 +++ ...osoft.AspNetCore.NodeServices.Tests.csproj | 9 +-- .../NodeServices/test/NodeServicesTest.cs | 3 +- 7 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 eng/helix/content/InstallNode.ps1 create mode 100644 eng/helix/content/installnode.sh diff --git a/eng/helix/content/InstallNode.ps1 b/eng/helix/content/InstallNode.ps1 new file mode 100644 index 0000000000..862e612582 --- /dev/null +++ b/eng/helix/content/InstallNode.ps1 @@ -0,0 +1,71 @@ + <# + .SYNOPSIS + Installs NodeJs from http://nodejs.org/dist on a machine + .DESCRIPTION + This script installs NodeJs from http://nodejs.org/dist on a machine. + .PARAMETER Version + The version of NodeJS to install. + .PARAMETER InstallDir + The directory to install NodeJS to. + .LINK + https://nodejs.org/en/ + #> +param( + [Parameter(Mandatory = $true)] + $Version, + + [Parameter(Mandatory = $true)] + $InstallDir +) + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' # Workaround PowerShell/PowerShell#2138 + +Set-StrictMode -Version 1 + +if (Get-Command "node.exe" -ErrorAction SilentlyContinue) +{ + Write-Host "Found node.exe in PATH" + exit +} + +if (Test-Path "$output_dir\node.exe") +{ + Write-Host "Node.exe found at $output_dir" + exit +} + +$nodeFile="node-v$Version-win-x64" +$url="http://nodejs.org/dist/v$Version/$nodeFile.zip" +Write-Host "Starting download of NodeJs ${Version} from $url" +Invoke-WebRequest -UseBasicParsing -Uri "$url" -OutFile "nodejs.zip" +Write-Host "Done downloading NodeJS ${Version}" + +$tempPath = [System.IO.Path]::GetTempPath() +$tempDir = Join-Path $tempPath nodejs +New-Item -Path "$tempDir" -ItemType "directory" -Force +Write-Host "Extracting to $tempDir" + +if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { + # Use built-in commands where possible as they are cross-plat compatible + Microsoft.PowerShell.Archive\Expand-Archive -Path "nodejs.zip" -DestinationPath $tempDir +} +else { + # Fallback to old approach for old installations of PowerShell + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory("nodejs.zip", $tempDir) +} + +Write-Host "Expanded NodeJs" +New-Item -Path "$InstallDir" -ItemType "directory" -Force +Write-Host "Copying $tempDir\$nodeFile\node.exe to $InstallDir" +Copy-Item "$tempDir\$nodeFile\node.exe" "$InstallDir\node.exe" + +if (Test-Path "$InstallDir\node.exe") +{ + Write-Host "Node.exe copied to $InstallDir" +} +else +{ + Write-Host "Node.exe not copied to $InstallDir" +} diff --git a/eng/helix/content/installnode.sh b/eng/helix/content/installnode.sh new file mode 100644 index 0000000000..5a72cca423 --- /dev/null +++ b/eng/helix/content/installnode.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Cause the script to fail if any subcommand fails +set -e + +if type -P "node" &>/dev/null; then + echo "node is in \$PATH" + exit +fi + +node_version=$1 +osname=`uname -s` +echo $osname +if [ "$osname" = "Darwin" ]; then + platformarch='darwin-x64' +else + platformarch='linux-x64' +fi +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +output_dir="$DIR/node" +url="http://nodejs.org/dist/v$node_version/node-v$node_version-$platformarch.tar.gz" +tmp="$(mktemp -d -t install-node.XXXXXX)" +trap "rm -rf $tmp" EXIT +cd "$tmp" +curl -Lsfo $(basename $url) "$url" +echo "Installing node from $(basename $url) $url" +mkdir $output_dir +echo "Unpacking to $output_dir" +tar --strip-components 1 -xzf "node-v$node_version-$platformarch.tar.gz" --no-same-owner --directory "$output_dir" diff --git a/eng/helix/content/runtests.cmd b/eng/helix/content/runtests.cmd index 8657c61ec1..8344fe5a66 100644 --- a/eng/helix/content/runtests.cmd +++ b/eng/helix/content/runtests.cmd @@ -14,7 +14,7 @@ set DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 set DOTNET_MULTILEVEL_LOOKUP=0 set DOTNET_CLI_HOME=%HELIX_CORRELATION_PAYLOAD%\home -set PATH=%DOTNET_ROOT%;%PATH% +set PATH=%DOTNET_ROOT%;%PATH%;%HELIX_CORRELATION_PAYLOAD%\node\bin powershell.exe -NoProfile -ExecutionPolicy unrestricted -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -useb 'https://dot.net/v1/dotnet-install.ps1'))) -Architecture x64 -Version %sdkVersion% -InstallDir %DOTNET_ROOT%" powershell.exe -NoProfile -ExecutionPolicy unrestricted -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -useb 'https://dot.net/v1/dotnet-install.ps1'))) -Architecture x64 -Runtime dotnet -Version %runtimeVersion% -InstallDir %DOTNET_ROOT%" diff --git a/eng/helix/content/runtests.sh b/eng/helix/content/runtests.sh index b3a786ee05..e864f097de 100644 --- a/eng/helix/content/runtests.sh +++ b/eng/helix/content/runtests.sh @@ -16,7 +16,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export DOTNET_ROOT="$DIR/.dotnet$RANDOM" # Ensure dotnet comes first on PATH -export PATH="$DOTNET_ROOT:$PATH" +export PATH="$DOTNET_ROOT:$PATH:$DIR/node/bin" # Prevent fallback to global .NET locations. This ensures our tests use the shared frameworks we specify and don't rollforward to something else that might be installed on the machine export DOTNET_MULTILEVEL_LOOKUP=0 diff --git a/eng/targets/Helix.props b/eng/targets/Helix.props index b2f297fe63..c6b1b68cd2 100644 --- a/eng/targets/Helix.props +++ b/eng/targets/Helix.props @@ -17,6 +17,7 @@ $(MSBuildProjectName)/$(TargetFramework) false true + 10.15.3 @@ -35,4 +36,12 @@ + + + + + + + + diff --git a/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj b/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj index 0b8c614e31..7d0bed922f 100644 --- a/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj +++ b/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj @@ -2,19 +2,14 @@ netcoreapp3.0 - - - - false - - - + true + diff --git a/src/Middleware/NodeServices/test/NodeServicesTest.cs b/src/Middleware/NodeServices/test/NodeServicesTest.cs index a5012ea8aa..f851e08d0c 100644 --- a/src/Middleware/NodeServices/test/NodeServicesTest.cs +++ b/src/Middleware/NodeServices/test/NodeServicesTest.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.NodeServices.HostingModels; using Microsoft.Extensions.DependencyInjection; using System; +using System.IO; using System.Threading.Tasks; using Xunit; @@ -115,7 +116,7 @@ namespace Microsoft.AspNetCore.NodeServices } private static string ModulePath(string testModuleName) - => $"../../../js/{testModuleName}"; + => Path.Combine(AppContext.BaseDirectory, "js", testModuleName); public void Dispose() { From bf5e3024a5090e0bcaf939090fa97dcc3ec1f1b1 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Thu, 30 May 2019 08:19:01 -0700 Subject: [PATCH 45/95] Update SignalR Java Client metadata (#10640) The repo URLs were pointing at the old one :) --- src/SignalR/clients/java/signalr/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SignalR/clients/java/signalr/build.gradle b/src/SignalR/clients/java/signalr/build.gradle index 20c3661bda..61b170a40d 100644 --- a/src/SignalR/clients/java/signalr/build.gradle +++ b/src/SignalR/clients/java/signalr/build.gradle @@ -83,7 +83,7 @@ task generatePOM { project { inceptionYear '2018' description 'ASP.NET Core SignalR Client for Java applications' - url 'https://github.com/aspnet/SignalR' + url 'https://github.com/aspnet/AspNetCore' name groupId + ':' + artifactId licenses { license { @@ -93,9 +93,9 @@ task generatePOM { } } scm { - connection 'scm:git:git://github.com/aspnet/SignalR.git' - developerConnection 'scm:git:git://github.com/aspnet/SignalR.git' - url 'http://github.com/aspnet/SignalR/tree/master' + connection 'scm:git:git://github.com/aspnet/AspNetCore.git' + developerConnection 'scm:git:git://github.com/aspnet/AspNetCore.git' + url 'http://github.com/aspnet/AspNetCore/tree/master' } developers { developer { From 6f7a8ec4c06759c77edad47acb588daac726ae78 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 30 May 2019 16:19:26 +0100 Subject: [PATCH 46/95] Ensure PlatformBenchmark is compiled for .NET Core 3.0 (#9619) --- .../BenchmarkApplication.HttpConnection.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs b/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs index 1d5cc6f55d..bda3e005a8 100644 --- a/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs +++ b/src/Servers/Kestrel/perf/PlatformBenchmarks/BenchmarkApplication.HttpConnection.cs @@ -125,11 +125,9 @@ namespace PlatformBenchmarks { } -#if NETCOREAPP3_0 public void OnHeadersComplete() { } -#endif public async ValueTask OnReadCompletedAsync() { @@ -182,9 +180,10 @@ namespace PlatformBenchmarks public void OnStartLine(HttpMethod method, HttpVersion version, Span target, Span path, Span query, Span customMethod, bool pathEncoded) => RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded); -#if NETCOREAPP3_0 public void OnHeadersComplete() => RequestHandler.OnHeadersComplete(); +#if !NETCOREAPP3_0 +#error This is a .NET Core 3.0 application and needs to be compiled for netcoreapp3.0 #endif } } From 08ed1afbbc1d1e11115eafcf4bd2c771c00ffd90 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 30 May 2019 09:29:48 -0700 Subject: [PATCH 47/95] Use SemVer2 in packaging (#10634) --- version.props | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/version.props b/version.props index 8a53be49d8..d7a8bb996d 100644 --- a/version.props +++ b/version.props @@ -34,13 +34,11 @@ <_BuildNumberShortDate>$([MSBuild]::Add($([MSBuild]::Add($([MSBuild]::Multiply($(_BuildNumberYY), 1000)), $([MSBuild]::Multiply($(_BuildNumberMM), 50)))), $(_BuildNumberDD))) $([System.Convert]::ToInt32($(_BuildNumberR))) - $(VersionSuffixBuildOfTheDay.PadLeft(2, '0')) - - $(_BuildNumberShortDate)-$(VersionSuffixBuildOfTheDayPadded) + $(_BuildNumberShortDate).$(VersionSuffixBuildOfTheDay) $(_BuildNumberShortDate) - $(PreReleaseLabel)-$(BuildNumberSuffix) + $(PreReleaseLabel).$(BuildNumberSuffix) From 76b8ca56af5afa8ff85bdcf8e180fcae89027b13 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Thu, 30 May 2019 12:45:56 -0700 Subject: [PATCH 48/95] Use web.config environment variables to read if in Development (#10642) --- .../AspNetCore/ShimOptions.cpp | 16 +++++-- .../CommonLib/ConfigurationSection.h | 3 ++ .../CommonLib/Environment.cpp | 4 +- .../Inprocess/StartupTests.cs | 46 ++++++++++++++++++- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp index fa1282cdb6..208d4a1eaa 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/AspNetCore/ShimOptions.cpp @@ -43,11 +43,17 @@ ShimOptions::ShimOptions(const ConfigurationSource &configurationSource) : m_struStdoutLogFile = section->GetRequiredString(CS_ASPNETCORE_STDOUT_LOG_FILE); m_fDisableStartupPage = section->GetRequiredBool(CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE); - // This will not include environment variables defined in the web.config. - // Reading environment variables can be added here, but it adds more code to the shim. - const auto detailedErrors = Environment::GetEnvironmentVariableValue(L"ASPNETCORE_DETAILEDERRORS").value_or(L""); - const auto aspnetCoreEnvironment = Environment::GetEnvironmentVariableValue(L"ASPNETCORE_ENVIRONMENT").value_or(L""); - const auto dotnetEnvironment = Environment::GetEnvironmentVariableValue(L"DOTNET_ENVIRONMENT").value_or(L""); + auto environmentVariables = section->GetMap(CS_ASPNETCORE_ENVIRONMENT_VARIABLES); + + // Indexing into environment variable map will add a default entry if none is present + // This is okay here as we throw away the map shortly after. + // Process set environment variables are prioritized over web config variables. + const auto detailedErrors = Environment::GetEnvironmentVariableValue(CS_ASPNETCORE_DETAILEDERRORS) + .value_or(environmentVariables[CS_ASPNETCORE_DETAILEDERRORS]); + const auto aspnetCoreEnvironment = Environment::GetEnvironmentVariableValue(CS_ASPNETCORE_ENVIRONMENT) + .value_or(environmentVariables[CS_ASPNETCORE_ENVIRONMENT]); + const auto dotnetEnvironment = Environment::GetEnvironmentVariableValue(CS_DOTNET_ENVIRONMENT) + .value_or(environmentVariables[CS_DOTNET_ENVIRONMENT]); auto detailedErrorsEnabled = equals_ignore_case(L"1", detailedErrors) || equals_ignore_case(L"true", detailedErrors); auto aspnetCoreEnvironmentEnabled = equals_ignore_case(L"Development", aspnetCoreEnvironment); diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index 446f8c7d4b..ae02dd3faa 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -30,6 +30,9 @@ #define CS_ENABLED L"enabled" #define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook" #define CS_ASPNETCORE_HANDLER_STACK_SIZE L"stackSize" +#define CS_ASPNETCORE_DETAILEDERRORS L"ASPNETCORE_DETAILEDERRORS" +#define CS_ASPNETCORE_ENVIRONMENT L"ASPNETCORE_ENVIRONMENT" +#define CS_DOTNET_ENVIRONMENT L"DOTNET_ENVIRONMENT" class ConfigurationSection: NonCopyable { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp index 118a67198b..fb63cdb20c 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp @@ -47,10 +47,10 @@ Environment::GetEnvironmentVariableValue(const std::wstring & str) } else if (requestedSize == 1) { - // String just contains a nullcharacter, return empty string + // String just contains a nullcharacter, return nothing // GetEnvironmentVariableW has inconsistent behavior when returning size for an empty // environment variable. - return L""; + return std::nullopt; } std::wstring expandedStr; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index d6ffd044c9..686164f3cf 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -365,6 +365,26 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.InProcessFailedToFindNativeDependencies(deploymentResult), Logger); } + [ConditionalFact] + public async Task TargedDifferenceSharedFramework_FailedToFindNativeDependenciesErrorInResponse() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(Fixture.InProcessTestSite); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = "TRUE"; + var deploymentResult = await DeployAsync(deploymentParameters); + + Helpers.ModifyFrameworkVersionInRuntimeConfig(deploymentResult); + if (DeployerSelector.HasNewShim) + { + await AssertSiteFailsToStartWithInProcessStaticContent(deploymentResult, "HTTP Error 500.31 - ANCM Failed to Find Native Dependencies"); + var responseString = await deploymentResult.HttpClient.GetStringAsync("/HelloWorld"); + Assert.Contains("The specified framework 'Microsoft.NETCore.App', version '2.9.9'", responseString); + } + else + { + await AssertSiteFailsToStartWithInProcessStaticContent(deploymentResult); + } + } + [ConditionalFact] public async Task RemoveInProcessReference_FailedToFindRequestHandler() { @@ -662,7 +682,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess deploymentParameters.TransformArguments((a, _) => $"{a} Throw"); // Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true - deploymentParameters.EnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = ""; + deploymentParameters.EnvironmentVariables.Remove("ASPNETCORE_DETAILEDERRORS"); deploymentParameters.EnvironmentVariables[environmentVariable] = value; var deploymentResult = await DeployAsync(deploymentParameters); @@ -678,6 +698,30 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess VerifyDotnetRuntimeEventLog(deploymentResult); } + [ConditionalFact] + [RequiresNewHandler] + public async Task ExceptionIsLoggedToEventLogAndPutInResponseWhenDeveloperExceptionPageIsEnabledViaWebConfig() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.TransformArguments((a, _) => $"{a} Throw"); + + // Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true + deploymentParameters.EnvironmentVariables.Remove("ASPNETCORE_DETAILEDERRORS"); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = "TRUE"; + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(result.IsSuccessStatusCode); + + var content = await result.Content.ReadAsStringAsync(); + Assert.Contains("InvalidOperationException", content); + Assert.Contains("TestSite.Program.Main", content); + + StopServer(); + + VerifyDotnetRuntimeEventLog(deploymentResult); + } + [ConditionalTheory] [RequiresIIS(IISCapability.PoolEnvironmentVariables)] [RequiresNewHandler] From 11061e412e81dcb3fb1b8cd63971d771306265cc Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 30 May 2019 13:27:09 -0700 Subject: [PATCH 49/95] EndpointMiddleware does not invoke endpoint (#10650) * EndpointMiddleware does not invoke endpoint Fixes: #10649 The bug is that the endpoint delegate is not invoked when SuppressCheckForUnhandledSecurityMetadata is set to true. This option is provided so that can you suppress the security checks done by routing, but right now what it does is suppress the entire middleware. We had tests for the supression cases, but they didn't actually validate that the middleware did any work, just that we don't throw. Fixed that. * harden tests --- src/Http/Routing/src/EndpointMiddleware.cs | 25 ++- .../test/UnitTests/EndpointMiddlewareTest.cs | 172 +++++++++++++----- 2 files changed, 134 insertions(+), 63 deletions(-) diff --git a/src/Http/Routing/src/EndpointMiddleware.cs b/src/Http/Routing/src/EndpointMiddleware.cs index 97e56a04e7..b481c1bdeb 100644 --- a/src/Http/Routing/src/EndpointMiddleware.cs +++ b/src/Http/Routing/src/EndpointMiddleware.cs @@ -36,22 +36,19 @@ namespace Microsoft.AspNetCore.Routing var endpoint = httpContext.Features.Get()?.Endpoint; if (endpoint?.RequestDelegate != null) { - if (_routeOptions.SuppressCheckForUnhandledSecurityMetadata) + if (!_routeOptions.SuppressCheckForUnhandledSecurityMetadata) { - // User opted out of this check. - return Task.CompletedTask; - } + if (endpoint.Metadata.GetMetadata() != null && + !httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey)) + { + ThrowMissingAuthMiddlewareException(endpoint); + } - if (endpoint.Metadata.GetMetadata() != null && - !httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey)) - { - ThrowMissingAuthMiddlewareException(endpoint); - } - - if (endpoint.Metadata.GetMetadata() != null && - !httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey)) - { - ThrowMissingCorsMiddlewareException(endpoint); + if (endpoint.Metadata.GetMetadata() != null && + !httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey)) + { + ThrowMissingCorsMiddlewareException(endpoint); + } } Log.ExecutingEndpoint(_logger, endpoint); diff --git a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs index dc7dc3d87c..1467425222 100644 --- a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs +++ b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs @@ -25,8 +25,10 @@ namespace Microsoft.AspNetCore.Routing var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); + var calledNext = false; RequestDelegate next = (c) => { + calledNext = true; return Task.CompletedTask; }; @@ -35,7 +37,8 @@ namespace Microsoft.AspNetCore.Routing // Act await middleware.Invoke(httpContext); - // Assert - does not throw + // Assert + Assert.True(calledNext); } [Fact] @@ -46,37 +49,10 @@ namespace Microsoft.AspNetCore.Routing httpContext.RequestServices = new ServiceProvider(); httpContext.SetEndpoint(null); + var calledNext = false; RequestDelegate next = (c) => { - return Task.CompletedTask; - }; - - var middleware = new EndpointMiddleware(NullLogger.Instance, next, RouteOptions); - - // Act - await middleware.Invoke(httpContext); - - // Assert - does not throw - } - - [Fact] - public async Task Invoke_WithEndpoint_InvokesDelegate() - { - // Arrange - var httpContext = new DefaultHttpContext(); - httpContext.RequestServices = new ServiceProvider(); - - var invoked = false; - RequestDelegate endpointFunc = (c) => - { - invoked = true; - return Task.CompletedTask; - }; - - httpContext.SetEndpoint(new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test")); - - RequestDelegate next = (c) => - { + calledNext = true; return Task.CompletedTask; }; @@ -86,7 +62,37 @@ namespace Microsoft.AspNetCore.Routing await middleware.Invoke(httpContext); // Assert - Assert.True(invoked); + Assert.True(calledNext); + } + + [Fact] + public async Task Invoke_WithEndpoint_InvokesDelegate() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = new ServiceProvider(); + + var calledEndpoint = false; + RequestDelegate endpointFunc = (c) => + { + calledEndpoint = true; + return Task.CompletedTask; + }; + + httpContext.SetEndpoint(new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test")); + + RequestDelegate next = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; + + var middleware = new EndpointMiddleware(NullLogger.Instance, next, RouteOptions); + + // Act + await middleware.Invoke(httpContext); + + // Assert + Assert.True(calledEndpoint); } [Fact] @@ -101,9 +107,14 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + RequestDelegate throwIfCalled = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); + httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(Mock.Of()), "Test")); + + var middleware = new EndpointMiddleware(NullLogger.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync(() => middleware.Invoke(httpContext)); @@ -121,16 +132,29 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + var calledEndpoint = false; + RequestDelegate endpointFunc = (c) => + { + calledEndpoint = true; + return Task.CompletedTask; + }; + + httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of()), "Test")); httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true; - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); + RequestDelegate next = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; - // Act & Assert + var middleware = new EndpointMiddleware(NullLogger.Instance, next, RouteOptions); + + // Act await middleware.Invoke(httpContext); - // If we got this far, we can sound the everything's OK alarm. + // Assert + Assert.True(calledEndpoint); } [Fact] @@ -142,13 +166,29 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + var calledEndpoint = false; + RequestDelegate endpointFunc = (c) => + { + calledEndpoint = true; + return Task.CompletedTask; + }; + + httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of()), "Test")); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, routeOptions); - // Act & Assert + RequestDelegate next = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; + + var middleware = new EndpointMiddleware(NullLogger.Instance, next, routeOptions); + + // Act await middleware.Invoke(httpContext); + + // Assert + Assert.True(calledEndpoint); } [Fact] @@ -163,9 +203,14 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + RequestDelegate throwIfCalled = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); + httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(Mock.Of()), "Test")); + + var middleware = new EndpointMiddleware(NullLogger.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync(() => middleware.Invoke(httpContext)); @@ -183,16 +228,29 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + var calledEndpoint = false; + RequestDelegate endpointFunc = (c) => + { + calledEndpoint = true; + return Task.CompletedTask; + }; + + httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of()), "Test")); httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true; - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); + RequestDelegate next = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; - // Act & Assert + var middleware = new EndpointMiddleware(NullLogger.Instance, next, RouteOptions); + + // Act await middleware.Invoke(httpContext); - // If we got this far, we can sound the everything's OK alarm. + // Assert + Assert.True(calledEndpoint); } [Fact] @@ -204,13 +262,29 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.SetEndpoint(new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test")); + var calledEndpoint = false; + RequestDelegate endpointFunc = (c) => + { + calledEndpoint = true; + return Task.CompletedTask; + }; + + httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of()), "Test")); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); - var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, routeOptions); - // Act & Assert + RequestDelegate next = (c) => + { + throw new InvalidTimeZoneException("Should not be called"); + }; + + var middleware = new EndpointMiddleware(NullLogger.Instance, next, routeOptions); + + // Act await middleware.Invoke(httpContext); + + // Assert + Assert.True(calledEndpoint); } private class ServiceProvider : IServiceProvider From 98d02bb61774731224703a8026bbb3699fe18265 Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Thu, 30 May 2019 14:00:12 -0700 Subject: [PATCH 50/95] Don't inject ANCM managed exception page into the static page (#10659) --- .../AspNetCoreModuleV2/CommonLib/file_utility.cpp | 4 ++-- .../InProcessRequestHandler/dllmain.cpp | 14 +++++++++----- .../Inprocess/StartupTests.cs | 1 + 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/file_utility.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/file_utility.cpp index fb10427e3f..b2c7adce61 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/file_utility.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/file_utility.cpp @@ -168,9 +168,9 @@ Finished: return hr; } -std::string FILE_UTILITY::GetHtml(HMODULE module, int page, USHORT statusCode, USHORT subStatusCode, const std::string& speicificReasonPhrase, const std::string& solution) +std::string FILE_UTILITY::GetHtml(HMODULE module, int page, USHORT statusCode, USHORT subStatusCode, const std::string& specificReasonPhrase, const std::string& solution) { - return GetHtml(module, page, statusCode, subStatusCode, speicificReasonPhrase, solution, std::string()); + return GetHtml(module, page, statusCode, subStatusCode, specificReasonPhrase, solution, std::string()); } std::string diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp index cd9261a56c..664d8130d2 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp @@ -134,17 +134,21 @@ CreateApplication( std::unique_ptr options; THROW_IF_FAILED(InProcessOptions::Create(*pServer, pSite, *pHttpApplication, options)); // Set the currently running application to a fake application that returns startup exceptions. - auto pErrorApplication = std::make_unique(*pServer, - *pHttpApplication, - options->QueryDisableStartUpErrorPage(), - hr, + auto content = !g_errorPageContent.empty() ? + g_errorPageContent : FILE_UTILITY::GetHtml(g_hServerModule, IN_PROCESS_RH_STATIC_HTML, 500i16, 30i16, "ANCM In-Process Start Failure", "
  • The application failed to start
  • The application started but then stopped
  • The application started but threw an exception during startup
", - g_errorPageContent), + ""); + + auto pErrorApplication = std::make_unique(*pServer, + *pHttpApplication, + options->QueryDisableStartUpErrorPage(), + hr, + content, 500i16, 30i16, "Internal Server Error"); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 686164f3cf..572b85d823 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -740,6 +740,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess Assert.Contains("InvalidOperationException", content); Assert.Contains("TestSite.Program.Main", content); Assert.Contains("From Configure", content); + Assert.DoesNotContain("ANCM In-Process Start Failure", content); StopServer(); From 18a230737a3ebf02b44db2c24a14399f41eadc4d Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 30 May 2019 14:15:17 -0700 Subject: [PATCH 51/95] Remove Newtonsoft.Json (#10630) --- .../Microsoft.DotNet.Web.ProjectTemplates.csproj | 1 - .../RazorComponentsWeb-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/StarterWeb-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/StarterWeb-FSharp.fsproj.in | 1 - .../Web.ProjectTemplates/WebApi-CSharp.csproj.in | 1 - .../Web.ProjectTemplates/WebApi-FSharp.fsproj.in | 4 ---- .../content/RazorComponentsWeb-CSharp/Startup.cs | 3 +-- .../content/RazorPagesWeb-CSharp/Startup.cs | 6 ++---- .../content/StarterWeb-CSharp/Startup.cs | 6 ++---- .../content/StarterWeb-FSharp/Startup.fs | 2 +- .../Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs | 3 +-- .../Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs | 2 +- .../Web.Spa.ProjectTemplates/Angular-CSharp.csproj.in | 1 - .../Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj | 1 - .../Web.Spa.ProjectTemplates/React-CSharp.csproj.in | 1 - .../Web.Spa.ProjectTemplates/ReactRedux-CSharp.csproj.in | 1 - .../content/Angular-CSharp/Startup.cs | 3 +-- .../content/React-CSharp/Startup.cs | 3 +-- .../content/ReactRedux-CSharp/Startup.cs | 3 +-- 20 files changed, 11 insertions(+), 34 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj index c138d59d9f..d32b7e75d4 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj @@ -32,7 +32,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in index 9b6510bf6f..5218e835bd 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/RazorComponentsWeb-CSharp.csproj.in @@ -23,7 +23,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in index 1b8a5103ff..3e14a0a1e0 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/RazorPagesWeb-CSharp.csproj.in @@ -22,7 +22,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in index c6fbbb7bf0..563b2c2927 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-CSharp.csproj.in @@ -22,7 +22,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-FSharp.fsproj.in b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-FSharp.fsproj.in index 40c54ea3be..4192478d05 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-FSharp.fsproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/StarterWeb-FSharp.fsproj.in @@ -14,7 +14,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in index bc72c6f34f..e146f8753d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in @@ -12,7 +12,6 @@ - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-FSharp.fsproj.in b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-FSharp.fsproj.in index ad52e94ac5..36af1dc9f5 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-FSharp.fsproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-FSharp.fsproj.in @@ -12,8 +12,4 @@ - - - - diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs index 828c349c43..ff517870d6 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs @@ -117,8 +117,7 @@ namespace RazorComponentsWeb_CSharp .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); - }) - .AddNewtonsoftJson(); + }); #endif services.AddRazorPages(); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs index 0b58b5b6b0..ea58c1f885 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Startup.cs @@ -121,11 +121,9 @@ namespace Company.WebApplication1 .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); - }) - .AddNewtonsoftJson(); + }); #else - services.AddRazorPages() - .AddNewtonsoftJson(); + services.AddRazorPages(); #endif } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs index e546c74fc2..0638b76cab 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Startup.cs @@ -121,11 +121,9 @@ namespace Company.WebApplication1 .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); - }) - .AddNewtonsoftJson(); + }); #else - services.AddControllersWithViews() - .AddNewtonsoftJson(); + services.AddControllersWithViews(); #endif services.AddRazorPages(); } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs index 91d078759a..607ec13b55 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Startup.fs @@ -22,7 +22,7 @@ type Startup private () = // This method gets called by the runtime. Use this method to add services to the container. member this.ConfigureServices(services: IServiceCollection) = // Add framework services. - services.AddControllersWithViews().AddNewtonsoftJson().AddRazorRuntimeCompilation() |> ignore + services.AddControllersWithViews().AddRazorRuntimeCompilation() |> ignore services.AddRazorPages() |> ignore // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 55f0049079..aeee9aa54b 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -43,8 +43,7 @@ namespace Company.WebApplication1 services.AddAuthentication(AzureADB2CDefaults.BearerAuthenticationScheme) .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options)); #endif - services.AddControllers() - .AddNewtonsoftJson(); + services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs index 3df5e59353..d1611219ff 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-FSharp/Startup.fs @@ -22,7 +22,7 @@ type Startup private () = // This method gets called by the runtime. Use this method to add services to the container. member this.ConfigureServices(services: IServiceCollection) = // Add framework services. - services.AddControllers().AddNewtonsoftJson() |> ignore + services.AddControllers() |> ignore // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. member this.Configure(app: IApplicationBuilder, env: IWebHostEnvironment) = diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Angular-CSharp.csproj.in b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Angular-CSharp.csproj.in index 082a437cb8..bd29440fcf 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Angular-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Angular-CSharp.csproj.in @@ -15,7 +15,6 @@
- diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj index ff584492e7..5929c7cf68 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj @@ -22,7 +22,6 @@ - diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/React-CSharp.csproj.in b/src/ProjectTemplates/Web.Spa.ProjectTemplates/React-CSharp.csproj.in index 5b930ebba1..dc4897f498 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/React-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/React-CSharp.csproj.in @@ -12,7 +12,6 @@ - diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/ReactRedux-CSharp.csproj.in b/src/ProjectTemplates/Web.Spa.ProjectTemplates/ReactRedux-CSharp.csproj.in index eee417f529..71a0a80686 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/ReactRedux-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/ReactRedux-CSharp.csproj.in @@ -12,7 +12,6 @@ - diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Startup.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Startup.cs index c00c010cb7..b5d6e749b9 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/Angular-CSharp/Startup.cs @@ -53,8 +53,7 @@ namespace Company.WebApplication1 services.AddAuthentication() .AddIdentityServerJwt(); #endif - services.AddMvc(options => options.EnableEndpointRouting = false) - .AddNewtonsoftJson(); + services.AddMvc(options => options.EnableEndpointRouting = false); // In production, the Angular files will be served from this directory services.AddSpaStaticFiles(configuration => diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Startup.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Startup.cs index c49c59d497..2303f02e1b 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/React-CSharp/Startup.cs @@ -53,8 +53,7 @@ namespace Company.WebApplication1 services.AddAuthentication() .AddIdentityServerJwt(); #endif - services.AddMvc(options => options.EnableEndpointRouting = false) - .AddNewtonsoftJson(); + services.AddMvc(options => options.EnableEndpointRouting = false); // In production, the React files will be served from this directory services.AddSpaStaticFiles(configuration => diff --git a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/Startup.cs b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/Startup.cs index f0e7cf2f43..620d39f9af 100644 --- a/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.Spa.ProjectTemplates/content/ReactRedux-CSharp/Startup.cs @@ -23,8 +23,7 @@ namespace Company.WebApplication1 // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc(options => options.EnableEndpointRouting = false) - .AddNewtonsoftJson(); + services.AddMvc(options => options.EnableEndpointRouting = false); // In production, the React files will be served from this directory services.AddSpaStaticFiles(configuration => From 9ebabc133795a573d3012f8f0e2746c104a9e96f Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Thu, 30 May 2019 13:20:29 -0700 Subject: [PATCH 52/95] Update Identity tests for EF breaking changes Specifically: * Removal of Relational() * Query changes There is some kind of flakiness still impacting these tests. It may be a race condition in EF query generation, or it may be an issue with the test infrastructure Also, one database error page test is currently disabled. --- .../EntityFrameworkCore/src/UserOnlyStore.cs | 7 +- .../EntityFrameworkCore/src/UserStore.cs | 7 +- .../test/EF.InMemory.Test/RoleStoreTest.cs | 3 - .../test/EF.Test/CustomPocoTest.cs | 67 +++++++++-------- .../test/EF.Test/DbUtil.cs | 6 +- .../test/EF.Test/DefaultPocoTest.cs | 3 +- .../test/EF.Test/UserStoreTest.cs | 74 +++++++++++++------ .../DatabaseErrorPageMiddlewareTest.cs | 45 +++++------ .../FunctionalTests/SqlServerTestStore.cs | 11 +-- .../DatabaseErrorPageSampleTest.cs | 4 +- 10 files changed, 126 insertions(+), 101 deletions(-) diff --git a/src/Identity/EntityFrameworkCore/src/UserOnlyStore.cs b/src/Identity/EntityFrameworkCore/src/UserOnlyStore.cs index 9358ee3fce..c66b73f267 100644 --- a/src/Identity/EntityFrameworkCore/src/UserOnlyStore.cs +++ b/src/Identity/EntityFrameworkCore/src/UserOnlyStore.cs @@ -250,6 +250,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken); } @@ -311,7 +312,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore throw new ArgumentNullException(nameof(user)); } - return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken); + // TODO: Fix once EF query works Issue #10668 + return (await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).ToListAsync(cancellationToken)).Select(c => c.ToClaim()).ToList(); } /// @@ -502,7 +504,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - return Users.SingleOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken); + // TODO: Fix once EF query works Issue #10668 + return Task.FromResult(Users.Where(u => u.NormalizedEmail == normalizedEmail).ToList().SingleOrDefault()); } /// diff --git a/src/Identity/EntityFrameworkCore/src/UserStore.cs b/src/Identity/EntityFrameworkCore/src/UserStore.cs index 28ae320a56..1038297db6 100644 --- a/src/Identity/EntityFrameworkCore/src/UserStore.cs +++ b/src/Identity/EntityFrameworkCore/src/UserStore.cs @@ -251,6 +251,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); + return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken); } @@ -444,7 +445,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore throw new ArgumentNullException(nameof(user)); } - return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken); + // TODO: Fix once EF query works Issue #10668 + return (await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).ToListAsync(cancellationToken)).Select(c => c.ToClaim()).ToList(); } /// @@ -635,7 +637,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); - return Users.SingleOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken); + // TODO: Fix once EF query works Issue #10668 + return Task.FromResult(Users.Where(u => u.NormalizedEmail == normalizedEmail).ToList().SingleOrDefault()); } /// diff --git a/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/RoleStoreTest.cs b/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/RoleStoreTest.cs index e395e6aa9b..853424f6d4 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/RoleStoreTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.InMemory.Test/RoleStoreTest.cs @@ -2,11 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Data.Common; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.Test; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Xunit; diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/CustomPocoTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/CustomPocoTest.cs index 5927810f12..9f34074bdb 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/CustomPocoTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/CustomPocoTest.cs @@ -6,19 +6,12 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; using Xunit; namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { - public class CustomPocoTest : IClassFixture + public class CustomPocoTest { - private readonly ScratchDatabaseFixture _fixture; - - public CustomPocoTest(ScratchDatabaseFixture fixture) - { - _fixture = fixture; - } public class User where TKey : IEquatable { @@ -35,30 +28,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test } - private CustomDbContext GetContext() where TKey : IEquatable - { - return DbUtil.Create>(_fixture.Connection); - } - - public CustomDbContext CreateContext(bool delete = false) where TKey : IEquatable - { - var db = GetContext(); - if (delete) - { - db.Database.EnsureDeleted(); - } - db.Database.EnsureCreated(); - return db; - } - [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.Mono)] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanUpdateNameGuid() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var oldName = Guid.NewGuid().ToString(); var user = new User { UserName = oldName, Id = Guid.NewGuid() }; db.Users.Add(user); @@ -68,6 +48,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + + db.Database.EnsureDeleted(); } } @@ -77,8 +59,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanUpdateNameString() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var oldName = Guid.NewGuid().ToString(); var user = new User { UserName = oldName, Id = Guid.NewGuid().ToString() }; db.Users.Add(user); @@ -88,6 +73,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + + db.Database.EnsureDeleted(); } } @@ -97,8 +84,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanCreateUserInt() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var user = new User(); db.Users.Add(user); await db.SaveChangesAsync(); @@ -106,6 +96,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); var fetch = db.Users.First(u => u.UserName == "Boo"); Assert.Equal(user, fetch); + + db.Database.EnsureDeleted(); } } @@ -115,8 +107,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanCreateUserIntViaSet() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var user = new User(); var users = db.Set>(); users.Add(user); @@ -125,6 +120,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); var fetch = users.First(u => u.UserName == "Boo"); Assert.Equal(user, fetch); + + db.Database.EnsureDeleted(); } } @@ -134,8 +131,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanUpdateNameInt() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var oldName = Guid.NewGuid().ToString(); var user = new User { UserName = oldName }; db.Users.Add(user); @@ -145,6 +145,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); Assert.Null(db.Users.SingleOrDefault(u => u.UserName == oldName)); Assert.Equal(user, db.Users.Single(u => u.UserName == newName)); + + db.Database.EnsureDeleted(); } } @@ -154,8 +156,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task CanUpdateNameIntWithSet() { - using (var db = CreateContext(true)) + using (var db = new CustomDbContext( + new DbContextOptionsBuilder().UseSqlite($"DataSource=D{Guid.NewGuid()}.db").Options)) { + db.Database.EnsureCreated(); + var oldName = Guid.NewGuid().ToString(); var user = new User { UserName = oldName }; db.Set>().Add(user); @@ -165,6 +170,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test await db.SaveChangesAsync(); Assert.Null(db.Set>().SingleOrDefault(u => u.UserName == oldName)); Assert.Equal(user, db.Set>().Single(u => u.UserName == newName)); + + db.Database.EnsureDeleted(); } } } diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/DbUtil.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/DbUtil.cs index 0b30e5ec68..5dd8195227 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/DbUtil.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/DbUtil.cs @@ -44,9 +44,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test { var count = 0; - foreach (var property in context.Model.GetEntityTypes().Single(e => e.Relational().TableName == table).GetProperties()) + // TODO: Use new API once EF is updated. Issue #10671 + foreach (var property in context.Model.GetEntityTypes().Single(e => (string)e.FindAnnotation("Relational:TableName")?.Value == table).GetProperties()) { - if (!columns.Contains(property.Relational().ColumnName)) + // TODO: Use new API once EF is updated. Issue #10671 + if (!columns.Contains((string)property.FindAnnotation("Relational:ColumnName")?.Value ?? property.Name)) { continue; } diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/DefaultPocoTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/DefaultPocoTest.cs index 59a8d1deb1..98f97df0d4 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/DefaultPocoTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/DefaultPocoTest.cs @@ -36,9 +36,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test _builder = new ApplicationBuilder(provider); using(var scoped = provider.GetRequiredService().CreateScope()) - using (var db = scoped.ServiceProvider.GetRequiredService()) { - db.Database.EnsureCreated(); + scoped.ServiceProvider.GetRequiredService().Database.EnsureCreated(); } } diff --git a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs index 3618b97309..7703c05996 100644 --- a/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs +++ b/src/Identity/EntityFrameworkCore/test/EF.Test/UserStoreTest.cs @@ -45,13 +45,9 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test } } - private IdentityDbContext CreateContext(bool delete = false) + private IdentityDbContext CreateContext() { var db = DbUtil.Create(_fixture.Connection); - if (delete) - { - db.Database.EnsureDeleted(); - } db.Database.EnsureCreated(); return db; } @@ -234,14 +230,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task ConcurrentUpdatesWillFail() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var user = CreateTestUser(); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateManager(db); var manager2 = CreateManager(db2); @@ -254,23 +253,28 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test user2.UserName = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1)); IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } - [ConditionalFact] + [ConditionalFact(Skip = "TODO: Fix for new EF. Issue #10670")] [FrameworkSkipCondition(RuntimeFrameworks.Mono)] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] public async Task ConcurrentUpdatesWillFailWithDetachedUser() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var user = CreateTestUser(); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateManager(db); var manager2 = CreateManager(db2); @@ -281,6 +285,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test user2.UserName = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user)); IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } @@ -290,14 +296,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task DeleteAModifiedUserWillFail() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var user = CreateTestUser(); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateManager(db); var manager2 = CreateManager(db2); @@ -309,6 +318,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test user1.UserName = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1)); IdentityResultAssert.IsFailure(await manager2.DeleteAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } @@ -318,14 +329,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task ConcurrentRoleUpdatesWillFail() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var role = new IdentityRole(Guid.NewGuid().ToString()); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateRoleManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateRoleManager(db); var manager2 = CreateRoleManager(db2); @@ -338,6 +352,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test role2.Name = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1)); IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } @@ -347,14 +363,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task ConcurrentRoleUpdatesWillFailWithDetachedRole() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var role = new IdentityRole(Guid.NewGuid().ToString()); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateRoleManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateRoleManager(db); var manager2 = CreateRoleManager(db2); @@ -366,6 +385,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test role2.Name = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role)); IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } @@ -375,14 +396,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test [OSSkipCondition(OperatingSystems.MacOSX)] public async Task DeleteAModifiedRoleWillFail() { + var options = new DbContextOptionsBuilder().UseSqlite($"Data Source=D{Guid.NewGuid()}.db").Options; var role = new IdentityRole(Guid.NewGuid().ToString()); - using (var db = CreateContext()) + using (var db = new IdentityDbContext(options)) { + db.Database.EnsureCreated(); + var manager = CreateRoleManager(db); IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); } - using (var db = CreateContext()) - using (var db2 = CreateContext()) + using (var db = new IdentityDbContext(options)) + using (var db2 = new IdentityDbContext(options)) { var manager1 = CreateRoleManager(db); var manager2 = CreateRoleManager(db2); @@ -394,6 +418,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test role1.Name = Guid.NewGuid().ToString(); IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1)); IdentityResultAssert.IsFailure(await manager2.DeleteAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure()); + + db.Database.EnsureDeleted(); } } diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs index bd38a0b981..60449f8ce8 100644 --- a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs +++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.Data.SqlClient; -using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; @@ -17,9 +15,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; @@ -160,8 +156,9 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests { db.SaveChanges(); } - catch (SqlException) + catch (Exception e) { + Assert.Equal("SqlException", e.GetType().Name); } } } @@ -351,17 +348,16 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests }); var server = new TestServer(builder); - var ex = await Assert.ThrowsAsync(async () => + try { - try - { - await server.CreateClient().GetAsync("http://localhost/"); - } - catch (InvalidOperationException exception) when (exception.InnerException != null) - { - throw exception.InnerException; - } - }); + await server.CreateClient().GetAsync("http://localhost/"); + } + catch (Exception exception) + { + Assert.True( + exception.GetType().Name == "SqlException" + || exception.InnerException?.GetType().Name == "SqlException"); + } Assert.Contains(logProvider.Logger.Messages.ToList(), m => m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_ContextNotRegistered", typeof(BloggingContext)))); @@ -406,17 +402,16 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests var server = SetupTestServer(database, logProvider); - var ex = await Assert.ThrowsAsync(async () => + try { - try - { - await server.CreateClient().GetAsync("http://localhost/"); - } - catch (InvalidOperationException exception) when (exception.InnerException != null) - { - throw exception.InnerException; - } - }); + await server.CreateClient().GetAsync("http://localhost/"); + } + catch (Exception exception) + { + Assert.True( + exception.GetType().Name == "SqlException" + || exception.InnerException?.GetType().Name == "SqlException"); + } Assert.Contains(logProvider.Logger.Messages.ToList(), m => m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_Exception"))); diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs index cfdfcf8d07..f77cc7e077 100644 --- a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs +++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Data.SqlClient; using System.Threading; using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers; using Microsoft.EntityFrameworkCore; @@ -24,13 +23,7 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests private SqlServerTestStore(string name) { - _connectionString = new SqlConnectionStringBuilder - { - DataSource = @"(localdb)\MSSQLLocalDB", - InitialCatalog = name, - IntegratedSecurity = true, - ConnectTimeout = 600 - }.ConnectionString; + _connectionString = $@"Server=(localdb)\mssqllocaldb;Database={name};Timeout=600"; } public string ConnectionString @@ -57,4 +50,4 @@ namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests EnsureDeleted(); } } -} \ No newline at end of file +} diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs index acb68868b1..fb059fa3fa 100644 --- a/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs +++ b/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.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.Net; @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests public HttpClient Client { get; } - [ConditionalFact] + [ConditionalFact(Skip = "TODO: EF query interception for opening connection. Issue #10672")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] public async Task DatabaseErrorPage_ShowsError() From 04bf1bf32e13d35d4d7f61f9cbeb9bfa22e0c617 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Thu, 30 May 2019 20:34:26 -0700 Subject: [PATCH 53/95] Implement new bedrock listener abstraction and re-plat Kestrel on top (#10321) This is a massive set of changes to Kestrel to remove the existing pubternal transport layer and implement a public facing API for listeners and clients, see the details here #10308. This change only has the server side pieces of the story as I don't want to add the client APIs without having ported SignalR to use them. Here are the highlights: - Transport.Abstractions is empty (will be removed in a separate PR as it requires removing it from a ton of places) - TransportConnection has been moved to Connection.Abstractions (we can decide if we need to consolidate with DefaultConnectionContext in a later PR) - Added FileHandleEndPoint which allows binding to a file handle (could be a pipe or tcp handle) ListenOptions has been gutted for most pubternal API and returns various types of binding information . The source of truth is the EndPoint instance. - Cleaned up a bunch of libuv tests decoupling them from Kestrel.Core ## Breaking changes - Removing pubternal API is itself a breaking change but one that we already planned to do. - We've removed the ability to set the scheduling mode on Kestrel - DisposeAsync was added to ConnectionContext - NoDelay was removed from ListenOptions. This has been moved to each of the transports. One major difference though is that it's no longer localized per endpoint but is global. We'd need a derived EndPoint type (or maybe extend IPEndPoint) to store both the socket options and the binding information. --- ...Connections.Abstractions.netstandard2.0.cs | 65 ++++- .../src/ConnectionContext.cs | 14 + .../src/ConnectionDelegate.cs | 5 +- .../src/DefaultConnectionContext.cs | 16 +- .../Features/IConnectionEndpointFeature.cs | 13 + .../src/Features/IConnectionUserFeature.cs | 5 +- .../src/FileHandleEndPoint.cs | 30 +++ .../src}/FileHandleType.cs | 6 +- .../src/IConnectionBuilder.cs | 5 +- .../src/IConnectionListener.cs | 20 ++ .../src/IConnectionListenerFactory.cs | 17 ++ .../TransportConnection.FeatureCollection.cs | 44 ++++ .../src}/TransportConnection.Generated.cs | 148 +---------- .../src/TransportConnection.cs | 62 +++++ ...soft.AspNetCore.Server.Kestrel.Core.csproj | 2 +- ...tCore.Server.Kestrel.Core.netcoreapp3.0.cs | 15 +- .../src/Adapter/Internal/AdaptedPipeline.cs | 9 +- .../Kestrel/Core/src/AnyIPListenOptions.cs | 4 +- .../Core/src/Internal/ConnectionDispatcher.cs | 127 ++++----- .../Http/Http1ChunkedEncodingMessageBody.cs | 3 +- .../Http/HttpProtocol.FeatureCollection.cs | 7 +- .../src/Internal/Http2/Http2OutputProducer.cs | 3 +- .../Core/src/Internal/Http2/Http2Stream.cs | 3 +- .../Core/src/Internal/HttpConnection.cs | 8 +- .../src/Internal/HttpConnectionMiddleware.cs | 16 +- .../Infrastructure/ConnectionManager.cs | 53 +++- .../ConnectionManagerShutdownExtensions.cs | 53 ---- .../Infrastructure/HeartbeatManager.cs | 4 +- .../Infrastructure/KestrelConnection.cs | 163 +++++++++++- .../Infrastructure/KestrelEventSource.cs | 11 +- .../Core/src/Internal/MemoryPoolExtensions.cs | 31 +++ src/Servers/Kestrel/Core/src/KestrelServer.cs | 53 ++-- .../Kestrel/Core/src/KestrelServerOptions.cs | 9 - src/Servers/Kestrel/Core/src/ListenOptions.cs | 91 ++----- .../Core/src/LocalhostListenOptions.cs | 4 +- ...soft.AspNetCore.Server.Kestrel.Core.csproj | 2 +- .../Kestrel/Core/test/AddressBinderTests.cs | 18 +- .../Core/test/ConnectionDispatcherTests.cs | 62 +++-- .../Kestrel/Core/test/Http1ConnectionTests.cs | 3 +- .../Core/test/HttpConnectionManagerTests.cs | 7 +- .../Core/test/HttpResponseHeadersTests.cs | 4 +- .../Core/test/KestrelServerOptionsTests.cs | 33 +-- .../Kestrel/Core/test/KestrelServerTests.cs | 132 +++++----- ...spNetCore.Server.Kestrel.Core.Tests.csproj | 4 +- .../Kestrel/Core/test/OutputProducerTests.cs | 3 +- .../Kestrel/Core/test/PipeOptionsTests.cs | 40 +-- .../Core/test/PipelineExtensionTests.cs | 3 +- .../Kestrel/Core/test/StartLineTests.cs | 3 +- .../Core/test/TestHelpers/TestInput.cs | 3 +- .../src/WebHostBuilderKestrelExtensions.cs | 4 +- .../test/KestrelConfigurationBuilderTests.cs | 6 - ...oft.AspNetCore.Server.Kestrel.Tests.csproj | 5 +- .../WebHostBuilderKestrelExtensionsTests.cs | 12 +- ...el.Transport.Abstractions.netcoreapp3.0.cs | 110 -------- .../Internal/IApplicationTransportFeature.cs | 12 - .../src/Internal/IConnectionDispatcher.cs | 12 - .../src/Internal/IEndPointInformation.cs | 46 ---- .../src/Internal/ITransport.cs | 15 -- .../src/Internal/ITransportFactory.cs | 10 - .../Internal/ITransportSchedulerFeature.cs | 14 - .../src/Internal/ListenType.cs | 15 -- .../src/Internal/SchedulingMode.cs | 12 - .../TransportConnection.FeatureCollection.cs | 188 ------------- .../src/Internal/TransportConnection.cs | 121 --------- ...rver.Kestrel.Transport.Abstractions.csproj | 2 +- ...Core.Server.Kestrel.Transport.Libuv.csproj | 3 +- ...r.Kestrel.Transport.Libuv.netcoreapp3.0.cs | 3 + .../src/Internal/LibuvConnection.cs | 82 ++++-- ...ransport.cs => LibuvConnectionListener.cs} | 133 ++++++++-- .../src/Internal/LibuvThread.cs | 37 +-- .../src/Internal/LibuvTransportContext.cs | 3 - .../src/Internal/LibuvTransportFactory.cs | 18 +- .../Transport.Libuv/src/Internal/Listener.cs | 44 ++-- .../src/Internal/ListenerContext.cs | 84 ++++-- .../src/Internal/ListenerPrimary.cs | 6 +- .../src/Internal/ListenerSecondary.cs | 14 +- .../src/LibuvTransportOptions.cs | 18 +- ...Core.Server.Kestrel.Transport.Libuv.csproj | 10 +- .../src/WebHostBuilderLibuvExtensions.cs | 6 +- .../test/LibuvConnectionTests.cs | 160 +++++------- .../test/LibuvOutputConsumerTests.cs | 45 +++- .../Transport.Libuv/test/LibuvThreadTests.cs | 6 +- .../test/LibuvTransportTests.cs | 169 +++++++++--- .../test/ListenerPrimaryTests.cs | 247 ++++++++---------- .../TestHelpers/MockConnectionDispatcher.cs | 31 --- .../TestHelpers/TestLibuvTransportContext.cs | 1 - ...re.Server.Kestrel.Transport.Sockets.csproj | 2 +- ...Kestrel.Transport.Sockets.netcoreapp3.0.cs | 9 +- .../src/Internal/SocketConnection.cs | 85 ++++-- ...re.Server.Kestrel.Transport.Sockets.csproj | 7 +- .../src/SocketConnectionListener.cs | 139 ++++++++++ .../Transport.Sockets/src/SocketTransport.cs | 205 --------------- .../src/SocketTransportFactory.cs | 46 +--- .../src/SocketTransportOptions.cs | 18 +- .../src/WebHostBuilderSocketExtensions.cs | 6 +- .../ChunkWriterBenchmark.cs | 3 +- .../Http1ConnectionBenchmark.cs | 3 +- ...Http1ConnectionParsingOverheadBenchmark.cs | 3 +- .../Http1ReadingBenchmark.cs | 3 +- .../Http1WritingBenchmark.cs | 3 +- .../HttpProtocolFeatureCollection.cs | 6 +- .../InMemoryTransportBenchmark.cs | 66 +++-- ...pNetCore.Server.Kestrel.Performance.csproj | 1 + .../PipeThroughputBenchmark.cs | 5 +- .../RequestParsingBenchmark.cs | 3 +- .../ResponseHeaderCollectionBenchmark.cs | 4 +- .../Kestrel/samples/Http2SampleApp/Program.cs | 4 - .../samples/PlaintextApp/PlaintextApp.csproj | 3 +- .../Kestrel/samples/PlaintextApp/Startup.cs | 5 +- .../Kestrel/samples/SampleApp/Startup.cs | 11 +- .../Kestrel/samples/SystemdTestApp/Startup.cs | 6 +- .../shared/test/TaskTimeoutExtensions.cs | 10 + .../Kestrel/shared/test/TestServiceContext.cs | 3 +- .../DiagnosticMemoryPoolFactory.cs | 7 +- .../test/TransportTestHelpers/TestServer.cs | 6 +- .../MaxRequestBufferSizeTests.cs | 2 +- .../ChunkedRequestTests.cs | 2 +- .../Http2/Http2TestBase.cs | 42 ++- .../InMemory.FunctionalTests.csproj | 3 + .../KeepAliveTimeoutTests.cs | 15 ++ .../RequestHeadersTimeoutTests.cs | 8 + .../InMemoryTransportConnection.cs | 162 +++++++++++- .../TestTransport/InMemoryTransportFactory.cs | 61 +++-- .../TestTransport/TestServer.cs | 34 +-- .../TransportSelector.cs | 11 +- .../TransportSelector.cs | 11 +- .../tools/CodeGenerator/CodeGenerator.csproj | 2 +- .../TransportConnectionFeatureCollection.cs | 10 +- .../Buffers.MemoryPool/MemoryPoolFactory.cs} | 9 +- .../Buffers.MemoryPool/SlabMemoryPool.cs | 5 + src/SignalR/SignalR.sln | 7 + .../Client/src/HttpConnectionFactory.cs | 4 +- .../FunctionalTests/HubConnectionTests.cs | 2 +- .../HubConnectionTests.ConnectionLifecycle.cs | 6 +- .../UnitTests/HubConnectionTests.Helpers.cs | 2 +- .../test/UnitTests/HubConnectionTests.cs | 2 +- .../Client/test/UnitTests/TestConnection.cs | 4 +- ...e.Http.Connections.Client.netcoreapp3.0.cs | 2 +- ....Http.Connections.Client.netstandard2.0.cs | 2 +- .../src/HttpConnection.cs | 2 +- .../testassets/Tests.Utils/TaskExtensions.cs | 10 + .../samples/ClientSample/Tcp/TcpConnection.cs | 4 +- .../Tcp/TcpHubConnectionBuilderExtensions.cs | 2 +- 143 files changed, 2128 insertions(+), 2110 deletions(-) create mode 100644 src/Servers/Connections.Abstractions/src/Features/IConnectionEndpointFeature.cs create mode 100644 src/Servers/Connections.Abstractions/src/FileHandleEndPoint.cs rename src/Servers/{Kestrel/Transport.Abstractions/src/Internal => Connections.Abstractions/src}/FileHandleType.cs (52%) create mode 100644 src/Servers/Connections.Abstractions/src/IConnectionListener.cs create mode 100644 src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs create mode 100644 src/Servers/Connections.Abstractions/src/TransportConnection.FeatureCollection.cs rename src/Servers/{Kestrel/Transport.Abstractions/src/Internal => Connections.Abstractions/src}/TransportConnection.Generated.cs (54%) create mode 100644 src/Servers/Connections.Abstractions/src/TransportConnection.cs delete mode 100644 src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManagerShutdownExtensions.cs create mode 100644 src/Servers/Kestrel/Core/src/Internal/MemoryPoolExtensions.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/IApplicationTransportFeature.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/IConnectionDispatcher.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/IEndPointInformation.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransport.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportFactory.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportSchedulerFeature.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/ListenType.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/SchedulingMode.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.FeatureCollection.cs delete mode 100644 src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.cs rename src/Servers/Kestrel/Transport.Libuv/src/Internal/{LibuvTransport.cs => LibuvConnectionListener.cs} (51%) delete mode 100644 src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/MockConnectionDispatcher.cs create mode 100644 src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs delete mode 100644 src/Servers/Kestrel/Transport.Sockets/src/SocketTransport.cs rename src/{Servers/Kestrel/Transport.Abstractions/src/Internal/KestrelMemoryPool.cs => Shared/Buffers.MemoryPool/MemoryPoolFactory.cs} (69%) diff --git a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs index 1189ad64cf..bb446f937d 100644 --- a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs +++ b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs @@ -30,12 +30,16 @@ namespace Microsoft.AspNetCore.Connections public abstract partial class ConnectionContext { protected ConnectionContext() { } + public virtual System.Threading.CancellationToken ConnectionClosed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public abstract string ConnectionId { get; set; } public abstract Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get; } public abstract System.Collections.Generic.IDictionary Items { get; set; } + public virtual System.Net.EndPoint LocalEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public virtual System.Net.EndPoint RemoteEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public abstract System.IO.Pipelines.IDuplexPipe Transport { get; set; } public virtual void Abort() { } public virtual void Abort(Microsoft.AspNetCore.Connections.ConnectionAbortedException abortReason) { } + public virtual System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } } public delegate System.Threading.Tasks.Task ConnectionDelegate(Microsoft.AspNetCore.Connections.ConnectionContext connection); public abstract partial class ConnectionHandler @@ -70,20 +74,35 @@ namespace Microsoft.AspNetCore.Connections public ConnectionResetException(string message) { } public ConnectionResetException(string message, System.Exception inner) { } } - public partial class DefaultConnectionContext : Microsoft.AspNetCore.Connections.ConnectionContext, Microsoft.AspNetCore.Connections.Features.IConnectionIdFeature, Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature, Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature, Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature, Microsoft.AspNetCore.Connections.Features.IConnectionUserFeature, System.IDisposable + public partial class DefaultConnectionContext : Microsoft.AspNetCore.Connections.ConnectionContext, Microsoft.AspNetCore.Connections.Features.IConnectionEndPointFeature, Microsoft.AspNetCore.Connections.Features.IConnectionIdFeature, Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature, Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature, Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature, Microsoft.AspNetCore.Connections.Features.IConnectionUserFeature, System.IDisposable { public DefaultConnectionContext() { } public DefaultConnectionContext(string id) { } public DefaultConnectionContext(string id, System.IO.Pipelines.IDuplexPipe transport, System.IO.Pipelines.IDuplexPipe application) { } public System.IO.Pipelines.IDuplexPipe Application { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.Threading.CancellationToken ConnectionClosed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Threading.CancellationToken ConnectionClosed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public override string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public override Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public override System.Collections.Generic.IDictionary Items { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Net.EndPoint LocalEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Net.EndPoint RemoteEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public override System.IO.Pipelines.IDuplexPipe Transport { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Security.Claims.ClaimsPrincipal User { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public override void Abort(Microsoft.AspNetCore.Connections.ConnectionAbortedException abortReason) { } public void Dispose() { } + public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } + } + public partial class FileHandleEndPoint : System.Net.EndPoint + { + public FileHandleEndPoint(ulong fileHandle, Microsoft.AspNetCore.Connections.FileHandleType fileHandleType) { } + public ulong FileHandle { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Connections.FileHandleType FileHandleType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public enum FileHandleType + { + Auto = 0, + Tcp = 1, + Pipe = 2, } public partial interface IConnectionBuilder { @@ -91,12 +110,49 @@ namespace Microsoft.AspNetCore.Connections Microsoft.AspNetCore.Connections.ConnectionDelegate Build(); Microsoft.AspNetCore.Connections.IConnectionBuilder Use(System.Func middleware); } + public partial interface IConnectionListener + { + System.Net.EndPoint EndPoint { get; } + System.Threading.Tasks.ValueTask AcceptAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.ValueTask DisposeAsync(); + System.Threading.Tasks.ValueTask UnbindAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public partial interface IConnectionListenerFactory + { + System.Threading.Tasks.ValueTask BindAsync(System.Net.EndPoint endpoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } [System.FlagsAttribute] public enum TransferFormat { Binary = 1, Text = 2, } + public abstract partial class TransportConnection : Microsoft.AspNetCore.Connections.ConnectionContext, Microsoft.AspNetCore.Connections.Features.IConnectionIdFeature, Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature, Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature, Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature, Microsoft.AspNetCore.Connections.Features.IMemoryPoolFeature, Microsoft.AspNetCore.Http.Features.IFeatureCollection, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + { + public TransportConnection() { } + public System.IO.Pipelines.IDuplexPipe Application { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Threading.CancellationToken ConnectionClosed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get { throw null; } } + public override System.Collections.Generic.IDictionary Items { get { throw null; } set { } } + public override System.Net.EndPoint LocalEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public virtual System.Buffers.MemoryPool MemoryPool { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + System.Collections.Generic.IDictionary Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature.Items { get { throw null; } set { } } + System.Threading.CancellationToken Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature.ConnectionClosed { get { throw null; } set { } } + System.IO.Pipelines.IDuplexPipe Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature.Transport { get { throw null; } set { } } + System.Buffers.MemoryPool Microsoft.AspNetCore.Connections.Features.IMemoryPoolFeature.MemoryPool { get { throw null; } } + bool Microsoft.AspNetCore.Http.Features.IFeatureCollection.IsReadOnly { get { throw null; } } + object Microsoft.AspNetCore.Http.Features.IFeatureCollection.this[System.Type key] { get { throw null; } set { } } + int Microsoft.AspNetCore.Http.Features.IFeatureCollection.Revision { get { throw null; } } + public override System.Net.EndPoint RemoteEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.IO.Pipelines.IDuplexPipe Transport { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override void Abort(Microsoft.AspNetCore.Connections.ConnectionAbortedException abortReason) { } + void Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature.Abort() { } + TFeature Microsoft.AspNetCore.Http.Features.IFeatureCollection.Get() { throw null; } + void Microsoft.AspNetCore.Http.Features.IFeatureCollection.Set(TFeature feature) { } + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } } namespace Microsoft.AspNetCore.Connections.Features { @@ -104,6 +160,11 @@ namespace Microsoft.AspNetCore.Connections.Features { void OnCompleted(System.Func callback, object state); } + public partial interface IConnectionEndPointFeature + { + System.Net.EndPoint LocalEndPoint { get; set; } + System.Net.EndPoint RemoteEndPoint { get; set; } + } public partial interface IConnectionHeartbeatFeature { void OnHeartbeat(System.Action action, object state); diff --git a/src/Servers/Connections.Abstractions/src/ConnectionContext.cs b/src/Servers/Connections.Abstractions/src/ConnectionContext.cs index a709a5f891..cecf3513c7 100644 --- a/src/Servers/Connections.Abstractions/src/ConnectionContext.cs +++ b/src/Servers/Connections.Abstractions/src/ConnectionContext.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.IO.Pipelines; +using System.Net; +using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; @@ -18,6 +21,12 @@ namespace Microsoft.AspNetCore.Connections public abstract IDuplexPipe Transport { get; set; } + public virtual CancellationToken ConnectionClosed { get; set; } + + public virtual EndPoint LocalEndPoint { get; set; } + + public virtual EndPoint RemoteEndPoint { get; set; } + public virtual void Abort(ConnectionAbortedException abortReason) { // We expect this to be overridden, but this helps maintain back compat @@ -27,5 +36,10 @@ namespace Microsoft.AspNetCore.Connections } public virtual void Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via ConnectionContext.Abort().")); + + public virtual ValueTask DisposeAsync() + { + return default; + } } } diff --git a/src/Servers/Connections.Abstractions/src/ConnectionDelegate.cs b/src/Servers/Connections.Abstractions/src/ConnectionDelegate.cs index f0d64d1587..dff0384f60 100644 --- a/src/Servers/Connections.Abstractions/src/ConnectionDelegate.cs +++ b/src/Servers/Connections.Abstractions/src/ConnectionDelegate.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +// 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.Threading.Tasks; namespace Microsoft.AspNetCore.Connections { diff --git a/src/Servers/Connections.Abstractions/src/DefaultConnectionContext.cs b/src/Servers/Connections.Abstractions/src/DefaultConnectionContext.cs index fab7c929e2..81a56478ba 100644 --- a/src/Servers/Connections.Abstractions/src/DefaultConnectionContext.cs +++ b/src/Servers/Connections.Abstractions/src/DefaultConnectionContext.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.IO.Pipelines; +using System.Net; using System.Security.Claims; using System.Threading; +using System.Threading.Tasks; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; @@ -17,7 +19,8 @@ namespace Microsoft.AspNetCore.Connections IConnectionItemsFeature, IConnectionTransportFeature, IConnectionUserFeature, - IConnectionLifetimeFeature + IConnectionLifetimeFeature, + IConnectionEndPointFeature { private CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource(); @@ -42,6 +45,7 @@ namespace Microsoft.AspNetCore.Connections Features.Set(this); Features.Set(this); Features.Set(this); + Features.Set(this); } public DefaultConnectionContext(string id, IDuplexPipe transport, IDuplexPipe application) @@ -63,7 +67,9 @@ namespace Microsoft.AspNetCore.Connections public override IDuplexPipe Transport { get; set; } - public CancellationToken ConnectionClosed { get; set; } + public override CancellationToken ConnectionClosed { get; set; } + public override EndPoint LocalEndPoint { get; set; } + public override EndPoint RemoteEndPoint { get; set; } public override void Abort(ConnectionAbortedException abortReason) { @@ -74,5 +80,11 @@ namespace Microsoft.AspNetCore.Connections { _connectionClosedTokenSource.Dispose(); } + + public override ValueTask DisposeAsync() + { + _connectionClosedTokenSource.Dispose(); + return base.DisposeAsync(); + } } } diff --git a/src/Servers/Connections.Abstractions/src/Features/IConnectionEndpointFeature.cs b/src/Servers/Connections.Abstractions/src/Features/IConnectionEndpointFeature.cs new file mode 100644 index 0000000000..7c44146ede --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/Features/IConnectionEndpointFeature.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Net; + +namespace Microsoft.AspNetCore.Connections.Features +{ + public interface IConnectionEndPointFeature + { + EndPoint LocalEndPoint { get; set; } + EndPoint RemoteEndPoint { get; set; } + } +} diff --git a/src/Servers/Connections.Abstractions/src/Features/IConnectionUserFeature.cs b/src/Servers/Connections.Abstractions/src/Features/IConnectionUserFeature.cs index 3efb362fc7..7698693a54 100644 --- a/src/Servers/Connections.Abstractions/src/Features/IConnectionUserFeature.cs +++ b/src/Servers/Connections.Abstractions/src/Features/IConnectionUserFeature.cs @@ -1,3 +1,6 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System.Security.Claims; namespace Microsoft.AspNetCore.Connections.Features @@ -6,4 +9,4 @@ namespace Microsoft.AspNetCore.Connections.Features { ClaimsPrincipal User { get; set; } } -} \ No newline at end of file +} diff --git a/src/Servers/Connections.Abstractions/src/FileHandleEndPoint.cs b/src/Servers/Connections.Abstractions/src/FileHandleEndPoint.cs new file mode 100644 index 0000000000..41f4d50812 --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/FileHandleEndPoint.cs @@ -0,0 +1,30 @@ +// 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.Net; + +namespace Microsoft.AspNetCore.Connections +{ + public class FileHandleEndPoint : EndPoint + { + public FileHandleEndPoint(ulong fileHandle, FileHandleType fileHandleType) + { + FileHandle = fileHandle; + FileHandleType = fileHandleType; + + switch (fileHandleType) + { + case FileHandleType.Auto: + case FileHandleType.Tcp: + case FileHandleType.Pipe: + break; + default: + throw new NotSupportedException(); + } + } + + public ulong FileHandle { get; } + public FileHandleType FileHandleType { get; } + } +} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/FileHandleType.cs b/src/Servers/Connections.Abstractions/src/FileHandleType.cs similarity index 52% rename from src/Servers/Kestrel/Transport.Abstractions/src/Internal/FileHandleType.cs rename to src/Servers/Connections.Abstractions/src/FileHandleType.cs index bb70e4ec34..f16935e044 100644 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/FileHandleType.cs +++ b/src/Servers/Connections.Abstractions/src/FileHandleType.cs @@ -1,10 +1,10 @@ -// 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. -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal +namespace Microsoft.AspNetCore.Connections { /// - /// Enumerates the types. + /// Enumerates the types. /// public enum FileHandleType { diff --git a/src/Servers/Connections.Abstractions/src/IConnectionBuilder.cs b/src/Servers/Connections.Abstractions/src/IConnectionBuilder.cs index 4825748292..5fe3ec25a0 100644 --- a/src/Servers/Connections.Abstractions/src/IConnectionBuilder.cs +++ b/src/Servers/Connections.Abstractions/src/IConnectionBuilder.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; namespace Microsoft.AspNetCore.Connections { diff --git a/src/Servers/Connections.Abstractions/src/IConnectionListener.cs b/src/Servers/Connections.Abstractions/src/IConnectionListener.cs new file mode 100644 index 0000000000..c9d0564447 --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/IConnectionListener.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.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Connections +{ + public interface IConnectionListener + { + EndPoint EndPoint { get; } + + ValueTask AcceptAsync(CancellationToken cancellationToken = default); + + ValueTask UnbindAsync(CancellationToken cancellationToken = default); + + ValueTask DisposeAsync(); + } +} diff --git a/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs b/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs new file mode 100644 index 0000000000..b28724e1dd --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs @@ -0,0 +1,17 @@ +// 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.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Connections +{ + public interface IConnectionListenerFactory + { + ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default); + } +} diff --git a/src/Servers/Connections.Abstractions/src/TransportConnection.FeatureCollection.cs b/src/Servers/Connections.Abstractions/src/TransportConnection.FeatureCollection.cs new file mode 100644 index 0000000000..fc5443ecfc --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/TransportConnection.FeatureCollection.cs @@ -0,0 +1,44 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Threading; +using Microsoft.AspNetCore.Connections.Features; + +namespace Microsoft.AspNetCore.Connections +{ + public partial class TransportConnection : IConnectionIdFeature, + IConnectionTransportFeature, + IConnectionItemsFeature, + IMemoryPoolFeature, + IConnectionLifetimeFeature + { + // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, + // then the list of `features` in the generated code project MUST also be updated. + // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs + + MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; + + IDuplexPipe IConnectionTransportFeature.Transport + { + get => Transport; + set => Transport = value; + } + + IDictionary IConnectionItemsFeature.Items + { + get => Items; + set => Items = value; + } + + CancellationToken IConnectionLifetimeFeature.ConnectionClosed + { + get => ConnectionClosed; + set => ConnectionClosed = value; + } + + void IConnectionLifetimeFeature.Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via IConnectionLifetimeFeature.Abort().")); + } +} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.Generated.cs b/src/Servers/Connections.Abstractions/src/TransportConnection.Generated.cs similarity index 54% rename from src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.Generated.cs rename to src/Servers/Connections.Abstractions/src/TransportConnection.Generated.cs index b5d0122ffb..eb6f2ba253 100644 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.Generated.cs +++ b/src/Servers/Connections.Abstractions/src/TransportConnection.Generated.cs @@ -8,33 +8,21 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal +namespace Microsoft.AspNetCore.Connections { public partial class TransportConnection : IFeatureCollection { - private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature); private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature); private static readonly Type IConnectionTransportFeatureType = typeof(IConnectionTransportFeature); private static readonly Type IConnectionItemsFeatureType = typeof(IConnectionItemsFeature); private static readonly Type IMemoryPoolFeatureType = typeof(IMemoryPoolFeature); - private static readonly Type IApplicationTransportFeatureType = typeof(IApplicationTransportFeature); - private static readonly Type ITransportSchedulerFeatureType = typeof(ITransportSchedulerFeature); private static readonly Type IConnectionLifetimeFeatureType = typeof(IConnectionLifetimeFeature); - private static readonly Type IConnectionHeartbeatFeatureType = typeof(IConnectionHeartbeatFeature); - private static readonly Type IConnectionLifetimeNotificationFeatureType = typeof(IConnectionLifetimeNotificationFeature); - private static readonly Type IConnectionCompleteFeatureType = typeof(IConnectionCompleteFeature); - private object _currentIHttpConnectionFeature; private object _currentIConnectionIdFeature; private object _currentIConnectionTransportFeature; private object _currentIConnectionItemsFeature; private object _currentIMemoryPoolFeature; - private object _currentIApplicationTransportFeature; - private object _currentITransportSchedulerFeature; private object _currentIConnectionLifetimeFeature; - private object _currentIConnectionHeartbeatFeature; - private object _currentIConnectionLifetimeNotificationFeature; - private object _currentIConnectionCompleteFeature; private int _featureRevision; @@ -42,17 +30,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal private void FastReset() { - _currentIHttpConnectionFeature = this; _currentIConnectionIdFeature = this; _currentIConnectionTransportFeature = this; _currentIConnectionItemsFeature = this; _currentIMemoryPoolFeature = this; - _currentIApplicationTransportFeature = this; - _currentITransportSchedulerFeature = this; _currentIConnectionLifetimeFeature = this; - _currentIConnectionHeartbeatFeature = this; - _currentIConnectionLifetimeNotificationFeature = this; - _currentIConnectionCompleteFeature = this; } @@ -108,11 +90,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal get { object feature = null; - if (key == IHttpConnectionFeatureType) - { - feature = _currentIHttpConnectionFeature; - } - else if (key == IConnectionIdFeatureType) + if (key == IConnectionIdFeatureType) { feature = _currentIConnectionIdFeature; } @@ -128,30 +106,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { feature = _currentIMemoryPoolFeature; } - else if (key == IApplicationTransportFeatureType) - { - feature = _currentIApplicationTransportFeature; - } - else if (key == ITransportSchedulerFeatureType) - { - feature = _currentITransportSchedulerFeature; - } else if (key == IConnectionLifetimeFeatureType) { feature = _currentIConnectionLifetimeFeature; } - else if (key == IConnectionHeartbeatFeatureType) - { - feature = _currentIConnectionHeartbeatFeature; - } - else if (key == IConnectionLifetimeNotificationFeatureType) - { - feature = _currentIConnectionLifetimeNotificationFeature; - } - else if (key == IConnectionCompleteFeatureType) - { - feature = _currentIConnectionCompleteFeature; - } else if (MaybeExtra != null) { feature = ExtraFeatureGet(key); @@ -164,11 +122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { _featureRevision++; - if (key == IHttpConnectionFeatureType) - { - _currentIHttpConnectionFeature = value; - } - else if (key == IConnectionIdFeatureType) + if (key == IConnectionIdFeatureType) { _currentIConnectionIdFeature = value; } @@ -184,30 +138,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { _currentIMemoryPoolFeature = value; } - else if (key == IApplicationTransportFeatureType) - { - _currentIApplicationTransportFeature = value; - } - else if (key == ITransportSchedulerFeatureType) - { - _currentITransportSchedulerFeature = value; - } else if (key == IConnectionLifetimeFeatureType) { _currentIConnectionLifetimeFeature = value; } - else if (key == IConnectionHeartbeatFeatureType) - { - _currentIConnectionHeartbeatFeature = value; - } - else if (key == IConnectionLifetimeNotificationFeatureType) - { - _currentIConnectionLifetimeNotificationFeature = value; - } - else if (key == IConnectionCompleteFeatureType) - { - _currentIConnectionCompleteFeature = value; - } else { ExtraFeatureSet(key, value); @@ -218,11 +152,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal TFeature IFeatureCollection.Get() { TFeature feature = default; - if (typeof(TFeature) == typeof(IHttpConnectionFeature)) - { - feature = (TFeature)_currentIHttpConnectionFeature; - } - else if (typeof(TFeature) == typeof(IConnectionIdFeature)) + if (typeof(TFeature) == typeof(IConnectionIdFeature)) { feature = (TFeature)_currentIConnectionIdFeature; } @@ -238,30 +168,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { feature = (TFeature)_currentIMemoryPoolFeature; } - else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) - { - feature = (TFeature)_currentIApplicationTransportFeature; - } - else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) - { - feature = (TFeature)_currentITransportSchedulerFeature; - } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { feature = (TFeature)_currentIConnectionLifetimeFeature; } - else if (typeof(TFeature) == typeof(IConnectionHeartbeatFeature)) - { - feature = (TFeature)_currentIConnectionHeartbeatFeature; - } - else if (typeof(TFeature) == typeof(IConnectionLifetimeNotificationFeature)) - { - feature = (TFeature)_currentIConnectionLifetimeNotificationFeature; - } - else if (typeof(TFeature) == typeof(IConnectionCompleteFeature)) - { - feature = (TFeature)_currentIConnectionCompleteFeature; - } else if (MaybeExtra != null) { feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); @@ -273,11 +183,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal void IFeatureCollection.Set(TFeature feature) { _featureRevision++; - if (typeof(TFeature) == typeof(IHttpConnectionFeature)) - { - _currentIHttpConnectionFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionIdFeature)) + if (typeof(TFeature) == typeof(IConnectionIdFeature)) { _currentIConnectionIdFeature = feature; } @@ -293,30 +199,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { _currentIMemoryPoolFeature = feature; } - else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) - { - _currentIApplicationTransportFeature = feature; - } - else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) - { - _currentITransportSchedulerFeature = feature; - } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { _currentIConnectionLifetimeFeature = feature; } - else if (typeof(TFeature) == typeof(IConnectionHeartbeatFeature)) - { - _currentIConnectionHeartbeatFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionLifetimeNotificationFeature)) - { - _currentIConnectionLifetimeNotificationFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionCompleteFeature)) - { - _currentIConnectionCompleteFeature = feature; - } else { ExtraFeatureSet(typeof(TFeature), feature); @@ -325,10 +211,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal private IEnumerable> FastEnumerable() { - if (_currentIHttpConnectionFeature != null) - { - yield return new KeyValuePair(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); - } if (_currentIConnectionIdFeature != null) { yield return new KeyValuePair(IConnectionIdFeatureType, _currentIConnectionIdFeature); @@ -345,30 +227,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { yield return new KeyValuePair(IMemoryPoolFeatureType, _currentIMemoryPoolFeature); } - if (_currentIApplicationTransportFeature != null) - { - yield return new KeyValuePair(IApplicationTransportFeatureType, _currentIApplicationTransportFeature); - } - if (_currentITransportSchedulerFeature != null) - { - yield return new KeyValuePair(ITransportSchedulerFeatureType, _currentITransportSchedulerFeature); - } if (_currentIConnectionLifetimeFeature != null) { yield return new KeyValuePair(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature); } - if (_currentIConnectionHeartbeatFeature != null) - { - yield return new KeyValuePair(IConnectionHeartbeatFeatureType, _currentIConnectionHeartbeatFeature); - } - if (_currentIConnectionLifetimeNotificationFeature != null) - { - yield return new KeyValuePair(IConnectionLifetimeNotificationFeatureType, _currentIConnectionLifetimeNotificationFeature); - } - if (_currentIConnectionCompleteFeature != null) - { - yield return new KeyValuePair(IConnectionCompleteFeatureType, _currentIConnectionCompleteFeature); - } if (MaybeExtra != null) { diff --git a/src/Servers/Connections.Abstractions/src/TransportConnection.cs b/src/Servers/Connections.Abstractions/src/TransportConnection.cs new file mode 100644 index 0000000000..4cef0363c9 --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/TransportConnection.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Net; +using System.Threading; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Connections +{ + public abstract partial class TransportConnection : ConnectionContext + { + private IDictionary _items; + + public TransportConnection() + { + FastReset(); + } + + public override EndPoint LocalEndPoint { get; set; } + public override EndPoint RemoteEndPoint { get; set; } + + public override string ConnectionId { get; set; } + + public override IFeatureCollection Features => this; + + public virtual MemoryPool MemoryPool { get; } + + public override IDuplexPipe Transport { get; set; } + + public IDuplexPipe Application { get; set; } + + public override IDictionary Items + { + get + { + // Lazily allocate connection metadata + return _items ?? (_items = new ConnectionItems()); + } + set + { + _items = value; + } + } + + public override CancellationToken ConnectionClosed { get; set; } + + // DO NOT remove this override to ConnectionContext.Abort. Doing so would cause + // any TransportConnection that does not override Abort or calls base.Abort + // to stack overflow when IConnectionLifetimeFeature.Abort() is called. + // That said, all derived types should override this method should override + // this implementation of Abort because canceling pending output reads is not + // sufficient to abort the connection if there is backpressure. + public override void Abort(ConnectionAbortedException abortReason) + { + Application.Input.CancelPendingRead(); + } + } +} diff --git a/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.csproj b/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.csproj index a7e5f1b75a..367e32ffec 100644 --- a/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.csproj +++ b/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.netcoreapp3.0.cs b/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.netcoreapp3.0.cs index 1c23c2dc98..8891d7feb7 100644 --- a/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.netcoreapp3.0.cs +++ b/src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.netcoreapp3.0.cs @@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core } public partial class KestrelServer : Microsoft.AspNetCore.Hosting.Server.IServer, System.IDisposable { - public KestrelServer(Microsoft.Extensions.Options.IOptions options, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportFactory transportFactory, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public KestrelServer(Microsoft.Extensions.Options.IOptions options, Microsoft.AspNetCore.Connections.IConnectionListenerFactory transportFactory, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } public Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions Options { get { throw null; } } public void Dispose() { } @@ -118,7 +118,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core public KestrelServerOptions() { } public bool AddServerHeader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.SchedulingMode ApplicationSchedulingMode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.IServiceProvider ApplicationServices { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader ConfigurationLoader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool DisableStringReuse { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } @@ -140,19 +139,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core public void ListenUnixSocket(string socketPath) { } public void ListenUnixSocket(string socketPath, System.Action configure) { } } - public partial class ListenOptions : Microsoft.AspNetCore.Connections.IConnectionBuilder, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IEndPointInformation + public partial class ListenOptions : Microsoft.AspNetCore.Connections.IConnectionBuilder { internal ListenOptions() { } public System.IServiceProvider ApplicationServices { get { throw null; } } public System.Collections.Generic.List ConnectionAdapters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public ulong FileHandle { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.FileHandleType HandleType { get { throw null; } set { } } - public System.Net.IPEndPoint IPEndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public System.Net.EndPoint EndPoint { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public ulong FileHandle { get { throw null; } } + public System.Net.IPEndPoint IPEndPoint { get { throw null; } } public Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions KestrelServerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public bool NoDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols Protocols { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public string SocketPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ListenType Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SocketPath { get { throw null; } } public Microsoft.AspNetCore.Connections.ConnectionDelegate Build() { throw null; } public override string ToString() { throw null; } public Microsoft.AspNetCore.Connections.IConnectionBuilder Use(System.Func middleware) { throw null; } diff --git a/src/Servers/Kestrel/Core/src/Adapter/Internal/AdaptedPipeline.cs b/src/Servers/Kestrel/Core/src/Adapter/Internal/AdaptedPipeline.cs index 8d9a35b456..8043ca28fe 100644 --- a/src/Servers/Kestrel/Core/src/Adapter/Internal/AdaptedPipeline.cs +++ b/src/Servers/Kestrel/Core/src/Adapter/Internal/AdaptedPipeline.cs @@ -6,26 +6,27 @@ using System.IO; using System.IO.Pipelines; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal { internal class AdaptedPipeline : IDuplexPipe { - private static readonly int MinAllocBufferSize = KestrelMemoryPool.MinimumSegmentSize / 2; + private readonly int _minAllocBufferSize; private readonly IDuplexPipe _transport; public AdaptedPipeline(IDuplexPipe transport, Pipe inputPipe, Pipe outputPipe, - IKestrelTrace log) + IKestrelTrace log, + int minAllocBufferSize) { _transport = transport; Input = inputPipe; Output = outputPipe; Log = log; + _minAllocBufferSize = minAllocBufferSize; } public Pipe Input { get; } @@ -115,7 +116,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal while (true) { - var outputBuffer = Input.Writer.GetMemory(MinAllocBufferSize); + var outputBuffer = Input.Writer.GetMemory(_minAllocBufferSize); var bytesRead = await stream.ReadAsync(outputBuffer); Input.Writer.Advance(bytesRead); diff --git a/src/Servers/Kestrel/Core/src/AnyIPListenOptions.cs b/src/Servers/Kestrel/Core/src/AnyIPListenOptions.cs index e2319b4977..555dc4af1d 100644 --- a/src/Servers/Kestrel/Core/src/AnyIPListenOptions.cs +++ b/src/Servers/Kestrel/Core/src/AnyIPListenOptions.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; @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core context.Logger.LogDebug(CoreStrings.FormatFallbackToIPv4Any(IPEndPoint.Port)); // for machines that do not support IPv6 - IPEndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.Port); + EndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.Port); await base.BindAsync(context).ConfigureAwait(false); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/ConnectionDispatcher.cs b/src/Servers/Kestrel/Core/src/Internal/ConnectionDispatcher.cs index 0c0cca1573..4e354d8113 100644 --- a/src/Servers/Kestrel/Core/src/Internal/ConnectionDispatcher.cs +++ b/src/Servers/Kestrel/Core/src/Internal/ConnectionDispatcher.cs @@ -2,23 +2,21 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Buffers; -using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { - internal class ConnectionDispatcher : IConnectionDispatcher + internal class ConnectionDispatcher { private static long _lastConnectionId = long.MinValue; private readonly ServiceContext _serviceContext; private readonly ConnectionDelegate _connectionDelegate; + private readonly TaskCompletionSource _acceptLoopTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); public ConnectionDispatcher(ServiceContext serviceContext, ConnectionDelegate connectionDelegate) { @@ -28,26 +26,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal private IKestrelTrace Log => _serviceContext.Log; - public Task OnConnection(TransportConnection connection) + public Task StartAcceptingConnections(IConnectionListener listener) { - // REVIEW: Unfortunately, we still need to use the service context to create the pipes since the settings - // for the scheduler and limits are specified here - var inputOptions = GetInputPipeOptions(_serviceContext, connection.MemoryPool, connection.InputWriterScheduler); - var outputOptions = GetOutputPipeOptions(_serviceContext, connection.MemoryPool, connection.OutputReaderScheduler); - - var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); - - // Set the transport and connection id - connection.ConnectionId = CorrelationIdGenerator.GetNextId(); - connection.Transport = pair.Transport; - - // This *must* be set before returning from OnConnection - connection.Application = pair.Application; - - return Execute(new KestrelConnection(connection)); + ThreadPool.UnsafeQueueUserWorkItem(StartAcceptingConnectionsCore, listener, preferLocal: false); + return _acceptLoopTcs.Task; } - private async Task Execute(KestrelConnection connection) + private void StartAcceptingConnectionsCore(IConnectionListener listener) + { + // REVIEW: Multiple accept loops in parallel? + _ = AcceptConnectionsAsync(); + + async Task AcceptConnectionsAsync() + { + try + { + while (true) + { + var connection = await listener.AcceptAsync(); + + if (connection == null) + { + // We're done listening + break; + } + + _ = Execute(new KestrelConnection(connection, _serviceContext.Log)); + } + } + catch (Exception ex) + { + // REVIEW: If the accept loop ends should this trigger a server shutdown? It will manifest as a hang + Log.LogCritical(0, ex, "The connection listener failed to accept any new connections."); + } + finally + { + _acceptLoopTcs.TrySetResult(null); + } + } + } + + internal async Task Execute(KestrelConnection connection) { var id = Interlocked.Increment(ref _lastConnectionId); var connectionContext = connection.TransportConnection; @@ -69,25 +88,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { Log.LogCritical(0, ex, $"{nameof(ConnectionDispatcher)}.{nameof(Execute)}() {connectionContext.ConnectionId}"); } - finally - { - // Complete the transport PipeReader and PipeWriter after calling into application code - connectionContext.Transport.Input.Complete(); - connectionContext.Transport.Output.Complete(); - } - - // Wait for the transport to close - await CancellationTokenAsTask(connectionContext.ConnectionClosed); } } finally { - await connectionContext.CompleteAsync(); + await connection.FireOnCompletedAsync(); Log.ConnectionStop(connectionContext.ConnectionId); KestrelEventSource.Log.ConnectionStop(connectionContext); - connection.Complete(); + // Dispose the transport connection, this needs to happen before removing it from the + // connection manager so that we only signal completion of this connection after the transport + // is properly torn down. + await connection.TransportConnection.DisposeAsync(); _serviceContext.ConnectionManager.RemoveConnection(id); } @@ -102,55 +115,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal return null; } - - private static Task CancellationTokenAsTask(CancellationToken token) - { - if (token.IsCancellationRequested) - { - return Task.CompletedTask; - } - - // Transports already dispatch prior to tripping ConnectionClosed - // since application code can register to this token. - var tcs = new TaskCompletionSource(); - token.Register(state => ((TaskCompletionSource)state).SetResult(null), tcs); - return tcs.Task; - } - - // Internal for testing - internal static PipeOptions GetInputPipeOptions(ServiceContext serviceContext, MemoryPool memoryPool, PipeScheduler writerScheduler) => new PipeOptions - ( - pool: memoryPool, - readerScheduler: serviceContext.Scheduler, - writerScheduler: writerScheduler, - pauseWriterThreshold: serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, - resumeWriterThreshold: serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, - useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize - ); - - internal static PipeOptions GetOutputPipeOptions(ServiceContext serviceContext, MemoryPool memoryPool, PipeScheduler readerScheduler) => new PipeOptions - ( - pool: memoryPool, - readerScheduler: readerScheduler, - writerScheduler: serviceContext.Scheduler, - pauseWriterThreshold: GetOutputResponseBufferSize(serviceContext), - resumeWriterThreshold: GetOutputResponseBufferSize(serviceContext), - useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize - ); - - private static long GetOutputResponseBufferSize(ServiceContext serviceContext) - { - var bufferSize = serviceContext.ServerOptions.Limits.MaxResponseBufferSize; - if (bufferSize == 0) - { - // 0 = no buffering so we need to configure the pipe so the writer waits on the reader directly - return 1; - } - - // null means that we have no back pressure - return bufferSize ?? 0; - } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs index 7c1327cd73..db9e78aa2f 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs @@ -9,7 +9,6 @@ using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { @@ -546,7 +545,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http pauseWriterThreshold: 1, resumeWriterThreshold: 1, useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize + minimumSegmentSize: context.MemoryPool.GetMinimumSegmentSize() )); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs index 11fcadc074..27b385888c 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http @@ -107,8 +106,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { RequestBody = value; var requestPipeReader = new StreamPipeReader(RequestBody, new StreamPipeReaderAdapterOptions( - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize, - minimumReadThreshold: KestrelMemoryPool.MinimumSegmentSize / 4, + minimumSegmentSize: _context.MemoryPool.GetMinimumSegmentSize(), + minimumReadThreshold: _context.MemoryPool.GetMinimumAllocSize(), _context.MemoryPool)); RequestBodyPipeReader = requestPipeReader; @@ -264,7 +263,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http set { ResponseBody = value; - var responsePipeWriter = new StreamPipeWriter(ResponseBody, minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize, _context.MemoryPool); + var responsePipeWriter = new StreamPipeWriter(ResponseBody, minimumSegmentSize: _context.MemoryPool.GetMinimumSegmentSize(), _context.MemoryPool); ResponsePipeWriter = responsePipeWriter; // The StreamPipeWrapper needs to be disposed as it hold onto blocks of memory diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs index fec48783ef..4f481850d7 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { @@ -363,7 +362,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 pauseWriterThreshold: 1, resumeWriterThreshold: 1, useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize + minimumSegmentSize: pool.GetMinimumSegmentSize() )); } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index 5a27a5641f..d9407a8318 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; @@ -494,7 +493,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 pauseWriterThreshold: windowSize + 1, resumeWriterThreshold: windowSize + 1, useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize + minimumSegmentSize: _context.MemoryPool.GetMinimumSegmentSize() )); private (StreamCompletionFlags OldState, StreamCompletionFlags NewState) ApplyCompletionFlag(StreamCompletionFlags completionState) diff --git a/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs b/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs index 2bb90b1ae0..2d7b3c7633 100644 --- a/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs @@ -18,7 +18,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal @@ -62,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal pauseWriterThreshold: _context.ServiceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, resumeWriterThreshold: _context.ServiceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize + minimumSegmentSize: MemoryPool.GetMinimumSegmentSize() ); internal PipeOptions AdaptedOutputPipeOptions => new PipeOptions @@ -73,7 +72,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal pauseWriterThreshold: _context.ServiceContext.ServerOptions.Limits.MaxResponseBufferSize ?? 0, resumeWriterThreshold: _context.ServiceContext.ServerOptions.Limits.MaxResponseBufferSize ?? 0, useSynchronizationContext: false, - minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize + minimumSegmentSize: MemoryPool.GetMinimumSegmentSize() ); private IKestrelTrace Log => _context.ServiceContext.Log; @@ -94,7 +93,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal adaptedPipeline = new AdaptedPipeline(_adaptedTransport, new Pipe(AdaptedInputPipeOptions), new Pipe(AdaptedOutputPipeOptions), - Log); + Log, + MemoryPool.GetMinimumAllocSize()); _adaptedTransport = adaptedPipeline; } diff --git a/src/Servers/Kestrel/Core/src/Internal/HttpConnectionMiddleware.cs b/src/Servers/Kestrel/Core/src/Internal/HttpConnectionMiddleware.cs index 76b2f3c8e0..efae39ce71 100644 --- a/src/Servers/Kestrel/Core/src/Internal/HttpConnectionMiddleware.cs +++ b/src/Servers/Kestrel/Core/src/Internal/HttpConnectionMiddleware.cs @@ -47,20 +47,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal Transport = connectionContext.Transport }; - var connectionFeature = connectionContext.Features.Get(); - - if (connectionFeature != null) - { - if (connectionFeature.LocalIpAddress != null) - { - httpConnectionContext.LocalEndPoint = new IPEndPoint(connectionFeature.LocalIpAddress, connectionFeature.LocalPort); - } - - if (connectionFeature.RemoteIpAddress != null) - { - httpConnectionContext.RemoteEndPoint = new IPEndPoint(connectionFeature.RemoteIpAddress, connectionFeature.RemotePort); - } - } + httpConnectionContext.LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint; + httpConnectionContext.RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint; var connection = new HttpConnection(httpConnectionContext); diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManager.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManager.cs index cb402facea..05bb0f0726 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManager.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManager.cs @@ -1,8 +1,12 @@ -// 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 System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure { @@ -37,10 +41,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure public void RemoveConnection(long id) { - if (!_connectionReferences.TryRemove(id, out _)) + if (!_connectionReferences.TryRemove(id, out var reference)) { throw new ArgumentException(nameof(id)); } + + if (reference.TryGetConnection(out var connection)) + { + connection.Complete(); + } } public void Walk(Action callback) @@ -64,6 +73,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure } } + public async Task CloseAllConnectionsAsync(CancellationToken token) + { + var closeTasks = new List(); + + Walk(connection => + { + connection.RequestClose(); + closeTasks.Add(connection.ExecutionTask); + }); + + var allClosedTask = Task.WhenAll(closeTasks.ToArray()); + return await Task.WhenAny(allClosedTask, CancellationTokenAsTask(token)).ConfigureAwait(false) == allClosedTask; + } + + public async Task AbortAllConnectionsAsync() + { + var abortTasks = new List(); + + Walk(connection => + { + connection.TransportConnection.Abort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedDuringServerShutdown)); + abortTasks.Add(connection.ExecutionTask); + }); + + var allAbortedTask = Task.WhenAll(abortTasks.ToArray()); + return await Task.WhenAny(allAbortedTask, Task.Delay(1000)).ConfigureAwait(false) == allAbortedTask; + } + + private static Task CancellationTokenAsTask(CancellationToken token) + { + if (token.IsCancellationRequested) + { + return Task.CompletedTask; + } + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + token.Register(() => tcs.SetResult(null)); + return tcs.Task; + } + private static ResourceCounter GetCounter(long? number) => number.HasValue ? ResourceCounter.Quota(number.Value) diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManagerShutdownExtensions.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManagerShutdownExtensions.cs deleted file mode 100644 index 5877cbcf55..0000000000 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/ConnectionManagerShutdownExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -// 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 System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Connections; - -namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure -{ - internal static class ConnectionManagerShutdownExtensions - { - public static async Task CloseAllConnectionsAsync(this ConnectionManager connectionManager, CancellationToken token) - { - var closeTasks = new List(); - - connectionManager.Walk(connection => - { - connection.TransportConnection.RequestClose(); - closeTasks.Add(connection.ExecutionTask); - }); - - var allClosedTask = Task.WhenAll(closeTasks.ToArray()); - return await Task.WhenAny(allClosedTask, CancellationTokenAsTask(token)).ConfigureAwait(false) == allClosedTask; - } - - public static async Task AbortAllConnectionsAsync(this ConnectionManager connectionManager) - { - var abortTasks = new List(); - - connectionManager.Walk(connection => - { - connection.TransportConnection.Abort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedDuringServerShutdown)); - abortTasks.Add(connection.ExecutionTask); - }); - - var allAbortedTask = Task.WhenAll(abortTasks.ToArray()); - return await Task.WhenAny(allAbortedTask, Task.Delay(1000)).ConfigureAwait(false) == allAbortedTask; - } - - private static Task CancellationTokenAsTask(CancellationToken token) - { - if (token.IsCancellationRequested) - { - return Task.CompletedTask; - } - - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - token.Register(() => tcs.SetResult(null)); - return tcs.Task; - } - } -} diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HeartbeatManager.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HeartbeatManager.cs index 8fcd25fe98..18635e1f27 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HeartbeatManager.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HeartbeatManager.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; @@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure private void WalkCallback(KestrelConnection connection) { - connection.TransportConnection.TickHeartbeat(); + connection.TickHeartbeat(); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs index a253aa5c32..ccb51230c5 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelConnection.cs @@ -1,25 +1,170 @@ // 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.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Connections.Features; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure { - internal class KestrelConnection + internal class KestrelConnection : IConnectionHeartbeatFeature, IConnectionCompleteFeature, IConnectionLifetimeNotificationFeature { - private TaskCompletionSource _executionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private List<(Action handler, object state)> _heartbeatHandlers; + private readonly object _heartbeatLock = new object(); - public KestrelConnection(TransportConnection transportConnection) + private Stack, object>> _onCompleted; + private bool _completed; + + private readonly CancellationTokenSource _connectionClosingCts = new CancellationTokenSource(); + private readonly TaskCompletionSource _completionTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public KestrelConnection(ConnectionContext connectionContext, ILogger logger) { - TransportConnection = transportConnection; - ExecutionTask = _executionTcs.Task; + Logger = logger; + TransportConnection = connectionContext; + + // Set a connection id if the transport didn't set one + TransportConnection.ConnectionId ??= CorrelationIdGenerator.GetNextId(); + connectionContext.Features.Set(this); + connectionContext.Features.Set(this); + connectionContext.Features.Set(this); + ConnectionClosedRequested = _connectionClosingCts.Token; } - public TransportConnection TransportConnection { get; } + private ILogger Logger { get; } - public Task ExecutionTask { get; } + public ConnectionContext TransportConnection { get; set; } + public CancellationToken ConnectionClosedRequested { get; set; } + public Task ExecutionTask => _completionTcs.Task; - internal void Complete() => _executionTcs.TrySetResult(null); + public void TickHeartbeat() + { + lock (_heartbeatLock) + { + if (_heartbeatHandlers == null) + { + return; + } + + foreach (var (handler, state) in _heartbeatHandlers) + { + handler(state); + } + } + } + + public void OnHeartbeat(Action action, object state) + { + lock (_heartbeatLock) + { + if (_heartbeatHandlers == null) + { + _heartbeatHandlers = new List<(Action handler, object state)>(); + } + + _heartbeatHandlers.Add((action, state)); + } + } + + void IConnectionCompleteFeature.OnCompleted(Func callback, object state) + { + if (_completed) + { + throw new InvalidOperationException("The connection is already complete."); + } + + if (_onCompleted == null) + { + _onCompleted = new Stack, object>>(); + } + _onCompleted.Push(new KeyValuePair, object>(callback, state)); + } + + public Task FireOnCompletedAsync() + { + if (_completed) + { + throw new InvalidOperationException("The connection is already complete."); + } + + _completed = true; + var onCompleted = _onCompleted; + + if (onCompleted == null || onCompleted.Count == 0) + { + return Task.CompletedTask; + } + + return CompleteAsyncMayAwait(onCompleted); + } + + private Task CompleteAsyncMayAwait(Stack, object>> onCompleted) + { + while (onCompleted.TryPop(out var entry)) + { + try + { + var task = entry.Key.Invoke(entry.Value); + if (!ReferenceEquals(task, Task.CompletedTask)) + { + return CompleteAsyncAwaited(task, onCompleted); + } + } + catch (Exception ex) + { + Logger.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); + } + } + + return Task.CompletedTask; + } + + private async Task CompleteAsyncAwaited(Task currentTask, Stack, object>> onCompleted) + { + try + { + await currentTask; + } + catch (Exception ex) + { + Logger.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); + } + + while (onCompleted.TryPop(out var entry)) + { + try + { + await entry.Key.Invoke(entry.Value); + } + catch (Exception ex) + { + Logger.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); + } + } + } + + public void RequestClose() + { + try + { + _connectionClosingCts.Cancel(); + } + catch (ObjectDisposedException) + { + // There's a race where the token could be disposed + // swallow the exception and no-op + } + } + + public void Complete() + { + _completionTcs.TrySetResult(null); + + _connectionClosingCts.Dispose(); + } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs index ae6a03915b..fdabf48247 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs @@ -2,10 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Tracing; -using System.Net; using System.Runtime.CompilerServices; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure { @@ -27,15 +26,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure // - Avoid renaming methods or parameters marked with EventAttribute. EventSource uses these to form the event object. [NonEvent] - public void ConnectionStart(TransportConnection connection) + public void ConnectionStart(ConnectionContext connection) { // avoid allocating strings unless this event source is enabled if (IsEnabled()) { ConnectionStart( connection.ConnectionId, - connection.LocalAddress != null ? new IPEndPoint(connection.LocalAddress, connection.LocalPort).ToString() : null, - connection.RemoteAddress != null ? new IPEndPoint(connection.RemoteAddress, connection.RemotePort).ToString() : null); + connection.LocalEndPoint?.ToString(), + connection.RemoteEndPoint?.ToString()); } } @@ -54,7 +53,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure } [NonEvent] - public void ConnectionStop(TransportConnection connection) + public void ConnectionStop(ConnectionContext connection) { if (IsEnabled()) { diff --git a/src/Servers/Kestrel/Core/src/Internal/MemoryPoolExtensions.cs b/src/Servers/Kestrel/Core/src/Internal/MemoryPoolExtensions.cs new file mode 100644 index 0000000000..d3b1f30a02 --- /dev/null +++ b/src/Servers/Kestrel/Core/src/Internal/MemoryPoolExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal +{ + internal static class MemoryPoolExtensions + { + /// + /// Computes a minimum segment size + /// + /// + /// + public static int GetMinimumSegmentSize(this MemoryPool pool) + { + if (pool == null) + { + return 4096; + } + + return Math.Min(4096, pool.MaxBufferSize); + } + + public static int GetMinimumAllocSize(this MemoryPool pool) + { + // 1/2 of a segment + return pool.GetMinimumSegmentSize() / 2; + } + } +} diff --git a/src/Servers/Kestrel/Core/src/KestrelServer.cs b/src/Servers/Kestrel/Core/src/KestrelServer.cs index 51b444ada2..0481df8fad 100644 --- a/src/Servers/Kestrel/Core/src/KestrelServer.cs +++ b/src/Servers/Kestrel/Core/src/KestrelServer.cs @@ -6,13 +6,13 @@ using System.Collections.Generic; using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -20,23 +20,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core { public class KestrelServer : IServer { - private readonly List _transports = new List(); + private readonly List<(IConnectionListener, Task)> _transports = new List<(IConnectionListener, Task)>(); private readonly IServerAddressesFeature _serverAddresses; - private readonly ITransportFactory _transportFactory; + private readonly IConnectionListenerFactory _transportFactory; private bool _hasStarted; private int _stopping; private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); -#pragma warning disable PUB0001 // Pubternal type in public API - public KestrelServer(IOptions options, ITransportFactory transportFactory, ILoggerFactory loggerFactory) -#pragma warning restore PUB0001 + public KestrelServer(IOptions options, IConnectionListenerFactory transportFactory, ILoggerFactory loggerFactory) : this(transportFactory, CreateServiceContext(options, loggerFactory)) { } // For testing - internal KestrelServer(ITransportFactory transportFactory, ServiceContext serviceContext) + internal KestrelServer(IConnectionListenerFactory transportFactory, ServiceContext serviceContext) { if (transportFactory == null) { @@ -79,27 +77,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core DebuggerWrapper.Singleton, trace); - // TODO: This logic will eventually move into the IConnectionHandler and off - // the service context once we get to https://github.com/aspnet/KestrelHttpServer/issues/1662 - PipeScheduler scheduler = null; - switch (serverOptions.ApplicationSchedulingMode) - { - case SchedulingMode.Default: - case SchedulingMode.ThreadPool: - scheduler = PipeScheduler.ThreadPool; - break; - case SchedulingMode.Inline: - scheduler = PipeScheduler.Inline; - break; - default: - throw new NotSupportedException(CoreStrings.FormatUnknownTransportMode(serverOptions.ApplicationSchedulingMode)); - } - return new ServiceContext { Log = trace, HttpParser = new HttpParser(trace.IsEnabled(LogLevel.Information)), - Scheduler = scheduler, + Scheduler = PipeScheduler.ThreadPool, SystemClock = heartbeatManager, DateHeaderValueManager = dateHeaderValueManager, ConnectionManager = connectionManager, @@ -138,12 +120,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ServiceContext.Heartbeat?.Start(); - async Task OnBind(ListenOptions endpoint) + async Task OnBind(ListenOptions options) { // Add the HTTP middleware as the terminal connection middleware - endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols); + options.UseHttpServer(options.ConnectionAdapters, ServiceContext, application, options.Protocols); - var connectionDelegate = endpoint.Build(); + var connectionDelegate = options.Build(); // Add the connection limit middleware if (Options.Limits.MaxConcurrentConnections.HasValue) @@ -152,10 +134,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core } var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate); - var transport = _transportFactory.Create(endpoint, connectionDispatcher); - _transports.Add(transport); + var transport = await _transportFactory.BindAsync(options.EndPoint).ConfigureAwait(false); - await transport.BindAsync().ConfigureAwait(false); + // Update the endpoint + options.EndPoint = transport.EndPoint; + var acceptLoopTask = connectionDispatcher.StartAcceptingConnections(transport); + + _transports.Add((transport, acceptLoopTask)); } await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false); @@ -182,8 +167,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core var tasks = new Task[_transports.Count]; for (int i = 0; i < _transports.Count; i++) { - tasks[i] = _transports[i].UnbindAsync(); + (IConnectionListener listener, Task acceptLoop) = _transports[i]; + tasks[i] = Task.WhenAll(listener.UnbindAsync(cancellationToken).AsTask(), acceptLoop); } + await Task.WhenAll(tasks).ConfigureAwait(false); if (!await ConnectionManager.CloseAllConnectionsAsync(cancellationToken).ConfigureAwait(false)) @@ -198,8 +185,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core for (int i = 0; i < _transports.Count; i++) { - tasks[i] = _transports[i].StopAsync(); + (IConnectionListener listener, Task acceptLoop) = _transports[i]; + tasks[i] = listener.DisposeAsync().AsTask(); } + await Task.WhenAll(tasks).ConfigureAwait(false); ServiceContext.Heartbeat?.Dispose(); diff --git a/src/Servers/Kestrel/Core/src/KestrelServerOptions.cs b/src/Servers/Kestrel/Core/src/KestrelServerOptions.cs index bbe5d30b9a..01a94fb0e7 100644 --- a/src/Servers/Kestrel/Core/src/KestrelServerOptions.cs +++ b/src/Servers/Kestrel/Core/src/KestrelServerOptions.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Certificates.Generation; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Https; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -38,14 +37,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core /// public bool AddServerHeader { get; set; } = true; - /// - /// Gets or sets a value that determines how Kestrel should schedule user callbacks. - /// - /// The default mode is -#pragma warning disable PUB0001 // Pubternal type in public API - public SchedulingMode ApplicationSchedulingMode { get; set; } = SchedulingMode.Default; -#pragma warning restore PUB0001 // Pubternal type in public API - /// /// Gets or sets a value that controls whether synchronous IO is allowed for the and /// diff --git a/src/Servers/Kestrel/Core/src/ListenOptions.cs b/src/Servers/Kestrel/Core/src/ListenOptions.cs index b3e4ec21ce..3a850c25f9 100644 --- a/src/Servers/Kestrel/Core/src/ListenOptions.cs +++ b/src/Servers/Kestrel/Core/src/ListenOptions.cs @@ -1,15 +1,15 @@ -// 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 System.Collections.Generic; using System.Linq; using System.Net; +using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Core { @@ -17,21 +17,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core /// Describes either an , Unix domain socket path, or a file descriptor for an already open /// socket that Kestrel should bind to or open. /// - public class ListenOptions : IEndPointInformation, IConnectionBuilder + public class ListenOptions : IConnectionBuilder { - private FileHandleType _handleType; internal readonly List> _middleware = new List>(); internal ListenOptions(IPEndPoint endPoint) { - Type = ListenType.IPEndPoint; - IPEndPoint = endPoint; + EndPoint = endPoint; } internal ListenOptions(string socketPath) { - Type = ListenType.SocketPath; - SocketPath = socketPath; + EndPoint = new UnixDomainSocketEndPoint(socketPath); } internal ListenOptions(ulong fileHandle) @@ -41,73 +38,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core internal ListenOptions(ulong fileHandle, FileHandleType handleType) { - Type = ListenType.FileHandle; - FileHandle = fileHandle; - switch (handleType) - { - case FileHandleType.Auto: - case FileHandleType.Tcp: - case FileHandleType.Pipe: - _handleType = handleType; - break; - default: - throw new NotSupportedException(); - } + EndPoint = new FileHandleEndPoint(fileHandle, handleType); } - /// - /// The type of interface being described: either an , Unix domain socket path, or a file descriptor. - /// -#pragma warning disable PUB0001 // Pubternal type in public API - public ListenType Type { get; } -#pragma warning restore PUB0001 // Pubternal type in public API - -#pragma warning disable PUB0001 // Pubternal type in public API - public FileHandleType HandleType -#pragma warning restore PUB0001 // Pubternal type in public API - { - get => _handleType; - set - { - if (value == _handleType) - { - return; - } - if (Type != ListenType.FileHandle || _handleType != FileHandleType.Auto) - { - throw new InvalidOperationException(); - } - - switch (value) - { - case FileHandleType.Tcp: - case FileHandleType.Pipe: - _handleType = value; - break; - default: - throw new ArgumentException(nameof(HandleType)); - } - } - } + public EndPoint EndPoint { get; internal set; } // IPEndPoint is mutable so port 0 can be updated to the bound port. /// /// The to bind to. - /// Only set if the is . + /// Only set if the is . /// - public IPEndPoint IPEndPoint { get; set; } + public IPEndPoint IPEndPoint => EndPoint as IPEndPoint; /// /// The absolute path to a Unix domain socket to bind to. - /// Only set if the is . + /// Only set if the is . /// - public string SocketPath { get; } + public string SocketPath => (EndPoint as UnixDomainSocketEndPoint)?.ToString(); /// /// A file descriptor for the socket to open. - /// Only set if the is . + /// Only set if the is . /// - public ulong FileHandle { get; } + public ulong FileHandle => (EndPoint as FileHandleEndPoint)?.FileHandle ?? 0; /// /// Enables an to resolve and use services registered by the application during startup. @@ -115,14 +68,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core /// public KestrelServerOptions KestrelServerOptions { get; internal set; } - /// - /// Set to false to enable Nagle's algorithm for all connections. - /// - /// - /// Defaults to true. - /// - public bool NoDelay { get; set; } = true; - /// /// The protocols enabled on this endpoint. /// @@ -153,13 +98,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ? "https" : "http"; - switch (Type) + switch (EndPoint) { - case ListenType.IPEndPoint: + case IPEndPoint _: return $"{scheme}://{IPEndPoint}"; - case ListenType.SocketPath: - return $"{scheme}://unix:{SocketPath}"; - case ListenType.FileHandle: + case UnixDomainSocketEndPoint _: + return $"{scheme}://unix:{EndPoint}"; + case FileHandleEndPoint _: return $"{scheme}://"; default: throw new InvalidOperationException(); diff --git a/src/Servers/Kestrel/Core/src/LocalhostListenOptions.cs b/src/Servers/Kestrel/Core/src/LocalhostListenOptions.cs index 80e008c78b..c7e5f47cad 100644 --- a/src/Servers/Kestrel/Core/src/LocalhostListenOptions.cs +++ b/src/Servers/Kestrel/Core/src/LocalhostListenOptions.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; @@ -76,9 +76,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core { var options = new ListenOptions(new IPEndPoint(address, IPEndPoint.Port)) { - HandleType = HandleType, KestrelServerOptions = KestrelServerOptions, - NoDelay = NoDelay, Protocols = Protocols, }; 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 a528049cf6..8161334613 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 @@ -19,7 +19,7 @@ - + diff --git a/src/Servers/Kestrel/Core/test/AddressBinderTests.cs b/src/Servers/Kestrel/Core/test/AddressBinderTests.cs index 74b067cae3..76d5172207 100644 --- a/src/Servers/Kestrel/Core/test/AddressBinderTests.cs +++ b/src/Servers/Kestrel/Core/test/AddressBinderTests.cs @@ -4,12 +4,13 @@ using System; using System.IO; using System.Net; +using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; +using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -53,10 +54,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [InlineData("contoso.com")] public void ParseAddressDefaultsToAnyIPOnInvalidIPAddress(string host) { - var options = new KestrelServerOptions(); var listenOptions = AddressBinder.ParseAddress($"http://{host}", out var https); Assert.IsType(listenOptions); - Assert.Equal(ListenType.IPEndPoint, listenOptions.Type); + Assert.IsType(listenOptions.EndPoint); Assert.Equal(IPAddress.IPv6Any, listenOptions.IPEndPoint.Address); Assert.Equal(80, listenOptions.IPEndPoint.Port); Assert.False(https); @@ -65,21 +65,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public void ParseAddressLocalhost() { - var options = new KestrelServerOptions(); var listenOptions = AddressBinder.ParseAddress("http://localhost", out var https); Assert.IsType(listenOptions); - Assert.Equal(ListenType.IPEndPoint, listenOptions.Type); + Assert.IsType(listenOptions.EndPoint); Assert.Equal(IPAddress.Loopback, listenOptions.IPEndPoint.Address); Assert.Equal(80, listenOptions.IPEndPoint.Port); Assert.False(https); } - [Fact] + [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win8, WindowsVersions.Win81, WindowsVersions.Win2008R2, SkipReason = "UnixDomainSocketEndPoint is not supported on older versions of Windows")] + [ConditionalFact] public void ParseAddressUnixPipe() { - var options = new KestrelServerOptions(); var listenOptions = AddressBinder.ParseAddress("http://unix:/tmp/kestrel-test.sock", out var https); - Assert.Equal(ListenType.SocketPath, listenOptions.Type); + Assert.IsType(listenOptions.EndPoint); Assert.Equal("/tmp/kestrel-test.sock", listenOptions.SocketPath); Assert.False(https); } @@ -92,9 +91,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [InlineData("https://127.0.0.1", "127.0.0.1", 443, true)] public void ParseAddressIP(string address, string ip, int port, bool isHttps) { - var options = new KestrelServerOptions(); var listenOptions = AddressBinder.ParseAddress(address, out var https); - Assert.Equal(ListenType.IPEndPoint, listenOptions.Type); + Assert.IsType(listenOptions.EndPoint); Assert.Equal(IPAddress.Parse(ip), listenOptions.IPEndPoint.Address); Assert.Equal(port, listenOptions.IPEndPoint.Port); Assert.Equal(isHttps, https); diff --git a/src/Servers/Kestrel/Core/test/ConnectionDispatcherTests.cs b/src/Servers/Kestrel/Core/test/ConnectionDispatcherTests.cs index 1d1c3b46e3..5302cccba7 100644 --- a/src/Servers/Kestrel/Core/test/ConnectionDispatcherTests.cs +++ b/src/Servers/Kestrel/Core/test/ConnectionDispatcherTests.cs @@ -3,13 +3,14 @@ using System; using System.Collections.Generic; -using System.IO.Pipelines; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Moq; @@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var connection = new Mock { CallBase = true }.Object; connection.ConnectionClosed = new CancellationToken(canceled: true); - dispatcher.OnConnection(connection); + _ = dispatcher.Execute(new KestrelConnection(connection, Mock.Of())); // The scope should be created var scopeObjects = ((TestKestrelTrace)serviceContext.Log) @@ -51,25 +52,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests } [Fact] - public async Task OnConnectionCompletesTransportPipesAfterReturning() + public async Task StartAcceptingConnectionsAsyncLogsIfAcceptAsyncThrows() { var serviceContext = new TestServiceContext(); + var logger = ((TestKestrelTrace)serviceContext.Log).Logger; + logger.ThrowOnCriticalErrors = false; + var dispatcher = new ConnectionDispatcher(serviceContext, _ => Task.CompletedTask); - var mockConnection = new Mock { CallBase = true }; - mockConnection.Object.ConnectionClosed = new CancellationToken(canceled: true); - var mockPipeReader = new Mock(); - var mockPipeWriter = new Mock(); - var mockPipe = new Mock(); - mockPipe.Setup(m => m.Input).Returns(mockPipeReader.Object); - mockPipe.Setup(m => m.Output).Returns(mockPipeWriter.Object); - mockConnection.Setup(m => m.Transport).Returns(mockPipe.Object); - var connection = mockConnection.Object; + await dispatcher.StartAcceptingConnections(new ThrowingListener()); - await dispatcher.OnConnection(connection); - - mockPipeWriter.Verify(m => m.Complete(It.IsAny()), Times.Once()); - mockPipeReader.Verify(m => m.Complete(It.IsAny()), Times.Once()); + Assert.Equal(1, logger.CriticalErrorsLogged); + var critical = logger.Messages.SingleOrDefault(m => m.LogLevel == LogLevel.Critical); + Assert.NotNull(critical); + Assert.IsType(critical.Exception); + Assert.Equal("Unexpected error listening", critical.Exception.Message); } [Fact] @@ -80,14 +77,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var connection = new Mock { CallBase = true }.Object; connection.ConnectionClosed = new CancellationToken(canceled: true); - var completeFeature = connection.Features.Get(); + var kestrelConnection = new KestrelConnection(connection, Mock.Of()); + var completeFeature = kestrelConnection.TransportConnection.Features.Get(); Assert.NotNull(completeFeature); object stateObject = new object(); object callbackState = null; completeFeature.OnCompleted(state => { callbackState = state; return Task.CompletedTask; }, stateObject); - await dispatcher.OnConnection(connection); + await dispatcher.Execute(kestrelConnection); Assert.Equal(stateObject, callbackState); } @@ -100,21 +98,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var connection = new Mock { CallBase = true }.Object; connection.ConnectionClosed = new CancellationToken(canceled: true); - var completeFeature = connection.Features.Get(); var mockLogger = new Mock(); - connection.Logger = mockLogger.Object; + var kestrelConnection = new KestrelConnection(connection, mockLogger.Object); + var completeFeature = kestrelConnection.TransportConnection.Features.Get(); Assert.NotNull(completeFeature); object stateObject = new object(); object callbackState = null; completeFeature.OnCompleted(state => { callbackState = state; throw new InvalidTimeZoneException(); }, stateObject); - await dispatcher.OnConnection(connection); + await dispatcher.Execute(kestrelConnection); Assert.Equal(stateObject, callbackState); var log = mockLogger.Invocations.First(); Assert.Equal("An error occured running an IConnectionCompleteFeature.OnCompleted callback.", log.Arguments[2].ToString()); Assert.IsType(log.Arguments[3]); } + + private class ThrowingListener : IConnectionListener + { + public EndPoint EndPoint { get; set; } + + public ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + throw new InvalidOperationException("Unexpected error listening"); + } + + public ValueTask DisposeAsync() + { + return default; + } + + public ValueTask UnbindAsync(CancellationToken cancellationToken = default) + { + return default; + } + } } } diff --git a/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs b/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs index 494e4531e6..042a136b3e 100644 --- a/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs +++ b/src/Servers/Kestrel/Core/test/Http1ConnectionTests.cs @@ -19,7 +19,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; @@ -43,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests public Http1ConnectionTests() { - _pipelineFactory = KestrelMemoryPool.Create(); + _pipelineFactory = MemoryPoolFactory.Create(); var options = new PipeOptions(_pipelineFactory, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/Core/test/HttpConnectionManagerTests.cs b/src/Servers/Kestrel/Core/test/HttpConnectionManagerTests.cs index 809c719e5b..b7c7df6a1d 100644 --- a/src/Servers/Kestrel/Core/test/HttpConnectionManagerTests.cs +++ b/src/Servers/Kestrel/Core/test/HttpConnectionManagerTests.cs @@ -3,8 +3,9 @@ using System; using System.Runtime.CompilerServices; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -38,9 +39,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests ConnectionManager httpConnectionManager, Mock trace) { - var mock = new Mock(); + var mock = new Mock() { CallBase = true }; mock.Setup(m => m.ConnectionId).Returns(connectionId); - var httpConnection = new KestrelConnection(mock.Object); + var httpConnection = new KestrelConnection(mock.Object, Mock.Of()); httpConnectionManager.AddConnection(0, httpConnection); diff --git a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs index 4aad2e3561..6a4e1c2352 100644 --- a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs +++ b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.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 System.Buffers; using System.Collections.Generic; using System.Globalization; using System.IO.Pipelines; @@ -9,7 +10,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Primitives; using Xunit; @@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public void InitialDictionaryIsEmpty() { - using (var memoryPool = KestrelMemoryPool.Create()) + using (var memoryPool = MemoryPoolFactory.Create()) { var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/Core/test/KestrelServerOptionsTests.cs b/src/Servers/Kestrel/Core/test/KestrelServerOptionsTests.cs index 02e52db7f0..c8243b6025 100644 --- a/src/Servers/Kestrel/Core/test/KestrelServerOptionsTests.cs +++ b/src/Servers/Kestrel/Core/test/KestrelServerOptionsTests.cs @@ -8,20 +8,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class KestrelServerOptionsTests { - [Fact] - public void NoDelayDefaultsToTrue() - { - var o1 = new KestrelServerOptions(); - o1.Listen(IPAddress.Loopback, 0); - o1.Listen(IPAddress.Loopback, 0, d => - { - d.NoDelay = false; - }); - - Assert.True(o1.ListenOptions[0].NoDelay); - Assert.False(o1.ListenOptions[1].NoDelay); - } - [Fact] public void AllowSynchronousIODefaultsToFalse() { @@ -36,33 +22,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var options = new KestrelServerOptions(); options.ListenLocalhost(5000); - Assert.True(options.ListenOptions[0].NoDelay); + Assert.Equal(HttpProtocols.Http1AndHttp2, options.ListenOptions[0].Protocols); options.ConfigureEndpointDefaults(opt => { - opt.NoDelay = false; + opt.Protocols = HttpProtocols.Http1; }); options.Listen(new IPEndPoint(IPAddress.Loopback, 5000), opt => { // ConfigureEndpointDefaults runs before this callback - Assert.False(opt.NoDelay); + Assert.Equal(HttpProtocols.Http1, opt.Protocols); }); - Assert.False(options.ListenOptions[1].NoDelay); + Assert.Equal(HttpProtocols.Http1, options.ListenOptions[1].Protocols); options.ListenLocalhost(5000, opt => { - Assert.False(opt.NoDelay); - opt.NoDelay = true; // Can be overriden + Assert.Equal(HttpProtocols.Http1, opt.Protocols); + opt.Protocols = HttpProtocols.Http2; // Can be overriden }); - Assert.True(options.ListenOptions[2].NoDelay); - + Assert.Equal(HttpProtocols.Http2, options.ListenOptions[2].Protocols); options.ListenAnyIP(5000, opt => { - Assert.False(opt.NoDelay); + opt.Protocols = HttpProtocols.Http2; }); - Assert.False(options.ListenOptions[3].NoDelay); + Assert.Equal(HttpProtocols.Http2, options.ListenOptions[3].Protocols); } } } diff --git a/src/Servers/Kestrel/Core/test/KestrelServerTests.cs b/src/Servers/Kestrel/Core/test/KestrelServerTests.cs index 180c7e9bf8..8af4397809 100644 --- a/src/Servers/Kestrel/Core/test/KestrelServerTests.cs +++ b/src/Servers/Kestrel/Core/test/KestrelServerTests.cs @@ -7,11 +7,11 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -203,7 +203,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLoggerFactory = new Mock(); var mockLogger = new Mock(); mockLoggerFactory.Setup(m => m.CreateLogger(It.IsAny())).Returns(mockLogger.Object); - new KestrelServer(Options.Create(null), Mock.Of(), mockLoggerFactory.Object); + new KestrelServer(Options.Create(null), Mock.Of(), mockLoggerFactory.Object); mockLoggerFactory.Verify(factory => factory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel")); } @@ -233,21 +233,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var unbind = new SemaphoreSlim(0); var stop = new SemaphoreSlim(0); - var mockTransport = new Mock(); - mockTransport - .Setup(transport => transport.BindAsync()) - .Returns(Task.CompletedTask); - mockTransport - .Setup(transport => transport.UnbindAsync()) - .Returns(async () => await unbind.WaitAsync()); - mockTransport - .Setup(transport => transport.StopAsync()) - .Returns(async () => await stop.WaitAsync()); - - var mockTransportFactory = new Mock(); + var mockTransport = new Mock(); + var mockTransportFactory = new Mock(); mockTransportFactory - .Setup(transportFactory => transportFactory.Create(It.IsAny(), It.IsAny())) - .Returns(mockTransport.Object); + .Setup(transportFactory => transportFactory.BindAsync(It.IsAny(), It.IsAny())) + .Returns((e, token) => + { + mockTransport + .Setup(transport => transport.AcceptAsync(It.IsAny())) + .Returns(new ValueTask((ConnectionContext)null)); + mockTransport + .Setup(transport => transport.UnbindAsync(It.IsAny())) + .Returns(() => new ValueTask(unbind.WaitAsync())); + mockTransport + .Setup(transport => transport.DisposeAsync()) + .Returns(() => new ValueTask(stop.WaitAsync())); + mockTransport + .Setup(transport => transport.EndPoint).Returns(e); + + return new ValueTask(mockTransport.Object); + }); var mockLoggerFactory = new Mock(); var mockLogger = new Mock(); @@ -255,9 +260,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var server = new KestrelServer(Options.Create(options), mockTransportFactory.Object, mockLoggerFactory.Object); await server.StartAsync(new DummyApplication(), CancellationToken.None); - var stopTask1 = server.StopAsync(default(CancellationToken)); - var stopTask2 = server.StopAsync(default(CancellationToken)); - var stopTask3 = server.StopAsync(default(CancellationToken)); + var stopTask1 = server.StopAsync(default); + var stopTask2 = server.StopAsync(default); + var stopTask3 = server.StopAsync(default); Assert.False(stopTask1.IsCompleted); Assert.False(stopTask2.IsCompleted); @@ -268,8 +273,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests await Task.WhenAll(new[] { stopTask1, stopTask2, stopTask3 }).DefaultTimeout(); - mockTransport.Verify(transport => transport.UnbindAsync(), Times.Once); - mockTransport.Verify(transport => transport.StopAsync(), Times.Once); + mockTransport.Verify(transport => transport.UnbindAsync(It.IsAny()), Times.Once); } [Fact] @@ -286,25 +290,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var unbind = new SemaphoreSlim(0); var unbindException = new InvalidOperationException(); - var mockTransport = new Mock(); - mockTransport - .Setup(transport => transport.BindAsync()) - .Returns(Task.CompletedTask); - mockTransport - .Setup(transport => transport.UnbindAsync()) - .Returns(async () => - { - await unbind.WaitAsync(); - throw unbindException; - }); - mockTransport - .Setup(transport => transport.StopAsync()) - .Returns(Task.CompletedTask); - - var mockTransportFactory = new Mock(); + var mockTransport = new Mock(); + var mockTransportFactory = new Mock(); mockTransportFactory - .Setup(transportFactory => transportFactory.Create(It.IsAny(), It.IsAny())) - .Returns(mockTransport.Object); + .Setup(transportFactory => transportFactory.BindAsync(It.IsAny(), It.IsAny())) + .Returns((e, token) => + { + mockTransport + .Setup(transport => transport.AcceptAsync(It.IsAny())) + .Returns(new ValueTask((ConnectionContext)null)); + mockTransport + .Setup(transport => transport.UnbindAsync(It.IsAny())) + .Returns(async () => + { + await unbind.WaitAsync(); + throw unbindException; + }); + mockTransport + .Setup(transport => transport.EndPoint).Returns(e); + + return new ValueTask(mockTransport.Object); + }); var mockLoggerFactory = new Mock(); var mockLogger = new Mock(); @@ -312,9 +318,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var server = new KestrelServer(Options.Create(options), mockTransportFactory.Object, mockLoggerFactory.Object); await server.StartAsync(new DummyApplication(), CancellationToken.None); - var stopTask1 = server.StopAsync(default(CancellationToken)); - var stopTask2 = server.StopAsync(default(CancellationToken)); - var stopTask3 = server.StopAsync(default(CancellationToken)); + var stopTask1 = server.StopAsync(default); + var stopTask2 = server.StopAsync(default); + var stopTask3 = server.StopAsync(default); Assert.False(stopTask1.IsCompleted); Assert.False(stopTask2.IsCompleted); @@ -327,7 +333,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests Assert.Same(unbindException, await Assert.ThrowsAsync(() => stopTask2.TimeoutAfter(timeout))); Assert.Same(unbindException, await Assert.ThrowsAsync(() => stopTask3.TimeoutAfter(timeout))); - mockTransport.Verify(transport => transport.UnbindAsync(), Times.Once); + mockTransport.Verify(transport => transport.UnbindAsync(It.IsAny()), Times.Once); } [Fact] @@ -343,21 +349,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var unbindTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var mockTransport = new Mock(); - mockTransport - .Setup(transport => transport.BindAsync()) - .Returns(Task.CompletedTask); - mockTransport - .Setup(transport => transport.UnbindAsync()) - .Returns(unbindTcs.Task); - mockTransport - .Setup(transport => transport.StopAsync()) - .Returns(Task.CompletedTask); - - var mockTransportFactory = new Mock(); + var mockTransport = new Mock(); + var mockTransportFactory = new Mock(); mockTransportFactory - .Setup(transportFactory => transportFactory.Create(It.IsAny(), It.IsAny())) - .Returns(mockTransport.Object); + .Setup(transportFactory => transportFactory.BindAsync(It.IsAny(), It.IsAny())) + .Returns((e, token) => + { + mockTransport + .Setup(transport => transport.AcceptAsync(It.IsAny())) + .Returns(new ValueTask((ConnectionContext)null)); + mockTransport + .Setup(transport => transport.UnbindAsync(It.IsAny())) + .Returns(new ValueTask(unbindTcs.Task)); + mockTransport + .Setup(transport => transport.EndPoint).Returns(e); + + return new ValueTask(mockTransport.Object); + }); var mockLoggerFactory = new Mock(); var mockLogger = new Mock(); @@ -384,7 +392,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests await stopTask2.DefaultTimeout(); await continuationTask.DefaultTimeout(); - mockTransport.Verify(transport => transport.UnbindAsync(), Times.Once); + mockTransport.Verify(transport => transport.UnbindAsync(It.IsAny()), Times.Once); } [Fact] @@ -438,11 +446,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests server.StartAsync(new DummyApplication(context => Task.CompletedTask), CancellationToken.None).GetAwaiter().GetResult(); } - private class MockTransportFactory : ITransportFactory + private class MockTransportFactory : IConnectionListenerFactory { - public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher handler) + public ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) { - return Mock.Of(); + var mock = new Mock(); + mock.Setup(m => m.EndPoint).Returns(endpoint); + return new ValueTask(mock.Object); } } } diff --git a/src/Servers/Kestrel/Core/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj b/src/Servers/Kestrel/Core/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj index b334a87ed9..fa82cf0beb 100644 --- a/src/Servers/Kestrel/Core/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj +++ b/src/Servers/Kestrel/Core/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj @@ -12,12 +12,12 @@ + - + - diff --git a/src/Servers/Kestrel/Core/test/OutputProducerTests.cs b/src/Servers/Kestrel/Core/test/OutputProducerTests.cs index 749e2473a5..53aa300f1c 100644 --- a/src/Servers/Kestrel/Core/test/OutputProducerTests.cs +++ b/src/Servers/Kestrel/Core/test/OutputProducerTests.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Moq; using Xunit; @@ -22,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests public OutputProducerTests() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); } public void Dispose() diff --git a/src/Servers/Kestrel/Core/test/PipeOptionsTests.cs b/src/Servers/Kestrel/Core/test/PipeOptionsTests.cs index 1f5b377360..0b06bec0e9 100644 --- a/src/Servers/Kestrel/Core/test/PipeOptionsTests.cs +++ b/src/Servers/Kestrel/Core/test/PipeOptionsTests.cs @@ -1,9 +1,8 @@ -// 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.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Moq; using Xunit; @@ -12,43 +11,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class PipeOptionsTests { - [Theory] - [InlineData(10, 10, 10)] - [InlineData(0, 1, 1)] - [InlineData(null, 0, 0)] - public void OutputPipeOptionsConfiguredCorrectly(long? maxResponseBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh) - { - var serviceContext = new TestServiceContext(); - serviceContext.ServerOptions.Limits.MaxResponseBufferSize = maxResponseBufferSize; - serviceContext.Scheduler = PipeScheduler.ThreadPool; - - var mockScheduler = Mock.Of(); - var outputPipeOptions = ConnectionDispatcher.GetOutputPipeOptions(serviceContext, KestrelMemoryPool.Create(), readerScheduler: mockScheduler); - - Assert.Equal(expectedMaximumSizeLow, outputPipeOptions.ResumeWriterThreshold); - Assert.Equal(expectedMaximumSizeHigh, outputPipeOptions.PauseWriterThreshold); - Assert.Same(mockScheduler, outputPipeOptions.ReaderScheduler); - Assert.Same(serviceContext.Scheduler, outputPipeOptions.WriterScheduler); - } - - [Theory] - [InlineData(10, 10, 10)] - [InlineData(null, 0, 0)] - public void InputPipeOptionsConfiguredCorrectly(long? maxRequestBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh) - { - var serviceContext = new TestServiceContext(); - serviceContext.ServerOptions.Limits.MaxRequestBufferSize = maxRequestBufferSize; - serviceContext.Scheduler = PipeScheduler.ThreadPool; - - var mockScheduler = Mock.Of(); - var inputPipeOptions = ConnectionDispatcher.GetInputPipeOptions(serviceContext, KestrelMemoryPool.Create(), writerScheduler: mockScheduler); - - Assert.Equal(expectedMaximumSizeLow, inputPipeOptions.ResumeWriterThreshold); - Assert.Equal(expectedMaximumSizeHigh, inputPipeOptions.PauseWriterThreshold); - Assert.Same(serviceContext.Scheduler, inputPipeOptions.ReaderScheduler); - Assert.Same(mockScheduler, inputPipeOptions.WriterScheduler); - } - [Theory] [InlineData(10, 10, 10)] [InlineData(null, 0, 0)] diff --git a/src/Servers/Kestrel/Core/test/PipelineExtensionTests.cs b/src/Servers/Kestrel/Core/test/PipelineExtensionTests.cs index 1e5f82677d..e2dfc2efd4 100644 --- a/src/Servers/Kestrel/Core/test/PipelineExtensionTests.cs +++ b/src/Servers/Kestrel/Core/test/PipelineExtensionTests.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.IO.Pipelines; using System.Text; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests @@ -17,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests private const int _ulongMaxValueLength = 20; private readonly Pipe _pipe; - private readonly MemoryPool _memoryPool = KestrelMemoryPool.Create(); + private readonly MemoryPool _memoryPool = MemoryPoolFactory.Create(); public PipelineExtensionTests() { diff --git a/src/Servers/Kestrel/Core/test/StartLineTests.cs b/src/Servers/Kestrel/Core/test/StartLineTests.cs index 9b9656fbba..bd4d5f4d5b 100644 --- a/src/Servers/Kestrel/Core/test/StartLineTests.cs +++ b/src/Servers/Kestrel/Core/test/StartLineTests.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Moq; using Xunit; @@ -500,7 +499,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests public StartLineTests() { - MemoryPool = KestrelMemoryPool.Create(); + MemoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(MemoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); Transport = pair.Transport; diff --git a/src/Servers/Kestrel/Core/test/TestHelpers/TestInput.cs b/src/Servers/Kestrel/Core/test/TestHelpers/TestInput.cs index 81f046b10d..3a8ce70e67 100644 --- a/src/Servers/Kestrel/Core/test/TestHelpers/TestInput.cs +++ b/src/Servers/Kestrel/Core/test/TestHelpers/TestInput.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Moq; @@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests public TestInput() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(pool: _memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); Transport = pair.Transport; diff --git a/src/Servers/Kestrel/Kestrel/src/WebHostBuilderKestrelExtensions.cs b/src/Servers/Kestrel/Kestrel/src/WebHostBuilderKestrelExtensions.cs index 62411b168d..3616679369 100644 --- a/src/Servers/Kestrel/Kestrel/src/WebHostBuilderKestrelExtensions.cs +++ b/src/Servers/Kestrel/Kestrel/src/WebHostBuilderKestrelExtensions.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Hosting return hostBuilder.ConfigureServices(services => { // Don't override an already-configured transport - services.TryAddSingleton(); + services.TryAddSingleton(); services.AddTransient, KestrelServerOptionsSetup>(); services.AddSingleton(); diff --git a/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationBuilderTests.cs b/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationBuilderTests.cs index c2bb493845..12d32431ae 100644 --- a/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationBuilderTests.cs +++ b/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationBuilderTests.cs @@ -133,7 +133,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests serverOptions.ConfigureEndpointDefaults(opt => { - opt.NoDelay = false; opt.Protocols = HttpProtocols.Http2; }); @@ -156,13 +155,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests Assert.True(opt.IsHttps); Assert.NotNull(opt.HttpsOptions.ServerCertificate); Assert.Equal(ClientCertificateMode.RequireCertificate, opt.HttpsOptions.ClientCertificateMode); - Assert.False(opt.ListenOptions.NoDelay); Assert.Equal(HttpProtocols.Http2, opt.ListenOptions.Protocols); }) .LocalhostEndpoint(5002, opt => { ran2 = true; - Assert.False(opt.NoDelay); Assert.Equal(HttpProtocols.Http2, opt.Protocols); }) .Load(); @@ -181,7 +178,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests serverOptions.ConfigureEndpointDefaults(opt => { - opt.NoDelay = false; opt.UseHttps(TestResources.GetTestCertificate()); }); @@ -202,12 +198,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests ran1 = true; Assert.True(opt.IsHttps); Assert.Equal(ClientCertificateMode.RequireCertificate, opt.HttpsOptions.ClientCertificateMode); - Assert.False(opt.ListenOptions.NoDelay); }) .LocalhostEndpoint(5002, opt => { ran2 = true; - Assert.False(opt.NoDelay); }) .Load(); diff --git a/src/Servers/Kestrel/Kestrel/test/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj b/src/Servers/Kestrel/Kestrel/test/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj index 814c905495..cb32fe57c1 100644 --- a/src/Servers/Kestrel/Kestrel/test/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj +++ b/src/Servers/Kestrel/Kestrel/test/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -12,7 +12,7 @@ - + @@ -20,7 +20,6 @@ - diff --git a/src/Servers/Kestrel/Kestrel/test/WebHostBuilderKestrelExtensionsTests.cs b/src/Servers/Kestrel/Kestrel/test/WebHostBuilderKestrelExtensionsTests.cs index 7ea16d4c85..fd5c31d032 100644 --- a/src/Servers/Kestrel/Kestrel/test/WebHostBuilderKestrelExtensionsTests.cs +++ b/src/Servers/Kestrel/Kestrel/test/WebHostBuilderKestrelExtensionsTests.cs @@ -1,9 +1,9 @@ // 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.Connections; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; using Microsoft.Extensions.DependencyInjection; @@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests .UseKestrel() .Configure(app => { }); - Assert.IsType(hostBuilder.Build().Services.GetService()); + Assert.IsType(hostBuilder.Build().Services.GetService()); } [Fact] @@ -68,14 +68,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests .UseLibuv() .Configure(app => { }); - Assert.IsType(hostBuilder.Build().Services.GetService()); + Assert.IsType(hostBuilder.Build().Services.GetService()); var hostBuilderReversed = new WebHostBuilder() .UseLibuv() .UseKestrel() .Configure(app => { }); - Assert.IsType(hostBuilderReversed.Build().Services.GetService()); + Assert.IsType(hostBuilderReversed.Build().Services.GetService()); } [Fact] @@ -86,14 +86,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests .UseSockets() .Configure(app => { }); - Assert.IsType(hostBuilder.Build().Services.GetService()); + Assert.IsType(hostBuilder.Build().Services.GetService()); var hostBuilderReversed = new WebHostBuilder() .UseSockets() .UseKestrel() .Configure(app => { }); - Assert.IsType(hostBuilderReversed.Build().Services.GetService()); + Assert.IsType(hostBuilderReversed.Build().Services.GetService()); } } } diff --git a/src/Servers/Kestrel/Transport.Abstractions/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.netcoreapp3.0.cs b/src/Servers/Kestrel/Transport.Abstractions/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.netcoreapp3.0.cs index 5420d07eac..618082bc4a 100644 --- a/src/Servers/Kestrel/Transport.Abstractions/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.netcoreapp3.0.cs +++ b/src/Servers/Kestrel/Transport.Abstractions/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.netcoreapp3.0.cs @@ -1,113 +1,3 @@ // 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public enum FileHandleType - { - Auto = 0, - Tcp = 1, - Pipe = 2, - } - public partial interface IApplicationTransportFeature - { - System.IO.Pipelines.IDuplexPipe Application { get; set; } - } - public partial interface IConnectionDispatcher - { - System.Threading.Tasks.Task OnConnection(Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.TransportConnection connection); - } - public partial interface IEndPointInformation - { - ulong FileHandle { get; } - Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.FileHandleType HandleType { get; set; } - System.Net.IPEndPoint IPEndPoint { get; set; } - bool NoDelay { get; } - string SocketPath { get; } - Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ListenType Type { get; } - } - public partial interface ITransport - { - System.Threading.Tasks.Task BindAsync(); - System.Threading.Tasks.Task StopAsync(); - System.Threading.Tasks.Task UnbindAsync(); - } - public partial interface ITransportFactory - { - Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransport Create(Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IEndPointInformation endPointInformation, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IConnectionDispatcher dispatcher); - } - public partial interface ITransportSchedulerFeature - { - System.IO.Pipelines.PipeScheduler InputWriterScheduler { get; } - System.IO.Pipelines.PipeScheduler OutputReaderScheduler { get; } - } - public static partial class KestrelMemoryPool - { - public static readonly int MinimumSegmentSize; - public static System.Buffers.MemoryPool Create() { throw null; } - public static System.Buffers.MemoryPool CreateSlabMemoryPool() { throw null; } - } - public enum ListenType - { - IPEndPoint = 0, - SocketPath = 1, - FileHandle = 2, - } - public enum SchedulingMode - { - Default = 0, - ThreadPool = 1, - Inline = 2, - } - public abstract partial class TransportConnection : Microsoft.AspNetCore.Connections.ConnectionContext, Microsoft.AspNetCore.Connections.Features.IConnectionCompleteFeature, Microsoft.AspNetCore.Connections.Features.IConnectionHeartbeatFeature, Microsoft.AspNetCore.Connections.Features.IConnectionIdFeature, Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature, Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature, Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeNotificationFeature, Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature, Microsoft.AspNetCore.Connections.Features.IMemoryPoolFeature, Microsoft.AspNetCore.Http.Features.IFeatureCollection, Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IApplicationTransportFeature, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportSchedulerFeature, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable - { - protected readonly System.Threading.CancellationTokenSource _connectionClosingCts; - public TransportConnection() { } - public System.IO.Pipelines.IDuplexPipe Application { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.Threading.CancellationToken ConnectionClosed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public System.Threading.CancellationToken ConnectionClosedRequested { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public override string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public override Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get { throw null; } } - public System.IO.Pipelines.PipeWriter Input { get { throw null; } } - public virtual System.IO.Pipelines.PipeScheduler InputWriterScheduler { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public override System.Collections.Generic.IDictionary Items { get { throw null; } set { } } - public System.Net.IPAddress LocalAddress { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int LocalPort { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - protected internal virtual Microsoft.Extensions.Logging.ILogger Logger { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public virtual System.Buffers.MemoryPool MemoryPool { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - System.Collections.Generic.IDictionary Microsoft.AspNetCore.Connections.Features.IConnectionItemsFeature.Items { get { throw null; } set { } } - System.Threading.CancellationToken Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature.ConnectionClosed { get { throw null; } set { } } - System.Threading.CancellationToken Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeNotificationFeature.ConnectionClosedRequested { get { throw null; } set { } } - System.IO.Pipelines.IDuplexPipe Microsoft.AspNetCore.Connections.Features.IConnectionTransportFeature.Transport { get { throw null; } set { } } - System.Buffers.MemoryPool Microsoft.AspNetCore.Connections.Features.IMemoryPoolFeature.MemoryPool { get { throw null; } } - bool Microsoft.AspNetCore.Http.Features.IFeatureCollection.IsReadOnly { get { throw null; } } - object Microsoft.AspNetCore.Http.Features.IFeatureCollection.this[System.Type key] { get { throw null; } set { } } - int Microsoft.AspNetCore.Http.Features.IFeatureCollection.Revision { get { throw null; } } - string Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature.ConnectionId { get { throw null; } set { } } - System.Net.IPAddress Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature.LocalIpAddress { get { throw null; } set { } } - int Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature.LocalPort { get { throw null; } set { } } - System.Net.IPAddress Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature.RemoteIpAddress { get { throw null; } set { } } - int Microsoft.AspNetCore.Http.Features.IHttpConnectionFeature.RemotePort { get { throw null; } set { } } - System.IO.Pipelines.IDuplexPipe Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IApplicationTransportFeature.Application { get { throw null; } set { } } - System.IO.Pipelines.PipeScheduler Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportSchedulerFeature.InputWriterScheduler { get { throw null; } } - System.IO.Pipelines.PipeScheduler Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportSchedulerFeature.OutputReaderScheduler { get { throw null; } } - public System.IO.Pipelines.PipeReader Output { get { throw null; } } - public virtual System.IO.Pipelines.PipeScheduler OutputReaderScheduler { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public System.Net.IPAddress RemoteAddress { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public int RemotePort { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public override System.IO.Pipelines.IDuplexPipe Transport { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } - public override void Abort(Microsoft.AspNetCore.Connections.ConnectionAbortedException abortReason) { } - public System.Threading.Tasks.Task CompleteAsync() { throw null; } - void Microsoft.AspNetCore.Connections.Features.IConnectionCompleteFeature.OnCompleted(System.Func callback, object state) { } - void Microsoft.AspNetCore.Connections.Features.IConnectionHeartbeatFeature.OnHeartbeat(System.Action action, object state) { } - void Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeFeature.Abort() { } - void Microsoft.AspNetCore.Connections.Features.IConnectionLifetimeNotificationFeature.RequestClose() { } - TFeature Microsoft.AspNetCore.Http.Features.IFeatureCollection.Get() { throw null; } - void Microsoft.AspNetCore.Http.Features.IFeatureCollection.Set(TFeature feature) { } - public void OnHeartbeat(System.Action action, object state) { } - public void RequestClose() { } - System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - public void TickHeartbeat() { } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IApplicationTransportFeature.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IApplicationTransportFeature.cs deleted file mode 100644 index 8aa8328a6b..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IApplicationTransportFeature.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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.IO.Pipelines; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface IApplicationTransportFeature - { - IDuplexPipe Application { get; set; } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IConnectionDispatcher.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IConnectionDispatcher.cs deleted file mode 100644 index 813c541d1a..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IConnectionDispatcher.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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.Threading.Tasks; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface IConnectionDispatcher - { - Task OnConnection(TransportConnection connection); - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IEndPointInformation.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IEndPointInformation.cs deleted file mode 100644 index 1b7abfa497..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/IEndPointInformation.cs +++ /dev/null @@ -1,46 +0,0 @@ -// 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.Net; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface IEndPointInformation - { - /// - /// The type of interface being described: either an , Unix domain socket path, or a file descriptor. - /// - ListenType Type { get; } - - // IPEndPoint is mutable so port 0 can be updated to the bound port. - /// - /// The to bind to. - /// Only set if is . - /// - IPEndPoint IPEndPoint { get; set; } - - /// - /// The absolute path to a Unix domain socket to bind to. - /// Only set if is . - /// - string SocketPath { get; } - - /// - /// A file descriptor for the socket to open. - /// Only set if is . - /// - ulong FileHandle { get; } - - // HandleType is mutable so it can be re-specified later. - /// - /// The type of file descriptor being used. - /// Only set if is . - /// - FileHandleType HandleType { get; set; } - - /// - /// Set to false to enable Nagle's algorithm for all connections. - /// - bool NoDelay { get; } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransport.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransport.cs deleted file mode 100644 index 5a6dc0c20c..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransport.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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.Threading.Tasks; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface ITransport - { - // Can only be called once per ITransport - Task BindAsync(); - Task UnbindAsync(); - Task StopAsync(); - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportFactory.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportFactory.cs deleted file mode 100644 index 4037467e87..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface ITransportFactory - { - ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher dispatcher); - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportSchedulerFeature.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportSchedulerFeature.cs deleted file mode 100644 index c4df6d5a37..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ITransportSchedulerFeature.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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.IO.Pipelines; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public interface ITransportSchedulerFeature - { - PipeScheduler InputWriterScheduler { get; } - - PipeScheduler OutputReaderScheduler { get; } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ListenType.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ListenType.cs deleted file mode 100644 index 3616f1967e..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/ListenType.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - /// - /// Enumerates the types. - /// - public enum ListenType - { - IPEndPoint, - SocketPath, - FileHandle - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/SchedulingMode.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/SchedulingMode.cs deleted file mode 100644 index 881006087c..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/SchedulingMode.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public enum SchedulingMode - { - Default, - ThreadPool, - Inline - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.FeatureCollection.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.FeatureCollection.cs deleted file mode 100644 index 7f98162507..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.FeatureCollection.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO.Pipelines; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Connections.Features; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public partial class TransportConnection : IHttpConnectionFeature, - IConnectionIdFeature, - IConnectionTransportFeature, - IConnectionItemsFeature, - IMemoryPoolFeature, - IApplicationTransportFeature, - ITransportSchedulerFeature, - IConnectionLifetimeFeature, - IConnectionHeartbeatFeature, - IConnectionLifetimeNotificationFeature, - IConnectionCompleteFeature - { - // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, - // then the list of `features` in the generated code project MUST also be updated. - // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs - - private Stack, object>> _onCompleted; - private bool _completed; - - string IHttpConnectionFeature.ConnectionId - { - get => ConnectionId; - set => ConnectionId = value; - } - - IPAddress IHttpConnectionFeature.RemoteIpAddress - { - get => RemoteAddress; - set => RemoteAddress = value; - } - - IPAddress IHttpConnectionFeature.LocalIpAddress - { - get => LocalAddress; - set => LocalAddress = value; - } - - int IHttpConnectionFeature.RemotePort - { - get => RemotePort; - set => RemotePort = value; - } - - int IHttpConnectionFeature.LocalPort - { - get => LocalPort; - set => LocalPort = value; - } - - MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; - - IDuplexPipe IConnectionTransportFeature.Transport - { - get => Transport; - set => Transport = value; - } - - IDuplexPipe IApplicationTransportFeature.Application - { - get => Application; - set => Application = value; - } - - IDictionary IConnectionItemsFeature.Items - { - get => Items; - set => Items = value; - } - - PipeScheduler ITransportSchedulerFeature.InputWriterScheduler => InputWriterScheduler; - PipeScheduler ITransportSchedulerFeature.OutputReaderScheduler => OutputReaderScheduler; - - CancellationToken IConnectionLifetimeFeature.ConnectionClosed - { - get => ConnectionClosed; - set => ConnectionClosed = value; - } - - CancellationToken IConnectionLifetimeNotificationFeature.ConnectionClosedRequested - { - get => ConnectionClosedRequested; - set => ConnectionClosedRequested = value; - } - - void IConnectionLifetimeFeature.Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via IConnectionLifetimeFeature.Abort().")); - - void IConnectionLifetimeNotificationFeature.RequestClose() => RequestClose(); - - void IConnectionHeartbeatFeature.OnHeartbeat(System.Action action, object state) - { - OnHeartbeat(action, state); - } - - void IConnectionCompleteFeature.OnCompleted(Func callback, object state) - { - if (_completed) - { - throw new InvalidOperationException("The connection is already complete."); - } - - if (_onCompleted == null) - { - _onCompleted = new Stack, object>>(); - } - _onCompleted.Push(new KeyValuePair, object>(callback, state)); - } - - public Task CompleteAsync() - { - if (_completed) - { - throw new InvalidOperationException("The connection is already complete."); - } - - _completed = true; - var onCompleted = _onCompleted; - - if (onCompleted == null || onCompleted.Count == 0) - { - return Task.CompletedTask; - } - - return CompleteAsyncMayAwait(onCompleted); - } - - private Task CompleteAsyncMayAwait(Stack, object>> onCompleted) - { - while (onCompleted.TryPop(out var entry)) - { - try - { - var task = entry.Key.Invoke(entry.Value); - if (!ReferenceEquals(task, Task.CompletedTask)) - { - return CompleteAsyncAwaited(task, onCompleted); - } - } - catch (Exception ex) - { - Logger?.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); - } - } - - return Task.CompletedTask; - } - - private async Task CompleteAsyncAwaited(Task currentTask, Stack, object>> onCompleted) - { - try - { - await currentTask; - } - catch (Exception ex) - { - Logger?.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); - } - - while (onCompleted.TryPop(out var entry)) - { - try - { - await entry.Key.Invoke(entry.Value); - } - catch (Exception ex) - { - Logger?.LogError(ex, "An error occured running an IConnectionCompleteFeature.OnCompleted callback."); - } - } - } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.cs b/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.cs deleted file mode 100644 index e3d012cc27..0000000000 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/TransportConnection.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO.Pipelines; -using System.Net; -using System.Threading; -using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal -{ - public abstract partial class TransportConnection : ConnectionContext - { - private IDictionary _items; - private List<(Action handler, object state)> _heartbeatHandlers; - private readonly object _heartbeatLock = new object(); - protected readonly CancellationTokenSource _connectionClosingCts = new CancellationTokenSource(); - - public TransportConnection() - { - FastReset(); - - ConnectionClosedRequested = _connectionClosingCts.Token; - } - - public IPAddress RemoteAddress { get; set; } - public int RemotePort { get; set; } - public IPAddress LocalAddress { get; set; } - public int LocalPort { get; set; } - - public override string ConnectionId { get; set; } - - public override IFeatureCollection Features => this; - - protected internal virtual ILogger Logger { get; set; } - - public virtual MemoryPool MemoryPool { get; } - public virtual PipeScheduler InputWriterScheduler { get; } - public virtual PipeScheduler OutputReaderScheduler { get; } - - public override IDuplexPipe Transport { get; set; } - public IDuplexPipe Application { get; set; } - - public override IDictionary Items - { - get - { - // Lazily allocate connection metadata - return _items ?? (_items = new ConnectionItems()); - } - set - { - _items = value; - } - } - - public PipeWriter Input => Application.Output; - public PipeReader Output => Application.Input; - - public CancellationToken ConnectionClosed { get; set; } - - public CancellationToken ConnectionClosedRequested { get; set; } - - public void TickHeartbeat() - { - lock (_heartbeatLock) - { - if (_heartbeatHandlers == null) - { - return; - } - - foreach (var (handler, state) in _heartbeatHandlers) - { - handler(state); - } - } - } - - public void OnHeartbeat(Action action, object state) - { - lock (_heartbeatLock) - { - if (_heartbeatHandlers == null) - { - _heartbeatHandlers = new List<(Action handler, object state)>(); - } - - _heartbeatHandlers.Add((action, state)); - } - } - - // DO NOT remove this override to ConnectionContext.Abort. Doing so would cause - // any TransportConnection that does not override Abort or calls base.Abort - // to stack overflow when IConnectionLifetimeFeature.Abort() is called. - // That said, all derived types should override this method should override - // this implementation of Abort because canceling pending output reads is not - // sufficient to abort the connection if there is backpressure. - public override void Abort(ConnectionAbortedException abortReason) - { - Output.CancelPendingRead(); - } - - public void RequestClose() - { - try - { - _connectionClosingCts.Cancel(); - } - catch (ObjectDisposedException) - { - // There's a race where the token could be disposed - // swallow the exception and no-op - } - } - } -} diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj b/src/Servers/Kestrel/Transport.Abstractions/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj index 9f9270d232..a20f6d2640 100644 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj +++ b/src/Servers/Kestrel/Transport.Abstractions/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj @@ -1,4 +1,4 @@ - + Transport abstractions for the ASP.NET Core Kestrel cross-platform web server. diff --git a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj index 381c885bdd..366ac6ec5c 100644 --- a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj +++ b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj @@ -7,8 +7,9 @@ - + + diff --git a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp3.0.cs b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp3.0.cs index 8e37f8714d..f24337e79d 100644 --- a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp3.0.cs +++ b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp3.0.cs @@ -14,6 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv public partial class LibuvTransportOptions { public LibuvTransportOptions() { } + public long? MaxReadBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public long? MaxWriteBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool NoDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public int ThreadCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnection.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnection.cs index adcc5cd09d..962359bd8f 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnection.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnection.cs @@ -9,15 +9,14 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - internal partial class LibuvConnection : TransportConnection, IDisposable + internal partial class LibuvConnection : TransportConnection { - private static readonly int MinAllocBufferSize = KestrelMemoryPool.MinimumSegmentSize / 2; + private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; private static readonly Action _readCallback = (handle, status, state) => ReadCallback(handle, status, state); @@ -31,31 +30,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal private volatile ConnectionAbortedException _abortReason; private MemoryHandle _bufferHandle; + private Task _processingTask; - public LibuvConnection(UvStreamHandle socket, ILibuvTrace log, LibuvThread thread, IPEndPoint remoteEndPoint, IPEndPoint localEndPoint) + public LibuvConnection(UvStreamHandle socket, + ILibuvTrace log, + LibuvThread thread, + IPEndPoint remoteEndPoint, + IPEndPoint localEndPoint, + PipeOptions inputOptions = null, + PipeOptions outputOptions = null, + long? maxReadBufferSize = null, + long? maxWriteBufferSize = null) { _socket = socket; - RemoteAddress = remoteEndPoint?.Address; - RemotePort = remoteEndPoint?.Port ?? 0; - - LocalAddress = localEndPoint?.Address; - LocalPort = localEndPoint?.Port ?? 0; + LocalEndPoint = localEndPoint; + RemoteEndPoint = remoteEndPoint; ConnectionClosed = _connectionClosedTokenSource.Token; - Logger = log; Log = log; Thread = thread; + + maxReadBufferSize ??= 0; + maxWriteBufferSize ??= 0; + + inputOptions ??= new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, Thread, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); + outputOptions ??= new PipeOptions(MemoryPool, Thread, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); + + var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); + + // Set the transport and connection id + Transport = pair.Transport; + Application = pair.Application; } + public PipeWriter Input => Application.Output; + + public PipeReader Output => Application.Input; + public LibuvOutputConsumer OutputConsumer { get; set; } private ILibuvTrace Log { get; } private LibuvThread Thread { get; } public override MemoryPool MemoryPool => Thread.MemoryPool; - public override PipeScheduler InputWriterScheduler => Thread; - public override PipeScheduler OutputReaderScheduler => Thread; - public async Task Start() + public void Start() + { + _processingTask = StartCore(); + } + + private async Task StartCore() { try { @@ -96,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal } finally { - inputError = inputError ?? _abortReason ?? new ConnectionAbortedException("The libuv transport's send loop completed gracefully."); + inputError ??= _abortReason ?? new ConnectionAbortedException("The libuv transport's send loop completed gracefully."); // Now, complete the input so that no more reads can happen Input.Complete(inputError); @@ -111,7 +134,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal // We're done with the socket now _socket.Dispose(); - ThreadPool.UnsafeQueueUserWorkItem(state => ((LibuvConnection)state).CancelConnectionClosedToken(), this); + + // Fire the connection closed token and wait for it to complete + var waitForConnectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + ThreadPool.UnsafeQueueUserWorkItem(state => + { + (var connection, var tcs) = state; + + connection.CancelConnectionClosedToken(); + + tcs.TrySetResult(null); + }, + (this, waitForConnectionClosedTcs), + preferLocal: false); + + await waitForConnectionClosedTcs.Task; } } catch (Exception e) @@ -123,7 +161,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public override void Abort(ConnectionAbortedException abortReason) { _abortReason = abortReason; - + // Cancel WriteOutputAsync loop after setting _abortReason. Output.CancelPendingRead(); @@ -131,11 +169,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal Thread.Post(s => s.Dispose(), _socket); } - // Only called after connection middleware is complete which means the ConnectionClosed token has fired. - public void Dispose() + public override async ValueTask DisposeAsync() { + Transport.Input.Complete(); + Transport.Output.Complete(); + + if (_processingTask != null) + { + await _processingTask; + } + _connectionClosedTokenSource.Dispose(); - _connectionClosingCts.Dispose(); } // Called on Libuv thread diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransport.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnectionListener.cs similarity index 51% rename from src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransport.cs rename to src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnectionListener.cs index 4bbeae88f1..d1b2e9a8e7 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransport.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConnectionListener.cs @@ -4,32 +4,34 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - internal class LibuvTransport : ITransport + internal class LibuvConnectionListener : IConnectionListener { - private readonly IEndPointInformation _endPointInformation; + private readonly List _listeners = new List(); + private IAsyncEnumerator _acceptEnumerator; + private bool _stopped; + private bool _disposed; - private readonly List _listeners = new List(); - - public LibuvTransport(LibuvTransportContext context, IEndPointInformation endPointInformation) - : this(new LibuvFunctions(), context, endPointInformation) + public LibuvConnectionListener(LibuvTransportContext context, EndPoint endPoint) + : this(new LibuvFunctions(), context, endPoint) { } // For testing - public LibuvTransport(LibuvFunctions uv, LibuvTransportContext context, IEndPointInformation endPointInformation) + public LibuvConnectionListener(LibuvFunctions uv, LibuvTransportContext context, EndPoint endPoint) { Libuv = uv; TransportContext = context; - _endPointInformation = endPointInformation; + EndPoint = endPoint; } public LibuvFunctions Libuv { get; } @@ -40,7 +42,64 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public ILibuvTrace Log => TransportContext.Log; public LibuvTransportOptions TransportOptions => TransportContext.Options; - public async Task StopAsync() + public EndPoint EndPoint { get; set; } + + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().FullName); + } + + if (await _acceptEnumerator.MoveNextAsync()) + { + return _acceptEnumerator.Current; + } + + // null means we're done... + return null; + } + + public async ValueTask UnbindAsync(CancellationToken cancellationToken = default) + { + if (_stopped) + { + return; + } + + _stopped = true; + + var disposeTasks = _listeners.Select(listener => ((IAsyncDisposable)listener).DisposeAsync()).ToArray(); + + if (!await WaitAsync(Task.WhenAll(disposeTasks), TimeSpan.FromSeconds(5)).ConfigureAwait(false)) + { + Log.LogError(0, null, "Disposing listeners failed"); + } + } + + + public async ValueTask DisposeAsync() + { + if (_disposed) + { + return; + } + + _disposed = true; + + await UnbindAsync().ConfigureAwait(false); + + foreach (var listener in _listeners) + { + await listener.AbortQueuedConnectionAsync().ConfigureAwait(false); + } + + _listeners.Clear(); + + await StopThreadsAsync().ConfigureAwait(false); + } + + internal async Task StopThreadsAsync() { try { @@ -68,13 +127,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal #endif } - public async Task BindAsync() + internal async Task BindAsync() { // TODO: Move thread management to LibuvTransportFactory // TODO: Split endpoint management from thread management for (var index = 0; index < TransportOptions.ThreadCount; index++) { - Threads.Add(new LibuvThread(this)); + Threads.Add(new LibuvThread(Libuv, TransportContext)); } foreach (var thread in Threads) @@ -88,7 +147,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { var listener = new Listener(TransportContext); _listeners.Add(listener); - await listener.StartAsync(_endPointInformation, Threads[0]).ConfigureAwait(false); + await listener.StartAsync(EndPoint, Threads[0]).ConfigureAwait(false); + EndPoint = listener.EndPoint; } else { @@ -97,15 +157,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal var listenerPrimary = new ListenerPrimary(TransportContext); _listeners.Add(listenerPrimary); - await listenerPrimary.StartAsync(pipeName, pipeMessage, _endPointInformation, Threads[0]).ConfigureAwait(false); + await listenerPrimary.StartAsync(pipeName, pipeMessage, EndPoint, Threads[0]).ConfigureAwait(false); + EndPoint = listenerPrimary.EndPoint; foreach (var thread in Threads.Skip(1)) { var listenerSecondary = new ListenerSecondary(TransportContext); _listeners.Add(listenerSecondary); - await listenerSecondary.StartAsync(pipeName, pipeMessage, _endPointInformation, thread).ConfigureAwait(false); + await listenerSecondary.StartAsync(pipeName, pipeMessage, EndPoint, thread).ConfigureAwait(false); } } + _acceptEnumerator = AcceptConnections(); } catch (UvException ex) when (ex.StatusCode == LibuvConstants.EADDRINUSE) { @@ -119,16 +181,45 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal } } - public async Task UnbindAsync() + private async IAsyncEnumerator AcceptConnections() { - var disposeTasks = _listeners.Select(listener => listener.DisposeAsync()).ToArray(); + var slots = new Task<(LibuvConnection, int)>[_listeners.Count]; + // This is the task we'll put in the slot when each listening completes. It'll prevent + // us from having to shrink the array. We'll just loop while there are active slots. + var incompleteTask = new TaskCompletionSource<(LibuvConnection, int)>().Task; - if (!await WaitAsync(Task.WhenAll(disposeTasks), TimeSpan.FromSeconds(5)).ConfigureAwait(false)) + var remainingSlots = slots.Length; + + // Issue parallel accepts on all listeners + for (int i = 0; i < remainingSlots; i++) { - Log.LogError(0, null, "Disposing listeners failed"); + slots[i] = AcceptAsync(_listeners[i], i); } - _listeners.Clear(); + while (remainingSlots > 0) + { + // Calling GetAwaiter().GetResult() is safe because we know the task is completed + (var connection, var slot) = (await Task.WhenAny(slots)).GetAwaiter().GetResult(); + + // If the connection is null then the listener was closed + if (connection == null) + { + remainingSlots--; + slots[slot] = incompleteTask; + } + else + { + // Fill that slot with another accept and yield the connection + slots[slot] = AcceptAsync(_listeners[slot], slot); + + yield return connection; + } + } + + static async Task<(LibuvConnection, int)> AcceptAsync(ListenerContext listener, int slot) + { + return (await listener.AcceptAsync(), slot); + } } private static async Task WaitAsync(Task task, TimeSpan timeout) diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvThread.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvThread.cs index 9d50eb5f67..de212ca89d 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvThread.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvThread.cs @@ -20,9 +20,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal // maximum times the work queues swapped and are processed in a single pass // as completing a task may immediately have write data to put on the network // otherwise it needs to wait till the next pass of the libuv loop - private readonly int _maxLoops = 8; + private readonly int _maxLoops; - private readonly LibuvTransport _transport; + private readonly LibuvFunctions _libuv; private readonly IHostApplicationLifetime _appLifetime; private readonly Thread _thread; private readonly TaskCompletionSource _threadTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -40,13 +40,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal private Exception _closeError; private readonly ILibuvTrace _log; - public LibuvThread(LibuvTransport transport) + public LibuvThread(LibuvFunctions libuv, LibuvTransportContext libuvTransportContext, int maxLoops = 8) + : this(libuv, libuvTransportContext.AppLifetime, libuvTransportContext.Options.MemoryPoolFactory(), libuvTransportContext.Log, maxLoops) { - _transport = transport; - _appLifetime = transport.AppLifetime; - _log = transport.Log; + } + + public LibuvThread(LibuvFunctions libuv, IHostApplicationLifetime appLifetime, MemoryPool pool, ILibuvTrace log, int maxLoops = 8) + { + _libuv = libuv; + _appLifetime = appLifetime; + _log = log; _loop = new UvLoopHandle(_log); _post = new UvAsyncHandle(_log); + _maxLoops = maxLoops; _thread = new Thread(ThreadStart); #if !INNER_LOOP @@ -60,17 +66,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal #endif QueueCloseHandle = PostCloseHandle; QueueCloseAsyncHandle = EnqueueCloseHandle; - MemoryPool = transport.TransportOptions.MemoryPoolFactory(); + MemoryPool = pool; WriteReqPool = new WriteReqPool(this, _log); } - // For testing - public LibuvThread(LibuvTransport transport, int maxLoops) - : this(transport) - { - _maxLoops = maxLoops; - } - public UvLoopHandle Loop { get { return _loop; } } public MemoryPool MemoryPool { get; } @@ -113,13 +112,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal Post(t => t.AllowStop()); if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) { + _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(AllowStop)}"); + Post(t => t.OnStopRude()); if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) { + _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(OnStopRude)}."); + Post(t => t.OnStopImmediate()); if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) { - _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread."); + _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(OnStopImmediate)}."); } } } @@ -253,7 +256,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal private void Walk(LibuvFunctions.uv_walk_cb callback, IntPtr arg) { - _transport.Libuv.walk( + _libuv.walk( _loop, callback, arg @@ -282,7 +285,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal var tcs = (TaskCompletionSource)parameter; try { - _loop.Init(_transport.Libuv); + _loop.Init(_libuv); _post.Init(_loop, OnPost, EnqueueCloseHandle); _initCompleted = true; tcs.SetResult(0); diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportContext.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportContext.cs index 7f9e1d62f9..973a0fec06 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportContext.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportContext.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Hosting; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal @@ -13,7 +12,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public IHostApplicationLifetime AppLifetime { get; set; } public ILibuvTrace Log { get; set; } - - public IConnectionDispatcher ConnectionDispatcher { get; set; } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportFactory.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportFactory.cs index 485582355d..689c473195 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportFactory.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvTransportFactory.cs @@ -2,14 +2,17 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - internal class LibuvTransportFactory : ITransportFactory + internal class LibuvTransportFactory : IConnectionListenerFactory { private readonly LibuvTransportContext _baseTransportContext; @@ -31,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal throw new ArgumentNullException(nameof(loggerFactory)); } - var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"); var trace = new LibuvTrace(logger); var threadCount = options.Value.ThreadCount; @@ -61,17 +64,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal }; } - public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher dispatcher) + public async ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) { var transportContext = new LibuvTransportContext { Options = _baseTransportContext.Options, AppLifetime = _baseTransportContext.AppLifetime, - Log = _baseTransportContext.Log, - ConnectionDispatcher = dispatcher + Log = _baseTransportContext.Log }; - return new LibuvTransport(transportContext, endPointInformation); + var transport = new LibuvConnectionListener(transportContext, endpoint); + await transport.BindAsync(); + return transport; } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs index 010a84e006..9dc11a31a4 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Net; +using System.Net.Sockets; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; @@ -14,6 +16,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal /// internal class Listener : ListenerContext, IAsyncDisposable { + // REVIEW: This needs to be bounded and we need a strategy for what to do when the queue is full private bool _closed; public Listener(LibuvTransportContext transportContext) : base(transportContext) @@ -25,10 +28,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public ILibuvTrace Log => TransportContext.Log; public Task StartAsync( - IEndPointInformation endPointInformation, + EndPoint endPoint, LibuvThread thread) { - EndPointInformation = endPointInformation; + EndPoint = endPoint; Thread = thread; return Thread.PostAsync(listener => @@ -43,13 +46,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal /// private UvStreamHandle CreateListenSocket() { - switch (EndPointInformation.Type) + switch (EndPoint) { - case ListenType.IPEndPoint: + case IPEndPoint _: return ListenTcp(useFileHandle: false); - case ListenType.SocketPath: + case UnixDomainSocketEndPoint _: return ListenPipe(useFileHandle: false); - case ListenType.FileHandle: + case FileHandleEndPoint _: return ListenHandle(); default: throw new NotSupportedException(); @@ -63,18 +66,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal try { socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.NoDelay(EndPointInformation.NoDelay); + socket.NoDelay(TransportContext.Options.NoDelay); if (!useFileHandle) { - socket.Bind(EndPointInformation.IPEndPoint); + socket.Bind((IPEndPoint)EndPoint); // If requested port was "0", replace with assigned dynamic port. - EndPointInformation.IPEndPoint = socket.GetSockIPEndPoint(); + EndPoint = socket.GetSockIPEndPoint(); } else { - socket.Open((IntPtr)EndPointInformation.FileHandle); + socket.Open((IntPtr)((FileHandleEndPoint)EndPoint).FileHandle); } } catch @@ -96,11 +99,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal if (!useFileHandle) { - pipe.Bind(EndPointInformation.SocketPath); + // UnixDomainSocketEndPoint.ToString() returns the path + pipe.Bind(EndPoint.ToString()); } else { - pipe.Open((IntPtr)EndPointInformation.FileHandle); + pipe.Open((IntPtr)((FileHandleEndPoint)EndPoint).FileHandle); } } catch @@ -114,7 +118,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal private UvStreamHandle ListenHandle() { - switch (EndPointInformation.HandleType) + var handleEndPoint = (FileHandleEndPoint)EndPoint; + + switch (handleEndPoint.FileHandleType) { case FileHandleType.Auto: break; @@ -130,7 +136,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal try { handle = ListenTcp(useFileHandle: true); - EndPointInformation.HandleType = FileHandleType.Tcp; + EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Tcp); return handle; } catch (UvException exception) when (exception.StatusCode == LibuvConstants.ENOTSUP) @@ -139,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal } handle = ListenPipe(useFileHandle: true); - EndPointInformation.HandleType = FileHandleType.Pipe; + EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Pipe); return handle; } @@ -186,9 +192,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal protected virtual void DispatchConnection(UvStreamHandle socket) { - // REVIEW: This task should be tracked by the server for graceful shutdown - // Today it's handled specifically for http but not for arbitrary middleware - _ = HandleConnectionAsync(socket); + HandleConnection(socket); } public virtual async Task DisposeAsync() @@ -205,6 +209,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal listener._closed = true; + listener.StopAcceptingConnections(); + }, this).ConfigureAwait(false); } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerContext.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerContext.cs index e153565af8..528ec5ca3b 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerContext.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerContext.cs @@ -2,9 +2,14 @@ // 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.Pipelines; using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Channels; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; @@ -12,6 +17,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { internal class ListenerContext { + // Single reader, single writer queue since all writes happen from the uv thread and reads happen sequentially + private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions + { + SingleReader = true, + SingleWriter = true + }); + public ListenerContext(LibuvTransportContext transportContext) { TransportContext = transportContext; @@ -19,29 +31,62 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public LibuvTransportContext TransportContext { get; set; } - public IEndPointInformation EndPointInformation { get; set; } + public EndPoint EndPoint { get; set; } public LibuvThread Thread { get; set; } + public PipeOptions InputOptions { get; set; } + + public PipeOptions OutputOptions { get; set; } + + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + while (await _acceptQueue.Reader.WaitToReadAsync()) + { + while (_acceptQueue.Reader.TryRead(out var connection)) + { + return connection; + } + } + + return null; + } + + /// + /// Aborts all unaccepted connections in the queue + /// + /// + public async Task AbortQueuedConnectionAsync() + { + while (await _acceptQueue.Reader.WaitToReadAsync()) + { + while (_acceptQueue.Reader.TryRead(out var connection)) + { + // REVIEW: Pass an abort reason? + connection.Abort(); + } + } + } + /// /// Creates a socket which can be used to accept an incoming connection. /// protected UvStreamHandle CreateAcceptSocket() { - switch (EndPointInformation.Type) + switch (EndPoint) { - case ListenType.IPEndPoint: + case IPEndPoint _: return AcceptTcp(); - case ListenType.SocketPath: + case UnixDomainSocketEndPoint _: return AcceptPipe(); - case ListenType.FileHandle: + case FileHandleEndPoint _: return AcceptHandle(); default: throw new InvalidOperationException(); } } - protected async Task HandleConnectionAsync(UvStreamHandle socket) + protected internal void HandleConnection(UvStreamHandle socket) { try { @@ -63,18 +108,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal } } - var connection = new LibuvConnection(socket, TransportContext.Log, Thread, remoteEndPoint, localEndPoint); - var middlewareTask = TransportContext.ConnectionDispatcher.OnConnection(connection); - var transportTask = connection.Start(); + var options = TransportContext.Options; + var connection = new LibuvConnection(socket, TransportContext.Log, Thread, remoteEndPoint, localEndPoint, InputOptions, OutputOptions, options.MaxReadBufferSize, options.MaxWriteBufferSize); + connection.Start(); - await transportTask; - await middlewareTask; - - connection.Dispose(); + bool accepted = _acceptQueue.Writer.TryWrite(connection); + Debug.Assert(accepted, "The connection was not written to the channel!"); } catch (Exception ex) { - TransportContext.Log.LogCritical(ex, $"Unexpected exception in {nameof(ListenerContext)}.{nameof(HandleConnectionAsync)}."); + TransportContext.Log.LogCritical(ex, $"Unexpected exception in {nameof(ListenerContext)}.{nameof(HandleConnection)}."); } } @@ -85,7 +128,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal try { socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.NoDelay(EndPointInformation.NoDelay); + socket.NoDelay(TransportContext.Options.NoDelay); } catch { @@ -113,9 +156,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal return pipe; } + protected void StopAcceptingConnections() + { + _acceptQueue.Writer.TryComplete(); + } + private UvStreamHandle AcceptHandle() { - switch (EndPointInformation.HandleType) + var fileHandleEndPoint = (FileHandleEndPoint)EndPoint; + + switch (fileHandleEndPoint.FileHandleType) { case FileHandleType.Auto: throw new InvalidOperationException("Cannot accept on a non-specific file handle, listen should be performed first."); diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs index 4ce0afab29..acbc356294 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; @@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public async Task StartAsync( string pipeName, byte[] pipeMessage, - IEndPointInformation endPointInformation, + EndPoint endPoint, LibuvThread thread) { _pipeName = pipeName; @@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal Marshal.StructureToPtr(fileCompletionInfo, _fileCompletionInfoPtr, false); } - await StartAsync(endPointInformation, thread).ConfigureAwait(false); + await StartAsync(endPoint, thread).ConfigureAwait(false); await Thread.PostAsync(listener => listener.PostCallback(), this).ConfigureAwait(false); } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerSecondary.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerSecondary.cs index 6597df28d7..477d391f88 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerSecondary.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerSecondary.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Net; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; @@ -35,14 +35,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal public Task StartAsync( string pipeName, byte[] pipeMessage, - IEndPointInformation endPointInformation, + EndPoint endPoint, LibuvThread thread) { _pipeName = pipeName; _pipeMessage = pipeMessage; _buf = thread.Loop.Libuv.buf_init(_ptr, 4); - EndPointInformation = endPointInformation; + EndPoint = endPoint; Thread = thread; DispatchPipe = new UvPipeHandle(Log); @@ -152,9 +152,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { DispatchPipe.Accept(acceptSocket); - // REVIEW: This task should be tracked by the server for graceful shutdown - // Today it's handled specifically for http but not for arbitrary middleware - _ = HandleConnectionAsync(acceptSocket); + HandleConnection(acceptSocket); } catch (UvException ex) when (LibuvConstants.IsConnectionReset(ex.StatusCode)) { @@ -192,11 +190,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal listener._closed = true; + listener.StopAcceptingConnections(); + }, this).ConfigureAwait(false); } else { FreeBuffer(); + + StopAcceptingConnections(); } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs b/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs index a040dea87b..9110b39587 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs @@ -1,9 +1,9 @@ -// 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 System.Buffers; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using System.IO.Pipelines; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv { @@ -20,7 +20,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv /// public int ThreadCount { get; set; } = ProcessorThreadCount; - internal Func> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create(); + /// + /// Set to false to enable Nagle's algorithm for all connections. + /// + /// + /// Defaults to true. + /// + public bool NoDelay { get; set; } = true; + + public long? MaxReadBufferSize { get; set; } = PipeOptions.Default.PauseWriterThreshold; + + public long? MaxWriteBufferSize { get; set; } = PipeOptions.Default.PauseWriterThreshold; + + internal Func> MemoryPoolFactory { get; set; } = System.Buffers.MemoryPoolFactory.Create; private static int ProcessorThreadCount { diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj b/src/Servers/Kestrel/Transport.Libuv/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj index 2b52fe7f46..d6bd6d4e45 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj +++ b/src/Servers/Kestrel/Transport.Libuv/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj @@ -1,4 +1,4 @@ - + Libuv transport for the ASP.NET Core Kestrel cross-platform web server. @@ -10,12 +10,18 @@ true + + + + + - + + diff --git a/src/Servers/Kestrel/Transport.Libuv/src/WebHostBuilderLibuvExtensions.cs b/src/Servers/Kestrel/Transport.Libuv/src/WebHostBuilderLibuvExtensions.cs index 386d0b6679..8f0986d334 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/WebHostBuilderLibuvExtensions.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/WebHostBuilderLibuvExtensions.cs @@ -1,8 +1,8 @@ -// 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.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.Extensions.DependencyInjection; @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Hosting { return hostBuilder.ConfigureServices(services => { - services.AddSingleton(); + services.AddSingleton(); }); } diff --git a/src/Servers/Kestrel/Transport.Libuv/test/LibuvConnectionTests.cs b/src/Servers/Kestrel/Transport.Libuv/test/LibuvConnectionTests.cs index c284ec1584..d1ff9b380c 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/LibuvConnectionTests.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/LibuvConnectionTests.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; @@ -19,39 +19,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests [Fact] public async Task DoesNotEndConnectionOnZeroRead() { - var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); - var transportContext = new TestLibuvTransportContext { ConnectionDispatcher = mockConnectionDispatcher }; - var transport = new LibuvTransport(mockLibuv, transportContext, null); - var thread = new LibuvThread(transport); - Task connectionTask = null; + var transportContext = new TestLibuvTransportContext(); + var thread = new LibuvThread(mockLibuv, transportContext); + var listenerContext = new ListenerContext(transportContext) + { + Thread = thread + }; + try { await thread.StartAsync(); await thread.PostAsync(_ => - { - var listenerContext = new ListenerContext(transportContext) - { - Thread = thread - }; + { var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); - var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); - listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); - connectionTask = connection.Start(); + listenerContext.HandleConnection(socket); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 0, ref ignored); }, (object)null); - var readAwaitable = mockConnectionDispatcher.Input.Reader.ReadAsync(); + await using var connection = await listenerContext.AcceptAsync(); + + var readAwaitable = connection.Transport.Input.ReadAsync(); Assert.False(readAwaitable.IsCompleted); } finally { - mockConnectionDispatcher.Input.Reader.Complete(); - mockConnectionDispatcher.Output.Writer.Complete(); - await connectionTask; - await thread.StopAsync(TimeSpan.FromSeconds(5)); } } @@ -59,25 +53,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests [Fact] public async Task ConnectionDoesNotResumeAfterSocketCloseIfBackpressureIsApplied() { - var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); - var transportContext = new TestLibuvTransportContext { ConnectionDispatcher = mockConnectionDispatcher }; - var transport = new LibuvTransport(mockLibuv, transportContext, null); - var thread = new LibuvThread(transport); - mockConnectionDispatcher.InputOptions = pool => - new PipeOptions( - pool: pool, + var transportContext = new TestLibuvTransportContext(); + var thread = new LibuvThread(mockLibuv, transportContext); + var listenerContext = new ListenerContext(transportContext) + { + Thread = thread, + InputOptions = new PipeOptions( + pool: thread.MemoryPool, pauseWriterThreshold: 3, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, - useSynchronizationContext: false); + useSynchronizationContext: false), - // We don't set the output writer scheduler here since we want to run the callback inline + // We don't set the output writer scheduler here since we want to run the callback inline + OutputOptions = new PipeOptions( + pool: thread.MemoryPool, + readerScheduler: thread, + writerScheduler: PipeScheduler.Inline, + useSynchronizationContext: false) + }; - mockConnectionDispatcher.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); - - - Task connectionTask = null; try { await thread.StartAsync(); @@ -85,28 +81,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests // Write enough to make sure back pressure will be applied await thread.PostAsync(_ => { - var listenerContext = new ListenerContext(transportContext) - { - Thread = thread - }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); - var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); - listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); - connectionTask = connection.Start(); + listenerContext.HandleConnection(socket); mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored); }, null); + var connection = await listenerContext.AcceptAsync(); + // Now assert that we removed the callback from libuv to stop reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now complete the output writer so that the connection closes - mockConnectionDispatcher.Output.Writer.Complete(); - - await connectionTask.DefaultTimeout(); + await connection.DisposeAsync(); // Assert that we don't try to start reading Assert.Null(mockLibuv.AllocCallback); @@ -114,9 +104,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests } finally { - mockConnectionDispatcher.Input.Reader.Complete(); - mockConnectionDispatcher.Output.Writer.Complete(); - await thread.StopAsync(TimeSpan.FromSeconds(5)); } } @@ -124,29 +111,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests [Fact] public async Task ConnectionDoesNotResumeAfterReadCallbackScheduledAndSocketCloseIfBackpressureIsApplied() { - var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); - var transportContext = new TestLibuvTransportContext { ConnectionDispatcher = mockConnectionDispatcher }; - var transport = new LibuvTransport(mockLibuv, transportContext, null); - var thread = new LibuvThread(transport); + var transportContext = new TestLibuvTransportContext(); + var thread = new LibuvThread(mockLibuv, transportContext); var mockScheduler = new Mock(); Action backPressure = null; mockScheduler.Setup(m => m.Schedule(It.IsAny>(), It.IsAny())).Callback, object>((a, o) => { backPressure = () => a(o); }); - mockConnectionDispatcher.InputOptions = pool => - new PipeOptions( - pool: pool, + var listenerContext = new ListenerContext(transportContext) + { + Thread = thread, + InputOptions = new PipeOptions( + pool: thread.MemoryPool, pauseWriterThreshold: 3, resumeWriterThreshold: 3, writerScheduler: mockScheduler.Object, readerScheduler: PipeScheduler.Inline, - useSynchronizationContext: false); + useSynchronizationContext: false), + OutputOptions = new PipeOptions( + pool: thread.MemoryPool, + readerScheduler: thread, + writerScheduler: PipeScheduler.Inline, + useSynchronizationContext: false) + }; - mockConnectionDispatcher.OutputOptions = pool => new PipeOptions(pool: pool, readerScheduler: thread, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); - - Task connectionTask = null; try { await thread.StartAsync(); @@ -154,32 +144,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests // Write enough to make sure back pressure will be applied await thread.PostAsync(_ => { - var listenerContext = new ListenerContext(transportContext) - { - Thread = thread - }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); - var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); - listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); - connectionTask = connection.Start(); - + listenerContext.HandleConnection(socket); + mockLibuv.AllocCallback(socket.InternalGetHandle(), 2048, out var ignored); mockLibuv.ReadCallback(socket.InternalGetHandle(), 5, ref ignored); }, null); + var connection = await listenerContext.AcceptAsync(); + // Now assert that we removed the callback from libuv to stop reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); // Now release backpressure by reading the input - var result = await mockConnectionDispatcher.Input.Reader.ReadAsync(); + var result = await connection.Transport.Input.ReadAsync(); // Calling advance will call into our custom scheduler that captures the back pressure // callback - mockConnectionDispatcher.Input.Reader.AdvanceTo(result.Buffer.End); + connection.Transport.Input.AdvanceTo(result.Buffer.End); // Cancel the current pending flush - mockConnectionDispatcher.Input.Writer.CancelPendingFlush(); + connection.Application.Output.CancelPendingFlush(); // Now release the back pressure await thread.PostAsync(a => a(), backPressure); @@ -189,19 +175,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests Assert.Null(mockLibuv.ReadCallback); // Now complete the output writer and wait for the connection to close - mockConnectionDispatcher.Output.Writer.Complete(); - - await connectionTask.DefaultTimeout(); - + await connection.DisposeAsync(); + // Assert that we don't try to start reading Assert.Null(mockLibuv.AllocCallback); Assert.Null(mockLibuv.ReadCallback); } finally { - mockConnectionDispatcher.Input.Reader.Complete(); - mockConnectionDispatcher.Output.Writer.Complete(); - await thread.StopAsync(TimeSpan.FromSeconds(5)); } } @@ -209,42 +190,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests [Fact] public async Task DoesNotThrowIfOnReadCallbackCalledWithEOFButAllocCallbackNotCalled() { - var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); - var transportContext = new TestLibuvTransportContext { ConnectionDispatcher = mockConnectionDispatcher }; - var transport = new LibuvTransport(mockLibuv, transportContext, null); - var thread = new LibuvThread(transport); + var transportContext = new TestLibuvTransportContext(); + var thread = new LibuvThread(mockLibuv, transportContext); + var listenerContext = new ListenerContext(transportContext) + { + Thread = thread + }; - Task connectionTask = null; try { await thread.StartAsync(); await thread.PostAsync(_ => { - var listenerContext = new ListenerContext(transportContext) - { - Thread = thread - }; var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); - var connection = new LibuvConnection(socket, listenerContext.TransportContext.Log, thread, null, null); - listenerContext.TransportContext.ConnectionDispatcher.OnConnection(connection); - connectionTask = connection.Start(); - + listenerContext.HandleConnection(socket); + var ignored = new LibuvFunctions.uv_buf_t(); mockLibuv.ReadCallback(socket.InternalGetHandle(), TestConstants.EOF, ref ignored); }, (object)null); - var readAwaitable = await mockConnectionDispatcher.Input.Reader.ReadAsync(); + await using var connection = await listenerContext.AcceptAsync(); + + var readAwaitable = await connection.Transport.Input.ReadAsync(); Assert.True(readAwaitable.IsCompleted); } finally { - mockConnectionDispatcher.Input.Reader.Complete(); - mockConnectionDispatcher.Output.Writer.Complete(); - await connectionTask; - await thread.StopAsync(TimeSpan.FromSeconds(5)); } } } -} \ No newline at end of file +} diff --git a/src/Servers/Kestrel/Transport.Libuv/test/LibuvOutputConsumerTests.cs b/src/Servers/Kestrel/Transport.Libuv/test/LibuvOutputConsumerTests.cs index d72e6485b5..f0a9769357 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/LibuvOutputConsumerTests.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/LibuvOutputConsumerTests.cs @@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; @@ -42,11 +41,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests public LibuvOutputConsumerTests() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); _mockLibuv = new MockLibuv(); - var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions((ulong)0)); - _libuvThread = new LibuvThread(libuvTransport, maxLoops: 1); + var context = new TestLibuvTransportContext(); + _libuvThread = new LibuvThread(_mockLibuv, context, maxLoops: 1); _libuvThread.StartAsync().Wait(); } @@ -769,5 +768,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests outputReader.Complete(ex); } } + + // Work around the internal type conflict (multiple assemblies have internalized this type and that fails with IVT) + private class DuplexPipe : IDuplexPipe + { + public DuplexPipe(PipeReader reader, PipeWriter writer) + { + Input = reader; + Output = writer; + } + + public PipeReader Input { get; } + + public PipeWriter Output { get; } + + public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions) + { + var input = new Pipe(inputOptions); + var output = new Pipe(outputOptions); + + var transportToApplication = new DuplexPipe(output.Reader, input.Writer); + var applicationToTransport = new DuplexPipe(input.Reader, output.Writer); + + return new DuplexPipePair(applicationToTransport, transportToApplication); + } + + // This class exists to work around issues with value tuple on .NET Framework + public readonly struct DuplexPipePair + { + public IDuplexPipe Transport { get; } + public IDuplexPipe Application { get; } + + public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application) + { + Transport = transport; + Application = application; + } + } + } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/test/LibuvThreadTests.cs b/src/Servers/Kestrel/Transport.Libuv/test/LibuvThreadTests.cs index 69daacab35..7bfd5280ef 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/LibuvThreadTests.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/LibuvThreadTests.cs @@ -14,11 +14,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests [Fact] public async Task LibuvThreadDoesNotThrowIfPostingWorkAfterDispose() { - var mockConnectionDispatcher = new MockConnectionDispatcher(); var mockLibuv = new MockLibuv(); - var transportContext = new TestLibuvTransportContext { ConnectionDispatcher = mockConnectionDispatcher }; - var transport = new LibuvTransport(mockLibuv, transportContext, null); - var thread = new LibuvThread(transport); + var transportContext = new TestLibuvTransportContext(); + var thread = new LibuvThread(mockLibuv, transportContext); var ranOne = false; var ranTwo = false; var ranThree = false; diff --git a/src/Servers/Kestrel/Transport.Libuv/test/LibuvTransportTests.cs b/src/Servers/Kestrel/Transport.Libuv/test/LibuvTransportTests.cs index abdd8e1c7a..d18af3aa39 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/LibuvTransportTests.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/LibuvTransportTests.cs @@ -1,18 +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; +using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Text; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; @@ -23,71 +23,154 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class LibuvTransportTests { - public static TheoryData ConnectionAdapterData => new TheoryData - { - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) - { - ConnectionAdapters = { new PassThroughConnectionAdapter() } - } - }; - public static IEnumerable OneToTen => Enumerable.Range(1, 10).Select(i => new object[] { i }); [Fact] public async Task TransportCanBindAndStop() { var transportContext = new TestLibuvTransportContext(); - var transport = new LibuvTransport(transportContext, - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))); + var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); // The transport can no longer start threads without binding to an endpoint. await transport.BindAsync(); - await transport.StopAsync(); + await transport.DisposeAsync(); } [Fact] public async Task TransportCanBindUnbindAndStop() { var transportContext = new TestLibuvTransportContext(); - var transport = new LibuvTransport(transportContext, new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))); + var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); await transport.BindAsync(); await transport.UnbindAsync(); - await transport.StopAsync(); + await transport.DisposeAsync(); } - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions) + [Fact] + public async Task ConnectionCanReadAndWrite() { - var serviceContext = new TestServiceContext(); - listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, new DummyApplication(TestApp.EchoApp), HttpProtocols.Http1); - - var transportContext = new TestLibuvTransportContext - { - ConnectionDispatcher = new ConnectionDispatcher(serviceContext, listenOptions.Build()) - }; - - var transport = new LibuvTransport(transportContext, listenOptions); + var transportContext = new TestLibuvTransportContext(); + await using var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); await transport.BindAsync(); + var endpoint = (IPEndPoint)transport.EndPoint; - using (var socket = TestConnection.CreateConnectedLoopbackSocket(listenOptions.IPEndPoint.Port)) + async Task EchoServerAsync() { - var data = "Hello World"; - socket.Send(Encoding.ASCII.GetBytes($"POST / HTTP/1.0\r\nContent-Length: 11\r\n\r\n{data}")); + while (true) + { + await using var connection = await transport.AcceptAsync(); + + if (connection == null) + { + break; + } + + while (true) + { + var result = await connection.Transport.Input.ReadAsync(); + + if (result.IsCompleted) + { + break; + } + await connection.Transport.Output.WriteAsync(result.Buffer.ToArray()); + + connection.Transport.Input.AdvanceTo(result.Buffer.End); + } + } + } + + var serverTask = EchoServerAsync(); + + using (var socket = TestConnection.CreateConnectedLoopbackSocket(endpoint.Port)) + { + var data = Encoding.ASCII.GetBytes("Hello World"); + await socket.SendAsync(data, SocketFlags.None); + var buffer = new byte[data.Length]; var read = 0; while (read < data.Length) { - read += socket.Receive(buffer, read, buffer.Length - read, SocketFlags.None); + read += await socket.ReceiveAsync(buffer.AsMemory(read, buffer.Length - read), SocketFlags.None); + } + + Assert.Equal(data, buffer); + } + + await transport.UnbindAsync(); + + await serverTask.DefaultTimeout(); + } + + [Fact] + public async Task UnacceptedConnectionsAreAborted() + { + var transportContext = new TestLibuvTransportContext(); + var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); + + await transport.BindAsync(); + var endpoint = (IPEndPoint)transport.EndPoint; + + async Task ConnectAsync() + { + using (var socket = TestConnection.CreateConnectedLoopbackSocket(endpoint.Port)) + { + try + { + var read = await socket.ReceiveAsync(new byte[10], SocketFlags.None); + Assert.Equal(0, read); + } + catch (SocketException) + { + // The connection can be reset sometimes + } } } - Assert.True(await serviceContext.ConnectionManager.CloseAllConnectionsAsync(new CancellationTokenSource(TestConstants.DefaultTimeout).Token)); + var connectTask = ConnectAsync(); + await transport.UnbindAsync(); - await transport.StopAsync(); + await transport.DisposeAsync(); + + // The connection was accepted because libuv eagerly accepts connections + // they sit in a queue in each listener, we want to make sure that resources + // are cleaned up if they are never accepted by the caller + + await connectTask.DefaultTimeout(); + } + + [Fact] + public async Task CallingAcceptAfterDisposeAsyncThrows() + { + var transportContext = new TestLibuvTransportContext(); + var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); + + await transport.BindAsync(); + var endpoint = (IPEndPoint)transport.EndPoint; + + await transport.UnbindAsync(); + await transport.DisposeAsync(); + + await Assert.ThrowsAsync(() => transport.AcceptAsync().AsTask()); + } + + [Fact] + public async Task CallingDisposeAsyncWillYieldPendingAccepts() + { + var transportContext = new TestLibuvTransportContext(); + await using var transport = new LibuvConnectionListener(transportContext, new IPEndPoint(IPAddress.Loopback, 0)); + + await transport.BindAsync(); + + var acceptTask = transport.AcceptAsync(); + + await transport.UnbindAsync(); + + var connection = await acceptTask.DefaultTimeout(); + + Assert.Null(connection); } [ConditionalTheory] @@ -106,13 +189,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests var transportContext = new TestLibuvTransportContext { - ConnectionDispatcher = new ConnectionDispatcher(serviceContext, listenOptions.Build()), Options = new LibuvTransportOptions { ThreadCount = threadCount } }; - var transport = new LibuvTransport(transportContext, listenOptions); - + await using var transport = new LibuvConnectionListener(transportContext, listenOptions.EndPoint); await transport.BindAsync(); + listenOptions.EndPoint = transport.EndPoint; + + var dispatcher = new ConnectionDispatcher(serviceContext, listenOptions.Build()); + var acceptTask = dispatcher.StartAcceptingConnections(transport); using (var client = new HttpClient()) { @@ -132,12 +217,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests await transport.UnbindAsync(); - if (!await serviceContext.ConnectionManager.CloseAllConnectionsAsync(default).ConfigureAwait(false)) - { - await serviceContext.ConnectionManager.AbortAllConnectionsAsync().ConfigureAwait(false); - } + await acceptTask; - await transport.StopAsync(); + if (!await serviceContext.ConnectionManager.CloseAllConnectionsAsync(default)) + { + await serviceContext.ConnectionManager.AbortAllConnectionsAsync(); + } } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/test/ListenerPrimaryTests.cs b/src/Servers/Kestrel/Transport.Libuv/test/ListenerPrimaryTests.cs index b1a338bd56..b3ed1b94fe 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/ListenerPrimaryTests.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/ListenerPrimaryTests.cs @@ -2,16 +2,10 @@ // 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.IO; using System.Linq; using System.Net; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; @@ -28,42 +22,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { var libuv = new LibuvFunctions(); - var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var endpoint = new IPEndPoint(IPAddress.Loopback, 0); - var serviceContextPrimary = new TestServiceContext(); var transportContextPrimary = new TestLibuvTransportContext(); - var builderPrimary = new ConnectionBuilder(); - builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); - transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); - - var serviceContextSecondary = new TestServiceContext(); - var builderSecondary = new ConnectionBuilder(); - builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1); var transportContextSecondary = new TestLibuvTransportContext(); - transportContextSecondary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextSecondary, builderSecondary.Build()); - - var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener - var libuvThreadPrimary = new LibuvThread(libuvTransport); + var libuvThreadPrimary = new LibuvThread(libuv, transportContextPrimary); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); - await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); - var address = GetUri(listenOptions); + await listenerPrimary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadPrimary); + var address = GetUri(listenerPrimary.EndPoint); - // Until a secondary listener is added, TCP connections get dispatched directly - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); + var acceptTask = listenerPrimary.AcceptAsync().AsTask(); + using (var socket = await HttpClientSlim.GetSocket(address)) + { + await (await acceptTask.DefaultTimeout()).DisposeAsync(); + } + + acceptTask = listenerPrimary.AcceptAsync().AsTask(); + using (var socket = await HttpClientSlim.GetSocket(address)) + { + await (await acceptTask.DefaultTimeout()).DisposeAsync(); + } var listenerCount = listenerPrimary.UvPipeCount; // Add secondary listener - var libuvThreadSecondary = new LibuvThread(libuvTransport); + var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); - await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); + await listenerSecondary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadSecondary); var maxWait = Task.Delay(TestConstants.DefaultTimeout); // wait for ListenerPrimary.ReadCallback to add the secondary pipe @@ -77,14 +68,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests } // Once a secondary listener is added, TCP connections start getting dispatched to it - await AssertResponseEventually(address, "Secondary", allowed: new[] { "Primary" }); + // This returns the incomplete primary task after the secondary listener got the last + // connection + var primary = await WaitForSecondaryListener(address, listenerPrimary, listenerSecondary); // TCP connections will still get round-robined to the primary listener - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); + ListenerContext currentListener = listenerSecondary; + Task expected = primary; + + await AssertRoundRobin(address, listenerPrimary, listenerSecondary, currentListener, expected); await listenerSecondary.DisposeAsync(); + await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); await listenerPrimary.DisposeAsync(); @@ -96,48 +91,36 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests public async Task NonListenerPipeConnectionsAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); - var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var endpoint = new IPEndPoint(IPAddress.Loopback, 0); var logger = new TestApplicationErrorLogger(); - var serviceContextPrimary = new TestServiceContext(); - var builderPrimary = new ConnectionBuilder(); - builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); var transportContextPrimary = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; - transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); - - var serviceContextSecondary = new TestServiceContext - { - DateHeaderValueManager = serviceContextPrimary.DateHeaderValueManager, - ServerOptions = serviceContextPrimary.ServerOptions, - Scheduler = serviceContextPrimary.Scheduler, - HttpParser = serviceContextPrimary.HttpParser, - }; - var builderSecondary = new ConnectionBuilder(); - builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1); var transportContextSecondary = new TestLibuvTransportContext(); - transportContextSecondary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextSecondary, builderSecondary.Build()); - - var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener - var libuvThreadPrimary = new LibuvThread(libuvTransport); + var libuvThreadPrimary = new LibuvThread(libuv, transportContextPrimary); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); - await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); - var address = GetUri(listenOptions); + await listenerPrimary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadPrimary); + var address = GetUri(listenerPrimary.EndPoint); // Add secondary listener - var libuvThreadSecondary = new LibuvThread(libuvTransport); + var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); - await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); + await listenerSecondary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadSecondary); // TCP Connections get round-robined - await AssertResponseEventually(address, "Secondary", allowed: new[] { "Primary" }); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); + var primary = await WaitForSecondaryListener(address, listenerPrimary, listenerSecondary); + + // Make sure the pending accept get yields + using (var socket = await HttpClientSlim.GetSocket(address)) + { + await (await primary.DefaultTimeout()).DisposeAsync(); + } // Create a pipe connection and keep it open without sending any data var connectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -173,9 +156,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests await connectTcs.Task; // TCP connections will still get round-robined between only the two listeners - Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); + await AssertRoundRobin(address, listenerPrimary, listenerSecondary, listenerPrimary); await libuvThreadPrimary.PostAsync(_ => pipe.Dispose(), (object)null); @@ -186,9 +167,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests } // Same for after the non-listener pipe connection is closed - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Secondary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); + await AssertRoundRobin(address, listenerPrimary, listenerSecondary, listenerPrimary); await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); @@ -207,45 +186,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests public async Task PipeConnectionsWithWrongMessageAreLoggedAndIgnored() { var libuv = new LibuvFunctions(); - var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var endpoint = new IPEndPoint(IPAddress.Loopback, 0); var logger = new TestApplicationErrorLogger(); - var serviceContextPrimary = new TestServiceContext(); - var builderPrimary = new ConnectionBuilder(); - builderPrimary.UseHttpServer(serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary")), HttpProtocols.Http1); var transportContextPrimary = new TestLibuvTransportContext { Log = new LibuvTrace(logger) }; - transportContextPrimary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextPrimary, builderPrimary.Build()); - - var serviceContextSecondary = new TestServiceContext - { - DateHeaderValueManager = serviceContextPrimary.DateHeaderValueManager, - ServerOptions = serviceContextPrimary.ServerOptions, - Scheduler = serviceContextPrimary.Scheduler, - HttpParser = serviceContextPrimary.HttpParser, - }; - var builderSecondary = new ConnectionBuilder(); - builderSecondary.UseHttpServer(serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary")), HttpProtocols.Http1); var transportContextSecondary = new TestLibuvTransportContext(); - transportContextSecondary.ConnectionDispatcher = new ConnectionDispatcher(serviceContextSecondary, builderSecondary.Build()); - - var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); // Start primary listener - var libuvThreadPrimary = new LibuvThread(libuvTransport); + var libuvThreadPrimary = new LibuvThread(libuv, transportContextPrimary); await libuvThreadPrimary.StartAsync(); var listenerPrimary = new ListenerPrimary(transportContextPrimary); - await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); - var address = GetUri(listenOptions); + await listenerPrimary.StartAsync(pipeName, pipeMessage, endpoint, libuvThreadPrimary); + var address = GetUri(listenerPrimary.EndPoint); // Add secondary listener with wrong pipe message - var libuvThreadSecondary = new LibuvThread(libuvTransport); + var libuvThreadSecondary = new LibuvThread(libuv, transportContextSecondary); await libuvThreadSecondary.StartAsync(); var listenerSecondary = new ListenerSecondary(transportContextSecondary); - await listenerSecondary.StartAsync(pipeName, Guid.NewGuid().ToByteArray(), listenOptions, libuvThreadSecondary); + await listenerSecondary.StartAsync(pipeName, Guid.NewGuid().ToByteArray(), endpoint, libuvThreadSecondary); // Wait up to 10 seconds for error to be logged for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) @@ -253,10 +215,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests await Task.Delay(100); } - // TCP Connections don't get round-robined - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); - Assert.Equal("Primary", await HttpClientSlim.GetStringAsync(address)); + // TCP Connections don't get round-robined. This should time out if the request goes to the secondary listener + for (int i = 0; i < 3; i++) + { + using var socket = await HttpClientSlim.GetSocket(address); + + await using var connection = await listenerPrimary.AcceptAsync().AsTask().DefaultTimeout(); + } await listenerSecondary.DisposeAsync(); await libuvThreadSecondary.StopAsync(TimeSpan.FromSeconds(5)); @@ -270,73 +235,73 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests Assert.Contains("Bad data", errorMessage.Exception.ToString()); } - private static async Task AssertResponseEventually( - Uri address, - string expected, - string[] allowed = null, - int maxRetries = 100, - int retryDelay = 100) + + private static async Task AssertRoundRobin(Uri address, ListenerPrimary listenerPrimary, ListenerSecondary listenerSecondary, ListenerContext currentListener, Task expected = null, int connections = 4) { - for (var i = 0; i < maxRetries; i++) + for (int i = 0; i < connections; i++) { - var response = await HttpClientSlim.GetStringAsync(address); - if (response == expected) + if (currentListener == listenerPrimary) { - return; + expected ??= listenerSecondary.AcceptAsync().AsTask(); + currentListener = listenerSecondary; + } + else + { + expected ??= listenerPrimary.AcceptAsync().AsTask(); + currentListener = listenerPrimary; } - if (allowed != null) + using var socket = await HttpClientSlim.GetSocket(address); + + await using var connection = await expected.DefaultTimeout(); + + expected = null; + } + } + + private static async Task> WaitForSecondaryListener(Uri address, ListenerContext listenerPrimary, ListenerContext listenerSecondary) + { + int maxRetries = 100; + int retryDelay = 100; + + Task primary = null; + Task secondary = null; + + for (var i = 0; i < maxRetries; i++) + { + primary ??= listenerPrimary.AcceptAsync().AsTask(); + secondary ??= listenerSecondary.AcceptAsync().AsTask(); + + using var _ = await HttpClientSlim.GetSocket(address); + + var task = await Task.WhenAny(primary, secondary); + + if (task == secondary) { - Assert.Contains(response, allowed); + // Dispose this connection now that we know the seconary listener is working + await (await secondary).DisposeAsync(); + + // Return the primary task (it should be incomplete), we do this so that we can + return primary; + } + else + { + // Dispose the connection + await (await primary).DisposeAsync(); + + primary = null; } await Task.Delay(retryDelay); } - Assert.True(false, $"'{address}' failed to respond with '{expected}' in {maxRetries} retries."); + Assert.True(false, $"'{address}' failed to get queued connection in secondary listener in {maxRetries} retries."); + return null; } - private static Uri GetUri(ListenOptions options) + private static Uri GetUri(EndPoint endpoint) { - if (options.Type != ListenType.IPEndPoint) - { - throw new InvalidOperationException($"Could not determine a proper URI for options with Type {options.Type}"); - } - - var scheme = options.ConnectionAdapters.Any(f => f.IsHttps) - ? "https" - : "http"; - - return new Uri($"{scheme}://{options.IPEndPoint}"); - } - - private class ConnectionBuilder : IConnectionBuilder - { - private readonly List> _components = new List>(); - - public IServiceProvider ApplicationServices { get; set; } - - public IConnectionBuilder Use(Func middleware) - { - _components.Add(middleware); - return this; - } - - public ConnectionDelegate Build() - { - ConnectionDelegate app = context => - { - return Task.CompletedTask; - }; - - for (int i = _components.Count - 1; i >= 0; i--) - { - var component = _components[i]; - app = component(app); - } - - return app; - } + return new Uri($"http://{endpoint}"); } } } diff --git a/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/MockConnectionDispatcher.cs b/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/MockConnectionDispatcher.cs deleted file mode 100644 index 01e0c049d0..0000000000 --- a/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/MockConnectionDispatcher.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Buffers; -using System.IO.Pipelines; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers -{ - public class MockConnectionDispatcher : IConnectionDispatcher - { - public Func, PipeOptions> InputOptions { get; set; } = pool => new PipeOptions(pool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); - public Func, PipeOptions> OutputOptions { get; set; } = pool => new PipeOptions(pool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); - - public Task OnConnection(TransportConnection connection) - { - Input = new Pipe(InputOptions(connection.MemoryPool)); - Output = new Pipe(OutputOptions(connection.MemoryPool)); - - connection.Transport = new DuplexPipe(Input.Reader, Output.Writer); - connection.Application = new DuplexPipe(Output.Reader, Input.Writer); - - return Task.CompletedTask; - } - - public Pipe Input { get; private set; } - public Pipe Output { get; private set; } - } -} diff --git a/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/TestLibuvTransportContext.cs b/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/TestLibuvTransportContext.cs index 155c31b2eb..1bccf57d32 100644 --- a/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/TestLibuvTransportContext.cs +++ b/src/Servers/Kestrel/Transport.Libuv/test/TestHelpers/TestLibuvTransportContext.cs @@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers var logger = new TestApplicationErrorLogger(); AppLifetime = new LifetimeNotImplemented(); - ConnectionDispatcher = new MockConnectionDispatcher(); Log = new LibuvTrace(logger); Options = new LibuvTransportOptions { ThreadCount = 1 }; } diff --git a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj index d41703c771..d5f1350fec 100644 --- a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj +++ b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp3.0.cs b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp3.0.cs index 2b9b9b1b87..34c7f982bb 100644 --- a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp3.0.cs +++ b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp3.0.cs @@ -11,15 +11,18 @@ namespace Microsoft.AspNetCore.Hosting } namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { - public sealed partial class SocketTransportFactory : Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransportFactory + public sealed partial class SocketTransportFactory : Microsoft.AspNetCore.Connections.IConnectionListenerFactory { - public SocketTransportFactory(Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - public Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.ITransport Create(Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IEndPointInformation endPointInformation, Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal.IConnectionDispatcher dispatcher) { throw null; } + public SocketTransportFactory(Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public System.Threading.Tasks.ValueTask BindAsync(System.Net.EndPoint endpoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public partial class SocketTransportOptions { public SocketTransportOptions() { } public int IOQueueCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public long? MaxReadBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public long? MaxWriteBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public bool NoDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } } namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index bfb467f94a..1c867f3199 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -11,19 +11,17 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal { - internal sealed class SocketConnection : TransportConnection, IDisposable + internal sealed class SocketConnection : TransportConnection { - private static readonly int MinAllocBufferSize = KestrelMemoryPool.MinimumSegmentSize / 2; + private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); private readonly Socket _socket; - private readonly PipeScheduler _scheduler; private readonly ISocketsTrace _trace; private readonly SocketReceiver _receiver; private readonly SocketSender _sender; @@ -32,8 +30,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal private readonly object _shutdownLock = new object(); private volatile bool _socketDisposed; private volatile Exception _shutdownReason; + private Task _processingTask; - internal SocketConnection(Socket socket, MemoryPool memoryPool, PipeScheduler scheduler, ISocketsTrace trace) + internal SocketConnection(Socket socket, + MemoryPool memoryPool, + PipeScheduler scheduler, + ISocketsTrace trace, + long? maxReadBufferSize = null, + long? maxWriteBufferSize = null) { Debug.Assert(socket != null); Debug.Assert(memoryPool != null); @@ -41,35 +45,46 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal _socket = socket; MemoryPool = memoryPool; - _scheduler = scheduler; - Logger = trace; _trace = trace; - var localEndPoint = (IPEndPoint)_socket.LocalEndPoint; - var remoteEndPoint = (IPEndPoint)_socket.RemoteEndPoint; - - LocalAddress = localEndPoint.Address; - LocalPort = localEndPoint.Port; - - RemoteAddress = remoteEndPoint.Address; - RemotePort = remoteEndPoint.Port; + LocalEndPoint = _socket.LocalEndPoint; + RemoteEndPoint = _socket.RemoteEndPoint; ConnectionClosed = _connectionClosedTokenSource.Token; // On *nix platforms, Sockets already dispatches to the ThreadPool. // Yes, the IOQueues are still used for the PipeSchedulers. This is intentional. // https://github.com/aspnet/KestrelHttpServer/issues/2573 - var awaiterScheduler = IsWindows ? _scheduler : PipeScheduler.Inline; + var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline; _receiver = new SocketReceiver(_socket, awaiterScheduler); _sender = new SocketSender(_socket, awaiterScheduler); + + maxReadBufferSize ??= 0; + maxWriteBufferSize ??= 0; + + var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); + var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); + + var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); + + // Set the transport and connection id + Transport = pair.Transport; + Application = pair.Application; } - public override MemoryPool MemoryPool { get; } - public override PipeScheduler InputWriterScheduler => _scheduler; - public override PipeScheduler OutputReaderScheduler => _scheduler; + public PipeWriter Input => Application.Output; - public async Task StartAsync() + public PipeReader Output => Application.Input; + + public override MemoryPool MemoryPool { get; } + + public void Start() + { + _processingTask = StartAsync(); + } + + private async Task StartAsync() { try { @@ -83,7 +98,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal _receiver.Dispose(); _sender.Dispose(); - ThreadPool.UnsafeQueueUserWorkItem(state => ((SocketConnection)state).CancelConnectionClosedToken(), this); + + // Fire the connection closed token and wait for it to complete + var waitForConnectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + ThreadPool.UnsafeQueueUserWorkItem(state => + { + (var connection, var tcs) = state; + + connection.CancelConnectionClosedToken(); + + tcs.TrySetResult(null); + }, + (this, waitForConnectionClosedTcs), + preferLocal: false); + + await waitForConnectionClosedTcs.Task; } catch (Exception ex) { @@ -101,10 +131,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal } // Only called after connection middleware is complete which means the ConnectionClosed token has fired. - public void Dispose() + public override async ValueTask DisposeAsync() { + Transport.Input.Complete(); + Transport.Output.Complete(); + + if (_processingTask != null) + { + await _processingTask; + } + _connectionClosedTokenSource.Dispose(); - _connectionClosingCts.Dispose(); } private async Task DoReceive() @@ -211,7 +248,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal } catch (SocketException ex) when (IsConnectionResetError(ex.SocketErrorCode)) { - shutdownReason = new ConnectionResetException(ex.Message, ex);; + shutdownReason = new ConnectionResetException(ex.Message, ex); _trace.ConnectionReset(ConnectionId); } catch (Exception ex) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj b/src/Servers/Kestrel/Transport.Sockets/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj index e5ffbb53b6..337685e9b7 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj +++ b/src/Servers/Kestrel/Transport.Sockets/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj @@ -10,9 +10,14 @@ CS1591;$(NoWarn) + + + + + - + diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs new file mode 100644 index 0000000000..b51e22e61e --- /dev/null +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs @@ -0,0 +1,139 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.IO.Pipelines; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets +{ + internal sealed class SocketConnectionListener : IConnectionListener + { + private readonly MemoryPool _memoryPool; + private readonly int _numSchedulers; + private readonly PipeScheduler[] _schedulers; + private readonly ISocketsTrace _trace; + private Socket _listenSocket; + private int _schedulerIndex; + private readonly SocketTransportOptions _options; + + public EndPoint EndPoint { get; private set; } + + internal SocketConnectionListener( + EndPoint endpoint, + SocketTransportOptions options, + ISocketsTrace trace) + { + Debug.Assert(endpoint != null); + Debug.Assert(endpoint is IPEndPoint); + Debug.Assert(trace != null); + + EndPoint = endpoint; + _trace = trace; + _options = options; + _memoryPool = _options.MemoryPoolFactory(); + var ioQueueCount = options.IOQueueCount; + + if (ioQueueCount > 0) + { + _numSchedulers = ioQueueCount; + _schedulers = new IOQueue[_numSchedulers]; + + for (var i = 0; i < _numSchedulers; i++) + { + _schedulers[i] = new IOQueue(); + } + } + else + { + var directScheduler = new PipeScheduler[] { PipeScheduler.ThreadPool }; + _numSchedulers = directScheduler.Length; + _schedulers = directScheduler; + } + } + + internal void Bind() + { + if (_listenSocket != null) + { + throw new InvalidOperationException(SocketsStrings.TransportAlreadyBound); + } + + // TODO: Add support for UnixDomainSocket + + var listenSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + // Kestrel expects IPv6Any to bind to both IPv6 and IPv4 + if (EndPoint is IPEndPoint ip && ip.Address == IPAddress.IPv6Any) + { + listenSocket.DualMode = true; + } + + try + { + listenSocket.Bind(EndPoint); + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse) + { + throw new AddressInUseException(e.Message, e); + } + + EndPoint = listenSocket.LocalEndPoint; + + listenSocket.Listen(512); + + _listenSocket = listenSocket; + } + + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + while (true) + { + try + { + var acceptSocket = await _listenSocket.AcceptAsync(); + acceptSocket.NoDelay = _options.NoDelay; + + var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace, _options.MaxReadBufferSize, _options.MaxWriteBufferSize); + + connection.Start(); + + _schedulerIndex = (_schedulerIndex + 1) % _numSchedulers; + + return connection; + } + catch (ObjectDisposedException) + { + // A call was made to UnbindAsync/DisposeAsync just return null which signals we're done + return null; + } + catch (SocketException) + { + // The connection got reset while it was in the backlog, so we try again. + _trace.ConnectionReset(connectionId: "(null)"); + } + } + } + + public ValueTask UnbindAsync(CancellationToken cancellationToken = default) + { + _listenSocket?.Dispose(); + return default; + } + + public ValueTask DisposeAsync() + { + _listenSocket?.Dispose(); + // Dispose the memory pool + _memoryPool.Dispose(); + return default; + } + } +} diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransport.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransport.cs deleted file mode 100644 index a1b01d45bb..0000000000 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransport.cs +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Buffers; -using System.Diagnostics; -using System.IO.Pipelines; -using System.Net; -using System.Net.Sockets; -using System.Runtime.ExceptionServices; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets -{ - internal sealed class SocketTransport : ITransport - { - private readonly MemoryPool _memoryPool; - private readonly IEndPointInformation _endPointInformation; - private readonly IConnectionDispatcher _dispatcher; - private readonly IHostApplicationLifetime _appLifetime; - private readonly int _numSchedulers; - private readonly PipeScheduler[] _schedulers; - private readonly ISocketsTrace _trace; - private Socket _listenSocket; - private Task _listenTask; - private Exception _listenException; - private volatile bool _unbinding; - - internal SocketTransport( - IEndPointInformation endPointInformation, - IConnectionDispatcher dispatcher, - IHostApplicationLifetime applicationLifetime, - int ioQueueCount, - ISocketsTrace trace, - MemoryPool memoryPool) - { - Debug.Assert(endPointInformation != null); - Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint); - Debug.Assert(dispatcher != null); - Debug.Assert(applicationLifetime != null); - Debug.Assert(trace != null); - - _endPointInformation = endPointInformation; - _dispatcher = dispatcher; - _appLifetime = applicationLifetime; - _trace = trace; - _memoryPool = memoryPool; - - if (ioQueueCount > 0) - { - _numSchedulers = ioQueueCount; - _schedulers = new IOQueue[_numSchedulers]; - - for (var i = 0; i < _numSchedulers; i++) - { - _schedulers[i] = new IOQueue(); - } - } - else - { - var directScheduler = new PipeScheduler[] { PipeScheduler.ThreadPool }; - _numSchedulers = directScheduler.Length; - _schedulers = directScheduler; - } - } - - public Task BindAsync() - { - if (_listenSocket != null) - { - throw new InvalidOperationException(SocketsStrings.TransportAlreadyBound); - } - - IPEndPoint endPoint = _endPointInformation.IPEndPoint; - - var listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - // Kestrel expects IPv6Any to bind to both IPv6 and IPv4 - if (endPoint.Address == IPAddress.IPv6Any) - { - listenSocket.DualMode = true; - } - - try - { - listenSocket.Bind(endPoint); - } - catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse) - { - throw new AddressInUseException(e.Message, e); - } - - // If requested port was "0", replace with assigned dynamic port. - if (_endPointInformation.IPEndPoint.Port == 0) - { - _endPointInformation.IPEndPoint = (IPEndPoint)listenSocket.LocalEndPoint; - } - - listenSocket.Listen(512); - - _listenSocket = listenSocket; - - _listenTask = Task.Run(() => RunAcceptLoopAsync()); - - return Task.CompletedTask; - } - - public async Task UnbindAsync() - { - if (_listenSocket != null) - { - _unbinding = true; - _listenSocket.Dispose(); - - Debug.Assert(_listenTask != null); - await _listenTask.ConfigureAwait(false); - - _unbinding = false; - _listenSocket = null; - _listenTask = null; - - if (_listenException != null) - { - var exInfo = ExceptionDispatchInfo.Capture(_listenException); - _listenException = null; - exInfo.Throw(); - } - } - } - - public Task StopAsync() - { - _memoryPool.Dispose(); - return Task.CompletedTask; - } - - private async Task RunAcceptLoopAsync() - { - try - { - while (true) - { - for (var schedulerIndex = 0; schedulerIndex < _numSchedulers; schedulerIndex++) - { - try - { - var acceptSocket = await _listenSocket.AcceptAsync(); - acceptSocket.NoDelay = _endPointInformation.NoDelay; - - var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[schedulerIndex], _trace); - - // REVIEW: This task should be tracked by the server for graceful shutdown - // Today it's handled specifically for http but not for arbitrary middleware - _ = HandleConnectionAsync(connection); - } - catch (SocketException) when (!_unbinding) - { - _trace.ConnectionReset(connectionId: "(null)"); - } - } - } - } - catch (Exception ex) - { - if (_unbinding) - { - // Means we must be unbinding. Eat the exception. - } - else - { - _trace.LogCritical(ex, $"Unexpected exception in {nameof(SocketTransport)}.{nameof(RunAcceptLoopAsync)}."); - _listenException = ex; - - // Request shutdown so we can rethrow this exception - // in Stop which should be observable. - _appLifetime.StopApplication(); - } - } - } - - private async Task HandleConnectionAsync(SocketConnection connection) - { - try - { - var middlewareTask = _dispatcher.OnConnection(connection); - var transportTask = connection.StartAsync(); - - await transportTask; - await middlewareTask; - - connection.Dispose(); - } - catch (Exception ex) - { - _trace.LogCritical(ex, $"Unexpected exception in {nameof(SocketTransport)}.{nameof(HandleConnectionAsync)}."); - } - } - } -} diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportFactory.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportFactory.cs index f6ef805fbb..1988897855 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportFactory.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportFactory.cs @@ -2,66 +2,46 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; -using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { -#pragma warning disable PUB0001 // Pubternal type in public API - public sealed class SocketTransportFactory : ITransportFactory -#pragma warning restore PUB0001 // Pubternal type in public API + public sealed class SocketTransportFactory : IConnectionListenerFactory { private readonly SocketTransportOptions _options; - private readonly IHostApplicationLifetime _appLifetime; private readonly SocketsTrace _trace; public SocketTransportFactory( IOptions options, - IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } - if (applicationLifetime == null) - { - throw new ArgumentNullException(nameof(applicationLifetime)); - } + if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } _options = options.Value; - _appLifetime = applicationLifetime; - var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"); + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"); _trace = new SocketsTrace(logger); } -#pragma warning disable PUB0001 // Pubternal type in public API - public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher dispatcher) -#pragma warning restore PUB0001 // Pubternal type in public API + public ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) { - if (endPointInformation == null) - { - throw new ArgumentNullException(nameof(endPointInformation)); - } - - if (endPointInformation.Type != ListenType.IPEndPoint) - { - throw new ArgumentException(SocketsStrings.OnlyIPEndPointsSupported, nameof(endPointInformation)); - } - - if (dispatcher == null) - { - throw new ArgumentNullException(nameof(dispatcher)); - } - - return new SocketTransport(endPointInformation, dispatcher, _appLifetime, _options.IOQueueCount, _trace, _options.MemoryPoolFactory()); + var transport = new SocketConnectionListener(endpoint, _options, _trace); + transport.Bind(); + return new ValueTask(transport); } } } diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs index 2dad914423..2ec6c52fd0 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs @@ -1,9 +1,9 @@ -// 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 System.Buffers; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using System.IO.Pipelines; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { @@ -17,6 +17,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets /// public int IOQueueCount { get; set; } = Math.Min(Environment.ProcessorCount, 16); - internal Func> MemoryPoolFactory { get; set; } = () => KestrelMemoryPool.Create(); + /// + /// Set to false to enable Nagle's algorithm for all connections. + /// + /// + /// Defaults to true. + /// + public bool NoDelay { get; set; } = true; + + public long? MaxReadBufferSize { get; set; } = PipeOptions.Default.PauseWriterThreshold; + + public long? MaxWriteBufferSize { get; set; } = PipeOptions.Default.PauseWriterThreshold; + + internal Func> MemoryPoolFactory { get; set; } = System.Buffers.MemoryPoolFactory.Create; } } diff --git a/src/Servers/Kestrel/Transport.Sockets/src/WebHostBuilderSocketExtensions.cs b/src/Servers/Kestrel/Transport.Sockets/src/WebHostBuilderSocketExtensions.cs index 95d27e46db..d073f91aa4 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/WebHostBuilderSocketExtensions.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/WebHostBuilderSocketExtensions.cs @@ -1,8 +1,8 @@ -// 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.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; using Microsoft.Extensions.DependencyInjection; @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Hosting { return hostBuilder.ConfigureServices(services => { - services.AddSingleton(); + services.AddSingleton(); }); } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/ChunkWriterBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/ChunkWriterBenchmark.cs index 3b24e49cab..c327d8d51f 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/ChunkWriterBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/ChunkWriterBenchmark.cs @@ -6,7 +6,6 @@ using System.IO.Pipelines; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -21,7 +20,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [GlobalSetup] public void Setup() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); var pipe = new Pipe(new PipeOptions(_memoryPool)); _reader = pipe.Reader; _writer = pipe.Writer; diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionBenchmark.cs index c404a16c27..d7bdd0a709 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionBenchmark.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -27,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [GlobalSetup] public void Setup() { - var memoryPool = KestrelMemoryPool.Create(); + var memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs index 84ac57e630..8ffe0a0059 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -23,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [IterationSetup] public void Setup() { - var memoryPool = KestrelMemoryPool.Create(); + var memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ReadingBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ReadingBenchmark.cs index 122c5b694c..2faca73d6a 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ReadingBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1ReadingBenchmark.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; namespace Microsoft.AspNetCore.Server.Kestrel.Performance @@ -35,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [GlobalSetup] public void GlobalSetup() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); _http1Connection = MakeHttp1Connection(); } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1WritingBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1WritingBenchmark.cs index d0c1cf3370..94dc4e66c5 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Http1WritingBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http1WritingBenchmark.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; namespace Microsoft.AspNetCore.Server.Kestrel.Performance @@ -35,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [GlobalSetup] public void GlobalSetup() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); _http1Connection = MakeHttp1Connection(); } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/HttpProtocolFeatureCollection.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/HttpProtocolFeatureCollection.cs index 3f8bae879f..7f948c5a4b 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/HttpProtocolFeatureCollection.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/HttpProtocolFeatureCollection.cs @@ -1,7 +1,8 @@ -// 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 System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Threading; @@ -11,7 +12,6 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance public HttpProtocolFeatureCollection() { - var memoryPool = KestrelMemoryPool.Create(); + var memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/InMemoryTransportBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/InMemoryTransportBenchmark.cs index d5421ab448..b3e4e13eff 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/InMemoryTransportBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/InMemoryTransportBenchmark.cs @@ -6,13 +6,15 @@ using System.Buffers; using System.Collections.Generic; using System.IO.Pipelines; using System.Linq; +using System.Net; using System.Text; +using System.Threading; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Server.Kestrel.Performance @@ -45,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance .UseKestrel() // Bind to a single non-HTTPS endpoint .UseUrls("http://127.0.0.1:5000") - .ConfigureServices(services => services.AddSingleton(transportFactory)) + .ConfigureServices(services => services.AddSingleton(transportFactory)) .Configure(app => app.UseMiddleware()) .Build(); @@ -94,21 +96,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance await _connection.ReadResponseAsync(_plaintextPipelinedExpectedResponse.Length); } - internal class InMemoryTransportFactory : ITransportFactory + internal class InMemoryTransportFactory : IConnectionListenerFactory { private readonly int _connectionsPerEndPoint; - private readonly Dictionary> _connections = - new Dictionary>(); + private readonly Dictionary> _connections = + new Dictionary>(); - public IReadOnlyDictionary> Connections => _connections; + public IReadOnlyDictionary> Connections => _connections; public InMemoryTransportFactory(int connectionsPerEndPoint) { _connectionsPerEndPoint = connectionsPerEndPoint; } - public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher handler) + public ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) { var connections = new InMemoryConnection[_connectionsPerEndPoint]; for (var i = 0; i < _connectionsPerEndPoint; i++) @@ -116,46 +118,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance connections[i] = new InMemoryConnection(); } - _connections.Add(endPointInformation, connections); + _connections.Add(endpoint, connections); - return new InMemoryTransport(handler, connections); + return new ValueTask(new InMemoryTransport(endpoint, connections)); } } - public class InMemoryTransport : ITransport + public class InMemoryTransport : IConnectionListener { - private readonly IConnectionDispatcher _dispatcher; private readonly IReadOnlyList _connections; + private readonly TaskCompletionSource _tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private int _acceptedConnections; - public InMemoryTransport(IConnectionDispatcher dispatcher, IReadOnlyList connections) + public InMemoryTransport(EndPoint endpoint, IReadOnlyList connections) { - _dispatcher = dispatcher; + EndPoint = endpoint; _connections = connections; } - public Task BindAsync() + public EndPoint EndPoint { get; } + + public ValueTask AcceptAsync(CancellationToken cancellationToken = default) { - foreach (var connection in _connections) + if (_acceptedConnections < _connections.Count) { - _dispatcher.OnConnection(connection); + return new ValueTask(_connections[_acceptedConnections++]); } - - return Task.CompletedTask; + return new ValueTask(_tcs.Task); } - public Task StopAsync() + public ValueTask DisposeAsync() { - return Task.CompletedTask; + _tcs.TrySetResult(null); + return default; } - public Task UnbindAsync() + public ValueTask UnbindAsync(CancellationToken cancellationToken = default) { - return Task.CompletedTask; + _tcs.TrySetResult(null); + return default; } } public class InMemoryConnection : TransportConnection { + public InMemoryConnection() + { + var inputOptions = new PipeOptions(useSynchronizationContext: false); + var outputOptions = new PipeOptions(useSynchronizationContext: false); + + var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); + + // Set the transport and connection id + Transport = pair.Transport; + Application = pair.Application; + } + + public PipeWriter Input => Application.Output; + + public PipeReader Output => Application.Input; + public ValueTask SendRequestAsync(byte[] request) { return Input.WriteAsync(request); diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj index e801048b67..93d77cab5f 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/PipeThroughputBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/PipeThroughputBenchmark.cs index b37656faec..03b3eb2f1d 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/PipeThroughputBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/PipeThroughputBenchmark.cs @@ -1,11 +1,10 @@ -// 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.Buffers; using System.IO.Pipelines; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [IterationSetup] public void Setup() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); _pipe = new Pipe(new PipeOptions(_memoryPool)); } diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/RequestParsingBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/RequestParsingBenchmark.cs index da9f0f7faf..a6cba51178 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/RequestParsingBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/RequestParsingBenchmark.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Performance { @@ -24,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [IterationSetup] public void Setup() { - _memoryPool = KestrelMemoryPool.Create(); + _memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs index 530a0ec968..417c5a8bbe 100644 --- a/src/Servers/Kestrel/perf/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs +++ b/src/Servers/Kestrel/perf/Kestrel.Performance/ResponseHeaderCollectionBenchmark.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 System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Text; @@ -12,7 +13,6 @@ using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Performance @@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [IterationSetup] public void Setup() { - var memoryPool = KestrelMemoryPool.Create(); + var memoryPool = MemoryPoolFactory.Create(); var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var pair = DuplexPipe.CreateConnectionPair(options, options); diff --git a/src/Servers/Kestrel/samples/Http2SampleApp/Program.cs b/src/Servers/Kestrel/samples/Http2SampleApp/Program.cs index f1fc2223cf..794f011ecc 100644 --- a/src/Servers/Kestrel/samples/Http2SampleApp/Program.cs +++ b/src/Servers/Kestrel/samples/Http2SampleApp/Program.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -29,9 +28,6 @@ namespace Http2SampleApp { var basePort = context.Configuration.GetValue("BASE_PORT") ?? 5000; - // Run callbacks on the transport thread - options.ApplicationSchedulingMode = SchedulingMode.Inline; - // Http/1.1 endpoint for comparison options.Listen(IPAddress.Any, basePort, listenOptions => { diff --git a/src/Servers/Kestrel/samples/PlaintextApp/PlaintextApp.csproj b/src/Servers/Kestrel/samples/PlaintextApp/PlaintextApp.csproj index 86fd68771b..8b48f958a9 100644 --- a/src/Servers/Kestrel/samples/PlaintextApp/PlaintextApp.csproj +++ b/src/Servers/Kestrel/samples/PlaintextApp/PlaintextApp.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -8,6 +8,7 @@ + diff --git a/src/Servers/Kestrel/samples/PlaintextApp/Startup.cs b/src/Servers/Kestrel/samples/PlaintextApp/Startup.cs index 98dc353f23..044e8b5dfe 100644 --- a/src/Servers/Kestrel/samples/PlaintextApp/Startup.cs +++ b/src/Servers/Kestrel/samples/PlaintextApp/Startup.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; namespace PlaintextApp @@ -31,7 +32,7 @@ namespace PlaintextApp }); } - public static Task Main(string[] args) + public static async Task Main(string[] args) { var host = new WebHostBuilder() .UseKestrel(options => @@ -42,7 +43,7 @@ namespace PlaintextApp .UseStartup() .Build(); - return host.RunAsync(); + await host.RunAsync(); } } diff --git a/src/Servers/Kestrel/samples/SampleApp/Startup.cs b/src/Servers/Kestrel/samples/SampleApp/Startup.cs index 71f218d816..82f505c510 100644 --- a/src/Servers/Kestrel/samples/SampleApp/Startup.cs +++ b/src/Servers/Kestrel/samples/SampleApp/Startup.cs @@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -85,19 +84,11 @@ namespace SampleApp var basePort = context.Configuration.GetValue("BASE_PORT") ?? 5000; - options.ConfigureEndpointDefaults(opt => - { - opt.NoDelay = true; - }); - options.ConfigureHttpsDefaults(httpsOptions => { httpsOptions.SslProtocols = SslProtocols.Tls12; }); - // Run callbacks on the transport thread - options.ApplicationSchedulingMode = SchedulingMode.Inline; - options.Listen(IPAddress.Loopback, basePort, listenOptions => { // Uncomment the following to enable Nagle's algorithm for this endpoint. @@ -148,7 +139,7 @@ namespace SampleApp .Configure(context.Configuration.GetSection("Kestrel")) .Endpoint("NamedEndpoint", opt => { - opt.ListenOptions.NoDelay = true; + }) .Endpoint("NamedHttpsEndpoint", opt => { diff --git a/src/Servers/Kestrel/samples/SystemdTestApp/Startup.cs b/src/Servers/Kestrel/samples/SystemdTestApp/Startup.cs index e91322a827..56e6f3f980 100644 --- a/src/Servers/Kestrel/samples/SystemdTestApp/Startup.cs +++ b/src/Servers/Kestrel/samples/SystemdTestApp/Startup.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -51,9 +50,6 @@ namespace SystemdTestApp { var basePort = context.Configuration.GetValue("BASE_PORT") ?? 5000; - // Run callbacks on the transport thread - options.ApplicationSchedulingMode = SchedulingMode.Inline; - options.Listen(IPAddress.Loopback, basePort, listenOptions => { // Uncomment the following to enable Nagle's algorithm for this endpoint. @@ -89,4 +85,4 @@ namespace SystemdTestApp return hostBuilder.Build().RunAsync(); } } -} \ No newline at end of file +} diff --git a/src/Servers/Kestrel/shared/test/TaskTimeoutExtensions.cs b/src/Servers/Kestrel/shared/test/TaskTimeoutExtensions.cs index 8e83a7a70e..68010b090a 100644 --- a/src/Servers/Kestrel/shared/test/TaskTimeoutExtensions.cs +++ b/src/Servers/Kestrel/shared/test/TaskTimeoutExtensions.cs @@ -7,6 +7,16 @@ namespace System.Threading.Tasks { public static class TaskTimeoutExtensions { + public static Task DefaultTimeout(this ValueTask task) + { + return task.AsTask().TimeoutAfter(TestConstants.DefaultTimeout); + } + + public static Task DefaultTimeout(this ValueTask task) + { + return task.AsTask().TimeoutAfter(TestConstants.DefaultTimeout); + } + public static Task DefaultTimeout(this Task task) { return task.TimeoutAfter(TestConstants.DefaultTimeout); diff --git a/src/Servers/Kestrel/shared/test/TestServiceContext.cs b/src/Servers/Kestrel/shared/test/TestServiceContext.cs index 7b6abf939d..e5332963aa 100644 --- a/src/Servers/Kestrel/shared/test/TestServiceContext.cs +++ b/src/Servers/Kestrel/shared/test/TestServiceContext.cs @@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Testing @@ -75,7 +74,7 @@ namespace Microsoft.AspNetCore.Testing public MockSystemClock MockSystemClock { get; set; } - public Func> MemoryPoolFactory { get; set; } = KestrelMemoryPool.Create; + public Func> MemoryPoolFactory { get; set; } = System.Buffers.MemoryPoolFactory.Create; public int ExpectedConnectionMiddlewareCount { get; set; } diff --git a/src/Servers/Kestrel/shared/test/TransportTestHelpers/DiagnosticMemoryPoolFactory.cs b/src/Servers/Kestrel/shared/test/TransportTestHelpers/DiagnosticMemoryPoolFactory.cs index d3a33bf2cf..26c8746e39 100644 --- a/src/Servers/Kestrel/shared/test/TransportTestHelpers/DiagnosticMemoryPoolFactory.cs +++ b/src/Servers/Kestrel/shared/test/TransportTestHelpers/DiagnosticMemoryPoolFactory.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; @@ -6,7 +6,6 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { @@ -29,7 +28,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { lock (_pools) { - var pool = new DiagnosticMemoryPool(KestrelMemoryPool.CreateSlabMemoryPool(), _allowLateReturn, _rentTracking); + var pool = new DiagnosticMemoryPool(new SlabMemoryPool(), _allowLateReturn, _rentTracking); _pools.Add(pool); return pool; } @@ -43,4 +42,4 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } } -} \ No newline at end of file +} diff --git a/src/Servers/Kestrel/shared/test/TransportTestHelpers/TestServer.cs b/src/Servers/Kestrel/shared/test/TransportTestHelpers/TestServer.cs index bbb6d6fe49..1382a70175 100644 --- a/src/Servers/Kestrel/shared/test/TransportTestHelpers/TestServer.cs +++ b/src/Servers/Kestrel/shared/test/TransportTestHelpers/TestServer.cs @@ -8,11 +8,11 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests _app = app; Context = context; - _host = TransportSelector.GetWebHostBuilder(context.MemoryPoolFactory) + _host = TransportSelector.GetWebHostBuilder(context.MemoryPoolFactory, context.ServerOptions.Limits.MaxRequestBufferSize) .UseKestrel(options => { configureKestrel(options); @@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests Assert.All(context.ServerOptions.ListenOptions, lo => Assert.Equal(context.ExpectedConnectionMiddlewareCount, lo._middleware.Count)); - return new KestrelServer(sp.GetRequiredService(), context); + return new KestrelServer(sp.GetRequiredService(), context); }); configureServices(services); }) diff --git a/src/Servers/Kestrel/test/FunctionalTests/MaxRequestBufferSizeTests.cs b/src/Servers/Kestrel/test/FunctionalTests/MaxRequestBufferSizeTests.cs index 540690317b..039b148db0 100644 --- a/src/Servers/Kestrel/test/FunctionalTests/MaxRequestBufferSizeTests.cs +++ b/src/Servers/Kestrel/test/FunctionalTests/MaxRequestBufferSizeTests.cs @@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests TaskCompletionSource clientFinishedSendingRequestBody, Func> memoryPoolFactory = null) { - var host = TransportSelector.GetWebHostBuilder(memoryPoolFactory) + var host = TransportSelector.GetWebHostBuilder(memoryPoolFactory, maxRequestBufferSize) .ConfigureServices(AddTestLogging) .UseKestrel(options => { diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs index 2f3f01a941..12ac5791ff 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/ChunkedRequestTests.cs @@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests [Fact] public async Task Http10KeepAliveTransferEncoding() { - var testContext = new TestServiceContext(); + var testContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(AppChunked, testContext)) { diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs index 6751565db1..424307ac26 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs @@ -23,7 +23,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; @@ -115,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests protected static readonly byte[] _noData = new byte[0]; protected static readonly byte[] _maxData = Encoding.ASCII.GetBytes(new string('a', Http2PeerSettings.MinAllowedMaxFrameSize)); - private readonly MemoryPool _memoryPool = KestrelMemoryPool.Create(); + private readonly MemoryPool _memoryPool = MemoryPoolFactory.Create(); internal readonly Http2PeerSettings _clientSettings = new Http2PeerSettings(); internal readonly HPackEncoder _hpackEncoder = new HPackEncoder(); @@ -429,8 +428,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests // Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing // Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make // it easier to verify request frames are processed correctly immediately after sending the them. - var inputPipeOptions = ConnectionDispatcher.GetInputPipeOptions(_serviceContext, _memoryPool, PipeScheduler.ThreadPool); - var outputPipeOptions = ConnectionDispatcher.GetOutputPipeOptions(_serviceContext, _memoryPool, PipeScheduler.ThreadPool); + var inputPipeOptions = GetInputPipeOptions(_serviceContext, _memoryPool, PipeScheduler.ThreadPool); + var outputPipeOptions = GetOutputPipeOptions(_serviceContext, _memoryPool, PipeScheduler.ThreadPool); _pair = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions); @@ -1248,6 +1247,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _timeoutControl.Tick(clock.UtcNow); } + private static PipeOptions GetInputPipeOptions(ServiceContext serviceContext, MemoryPool memoryPool, PipeScheduler writerScheduler) => new PipeOptions + ( + pool: memoryPool, + readerScheduler: serviceContext.Scheduler, + writerScheduler: writerScheduler, + pauseWriterThreshold: serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, + resumeWriterThreshold: serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0, + useSynchronizationContext: false, + minimumSegmentSize: memoryPool.GetMinimumSegmentSize() + ); + + private static PipeOptions GetOutputPipeOptions(ServiceContext serviceContext, MemoryPool memoryPool, PipeScheduler readerScheduler) => new PipeOptions + ( + pool: memoryPool, + readerScheduler: readerScheduler, + writerScheduler: serviceContext.Scheduler, + pauseWriterThreshold: GetOutputResponseBufferSize(serviceContext), + resumeWriterThreshold: GetOutputResponseBufferSize(serviceContext), + useSynchronizationContext: false, + minimumSegmentSize: memoryPool.GetMinimumSegmentSize() + ); + + private static long GetOutputResponseBufferSize(ServiceContext serviceContext) + { + var bufferSize = serviceContext.ServerOptions.Limits.MaxResponseBufferSize; + if (bufferSize == 0) + { + // 0 = no buffering so we need to configure the pipe so the writer waits on the reader directly + return 1; + } + + // null means that we have no back pressure + return bufferSize ?? 0; + } + internal class Http2FrameWithPayload : Http2Frame { public Http2FrameWithPayload() : base() diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj index c7b344c2c8..c1c9107598 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj @@ -4,6 +4,7 @@ netcoreapp3.0 true InMemory.FunctionalTests + true @@ -11,9 +12,11 @@ + + diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/KeepAliveTimeoutTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/KeepAliveTimeoutTests.cs index 422520f5b8..34f3932129 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/KeepAliveTimeoutTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/KeepAliveTimeoutTests.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 System.IO.Pipelines; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -33,6 +34,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "GET / HTTP/1.1", "Host:", @@ -59,6 +62,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + for (var i = 0; i < 10; i++) { await connection.Send( @@ -86,6 +91,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "POST /consume HTTP/1.1", "Host:", @@ -126,6 +133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "GET /longrunning HTTP/1.1", "Host:", @@ -164,6 +173,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + // Min amount of time between requests that triggers a keep-alive timeout. testContext.MockSystemClock.UtcNow += _keepAliveTimeout + Heartbeat.Interval + TimeSpan.FromTicks(1); heartbeatManager.OnHeartbeat(testContext.SystemClock.UtcNow); @@ -184,6 +195,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "GET /upgrade HTTP/1.1", "Host:", @@ -212,6 +225,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests private TestServer CreateServer(TestServiceContext context, CancellationToken longRunningCt = default, CancellationToken upgradeCt = default) { + // Ensure request headers timeout is started as soon as the tests send requests. + context.Scheduler = PipeScheduler.Inline; context.ServerOptions.AddServerHeader = false; context.ServerOptions.Limits.KeepAliveTimeout = _keepAliveTimeout; context.ServerOptions.Limits.MinRequestBodyDataRate = null; diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestHeadersTimeoutTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestHeadersTimeoutTests.cs index 65448bd6d2..e217ece3f0 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestHeadersTimeoutTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestHeadersTimeoutTests.cs @@ -32,6 +32,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "GET / HTTP/1.1", headers); @@ -55,6 +57,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send( "POST / HTTP/1.1", "Host:", @@ -86,6 +90,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + await connection.Send(requestLine); // Min amount of time between requests that triggers a request headers timeout. @@ -110,6 +116,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests { using (var connection = server.CreateConnection()) { + await connection.TransportConnection.WaitForReadTask; + foreach (var ch in "POST / HTTP/1.1\r\nHost:\r\n\r\n") { await connection.Send(ch.ToString()); diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportConnection.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportConnection.cs index 95c1907c0a..cc91efd9b2 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportConnection.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportConnection.cs @@ -6,34 +6,45 @@ using System.Buffers; using System.IO.Pipelines; using System.Net; using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport { - internal class InMemoryTransportConnection : TransportConnection, IDisposable + internal class InMemoryTransportConnection : TransportConnection { private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource(); private readonly ILogger _logger; private bool _isClosed; + private readonly TaskCompletionSource _waitForCloseTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - public InMemoryTransportConnection(MemoryPool memoryPool, ILogger logger) + public InMemoryTransportConnection(MemoryPool memoryPool, ILogger logger, PipeScheduler scheduler = null) { MemoryPool = memoryPool; _logger = logger; - LocalAddress = IPAddress.Loopback; - RemoteAddress = IPAddress.Loopback; + LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + + var pair = DuplexPipe.CreateConnectionPair(new PipeOptions(memoryPool, readerScheduler: scheduler, useSynchronizationContext: false), new PipeOptions(memoryPool, writerScheduler: scheduler, useSynchronizationContext: false)); + Application = pair.Application; + var wrapper = new ObservableDuplexPipe(pair.Transport); + Transport = wrapper; + WaitForReadTask = wrapper.WaitForReadTask; ConnectionClosed = _connectionClosedTokenSource.Token; } - public override MemoryPool MemoryPool { get; } + public PipeWriter Input => Application.Output; - public override PipeScheduler InputWriterScheduler => PipeScheduler.ThreadPool; - public override PipeScheduler OutputReaderScheduler => PipeScheduler.ThreadPool; + public PipeReader Output => Application.Input; + + public Task WaitForReadTask { get; } + + public override MemoryPool MemoryPool { get; } public ConnectionAbortedException AbortReason { get; private set; } @@ -57,14 +68,141 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTrans ThreadPool.UnsafeQueueUserWorkItem(state => { - var self = (InMemoryTransportConnection)state; - self._connectionClosedTokenSource.Cancel(); - }, this); + state._connectionClosedTokenSource.Cancel(); + + state._waitForCloseTcs.TrySetResult(null); + }, + this, + preferLocal: false); } - public void Dispose() + public override async ValueTask DisposeAsync() { + Transport.Input.Complete(); + Transport.Output.Complete(); + + await _waitForCloseTcs.Task; + _connectionClosedTokenSource.Dispose(); } + + // This piece of code allows us to wait until the PipeReader has been awaited on. + // We need to wrap lots of layers (including the ValueTask) to gain visiblity into when + // the machinery for the await happens + private class ObservableDuplexPipe : IDuplexPipe + { + private readonly ObservablePipeReader _reader; + + public ObservableDuplexPipe(IDuplexPipe duplexPipe) + { + _reader = new ObservablePipeReader(duplexPipe.Input); + + Input = _reader; + Output = duplexPipe.Output; + + } + + public Task WaitForReadTask => _reader.WaitForReadTask; + + public PipeReader Input { get; } + + public PipeWriter Output { get; } + + private class ObservablePipeReader : PipeReader + { + private readonly PipeReader _reader; + private readonly TaskCompletionSource _tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public Task WaitForReadTask => _tcs.Task; + + public ObservablePipeReader(PipeReader reader) + { + _reader = reader; + } + + public override void AdvanceTo(SequencePosition consumed) + { + _reader.AdvanceTo(consumed); + } + + public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) + { + _reader.AdvanceTo(consumed, examined); + } + + public override void CancelPendingRead() + { + _reader.CancelPendingRead(); + } + + public override void Complete(Exception exception = null) + { + _reader.Complete(exception); + } + + public override void OnWriterCompleted(Action callback, object state) + { + _reader.OnWriterCompleted(callback, state); + } + + public override ValueTask ReadAsync(CancellationToken cancellationToken = default) + { + var task = _reader.ReadAsync(cancellationToken); + + if (_tcs.Task.IsCompleted) + { + return task; + } + + return new ValueTask(new ObservableValueTask(task, _tcs), 0); + } + + public override bool TryRead(out ReadResult result) + { + return _reader.TryRead(out result); + } + + private class ObservableValueTask : IValueTaskSource + { + private readonly ValueTask _task; + private readonly TaskCompletionSource _tcs; + + public ObservableValueTask(ValueTask task, TaskCompletionSource tcs) + { + _task = task; + _tcs = tcs; + } + + public T GetResult(short token) + { + return _task.GetAwaiter().GetResult(); + } + + public ValueTaskSourceStatus GetStatus(short token) + { + if (_task.IsCanceled) + { + return ValueTaskSourceStatus.Canceled; + } + if (_task.IsFaulted) + { + return ValueTaskSourceStatus.Faulted; + } + if (_task.IsCompleted) + { + return ValueTaskSourceStatus.Succeeded; + } + return ValueTaskSourceStatus.Pending; + } + + public void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) + { + _task.GetAwaiter().UnsafeOnCompleted(() => continuation(state)); + + _tcs.TrySetResult(null); + } + } + } + } } } diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportFactory.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportFactory.cs index eeb271a706..6c5ff14882 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportFactory.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/InMemoryTransportFactory.cs @@ -2,43 +2,56 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Net; +using System.Threading; +using System.Threading.Channels; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Connections; namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport { - internal class InMemoryTransportFactory : ITransportFactory + internal class InMemoryTransportFactory : IConnectionListenerFactory, IConnectionListener { - public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher dispatcher) + private Channel _acceptQueue = Channel.CreateUnbounded(); + + public EndPoint EndPoint { get; set; } + + public void AddConnection(ConnectionContext connection) { - if (ConnectionDispatcher != null) - { - throw new InvalidOperationException("InMemoryTransportFactory doesn't support creating multiple endpoints"); - } - - ConnectionDispatcher = dispatcher; - - return new NoopTransport(); + _acceptQueue.Writer.TryWrite(connection); } - public IConnectionDispatcher ConnectionDispatcher { get; private set; } - - private class NoopTransport : ITransport + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) { - public Task BindAsync() + if (await _acceptQueue.Reader.WaitToReadAsync(cancellationToken)) { - return Task.CompletedTask; + while (_acceptQueue.Reader.TryRead(out var item)) + { + return item; + } } - public Task StopAsync() - { - return Task.CompletedTask; - } + return null; - public Task UnbindAsync() - { - return Task.CompletedTask; - } + } + + public ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) + { + EndPoint = endpoint; + + return new ValueTask(this); + } + + public ValueTask DisposeAsync() + { + return UnbindAsync(default); + } + + public ValueTask UnbindAsync(CancellationToken cancellationToken = default) + { + _acceptQueue.Writer.TryComplete(); + + return default; } } } diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/TestServer.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/TestServer.cs index 9f3828f07b..c7bab027d8 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/TestServer.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/TestTransport/TestServer.cs @@ -102,8 +102,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTrans public InMemoryConnection CreateConnection() { - var transportConnection = new InMemoryTransportConnection(_memoryPool, Context.Log); - _ = HandleConnection(transportConnection); + var transportConnection = new InMemoryTransportConnection(_memoryPool, Context.Log, Context.Scheduler); + _transportFactory.AddConnection(transportConnection); return new InMemoryConnection(transportConnection); } @@ -128,36 +128,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTrans return services.BuildServiceProvider(); } - private async Task HandleConnection(InMemoryTransportConnection transportConnection) - { - try - { - var middlewareTask = _transportFactory.ConnectionDispatcher.OnConnection(transportConnection); - var transportTask = CancellationTokenAsTask(transportConnection.ConnectionClosed); - - await transportTask; - await middlewareTask; - - transportConnection.Dispose(); - } - catch (Exception ex) - { - Debug.Assert(false, $"Unexpected exception: {ex}."); - } - } - - private static Task CancellationTokenAsTask(CancellationToken token) - { - if (token.IsCancellationRequested) - { - return Task.CompletedTask; - } - - var tcs = new TaskCompletionSource(); - token.Register(() => tcs.SetResult(null)); - return tcs.Task; - } - public async ValueTask DisposeAsync() { // The concrete WebHost implements IAsyncDisposable diff --git a/src/Servers/Kestrel/test/Libuv.FunctionalTests/TransportSelector.cs b/src/Servers/Kestrel/test/Libuv.FunctionalTests/TransportSelector.cs index ca209ba6e7..4bcae2a6cf 100644 --- a/src/Servers/Kestrel/test/Libuv.FunctionalTests/TransportSelector.cs +++ b/src/Servers/Kestrel/test/Libuv.FunctionalTests/TransportSelector.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; @@ -9,9 +9,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public static class TransportSelector { - public static IWebHostBuilder GetWebHostBuilder(Func> memoryPoolFactory = null) + public static IWebHostBuilder GetWebHostBuilder(Func> memoryPoolFactory = null, + long? maxReadBufferSize = null) { - return new WebHostBuilder().UseLibuv(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }); + return new WebHostBuilder().UseLibuv(options => + { + options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; + options.MaxReadBufferSize = maxReadBufferSize; + }); } } } diff --git a/src/Servers/Kestrel/test/Sockets.FunctionalTests/TransportSelector.cs b/src/Servers/Kestrel/test/Sockets.FunctionalTests/TransportSelector.cs index 3e3cfe3f6c..6d2461866e 100644 --- a/src/Servers/Kestrel/test/Sockets.FunctionalTests/TransportSelector.cs +++ b/src/Servers/Kestrel/test/Sockets.FunctionalTests/TransportSelector.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; @@ -9,9 +9,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public static class TransportSelector { - public static IWebHostBuilder GetWebHostBuilder(Func> memoryPoolFactory = null) + public static IWebHostBuilder GetWebHostBuilder(Func> memoryPoolFactory = null, + long? maxReadBufferSize = null) { - return new WebHostBuilder().UseSockets(options => { options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; }); + return new WebHostBuilder().UseSockets(options => + { + options.MemoryPoolFactory = memoryPoolFactory ?? options.MemoryPoolFactory; + options.MaxReadBufferSize = maxReadBufferSize; + }); } } } diff --git a/src/Servers/Kestrel/tools/CodeGenerator/CodeGenerator.csproj b/src/Servers/Kestrel/tools/CodeGenerator/CodeGenerator.csproj index afb106a5cf..90f9bb3627 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/CodeGenerator.csproj +++ b/src/Servers/Kestrel/tools/CodeGenerator/CodeGenerator.csproj @@ -18,7 +18,7 @@ $(MSBuildThisFileDirectory)..\..\ - Core/src/Internal/Http/HttpHeaders.Generated.cs Core/src/Internal/Http/HttpProtocol.Generated.cs Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs Transport.Abstractions/src/Internal/TransportConnection.Generated.cs + Core/src/Internal/Http/HttpHeaders.Generated.cs Core/src/Internal/Http/HttpProtocol.Generated.cs Core/src/Internal/Infrastructure/HttpUtilities.Generated.cs ../Connections.Abstractions/src/TransportConnection.Generated.cs diff --git a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs index 93d0339a53..a9eabbe474 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs @@ -11,17 +11,11 @@ namespace CodeGenerator // See also: src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs var features = new[] { - "IHttpConnectionFeature", "IConnectionIdFeature", "IConnectionTransportFeature", "IConnectionItemsFeature", "IMemoryPoolFeature", - "IApplicationTransportFeature", - "ITransportSchedulerFeature", - "IConnectionLifetimeFeature", - "IConnectionHeartbeatFeature", - "IConnectionLifetimeNotificationFeature", - "IConnectionCompleteFeature" + "IConnectionLifetimeFeature" }; var usings = $@" @@ -29,7 +23,7 @@ using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features;"; return FeatureCollectionGenerator.GenerateFile( - namespaceName: "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal", + namespaceName: "Microsoft.AspNetCore.Connections", className: "TransportConnection", allFeatures: features, implementedFeatures: features, diff --git a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/KestrelMemoryPool.cs b/src/Shared/Buffers.MemoryPool/MemoryPoolFactory.cs similarity index 69% rename from src/Servers/Kestrel/Transport.Abstractions/src/Internal/KestrelMemoryPool.cs rename to src/Shared/Buffers.MemoryPool/MemoryPoolFactory.cs index b3577f2c0a..c7ee26ca25 100644 --- a/src/Servers/Kestrel/Transport.Abstractions/src/Internal/KestrelMemoryPool.cs +++ b/src/Shared/Buffers.MemoryPool/MemoryPoolFactory.cs @@ -1,12 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Buffers; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal +namespace System.Buffers { - public static class KestrelMemoryPool + internal static class MemoryPoolFactory { public static MemoryPool Create() { @@ -21,7 +18,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { return new SlabMemoryPool(); } - - public static readonly int MinimumSegmentSize = 4096; } } diff --git a/src/Shared/Buffers.MemoryPool/SlabMemoryPool.cs b/src/Shared/Buffers.MemoryPool/SlabMemoryPool.cs index 0359c72193..e808624010 100644 --- a/src/Shared/Buffers.MemoryPool/SlabMemoryPool.cs +++ b/src/Shared/Buffers.MemoryPool/SlabMemoryPool.cs @@ -30,6 +30,11 @@ namespace System.Buffers /// public override int MaxBufferSize { get; } = _blockSize; + /// + /// The size of a block. 4096 is chosen because most operating systems use 4k pages. + /// + public static int BlockSize => _blockSize; + /// /// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab /// diff --git a/src/SignalR/SignalR.sln b/src/SignalR/SignalR.sln index bfe46375f3..8a76052bfd 100644 --- a/src/SignalR/SignalR.sln +++ b/src/SignalR/SignalR.sln @@ -147,6 +147,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpOv EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Protocols.Json", "common\Protocols.Json\src\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj", "{BB52C0FB-19FD-485A-9EBD-3FC173ECAEA0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Metadata", "..\Http\Metadata\src\Microsoft.AspNetCore.Metadata.csproj", "{2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -405,6 +407,10 @@ Global {BB52C0FB-19FD-485A-9EBD-3FC173ECAEA0}.Debug|Any CPU.Build.0 = Debug|Any CPU {BB52C0FB-19FD-485A-9EBD-3FC173ECAEA0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BB52C0FB-19FD-485A-9EBD-3FC173ECAEA0}.Release|Any CPU.Build.0 = Release|Any CPU + {2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -475,6 +481,7 @@ Global {762A7DD1-E45E-4EA3-8109-521E844AE613} = {1C8016A8-F362-45C7-9EA9-A1CCE7918F2F} {FD3A8F8D-2967-4635-86FC-CC49BAF651C1} = {EDE8E45E-A5D0-4F0E-B72C-7CC14146C60A} {BB52C0FB-19FD-485A-9EBD-3FC173ECAEA0} = {9FCD621E-E710-4991-B45C-1BABC977BEEC} + {2E107FBB-2387-4A9F-A2CA-EFDF2E4DD64D} = {EDE8E45E-A5D0-4F0E-B72C-7CC14146C60A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59} diff --git a/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs b/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs index 99da56fbb4..3ecf4dacd0 100644 --- a/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs +++ b/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.SignalR.Client /// public Task DisposeAsync(ConnectionContext connection) { - return ((HttpConnection)connection).DisposeAsync(); + return connection.DisposeAsync().AsTask(); } } -} \ No newline at end of file +} diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs index 5a8d26f060..d723973629 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests var delegateConnectionFactory = new DelegateConnectionFactory( GetHttpConnectionFactory(url, loggerFactory, path, transportType ?? HttpTransportType.LongPolling | HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents), - connection => ((HttpConnection)connection).DisposeAsync()); + connection => ((HttpConnection)connection).DisposeAsync().AsTask()); hubConnectionBuilder.Services.AddSingleton(delegateConnectionFactory); return hubConnectionBuilder.Build(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs index 647aa433ca..7e29231574 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs @@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Task DisposeAsync(ConnectionContext connection) { - return ((TestConnection)connection).DisposeAsync(); + return connection.DisposeAsync().AsTask(); } var builder = new HubConnectionBuilder(); @@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests return new TestConnection(onDispose: createCount == 1 ? onDisposeForFirstConnection : null).StartAsync(format); } - Task DisposeAsync(ConnectionContext connection) => ((TestConnection)connection).DisposeAsync(); + Task DisposeAsync(ConnectionContext connection) => connection.DisposeAsync().AsTask(); var builder = new HubConnectionBuilder(); var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory, DisposeAsync); @@ -601,7 +601,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var delegateConnectionFactory = new DelegateConnectionFactory( format => innerConnection.StartAsync(format), - connection => ((TestConnection)connection).DisposeAsync()); + connection => connection.DisposeAsync().AsTask()); builder.Services.AddSingleton(delegateConnectionFactory); var hubConnection = builder.Build(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs index 0ec53e6479..96c18db8d0 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var delegateConnectionFactory = new DelegateConnectionFactory( connection.StartAsync, - c => ((TestConnection)c).DisposeAsync()); + c => c.DisposeAsync().AsTask()); builder.Services.AddSingleton(delegateConnectionFactory); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs index 40cca161c0..74a2da0c7f 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs @@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var delegateConnectionFactory = new DelegateConnectionFactory( format => new TestConnection().StartAsync(format), - connection => ((TestConnection)connection).DisposeAsync()); + connection => ((TestConnection)connection).DisposeAsync().AsTask()); builder.Services.AddSingleton(delegateConnectionFactory); var hubConnection = builder.Build(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs index dc9e1b9d56..cdb61db868 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests null); } - public Task DisposeAsync() => DisposeCoreAsync(); + public override ValueTask DisposeAsync() => DisposeCoreAsync(); public async Task StartAsync(TransferFormat transferFormat = TransferFormat.Binary) { @@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Application.Output.Complete(ex); } - private async Task DisposeCoreAsync(Exception ex = null) + private async ValueTask DisposeCoreAsync(Exception ex = null) { Interlocked.Increment(ref _disposeCount); _disposed.TrySetResult(null); diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netcoreapp3.0.cs b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netcoreapp3.0.cs index dd78ecdf90..3c74a6e8c1 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netcoreapp3.0.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netcoreapp3.0.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client bool Microsoft.AspNetCore.Connections.Features.IConnectionInherentKeepAliveFeature.HasInherentKeepAlive { get { throw null; } } public override System.IO.Pipelines.IDuplexPipe Transport { get { throw null; } set { } } [System.Diagnostics.DebuggerStepThroughAttribute] - public System.Threading.Tasks.Task DisposeAsync() { throw null; } + public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] public System.Threading.Tasks.Task StartAsync(Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs index dd78ecdf90..3c74a6e8c1 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client bool Microsoft.AspNetCore.Connections.Features.IConnectionInherentKeepAliveFeature.HasInherentKeepAlive { get { throw null; } } public override System.IO.Pipelines.IDuplexPipe Transport { get { throw null; } set { } } [System.Diagnostics.DebuggerStepThroughAttribute] - public System.Threading.Tasks.Task DisposeAsync() { throw null; } + public override System.Threading.Tasks.ValueTask DisposeAsync() { throw null; } [System.Diagnostics.DebuggerStepThroughAttribute] public System.Threading.Tasks.Task StartAsync(Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task StartAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs index fdbecdef64..4953e82c92 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs @@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client /// A connection cannot be restarted after it has stopped. To restart a connection /// a new instance should be created using the same options. /// - public async Task DisposeAsync() + public override async ValueTask DisposeAsync() { using (_logger.BeginScope(_logScope)) { diff --git a/src/SignalR/common/testassets/Tests.Utils/TaskExtensions.cs b/src/SignalR/common/testassets/Tests.Utils/TaskExtensions.cs index 5aa1675012..2e0a5215c3 100644 --- a/src/SignalR/common/testassets/Tests.Utils/TaskExtensions.cs +++ b/src/SignalR/common/testassets/Tests.Utils/TaskExtensions.cs @@ -17,6 +17,16 @@ namespace System.Threading.Tasks { private const int DefaultTimeout = 30 * 1000; + public static Task OrTimeout(this ValueTask task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); + } + + public static Task OrTimeout(this ValueTask task, TimeSpan timeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) + { + return task.AsTask().TimeoutAfter(timeout, filePath, lineNumber ?? 0); + } + public static Task OrTimeout(this Task task, int milliseconds = DefaultTimeout, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int? lineNumber = null) { return OrTimeout(task, new TimeSpan(0, 0, 0, 0, milliseconds), memberName, filePath, lineNumber); diff --git a/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs b/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs index 1fbb213b9b..d2802b2e0c 100644 --- a/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs +++ b/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs @@ -43,14 +43,14 @@ namespace ClientSample // We claim to have inherent keep-alive so the client doesn't kill the connection when it hasn't seen ping frames. public bool HasInherentKeepAlive { get; } = true; - public Task DisposeAsync() + public override ValueTask DisposeAsync() { Transport?.Output.Complete(); Transport?.Input.Complete(); _socket?.Dispose(); - return Task.CompletedTask; + return default; } public async Task StartAsync() diff --git a/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs b/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs index 763725cb69..a52b18600e 100644 --- a/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs +++ b/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs @@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.SignalR.Client public Task DisposeAsync(ConnectionContext connection) { - return ((TcpConnection)connection).DisposeAsync(); + return connection.DisposeAsync().AsTask(); } } } From dd07fa09d2895bba3231d9e433da1a4671630512 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 30 May 2019 20:38:52 -0700 Subject: [PATCH 54/95] [master] Update dependencies from aspnet/AspNetCore-Tooling (#10596) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190527.1 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19277.1 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19277.1 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19277.1 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19277.1 * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190528.1 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19278.1 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19278.1 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19278.1 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19278.1 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190528.3 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19278.3 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19278.3 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19278.3 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19278.3 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19274.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27727-02 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19278.1 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190529.3 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19279.3 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19279.3 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19279.3 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19279.3 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190529.4 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19279.4 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19279.4 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19279.4 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19279.4 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - NETStandard.Library.Ref - 2.1.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19278.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27728-04 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19279.2 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190529.5 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19279.5 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19279.5 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19279.5 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19279.5 * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190529.7 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19279.7 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19279.7 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19279.7 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19279.7 * Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - NETStandard.Library.Ref - 2.1.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19277.2 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-03 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19279.7 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190529.8 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19279.8 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19279.8 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19279.8 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19279.8 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - NETStandard.Library.Ref - 2.1.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19279.5 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27729-07 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19279.11 (parent: Microsoft.CodeAnalysis.Razor) * Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190530.2 - Microsoft.NET.Sdk.Razor - 3.0.0-preview6.19280.2 - Microsoft.CodeAnalysis.Razor - 3.0.0-preview6.19280.2 - Microsoft.AspNetCore.Razor.Language - 3.0.0-preview6.19280.2 - Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview6.19280.2 Dependency coherency updates - Microsoft.AspNetCore.Analyzer.Testing - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.BenchmarkRunner.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ActivatorUtilities.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.Memory - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.SqlServer - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Caching.StackExchangeRedis - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.CommandLineUtils.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.AzureKeyVault - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Binder - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.CommandLine - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.EnvironmentVariables - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.FileExtensions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Ini - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Json - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.KeyPerFile - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.UserSecrets - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration.Xml - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Configuration - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyInjection - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DiagnosticAdapter - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Diagnostics.HealthChecks - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Composite - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Embedded - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileProviders.Physical - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.FileSystemGlobbing - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HashCodeCombiner.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Hosting - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.HostFactoryResolver.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Http - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Localization - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Abstractions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.AzureAppServices - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Configuration - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Console - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Debug - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventSource - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.EventLog - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.TraceSource - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Logging.Testing - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ObjectPool - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.ConfigurationExtensions - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options.DataAnnotations - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Options - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ParameterDefaultValue.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.Primitives - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.TypeNameHelper.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.ValueStopwatch.Sources - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.WebEncoders - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Internal.Extensions.Refs - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.JSInterop - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Mono.WebAssembly.Interop - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.CSharp - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Win32.Registry - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ComponentModel.Annotations - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Diagnostics.EventLog - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.IO.Pipelines - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.Http.WinHttpHandler - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Net.WebSockets.WebSocketProtocol - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Reflection.Metadata - 1.7.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Runtime.CompilerServices.Unsafe - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Cng - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Pkcs - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Cryptography.Xml - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Permissions - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Security.Principal.Windows - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.ServiceProcess.ServiceController - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Encodings.Web - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Text.Json - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - System.Threading.Channels - 4.6.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.Extensions.DependencyModel - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - NETStandard.Library.Ref - 2.1.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.NETCore.Platforms - 3.0.0-preview6.19279.8 (parent: Microsoft.NETCore.App) - Microsoft.NETCore.App - 3.0.0-preview6-27730-01 (parent: Microsoft.Extensions.Logging) - Microsoft.Extensions.Logging - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Internal.AspNetCore.Analyzers - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) - Microsoft.AspNetCore.Testing - 3.0.0-preview6.19280.1 (parent: Microsoft.CodeAnalysis.Razor) --- eng/Version.Details.xml | 348 +++++++++--------- eng/Versions.props | 174 ++++----- .../src/Http/WebAssemblyHttpMessageHandler.cs | 47 ++- .../src/Microsoft.AspNetCore.Blazor.csproj | 2 +- .../Blazor/Build/src/Core/BootJsonWriter.cs | 7 +- .../Microsoft.AspNetCore.Blazor.Build.csproj | 6 +- .../Build/test/BindRazorIntegrationTest.cs | 40 +- .../ComponentRenderingRazorIntegrationTest.cs | 2 +- .../GenericComponentRazorIntegrationTest.cs | 6 +- .../test/RenderingRazorIntegrationTest.cs | 18 +- .../test/RuntimeDependenciesResolverTest.cs | 52 ++- .../Pages/Counter.razor | 2 +- .../Shared/NavMenu.razor | 4 +- .../Pages/Counter.razor | 2 +- .../Shared/NavMenu.razor | 4 +- .../Pages/Json.razor | 21 +- .../Pages/RenderList.razor | 6 +- .../StandaloneApp/Pages/Counter.razor | 4 +- .../StandaloneApp/Shared/NavMenu.razor | 6 +- src/Components/Browser.JS/.npmrc | Bin 0 -> 116 bytes .../Browser.JS/dist/Debug/blazor.server.js | 24 +- .../dist/Debug/blazor.webassembly.js | 24 +- .../Browser.JS/dist/Release/blazor.server.js | 2 +- .../dist/Release/blazor.webassembly.js | 2 +- src/Components/Browser.JS/package.json | 2 +- .../src/Rendering/ElementReferenceCapture.ts | 2 +- .../Browser.JS/src/Services/Http.ts | 27 +- src/Components/Browser.JS/yarn.lock | 8 +- ...osoft.AspNetCore.Components.Browser.csproj | 6 +- .../src/RendererRegistryEventDispatcher.cs | 53 ++- ...ft.AspNetCore.Components.netstandard2.0.cs | 5 +- .../Components/src/BindAttributes.cs | 4 +- src/Components/Components/src/ElementRef.cs | 28 +- .../src/Forms/InputComponents/InputBase.cs | 2 +- .../Forms/InputComponents/InputCheckbox.cs | 2 +- .../src/Forms/InputComponents/InputText.cs | 2 +- .../Forms/InputComponents/InputTextArea.cs | 2 +- .../src/HttpClientJsonExtensions.cs | 12 +- .../src/JsonSerializerOptionsProvider.cs | 15 + .../Microsoft.AspNetCore.Components.csproj | 2 +- .../test/HttpClientJsonExtensionsTest.cs | 13 +- .../TestJsonSerializerOptionsProvider.cs | 15 + .../test/E2ETest/Tests/InteropTest.cs | 4 +- src/Components/test/E2ETest/Tests/KeyTest.cs | 9 +- .../test/E2ETest/Tests/PerformanceTest.cs | 5 +- .../AddRemoveChildComponents.razor | 6 +- .../AfterRenderInteropComponent.razor | 2 +- .../AsyncEventHandlerComponent.razor | 6 +- .../ClientSideAuthenticationStateData.cs | 8 +- .../BasicTestApp/BindCasesComponent.razor | 80 ++-- ...ascadingValueReceiveFixedByInterface.razor | 2 +- .../CascadingValueSupplier.razor | 6 +- .../BasicTestApp/ComponentRefComponent.razor | 8 +- .../BasicTestApp/CounterComponent.razor | 4 +- .../CounterComponentUsingChild.razor | 4 +- .../BasicTestApp/DispatchingComponent.razor | 8 +- .../BasicTestApp/ElementRefComponent.razor | 6 +- .../BasicTestApp/EventBubblingComponent.razor | 10 +- .../EventCallbackTest/ButtonComponent.razor | 2 +- .../EventCallbackCases.razor | 4 +- .../EventCallbackTest/InnerButton.razor | 2 +- .../StronglyTypedButton.razor | 2 +- .../EventPreventDefaultComponent.razor | 6 +- .../BasicTestApp/ExternalContentPackage.razor | 2 +- .../BasicTestApp/FocusEventComponent.razor | 8 +- ...fyPropertyChangedValidationComponent.razor | 6 +- .../FormsTest/SimpleValidationComponent.razor | 4 +- .../TypicalValidationComponent.razor | 20 +- .../BinaryHttpRequestsComponent.razor | 6 +- .../CookieCounterComponent.razor | 8 +- .../HttpRequestsComponent.razor | 18 +- .../test/testassets/BasicTestApp/Index.razor | 2 +- .../BasicTestApp/InputEventComponent.razor | 2 +- .../BasicTestApp/InteropComponent.razor | 54 ++- .../InteropOnInitializationComponent.razor | 2 +- .../InteropTest/JavaScriptInterop.cs | 157 ++++---- .../BasicTestApp/InteropTest/Segment.cs | 2 +- .../BasicTestApp/KeyCasesComponent.razor | 8 +- .../BasicTestApp/KeyCasesTreeNode.razor | 4 +- .../BasicTestApp/KeyPressEventComponent.razor | 6 +- .../BasicTestApp/MarkupBlockComponent.razor | 2 +- .../BasicTestApp/MouseEventComponent.razor | 12 +- .../BasicTestApp/MultipleChildContent.razor | 2 +- .../PrerenderedToInteractiveTransition.razor | 2 +- .../PropertiesChangedHandlerParent.razor | 4 +- .../BasicTestApp/RenderFragmentToggler.razor | 4 +- .../ReorderingFocusComponent.razor | 12 +- .../BasicTestApp/RouterTest/Links.razor | 4 +- .../RouterTest/UriHelperComponent.razor | 2 +- .../BasicTestApp/SvgComponent.razor | 2 +- .../TestJsonSerializerOptionsProvider.cs | 15 + .../BasicTestApp/TouchEventComponent.razor | 18 +- .../ComponentsApp.App/Pages/Counter.razor | 4 +- .../ComponentsApp.App/Pages/Error.razor | 2 +- .../ComponentsApp.App/Shared/NavMenu.razor | 4 +- .../ComponentFromPackage.razor | 4 +- .../TestServer/Controllers/UserController.cs | 2 +- .../EnumerableValueProviderTest.cs | 1 - .../test/HtmlLocalizerTest.cs | 16 +- .../UnsupportedJavaScriptRuntime.cs | 5 - .../Pages/Counter.razor | 2 +- .../Shared/NavMenu.razor | 4 +- .../src/Protocol/JsonHubProtocol.cs | 2 +- 103 files changed, 915 insertions(+), 706 deletions(-) create mode 100644 src/Components/Browser.JS/.npmrc create mode 100644 src/Components/Components/src/JsonSerializerOptionsProvider.cs create mode 100644 src/Components/test/E2ETest/TestJsonSerializerOptionsProvider.cs create mode 100644 src/Components/test/testassets/BasicTestApp/TestJsonSerializerOptionsProvider.cs diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5971f3367b..bec457a835 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,21 +13,21 @@ https://github.com/aspnet/Blazor c879c3a911b4c2d6cccd4d6ff2de86a6949cda88 - + https://github.com/aspnet/AspNetCore-Tooling - 187d2dae3f94981518ac37588925fe1414127248 + fd34479f7cb75a088f5517d0a79d9499fdf44036 - + https://github.com/aspnet/AspNetCore-Tooling - 187d2dae3f94981518ac37588925fe1414127248 + fd34479f7cb75a088f5517d0a79d9499fdf44036 - + https://github.com/aspnet/AspNetCore-Tooling - 187d2dae3f94981518ac37588925fe1414127248 + fd34479f7cb75a088f5517d0a79d9499fdf44036 - + https://github.com/aspnet/AspNetCore-Tooling - 187d2dae3f94981518ac37588925fe1414127248 + fd34479f7cb75a088f5517d0a79d9499fdf44036 https://github.com/aspnet/EntityFrameworkCore @@ -57,340 +57,340 @@ https://github.com/aspnet/EntityFrameworkCore 08edd86216be4857b45b47bf0a9b29e98e525c05 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 https://github.com/dotnet/corefx a28176b5ec68b6da1472934fe9493790d1665cae - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/dotnet/core-setup - 20426e8c486d8715337cb6438ec70bc3619a514d + 63abc77da6d99470caa5bfa0465afe244105e595 - + https://github.com/dotnet/core-setup - 20426e8c486d8715337cb6438ec70bc3619a514d + 63abc77da6d99470caa5bfa0465afe244105e595 - + https://github.com/dotnet/core-setup - 20426e8c486d8715337cb6438ec70bc3619a514d + 63abc77da6d99470caa5bfa0465afe244105e595 - + https://github.com/dotnet/corefx - 41489a93acf3f36abcaaaea2003a8fdbb577cf35 + e23119d577e644d2c2a25419c88c1181681358e0 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 https://github.com/dotnet/arcade @@ -404,9 +404,9 @@ https://github.com/dotnet/arcade b5016f5688dc8ca9f3e4811ee7e2e86ad8907a40 - + https://github.com/aspnet/Extensions - 8550f61acc7d78990b7c67ea1647eaff29f80dc3 + bfea1edf9e2e9a5465f331517149c4f543ac2ba6 diff --git a/eng/Versions.props b/eng/Versions.props index 553e0d4c7f..82cc9dd8d3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,95 +23,95 @@ 1.0.0-beta.19274.6 - 3.0.0-preview6-27723-08 - 3.0.0-preview6-27723-08 - 2.1.0-preview6-27723-08 + 3.0.0-preview6-27730-01 + 3.0.0-preview6-27730-01 + 2.1.0-preview6-27730-01 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 4.7.0-preview6.19264.9 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 1.7.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 - 4.6.0-preview6.19273.5 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 1.7.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 + 4.6.0-preview6.19279.8 - 3.0.0-preview6.19273.5 + 3.0.0-preview6.19279.8 0.10.0-preview6.19273.9 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 - 3.0.0-preview6.19274.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 + 3.0.0-preview6.19280.1 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 @@ -121,10 +121,10 @@ 3.0.0-preview6.19252.4 3.0.0-preview6.19252.4 - 3.0.0-preview6.19274.4 - 3.0.0-preview6.19274.4 - 3.0.0-preview6.19274.4 - 3.0.0-preview6.19274.4 + 3.0.0-preview6.19280.2 + 3.0.0-preview6.19280.2 + 3.0.0-preview6.19280.2 + 3.0.0-preview6.19280.2

Current count:

- + @code { int currentCount = 0; diff --git a/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor b/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor index e55cc33965..89997ee1ff 100644 --- a/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor @@ -10,10 +10,10 @@ Result: @result

- - - - + + + + @code { string result; diff --git a/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor b/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor index 17010b9d6e..fa00db1d90 100644 --- a/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/ElementRefComponent.razor @@ -17,11 +17,11 @@ @if (_toggleCapturedElementPresence) { - + } - + diff --git a/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor b/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor index 5afc89a4fa..17984e9ee6 100644 --- a/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventBubblingComponent.razor @@ -1,7 +1,7 @@

Bubbling standard event

-
- +
+
@@ -15,14 +15,14 @@

Non-bubbling standard event

-
-

With onfocus:

+
+

With onfocus:

Without onfocus:

Event log

- + @code { string logValue = string.Empty; diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor index 08ffee0422..57264d659c 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/ButtonComponent.razor @@ -1,5 +1,5 @@ - + @code { [Parameter] int Count { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor index b3859bfb0e..20c64b0c95 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/EventCallbackCases.razor @@ -27,12 +27,12 @@

Passing Child Content

- +

Passing Child Content

- +
@code { diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor index 70eb5f662a..1d6a5e25fb 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/InnerButton.razor @@ -1,5 +1,5 @@ - + @code { [Parameter] EventCallback OnClick { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor index b9520414e2..d4e0904167 100644 --- a/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor +++ b/src/Components/test/testassets/BasicTestApp/EventCallbackTest/StronglyTypedButton.razor @@ -1,5 +1,5 @@ - + @code { [Parameter] EventCallback OnClick { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor b/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor index 5a1c7c0b07..2e23df0070 100644 --- a/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/EventPreventDefaultComponent.razor @@ -13,14 +13,14 @@

Form with onsubmit handler

- { })> - + { })> +

Form without onsubmit handler

- +
@if (didHandleEvent) diff --git a/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor b/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor index 0f4ef9e5f3..c90b7096da 100644 --- a/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor +++ b/src/Components/test/testassets/BasicTestApp/ExternalContentPackage.razor @@ -13,7 +13,7 @@

Click the following button to invoke a JavaScript function.

- + @if (!string.IsNullOrEmpty(result)) { diff --git a/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor b/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor index 7eb958bbf5..00843460fa 100644 --- a/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FocusEventComponent.razor @@ -1,15 +1,15 @@ -@using System.Collections.Generic +@using System.Collections.Generic

Focus and activation

-

- Input: +

+ Input:

Output: @message

- +

diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor index e094ce6ecd..51393a1e3f 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/NotifyPropertyChangedValidationComponent.razor @@ -15,14 +15,14 @@ cascade an EditContext to the components that integrate with it.

-
+

User name: - +

Accept terms: - +

diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor index f2cee1b6c4..b78ce413b4 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/SimpleValidationComponent.razor @@ -5,10 +5,10 @@

- User name: + User name:

- Accept terms: + Accept terms:

diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor index ad2aa89550..8776f05bf9 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor @@ -5,30 +5,30 @@

- Name: + Name:

- Age (years): + Age (years):

- Height (optional): + Height (optional):

- Description: + Description:

- Renewal date: + Renewal date:

- Expiry date (optional): + Expiry date (optional):

Ticket class: - + @@ -37,10 +37,10 @@ @person.TicketClass

- Accepts terms: + Accepts terms:

- Is evil: + Is evil:

diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor index 58107214d8..f4906da4fc 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/BinaryHttpRequestsComponent.razor @@ -1,4 +1,4 @@ -@using System.Net +@using System.Net @using System.Net.Http @inject HttpClient Http @@ -6,10 +6,10 @@

URI:
- +

- + @if (responseStatusCode.HasValue) { diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor index b2e9ec6101..0065dc34e8 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/CookieCounterComponent.razor @@ -1,10 +1,10 @@ -@inject System.Net.Http.HttpClient Http +@inject System.Net.Http.HttpClient Http

Cookie counter

The server increments the count by one on each request.

-

TestServer base URL:

- - +

TestServer base URL:

+ + @if (!requestInProgress) { diff --git a/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor b/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor index 6352a7e7b1..a6c0b9badc 100644 --- a/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/HttpClientTest/HttpRequestsComponent.razor @@ -7,12 +7,12 @@

URI:
- +

Method:
- @@ -22,7 +22,7 @@

Request body:
- +

@@ -30,20 +30,20 @@ @foreach (var header in requestHeaders) {

- Name: - Value: - [remove] + Name: + Value: + [remove]
} - +

Request referrer:
- +

- + @if (responseStatusCode.HasValue) { diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index 6a1d2886c9..addf55f07e 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -1,7 +1,7 @@ @using Microsoft.AspNetCore.Components.RenderTree
Select test: - diff --git a/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor b/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor index eb469715db..f7aafa6277 100644 --- a/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InputEventComponent.razor @@ -1,4 +1,4 @@ - +

The text below should update automatically as you type in the text field above

diff --git a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor index 2ae6ccd0ae..987f05ad12 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor @@ -1,9 +1,10 @@ @using Microsoft.JSInterop @using BasicTestApp.InteropTest @using System.Runtime.InteropServices +@using System.Text.Json.Serialization @inject IJSRuntime JSRuntime - +

Invocations

@@ -52,7 +53,6 @@ } @code { - public IDictionary ReturnValues { get; set; } = new Dictionary(); public IDictionary Invocations { get; set; } = new Dictionary(); @@ -75,8 +75,8 @@ await JSRuntime.InvokeAsync( "jsInteropTests.invokeDotNetInteropMethodsAsync", shouldSupportSyncInterop, - new DotNetObjectRef(testDTOTOPassByRef), - new DotNetObjectRef(instanceMethodsTarget)); + DotNetObjectRef.Create(testDTOTOPassByRef), + DotNetObjectRef.Create(instanceMethodsTarget)); if (shouldSupportSyncInterop) { @@ -84,14 +84,14 @@ } Console.WriteLine("Showing interop invocation results."); - var collectResults = await JSRuntime.InvokeAsync>("jsInteropTests.collectInteropResults"); + var collectResults = await JSRuntime.InvokeAsync>("jsInteropTests.collectInteropResults"); - ReturnValues = collectResults.ToDictionary(kvp => kvp.Key,kvp => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(kvp.Value))); + ReturnValues = collectResults.ToDictionary(kvp => kvp.Key, kvp => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(kvp.Value))); var invocations = new Dictionary(); foreach (var interopResult in JavaScriptInterop.Invocations) { - var interopResultValue = Json.Serialize(interopResult.Value); + var interopResultValue = JsonSerializer.ToString(interopResult.Value, TestJsonSerializerOptionsProvider.Options); invocations[interopResult.Key] = interopResultValue; } @@ -114,13 +114,15 @@ } var passDotNetObjectByRef = new TestDTO(99999); - var passDotNetObjectByRefArg = new Dictionary + var passDotNetObjectByRefArg = new PassDotNetObjectByRefArgs { - { "stringValue", "My string" }, - { "testDto", new DotNetObjectRef(passDotNetObjectByRef) }, + StringValue = "My string", + TestDto = DotNetObjectRef.Create(passDotNetObjectByRef), }; - ReceiveDotNetObjectByRefAsyncResult = await JSRuntime.InvokeAsync>("receiveDotNetObjectByRefAsync", passDotNetObjectByRefArg); - ReceiveDotNetObjectByRefAsyncResult["testDto"] = ReceiveDotNetObjectByRefAsyncResult["testDto"] == passDotNetObjectByRef ? "Same" : "Different"; + var result = await JSRuntime.InvokeAsync("receiveDotNetObjectByRefAsync", passDotNetObjectByRefArg); + ReceiveDotNetObjectByRefAsyncResult["stringValueUpper"] = result.StringValueUpper; + ReceiveDotNetObjectByRefAsyncResult["testDtoNonSerializedValue"] = result.TestDtoNonSerializedValue; + ReceiveDotNetObjectByRefAsyncResult["testDto"] = result.TestDto.Value == passDotNetObjectByRef ? "Same" : "Different"; ReturnValues["returnPrimitiveAsync"] = (await JSRuntime.InvokeAsync("returnPrimitiveAsync")).ToString(); ReturnValues["returnArrayAsync"] = string.Join(",", (await JSRuntime.InvokeAsync("returnArrayAsync")).Select(x => x.Source).ToArray()); @@ -148,12 +150,30 @@ } var passDotNetObjectByRef = new TestDTO(99999); - var passDotNetObjectByRefArg = new Dictionary + var passDotNetObjectByRefArg = new PassDotNetObjectByRefArgs { - { "stringValue", "My string" }, - { "testDto", new DotNetObjectRef(passDotNetObjectByRef) }, + StringValue = "My string", + TestDto = DotNetObjectRef.Create(passDotNetObjectByRef), }; - ReceiveDotNetObjectByRefResult = inProcRuntime.Invoke>("receiveDotNetObjectByRef", passDotNetObjectByRefArg); - ReceiveDotNetObjectByRefResult["testDto"] = ReceiveDotNetObjectByRefResult["testDto"] == passDotNetObjectByRef ? "Same" : "Different"; + var result = inProcRuntime.Invoke("receiveDotNetObjectByRef", passDotNetObjectByRefArg); + ReceiveDotNetObjectByRefResult["stringValueUpper"] = result.StringValueUpper; + ReceiveDotNetObjectByRefResult["testDtoNonSerializedValue"] = result.TestDtoNonSerializedValue; + ReceiveDotNetObjectByRefResult["testDto"] = result.TestDto.Value == passDotNetObjectByRef ? "Same" : "Different"; + } + + public class PassDotNetObjectByRefArgs + { + public string StringValue { get; set; } + + public DotNetObjectRef TestDto { get; set; } + } + + public class ReceiveDotNetObjectByRefArgs + { + public string StringValueUpper { get; set; } + + public int TestDtoNonSerializedValue { get; set; } + + public DotNetObjectRef TestDto { get; set; } } } diff --git a/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor index e3cafe0d65..125db6a477 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropOnInitializationComponent.razor @@ -19,7 +19,7 @@

Value set via JS interop call: - +

@code { diff --git a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs index f1487c06e3..a0f35c8040 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs +++ b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs @@ -4,6 +4,7 @@ using Microsoft.JSInterop; using System; using System.Collections.Generic; +using System.Text.Json; using System.Threading.Tasks; namespace BasicTestApp.InteropTest @@ -50,69 +51,69 @@ namespace BasicTestApp.InteropTest public static void VoidWithThreeParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3) + DotNetObjectRef parameter3) { - Invocations[nameof(VoidWithThreeParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue() }; + Invocations[nameof(VoidWithThreeParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue() }; } [JSInvokable] public static void VoidWithFourParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4) { - Invocations[nameof(VoidWithFourParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4 }; + Invocations[nameof(VoidWithFourParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4 }; } [JSInvokable] public static void VoidWithFiveParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5) { - Invocations[nameof(VoidWithFiveParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5 }; + Invocations[nameof(VoidWithFiveParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5 }; } [JSInvokable] public static void VoidWithSixParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6) { - Invocations[nameof(VoidWithSixParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; + Invocations[nameof(VoidWithSixParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; } [JSInvokable] public static void VoidWithSevenParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7) { - Invocations[nameof(VoidWithSevenParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; + Invocations[nameof(VoidWithSevenParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; } [JSInvokable] public static void VoidWithEightParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7, Segment parameter8) { - Invocations[nameof(VoidWithEightParameters)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; + Invocations[nameof(VoidWithEightParameters)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; } [JSInvokable] @@ -139,67 +140,67 @@ namespace BasicTestApp.InteropTest public static object[] EchoThreeParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3) + DotNetObjectRef parameter3) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue() }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue() }; } [JSInvokable] public static object[] EchoFourParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4 }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4 }; } [JSInvokable] public static object[] EchoFiveParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5 }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5 }; } [JSInvokable] public static object[] EchoSixParameters(ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; } [JSInvokable] public static object[] EchoSevenParameters(ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; } [JSInvokable] public static object[] EchoEightParameters( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7, Segment parameter8) { - return new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; + return new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; } [JSInvokable] @@ -229,9 +230,9 @@ namespace BasicTestApp.InteropTest public static Task VoidWithThreeParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3) + DotNetObjectRef parameter3) { - Invocations[nameof(VoidWithThreeParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue() }; + Invocations[nameof(VoidWithThreeParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue() }; return Task.CompletedTask; } @@ -239,10 +240,10 @@ namespace BasicTestApp.InteropTest public static Task VoidWithFourParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4) { - Invocations[nameof(VoidWithFourParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4 }; + Invocations[nameof(VoidWithFourParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4 }; return Task.CompletedTask; } @@ -250,11 +251,11 @@ namespace BasicTestApp.InteropTest public static Task VoidWithFiveParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5) { - Invocations[nameof(VoidWithFiveParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5 }; + Invocations[nameof(VoidWithFiveParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5 }; return Task.CompletedTask; } @@ -262,12 +263,12 @@ namespace BasicTestApp.InteropTest public static Task VoidWithSixParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6) { - Invocations[nameof(VoidWithSixParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; + Invocations[nameof(VoidWithSixParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6 }; return Task.CompletedTask; } @@ -275,13 +276,13 @@ namespace BasicTestApp.InteropTest public static Task VoidWithSevenParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7) { - Invocations[nameof(VoidWithSevenParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; + Invocations[nameof(VoidWithSevenParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }; return Task.CompletedTask; } @@ -289,14 +290,14 @@ namespace BasicTestApp.InteropTest public static Task VoidWithEightParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7, Segment parameter8) { - Invocations[nameof(VoidWithEightParametersAsync)] = new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; + Invocations[nameof(VoidWithEightParametersAsync)] = new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }; return Task.CompletedTask; } @@ -324,124 +325,142 @@ namespace BasicTestApp.InteropTest public static Task EchoThreeParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3) + DotNetObjectRef parameter3) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue() }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue() }); } [JSInvokable] public static Task EchoFourParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4 }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4 }); } [JSInvokable] public static Task EchoFiveParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5 }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5 }); } [JSInvokable] public static Task EchoSixParametersAsync(ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6 }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6 }); } [JSInvokable] public static Task EchoSevenParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7 }); } [JSInvokable] public static Task EchoEightParametersAsync( ComplexParameter parameter1, byte parameter2, - TestDTO parameter3, + DotNetObjectRef parameter3, int parameter4, long parameter5, float parameter6, List parameter7, Segment parameter8) { - return Task.FromResult(new object[] { parameter1, parameter2, parameter3.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }); + return Task.FromResult(new object[] { parameter1, parameter2, parameter3.Value.GetNonSerializedValue(), parameter4, parameter5, parameter6, parameter7, parameter8 }); } [JSInvokable] - public static Dictionary ReturnDotNetObjectByRef() + public static Dictionary> ReturnDotNetObjectByRef() { - return new Dictionary + return new Dictionary> { - { "Some sync instance", new DotNetObjectRef(new TestDTO(1000)) } + { "Some sync instance", DotNetObjectRef.Create(new TestDTO(1000)) } }; } [JSInvokable] - public static async Task> ReturnDotNetObjectByRefAsync() + public static async Task>> ReturnDotNetObjectByRefAsync() { await Task.Yield(); - return new Dictionary + return new Dictionary> { - { "Some async instance", new DotNetObjectRef(new TestDTO(1001)) } + { "Some async instance", DotNetObjectRef.Create(new TestDTO(1001)) } }; } [JSInvokable] - public static int ExtractNonSerializedValue(TestDTO objectByRef) + public static int ExtractNonSerializedValue(DotNetObjectRef objectByRef) { - return objectByRef.GetNonSerializedValue(); + return objectByRef.Value.GetNonSerializedValue(); } [JSInvokable] - public Dictionary InstanceMethod(Dictionary dict) + public InstanceMethodOutput InstanceMethod(InstanceMethodInput input) { // This method shows we can pass in values marshalled both as JSON (the dict itself) // and by ref (the incoming dtoByRef), plus that we can return values marshalled as // JSON (the returned dictionary) and by ref (the outgoingByRef value) - return new Dictionary + return new InstanceMethodOutput { - { "thisTypeName", GetType().Name }, - { "stringValueUpper", ((string)dict["stringValue"]).ToUpperInvariant() }, - { "incomingByRef", ((TestDTO)dict["dtoByRef"]).GetNonSerializedValue() }, - { "outgoingByRef", new DotNetObjectRef(new TestDTO(1234)) }, + ThisTypeName = GetType().Name, + StringValueUpper = input.StringValue.ToUpperInvariant(), + IncomingByRef = input.DTOByRef.Value.GetNonSerializedValue(), + OutgoingByRef = DotNetObjectRef.Create(new TestDTO(1234)), }; } [JSInvokable] - public async Task> InstanceMethodAsync(Dictionary dict) + public async Task InstanceMethodAsync(InstanceMethodInput input) { - // This method shows we can pass in values marshalled both as JSON (the dict itself) + // This method shows we can pass in values marshalled both as JSON // and by ref (the incoming dtoByRef), plus that we can return values marshalled as // JSON (the returned dictionary) and by ref (the outgoingByRef value) await Task.Yield(); - return new Dictionary + return new InstanceMethodOutput { - { "thisTypeName", GetType().Name }, - { "stringValueUpper", ((string)dict["stringValue"]).ToUpperInvariant() }, - { "incomingByRef", ((TestDTO)dict["dtoByRef"]).GetNonSerializedValue() }, - { "outgoingByRef", new DotNetObjectRef(new TestDTO(1234)) }, + ThisTypeName = GetType().Name, + StringValueUpper = input.StringValue.ToUpperInvariant(), + IncomingByRef = input.DTOByRef.Value.GetNonSerializedValue(), + OutgoingByRef = DotNetObjectRef.Create(new TestDTO(1234)), }; } + + public class InstanceMethodInput + { + public string StringValue { get; set; } + + public DotNetObjectRef DTOByRef { get; set; } + } + + public class InstanceMethodOutput + { + public string ThisTypeName { get; set; } + + public string StringValueUpper { get; set; } + + public int IncomingByRef { get; set; } + + public DotNetObjectRef OutgoingByRef { get; set; } + } } } diff --git a/src/Components/test/testassets/BasicTestApp/InteropTest/Segment.cs b/src/Components/test/testassets/BasicTestApp/InteropTest/Segment.cs index 2d7967775d..09c9cf282e 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropTest/Segment.cs +++ b/src/Components/test/testassets/BasicTestApp/InteropTest/Segment.cs @@ -3,7 +3,7 @@ namespace BasicTestApp.InteropTest { - public struct Segment + public class Segment { public string Source { get; set; } public int Start { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor b/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor index ab5271118d..7cf94526d9 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyCasesComponent.razor @@ -1,9 +1,9 @@ -@using Microsoft.JSInterop +@using System.Text.Json.Serialization

Model

- - + +

Output

@@ -61,7 +61,7 @@ void Update() { renderContext.UpdateCount++; - parsedRootNode = Json.Deserialize(modelJson); + parsedRootNode = JsonSerializer.Parse(modelJson, TestJsonSerializerOptionsProvider.Options); } public class RenderContext diff --git a/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor b/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor index 7634bb95f0..551038293c 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyCasesTreeNode.razor @@ -21,7 +21,7 @@ { if (child.Key != null) { - + } else { @@ -35,7 +35,7 @@ @code { public class Node { - public object Key { get; set; } + public string Key { get; set; } public string Label { get; set; } public List Children { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor b/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor index bc8f8b95ea..31a2248609 100644 --- a/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/KeyPressEventComponent.razor @@ -1,6 +1,6 @@ -@using Microsoft.JSInterop +@using System.Text.Json.Serialization -Type here: +Type here:
    @foreach (var key in keysPressed) { @@ -13,7 +13,7 @@ Type here: void OnKeyPressed(UIKeyboardEventArgs eventArgs) { - Console.WriteLine(Json.Serialize(eventArgs)); + Console.WriteLine(JsonSerializer.ToString(eventArgs)); keysPressed.Add(eventArgs.Key); } } diff --git a/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor b/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor index 186eeec72e..28055dbff4 100644 --- a/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/MarkupBlockComponent.razor @@ -16,7 +16,7 @@ [@((RenderFragment)EmitMarkupBlock)]
- +

Markup string

diff --git a/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor b/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor index 2bba6f2b62..8b3fe5cd0a 100644 --- a/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/MouseEventComponent.razor @@ -1,5 +1,5 @@ @using System.Collections.Generic -@using Microsoft.JSInterop +@using System.Text.Json.Serialization

Mouse position

@@ -7,16 +7,16 @@ Output: @message

- Mouseover: + Mouseover:

- Mousemove city! + Mousemove city!

- Mousedown: + Mousedown:

- +

@@ -65,7 +65,7 @@ void DumpEvent(UIMouseEventArgs e) { - Console.WriteLine(Json.Serialize(e)); + Console.WriteLine(JsonSerializer.ToString(e)); } void Clear() diff --git a/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor b/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor index c6a5e9f624..9229fb49ae 100644 --- a/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor +++ b/src/Components/test/testassets/BasicTestApp/MultipleChildContent.razor @@ -11,7 +11,7 @@ -Toggle: +Toggle: @code { List Items { get; } = new List() diff --git a/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor b/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor index 27bb0041cf..192efbacd0 100644 --- a/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor +++ b/src/Components/test/testassets/BasicTestApp/PrerenderedToInteractiveTransition.razor @@ -12,7 +12,7 @@

Clicks: @count - +

@code { diff --git a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor index b201311876..476e8cbb10 100644 --- a/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor +++ b/src/Components/test/testassets/BasicTestApp/PropertiesChangedHandlerParent.razor @@ -1,5 +1,5 @@ - - + + @code { private int valueToSupply = 100; diff --git a/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor b/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor index fb6b98aba6..a13806f756 100644 --- a/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor +++ b/src/Components/test/testassets/BasicTestApp/RenderFragmentToggler.razor @@ -1,4 +1,4 @@ -
+

Fragment will be toggled below

@if (showFragment) @@ -6,7 +6,7 @@ @ExampleFragment } - +

The end

diff --git a/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor b/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor index 594555a689..bc06f23056 100644 --- a/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/ReorderingFocusComponent.razor @@ -11,9 +11,9 @@
    @foreach (var item in todoItems.Where(item => !item.IsDone)) { -
  • - - +
  • + +
  • }
@@ -23,9 +23,9 @@
    @foreach (var item in todoItems.Where(item => item.IsDone)) { -
  • - - +
  • + +
  • }
diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/Links.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/Links.razor index 6c405d4774..fdedf1852f 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/Links.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/Links.razor @@ -14,11 +14,11 @@
  • With more parameters
  • - - diff --git a/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor b/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor index c27f827a6d..5d5d2cf4ec 100644 --- a/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/RouterTest/UriHelperComponent.razor @@ -1,7 +1,7 @@ @inject IUriHelper UriHelper @inject Microsoft.JSInterop.IJSRuntime JSRuntime - + @UrlLocation diff --git a/src/Components/test/testassets/BasicTestApp/SvgComponent.razor b/src/Components/test/testassets/BasicTestApp/SvgComponent.razor index 4c538e6c2d..2cece59303 100644 --- a/src/Components/test/testassets/BasicTestApp/SvgComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/SvgComponent.razor @@ -2,7 +2,7 @@ - + @code { int radius = 10; diff --git a/src/Components/test/testassets/BasicTestApp/TestJsonSerializerOptionsProvider.cs b/src/Components/test/testassets/BasicTestApp/TestJsonSerializerOptionsProvider.cs new file mode 100644 index 0000000000..d125ae306d --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/TestJsonSerializerOptionsProvider.cs @@ -0,0 +1,15 @@ +// 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.Text.Json.Serialization; + +namespace BasicTestApp +{ + internal static class TestJsonSerializerOptionsProvider + { + public static JsonSerializerOptions Options { get; } = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + } +} diff --git a/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor b/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor index 0a7fe9e462..3e11741903 100644 --- a/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/TouchEventComponent.razor @@ -1,5 +1,5 @@ @using System.Collections.Generic -@using Microsoft.JSInterop +@using System.Text.Json.Serialization

    Touch position

    @@ -7,17 +7,17 @@ Output: @message

    -

    - +

    @@ -28,7 +28,7 @@ void OnTouch(UITouchEventArgs e) { message += e.Type; - Console.WriteLine(Json.Serialize(e)); + Console.WriteLine(JsonSerializer.ToString(e)); StateHasChanged(); } diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor index 572f9c88dc..ea87f6be2d 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Counter.razor @@ -1,10 +1,10 @@ -@page "/counter" +@page "/counter"

    Counter

    Current count: @currentCount

    - + @code { int currentCount = 0; diff --git a/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor b/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor index cb1be064f1..5bcc5fd85e 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Pages/Error.razor @@ -7,7 +7,7 @@ } else { - + } @code { public bool ShouldCauseError { get; set; } diff --git a/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor b/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor index 38e23a6b6d..daace22537 100644 --- a/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor +++ b/src/Components/test/testassets/ComponentsApp.App/Shared/NavMenu.razor @@ -1,11 +1,11 @@ -
    +