Skip to content

BridgeTo hangs when called multiple times #62

@mguerrieri

Description

@mguerrieri

I am able to reproduce this issue against the stable and latest codebases. Open an inbound socket and originate to a phone. Using Channel API, get channel for successfully originated call. Wait 10 seconds and call BridgeTo to bridge to a different phone number. Bridge is successful. Hangup bridged phone, and detect the hangup in Channel API. Wait 10 seconds and call BridgeTo to bridge to the phone number again. Bridge is successful. Wait 10 seconds and call BridgeTo a 3rd time. BridgeTo hangs and never returns, fs_cli shows no indication that it received the bridge command. Channel remains hung until you hangup the originally originated call, then BridgeTo returns and fs_cli shows the bridge command failing (obviously) because of the hangup. I have tried this countless times and it always hangs on the 3rd BridgeTo. I also tried setting the event-lock option on the bridge (as recommended by the FS docs for an outbound socket bridge, but it made no difference. Also, my original version of the code used only the inbound socket and no Channel API, but I had the same problem (which prompted me to try outbound sockets/Channel API).

This is a showstopper for me and I am at a loss, not being very fluent in the reactive extensions (I can only debug so far before I get in over my head). Any help is greatly appreciated.

Below is a console app to reproduce (.NET Core console app)- obviously substitute your own FS config and phone numbers.

Dial Plan:

<extension name="bridge_test">
  <condition field="destination_number" expression="^5557$">
      <action application="set" data="park_after_bridge=true" />
      <action application="pre_answer" />
      <action application="playback" data="{loops=2}tone_stream://%(300,25,440)" />
      <action application="answer" />
      <action application="socket" data="127.0.0.1:8084 async full" />
  </condition>
</extension>

project.json:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "NETStandard.Library": "1.6.0",
    "NEventSocket": "1.1.0",
    "Rx-Main": "2.2.5"
  },

  "frameworks": {
    "net452": {
    }
  }
}

Program.cs:

using NEventSocket;
using NEventSocket.Channels;
using NEventSocket.FreeSwitch;
using System;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FSBridgeTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                var cancellationTokenSource = new CancellationTokenSource();
                Console.WriteLine("Starting bridge test- hit any key abort...");
                var task = RunBridgeTest(cancellationTokenSource.Token);
                Console.ReadKey();
                Console.WriteLine("Shutting down...");
                cancellationTokenSource.Cancel();
                Console.WriteLine("Shutdown complete.");
                task.Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                Console.WriteLine("Hit any key exit...");
                Console.ReadKey();
            }
        }

        static async Task<bool> BridgeToPhone(Channel station, string phone)
        {
            var lineUUID = Guid.NewGuid().ToString("N");

            var bridgeOptions = new BridgeOptions
            {
                CallerIdNumber = "1003",
                CallerIdName = "Bridge Test",
                HangupAfterBridge = true,
                IgnoreEarlyMedia = false,
                TimeoutSeconds = 30,
                UUID = lineUUID
            };

            string endpoint = $"sofia/gateway/my-gateway/{phone}";

            Console.WriteLine($"Bridging {phone} to station channel {station.UUID}...");

            await station.BridgeTo(
                endpoint,
                bridgeOptions);

            if (!station.IsBridged)
            {
                Console.WriteLine($"Bridge failed.");
                return false;
            }
            else
            {
                Console.WriteLine($"Bridge successful.");
                return true;
            }
        }

        static Task RunBridgeTest(CancellationToken token)
        {
            var bridgeTestCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);

            return Task.Run(async () =>
            {
                try
                {
                    Console.WriteLine("Bridge test starting...");

                    using (var client = await InboundSocket.Connect())
                    {
                        using (var listener = new OutboundListener(8084))
                        {
                            listener.Channels.Subscribe(
                                async station =>
                                {
                                    try
                                    {
                                        Console.WriteLine($"Outbound socket connected for station channel {station?.UUID}.");

                                        station.HangupCallBack = (evt) =>
                                        {
                                            var hangupCause = evt.HangupCause?.ToString() ?? "Unknown";
                                            Console.WriteLine($"Station hangup detected for station channel {station?.UUID}- {hangupCause}.");
                                        };

                                        station.BridgedChannels.Subscribe(
                                            line =>
                                            {
                                                try
                                                {
                                                    Console.WriteLine($"Outbound socket connected for line channel {line?.UUID}.");

                                                    // Handle line hangup.
                                                    line.HangupCallBack = async (evt) =>
                                                        {
                                                            var hangupCause = evt.HangupCause?.ToString() ?? "Unknown";
                                                            Console.WriteLine($"Line hangup detected for line channel {line?.UUID}- {hangupCause}.");

                                                            await Task.Delay(10000);
                                                            await BridgeToPhone(station, "12155551212"); // Your cellphone here
                                                        };
                                                }
                                                catch (OperationCanceledException)
                                                {
                                                    Console.WriteLine($"Outbound socket disconnected for line channel {line?.UUID}.");
                                                }
                                            });

                                        await Task.Delay(10000);
                                        await BridgeToPhone(station, "12155551212"); // Your cellphone here.
                                    }
                                    catch (OperationCanceledException)
                                    {
                                        Console.WriteLine($"Outbound socket disconnected for station channel {station?.UUID}.");
                                    }
                                });

                            listener.Start();

                            var stationUUID = Guid.NewGuid().ToString("N");
                            var originateOptions = new OriginateOptions
                            {
                                CallerIdNumber = "1003",
                                CallerIdName = $"Station 1003",
                                HangupAfterBridge = false,
                                TimeoutSeconds = 20,
                                UUID = stationUUID
                            };

                            Console.WriteLine($"Originating to station channel {stationUUID}.");
                            var originate = await client.Originate(
                                "user/1003",
                                "5557",
                                "XML",
                                "default",
                                originateOptions);

                            if (!originate.Success)
                            {
                                var hangupCause = originate.HangupCause?.ToString() ?? "Unknown";
                                throw new Exception($"Originate failed: {hangupCause}");
                            }

                            Console.WriteLine($"Origination to station channel {stationUUID} complete.");

                            await Task.Delay(Timeout.Infinite, bridgeTestCancellationTokenSource.Token).ConfigureAwait(false);
                        }
                    }
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                Console.WriteLine("Bridge test complete.");
            });
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions