Connecting Nexmo to a Websocket fails(remote party closed the WebSocket) in .Net Core

4

I'm trying to connect Nexmo to a web socket when doing an inbound call (the user calls the number bought with nexmo and link to the app).

As of now, I'm simply trying this Sample Code (which simply echoes back what the caller says) and to connect to this websocket via Nexmo following the "documentation" Here.

I successfully send an action "connect" to nexmo. While calling the number bought with Nexmo, it properly redirects to the endpoint (api/nexmo/socket), as shown when using breakpoints, but then it hangs up when reaching webSocket.ReceiveAsync in the Echo method.

    using System;
    using System.Net.WebSockets;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Newtonsoft.Json.Linq;

    namespace MyProject.Web.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class NexmoController : ControllerBase
        {
            private WebSocket _webSocket;

            [HttpGet("answer")]
            public IActionResult AnswerHandler()
            {
                const string host = "MY_NGROK_URL";
                const string locale = "fr-FR";

                var nccos = new JArray();
                var nccoConnect = new JObject()
                {
                    { "action", "connect" },
                    { "endpoint", new JArray(new JObject{
                            { "type", "websocket" },
                            { "uri", $"wss://{host}/api/nexmo/socket"},
                            { "content-type", "audio/l16;rate=16000"},
                            { "headers", new JObject {
                                    { "language", locale },
                                    { "callerID", "MY_NUMBER_HARDCODED_WHILE_TESTING" }
                                }
                            }
                        })
                    }
                };
                nccos.Add(nccoConnect);
                return Content(nccos.ToString(), "application/json");
            }

            [HttpPost("event")]
            public IActionResult EventHandler()
            {
                return Ok();
            }

            [HttpGet("socket")]
            public async Task GetAudio()
            {
                if (HttpContext.WebSockets.IsWebSocketRequest)
                {
                    _webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
                    await Echo(HttpContext, _webSocket);
                }
                else
                {
                    HttpContext.Response.StatusCode = 400;
                }
            }

            //Copy Paste from the Sample Code
            private async Task Echo(HttpContext context, WebSocket webSocket)
            {
                var buffer = new byte[1024 * 4];
                //Breakpoint : ReceiveAsync generates an exception
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                while (!result.CloseStatus.HasValue)
                {
                    await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                }
                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
            }
        }
    }

Here the exception that was caught :

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.

More of the exception:

at System.Net.WebSockets.ManagedWebSocket.ThrowIfEOFUnexpected(Boolean throwOnPrematureClosure) at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken, Boolean throwOnPrematureClosure) at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter) at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter) at ....Controllers.NexmoController.Echo(HttpContext context, WebSocket webSocket) in C:...\Controllers\NexmoController.cs:line 97 at ....Controllers.NexmoController.GetAudio() in C:...\Controllers\NexmoController.cs:line 68 at lambda_method(Closure , Object ) at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult() at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at System.Threading.Tasks.ValueTask`1.get_Result() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Any ideas what went down? Or how I could fix this? I don't really understand what's the issue here.

I also tried to check the WebSocketState of the Websocket and it is set on "Open". For info, I tested the sample code (websocket echoing the user's input) on its own and it works.

c#
asp.net-core
websocket
phone-call
nexmo
asked on Stack Overflow Feb 25, 2019 by MissRedfreak • edited Feb 25, 2019 by Ali Yousefi Sabzevar

1 Answer

1

We've found a solution :

  • ngrock doesn't work with websockets (not without a fee), so we published our app on azure.
  • We needed to instantiate the websocket in the StartUp and not in our controller (like the sample code listed in the original post). We've put the "Echo" method in another class (more so to keep a good structure) and set it has static.

Now everything works :)

Clarification : ngrock doesn't work with secured websockets (wss)

answered on Stack Overflow Feb 26, 2019 by MissRedfreak • edited Feb 26, 2019 by MissRedfreak

User contributions licensed under CC BY-SA 3.0