-
Notifications
You must be signed in to change notification settings - Fork 10.4k
[Blazor] Enable websocket compression for Blazor Server and Interactive Server components in Blazor web #53389
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
javiercn
merged 18 commits into
main
from
javiercn/net90-enable-websocket-compression-blazor-server-web
Feb 5, 2024
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4f55445
[Blazor][SignalR] Proof of concept. Enabling compression in websockets
javiercn 2d8a248
Enable websocket compression
javiercn 1f140af
Cleanups
javiercn c144ef2
Cleanups
javiercn 9208894
Apply CSP policy
javiercn 6bc5586
Add E2E tests
javiercn 208f098
Default to 'self'
javiercn 03f1079
Simplify settings by just providing the callback
javiercn 49f9824
Address feedback
javiercn 669ebea
Fix build
javiercn 2940632
Fix tests
javiercn 8e34e50
Alternative approach that avoids configuring compression via SignalR
javiercn bcbd6df
Undo SignalR changes
javiercn 60454e8
Clean up API and fix build
javiercn ca95d24
Update tests
javiercn 9384ce7
Fix tests
javiercn ed7087f
Cleanup implementation
javiercn f7452f4
Cleanup implementation
javiercn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 4 additions & 3 deletions
7
src/Components/Server/src/Builder/InternalServerRenderMode.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.AspNetCore.Components.Web; | ||
|
||
namespace Microsoft.AspNetCore.Builder; | ||
namespace Microsoft.AspNetCore.Components.Server; | ||
|
||
internal class InternalServerRenderMode : InteractiveServerRenderMode | ||
internal class InternalServerRenderMode(ServerComponentsEndpointOptions options) : InteractiveServerRenderMode | ||
{ | ||
public ServerComponentsEndpointOptions? Options { get; } = options; | ||
} |
44 changes: 44 additions & 0 deletions
44
src/Components/Server/src/Builder/ServerComponentsEndpointOptions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace Microsoft.AspNetCore.Components.Server; | ||
|
||
/// <summary> | ||
/// Options to configure interactive Server components. | ||
/// </summary> | ||
public class ServerComponentsEndpointOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the <c>frame-ancestors</c> <c>Content-Security-Policy</c> to set in the | ||
/// <see cref="HttpResponse"/> when <see cref="ConfigureWebsocketOptions" /> is set. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para>Setting this value to <see langword="null" /> will prevent the policy from being | ||
/// automatically applied, which might make the app vulnerable. Care must be taken to apply | ||
/// a policy in this case whenever the first document is rendered. | ||
/// </para> | ||
/// <para> | ||
/// A content security policy provides defense against security threats that can occur if | ||
/// the app uses compression and can be embedded in other origins. When compression is enabled, | ||
/// embedding the app inside an <c>iframe</c> from other origins is prohibited. | ||
/// </para> | ||
/// <para> | ||
/// For more details see the security recommendations for Interactive Server Components in | ||
/// the official documentation. | ||
/// </para> | ||
/// </remarks> | ||
public string? ContentSecurityFrameAncestorPolicy { get; set; } = "'self'"; | ||
|
||
/// <summary> | ||
/// Gets or sets a function to configure the <see cref="WebSocketAcceptContext"/> for the websocket connections | ||
/// used by the server components. | ||
/// By default, a policy that enables compression and sets a Content Security Policy for the frame ancestors | ||
/// defined in <see cref="ContentSecurityFrameAncestorPolicy"/> will be applied. | ||
/// </summary> | ||
public Func<HttpContext, WebSocketAcceptContext>? ConfigureWebsocketOptions { get; set; } = EnableCompressionDefaults; | ||
|
||
private static WebSocketAcceptContext EnableCompressionDefaults(HttpContext context) => | ||
new() { DangerousEnableCompression = true }; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,8 @@ | ||
#nullable enable | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ConfigureWebsocketOptions.get -> System.Func<Microsoft.AspNetCore.Http.HttpContext!, Microsoft.AspNetCore.Http.WebSocketAcceptContext!>? | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ConfigureWebsocketOptions.set -> void | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ContentSecurityFrameAncestorPolicy.get -> string? | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ContentSecurityFrameAncestorPolicy.set -> void | ||
Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions.ServerComponentsEndpointOptions() -> void | ||
static Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions.AddInteractiveServerRenderMode(this Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! builder, System.Action<Microsoft.AspNetCore.Components.Server.ServerComponentsEndpointOptions!>? callback = null) -> Microsoft.AspNetCore.Builder.RazorComponentsEndpointConventionBuilder! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
src/Components/test/E2ETest/ServerExecutionTests/WebSocketCompressionTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Text.RegularExpressions; | ||
using Components.TestServer.RazorComponents; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; | ||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; | ||
using Microsoft.AspNetCore.E2ETesting; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.VisualStudio.TestPlatform.Utilities; | ||
using OpenQA.Selenium; | ||
using TestServer; | ||
using Xunit.Abstractions; | ||
|
||
namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests; | ||
|
||
public abstract partial class AllowedWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) | ||
: ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>>>(browserFixture, serverFixture, output) | ||
{ | ||
[Fact] | ||
public void EmbeddingServerAppInsideIframe_Works() | ||
{ | ||
Navigate("/subdir/iframe"); | ||
|
||
var logs = Browser.GetBrowserLogs(LogLevel.Severe); | ||
|
||
Assert.Empty(logs); | ||
|
||
// Get the iframe element from the page, and inspect its contents for a p element with id inside-iframe | ||
var iframe = Browser.FindElement(By.TagName("iframe")); | ||
Browser.SwitchTo().Frame(iframe); | ||
Browser.Exists(By.Id("inside-iframe")); | ||
} | ||
} | ||
|
||
public abstract partial class BlockedWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) | ||
: ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>>>(browserFixture, serverFixture, output) | ||
{ | ||
[Fact] | ||
public void EmbeddingServerAppInsideIframe_WithCompressionEnabled_Fails() | ||
{ | ||
Navigate("/subdir/iframe"); | ||
|
||
var logs = Browser.GetBrowserLogs(LogLevel.Severe); | ||
|
||
Assert.True(logs.Count > 0); | ||
|
||
Assert.Matches(ParseErrorMessage(), logs[0].Message); | ||
} | ||
|
||
[GeneratedRegex(@"security - Refused to frame 'http://\d+\.\d+\.\d+\.\d+:\d+/' because an ancestor violates the following Content Security Policy directive: ""frame-ancestors 'none'"".")] | ||
private static partial Regex ParseErrorMessage(); | ||
} | ||
|
||
public partial class DefaultConfigurationWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) | ||
: AllowedWebSocketCompressionTests(browserFixture, serverFixture, output) | ||
{ | ||
} | ||
|
||
public partial class CustomConfigurationCallbackWebSocketCompressionTests : AllowedWebSocketCompressionTests | ||
{ | ||
public CustomConfigurationCallbackWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) : base(browserFixture, serverFixture, output) | ||
{ | ||
serverFixture.UpdateHostServices = services => | ||
{ | ||
var configuration = services.GetService<WebSocketCompressionConfiguration>(); | ||
configuration.ConnectionDispatcherOptions = context => new() { DangerousEnableCompression = true }; | ||
}; | ||
} | ||
} | ||
|
||
public partial class CompressionDisabledWebSocketCompressionTests : AllowedWebSocketCompressionTests | ||
{ | ||
public CompressionDisabledWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) : base( | ||
browserFixture, serverFixture, output) | ||
{ | ||
serverFixture.UpdateHostServices = services => | ||
{ | ||
var configuration = services.GetService<WebSocketCompressionConfiguration>(); | ||
configuration.IsCompressionEnabled = false; | ||
configuration.ConnectionDispatcherOptions = null; | ||
}; | ||
} | ||
} | ||
|
||
public partial class NoneAncestorWebSocketCompressionTests : BlockedWebSocketCompressionTests | ||
{ | ||
public NoneAncestorWebSocketCompressionTests( | ||
BrowserFixture browserFixture, | ||
BasicTestAppServerSiteFixture<RazorComponentEndpointsStartup<App>> serverFixture, | ||
ITestOutputHelper output) | ||
: base(browserFixture, serverFixture, output) | ||
{ | ||
serverFixture.UpdateHostServices = services => | ||
{ | ||
var configuration = services.GetService<WebSocketCompressionConfiguration>(); | ||
configuration.CspPolicy = "'none'"; | ||
}; | ||
} | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.