Search code examples
javascriptc#reactjsasp.net-corewebsocket

WebSocketException: 'The remote party closed the WebSocket connection without completing the close handshake.'


I'm working on a React application where I need to establish a WebSocket connection to a server which is in Asp .net core for file uploading purposes. The server requires authentication, and I'm attempting to pass an authentication token as a subprotocol using the useWebSocket hook from the react-use-websocket library.

import React, { useState } from 'react';
import useWebSocket from 'react-use-websocket';

function App() {
  const socketUrl = "ws://localhost:5204/api/v1/upload/file?projectId=102";
  const token = "my-token";

  const { sendMessage, readyState } = useWebSocket(socketUrl, {
    protocols: ['realProtocol', token],
  });

  // Remaining component code...
}

Here's the issue: When I attempt to establish the WebSocket connection with the authentication token passed as a subprotocol, the connection fails with the error message:

System.Net.WebSockets.WebSocketException: 'The remote party closed the WebSocket connection without completing the close handshake.'

However, if I remove the authentication token from the subprotocol list, the WebSocket connection is successfully established.

I've verified that the authentication token is valid and the WebSocket URL is correct. Why does the WebSocket connection fail when passing the authentication token as a subprotocol, and how can I resolve this issue?

Here is my backend code :

[Route("file")]
        [AllowAnonymous]
        public async Task<object> UploadListOfFiles(int projectId)
        {
            try
            {
                if (HttpContext.WebSockets.IsWebSocketRequest)
                {
                    // Get the value of the Sec-WebSocket-Protocol header
                    var protocolHeader = HttpContext.Request.Headers["Sec-WebSocket-Protocol"];
                    var token = protocolHeader.FirstOrDefault()?.Replace("realProtocol, ", "");
                    var authenticationResult = await _customJwtBearerHandler.HandleTokenAuthenticationAsync(token);

                    if (authenticationResult.Succeeded)
                    {
                        var email = _customJwtBearerHandler.ExtractEmailFromToken(token).ToLower();

                        using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();

                        var processedFiles = await _fileUploadClient.ReceiveFileChunks(HttpContext, webSocket);

                        List<FileUploadValidationResultModel> validationResults = await _validators.Validate(processedFiles, projectId);

                        List<ApiResponse> responses = await _fileUploadClient.ValidateAndProcessUpload(email, validationResults, projectId);

                        return responses;
                    }
                    else
                    {
                        return StatusCodes.Status401Unauthorized;
                    }
                }
                else
                {
                    return StatusCodes.Status400BadRequest;
                }

            }

Solution

  • You are telling it to expect no sub-protocols

    using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
    

    You need to add at least one subprotocol that they will have in common.

    using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync("realprotocol");
    

    Exactly why the client is sending the authentication token as one of the subprotocols is unclear. It's already sending it as a normal HTTP header, so this seems unnecessary.