diff --git a/samples/SocketsSample/EndPoints/ChatEndPoint.cs b/samples/SocketsSample/EndPoints/ChatEndPoint.cs index f75545d839..60afc6bee2 100644 --- a/samples/SocketsSample/EndPoints/ChatEndPoint.cs +++ b/samples/SocketsSample/EndPoints/ChatEndPoint.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Channels; diff --git a/samples/SocketsSample/EndPoints/HubEndpoint.cs b/samples/SocketsSample/EndPoints/HubEndpoint.cs index 4c97279831..eb48626b9a 100644 --- a/samples/SocketsSample/EndPoints/HubEndpoint.cs +++ b/samples/SocketsSample/EndPoints/HubEndpoint.cs @@ -59,6 +59,7 @@ namespace SocketsSample Arguments = args }; + // TODO: serialize once per format by providing a different stream? foreach (var connection in _endPoint.Connections) { diff --git a/samples/SocketsSample/Hubs/Chat.cs b/samples/SocketsSample/Hubs/Chat.cs index abef1b6029..a480807b57 100644 --- a/samples/SocketsSample/Hubs/Chat.cs +++ b/samples/SocketsSample/Hubs/Chat.cs @@ -11,5 +11,10 @@ namespace SocketsSample.Hubs { Clients.All.Invoke("Send", message); } + + public Person EchoPerson(Person p) + { + return p; + } } } diff --git a/samples/SocketsSample/Hubs/Person.cs b/samples/SocketsSample/Hubs/Person.cs new file mode 100644 index 0000000000..66f38a1b9e --- /dev/null +++ b/samples/SocketsSample/Hubs/Person.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SocketsSample.Hubs +{ + public class Person + { + public string Name { get; set; } + public long Age { get; set; } + } +} diff --git a/samples/SocketsSample/Protobuf/ProtobufInvocationAdapter.cs b/samples/SocketsSample/Protobuf/ProtobufInvocationAdapter.cs index 2ff60774af..c993e25f61 100644 --- a/samples/SocketsSample/Protobuf/ProtobufInvocationAdapter.cs +++ b/samples/SocketsSample/Protobuf/ProtobufInvocationAdapter.cs @@ -2,17 +2,25 @@ using System.IO; using System.Threading.Tasks; using Google.Protobuf; +using Microsoft.Extensions.DependencyInjection; namespace SocketsSample.Protobuf { public class ProtobufInvocationAdapter : IInvocationAdapter { + IServiceProvider _serviceProvider; + + public ProtobufInvocationAdapter(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + public async Task CreateInvocationDescriptor(Stream stream, Func getParams) { return await Task.Run(() => CreateInvocationDescriptorInt(stream, getParams)); } - private static Task CreateInvocationDescriptorInt(Stream stream, Func getParams) + private Task CreateInvocationDescriptorInt(Stream stream, Func getParams) { var inputStream = new CodedInputStream(stream, leaveOpen: true); var invocationHeader = new RpcInvocationHeader(); @@ -28,19 +36,22 @@ namespace SocketsSample.Protobuf for (var i = 0; i < argumentTypes.Length; i++) { - var value = new PrimitiveValue(); - inputStream.ReadMessage(value); if (typeof(int) == argumentTypes[i]) { + var value = new PrimitiveValue(); + inputStream.ReadMessage(value); invocationDescriptor.Arguments[i] = value.Int32Value; } else if (typeof(string) == argumentTypes[i]) { + var value = new PrimitiveValue(); + inputStream.ReadMessage(value); invocationDescriptor.Arguments[i] = value.StringValue; } else { - throw new InvalidOperationException(); + var serializer = _serviceProvider.GetRequiredService(); + invocationDescriptor.Arguments[i] = serializer.GetValue(inputStream, argumentTypes[i]); } } @@ -65,7 +76,7 @@ namespace SocketsSample.Protobuf outputStream.WriteMessage(resultHeader); - if (resultHeader.Error == null && resultDescriptor.Result != null) + if (string.IsNullOrEmpty(resultHeader.Error) && resultDescriptor.Result != null) { var result = resultDescriptor.Result; @@ -77,6 +88,12 @@ namespace SocketsSample.Protobuf { outputStream.WriteMessage(new PrimitiveValue { StringValue = (string)result }); } + else + { + var serializer = _serviceProvider.GetRequiredService(); + var message = serializer.GetMessage(result); + outputStream.WriteMessage(message); + } } outputStream.Flush(); diff --git a/samples/SocketsSample/Protobuf/RpcInvocation.cs b/samples/SocketsSample/Protobuf/RpcInvocation.cs index 1a4ed9ed19..c006bb809f 100644 --- a/samples/SocketsSample/Protobuf/RpcInvocation.cs +++ b/samples/SocketsSample/Protobuf/RpcInvocation.cs @@ -27,14 +27,16 @@ public static partial class RpcInvocationReflection { "IAEoBSJJChlScGNJbnZvY2F0aW9uUmVzdWx0SGVhZGVyEgoKAklkGAEgASgF", "EhEKCUhhc1Jlc3VsdBgCIAEoCBINCgVFcnJvchgDIAEoCSJHCg5QcmltaXRp", "dmVWYWx1ZRIUCgpJbnQzMlZhbHVlGAEgASgFSAASFQoLU3RyaW5nVmFsdWUY", - "AiABKAlIAEIICgZvbmVvZl9iBnByb3RvMw==")); + "AiABKAlIAEIICgZvbmVvZl8iKgoNUGVyc29uTWVzc2FnZRIMCgROYW1lGAEg", + "ASgJEgsKA0FnZRgCIAEoA2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::RpcMessageKind), global::RpcMessageKind.Parser, new[]{ "MessageKind" }, null, new[]{ typeof(global::RpcMessageKind.Types.Kind) }, null), new pbr::GeneratedClrTypeInfo(typeof(global::RpcInvocationHeader), global::RpcInvocationHeader.Parser, new[]{ "Name", "Id", "NumArgs" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::RpcInvocationResultHeader), global::RpcInvocationResultHeader.Parser, new[]{ "Id", "HasResult", "Error" }, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::PrimitiveValue), global::PrimitiveValue.Parser, new[]{ "Int32Value", "StringValue" }, new[]{ "Oneof" }, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::PrimitiveValue), global::PrimitiveValue.Parser, new[]{ "Int32Value", "StringValue" }, new[]{ "Oneof" }, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::PersonMessage), global::PersonMessage.Parser, new[]{ "Name", "Age" }, null, null, null) })); } #endregion @@ -692,6 +694,151 @@ public sealed partial class PrimitiveValue : pb::IMessage { } +public sealed partial class PersonMessage : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new PersonMessage()); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::RpcInvocationReflection.Descriptor.MessageTypes[4]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PersonMessage() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PersonMessage(PersonMessage other) : this() { + name_ = other.name_; + age_ = other.age_; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PersonMessage Clone() { + return new PersonMessage(this); + } + + /// Field number for the "Name" field. + public const int NameFieldNumber = 1; + private string name_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "Age" field. + public const int AgeFieldNumber = 2; + private long age_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long Age { + get { return age_; } + set { + age_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as PersonMessage); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(PersonMessage other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + if (Age != other.Age) return false; + return true; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (Age != 0L) hash ^= Age.GetHashCode(); + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + if (Age != 0L) { + output.WriteRawTag(16); + output.WriteInt64(Age); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (Age != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(Age); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(PersonMessage other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.Age != 0L) { + Age = other.Age; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + case 16: { + Age = input.ReadInt64(); + break; + } + } + } + } + +} + #endregion diff --git a/samples/SocketsSample/Protobuf/RpcInvocation.proto b/samples/SocketsSample/Protobuf/RpcInvocation.proto index 7cb9ca3361..f63ad75a05 100644 --- a/samples/SocketsSample/Protobuf/RpcInvocation.proto +++ b/samples/SocketsSample/Protobuf/RpcInvocation.proto @@ -22,4 +22,9 @@ message PrimitiveValue { int32 Int32Value = 1; string StringValue = 2; } +} + +message PersonMessage { + string Name = 1; + int64 Age = 2; } \ No newline at end of file diff --git a/samples/SocketsSample/ProtobufSerializer.cs b/samples/SocketsSample/ProtobufSerializer.cs new file mode 100644 index 0000000000..c5c602343f --- /dev/null +++ b/samples/SocketsSample/ProtobufSerializer.cs @@ -0,0 +1,33 @@ +using System; +using Google.Protobuf; +using SocketsSample.Hubs; + +namespace SocketsSample +{ + public class ProtobufSerializer + { + public object GetValue(CodedInputStream inputStream, Type type) + { + if (type == typeof(Person)) + { + var value = new PersonMessage(); + inputStream.ReadMessage(value); + + return new Person { Name = value.Name, Age = value.Age }; + } + + throw new InvalidOperationException("(Deserialize) Unknown type."); + } + + public IMessage GetMessage(object value) + { + Person person = value as Person; + if (person != null) + { + return new PersonMessage { Name = person.Name, Age = person.Age }; + } + + throw new InvalidOperationException("(Serialize) Unknown type."); + } + } +} diff --git a/samples/SocketsSample/Startup.cs b/samples/SocketsSample/Startup.cs index be9dc3a8ad..2ba8bc7570 100644 --- a/samples/SocketsSample/Startup.cs +++ b/samples/SocketsSample/Startup.cs @@ -20,6 +20,7 @@ namespace SocketsSample services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); } @@ -45,7 +46,7 @@ namespace SocketsSample app.UseFormatters(formatters=> { - formatters.AddInvocationAdapter("protobuf", new Protobuf.ProtobufInvocationAdapter()); + formatters.AddInvocationAdapter("protobuf", new Protobuf.ProtobufInvocationAdapter(app.ApplicationServices)); formatters.AddInvocationAdapter("json", new JSonInvocationAdapter()); formatters.AddInvocationAdapter("line", new LineInvocationAdapter()); });