Skip to content

[automated] Merge branch 'release/8.0' => 'main' #50973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/targets/Helix.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<HelixTimeout>00:45:00</HelixTimeout>
<HelixTestName>$(MSBuildProjectName)--$(TargetFramework)</HelixTestName>
<LoggingTestingDisableFileLogging Condition="'$(IsHelixJob)' == 'true'">false</LoggingTestingDisableFileLogging>
<NodeVersion>16.11.0</NodeVersion>
<NodeVersion>20.7.0</NodeVersion>

<!--
Many tests depend on the .dotnet/ layouts but only a few (those that restore or test packages) require
Expand Down
15 changes: 0 additions & 15 deletions src/Antiforgery/src/AntiforgeryApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Antiforgery.Internal;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.AspNetCore.Builder;

Expand All @@ -26,19 +24,6 @@ public static IApplicationBuilder UseAntiforgery(this IApplicationBuilder builde
builder.VerifyAntiforgeryServicesAreRegistered();

builder.Properties[AntiforgeryMiddlewareSetKey] = true;

// The anti-forgery middleware adds annotations to HttpContext.Items to indicate that it has run
// that will be validated by the EndpointsRoutingMiddleware later. To do this, we need to ensure
// that routing has run and set the endpoint feature on the HttpContext associated with the request.
if (builder.Properties.TryGetValue(RerouteHelper.GlobalRouteBuilderKey, out var routeBuilder) && routeBuilder is not null)
{
return builder.Use(next =>
{
var newNext = RerouteHelper.Reroute(builder, routeBuilder, next);
var antiforgery = builder.ApplicationServices.GetRequiredService<IAntiforgery>();
return new AntiforgeryMiddleware(antiforgery, newNext).Invoke;
});
}
builder.UseMiddleware<AntiforgeryMiddleware>();

return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@

<ItemGroup>
<Compile Include="$(SharedSourceRoot)HttpExtensions.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)Reroute.cs" LinkBase="Shared"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public DefaultAntiforgeryStateProvider(PersistentComponentState state)
// don't have access to the request.
_subscription = state.RegisterOnPersisting(() =>
{
state.PersistAsJson(PersistenceKey, _currentToken);
state.PersistAsJson(PersistenceKey, GetAntiforgeryToken());
return Task.CompletedTask;
}, new InteractiveAutoRenderMode());
}, RenderMode.InteractiveWebAssembly);

state.TryTakeFromJson(PersistenceKey, out _currentToken);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.web.js

Large diffs are not rendered by default.

52 changes: 35 additions & 17 deletions src/Components/Web.JS/src/Services/WebRootComponentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type RootComponentInfo = {
assignedRendererId?: WebRendererId;
uniqueIdAtLastUpdate?: number;
interactiveComponentId?: number;
}
};

export class WebRootComponentManager implements DescriptorHandler, RootComponentManager<never> {
private readonly _rootComponents = new Set<RootComponentInfo>();
Expand Down Expand Up @@ -193,7 +193,7 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
}

