#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