Skip to content
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
acab2e9
Just keep this the same for multiple dev environments on this branch
Feb 11, 2020
c3a13f7
Merge in the rest of the stuff that's been developed out of repo
drk9999 Feb 11, 2020
16bee6f
Add the plugininterface project and sln for building
drk9999 Feb 11, 2020
d713f90
had to move log and swig to the shared project so plugins can access …
drk9999 Feb 11, 2020
2b6c287
Fix sigs and namespaces
drk9999 Feb 11, 2020
0c16754
Just remembered I forgot this API arg
drk9999 Feb 11, 2020
6186cf5
Fix paths and change api/app name to dotnet
drk9999 Feb 12, 2020
e2b7093
update .gitignore
drk9999 Feb 12, 2020
79edeac
API connected through time to test to see that this method works
drk9999 Feb 12, 2020
7ac5977
Cut through DialPlanApp dispatcher. Again just bypass shane's test code
drk9999 Feb 12, 2020
6546120
Fix .gitignore again
drk9999 Feb 13, 2020
aa1dae8
add interface member for xml callback
drk9999 Feb 15, 2020
e02eb1a
Finish up dispatcher for XML callback at this point it shoud be usabl…
drk9999 Feb 15, 2020
2308892
Add helper methods. Shane: A little more swig magic needs to be done …
drk9999 Feb 16, 2020
be1266d
woops forgot load commands
drk9999 Feb 16, 2020
b6760b6
Add first test plugin
drk9999 Feb 16, 2020
32ed3db
tweak to log
drk9999 Feb 16, 2020
f553100
more logging
drk9999 Feb 17, 2020
de701e0
change entrypoint to public
drk9999 Feb 17, 2020
28a3cd4
some test code
drk9999 Feb 17, 2020
d23bf34
Try loading plugins from main, rather than API callback
drk9999 Feb 17, 2020
4380931
Attempt fix from M$'s info that when loaded from native host, there i…
drk9999 Feb 28, 2020
ef42892
Stop loading all plugins at module startup, and make sure it works to…
drk9999 Feb 28, 2020
c79e4eb
first simple test in pluging routing a call
drk9999 Feb 28, 2020
6583cea
test 2 handle APIs
drk9999 Feb 28, 2020
21ad902
add dial plan app to make sure it runs
drk9999 Feb 28, 2020
2545927
Move the answer from dialplan to dotnet dp app
drk9999 Feb 28, 2020
ff24548
Remove unused code, and get rid of extra logging now that what we hav…
drk9999 Mar 1, 2020
b27a050
Now that it has been working complete OnLoad and put itin the tests
drk9999 Mar 7, 2020
926300c
Fix for 3.1.3 paths
drk9999 Apr 1, 2020
e4b36f9
Lets finally try the test to use aspnet core
drk9999 May 11, 2020
bf0dd61
Update path for current .NET version
drk9999 Jul 4, 2020
b2ae2f0
May need help with this
drk9999 Jul 10, 2020
65ded2b
Getting there
drk9999 Jul 10, 2020
5af64f6
Getting there more
drk9999 Jul 10, 2020
cdd8af5
I think this will work
drk9999 Jul 10, 2020
b154db3
first attempt to make dtmf work
drk9999 Jul 12, 2020
58807b3
Merge branch 'v0.1beta' of github.com:freeswitch/mod_coreclr into v0.…
drk9999 Jul 12, 2020
d00dc95
Fix IVR.cs to work with new callbacks and add it to repo. Not Tested …
drk9999 Jul 13, 2020
9caefa1
add IVR test to example
drk9999 Jul 13, 2020
9206070
Update coreclr version
drk9999 Oct 20, 2020
89f0819
make sure stuff dont get GCed
drk9999 May 8, 2021
ca369d3
Keep GC from cleaning up the managed delegates
Astaelan May 8, 2021
51888af
This should fix the last GCed delegate issue
drk9999 May 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ libtool
mod_coreclr.la
*.lo
*.o
/Loader/.vs/Loader/v16/Server/sqlite3
/.vs/Managed/v16
/Loader/.vs/Loader/DesignTimeBuild/.dtbcache
*.dtbcache
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduce all this to just:
.vs

