Skip to content

[browser][websocket] Hang with responses without ever signaling "end of message" #46983

@kjpou1

Description

@kjpou1

The javascript websocket implementation does not support partial message without end of message sent.

The following code that specifies that the end of message should be sent:

                    await socket.SendAsync(
                            new ArraySegment<byte>(receiveBuffer, 0, offset),
                            receiveResult.MessageType,
                            false,  // end of message
                            CancellationToken.None);

When executed it leaves the network request in a pending state and never fires the onMessage event. This can be viewed from the network status page of a browser.

The test code that runs the websocket echo server for tests can be viewed here: EchoWebSocket.ashx.cs

The lines have to do with the _replyWithPartialMessages.

Javascript test file attached named index.html.

This connects to the default address wss://corefx-net-http11.azurewebsites.net:443/WebSocket/EchoWebSocket.ashx and passes ?replyWithPartialMessages for partial messages.

The test that concerns this is SendReceive_PartialMessageBeforeCompleteMessageArrives_Success which describes the test as Ask the remote server to echo back received messages without ever signaling "end of message".

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="connect-src * 'unsafe-inline';">
    <style>
        table {
            border: 2px solid black;
        }
        input {
            width: 300px;
        }
        select {
            width: 300px;
        }
        textarea {
            width: 513px;
            border: 2px solid black;
        }
    </style>

</head>
    <body>
        <div id="main-body">
            <h1>WebSocket Client</h1>
            <!-- WebSocket Connection Parameters Table -->
            <table>
                <tr>
                    <td width="200px">WS Protocol</td>
                    <td>
                        <select id="protocol">
                            <option value="ws" >ws</option>
                            <option value="wss" selected="selected">wss</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <td>WS Hostname</td>
                    <td><input type="text" id="hostname" value="corefx-net-http11.azurewebsites.net"/></td>
                </tr>
                <tr>
                    <td>WS Port</td>
                    <td><input type="text" id="port" value="443"/></td>
                </tr>
                <tr>
                    <td>WS Endpoint</td>
                    <td><input type="text" id="endpoint" value="/WebSocket/EchoWebSocket.ashx"/></td>
                </tr>
            </table><br/>
            <!-- Send Message Table -->
            <table>
                <tr>
                    <td width="200px">Message</td>
                    <td><input type="text" id="message"/></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input id="btnSendBinPartialJS" type="button" value="Send Partial Message javascript" onclick="App.onSendBinPartialJSClick()">&nbsp;&nbsp;
                    </td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input id="btnonSendBinNoPartialJS" type="button" value="Send Message javascript" onclick="App.onSendBinNoPartialJSClick()">
                    </td>
                </tr>
            </table><br/>
            <textarea id="incomingMsgOutput" rows="10" cols="20" disabled="disabled"></textarea>
        </div>

        <script type="text/javascript">

             var App = {
                onSendBinPartialJSClick: function () {

                    var ws_protocol = document.getElementById("protocol").value;
                    var ws_hostname = document.getElementById("hostname").value;
                    var ws_port     = document.getElementById("port").value;
                    var ws_endpoint = document.getElementById("endpoint").value;

                    var webSocketURL = ws_protocol + "://" + ws_hostname + ":" + ws_port + ws_endpoint + "?replyWithPartialMessages";

                    LogIt("onSendBinPartialJS: " + webSocketURL);

                    DoSocketStuff(webSocketURL);
                },
                onSendBinNoPartialJSClick: function () {

                    var ws_protocol = document.getElementById("protocol").value;
                    var ws_hostname = document.getElementById("hostname").value;
                    var ws_port     = document.getElementById("port").value;
                    var ws_endpoint = document.getElementById("endpoint").value;

                    var webSocketURL = ws_protocol + "://" + ws_hostname + ":" + ws_port + ws_endpoint;

                    LogIt("onSendBinNoPartialJS: " + webSocketURL);
                    DoSocketStuff(webSocketURL);

                }
                //ws://corefx-net-http11.azurewebsites.net:80/WebSocket/EchoWebSocket.ashx?replyWithPartialMessages
                //wss://corefx-net-http11.azurewebsites.net:443/WebSocket/EchoWebSocket.ashx?replyWithPartialMessages

            };

            function DoSocketStuff (webSocketURL)
            {
                // Create WebSocket connection.
                const socket = new WebSocket(webSocketURL);
                socket.binaryType = "arraybuffer";
                // Connection opened
                socket.addEventListener('open', function (event) {
                    var bytearray = new Uint8Array( 65000 );
                    for ( var i = 0; i < 65000; ++i ) {
                        bytearray[i] = 255;
                    }
                    LogIt('Sending to server: ' + bytearray + " count: " + bytearray.buffer.byteLength);
                    socket.send(bytearray.buffer);
                });

                // Listen for messages
                socket.addEventListener('message', function (event) {
                    var bytearray = new Uint8Array( event.data );
                    LogIt('Message from server: ' + bytearray + " count: " + bytearray.buffer.byteLength);
                    socket.close(4000, 'Hic sunt Dracones');
                });

            }

            function LogIt (message)
            {
                document.getElementById("incomingMsgOutput").value += '\n---------\n' + message + '\n---------';
                console.log(message);
            }
            </script>
    </body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions