Add event support to Epoxy
- Add codegen for events - Add ServiceHost logic - Add relevant tests - Add event example
This commit is contained in:
Родитель
0a61dd43b2
Коммит
87d1f2480f
|
@ -52,7 +52,11 @@ namespace #{csNamespace}
|
|||
where
|
||||
getMessageResultTypeName = getMessageTypeName cs methodResult
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
methodDeclaration _ = mempty
|
||||
|
||||
methodDeclaration Event{..} = [lt|void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|]
|
||||
where
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
|
||||
comm _ = mempty
|
||||
|
||||
comm_proxy_cs :: MappingContext -> String -> [Import] -> [Declaration] -> (String, L.Text)
|
||||
|
@ -81,7 +85,7 @@ namespace #{csNamespace}
|
|||
}|]
|
||||
where
|
||||
methodCapability Function {} = "global::Bond.Comm.IRequestResponseConnection"
|
||||
methodCapability _ = ""
|
||||
methodCapability Event {} = "global::Bond.Comm.IEventConnection"
|
||||
|
||||
getCapabilities :: [Method] -> [String]
|
||||
getCapabilities m = nub $ map methodCapability m
|
||||
|
@ -106,7 +110,21 @@ namespace #{csNamespace}
|
|||
where
|
||||
getMessageResultTypeName = getMessageTypeName cs methodResult
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
proxyMethod _ = mempty
|
||||
|
||||
proxyMethod Event{..} = [lt|public void #{methodName}Async(#{getMessageInputTypeName} param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<#{getMessageInputTypeName}>(param);
|
||||
#{methodName}Async(message);
|
||||
}
|
||||
|
||||
public void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param)
|
||||
{
|
||||
m_connection.FireEventAsync<#{getMessageInputTypeName}>(
|
||||
"#{getDeclTypeName idl s}.#{methodName}",
|
||||
param);
|
||||
}|]
|
||||
where
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
|
||||
comm _ = mempty
|
||||
|
||||
|
@ -141,14 +159,17 @@ namespace #{csNamespace}
|
|||
where
|
||||
generics = angles $ sepBy ", " paramName declParams
|
||||
|
||||
methodInfo Function{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue};|]
|
||||
methodInfo _ = mempty
|
||||
methodInfo Function{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};|]
|
||||
methodInfo Event{..} = [lt|yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="#{getDeclTypeName idl s}.#{methodName}", Callback = #{methodName}Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};|]
|
||||
|
||||
methodAbstract Function{..} = [lt|public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<#{getMessageResultTypeName}>> #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param, global::System.Threading.CancellationToken ct);|]
|
||||
where
|
||||
getMessageResultTypeName = getMessageTypeName cs methodResult
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
methodAbstract _ = mempty
|
||||
|
||||
methodAbstract Event{..} = [lt|public abstract void #{methodName}Async(global::Bond.Comm.IMessage<#{getMessageInputTypeName}> param);|]
|
||||
where
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
|
||||
methodGlue Function{..} = [lt|private global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage> #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
|
@ -159,5 +180,13 @@ namespace #{csNamespace}
|
|||
where
|
||||
getMessageResultTypeName = getMessageTypeName cs methodResult
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
methodGlue _ = mempty
|
||||
|
||||
methodGlue Event{..} = [lt|private global::System.Threading.Tasks.Task #{methodName}Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
#{methodName}Async(param.Convert<#{getMessageInputTypeName}>());
|
||||
return global::Bond.Comm.CodegenHelpers.CompletedTask;
|
||||
}|]
|
||||
where
|
||||
getMessageInputTypeName = getMessageTypeName cs methodInput
|
||||
|
||||
comm _ = mempty
|
||||
|
|
|
@ -22,9 +22,9 @@ namespace tests
|
|||
{
|
||||
get
|
||||
{
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo31", Callback = foo31Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo32", Callback = foo32Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo33", Callback = foo33Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo31", Callback = foo31Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo32", Callback = foo32Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo33", Callback = foo33Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,14 @@ namespace tests
|
|||
[System.CodeDom.Compiler.GeneratedCode("gbc", "0.4.0.2")]
|
||||
interface IFoo
|
||||
{
|
||||
void foo11Async(global::Bond.Comm.IMessage<global::Bond.Void> param);
|
||||
|
||||
void foo12Async(global::Bond.Comm.IMessage<global::Bond.Void> param);
|
||||
|
||||
void foo13Async(global::Bond.Comm.IMessage<BasicTypes> param);
|
||||
|
||||
void foo14Async(global::Bond.Comm.IMessage<dummy> param);
|
||||
|
||||
global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<global::Bond.Void>> foo21Async(global::Bond.Comm.IMessage<global::Bond.Void> param, global::System.Threading.CancellationToken ct);
|
||||
|
||||
global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<global::Bond.Void>> foo22Async(global::Bond.Comm.IMessage<global::Bond.Void> param, global::System.Threading.CancellationToken ct);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
namespace tests
|
||||
{
|
||||
[System.CodeDom.Compiler.GeneratedCode("gbc", "0.4.0.2")]
|
||||
public class FooProxy<TConnection> : IFoo where TConnection : global::Bond.Comm.IRequestResponseConnection
|
||||
public class FooProxy<TConnection> : IFoo where TConnection : global::Bond.Comm.IEventConnection, global::Bond.Comm.IRequestResponseConnection
|
||||
{
|
||||
private readonly TConnection m_connection;
|
||||
|
||||
|
@ -25,6 +25,58 @@ namespace tests
|
|||
m_connection = connection;
|
||||
}
|
||||
|
||||
public void foo11Async(global::Bond.Void param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<global::Bond.Void>(param);
|
||||
foo11Async(message);
|
||||
}
|
||||
|
||||
public void foo11Async(global::Bond.Comm.IMessage<global::Bond.Void> param)
|
||||
{
|
||||
m_connection.FireEventAsync<global::Bond.Void>(
|
||||
"tests.Foo.foo11",
|
||||
param);
|
||||
}
|
||||
|
||||
public void foo12Async(global::Bond.Void param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<global::Bond.Void>(param);
|
||||
foo12Async(message);
|
||||
}
|
||||
|
||||
public void foo12Async(global::Bond.Comm.IMessage<global::Bond.Void> param)
|
||||
{
|
||||
m_connection.FireEventAsync<global::Bond.Void>(
|
||||
"tests.Foo.foo12",
|
||||
param);
|
||||
}
|
||||
|
||||
public void foo13Async(BasicTypes param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<BasicTypes>(param);
|
||||
foo13Async(message);
|
||||
}
|
||||
|
||||
public void foo13Async(global::Bond.Comm.IMessage<BasicTypes> param)
|
||||
{
|
||||
m_connection.FireEventAsync<BasicTypes>(
|
||||
"tests.Foo.foo13",
|
||||
param);
|
||||
}
|
||||
|
||||
public void foo14Async(dummy param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<dummy>(param);
|
||||
foo14Async(message);
|
||||
}
|
||||
|
||||
public void foo14Async(global::Bond.Comm.IMessage<dummy> param)
|
||||
{
|
||||
m_connection.FireEventAsync<dummy>(
|
||||
"tests.Foo.foo14",
|
||||
param);
|
||||
}
|
||||
|
||||
public global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<global::Bond.Void>> foo21Async(global::Bond.Void param)
|
||||
{
|
||||
var message = new global::Bond.Comm.Message<global::Bond.Void>(param);
|
||||
|
|
|
@ -22,21 +22,33 @@ namespace tests
|
|||
{
|
||||
get
|
||||
{
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo21", Callback = foo21Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo22", Callback = foo22Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo23", Callback = foo23Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo24", Callback = foo24Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo31", Callback = foo31Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo32", Callback = foo32Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo33", Callback = foo33Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo34", Callback = foo34Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo41", Callback = foo41Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo42", Callback = foo42Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo43", Callback = foo43Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo44", Callback = foo44Async_Glue};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo11", Callback = foo11Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo12", Callback = foo12Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo13", Callback = foo13Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo14", Callback = foo14Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.Event};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo21", Callback = foo21Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo22", Callback = foo22Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo23", Callback = foo23Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo24", Callback = foo24Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo31", Callback = foo31Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo32", Callback = foo32Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo33", Callback = foo33Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo34", Callback = foo34Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo41", Callback = foo41Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo42", Callback = foo42Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo43", Callback = foo43Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
yield return new global::Bond.Comm.ServiceMethodInfo {MethodName="tests.Foo.foo44", Callback = foo44Async_Glue, CallbackType = global::Bond.Comm.ServiceCallbackType.RequestResponse};
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void foo11Async(global::Bond.Comm.IMessage<global::Bond.Void> param);
|
||||
|
||||
public abstract void foo12Async(global::Bond.Comm.IMessage<global::Bond.Void> param);
|
||||
|
||||
public abstract void foo13Async(global::Bond.Comm.IMessage<BasicTypes> param);
|
||||
|
||||
public abstract void foo14Async(global::Bond.Comm.IMessage<dummy> param);
|
||||
|
||||
public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<global::Bond.Void>> foo21Async(global::Bond.Comm.IMessage<global::Bond.Void> param, global::System.Threading.CancellationToken ct);
|
||||
|
||||
public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<global::Bond.Void>> foo22Async(global::Bond.Comm.IMessage<global::Bond.Void> param, global::System.Threading.CancellationToken ct);
|
||||
|
@ -61,6 +73,30 @@ namespace tests
|
|||
|
||||
public abstract global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage<dummy>> foo44Async(global::Bond.Comm.IMessage<dummy> param, global::System.Threading.CancellationToken ct);
|
||||
|
||||
private global::System.Threading.Tasks.Task foo11Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
foo11Async(param.Convert<global::Bond.Void>());
|
||||
return global::Bond.Comm.CodegenHelpers.CompletedTask;
|
||||
}
|
||||
|
||||
private global::System.Threading.Tasks.Task foo12Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
foo12Async(param.Convert<global::Bond.Void>());
|
||||
return global::Bond.Comm.CodegenHelpers.CompletedTask;
|
||||
}
|
||||
|
||||
private global::System.Threading.Tasks.Task foo13Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
foo13Async(param.Convert<BasicTypes>());
|
||||
return global::Bond.Comm.CodegenHelpers.CompletedTask;
|
||||
}
|
||||
|
||||
private global::System.Threading.Tasks.Task foo14Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
foo14Async(param.Convert<dummy>());
|
||||
return global::Bond.Comm.CodegenHelpers.CompletedTask;
|
||||
}
|
||||
|
||||
private global::System.Threading.Tasks.Task<global::Bond.Comm.IMessage> foo21Async_Glue(global::Bond.Comm.IMessage param, global::Bond.Comm.ReceiveContext context, global::System.Threading.CancellationToken ct)
|
||||
{
|
||||
return global::Bond.Comm.CodegenHelpers.Upcast<global::Bond.Comm.IMessage<global::Bond.Void>,
|
||||
|
|
21
cs/cs.sln
21
cs/cs.sln
|
@ -202,6 +202,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "logging", "..\examples\cs\c
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "service", "src\comm\service\service.csproj", "{79D2423A-87C8-44A2-89C2-2FA94521747E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notifyevent", "..\examples\cs\comm\notifyevent\notifyevent.csproj", "{12279366-F646-4FEC-8CAA-B62A8EC477BB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -779,6 +781,24 @@ Global
|
|||
{79D2423A-87C8-44A2-89C2-2FA94521747E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{79D2423A-87C8-44A2-89C2-2FA94521747E}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{79D2423A-87C8-44A2-89C2-2FA94521747E}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Win32.ActiveCfg = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Fields|Win32.Build.0 = Debug|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB}.Release|Win32.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -816,5 +836,6 @@ Global
|
|||
{54A3432B-99E1-4DEB-B4EB-2D6E158ECD24} = {ED161076-6BB8-4A13-83ED-0E9C01461D5E}
|
||||
{5C8132A8-C4B1-45E0-BCA6-379DA23B86D3} = {621A2166-EEE0-4A27-88AA-5BE5AC996452}
|
||||
{79D2423A-87C8-44A2-89C2-2FA94521747E} = {ED161076-6BB8-4A13-83ED-0E9C01461D5E}
|
||||
{12279366-F646-4FEC-8CAA-B62A8EC477BB} = {621A2166-EEE0-4A27-88AA-5BE5AC996452}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -48,5 +48,18 @@ namespace Bond.Comm
|
|||
|
||||
return tcsOuter.Task;
|
||||
}
|
||||
|
||||
private static Task s_completedTask = Task.FromResult(default(object));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a completed Task
|
||||
/// </summary>
|
||||
public static Task CompletedTask
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_completedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ enum ErrorCode
|
|||
OK = 0x0;
|
||||
InternalServerError = 0xA0BD0002;
|
||||
MethodNotFound = 0xA0BD0003;
|
||||
InvalidInvocation = 0xA0BD0004;
|
||||
}
|
||||
|
||||
struct Error
|
||||
|
|
|
@ -48,16 +48,13 @@ namespace Bond.Comm
|
|||
/// invoke.
|
||||
/// </param>
|
||||
/// <param name="message">The message to send.</param>
|
||||
/// <param name="ct">
|
||||
/// The cancellation token for cooperative cancellation.
|
||||
/// </param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
/// <remarks>
|
||||
/// Event methods cannot send responses or error. However, the returned
|
||||
/// task may represent an error if there was a local error sending the
|
||||
/// message.
|
||||
/// </remarks>
|
||||
Task FireEventAsync<TPayload>(string methodName, IMessage<TPayload> message, CancellationToken ct);
|
||||
Task FireEventAsync<TPayload>(string methodName, IMessage<TPayload> message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -22,7 +22,23 @@ namespace Bond.Comm
|
|||
/// This class is primarily used by Bond's generated service
|
||||
/// implementations.
|
||||
/// </remarks>
|
||||
public delegate Task<IMessage> ServiceCallback(IMessage request, ReceiveContext context, CancellationToken ct);
|
||||
public delegate Task ServiceCallback(IMessage request, ReceiveContext context, CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the type of ServiceCallback
|
||||
/// </summary>
|
||||
public enum ServiceCallbackType
|
||||
{
|
||||
/// <summary>
|
||||
/// Method returns a Task<IMessage>
|
||||
/// </summary>
|
||||
RequestResponse,
|
||||
|
||||
/// <summary>
|
||||
/// Method returns a Task
|
||||
/// </summary>
|
||||
Event
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Data about a Bond service method.
|
||||
|
@ -38,6 +54,7 @@ namespace Bond.Comm
|
|||
/// The delegate to invoke for this method.
|
||||
/// </summary>
|
||||
public ServiceCallback Callback;
|
||||
public ServiceCallbackType CallbackType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Bond.Comm.Service
|
|||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bond.Comm;
|
||||
|
@ -13,14 +14,14 @@ namespace Bond.Comm.Service
|
|||
{
|
||||
private readonly Transport m_parentTransport;
|
||||
private readonly object m_lock;
|
||||
private readonly Dictionary<string, ServiceCallback> m_dispatchTable;
|
||||
private readonly Dictionary<string, ServiceMethodInfo> m_dispatchTable;
|
||||
|
||||
|
||||
public ServiceHost(Transport parentTransport)
|
||||
{
|
||||
m_parentTransport = parentTransport;
|
||||
m_lock = new object();
|
||||
m_dispatchTable = new Dictionary<string, ServiceCallback>();
|
||||
m_dispatchTable = new Dictionary<string, ServiceMethodInfo>();
|
||||
}
|
||||
|
||||
public bool IsRegistered(string serviceMethodName)
|
||||
|
@ -31,9 +32,43 @@ namespace Bond.Comm.Service
|
|||
}
|
||||
}
|
||||
|
||||
public void ValidateServiceMethods(IEnumerable<ServiceMethodInfo> serviceMethods)
|
||||
{
|
||||
Type returnParameter;
|
||||
|
||||
foreach (var serviceMethod in serviceMethods)
|
||||
{
|
||||
switch (serviceMethod.CallbackType)
|
||||
{
|
||||
case ServiceCallbackType.RequestResponse:
|
||||
returnParameter = serviceMethod.Callback.GetMethodInfo().ReturnType;
|
||||
if (returnParameter != typeof(Task<IMessage>))
|
||||
{
|
||||
throw new ArgumentException($"{serviceMethod.MethodName} registered as " +
|
||||
$"{serviceMethod.CallbackType} but callback not implemented as such.");
|
||||
}
|
||||
break;
|
||||
case ServiceCallbackType.Event:
|
||||
returnParameter = serviceMethod.Callback.GetMethodInfo().ReturnType;
|
||||
if (returnParameter != typeof(Task))
|
||||
{
|
||||
throw new ArgumentException($"{serviceMethod.MethodName} registered as " +
|
||||
$"{serviceMethod.CallbackType} but callback not implemented as such.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"{serviceMethod.MethodName} registered as invalid type " +
|
||||
$"{serviceMethod.CallbackType}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Register(IService service)
|
||||
{
|
||||
var methodNames = new SortedSet<string>();
|
||||
|
||||
ValidateServiceMethods(service.Methods);
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
// Service methods are registerd as a unit - either register all or none.
|
||||
|
@ -52,7 +87,7 @@ namespace Bond.Comm.Service
|
|||
|
||||
foreach (var serviceMethod in service.Methods)
|
||||
{
|
||||
m_dispatchTable.Add(serviceMethod.MethodName, serviceMethod.Callback);
|
||||
m_dispatchTable.Add(serviceMethod.MethodName, serviceMethod);
|
||||
methodNames.Add(serviceMethod.MethodName);
|
||||
}
|
||||
}
|
||||
|
@ -78,54 +113,116 @@ namespace Bond.Comm.Service
|
|||
{
|
||||
Log.Information("{0}.{1}: Got request {2} from {3}.",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName, context.Connection);
|
||||
ServiceCallback callback;
|
||||
IMessage result = null;
|
||||
ServiceMethodInfo methodInfo;
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (!m_dispatchTable.TryGetValue(methodName, out callback))
|
||||
if (!m_dispatchTable.TryGetValue(methodName, out methodInfo))
|
||||
{
|
||||
var errorMessage = LogUtil.FatalAndReturnFormatted("{0}.{1}: Got request for unknown method {2}.",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName);
|
||||
|
||||
var error = new Error()
|
||||
var error = new Error
|
||||
{
|
||||
message = errorMessage,
|
||||
error_code = (int)ErrorCode.MethodNotFound
|
||||
};
|
||||
result = Message.FromError(error);
|
||||
return Message.FromError(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
if (methodInfo.CallbackType != ServiceCallbackType.RequestResponse)
|
||||
{
|
||||
var errorMessage = LogUtil.FatalAndReturnFormatted("{0}.{1}: Method {2} invoked as if it were {3}, but it was registered as {4}.",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName, ServiceCallbackType.RequestResponse, methodInfo.CallbackType);
|
||||
|
||||
var error = new Error
|
||||
{
|
||||
message = errorMessage,
|
||||
error_code = (int)ErrorCode.InvalidInvocation
|
||||
};
|
||||
return Message.FromError(error);
|
||||
}
|
||||
|
||||
IMessage result = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cast to appropriate return type which we validated when registering the service
|
||||
result = await (Task<IMessage>)methodInfo.Callback(message, context, CancellationToken.None);
|
||||
}
|
||||
catch (Exception callbackEx)
|
||||
{
|
||||
Error error = null;
|
||||
|
||||
try
|
||||
{
|
||||
result = await callback(message, context, CancellationToken.None);
|
||||
error = m_parentTransport.UnhandledExceptionHandler(callbackEx);
|
||||
}
|
||||
catch (Exception callbackEx)
|
||||
catch (Exception handlerEx)
|
||||
{
|
||||
Error error = null;
|
||||
|
||||
try
|
||||
{
|
||||
error = m_parentTransport.UnhandledExceptionHandler(callbackEx);
|
||||
}
|
||||
catch (Exception handlerEx)
|
||||
{
|
||||
Transport.FailFastExceptionHandler(handlerEx);
|
||||
}
|
||||
|
||||
if (error == null)
|
||||
{
|
||||
Transport.FailFastExceptionHandler(callbackEx);
|
||||
}
|
||||
|
||||
result = Message.FromError(error);
|
||||
Transport.FailFastExceptionHandler(handlerEx);
|
||||
}
|
||||
|
||||
if (error == null)
|
||||
{
|
||||
Transport.FailFastExceptionHandler(callbackEx);
|
||||
}
|
||||
|
||||
result = Message.FromError(error);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task DispatchEvent(string methodName, ReceiveContext context, IMessage message)
|
||||
{
|
||||
Log.Information("{0}.{1}: Got event {2} from {3}.",
|
||||
nameof(ServiceHost), nameof(DispatchEvent), methodName, context.Connection);
|
||||
ServiceMethodInfo methodInfo;
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (!m_dispatchTable.TryGetValue(methodName, out methodInfo))
|
||||
{
|
||||
LogUtil.FatalAndReturnFormatted("{0}.{1}: Got request for unknown method {2}.",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodInfo.CallbackType != ServiceCallbackType.Event)
|
||||
{
|
||||
LogUtil.FatalAndReturnFormatted("{0}.{1}: Method {2} invoked as if it were {3}, but it was registered as {4}.",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName, ServiceCallbackType.Event, methodInfo.CallbackType);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await methodInfo.Callback(message, context, CancellationToken.None);
|
||||
}
|
||||
catch (Exception callbackEx)
|
||||
{
|
||||
Error error = null;
|
||||
|
||||
try
|
||||
{
|
||||
error = m_parentTransport.UnhandledExceptionHandler(callbackEx);
|
||||
}
|
||||
catch (Exception handlerEx)
|
||||
{
|
||||
Transport.FailFastExceptionHandler(handlerEx);
|
||||
}
|
||||
|
||||
if (error == null)
|
||||
{
|
||||
Transport.FailFastExceptionHandler(callbackEx);
|
||||
}
|
||||
|
||||
LogUtil.FatalAndReturnFormatted("{0}.{1}: Failed to complete method {2}. With exception: {3}",
|
||||
nameof(ServiceHost), nameof(DispatchRequest), methodName, callbackEx.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Bond.Comm.Tcp
|
|||
Server
|
||||
}
|
||||
|
||||
public class TcpConnection : Connection, IRequestResponseConnection
|
||||
public class TcpConnection : Connection, IRequestResponseConnection, IEventConnection
|
||||
{
|
||||
private TcpTransport m_parentTransport;
|
||||
|
||||
|
@ -188,6 +188,25 @@ namespace Bond.Comm.Tcp
|
|||
}
|
||||
}
|
||||
|
||||
internal async Task SendEventAsync(string methodName, IMessage message)
|
||||
{
|
||||
uint requestId = AllocateNextRequestId();
|
||||
var frame = MessageToFrame(requestId, methodName, PayloadType.Event, message);
|
||||
|
||||
Log.Debug("{0}.{1}: Sending event {2}/{3}.", this, nameof(SendEventAsync), requestId, methodName);
|
||||
using (var binWriter = new BinaryWriter(m_networkStream, encoding: Encoding.UTF8, leaveOpen: true))
|
||||
{
|
||||
lock (m_networkStream)
|
||||
{
|
||||
frame.Write(binWriter);
|
||||
binWriter.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
await m_networkStream.FlushAsync();
|
||||
Log.Debug("{0}.{1}: Sent event {2}/{3}.", this, nameof(SendEventAsync), requestId, methodName);
|
||||
}
|
||||
|
||||
internal void Start()
|
||||
{
|
||||
Task.Run(() => ProcessFramesAsync(m_networkStream));
|
||||
|
@ -221,6 +240,10 @@ namespace Bond.Comm.Tcp
|
|||
DispatchResponse(result.Headers, result.Payload);
|
||||
break;
|
||||
|
||||
case TcpProtocol.FrameDisposition.DeliverEventToService:
|
||||
DispatchEvent(result.Headers, result.Payload);
|
||||
break;
|
||||
|
||||
case TcpProtocol.FrameDisposition.SendProtocolError:
|
||||
await SendProtocolErrorAsync(result.ErrorCode ?? ProtocolErrorCode.INTERNAL_ERROR);
|
||||
break;
|
||||
|
@ -278,6 +301,21 @@ namespace Bond.Comm.Tcp
|
|||
responseCompletionSource.SetResult(response);
|
||||
}
|
||||
|
||||
private void DispatchEvent(TcpHeaders headers, ArraySegment<byte> payload)
|
||||
{
|
||||
if (headers.error_code != (int)ErrorCode.OK)
|
||||
{
|
||||
throw new TcpProtocolErrorException("Received a request with non-zero error code. Request ID " + headers.request_id);
|
||||
}
|
||||
|
||||
IMessage request = Message.FromPayload(Unmarshal.From(payload));
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await m_serviceHost.DispatchEvent(headers.method_name, new TcpReceiveContext(this), request);
|
||||
});
|
||||
}
|
||||
|
||||
public override Task StopAsync()
|
||||
{
|
||||
Log.Debug("{0}.{1}: Shutting down.", this, nameof(StopAsync));
|
||||
|
@ -291,5 +329,10 @@ namespace Bond.Comm.Tcp
|
|||
IMessage response = await SendRequestAsync(methodName, message);
|
||||
return response.Convert<TResponse>();
|
||||
}
|
||||
|
||||
public Task FireEventAsync<TPayload>(string methodName, IMessage<TPayload> message)
|
||||
{
|
||||
return SendEventAsync(methodName, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Bond.IO.Safe;
|
||||
using Bond.Protocols;
|
||||
|
@ -28,6 +31,11 @@ namespace Bond.Comm.Tcp
|
|||
/// </summary>
|
||||
DeliverResponseToProxy,
|
||||
|
||||
/// <summary>
|
||||
/// The frame was a valid Event.
|
||||
/// </summary>
|
||||
DeliverEventToService,
|
||||
|
||||
/// <summary>
|
||||
/// The frame was not valid, and the caller should send an error to the remote host.
|
||||
/// </summary>
|
||||
|
@ -396,17 +404,12 @@ namespace Bond.Comm.Tcp
|
|||
{
|
||||
case PayloadType.Request:
|
||||
case PayloadType.Response:
|
||||
return ClassifyState.ValidFrame;
|
||||
|
||||
case PayloadType.Event:
|
||||
Log.Warning("{0}.{1}: Received unimplemented payload type {2}.",
|
||||
nameof(TcpProtocol), nameof(TransitionFrameComplete), headers.payload_type);
|
||||
errorCode = ProtocolErrorCode.NOT_SUPPORTED;
|
||||
return ClassifyState.MalformedFrame;
|
||||
|
||||
return ClassifyState.ValidFrame;
|
||||
default:
|
||||
Log.Warning("{0}.{1}: Received unrecognized payload type {2}.",
|
||||
nameof(TcpProtocol), nameof(TransitionFrameComplete), headers.payload_type);
|
||||
errorCode = ProtocolErrorCode.NOT_SUPPORTED;
|
||||
return ClassifyState.MalformedFrame;
|
||||
}
|
||||
}
|
||||
|
@ -429,6 +432,10 @@ namespace Bond.Comm.Tcp
|
|||
disposition = FrameDisposition.DeliverResponseToProxy;
|
||||
return ClassifyState.ClassifiedValidFrame;
|
||||
|
||||
case PayloadType.Event:
|
||||
disposition = FrameDisposition.DeliverEventToService;
|
||||
return ClassifyState.ClassifiedValidFrame;
|
||||
|
||||
default:
|
||||
return ClassifyState.InternalStateError;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -45,6 +48,13 @@ namespace UnitTest.Tcp
|
|||
payload_type = PayloadType.Event,
|
||||
request_id = GoodRequestId
|
||||
};
|
||||
private static readonly TcpHeaders unknownTypeHeaders = new TcpHeaders
|
||||
{
|
||||
error_code = 0,
|
||||
method_name = GoodMethod,
|
||||
payload_type = (PayloadType)(-100),
|
||||
request_id = GoodRequestId
|
||||
};
|
||||
|
||||
private static Frame goodRequestFrame;
|
||||
private static Frame goodResponseFrame;
|
||||
|
@ -411,7 +421,7 @@ namespace UnitTest.Tcp
|
|||
ProtocolErrorCode? errorCode = null;
|
||||
|
||||
var after = TcpProtocol.TransitionFrameComplete(
|
||||
TcpProtocol.ClassifyState.FrameComplete, goodEventHeaders, ref errorCode);
|
||||
TcpProtocol.ClassifyState.FrameComplete, unknownTypeHeaders, ref errorCode);
|
||||
Assert.AreEqual(TcpProtocol.ClassifyState.MalformedFrame, after);
|
||||
Assert.AreEqual(ProtocolErrorCode.NOT_SUPPORTED, errorCode);
|
||||
}
|
||||
|
@ -455,11 +465,6 @@ namespace UnitTest.Tcp
|
|||
var after = TcpProtocol.TransitionValidFrame(TcpProtocol.ClassifyState.ValidFrame, null, ref disposition);
|
||||
Assert.AreEqual(TcpProtocol.ClassifyState.InternalStateError, after);
|
||||
Assert.AreEqual(TcpProtocol.FrameDisposition.Indeterminate, disposition);
|
||||
|
||||
after = TcpProtocol.TransitionValidFrame(
|
||||
TcpProtocol.ClassifyState.ValidFrame, goodEventHeaders, ref disposition);
|
||||
Assert.AreEqual(TcpProtocol.ClassifyState.InternalStateError, after);
|
||||
Assert.AreEqual(TcpProtocol.FrameDisposition.Indeterminate, disposition);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -538,6 +543,14 @@ namespace UnitTest.Tcp
|
|||
Assert.Null(protocolErrorResult.Headers);
|
||||
Assert.Null(protocolErrorResult.Payload.Array);
|
||||
Assert.AreEqual(meaninglessErrorCode, protocolErrorResult.Error.error_code);
|
||||
|
||||
var eventResult = TcpProtocol.Classify(goodEventFrame);
|
||||
Assert.AreEqual(TcpProtocol.FrameDisposition.DeliverEventToService, eventResult.Disposition);
|
||||
Assert.AreEqual(goodEventHeaders.error_code, eventResult.Headers.error_code);
|
||||
Assert.AreEqual(goodEventHeaders.method_name, eventResult.Headers.method_name);
|
||||
Assert.AreEqual(goodEventHeaders.payload_type, eventResult.Headers.payload_type);
|
||||
Assert.AreEqual(goodEventHeaders.request_id, eventResult.Headers.request_id);
|
||||
Assert.AreEqual(goodEventFrame.Framelets[goodEventFrame.Count - 1].Contents, eventResult.Payload);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -553,12 +566,6 @@ namespace UnitTest.Tcp
|
|||
[Test]
|
||||
public void Classify_MalformedFrame()
|
||||
{
|
||||
var eventResult = TcpProtocol.Classify(goodEventFrame);
|
||||
Assert.AreEqual(TcpProtocol.FrameDisposition.SendProtocolError, eventResult.Disposition);
|
||||
Assert.Null(eventResult.Headers);
|
||||
Assert.Null(eventResult.Payload.Array);
|
||||
Assert.AreEqual(ProtocolErrorCode.NOT_SUPPORTED, eventResult.ErrorCode);
|
||||
|
||||
var emptyResult = TcpProtocol.Classify(emptyFrame);
|
||||
Assert.AreEqual(TcpProtocol.FrameDisposition.SendProtocolError, emptyResult.Disposition);
|
||||
Assert.Null(emptyResult.Headers);
|
||||
|
|
|
@ -160,6 +160,17 @@ namespace UnitTest.Tcp
|
|||
await testClientServer.Transport.StopAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestServiceMethodTypeValidation_Throws()
|
||||
{
|
||||
var exception = Assert.Throws<ArgumentException>(async () => await SetupTestClientServer<TestServiceEventMismatch>());
|
||||
Assert.That(exception.Message, Is.StringContaining("registered as Event but callback not implemented as such"));
|
||||
exception = Assert.Throws<ArgumentException>(async () => await SetupTestClientServer<TestServiceReqResMismatch>());
|
||||
Assert.That(exception.Message, Is.StringContaining("registered as RequestResponse but callback not implemented as such"));
|
||||
exception = Assert.Throws<ArgumentException>(async () => await SetupTestClientServer<TestServiceUnsupported>());
|
||||
Assert.That(exception.Message, Is.StringContaining("registered as invalid type"));
|
||||
}
|
||||
|
||||
private class TestClientServer<TService>
|
||||
{
|
||||
public TService Service;
|
||||
|
@ -274,5 +285,75 @@ namespace UnitTest.Tcp
|
|||
return Task.FromResult<IMessage<Dummy>>(Message.FromPayload(result));
|
||||
}
|
||||
}
|
||||
private class TestServiceEventMismatch : IService
|
||||
{
|
||||
public IEnumerable<ServiceMethodInfo> Methods
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ServiceMethodInfo
|
||||
{
|
||||
MethodName = "TestService.RespondWithEmpty",
|
||||
Callback = RespondWithEmpty,
|
||||
CallbackType = ServiceCallbackType.Event
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private Task<IMessage> RespondWithEmpty(IMessage request, ReceiveContext context, CancellationToken ct)
|
||||
{
|
||||
var emptyMessage = Message.FromPayload(new Bond.Void());
|
||||
return Task.FromResult<IMessage>(emptyMessage);
|
||||
}
|
||||
}
|
||||
private class TestServiceReqResMismatch : IService
|
||||
{
|
||||
public IEnumerable<ServiceMethodInfo> Methods
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ServiceMethodInfo
|
||||
{
|
||||
MethodName = "TestService.DoBeep",
|
||||
Callback = DoBeep,
|
||||
CallbackType = ServiceCallbackType.RequestResponse
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private Task DoBeep(IMessage request, ReceiveContext context, CancellationToken ct)
|
||||
{
|
||||
return CodegenHelpers.CompletedTask;
|
||||
}
|
||||
}
|
||||
private class TestServiceUnsupported : IService
|
||||
{
|
||||
public IEnumerable<ServiceMethodInfo> Methods
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ServiceMethodInfo
|
||||
{
|
||||
MethodName = "TestService.DoBeep",
|
||||
Callback = DoBeep,
|
||||
CallbackType = (ServiceCallbackType)(-100)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private Task DoBeep(IMessage request, ReceiveContext context, CancellationToken ct)
|
||||
{
|
||||
return CodegenHelpers.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Bond.Examples.NotifyEvent
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bond.Comm;
|
||||
using Bond.Comm.Tcp;
|
||||
using Bond.Examples.Logging;
|
||||
|
||||
public static class NotifyEvent
|
||||
{
|
||||
private static TcpConnection s_connection;
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
var transport = SetupAsync().Result;
|
||||
|
||||
MakeRequestsAndPrint(5);
|
||||
|
||||
Console.WriteLine("Done with all requests.");
|
||||
|
||||
// TODO: Shutdown not yet implemented.
|
||||
// transport.StopAsync().Wait();
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
private async static Task<TcpTransport> SetupAsync()
|
||||
{
|
||||
var handler = new ConsoleLogger();
|
||||
Log.AddHandler(handler);
|
||||
|
||||
var transport = new TcpTransportBuilder()
|
||||
.SetUnhandledExceptionHandler(Transport.ToErrorExceptionHandler)
|
||||
.Construct();
|
||||
|
||||
var assignAPortEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
|
||||
|
||||
var notifyService = new NotifyEventService();
|
||||
TcpListener notifyListener = transport.MakeListener(assignAPortEndPoint);
|
||||
notifyListener.AddService(notifyService);
|
||||
|
||||
await notifyListener.StartAsync();
|
||||
|
||||
s_connection = await transport.ConnectToAsync(notifyListener.ListenEndpoint, CancellationToken.None);
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
private static void MakeRequestsAndPrint(int numRequests)
|
||||
{
|
||||
var notifyEventProxy = new NotifyEventProxy<TcpConnection>(s_connection);
|
||||
|
||||
var rnd = new Random();
|
||||
|
||||
foreach (var requestNum in Enumerable.Range(0, numRequests))
|
||||
{
|
||||
UInt16 delay = (UInt16)rnd.Next(2000);
|
||||
DoNotify(notifyEventProxy, requestNum, "notify" + requestNum.ToString(), delay);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DoNotify(NotifyEventProxy<TcpConnection> proxy, int requestNum, string payload, UInt16 delay)
|
||||
{
|
||||
var request = new PingRequest { Payload = payload, DelayMilliseconds = delay };
|
||||
proxy.NotifyAsync(request);
|
||||
|
||||
Console.WriteLine($"P Event #{requestNum} Delay: {delay}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Bond.Examples.NotifyEvent
|
||||
{
|
||||
using Comm;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class NotifyEventService : NotifyEventServiceBase
|
||||
{
|
||||
private const UInt16 MaxDelayMilliseconds = 2000;
|
||||
|
||||
public override void NotifyAsync(IMessage<PingRequest> param)
|
||||
{
|
||||
PingRequest request = param.Payload.Deserialize();
|
||||
|
||||
if (request.DelayMilliseconds > 0)
|
||||
{
|
||||
UInt16 delayMs = Math.Min(MaxDelayMilliseconds, request.DelayMilliseconds);
|
||||
Thread.Sleep(delayMs);
|
||||
}
|
||||
Console.WriteLine("Notified server-side, payload: " + request.Payload + " delay: "+request.DelayMilliseconds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Bond.Examples.NotifyEvent;
|
||||
|
||||
struct PingRequest
|
||||
{
|
||||
0: string Payload;
|
||||
1: uint16 DelayMilliseconds;
|
||||
}
|
||||
|
||||
service NotifyEvent
|
||||
{
|
||||
nothing Notify(PingRequest);
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\..\..\..\cs\build\Bond.CSharp.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{12279366-F646-4FEC-8CAA-B62A8EC477BB}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>notifyevent</RootNamespace>
|
||||
<AssemblyName>notifyevent</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="NotifyEvent.cs" />
|
||||
<Compile Include="NotifyEventService.cs" />
|
||||
<BondCodegen Include="notify.bond" />
|
||||
<!-- Resharper Workaround -->
|
||||
<Compile Include="$(IntermediateOutputPath)\notify_types.cs" Condition="False" />
|
||||
<!-- End Resharper Workaround -->
|
||||
<!-- TODO: edit the .targets to automatically include the comm files -->
|
||||
<Compile Include="$(IntermediateOutputPath)\notify_interfaces.cs" />
|
||||
<Compile Include="$(IntermediateOutputPath)\notify_proxies.cs" />
|
||||
<Compile Include="$(IntermediateOutputPath)\notify_services.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Attributes">
|
||||
<HintPath>$(BOND_BINARY_PATH)\net45\Bond.Attributes.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Bond">
|
||||
<HintPath>$(BOND_BINARY_PATH)\net45\Bond.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Bond.IO">
|
||||
<HintPath>$(BOND_BINARY_PATH)\net45\Bond.IO.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Bond.Comm.Interfaces">
|
||||
<HintPath>$(BOND_BINARY_PATH)\net45\Bond.Comm.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Bond.Comm.Tcp">
|
||||
<HintPath>$(BOND_BINARY_PATH)\net45\Bond.Comm.Tcp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\logging\logging.csproj">
|
||||
<Project>{5c8132a8-c4b1-45e0-bca6-379da23b86d3}</Project>
|
||||
<Name>logging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(BOND_PATH)\build\Bond.CSharp.targets" />
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче