WebSocketListener icon indicating copy to clipboard operation
WebSocketListener copied to clipboard

Corrupted message from IE11, Edge when the content is larger than 1MB and sent multiple times

Open Peter-Optiway opened this issue 7 years ago • 11 comments

I'm using your WebSocketListener the following way:

class Program
{
    static void Main(string[] args)
    {
        CancellationTokenSource cancellation = new CancellationTokenSource();
            
        var task = Task.Run(() => RunServer(cancellation.Token));
        Console.WriteLine("Press any key to stop");
        Console.ReadKey(true);
        cancellation.Cancel();
        task.Wait();
        Console.ReadKey(true);
    }

    static async Task RunServer(CancellationToken cancellationToken)
    {
        try
        {
            var options = new WebSocketListenerOptions();
            options.Logger = new WebSocketLogger();
            options.Standards.RegisterRfc6455();
            var server = new WebSocketListener(new IPEndPoint(IPAddress.Any, 1234), options);
            await server.StartAsync();
            while (!cancellationToken.IsCancellationRequested)
            {
                var client = await server.AcceptWebSocketAsync(cancellationToken);
                Console.WriteLine("Client connected");
                while (client.IsConnected)
                {
                    var content = await client.ReadStringAsync(cancellationToken);
                    if (content == null)
                    {
                        //client disconnected
                        continue;
                    }
                    Console.WriteLine("Message received");
                    //validate content!
                    if (!Regex.IsMatch(content, @"^([0-9a-zA-Z+])*$"))
                    {
                        await client.WriteStringAsync("Content was invalid", cancellationToken);
                        Console.WriteLine("Content was invalid!");
                    }
                    else
                    {
                        await client.WriteStringAsync("Content was valid", cancellationToken);
                    }
                }
                Console.WriteLine("Client disconnected");
            }
            await server.StopAsync();
        }
        catch(Exception ex)
        {
            Console.WriteLine("Error occurred, " + ex.ToString());
        }
        finally
        {
            Console.WriteLine("Exited event loop");
        }
    }
}

class WebSocketLogger : vtortola.WebSockets.ILogger
{

    public bool IsDebugEnabled
    {
        get
        {
            return true;
        }
    }

    public bool IsWarningEnabled
    {
        get
        {
            return true;
        }
    }

    public bool IsErrorEnabled
    {
        get
        {
            return true;
        }
    }

    public void Debug(string message, Exception error = null)
    {
        Console.WriteLine(DateTime.Now.ToString("G") + " Debug " + message);
        if (error != null)
            Console.WriteLine(error.ToString());
    }

    public void Warning(string message, Exception error = null)
    {
        Console.WriteLine(DateTime.Now.ToString("G") + " Warn " + message);
        if (error != null)
            Console.WriteLine(error.ToString());
    }

    public void Error(string message, Exception error = null)
    {
        Console.WriteLine(DateTime.Now.ToString("G") + " Error " + message);
        if (error != null)
            Console.WriteLine(error.ToString());
    }
}

Then i have the following page

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Websocket tester!</title>
    <script src="assets/js/jquery.slim.min.js"></script>
    <script src="assets/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="assets/css/bootstrap.min.css" />
    <link rel="stylesheet" href="assets/css/bootstrap-theme.min.css" />
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-sm-12">
          <form id="openForm" class="form-horizontal">
            <div class="form-group">
              <label for="dataFile" class="col-sm-2 control-label">Data to spam:</label>
              <div class="col-sm-10">
                <input type="file" class="control-label" id="dataFile" />
              </div>
            </div>
              <div class="form-group">
                <label for="count" class="col-sm-2 control-label">count:</label>
                <div class="col-sm-10">
                  <input type="text" class="control-label" id="count" />
                </div>
              </div>
            <div>
              <div class="col-sm-offset-2 col-sm-10">
                <button id="send" type="submit" class="btn btn-default">Send</button>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
    <script src="assets/js/index.js"></script>
  </body>
</html>

and the following javascript file:

var openFormElement = $('#openForm');
var dataFileElement = $('#dataFile').get(0);
var countElement = $('#count');
var sendButton = $('#send');

var socket;

