Aspnetcore: Server side Blazor + Paste large amount of text into text area with binding = Disconnection of client

Created on 31 Oct 2018  路  22Comments  路  Source: dotnet/aspnetcore

I have a server side execution Blazor app that is displaying a text area with a simple 'bind="@backingProperty"'. If I paste moderate amounts of text into the text area everything functions as it should. However, if I paste a large amount of text (it happens to be a base64 representation of an image in some html) then the connection is closed with the following error appearing:

```
Error: Connection disconnected with error 'Error: Server returned an error on close: Connection closed with an error.'. blazor.server.js:16:6839
Ihttps://localhost:44379/_framework/blazor.server.js:16:6839
Phttps://localhost:44379/_framework/blazor.server.js:16:27531
a/https://localhost:44379/_framework/blazor.server.js:16:1652
a/< https://localhost:44379/_framework/blazor.server.js:16:970
s/< https://localhost:44379/_framework/blazor.server.js:16:697
s https://localhost:44379/_framework/blazor.server.js:16:474
Phttps://localhost:44379/_framework/blazor.server.js:16:27424
Phttps://localhost:44379/_framework/blazor.server.js:16:23753
Uhttps://localhost:44379/_framework/blazor.server.js:16:20576

Blazor 0.6.0
Firefox 63.0
Window 10 Pro x64
IIS Express hosted server side Blazor

Design affected-few area-blazor bug investigate severity-major

Most helpful comment

I agree with @Kaffeegangster that setting a buffer size merely hides an implementation issue. After all, you can't predict what an end user is going to do when using an app.

All 22 comments

Do you see any exceptions in the logs on the server? You can check the server logs by selecting ASP.NET Core Web Server in the output tab in VS.

You might be hitting the default buffer size limit in SignalR. See https://github.com/aspnet/Blazor/issues/1614#issuecomment-433983017 for additional details.

There is a message in the server logs referring to an exception BackpressureDeadlock:

2018-10-31 13:37:54.9909|6|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager|Scanning connections. |url: |action: 
2018-10-31 13:37:54.9909|8|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager|Scanned connections in 00:00:00.0008526. |url: |action: 
2018-10-31 13:37:55.6296|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6296|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|9|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Message received. Type: Binary, size: 2048, EndOfMessage: False. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6319|2|ERROR|Microsoft.AspNetCore.SignalR.HubConnectionHandler|Error when processing requests. System.InvalidOperationException: Advancing examined to the end would cause pipe to deadlock because FlushAsync is waiting.
   at System.IO.Pipelines.ThrowHelper.ThrowInvalidOperationException_BackpressureDeadlock()
   at System.IO.Pipelines.Pipe.AdvanceReader(BufferSegment consumedSegment, Int32 consumedIndex, BufferSegment examinedSegment, Int32 examinedIndex)
   at System.IO.Pipelines.Pipe.AdvanceReader(SequencePosition& consumed, SequencePosition& examined)
   at System.IO.Pipelines.Pipe.DefaultPipeReader.AdvanceTo(SequencePosition consumed, SequencePosition examined)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.DispatchMessagesAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)|url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|11|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Sending payload: 37 bytes. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|6|DEBUG|Microsoft.AspNetCore.SignalR.HubConnectionHandler|OnConnectedAsync ending. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|1|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext|Disposing connection TbfoNr4pa32HK3BjL98NKw. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|4|DEBUG|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Waiting for the application to finish sending data. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|6|DEBUG|Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets|Connection id "0HLHV9AQTL4T7" received FIN. |url: |action: 
2018-10-31 13:37:55.6523|2|DEBUG|Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport|Socket closed. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|4|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext|Waiting for WebSockets transport to complete. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|10|DEBUG|Microsoft.AspNetCore.Server.Kestrel|Connection id "0HLHV9AQTL4T7" disconnecting. |url: |action: 
2018-10-31 13:37:55.6523|5|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext|WebSockets transport complete. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|7|DEBUG|Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets|Connection id "0HLHV9AQTL4T7" sending FIN. |url: |action: 
2018-10-31 13:37:55.6523|2|DEBUG|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager|Removing connection TbfoNr4pa32HK3BjL98NKw from the list of connections. |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|2|INFO|Microsoft.AspNetCore.Hosting.Internal.WebHost|Request finished in 106607.7491ms 101  |url: https://localhost/_blazor|action: 
2018-10-31 13:37:55.6523|2|DEBUG|Microsoft.AspNetCore.Server.Kestrel|Connection id "0HLHV9AQTL4T7" stopped. |url: |action: 
2018-10-31 13:37:55.9910|6|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager|Scanning connections. |url: |action: 
2018-10-31 13:37:55.9910|8|TRACE|Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager|Scanned connections in 00:00:00.0009206. |url: |action: 

Following the referenced issue with SignalR buffer size takes care of the issue, but shouldn't the implementation of the client 'chunk' a large buffer so as not to cause an overflow?

```
app.UseSignalR(route => route.MapHub(BlazorHub.DefaultPath, o =>
{
o.ApplicationMaxBufferSize = 131072; // larger size
o.TransportMaxBufferSize = 131072; // larger size
}));
app.UseBlazor();

but shouldn't the implementation of the client 'chunk' a large buffer so as not to cause an overflow?

@anurse Thoughts on this?

We don't automatically chunk. The streaming functionality makes it possible for users to chunk the data themselves, but we have no plans to automatically chunk data. SignalR isn't really well suited to sending large payloads.

@SteveSandersonMS @rynowak We should discuss if we want to do anything here beyond telling folks to increase the SignalR buffer sizes.

i would appreciate if JSInterop would implement chunking on top of SignalR. Setting buffer size delays the problem but not solves it. The case where i needed to pass a string >32k i split it into substrings and called the interop function several times with the substrings and offsets. This works but it's not nice.

I agree with @Kaffeegangster that setting a buffer size merely hides an implementation issue. After all, you can't predict what an end user is going to do when using an app.

@danroth27 @rynowak I agree with @MarkStega that increasing the buffer size isn't really a proper solution. There's no scenario I can think of where you'd want there to be an arbitrary size limit on the diff. So, either Razor Components has to implement chunking on top of SignalR, or SignalR itself needs to.

I don't mind desperately either way, but since supporting that would benefit all SignalR apps, I think we should at least discuss a bit more with the SignalR folks if it would be viable as a future SignalR feature, otherwise the Razor Components implementation would be a standalone thing that doesn't benefit anyone else. CC @anurse @davidfowl.

We've long avoided automatic chunking (in both ASP.NET and ASP.NET Core SignalR), I don't see it being a feature we intend to support. Besides, it's only possible for us to fix this at the Hub layer, and if you use ConnectionHandler you wouldn't benefit from any work we do there at all.

@anurse Would it justify having some thin layer on top of ConnectionHandler that provides delivery of arbitrary-sized messages?

Perhaps, but I don't think that would be a public API component. I could see building a shared-source component for this. I don't think we'd use it in SignalR as we haven't really seen demand for it at the Hub layer (since Hubs aren't really designed for large data anyway and have their own streaming primitives).

In 3.0 we currently plan to add the ability to "stream" data as a parameter (much like you can stream results) which might also help here, but again, only for the Hub layer.

I don鈥檛 quite understand the chunking request, are you asking for streaming? The limit is about total buffered memory so this needs to be implemented in the app layer.

@davidfowl I haven't reproduced the issue so I'm not certain what the ideal resolution would be.

I think what we need for sure is the ability for the server to dispatch an arbitrarily large message to the client, since the render tree diff could be arbitrarily large. Streaming may not be relevant, since the server already has the data in a buffer in memory.

On the other hand, we don't necessarily need to let the client send an arbitrarily large message to the server. I'm aware that if we do, then there are DOS concerns since the server would need to allocate a correspondingly-sized buffer (and there's no equivalent DOS scenario in the server->client direction).

Perhaps there isn't any problem to solve here, other than possibly implementing JS interop support for streams at some point in the future. The only short-term problem is if the server->client direction has arbitrary size limits despite the data already being in some byte[] in server memory.

For my use case i need to send a bigger (a few 100 kb) string (xml) from the server->client and later back from client->server to save it in a database.
Just in case JS Interop supports a SignalR ChannelReader in the future a pipe with ChannelWriter back to the server could be usefull.
The SignalR AdvanceReader Exception is easy to reproduce if you call the public static Task Prompt(string message) from the blazorlib template with a message size >32k

@Kaffeegangster I agree that support for streaming would be valuable. I think that's the right solution for sending arbitrarily large amounts of data from client to server. We wouldn't implement a non-streaming way to send arbitrarily large blocks of data, because that would be a DOS risk to the server.

I am also seeing this error if I put 31085 chars in a textarea with a bind property in blazor server-side. If i put 31084 chars it works, anything higher i see the same error.

31085 chars is not much, if you write a blog article in a textarea it will exceed that probably.

Any solution or workaround for that?

@agonzalezm @danroth27

The reference in Dan's post right after the first post "( See #1614 fo additional details.)" for a short term work-around for this implementation issue.

How to do it using .netcore 3.0 and blazor ?

Is there any workaround for this using .net core 3.0 ?
I tried something like below, but it is not working:

app.UseEndpoints(endpoints => {
endpoints.MapBlazorHub(o =>
{
o.ApplicationMaxBufferSize = 92233720; // larger size
o.TransportMaxBufferSize = 92233720; // larger size
});

                       endpoints.MapFallbackToPage("/_Host");
    });

had the same issue
adding this to the startup fixed it for me

using Microsoft.AspNetCore.SignalR;
..........
///increase size of textarea accepted value value
services.Configure(options =>
{
options.MaximumReceiveMessageSize = null;
});

I am also seeing this error if I put 31085 chars in a textarea with a bind property in blazor server-side. If i put 31084 chars it works, anything higher i see the same error.

31085 chars is not much, if you write a blog article in a textarea it will exceed that probably.

Any solution or workaround for that?

Max size I can paste is 16290 chars.

This fixed my problem:

c# services .AddServerSideBlazor() .AddHubOptions(x => x.MaximumReceiveMessageSize = 102400000);

Can confirm the comment above worked for me as well. This seems like a pretty big issue.

I have a razor page with a decently long table (79 rows), and for each row I added a razor component, this was enough to make the request 33KB and I got this error, took me an hour to figure out what is happening (initially thought my component was broken).

Was this page helpful?
0 / 5 - 0 ratings