.Empty, NextCounterId: 0);
bool _isStreaming = false;
+ [CascadingParameter]
+ public HttpContext HttpContext { get; set; }
+
[SupplyParameterFromQuery]
public string? InitialState { get; set; }
@@ -109,6 +112,9 @@ else
[SupplyParameterFromQuery]
public bool DisableKeys { get; set; }
+ [SupplyParameterFromQuery]
+ public bool ClearSiteData { get; set; }
+
protected override async Task OnInitializedAsync()
{
if (InitialState is not null)
@@ -116,6 +122,11 @@ else
_state = ReadStateFromJson(InitialState);
}
+ if (ClearSiteData)
+ {
+ HttpContext.Response.Headers["Clear-Site-Data"] = "\"*\"";
+ }
+
if (ShouldStream)
{
_isStreaming = true;
diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithWebAssemblyInteractivity.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithWebAssemblyInteractivity.razor
new file mode 100644
index 000000000000..0d24a1dfcac2
--- /dev/null
+++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/PersistentState/PageWithWebAssemblyInteractivity.razor
@@ -0,0 +1,8 @@
+@page "/persistent-state/page-with-webassembly-interactivity"
+
+
+ This page is used to ensure that the WebAssembly runtime is downloaded and available
+ so that WebAssembly interactivity will get used for components with the Auto render mode
+
+
+
diff --git a/src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs b/src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs
index f9da7eb5e304..8fa7ccc18491 100644
--- a/src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs
+++ b/src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs
@@ -112,7 +112,6 @@ public TimeSpan TransportSendTimeout
ArgumentOutOfRangeException.ThrowIfEqual(value, TimeSpan.Zero);
_transportSendTimeout = value;
- TransportSendTimeoutTicks = value.Ticks;
}
}
@@ -133,7 +132,6 @@ public TimeSpan TransportSendTimeout
///
public bool AllowStatefulReconnects { get; set; }
- internal long TransportSendTimeoutTicks { get; private set; }
internal bool TransportSendTimeoutEnabled => _transportSendTimeout != Timeout.InfiniteTimeSpan;
// We initialize these lazily based on the state of the options specified here.
diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs
index bb426523ba81..3a859123651d 100644
--- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs
+++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs
@@ -50,7 +50,7 @@ internal sealed partial class HttpConnectionContext : ConnectionContext,
private CancellationTokenSource? _sendCts;
private bool _activeSend;
- private long _startedSendTime;
+ private TimeSpan _startedSendTime;
private bool _useStatefulReconnect;
private readonly object _sendingLock = new object();
internal CancellationToken SendingToken { get; private set; }
@@ -74,7 +74,7 @@ public HttpConnectionContext(string connectionId, string connectionToken, ILogge
ConnectionId = connectionId;
ConnectionToken = connectionToken;
- LastSeenTicks = Environment.TickCount64;
+ LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64);
_options = options;
// The default behavior is that both formats are supported.
@@ -140,9 +140,9 @@ public HttpConnectionContext(string connectionId, string connectionToken, ILogge
public Task? ApplicationTask { get; set; }
- public long LastSeenTicks { get; set; }
+ public TimeSpan LastSeenTicks { get; set; }
- public long? LastSeenTicksIfInactive
+ public TimeSpan? LastSeenTicksIfInactive
{
get
{
@@ -618,7 +618,7 @@ public void MarkInactive()
if (Status == HttpConnectionStatus.Active)
{
Status = HttpConnectionStatus.Inactive;
- LastSeenTicks = Environment.TickCount64;
+ LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64);
}
}
}
@@ -650,12 +650,12 @@ internal void StartSendCancellation()
_sendCts = new CancellationTokenSource();
SendingToken = _sendCts.Token;
}
- _startedSendTime = Environment.TickCount64;
+ _startedSendTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
_activeSend = true;
}
}
- internal void TryCancelSend(long currentTicks)
+ internal void TryCancelSend(TimeSpan currentTicks)
{
if (!_options.TransportSendTimeoutEnabled)
{
@@ -666,7 +666,7 @@ internal void TryCancelSend(long currentTicks)
{
if (_activeSend)
{
- if (currentTicks - _startedSendTime > _options.TransportSendTimeoutTicks)
+ if (currentTicks - _startedSendTime > _options.TransportSendTimeout)
{
_sendCts!.Cancel();
diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
index 2677f5b4fc0f..ba658aa38ac7 100644
--- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
+++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
@@ -24,7 +24,7 @@ internal sealed partial class HttpConnectionManager
private readonly PeriodicTimer _nextHeartbeat;
private readonly ILogger _logger;
private readonly ILogger _connectionLogger;
- private readonly long _disconnectTimeoutTicks;
+ private readonly TimeSpan _disconnectTimeout;
private readonly HttpConnectionsMetrics _metrics;
public HttpConnectionManager(ILoggerFactory loggerFactory, IHostApplicationLifetime appLifetime, IOptions connectionOptions, HttpConnectionsMetrics metrics)
@@ -32,7 +32,7 @@ public HttpConnectionManager(ILoggerFactory loggerFactory, IHostApplicationLifet
_logger = loggerFactory.CreateLogger();
_connectionLogger = loggerFactory.CreateLogger();
_nextHeartbeat = new PeriodicTimer(_heartbeatTickRate);
- _disconnectTimeoutTicks = (long)(connectionOptions.Value.DisconnectTimeout ?? ConnectionOptionsSetup.DefaultDisconectTimeout).TotalMilliseconds;
+ _disconnectTimeout = connectionOptions.Value.DisconnectTimeout ?? ConnectionOptionsSetup.DefaultDisconectTimeout;
_metrics = metrics;
// Register these last as the callbacks could run immediately
@@ -141,7 +141,7 @@ private async Task ExecuteTimerLoop()
public void Scan()
{
var now = DateTimeOffset.UtcNow;
- var ticks = Environment.TickCount64;
+ var ticks = TimeSpan.FromMilliseconds(Environment.TickCount64);
// Scan the registered connections looking for ones that have timed out
foreach (var c in _connections)
@@ -152,7 +152,7 @@ public void Scan()
// Once the decision has been made to dispose we don't check the status again
// But don't clean up connections while the debugger is attached.
- if (!Debugger.IsAttached && lastSeenTick.HasValue && (ticks - lastSeenTick.Value) > _disconnectTimeoutTicks)
+ if (!Debugger.IsAttached && lastSeenTick.HasValue && (ticks - lastSeenTick.Value) > _disconnectTimeout)
{
Log.ConnectionTimedOut(_logger, connection.ConnectionId);
HttpConnectionsEventSource.Log.ConnectionTimedOut(connection.ConnectionId);
diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
index 0f72f214fae9..f55f5756ac45 100644
--- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
+++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
@@ -555,7 +555,7 @@ public async Task TransportEndingGracefullyWaitsOnApplicationLongPolling()
await task.DefaultTimeout();
// We've been gone longer than the expiration time
- connection.LastSeenTicks = Environment.TickCount64 - (long)disconnectTimeout.TotalMilliseconds - 1;
+ connection.LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64) - disconnectTimeout - TimeSpan.FromTicks(1);
// The application is still running here because the poll is only killed
// by the heartbeat so we pretend to do a scan and this should force the application task to complete
@@ -1277,6 +1277,7 @@ bool ExpectedErrors(WriteContext writeContext)
using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
{
+ var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
var manager = CreateConnectionManager(LoggerFactory);
var connection = manager.CreateConnection();
connection.TransportType = HttpTransportType.LongPolling;
@@ -1287,16 +1288,23 @@ bool ExpectedErrors(WriteContext writeContext)
var builder = new ConnectionBuilder(services.BuildServiceProvider());
builder.UseConnectionHandler();
var app = builder.Build();
- var options = new HttpConnectionDispatcherOptions();
// First poll completes immediately
+ var options = new HttpConnectionDispatcherOptions();
await dispatcher.ExecuteAsync(context, options, app).DefaultTimeout();
var sync = new SyncPoint();
context.Response.Body = new BlockingStream(sync);
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
await sync.WaitForSyncPoint().DefaultTimeout();
+
+ // Try cancel before cancellation should occur
+ connection.TryCancelSend(initialTime + options.TransportSendTimeout);
+ Assert.False(connection.SendingToken.IsCancellationRequested);
+
// Cancel write to response body
- connection.TryCancelSend(long.MaxValue);
+ connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
+ Assert.True(connection.SendingToken.IsCancellationRequested);
+
sync.Continue();
await dispatcherTask.DefaultTimeout();
// Connection should be removed on canceled write
@@ -1310,6 +1318,7 @@ public async Task SSEConnectionClosesWhenSendTimeoutReached()
{
using (StartVerifiableLog())
{
+ var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
var manager = CreateConnectionManager(LoggerFactory);
var connection = manager.CreateConnection();
var dispatcher = CreateDispatcher(manager, LoggerFactory);
@@ -1326,8 +1335,15 @@ public async Task SSEConnectionClosesWhenSendTimeoutReached()
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
await sync.WaitForSyncPoint().DefaultTimeout();
+
+ // Try cancel before cancellation should occur
+ connection.TryCancelSend(initialTime + options.TransportSendTimeout);
+ Assert.False(connection.SendingToken.IsCancellationRequested);
+
// Cancel write to response body
- connection.TryCancelSend(long.MaxValue);
+ connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
+ Assert.True(connection.SendingToken.IsCancellationRequested);
+
sync.Continue();
await dispatcherTask.DefaultTimeout();
// Connection should be removed on canceled write
@@ -1346,6 +1362,7 @@ bool ExpectedErrors(WriteContext writeContext)
}
using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
{
+ var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
var manager = CreateConnectionManager(LoggerFactory);
var connection = manager.CreateConnection();
var dispatcher = CreateDispatcher(manager, LoggerFactory);
@@ -1362,8 +1379,15 @@ bool ExpectedErrors(WriteContext writeContext)
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
await sync.WaitForSyncPoint().DefaultTimeout();
+
+ // Try cancel before cancellation should occur
+ connection.TryCancelSend(initialTime + options.TransportSendTimeout);
+ Assert.False(connection.SendingToken.IsCancellationRequested);
+
// Cancel write to response body
- connection.TryCancelSend(long.MaxValue);
+ connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
+ Assert.True(connection.SendingToken.IsCancellationRequested);
+
sync.Continue();
await dispatcherTask.DefaultTimeout();
// Connection should be removed on canceled write