*.user
Binary file added .vs/Managed/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
228 changes: 77 additions & 151 deletions Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,173 +2,99 @@
using System.Collections.Concurrent;
using System.IO;
using System.Runtime.InteropServices;
using FreeSWITCH;
using PluginInterface;
using FreeSWITCH.Helpers;
using System.Reflection;

namespace FreeSWITCH
{
public static class Loader
{
// The primary entry point delegate for obtaining the native callbacks during host initialization
// The primary entry point delegate for obtaining the native callbacks during host initialization
private delegate NativeCallbacks LoadDelegate();

// The FreeSWITCH API interface callback delegate
private delegate int NativeAPICallback(string command, IntPtr sessionptr, IntPtr streamptr);
// The FreeSWITCH API interface callback delegate
private delegate int NativeAPICallback(string command, IntPtr sessionptr, IntPtr streamptr);

// The FreeSWITCH APP interface callback delegate
private delegate void NativeAPPCallback(IntPtr sessionptr, string data);
// The FreeSWITCH APP interface callback delegate
private delegate void NativeAPPCallback(IntPtr sessionptr, string data);

// The FreeSWITCH APP interface callback delegate
private delegate string NativeXMLCallback(string section, string tag, string key, string value, IntPtr eventptr);
// The FreeSWITCH APP interface callback delegate
private delegate string NativeXMLCallback(string section, string tag, string key, string value, IntPtr eventptr);

// Contains all native callbacks that will be called from the native host, each callback
// is produced by marshalling delegates to native function pointers
// Important: Must maintain the same structure in native_callbacks_t in mod_coreclr.c
// Contains all native callbacks that will be called from the native host, each callback
// is produced by marshalling delegates to native function pointers
// Important: Must maintain the same structure in native_callbacks_t in mod_coreclr.c
[StructLayout(LayoutKind.Sequential)]
private struct NativeCallbacks
public struct NativeCallbacks
{
public IntPtr NativeAPICallback;
public IntPtr NativeAPPCallback;
public IntPtr NativeXMLCallback;
public IntPtr NativeAPICallback;
public IntPtr NativeAPPCallback;
public IntPtr NativeXMLCallback;
}

// This is the only predefined entry point, this must match what mod_coreclr.c is looking for
private static NativeCallbacks Load()
// This is the only predefined entry point, this must match what mod_coreclr.c is looking for
public static NativeCallbacks Load()
{
// Register some reserved Managed API's
sAPIRegistry.TryAdd("load", LoadAPI);

// Return the marshalled callbacks for the native interfaces
return new NativeCallbacks {
NativeAPICallback = Marshal.GetFunctionPointerForDelegate<NativeAPICallback>(NativeAPIHandler),
NativeAPPCallback = Marshal.GetFunctionPointerForDelegate<NativeAPPCallback>(NativeAPPHandler),
NativeXMLCallback = Marshal.GetFunctionPointerForDelegate<NativeXMLCallback>(NativeXMLHandler)
};
// Register some reserved Managed API's
//sAPIRegistry.TryAdd("load", LoadAPI);
//var myLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//PluginsContainer.LoadPluginsFromSubDirs(myLocation);

// Return the marshalled callbacks for the native interfaces
return new NativeCallbacks
{
NativeAPICallback = Marshal.GetFunctionPointerForDelegate<NativeAPICallback>(NativeAPIHandler),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to:
NativeAPICallback = Marshal.GetFunctionPointerForDelegate(APICallback);
etc for the 3

NativeAPPCallback = Marshal.GetFunctionPointerForDelegate<NativeAPPCallback>(NativeAPPHandler),
NativeXMLCallback = Marshal.GetFunctionPointerForDelegate<NativeXMLCallback>(NativeXMLHandler)
};
}

// The Managed API interface callback delegate
public delegate string APICallback(string args, ManagedSession session);
// The Managed API callback registry
// TODO: value type subject to change to a more complex type including an APIAttribute configuration
private static ConcurrentDictionary<string, APICallback> sAPIRegistry = new ConcurrentDictionary<string, APICallback>();

// This is the FreeSWITCH API interface callback handler which is bound to "coreclr" API commands
private static int NativeAPIHandler(string command, IntPtr sessionptr, IntPtr streamptr)
{
using ManagedSession session = new ManagedSession(new SWIGTYPE_p_switch_core_session_t(sessionptr, false));
using Stream stream = new Stream(new SWIGTYPE_p_switch_stream_handle_t(streamptr, false));

string args = command;
if (!ParseArgument(ref args, out command, ' '))
{
Log.WriteLine(LogLevel.Error, "Missing Managed API");
stream.Write("-ERROR Missing Managed API");
return 0;
}
Log.WriteLine(LogLevel.Info, "Managed API: {0} {1}", command, args);

if (!sAPIRegistry.TryGetValue(command.ToLower(), out APICallback callback))
{
Log.WriteLine(LogLevel.Error, "Managed API does not exist");
stream.Write("-ERROR Managed API does not exist");
return 0;
}
string result = null;
try
{
result = callback(args, session);
}
catch (Exception)
{
// TODO: Log more of the exception data out
Log.WriteLine(LogLevel.Error, "Managed API exception");
result = "-ERROR Managed API exception";
}
if (result != null) stream.Write(result);
return 0;
}

// The Managed APP interface callback delegate
public delegate void APPCallback(string args, ManagedSession session);
// The Managed APP callback registry
// TODO: value type subject to change to a more complex type including an APPAttribute configuration
private static ConcurrentDictionary<string, APPCallback> sAPPRegistry = new ConcurrentDictionary<string, APPCallback>();

// This is the FreeSWITCH APP interface callback handler which is bound to "coreclr" APP commands
private static void NativeAPPHandler(IntPtr sessionptr, string data)
{
using ManagedSession session = new ManagedSession(new SWIGTYPE_p_switch_core_session_t(sessionptr, false));

string args = data;
if (!ParseArgument(ref args, out string command, ' '))
{
Log.WriteLine(LogLevel.Error, "Missing Managed APP");
return;
}
Log.WriteLine(LogLevel.Info, "Managed APP: {0} {1}", command, args);

if (!sAPPRegistry.TryGetValue(command.ToLower(), out APPCallback callback))
{
Log.WriteLine(LogLevel.Error, "Managed APP does not exist");
return;
}
try
{
callback(args, session);
}
catch (Exception)
{
// TODO: Log more of the exception data out
Log.WriteLine(LogLevel.Error, "Managed APP exception");
}
}

// The Managed XML interface callback delegate
public delegate void XMLCallback(string section, string tag, string key, string value, Event evt, ref string result);

public static event XMLCallback OnXMLSearch;

// This is the FreeSWITCH XML interface callback handler
private static string NativeXMLHandler(string section, string tag, string key, string value, IntPtr eventptr)
{
using Event evt = new Event(new SWIGTYPE_p_switch_event_t(eventptr, false), 0);
Log.WriteLine(LogLevel.Info, "Managed XML Handler: {0} - {1} - {2} - {3}", section, tag, key, value);
string result = null;
OnXMLSearch?.Invoke(section, tag, key, value, evt, ref result);
return result;
}

// TODO: Put this somewhere more reusable
private static bool ParseArgument(ref string args, out string arg, char separator)
{
args = args.Trim();
int eoa = args.IndexOf(separator);
arg = null;
if (eoa < 0)
{
string tmp = args;
args = string.Empty;
if (tmp.Length > 0) arg = tmp;
return arg != null;
}
arg = args.Substring(0, eoa);
args = args.Remove(0, eoa + 1).Trim();
return true;
}

// Managed API for loading user assemblies and reflecting on attributes to register callbacks
private static string LoadAPI(string args, ManagedSession session)
{
string path = Path.GetFullPath(args);
if (!Path.HasExtension(path)) path = Path.ChangeExtension(path, ".dll");
if (!File.Exists(path))
{
Log.WriteLine(LogLevel.Error, "File not found: {0}", path);
return "-ERROR File not found";
}

// TODO: Load the assembly, kick off reflection scan for relevant attributes, add API's to sAPIRegistry

return "+OK " + path;
}
// The Managed API interface callback delegate
public delegate string APICallback(string args, ManagedSession session);
// The Managed API callback registry
// TODO: value type subject to change to a more complex type including an APIAttribute configuration
private static ConcurrentDictionary<string, APICallback> sAPIRegistry = new ConcurrentDictionary<string, APICallback>();

// This is the FreeSWITCH API interface callback handler which is bound to "coreclr" API commands
private static int NativeAPIHandler(string command, IntPtr sessionptr, IntPtr streamptr)
{
using ManagedSession session = new ManagedSession(new SWIGTYPE_p_switch_core_session_t(sessionptr, false));
using Stream stream = new Stream(new SWIGTYPE_p_switch_stream_handle_t(streamptr, false));


// for now just call other dispatcher and return Todo: remove the rest of this code
return PluginsContainer.DispatchAPI(command, stream, session);

}

// The Managed APP interface callback delegate
public delegate void APPCallback(string args, ManagedSession session);
// The Managed APP callback registry
// TODO: value type subject to change to a more complex type including an APPAttribute configuration
private static ConcurrentDictionary<string, APPCallback> sAPPRegistry = new ConcurrentDictionary<string, APPCallback>();

// This is the FreeSWITCH APP interface callback handler which is bound to "coreclr" APP commands
private static void NativeAPPHandler(IntPtr sessionptr, string data)
{
using ManagedSession session = new ManagedSession(new SWIGTYPE_p_switch_core_session_t(sessionptr, false));

PluginsContainer.DispatchDialPlanApp(data, session);
return;

}

// The Managed XML interface callback delegate
public delegate void XMLCallback(string section, string tag, string key, string value, Event evt, ref string result);

// public static event XMLCallback OnXMLSearch;

// This is the FreeSWITCH XML interface callback handler
private static string NativeXMLHandler(string section, string tag, string key, string value, IntPtr eventptr)
{
using Event evt = new Event(new SWIGTYPE_p_switch_event_t(eventptr, false), 0);
//Log.WriteLine(LogLevel.Info, "Managed XML Handler: {0} - {1} - {2} - {3}", section, tag, key, value);
return PluginsContainer.DispatchXMLCallback(section, tag, key, value, evt);
}
}
}
10 changes: 10 additions & 0 deletions Loader/Loader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@
<AllOutputs Include="$(OutputPath)$(MSBuildProjectName).dll" />
<AllOutputs Include="$(OutputPath)$(MSBuildProjectName).runtimeconfig.json" />
<AllOutputs Include="$(OutputPath)$(MSBuildProjectName).deps.json" />
<AllOutputs Include="$(OutputPath)PluginInterface.dll" />
</ItemGroup>

<Copy SourceFiles="@(AllOutputs)" DestinationFolder="$(MSBuildProjectDirectory)/../LoaderRuntime" />
</Target>

<ItemGroup>
<ProjectReference Include="..\PluginInterface\PluginInterface.csproj" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ASP reference necessary or left over from testing?


</Project>
6 changes: 6 additions & 0 deletions Loader/Loader.csproj.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
</PropertyGroup>
</Project>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file, .user files should be local only.

39 changes: 39 additions & 0 deletions Loader/PluginLoadContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;

namespace FreeSWITCH
{
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;

public List<PluginInterface.IPluginDispatcher> Dispatchers { get; set; } = new List<PluginInterface.IPluginDispatcher>();

public PluginLoadContext(string pluginPath)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}

protected override Assembly Load(AssemblyName assemblyName)
{
var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);

return (assemblyPath != null ? LoadFromAssemblyPath(assemblyPath)
: AssemblyLoadContext.GetLoadContext(typeof(PluginLoadContext).Assembly).LoadFromAssemblyName(assemblyName));
}

protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}

return IntPtr.Zero;
}
}
}
Loading