private circuitMayHaveNoRootComponents() {
const isCircuitInUse = this.hasAnyExistingOrPendingServerComponents();
const isCircuitInUse = this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server', 'auto');
if (isCircuitInUse) {
// Clear the timeout because we know the circuit is in use.
clearTimeout(this._circuitInactivityTimeoutId);
Expand All @@ -208,31 +208,38 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent

// Start a new timeout to dispose the circuit unless it starts getting used.
this._circuitInactivityTimeoutId = setTimeout(() => {
if (!this.hasAnyExistingOrPendingServerComponents()) {
if (!this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server', 'auto')) {
disposeCircuit();
this._circuitInactivityTimeoutId = undefined;
}
}, this._circuitInactivityTimeoutMs) as unknown as number;
}

private hasAnyExistingOrPendingServerComponents(): boolean {
// If there are active Blazor Server components on the page, we shouldn't dispose the circuit.
const renderer = getRendererer(WebRendererId.Server);
if (renderer && renderer.getRootComponentCount() > 0) {
private rendererHasComponents(rendererId: WebRendererId): boolean {
const renderer = getRendererer(rendererId);
return renderer !== undefined && renderer.getRootComponentCount() > 0;
}

private rendererHasExistingOrPendingComponents(rendererId: WebRendererId, ...descriptorTypesToConsider: ComponentMarker['type'][]): boolean {
if (this.rendererHasComponents(rendererId)) {
return true;
}

// If we have SSR components that may become Blazor Server components in the future,
// we shouldn't dispose the circuit.
// We consider SSR'd components on the page that may get activated using the specified renderer.
for (const { descriptor: { type }, assignedRendererId } of this._rootComponents) {
if (assignedRendererId === WebRendererId.Server) {
// The component has been assigned to use Blazor Server.
if (assignedRendererId === rendererId) {
// The component has been assigned to use the specified renderer.
return true;
}

if (assignedRendererId === undefined && (type === 'auto' || type === 'server')) {
// The component has not been assigned a renderer yet, so it's possible it might
// use Blazor Server.
if (assignedRendererId !== undefined) {
// The component has been assigned to use another renderer.
continue;
}

if (descriptorTypesToConsider.indexOf(type) !== -1) {
// The component has not been assigned a renderer yet, but it might get activated with the specified renderer
// if it doesn't get removed from the page.
return true;
}
}
Expand Down Expand Up @@ -298,9 +305,20 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
}

private getAutoRenderMode(): 'webassembly' | 'server' | null {
// If the WebAssembly runtime has loaded, we will always use WebAssembly
// for auto components. Otherwise, we'll wait to activate root components
// until we determine whether the WebAssembly runtime can be loaded quickly.
// If WebAssembly components exist or may exist soon, use WebAssembly.
if (this.rendererHasExistingOrPendingComponents(WebRendererId.WebAssembly, 'webassembly')) {
return 'webassembly';
}

// If Server components exist or may exist soon, use WebAssembly.
if (this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server')) {
return 'server';
}

// If no interactive components are on the page, we use WebAssembly
// if the WebAssembly runtime has loaded. Otherwise, we'll wait to activate
// root components until we determine whether the WebAssembly runtime can be
// loaded quickly.
if (hasLoadedWebAssemblyPlatform()) {
return 'webassembly';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,19 @@ public void FormNoAntiforgeryReturnBadRequest(bool suppressEnhancedNavigation)
DispatchToFormCore(dispatchToForm);
}

[Fact]
public void CanUseAntiforgeryTokenInWasm()
{
var dispatchToForm = new DispatchToForm(this)
{
Url = "forms/antiforgery-wasm",
FormCssSelector = "form",
InputFieldId = "Value",
SuppressEnhancedNavigation = true,
};
DispatchToFormCore(dispatchToForm);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,84 @@ public void AutoRenderMode_CanUseBlazorWebAssembly_WhenMultipleAutoComponentsAre
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);
}

[Fact]
public void AutoRenderMode_UsesBlazorWebAssembly_WhenBothServerAndWebAssemblyComponentsExist()
{
Navigate($"{ServerPathBase}/streaming-interactivity");
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);

Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);

Browser.Click(By.Id(AddServerPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);

Browser.Click(By.Id(AddAutoPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);
}

[Fact]
public void AutoRenderMode_UsesBlazorServer_WhenOnlyServerComponentsExist_EvenAfterWebAssemblyResourcesLoad()
{
Navigate(ServerPathBase);
Browser.Equal("Hello", () => Browser.Exists(By.TagName("h1")).Text);
ForceWebAssemblyResourceCacheMiss();

Navigate($"{ServerPathBase}/streaming-interactivity");
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);

// We start by adding a WebAssembly component to ensure the WebAssembly runtime
// will be cached after we refresh the page.
Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);

Browser.Click(By.Id($"remove-counter-link-0"));
Browser.DoesNotExist(By.Id("is-interactive-0"));

Browser.Navigate().Refresh();

Browser.Click(By.Id(AddServerPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);

// Verify that Auto mode will use Blazor Server, even though the WebAssembly runtime is cached
Browser.Click(By.Id(AddAutoPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-2")).Text);
}

[Fact]
public void AutoRenderMode_UsesBlazorServer_AfterWebAssemblyComponentsNoLongerExist_ButServerComponentsDo()
{
Navigate($"{ServerPathBase}/streaming-interactivity");
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);

Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);

Browser.Click(By.Id(AddServerPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);

Browser.Click(By.Id(AddAutoPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);

// Remove all WebAssembly components
Browser.Click(By.Id("remove-counter-link-0"));
Browser.Click(By.Id("remove-counter-link-2"));

// Verify that Blazor Server gets used
Browser.Click(By.Id(AddAutoPrerenderedId));
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-3")).Text);
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-3")).Text);
}

[Fact]
public void Circuit_ShutsDown_WhenAllBlazorServerComponentsGetRemoved()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
<Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<Reference Include="Microsoft.AspNetCore.Components.Server" />
<Reference Include="Microsoft.AspNetCore.Http.Results" />
<Reference Include="Microsoft.AspNetCore.Cors" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.Components.Server" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Components.TestServer.RazorComponents.Pages.Forms;
using Components.TestServer.Services;
using Microsoft.AspNetCore.Components.WebAssembly.Server;
using Microsoft.AspNetCore.Mvc;

namespace TestServer;

Expand Down Expand Up @@ -154,6 +155,11 @@ private static void MapEnhancedNavigationEndpoints(IEndpointRouteBuilder endpoin
await response.WriteAsync("<html><body><h1>This is a non-Blazor endpoint</h1><p>That's all</p></body></html>");
});

endpoints.MapPost("api/antiforgery-form", ([FromForm] string value) =>
{
return Results.Ok(value);
});

static Task PerformRedirection(HttpRequest request, HttpResponse response)
{
response.Redirect(request.Query["external"] == "true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@page "/forms/antiforgery-wasm"
<h3>FormRunningOnWasmCanUseAntiforgeryToken</h3>

<TestContentPackage.WasmFormComponent/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
@attribute [RenderModeInteractiveWebAssembly]
@using Microsoft.AspNetCore.Components.Forms
@using System.Net.Http
<h3>WasmFormComponent</h3>

<form @formname="WasmForm" @onsubmit="SubmitForm">
<input id="Value" @bind="_value" name="Value" />
@if (OperatingSystem.IsBrowser())
{
<input id="send" type="submit" value="Send" />
}
<AntiforgeryToken />
</form>

@if (_posted)
{
@if (_succeeded)
{
<p id="pass">Posting the value succeded.</p>
}
else
{
<p>Posting the value failed.</p>
}
}
else
{
<p>Antiforgery: @_token</p>
}

@code {
string _value;
string _token;
bool _succeeded;
bool _posted;

[Inject] public AntiforgeryStateProvider AntiforgeryState { get; set; }
[Inject] public NavigationManager Navigation { get; set; }

protected override void OnInitialized()
{
if (OperatingSystem.IsBrowser())
{
var antiforgery = AntiforgeryState.GetAntiforgeryToken();
_token = antiforgery.Value;
}
}

private async Task SubmitForm()
{
if (OperatingSystem.IsBrowser())
{
var client = new HttpClient() { BaseAddress = new Uri(Navigation.BaseUri) };
var antiforgery = AntiforgeryState.GetAntiforgeryToken();
if (antiforgery != null)
{
_posted = true;
var request = new HttpRequestMessage(HttpMethod.Post, "api/antiforgery-form");
var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new ("Value", _value) });
request.Content = content;
request.Headers.Add("RequestVerificationToken", antiforgery.Value);
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
_succeeded = true;
}
else
{
_succeeded = false;
}
}
}
}
}
11 changes: 0 additions & 11 deletions src/DefaultBuilder/src/WebApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
Expand All @@ -25,7 +24,6 @@ public sealed class WebApplicationBuilder : IHostApplicationBuilder
private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";
private const string AuthenticationMiddlewareSetKey = "__AuthenticationMiddlewareSet";
private const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet";
private const string AntiforgeryMiddlewareSetKey = "__AntiforgeryMiddlewareSet";
private const string UseRoutingKey = "__UseRouting";

private readonly HostApplicationBuilder _hostApplicationBuilder;
Expand Down Expand Up @@ -453,15 +451,6 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
}
}

if (serviceProviderIsService?.IsService(typeof(IAntiforgery)) is true)
{
if (!_builtApplication.Properties.ContainsKey(AntiforgeryMiddlewareSetKey))
{
_builtApplication.Properties[AntiforgeryMiddlewareSetKey] = true;
app.UseAntiforgery();
}
}

// Wire the source pipeline to run in the destination pipeline
var wireSourcePipeline = new WireSourcePipeline(_builtApplication);
app.Use(wireSourcePipeline.CreateMiddleware);
Expand Down
Loading