#if !BESTHTTP_DISABLE_SIGNALR_CORE && !BESTHTTP_DISABLE_WEBSOCKET using BestHTTP.SignalRCore.Messages; using System; using System.Collections.Generic; #if NETFX_CORE || NET_4_6 using System.Reflection; #endif namespace BestHTTP.SignalRCore { public interface IProtocol { TransferModes Type { get; } IEncoder Encoder { get; } HubConnection Connection { get; set; } /// /// This function must parse textual representation of the messages into the list of Messages. /// void ParseMessages(string data, ref List messages); /// /// This function must parse binary representation of the messages into the list of Messages. /// void ParseMessages(byte[] data, ref List messages); /// /// This function must return the encoded representation of the given message. /// byte[] EncodeMessage(Message message); /// /// This function must convert all element in the arguments array to the corresponding type from the argTypes array. /// object[] GetRealArguments(Type[] argTypes, object[] arguments); /// /// Convert a value to the given type. /// object ConvertTo(Type toType, object obj); } public sealed class JsonProtocol : IProtocol { public const char Separator = (char)0x1E; public TransferModes Type { get { return TransferModes.Text; } } public IEncoder Encoder { get; private set; } public HubConnection Connection { get; set; } public JsonProtocol(IEncoder encoder) { if (encoder == null) throw new ArgumentNullException("encoder"); if (encoder.Name != "json") throw new ArgumentException("Encoder must be a json encoder!"); this.Encoder = encoder; } public void ParseMessages(string data, ref List messages) { int from = 0; int separatorIdx = data.IndexOf(JsonProtocol.Separator); if (separatorIdx == -1) throw new Exception("Missing separator!"); while (separatorIdx != -1) { string sub = data.Substring(from, separatorIdx - from); var message = this.Encoder.DecodeAs(sub); messages.Add(message); from = separatorIdx + 1; separatorIdx = data.IndexOf(JsonProtocol.Separator, from); } } public void ParseMessages(byte[] data, ref List messages) { } public byte[] EncodeMessage(Message message) { string json = null; switch (message.type) { case MessageTypes.Invocation: case MessageTypes.StreamInvocation: // While message contains all informations already, the spec states that no additional field are allowed in messages // So we are creating 'specialized' messages here to send to the server. json = this.Encoder.EncodeAsText(new InvocationMessage() { type = message.type, invocationId = message.invocationId, nonblocking = message.nonblocking, target = message.target, arguments = message.arguments }); break; case MessageTypes.CancelInvocation: json = this.Encoder.EncodeAsText(new CancelInvocationMessage() { invocationId = message.invocationId }); break; } return !string.IsNullOrEmpty(json) ? JsonProtocol.WithSeparator(json) : null; } public object[] GetRealArguments(Type[] argTypes, object[] arguments) { if (arguments == null || arguments.Length == 0) return null; if (argTypes.Length > arguments.Length) throw new Exception(string.Format("argType.Length({0}) < arguments.length({1})", argTypes.Length, arguments.Length)); object[] realArgs = new object[arguments.Length]; for (int i = 0; i < arguments.Length; ++i) realArgs[i] = ConvertTo(argTypes[i], arguments[i]); return realArgs; } public object ConvertTo(Type toType, object obj) { if (obj == null) return null; #if NETFX_CORE //|| NET_4_6 if (toType.GetTypeInfo().IsPrimitive || toType.GetTypeInfo().IsEnum) #else if (toType.IsPrimitive || toType.IsEnum) #endif return Convert.ChangeType(obj, toType); if (toType == typeof(string)) return obj.ToString(); return this.Encoder.ConvertTo(toType, obj); } /// /// Returns the given string parameter's bytes with the added separator(0x1E). /// public static byte[] WithSeparator(string str) { int len = System.Text.Encoding.UTF8.GetByteCount(str); byte[] buffer = new byte[len + 1]; System.Text.Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0); buffer[len] = 0x1e; return buffer; } } } #endif