@@ -40,9 +40,10 @@ public static async ValueTask<RemoteJSDataStream> CreateRemoteJSDataStreamAsync(
40
40
RemoteJSRuntime runtime ,
41
41
IJSStreamReference jsStreamReference ,
42
42
long totalLength ,
43
- long maxBufferSize ,
44
43
long maximumIncomingBytes ,
45
44
TimeSpan jsInteropDefaultCallTimeout ,
45
+ long pauseIncomingBytesThreshold = - 1 ,
46
+ long resumeIncomingBytesThreshold = - 1 ,
46
47
CancellationToken cancellationToken = default )
47
48
{
48
49
// Enforce minimum 1 kb, maximum 50 kb, SignalR message size.
@@ -54,7 +55,7 @@ public static async ValueTask<RemoteJSDataStream> CreateRemoteJSDataStreamAsync(
54
55
throw new ArgumentException ( $ "SignalR MaximumIncomingBytes must be at least 1 kb.") ;
55
56
56
57
var streamId = runtime . RemoteJSDataStreamNextInstanceId ++ ;
57
- var remoteJSDataStream = new RemoteJSDataStream ( runtime , streamId , totalLength , maxBufferSize , jsInteropDefaultCallTimeout , cancellationToken ) ;
58
+ var remoteJSDataStream = new RemoteJSDataStream ( runtime , streamId , totalLength , jsInteropDefaultCallTimeout , pauseIncomingBytesThreshold , resumeIncomingBytesThreshold , cancellationToken ) ;
58
59
await runtime . InvokeVoidAsync ( "Blazor._internal.sendJSDataStream" , jsStreamReference , streamId , chunkSize ) ;
59
60
return remoteJSDataStream ;
60
61
}
@@ -63,8 +64,9 @@ private RemoteJSDataStream(
63
64
RemoteJSRuntime runtime ,
64
65
long streamId ,
65
66
long totalLength ,
66
- long maxBufferSize ,
67
67
TimeSpan jsInteropDefaultCallTimeout ,
68
+ long pauseIncomingBytesThreshold ,
69
+ long resumeIncomingBytesThreshold ,
68
70
CancellationToken cancellationToken )
69
71
{
70
72
_runtime = runtime ;
@@ -78,10 +80,16 @@ private RemoteJSDataStream(
78
80
79
81
_runtime . RemoteJSDataStreamInstances . Add ( _streamId , this ) ;
80
82
81
- _pipe = new Pipe ( new PipeOptions ( pauseWriterThreshold : maxBufferSize , resumeWriterThreshold : maxBufferSize / 2 ) ) ;
83
+ _pipe = new Pipe ( new PipeOptions ( pauseWriterThreshold : pauseIncomingBytesThreshold , resumeWriterThreshold : resumeIncomingBytesThreshold ) ) ;
82
84
_pipeReaderStream = _pipe . Reader . AsStream ( ) ;
85
+ PipeReader = _pipe . Reader ;
83
86
}
84
87
88
+ /// <summary>
89
+ /// Gets a <see cref="PipeReader"/> to directly read data sent by the JavaScript client.
90
+ /// </summary>
91
+ public PipeReader PipeReader { get ; }
92
+
85
93
private async Task < bool > ReceiveData ( long chunkId , byte [ ] chunk , string error )
86
94
{
87
95
try
@@ -199,13 +207,23 @@ private async Task ThrowOnTimeout()
199
207
if ( ! _disposed && ( DateTimeOffset . UtcNow >= _lastDataReceivedTime . Add ( _jsInteropDefaultCallTimeout ) ) )
200
208
{
201
209
// Dispose of the stream if a chunk isn't received within the jsInteropDefaultCallTimeout.
202
- var timeoutException = new TimeoutException ( "Did not receive any data in the alloted time." ) ;
210
+ var timeoutException = new TimeoutException ( "Did not receive any data in the allotted time." ) ;
203
211
await CompletePipeAndDisposeStream ( timeoutException ) ;
204
212
_runtime . RaiseUnhandledException ( timeoutException ) ;
205
213
}
206
214
}
207
215
208
- internal async Task CompletePipeAndDisposeStream ( Exception ? ex = null )
216
+ /// <summary>
217
+ /// For testing purposes only.
218
+ ///
219
+ /// Triggers the timeout on the next check.
220
+ /// </summary>
221
+ internal void InvalidateLastDataReceivedTimeForTimeout ( )
222
+ {
223
+ _lastDataReceivedTime = _lastDataReceivedTime . Subtract ( _jsInteropDefaultCallTimeout ) ;
224
+ }
225
+
226
+ private async Task CompletePipeAndDisposeStream ( Exception ? ex = null )
209
227
{
210
228
await _pipe . Writer . CompleteAsync ( ex ) ;
211
229
Dispose ( true ) ;
0 commit comments