sendButton.click(function(e) {
  if (dataFileElement.files.length === 1) {
    var dataReader = new FileReader();
    dataReader.onload = function(dataEvent) {
      var data = dataEvent.target.result;
      //now we have the data to spam send!
      if (socket) {
        socket.close();
      }
      socket = new WebSocket("ws://localhost:1234/");
      socket.onopen = function(event) {
        console.log('connected');
        //Now we can spam!
        var count = parseInt(countElement.val());
        for (var i = 0; i < count; i++) {
          console.log('sending data');
          socket.send(data);
        }
      };
      socket.onmessage = function(event) {
        console.log('onmessage', event);
      };
      socket.onclose = function(event) {
        console.log('closed', event);
      };
      socket.onerror = function(event) {
        console.log('error', event);
      };
    };
    dataReader.readAsText(dataFileElement.files[0]);
  }
  e.stopPropagation();
  return false;
});

If i in IE or Edge: Select the Random1MB.txt and enter 10 in the count and press send it works. But if i use Random1.5MB.txt instead it crashes after 6 messages and the following is logged by the WebSocketListener. (Also the content is corrupted.)

2018-01-22 13:17:37 Debug (BAB87) An error occurred while async awaiting header.
vtortola.WebSockets.WebSocketException: Frame header is malformed.
   at vtortola.WebSockets.Rfc6455.WebSocketConnectionRfc6455.<ParseHeaderAsync>d__44.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at vtortola.WebSockets.Rfc6455.WebSocketConnectionRfc6455.<AwaitHeaderAsync>d__36.MoveNext()
2018-01-22 13:17:37 Debug (BAB87) [FRAME->] ConnectionClose, len: 2, key: 0, flags: FIN
Error occurred, vtortola.WebSockets.WebSocketException: Frame header is malformed.
   at vtortola.WebSockets.Rfc6455.WebSocketConnectionRfc6455.<ParseHeaderAsync>d__44.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at vtortola.WebSockets.Rfc6455.WebSocketConnectionRfc6455.<AwaitHeaderAsync>d__36.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at vtortola.WebSockets.Rfc6455.WebSocketConnectionRfc6455.<AwaitHeaderAsync>d__36.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at vtortola.WebSockets.Rfc6455.WebSocketRfc6455.<ReadMessageAsync>d__19.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at vtortola.WebSockets.WebSocketStringExtensions.<ReadStringAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at WebSocketServerTest.Program.<RunServer>d__1.MoveNext() in ...\WebSocketServerTest\Program.cs:line 43

Do you have any idea what is wrong, or what i could do about it? (I'm using IE11 and Edge, but it seems to work correctly in Chrome)

Example.zip

Peter-Optiway avatar Jan 22 '18 12:01 Peter-Optiway

It seems Edge is even more picky and crashes on everything above 0.25MB.

Peter-Optiway avatar Jan 23 '18 07:01 Peter-Optiway

Hi Peter! Thanks for report, I will check it.

deniszykov avatar Jan 23 '18 07:01 deniszykov

I tried the 4.1.5-alpha version and it has the same issue.

Peter-Optiway avatar Jan 23 '18 08:01 Peter-Optiway

It is ping related. As temporary workaround you could disable pings with options.PingMode = PingMode.Manual. I will investigate further.

deniszykov avatar Jan 24 '18 08:01 deniszykov

Your correct, are there any downsides to not having the ping?

Peter-Optiway avatar Jan 24 '18 09:01 Peter-Optiway

Without ping feature you wont notice lost client until TCP connection is terminated and error popped. TCP connection timeout varies from 30secs to 2mins,

deniszykov avatar Jan 24 '18 10:01 deniszykov

Could this happen if send and receive at the same time or is this a ping specific error?

Peter-Optiway avatar Jan 24 '18 11:01 Peter-Optiway

this a ping specific error

Somehow inbound data is being corrupted when ping is sent(or pong is received). Other operations are fine.

deniszykov avatar Jan 24 '18 11:01 deniszykov

Have you managed to find a solution for this issue?

Peter-Optiway avatar Sep 25 '18 06:09 Peter-Optiway

Actually my last investigations end up here - https://github.com/hoaproject/Websocket/issues/33#issuecomment-92274940. There is no remedy for IE11.

deniszykov avatar Sep 25 '18 10:09 deniszykov

But somehow default System.Net.WebSockets works fine with your example code. I will try to find implementation code for them.

deniszykov avatar Sep 25 '18 10:09 deniszykov